Omniscia AmpleSense Audit
BalancerTrader Manual Review Findings
BalancerTrader Manual Review Findings
BTR-01M: Inexistent Approval of Vault
Type | Severity | Location |
---|---|---|
Logical Fault | Major | BalancerTrader.sol:L64-L76 |
Description:
The vault
of Balancer requires an approval to perform a trade if it is not utilizing internal balances as indicated by the false
flags in the IVault.FundManagement
struct. As a result, the operation will fail to execute at all times.
Example:
58/**59* @dev Caller must transfer the right amount of tokens to the trader (USDC will be replaced with ETH)60 */61function sellAMPLForEEFI(uint256 amount) external override returns (uint256 eefiAmount) {62 amplToken.transferFrom(msg.sender, address(this), amount);63 (uint256 usdcAmount,) = amplUsdc.swapExactAmountIn(address(amplToken), amount, address(usdcToken), 0, MAX_INT);64 eefiAmount = vault.swap(IVault.SingleSwap(65 eefiUsdcPoolID,66 IVault.SwapKind.GIVEN_IN,67 IAsset(address(usdcToken)),68 IAsset(address(eefiToken)),69 usdcAmount,70 "0x"71 ), IVault.FundManagement(72 address(this),73 false,74 msg.sender,75 false),76 0, block.timestamp);77 emit Sale_EEFI(amount, eefiAmount);78}
Recommendation:
We strongly recommend approval to be set prior to each swap
execution to ensure that it operates as expected and securely.
Alleviation:
The code was updated to properly approve
the vault prior to each swap execution.
BTR-02M: Deprecated Native Asset Transfer
Type | Severity | Location |
---|---|---|
Language Specific | Medium | BalancerTrader.sol:L54 |
Description:
The transfer
member accessible in payable
variables forwards a fixed gas stipend for the operation which can change on upcoming EIPs as evidenced by f.e. EIP-2929.
Example:
47/**48* @dev Caller must transfer the right amount of tokens to the trader49 */50function sellAMPLForEth(uint256 amount) external override returns (uint256 ethAmount) {51 amplToken.transferFrom(msg.sender, address(this), amount);52 (ethAmount,) = amplEth.swapExactAmountIn(address(amplToken), amount, address(wethToken), 0, MAX_INT);53 wethToken.withdraw(ethAmount);54 msg.sender.transfer(ethAmount);55 emit Sale_ETH(amount, ethAmount);56}
Recommendation:
We strongly recommend a more robust payout method to be utilized such as the sendValue
function of OpenZeppelin's Address
library that forwards the currently available gas and is guaranteed to always be executable.
Alleviation:
The sendValue
method of the OpenZeppelin Address
library is now properly utilized.
BTR-03M: Inexistent Slippage Arguments
Type | Severity | Location |
---|---|---|
Logical Fault | Medium | BalancerTrader.sol:L52, L63, L76 |
Description:
All trades performed by the contract do not enforce any slippage checks, rendering them completely prone to sandwhich attacks as well as potential flash-loan arbitrage opportunities.
Example:
47/**48* @dev Caller must transfer the right amount of tokens to the trader49 */50function sellAMPLForEth(uint256 amount) external override returns (uint256 ethAmount) {51 amplToken.transferFrom(msg.sender, address(this), amount);52 (ethAmount,) = amplEth.swapExactAmountIn(address(amplToken), amount, address(wethToken), 0, MAX_INT);53 wethToken.withdraw(ethAmount);54 msg.sender.transfer(ethAmount);55 emit Sale_ETH(amount, ethAmount);56}57
58/**59* @dev Caller must transfer the right amount of tokens to the trader (USDC will be replaced with ETH)60 */61function sellAMPLForEEFI(uint256 amount) external override returns (uint256 eefiAmount) {62 amplToken.transferFrom(msg.sender, address(this), amount);63 (uint256 usdcAmount,) = amplUsdc.swapExactAmountIn(address(amplToken), amount, address(usdcToken), 0, MAX_INT);64 eefiAmount = vault.swap(IVault.SingleSwap(65 eefiUsdcPoolID,66 IVault.SwapKind.GIVEN_IN,67 IAsset(address(usdcToken)),68 IAsset(address(eefiToken)),69 usdcAmount,70 "0x"71 ), IVault.FundManagement(72 address(this),73 false,74 msg.sender,75 false),76 0, block.timestamp);77 emit Sale_EEFI(amount, eefiAmount);78}
Recommendation:
We advise this trait to be carefully assessed and potentially mitigated by introducing new arguments to the functions as in their current state they would be exploitable by arbitreurs.
Alleviation:
Both linked functions had a new argument introuduced that is properly utilized to ensure expected behaviour and a minimum return of the swaps performed.
BTR-04M: Potential for Lock of Ether
Type | Severity | Location |
---|---|---|
Logical Fault | Minor | BalancerTrader.sol:L43-L45 |
Description:
The receive
fallback function does not validate its caller, meaning anyone can directly send ether to the contract and have it permanently locked.
Example:
43receive() external payable {44
45}46
47/**48* @dev Caller must transfer the right amount of tokens to the trader49 */50function sellAMPLForEth(uint256 amount) external override returns (uint256 ethAmount) {51 amplToken.transferFrom(msg.sender, address(this), amount);52 (ethAmount,) = amplEth.swapExactAmountIn(address(amplToken), amount, address(wethToken), 0, MAX_INT);53 wethToken.withdraw(ethAmount);54 msg.sender.transfer(ethAmount);55 emit Sale_ETH(amount, ethAmount);56}
Recommendation:
We strongly recommend a require
check to be introduced here that ensures msg.sender
is equivalent to the wethToken
to ensure that only unwrapped WETH is ever sent to the contract in the sellAMPLForEth
call path.
Alleviation:
A require
check was introduced ensuring that only the wethToken
sends native assets to the contract alleviating this exhibit.