Assume there was a long pause and weights have to be set again manually as it depends on previous recent weights and multiplier so the solution is to set it manually
as done with setting initial weights during initialization manager tries to add the correct weights with the initial time (both lastInetpolationTimePossible and lastUpdateIntervalTime is the same until the update)
however issue arises as
in the scenario, it's assumed that all values inputted by the manager are correct and not malicious and the error arises because of the wrong implementation (missing guards).
int256 internal _normalizedFirstFourWeights ;
int256 internal _normalizedSecondFourWeights ;
function testUnderflow() public{
int256[] memory first4weights = new int256[]();
first4weights[0] = 0.2e18;
first4weights[1] = 0.1e18;
first4weights[2] = 0.1e18;
first4weights[3] = 0.1e18;
first4weights[4] = 0.1e18;
first4weights[5] = 0.2e18;
first4weights[6] = 0.1e18;
first4weights[7] = 0.1e18;
_normalizedFirstFourWeights = quantAMMPack32Array(first4weights)[0];
int256[] memory second4weights = new int256[]();
second4weights[0] = 0.1e18;
second4weights[1] = 0.2e18;
second4weights[2] = 0.1e18;
second4weights[3] = 0.1e18;
second4weights[4] = 0.1e18;
second4weights[5] = 0.2e18;
second4weights[6] = 0.1e18;
second4weights[7] = 0.1e18;
_normalizedSecondFourWeights = quantAMMPack32Array(second4weights)[0];
uint256[] memory weights = new uint256[]();
weights[0] = 0.2e18;
weights[1] = 0.1e18;
weights[2] = 0.1e18;
weights[3] = 0.1e18;
weights[4] = 0.1e18;
weights[5] = 0.2e18;
weights[6] = 0.1e18;
weights[7] = 0.1e18;
assertNotEq(_getNormalizedWeights(),weights);
}
uint40 mockedtimestamp = 1700000010;
function _getNormalizedWeights() public view virtual returns (uint256[] memory) {
uint256 totalTokens = 4;
uint256[] memory normalizedWeights = new uint256[]();
uint40 multiplierTime = mockedtimestamp;
uint40 lastInterpolationTime = (mockedtimestamp - 5) ;
if (multiplierTime >= lastInterpolationTime) {
multiplierTime = lastInterpolationTime;
}
unchecked {
uint256 timeSinceLastUpdate = uint256(
multiplierTime - mockedtimestamp
);
int256[] memory firstFourWeights = quantAMMUnpack32(_normalizedFirstFourWeights);
uint256 tokenIndex = totalTokens;
if (totalTokens > 4) {
tokenIndex = 4;
}
normalizedWeights[0] = calculateBlockNormalisedWeight(
firstFourWeights[0],
firstFourWeights[tokenIndex],
timeSinceLastUpdate
);
normalizedWeights[1] = calculateBlockNormalisedWeight(
firstFourWeights[1],
firstFourWeights[tokenIndex + 1],
timeSinceLastUpdate
);
if (totalTokens > 2) {
normalizedWeights[2] = calculateBlockNormalisedWeight(
firstFourWeights[2],
firstFourWeights[tokenIndex + 2],
timeSinceLastUpdate
);
} else {
return normalizedWeights;
}
if (totalTokens > 3) {
normalizedWeights[3] = calculateBlockNormalisedWeight(
firstFourWeights[3],
firstFourWeights[tokenIndex + 3],
timeSinceLastUpdate
);
} else {
return normalizedWeights;
}
if (totalTokens == 4) {
return normalizedWeights;
}
int256[] memory secondFourWeights = quantAMMUnpack32(_normalizedSecondFourWeights);
uint256 moreThan4Tokens = totalTokens - 4;
if (totalTokens > 4) {
normalizedWeights[4] = calculateBlockNormalisedWeight(
secondFourWeights[0],
secondFourWeights[moreThan4Tokens],
timeSinceLastUpdate
);
} else {
return normalizedWeights;
}
if (totalTokens > 5) {
normalizedWeights[5] = calculateBlockNormalisedWeight(
secondFourWeights[1],
secondFourWeights[moreThan4Tokens + 1],
timeSinceLastUpdate
);
} else {
return normalizedWeights;
}
if (totalTokens > 6) {
normalizedWeights[6] = calculateBlockNormalisedWeight(
secondFourWeights[2],
secondFourWeights[moreThan4Tokens + 2],
timeSinceLastUpdate
);
} else {
return normalizedWeights;
}
if (totalTokens > 7) {
normalizedWeights[7] = calculateBlockNormalisedWeight(
secondFourWeights[3],
secondFourWeights[moreThan4Tokens + 3],
timeSinceLastUpdate
);
} else {
return normalizedWeights;
}
return normalizedWeights;
}
}
uint256 constant ONE = 1e18;
function mulUp(uint256 a, uint256 b) public pure returns (uint256 result) {
uint256 product = a * b;
assembly ("memory-safe") {
result := mul(iszero(iszero(product)), add(div(sub(product, 1), ONE), 1))
}
}
function mulDown(uint256 a, uint256 b) public view returns (uint256) {
uint256 product = a * b;
return product / ONE;
}
function calculateBlockNormalisedWeight(
int256 weight,
int256 multiplier,
uint256 timeSinceLastUpdate
) public view returns (uint256) {
int256 multiplierScaled18 = multiplier * 1e18;
if (multiplier > 0) {
return uint256(weight) + mulDown(uint256(multiplierScaled18), timeSinceLastUpdate);
} else {
return uint256(weight) - mulUp(uint256(-multiplierScaled18), timeSinceLastUpdate);
}
}
function quantAMMPack32Array(int256[] memory _sourceArray) internal pure returns (int256[] memory targetArray) {
uint targetArrayLength;
uint storageIndex;
uint nonStickySourceLength;
if (_sourceArray.length >= 8) {
for (uint i = _sourceArray.length; i >= 8; ) {
unchecked {
if (i % 8 == 0) {
nonStickySourceLength = i;
break;
}
--i;
}
}
if (_sourceArray.length != nonStickySourceLength) {
unchecked {
targetArrayLength = (nonStickySourceLength / 8) + 1;
}
} else {
unchecked {
targetArrayLength = (nonStickySourceLength) / 8;
}
}
targetArray = new int256[](targetArrayLength);
for (uint i; i < nonStickySourceLength; ) {
unchecked {
targetArray[storageIndex] = quantAMMPackEight32(
int256(_sourceArray[i] / 1e9),
int256(_sourceArray[i + 1] / 1e9),
int256(_sourceArray[i + 2] / 1e9),
int256(_sourceArray[i + 3] / 1e9),
int256(_sourceArray[i + 4] / 1e9),
int256(_sourceArray[i + 5] / 1e9),
int256(_sourceArray[i + 6] / 1e9),
int256(_sourceArray[i + 7] / 1e9)
);
i += 8;
++storageIndex;
}
}
}
if (targetArrayLength == 0) {
unchecked {
if (_sourceArray.length <= 8) {
targetArrayLength = 1;
} else {
targetArrayLength = (nonStickySourceLength / 8) + 1;
}
targetArray = new int256[](targetArrayLength);
}
}
uint stickyEndElems = _sourceArray.length - nonStickySourceLength;
if (stickyEndElems > 0) {
uint offset = 224;
int256 packed;
for (uint i = nonStickySourceLength; i < _sourceArray.length; ) {
unchecked {
int256 elem = _sourceArray[i] / 1e9;
require(elem <= MAX32 && elem >= MIN32, "Overflow");
packed |= int256(uint256(elem << 224) >> 224) << offset;
offset -= 32;
++i;
}
}
targetArray[storageIndex] = packed;
}
}
int256 private constant MAX32 = int256(type(int32).max);
int256 private constant MIN32 = int256(type(int32).min);
function quantAMMPackEight32(
int256 _firstInt,
int256 _secondInt,
int256 _thirdInt,
int256 _fourthInt,
int256 _fifthInt,
int256 _sixthInt,
int256 _seventhInt,
int256 _eighthInt
) internal pure returns (int256 packed) {
require(
_firstInt <= MAX32 &&
_firstInt >= MIN32 &&
_secondInt <= MAX32 &&
_secondInt >= MIN32 &&
_thirdInt <= MAX32 &&
_thirdInt >= MIN32 &&
_fourthInt <= MAX32 &&
_fourthInt >= MIN32 &&
_fifthInt <= MAX32 &&
_fifthInt >= MIN32 &&
_sixthInt <= MAX32 &&
_sixthInt >= MIN32 &&
_seventhInt <= MAX32 &&
_seventhInt >= MIN32 &&
_eighthInt <= MAX32 &&
_eighthInt >= MIN32,
"Overflow"
);
int256 firstPacked = int256(uint256(_firstInt << 224) >> 224) << 224;
int256 secondPacked = int256(uint256(_secondInt << 224) >> 224) << 192;
int256 thirdPacked = int256(uint256(_thirdInt << 224) >> 224) << 160;
int256 fourthPacked = int256(uint256(_fourthInt << 224) >> 224) << 128;
int256 fifthPacked = int256(uint256(_fifthInt << 224) >> 224) << 96;
int256 sixthPacked = int256(uint256(_sixthInt << 224) >> 224) << 64;
int256 seventhPacked = int256(uint256(_seventhInt << 224) >> 224) << 32;
int256 eighthPacked = int256(uint256(_eighthInt << 224) >> 224);
packed =
firstPacked |
secondPacked |
thirdPacked |
fourthPacked |
fifthPacked |
sixthPacked |
seventhPacked |
eighthPacked;
}
function quantAMMUnpack32(int256 sourceElem) internal pure returns (int256[] memory targetArray) {
targetArray = new int256[](8);
targetArray[0] = (sourceElem >> 224) * 1e9;
targetArray[1] = int256(int32(sourceElem >> 192)) * 1e9;
targetArray[2] = int256(int32(sourceElem >> 160)) * 1e9;
targetArray[3] = int256(int32(sourceElem >> 128)) * 1e9;
targetArray[4] = int256(int32(sourceElem >> 96)) * 1e9;
targetArray[5] = int256(int32(sourceElem >> 64)) * 1e9;
targetArray[6] = int256(int32(sourceElem >> 32)) * 1e9;
targetArray[7] = int256(int32(sourceElem)) * 1e9;
return targetArray;
}
function quantAMMUnpack32Array(
int256[] memory _sourceArray,
uint _targetArrayLength
) internal pure returns (int256[] memory targetArray) {
require(_sourceArray.length * 8 >= _targetArrayLength, "SRC!=TGT");
targetArray = new int256[](_targetArrayLength);
uint targetIndex;
uint sourceArrayLengthMinusOne = _sourceArray.length - 1;
bool divisibleByEight = _targetArrayLength % 8 == 0;
uint stickyEndSourceElem;
if (_targetArrayLength > 8) {
for (uint i; i < _sourceArray.length; ) {
if (divisibleByEight || i < sourceArrayLengthMinusOne) {
unchecked {
int256 sourceElem = _sourceArray[i];
targetArray[targetIndex] = (sourceElem >> 224) * 1e9;
++targetIndex;
targetArray[targetIndex] = int256(int32(sourceElem >> 192)) * 1e9;
++targetIndex;
targetArray[targetIndex] = int256(int32(sourceElem >> 160)) * 1e9;
++targetIndex;
targetArray[targetIndex] = int256(int32(sourceElem >> 128)) * 1e9;
++targetIndex;
targetArray[targetIndex] = int256(int32(sourceElem >> 96)) * 1e9;
++targetIndex;
targetArray[targetIndex] = int256(int32(sourceElem >> 64)) * 1e9;
++targetIndex;
targetArray[targetIndex] = int256(int32(sourceElem >> 32)) * 1e9;
++targetIndex;
targetArray[targetIndex] = int256(int32(sourceElem)) * 1e9;
++targetIndex;
}
}
unchecked {
++i;
}
}
if (!divisibleByEight) {
unchecked {
stickyEndSourceElem = _sourceArray.length - 1;
}
}
} else if (_targetArrayLength == 8) {
int256 sourceElem = _sourceArray[0];
targetArray[0] = (sourceElem >> 224) * 1e9;
targetArray[1] = int256(int32(sourceElem >> 192)) * 1e9;
targetArray[2] = int256(int32(sourceElem >> 160)) * 1e9;
targetArray[3] = int256(int32(sourceElem >> 128)) * 1e9;
targetArray[4] = int256(int32(sourceElem >> 96)) * 1e9;
targetArray[5] = int256(int32(sourceElem >> 64)) * 1e9;
targetArray[6] = int256(int32(sourceElem >> 32)) * 1e9;
targetArray[7] = int256(int32(sourceElem)) * 1e9;
}
if (!divisibleByEight) {
unchecked {
uint offset = 224;
for (uint i = targetIndex; i < targetArray.length; ) {
targetArray[i] = int256(int32(_sourceArray[stickyEndSourceElem] >> offset)) * 1e9;
offset -= 32;
++i;
}
}
}
you'll notice that weights are broken giving wrong values far away from real ones breaking the core logic of the protocol.