Super pool uses ERC20.approve instead of safe approvals, causing it to always revert on some ERC20s
Summary
Super pools that get created on a specific asset then leverage its positions and deposit them in the "main" pools. Super pools get created in SuperPoolFactory::deploySuperPool, where some initial amount is sent from the user, and then deposited in the deployed super pool. When the assets are sent from the user, the factory approves the deployed pool, to allow outbound transfers, this is done using https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/main/protocol-v2/src/SuperPoolFactory.sol#L73:
And the "default" ERC20 behavior expects the approve function to return a boolean, however, some ERC20s on some chains don't return a value.
The most popular example is USDT on the main net, and as the docs mention it should be compatible on any EVM chain and will support USDT:
Q: On what chains are the smart contracts going to be deployed?
Any EVM-compatbile network
Q: If you are integrating tokens, are you allowing only whitelisted tokens to work with the codebase or any complying with the standard? Are they assumed to have certain properties, e.g. be non-reentrant? Are there any types of weird tokens you want to integrate?
Tokens are whitelisted, only tokens with valid oracles can be used to create Base Pools.
Protocol governance will ensure that oracles are only set for standard ERC-20 tokens (plus USDC/USDT)
Another occurrence of this is SuperPool::reallocate, here.
This causes Super pool to never work on these chains/tokens.
Root Cause
Some known tokens don't return a value on approvals, more info here, an example of this is USDT, which is mentioned that the protocol will use it.
0xAlix2
Medium
Super pool uses
ERC20.approve
instead of safe approvals, causing it to always revert on some ERC20sSummary
Super pools that get created on a specific asset then leverage its positions and deposit them in the "main" pools. Super pools get created in
SuperPoolFactory::deploySuperPool
, where some initial amount is sent from the user, and then deposited in the deployed super pool. When the assets are sent from the user, the factory approves the deployed pool, to allow outbound transfers, this is done using https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/main/protocol-v2/src/SuperPoolFactory.sol#L73:And the "default" ERC20 behavior expects the
approve
function to return a boolean, however, some ERC20s on some chains don't return a value. The most popular example is USDT on the main net, and as the docs mention it should be compatible on any EVM chain and will support USDT:Another occurrence of this is
SuperPool::reallocate
, here.This causes Super pool to never work on these chains/tokens.
Root Cause
Some known tokens don't return a value on approvals, more info here, an example of this is USDT, which is mentioned that the protocol will use it.
Standard ERC20s return a boolean on approval, https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L67.
USDT on the main net doesn't return a value, https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7#code.
Impact
Super pools can never be created and used for assets that don't return a value on approval, an example of this is USDT on Ethereum main net.
PoC
Minimal mock USDT token:
Add the following test in
protocol-v2/test/core/Superpool.t.sol
.Mitigation
Use
safeApprove
instead ofapprove
inSuperPoolFactory::deploySuperPool
andSuperPool::reallocate
.