sherlock-2025-04-burve-m05

[M-05] Invariant Breaking

보고서

Summary

addValue 함수에서 Closure.balancesClosure.targetX128deMinimus 조건 확인 없이 비례적으로 증가시킨다. 이때 실제로 예치된 값과 목표 값 사이의 불균형 금액도 함께 증가한다. deMinimus 조건을 확인하지 않으므로 목표값과 실제값 사이의 차이가 범위 내인지 확인하지 않는다. 따라서 시스템의 중요한 조건(실제 값이 목표 값의 범위 내일 것)을 위반하게 만들 수 있다.

Keyword

arithmetic error, business logic vul, dex

Vulnerability

유저는 ValueFacet.addValue를 호출하여 클로저에 속한 모든 종류의 토큰을 제공하며 유동성을 제공할 수 있다. 현재 예치된 토큰의 비율과 동일한 비율로 토큰을 제공하므로 토큰의 비율은 유지된다.

이 함수는 내부적으로 Closure.addValue를 호출하여 Closure.balancesClosure.targetX128를 비례적으로 스케일링한다.

function addValue(  
    Closure storage self,
    uint256 value,
    uint256 bgtValue
) internal returns (uint256[MAX_TOKENS] memory requiredBalances) {
    trimAllBalances(self); 
@>  uint256 scaleX128 = FullMath.mulDivX256(
        value,
        self.n * self.targetX128, 
        true
    );
@>  uint256 valueX128 = value << 128;
@>  self.targetX128 +=
        valueX128 / self.n + ((valueX128 % self.n) > 0 ? 1 : 0);  
    self.valueStaked += value;
    self.bgtValueStaked += bgtValue;  
 
    for (uint8 i = 0; i < MAX_TOKENS; ++i) {
        if (!self.cid.contains(i)) continue;
@>      requiredBalances[i] = FullMath.mulX128(
            scaleX128,
            self.balances[i],
            true
        );
@>      self.setBalance(i, self.balances[i] + requiredBalances[i]); // Closure.balances 업데이트
    }
}

클로저가 이미 조금 불균형 상태일 때(목표 잔고와 불일치) 스케일링 하면 기존의 불일치를 더욱 증폭시킬 수 있다. 이는 클로저의 총 가치가 시스템이 정의한 범위를 넘게 하고, 해당 클로저를 더 이상 사용할 수 없게 할 수 있다.

먼저 기존의 토큰 가치(Value) 은 다음과 같다. e는 효율성 계수, x는 현재 토큰 잔고, t는 목표로 하는 잔고이다. 가치 계산 문서 참고

수학적으로 t와 x를 k배만큼 스케일링했을 때의 토큰 가치 는 다음과 같다. 토큰 가치가 k배로 선형적으로 스케일링되긴 했지만, 이는 기존의 불일치도 이에 비례하여 증폭되었음을 의미한다.

README 에 명시된 바에 따르면 시스템은 “클로저의 가치는 t * 클로저에 등록된 토큰 수 로부터 deMinimus * (클로저에 등록된 토큰 수) 보다 많이 벗어날 수 없다.” 라는 조건을 만족해야 한다(코드를 보면 README의 설명은 좀 불완전한 것 같다). 즉, |클로저 총가치 - 클로저 총 목표가치| <= deMinimus * n 라는 의미이다.

또한, multi/Value.sol#L169-L170를 참고하면 클로저 총 가치의 상한은 t * 클로저에 등록된 토큰 수 + targetSlippageX128 임을 알 수 있다.

즉, 클로저의 총 가치는 다음 공식을 만족해야 한다.

예를 들어 클로저에 A, B, C 토큰이 등록되어 있다고 하자.

초기: A 토큰과 B토큰은 목표 잔고만큼 있다. C 토큰은 목표 잔고 + targetSlippageX128 만큼이 있다. 즉, C 토큰에 약간의 불균형이 존재한다.

v(A) = v(B) = target
v(C) = target + targetSlippageX128

위 상태에서는 시스템 조건을 만족한다.

이후 유저가 addValue를 호출하여 모든 토큰의 양을 1.5배 하였다. 클로저에 예치된 각 토큰의 전체 가치는 1.5배 된다.

v(A) = v(B) = 1.5 * target
v(c) = 1.5 * (target + targetSlippageX128)

이로 인해 클로저의 전체 가치는 다음과 같이 변한다.

시스템 조건을 만족하려면 클로저의 전체 가치는 다음 값 사이에 있어야 한다. 하지만 위 값은 이 조건을 만족하지 못한다. 불일치가 있는 상태에서 동일 비율로 입금하기에, 기존의 targetSlippageX128 만큼의 불일치를 포함한 비율로 입금하고, 이로 인해 (비율은 유지하지만) 불일치하는 양은 증가하기 때문이다.

따라서 addValue 사이의 차이를 증폭시켜 시스템 조건을 깨트릴 수 있다.

Impact

기존의 불일치를 조정하지 않고 addValue를 호출하면 중요한 시스템 조건이 깨질 수 있으며, 이로 인해 해당 클로저에서의 유동성 관련 작업이 작동하지 않을 수 있다.

Mitigation

addValue를 호출할 때, 즉 한 종류의 토큰으로 유동성을 제공하는 게 아니라 전 종류 토큰을 비율에 맞춰 입금할 때에도 t 함수를 사용하여 목표 값을 재계산해야 한다.


tags: bughunting, burve, smart contract, solidity, severity medium, arithmetic error, business-logic-vul, dex