The weight adjustment mechanism in the pool has a potential issue when updates do not occur exactly after the updateInterval. If weights are not updated exactly at the updateInterval, they may move away from the intended target values. This deviation creates an arbitrage opportunity for users and could result in financial losses for the protocol.
contract QuantAMMWeightedPoolWeightTest is QuantAMMWeightedPoolContractsDeployer, BaseVaultTest {
using CastingHelpers for address[];
using ArrayHelpers for *;
uint256 internal daiIdx;
uint256 internal usdcIdx;
MockChainlinkOracle chainlinkOracle1;
MockIdentityRule mockRule;
uint64 public constant MAX_SWAP_FEE_PERCENTAGE = 10e16;
QuantAMMWeightedPoolFactory internal quantAMMWeightedPoolFactory;
function setUp() public override {
int216 fixedValue = 1000;
uint delay = 3600;
super.setUp();
(address ownerLocal, address addr1Local, address addr2Local) = (vm.addr(1), vm.addr(2), vm.addr(3));
owner = ownerLocal;
addr1 = addr1Local;
addr2 = addr2Local;
vm.startPrank(owner);
updateWeightRunner = new MockUpdateWeightRunner(owner, addr2, false);
chainlinkOracle = _deployOracle(fixedValue, delay);
updateWeightRunner.addOracle(OracleWrapper(chainlinkOracle));
int216 fixedValue1 = 1000;
int216 fixedValue2 = 1001;
chainlinkOracle1 = new MockChainlinkOracle(fixedValue1, 0);
updateWeightRunner.addOracle(chainlinkOracle1);
mockRule = new MockIdentityRule();
vm.stopPrank();
quantAMMWeightedPoolFactory = deployQuantAMMWeightedPoolFactory(
IVault(address(vault)),
365 days,
"Factory v1",
"Pool v1"
);
vm.label(address(quantAMMWeightedPoolFactory), "quantamm weighted pool factory");
(daiIdx, usdcIdx) = getSortedIndexes(address(dai), address(usdc));
}
function test_WeightAfterIntervalPassed() public {
uint16 updateInterval = 10;
QuantAMMWeightedPoolFactory.NewPoolParams memory params = _createPoolParams(updateInterval);
(address quantAMMWeightedPool, ) = quantAMMWeightedPoolFactory.create(params);
vm.startPrank(owner);
updateWeightRunner.setApprovedActionsForPool(address(quantAMMWeightedPool), 9);
vm.stopPrank();
int256[] memory initialWeights = new int256[]();
initialWeights[0] = 0.5e18;
initialWeights[1] = 0.5e18;
initialWeights[2] = 0;
initialWeights[3] = 0;
mockRule.setWeights(initialWeights);
updateWeightRunner.performUpdate(address(quantAMMWeightedPool));
uint256[] memory calcWeights2 = QuantAMMWeightedPool(quantAMMWeightedPool).getNormalizedWeights();
vm.warp(block.timestamp + updateInterval);
int256[] memory targetWeights = new int256[]();
targetWeights[0] = 0.7e18;
targetWeights[1] = 0.3e18;
mockRule.setWeights(targetWeights);
updateWeightRunner.performUpdate(address(quantAMMWeightedPool));
vm.warp(block.timestamp + updateInterval);
uint256[] memory normalizedWeights = QuantAMMWeightedPool(quantAMMWeightedPool).getNormalizedWeights();
assertEq(normalizedWeights[0] == uint256(targetWeights[0]), true);
assertEq(normalizedWeights[1] == uint256(targetWeights[1]), true);
vm.warp(block.timestamp + 1);
uint256[] memory normalizedWeights2 = QuantAMMWeightedPool(quantAMMWeightedPool).getNormalizedWeights();
assertEq(normalizedWeights2[0] == 0.72e18, true);
assertEq(normalizedWeights2[1] == 0.28e18, true);
}
function _createPoolParams(
uint16 updateInterval
) internal returns (QuantAMMWeightedPoolFactory.NewPoolParams memory retParams) {
PoolRoleAccounts memory roleAccounts;
IERC20[] memory tokens = [address(dai), address(usdc)].toMemoryArray().asIERC20();
uint32[] memory weights = new uint32[]();
weights[0] = uint32(uint256(0.5e18));
weights[1] = uint32(uint256(0.5e18));
int256[] memory initialWeights = new int256[]();
initialWeights[0] = 0.5e18;
initialWeights[1] = 0.5e18;
uint256[] memory initialWeightsUint = new uint256[]();
initialWeightsUint[0] = 0.5e18;
initialWeightsUint[1] = 0.5e18;
uint64[] memory lambdas = new uint64[]();
lambdas[0] = 0.0000000005e18;
address[][] memory oracles = new address[][]();
oracles[0] = new address[]();
oracles[0][0] = address(chainlinkOracle1);
retParams = QuantAMMWeightedPoolFactory.NewPoolParams(
"Pool With Donation",
"PwD",
vault.buildTokenConfig(tokens),
initialWeightsUint,
roleAccounts,
MAX_SWAP_FEE_PERCENTAGE,
address(0),
true,
false,
ZERO_BYTES32,
initialWeights,
IQuantAMMWeightedPool.PoolSettings(
new IERC20[](2),
IUpdateRule(mockRule),
oracles,
updateInterval,
lambdas,
0.2e18,
0.2e18,
0.2e18,
new int256[][](),
address(0)
),
initialWeights,
initialWeights,
3600,
0,
new string[][]()
);
}
}
Deviations from target weights can cause imbalances in the pool and create arbitrage oppurtunities