sherlock-2025-04-burve-h08
[H-08] ValueFacet.removeValueSingle will withdraw less than required from the vertex vault due to unaccounted tax
Summary
유저가 출금을 할 때 수수료를 지불해야 한다. 수수료를 제거한 금액을 계산한 뒤 그 금액에서 중복으로 수수료를 제거하였다. 따라서 유저는 수수료를 2배 지불하고 실제로 받아야 하는 것보다 적은 토큰을 받는다.
Keyword
fee, arithmetic error, logic flaw
Vulnerability
유저는 엔트리포인트인 ValueFacet.removeValueSingle 함수를 호출해 출금을 요청한다. 이 함수에서는 Closure.removeValueSingle 를 호출하여 출금될 토큰 양과 수수료의 양을 계산한다. 여기서 리턴값 removedNominal 는 수수료를 제외한 출금액을 의미한다.
그런데 이후 realTax 만큼을 중복 제외한다. 즉, 수수료를 2배 지불하는 것과 마찬가지이다.
function removeValueSingle(
address recipient,
uint16 _closureId,
uint128 value,
uint128 bgtValue,
address token,
uint128 minReceive
) external nonReentrant returns (uint256 removedBalance) {
...
@> (uint256 removedNominal, uint256 nominalTax) = c.removeValueSingle(
value,
bgtValue,
vid
);
uint256 realRemoved = AdjustorLib.toReal(token, removedNominal, false);
Store.vertex(vid).withdraw(cid, realRemoved, false);
@> uint256 realTax = FullMath.mulDiv(
removedBalance,
nominalTax,
removedNominal
);
c.addEarnings(vid, realTax);
@> removedBalance = realRemoved - realTax; // How much the user actually gets.
require(removedBalance >= minReceive, PastSlippageBounds());
@> TransferHelper.safeTransfer(token, recipient, removedBalance);
}다음은 Closure.removeValueSingle 함수이다. 리턴값 removedAmount는 수수료를 제외한 금액임을 확인할 수 있다.
function removeValueSingle(
Closure storage self,
uint256 value,
uint256 bgtValue,
VertexId vid
) internal returns (uint256 removedAmount, uint256 tax) {
...
// We first calculate what value is effectively "added" by not removing the tokens.
// And then we make sure to remove that amount of value with the out token.
uint256 fairVBalance = iterSingleValueDiff(self, valIter, false);
@> removedAmount = self.balances[valIter.vIdx] - fairVBalance;
// Now we have the addedValue which we can remove, and the fair balance for our vertex.
...
tax = FullMath.mulX128(untaxedRemove, self.baseFeeX128, true);
@> removedAmount += untaxedRemove - tax;
// This needs to happen last.
self.valueStaked -= value;
self.bgtValueStaked -= bgtValue;
}Impact
유저가 실제로 받아야 하는 것보다 적은 금액을 출금받는다.
Mitigation
수수료를 중복으로 떼지 않도록 어느 한쪽을 삭제한다.
tags: bughunting, burve, smart contract, solidity, severity high, fee, arithmetic error, logic flaw