pragma solidity >=0.8.24;
import {Test} from "forge-std/Test.sol";
import {PRBMathSD59x18} from "@prb/math/contracts/PRBMathSD59x18.sol";
import {QuantAMMCovarianceBasedRule} from "../../../contracts/rules/base/QuantammCovarianceBasedRule.sol";
import {QuantAMMPoolParameters} from "../../../contracts/UpdateWeightRunner.sol";
contract MockQuantammCovarianceBasedRule is QuantAMMCovarianceBasedRule {
using PRBMathSD59x18 for int256;
function exposed_calculateQuantAMMCovariance(
int256[] memory _newData,
QuantAMMPoolParameters memory _poolParameters
) public returns (int256[][] memory) {
return _calculateQuantAMMCovariance(_newData, _poolParameters);
}
function exposed_setIntermediateCovariance(
address _poolAddress,
int256[][] memory _initialValues,
uint _numberOfAssets
) public {
_setIntermediateCovariance(_poolAddress, _initialValues, _numberOfAssets);
}
}
contract QuantammCovarianceBasedRuleTest is Test {
using PRBMathSD59x18 for int256;
MockQuantammCovarianceBasedRule public rule;
address public mockPool;
uint256 public constant N_ASSETS = 2;
int256 public constant LAMBDA = 0.95e18;
int256 public constant ONE = 1e18;
int256 public constant TOLERANCE = 0.0001e18;
int256 public constant PRICE = 100e18;
function setUp() public {
rule = new MockQuantammCovarianceBasedRule();
mockPool = address(0x1);
}
function testCovarianceScenarioC_MovingAverageCatchup() public {
int256[][] memory initialCovariance = new int256[][]();
for (uint i = 0; i < N_ASSETS; i++) {
initialCovariance[i] = new int256[]();
for (uint j = 0; j < N_ASSETS; j++) {
initialCovariance[i][j] = 100e18;
}
}
rule.exposed_setIntermediateCovariance(mockPool, initialCovariance, N_ASSETS);
QuantAMMPoolParameters memory params = QuantAMMPoolParameters({
pool: mockPool,
numberOfAssets: N_ASSETS,
lambda: new int128[](N_ASSETS),
movingAverage: new int256[](N_ASSETS * 2)
});
for (uint i = 0; i < N_ASSETS; i++) {
params.lambda[i] = int128(LAMBDA);
}
int256[] memory newData = new int256[]();
newData[0] = PRICE;
newData[1] = PRICE;
int256[][] memory prevResult;
int256[][] memory result;
int256 prevDeviation;
for(uint step = 0; step <= 10; step++) {
int256 currentMA = (PRICE * int256(step)) / 10;
for (uint i = 0; i < N_ASSETS; i++) {
params.movingAverage[i] = currentMA;
params.movingAverage[N_ASSETS + i] = currentMA;
}
result = rule.exposed_calculateQuantAMMCovariance(newData, params);
int256 currentDeviation = (PRICE - currentMA).abs();
emit log_named_uint("Step", step);
emit log_named_int("Moving Average", currentMA);
emit log_named_int("Current Deviation", currentDeviation);
emit log_named_int("Covariance", result[0][0]);
if(step > 0) {
int256 changePercent = (result[0][0] - prevResult[0][0]).mul(100e18).div(prevResult[0][0]);
emit log_named_int("Change %", changePercent);
emit log_named_int("Prev Deviation", prevDeviation);
assertTrue(
currentDeviation < prevDeviation,
"Deviation should decrease"
);
if(step > 5) {
assertTrue(
result[0][0] < prevResult[0][0],
"Scenario C: Covariance should decrease as MA catches up"
);
}
}
prevResult = result;
prevDeviation = currentDeviation;
}
assertTrue(
result[0][0].mul(0.01e18).div(ONE) >= (result[0][0] - result[0][0].mul(LAMBDA)).abs(),
"Scenario C: Should approach scenario A behavior"
);
}
}
Step 0: MA=0, Dev=100, Cov=504.75
Step 1: MA=10, Dev=90, Cov=884.51 (+75.23%)
Step 2: MA=20, Dev=80, Cov=1160.28 (+31.17%)
Step 3: MA=30, Dev=70, Cov=1347.27 (+16.11%)
Step 4: MA=40, Dev=60, Cov=1459.90 (+8.36%)
Step 5: MA=50, Dev=50, Cov=1511.91 (+3.56%)
Step 6: MA=60, Dev=40, Cov=1516.31 (+0.29%)
The test shows that despite decreasing price-MA deviation (100 → 40), covariance grows from 504.75 to 1516.31, contradicting both mathematical principles and the whitepaper's specifications.