ctf_sec
high
User can inject the tokenX direclty to the BufferBinaryPool to inflate the totalTokenXBalance to manipulate the share value
User can inject the tokenX direclty to the BufferBinaryPool to inflate the totalTokenXBalance to manipulate the share value
User can deposit tokenX into BufferBinaryPool and get liqudity token.
The amount of the liquidity token is calculated below
function _provide(
uint256 tokenXAmount,
uint256 minMint,
address account
) internal returns (uint256 mint) {
uint256 supply = totalSupply();
uint256 balance = totalTokenXBalance();
require(
balance + tokenXAmount <= maxLiquidity,
"Pool has already reached it's max limit"
);
if (supply > 0 && balance > 0)
mint = (tokenXAmount * supply) / (balance);
else mint = tokenXAmount * INITIAL_RATE;
the balance is calculated below:
/**
* @notice Returns the total balance of X provided to the pool
*/
function totalTokenXBalance()
public
view
override
returns (uint256 balance)
{
return tokenX.balanceOf(address(this)) - lockedPremium;
}
if we look back to the calculation for minting amount:
mint = (tokenXAmount * supply) / (balance);
the larger than the balance, the smaller the share minted.
Because of the use tokenX.balanceOf(this), user can inject the tokenX directly to the BufferBinaryPool to inflate the totalTokenXBalance(), which decrease the minted amount for new user that provides the liqudity.
On the other hand, when user burn the liqudity token in exchange for the tokenX, we are calling the withdraw.
function _withdraw(uint256 tokenXAmount, address account)
internal
returns (uint256 burn)
{
require(
tokenXAmount <= availableBalance(),
"Pool: Not enough funds on the pool contract. Please lower the amount."
);
uint256 totalSupply = totalSupply();
uint256 balance = totalTokenXBalance();
uint256 maxUserTokenXWithdrawal = (balanceOf(account) * balance) /
totalSupply;
uint256 tokenXAmountToWithdraw = maxUserTokenXWithdrawal < tokenXAmount
? maxUserTokenXWithdrawal
: tokenXAmount;
burn = divCeil((tokenXAmountToWithdraw * totalSupply), balance);
note the calculation:
uint256 maxUserTokenXWithdrawal = (balanceOf(account) * balance) / totalSupply;
inflating the totalTokenXBalance() increase the max tokenX user can withdraw.
By sending the tokenX directly to the pool, hacker decreases further user's liquidity token minted amount, yet increase the share of the current liquidity token.
In fact, because the tokenX is either USDC or the Buffer token, if the tokenX is USDC, user can take USDC flash loan to manipulate the price per share.
Manual Review
We recommend the project not use the balanceOf check and take snapshot of the balance.