sherlock-2025-04-burve-h01

[H-01] Incorrect handling of ERC4626 vaults with fees

보고서

Summary

사용자가 멀티토큰 풀에 유동성을 추가하면, 사용자가 전달한 토큰은 ERC4626 볼트(서드파티 서비스)에 예치된다. ERC4626 볼트는 구현에 따라 입출금 수수료가 부과될 수 있다. 그런데 Burve에서는 이를 제대로 핸들링하지 못했다. 사용자는 이러한 수수료를 피할 수 있었다.

Keyword

erc4626, fee, logic flaw

Vulnerability

addValue 함수는 이 클로저에 속한 모든 토큰을 각자의 비율에 맞춰 입금하는 함수이다. 유저는 이 함수를 통해 멀티토큰 풀에 예치할 수 있다. 각자의 비율에 맞는 필요한 토큰의 양을 계산하여 realNeeded 변수에 저장한다. 이 만큼의 토큰을 유저에게 받아 서드파티 ERC4626에 예치한다. Store.vertex(VertexLib.newId(i)).deposit(cid, realNeeded); 에서 각 클로저 별 입금액을 기록할 때에도 유저가 입금한 금액을 그대로 이용한다.

function addValue(
    address recipient,
    uint16 _closureId,
    uint128 value,
    uint128 bgtValue
)
    external
    nonReentrant
    returns (uint256[MAX_TOKENS] memory requiredBalances)
{
    if (value == 0) revert DeMinimisDeposit();
    require(bgtValue <= value, InsufficientValueForBgt(value, bgtValue));
    ClosureId cid = ClosureId.wrap(_closureId);
    Closure storage c = Store.closure(cid);
    uint256[MAX_TOKENS] memory requiredNominal = c.addValue(
        value,
        bgtValue
    );
    // Fetch balances
    TokenRegistry storage tokenReg = Store.tokenRegistry();
    for (uint8 i = 0; i < MAX_TOKENS; ++i) {
        if (!cid.contains(i)) continue; // Irrelevant token.
        address token = tokenReg.tokens[i];
@>      uint256 realNeeded = AdjustorLib.toReal(
            token,
            requiredNominal[i],
            true
        );
        requiredBalances[i] = realNeeded;
        TransferHelper.safeTransferFrom(
            token,
            msg.sender,
            address(this),
            realNeeded
        );
@>      Store.vertex(VertexLib.newId(i)).deposit(cid, realNeeded);
    }
    Store.assets().add(recipient, cid, value, bgtValue);
}

하지만 서드파티 ERC4626은 입출금 시 수수료를 받을 수 있다. vertex.deposit 함수에서는 ERC4626 입금 시 수수료로 사라진 금액을 제외하지 않고 이 클로저의 총 입금액에 포함한다.

  1. 유저가 멀티토큰 풀에서 100의 가치만큼을 예치한다. 100개의 자산이 예치되었다고 하자. 이는 서드파티 ERC4626으로 투자된다고 하자.
  2. 서드파티 ERC4626의 예치 수수료가 1%라 하자. 100개의 자산이 예치되었지만 99개의 자산 상당의 share가 발행된다.
  3. 유저가 멀티토큰 풀에서 100 가치만큼의 토큰을 출금하여 100개의 자산을 돌려받는다.
  4. 수수료로 사용된 1개의 자산이 손실된다.

Impact

유저가 ERC4626에서 부과하는 수수료를 피할 수 있다. 실제로 남아있는 토큰의 수가 추적되는 토큰의 수보다 적기 때문에 마지막 사용자는 자산을 출금할 때 손해를 보게 된다.

Mitigation

연동하는 ERC4626 볼트가 입출금 수수료를 받는다면 수수료를 충당하기 위해 사용자에게 더 많은 토큰을 받아야 한다.


tags: bughunting, burve, smart contract, solidity, erc4626, fee, logic flaw, severity high