In many AMM or WeightedPool designs, exponents are meant to capture real “trend” dynamics. The chosen approach discards that domain issue by forcibly re-signing the result.
For non-integer exponents, ( − 𝑥 ) 𝑒 (−x) e is not simply − ( 𝑥 𝑒 ) −(x e ) if 𝑒 e has a fractional part.
function testPoCNegativeGradientNonIntegerExponentMultiAsset() public {
* --------------------------------------------------------------
* 1) Set up parameters for two assets
* - Both have the same fractional exponent=1.5
* - Keep other parameters fairly standard
* --------------------------------------------------------------
*/
int256[][] memory parameters = new int256[][]();
parameters[0] = new int256[]();
parameters[0][0] = 200e18;
parameters[0][1] = 200e18;
parameters[1] = new int256[]();
parameters[1][0] = 1e18;
parameters[1][1] = 1e18;
parameters[2] = new int256[]();
parameters[2][0] = 0.1e18;
parameters[2][1] = 0.1e18;
parameters[3] = new int256[]();
parameters[3][0] = 1.5e18;
parameters[3][1] = 1.5e18;
parameters[4] = new int256[]();
parameters[4][0] = 1e18;
parameters[4][1] = 1e18;
parameters[5] = new int256[]();
parameters[5][0] = 0.5e18;
parameters[5][1] = 0.5e18;
parameters[6] = new int256[]();
parameters[6][0] = 1e18;
* --------------------------------------------------------------
* 2) Pool setup for two assets
* --------------------------------------------------------------
*/
mockPool.setNumberOfAssets(2);
int256[] memory prevWeights = new int256[]();
prevWeights[0] = 0.5e18;
prevWeights[1] = 0.5e18;
int256[] memory prevAlphas = new int256[]();
prevAlphas[0] = 1e18;
prevAlphas[1] = 1e18;
int256[] memory prevMovingAverages = new int256[]();
prevMovingAverages[0] = 5e18;
prevMovingAverages[1] = 5e18;
int256[] memory movingAverages = new int256[]();
movingAverages[0] = 5e18;
movingAverages[1] = 5e18;
* --------------------------------------------------------------
* 5) New "raw" prices data:
* Asset0: from 5 => 1 => big negative slope
* Asset1: from 5 => 8 => positive slope
* --------------------------------------------------------------
*/
int256[] memory data = new int256[]();
data[0] = 1e18;
data[1] = 8e18;
int128[] memory lambdas = new int128[]();
lambdas[0] = int128(0.9e18);
rule.initialisePoolRuleIntermediateValues(
address(mockPool),
prevMovingAverages,
prevAlphas,
mockPool.numAssets()
);
* --------------------------------------------------------------
* 8) Calculate new weights
* --------------------------------------------------------------
*/
rule.CalculateUnguardedWeights(
prevWeights,
data,
address(mockPool),
parameters,
lambdas,
movingAverages
);
int256[] memory resultWeights = rule.GetResultWeights();
* --------------------------------------------------------------
* 9) Demonstrate the bug
* - The exponent=1.5 on a negative base (Asset0) is
* mathematically invalid in real numbers. The contract's
* sign-flip logic will produce a *seemingly valid* negative
* real number. That leads to an incorrect final weight
* for Asset0.
*
* - We show that the result is suspicious by forcing the test
* to fail if it remains in a plausible 0..1 range.
* --------------------------------------------------------------
*/
console.log("Asset0 (negative gradient) final weight: %e", uint256(resultWeights[0]));
console.log("Asset1 (positive gradient) final weight: %e", uint256(resultWeights[1]));
* In a mathematically correct system, sqrt(negativeNumber)
* should be imaginary, or the logic would need to revert/fallback
* to a real-valued alternative. If the code just flips the sign,
* it yields a real negative partial value that can push the weight
* around incorrectly.
*
* Here, we demonstrate a naive check that simply fails if
* Asset0 remains in the standard [0..1] range, implying the
* code quietly computed an impossible real number.
*/
bool isAsset0InRange = (resultWeights[0] >= 0 && resultWeights[0] <= 1e18);
assertFalse(
isAsset0InRange,
"BUG DEMO: Asset0 final weight incorrectly ended up in normal range despite sqrt of negative base"
);
}
Either enforce integer exponents only, ensuring that negative bases remain mathematically valid (e.g.,(−𝑥)2(−x)2=𝑥2x2).