function withdrawUnderlying(
address alchemist,
address yieldToken,
uint256 shares,
address recipient,
uint256 minimumAmountOut
) external {
_onlyWhitelisted();
// Ensure that the underlying of the target yield token is in fact WETH
IAlchemistV2.YieldTokenParams memory params = IAlchemistV2(alchemist).getYieldTokenParameters(yieldToken);
if (params.underlyingToken != address(WETH)) {
revert IllegalArgument();
}
IAlchemistV2(alchemist).withdrawUnderlyingFrom(msg.sender, yieldToken, shares, address(this), minimumAmountOut);
uint256 amount = WETH.balanceOf(address(this));
WETH.withdraw(amount);
(bool success, ) = recipient.call{value: amount}(new bytes(0));
if (!success) {
revert IllegalState();
}
}
If the gateway contract has an existing WETH balance, (for example, if a third party has accidentally or intentionally transferred WETH to the contract address), the caller may receive more ETH than expected.
Suggestion: use the return value of IAlchemistV2(alchemist).withdrawUnderlyingFrom.
Noncritical
Staking pools are incompatible with fee-on-transfer tokens
The internal accounting in StakingPools credits users with an amount equal to the _depositAmount argument:
However, if the pool token is a fee-on-transfer ERC20, the amount received from safeTransferFrom on line 382 may be less than _depositAmount. This could impact reward accounting and potentially cause attempted withdrawals to revert for unlucky users.
Since the supported tokens are permissioned, this is a noncritical issue, but be aware of this incompatibility.
Address inputs are validated in most locations, but are not validated in a few places. Consider whether a zero or invalid address would impact these contracts.
It's a best practice to lock version pragmas for concrete contracts. Many contracts are using a fixed 0.8.13 pragma, but the following contracts have a floating ^0.8.11 pragma:
I think the first point is useful to keep as is. Helps to ensure there is never locked ETH in the contract. If Ether is mistakenly sent to this contract, that is fair game.
Low
WETH Gateway transfers out full contract balance
WethGateway#withdrawUnderlying
unwraps and transfers the contract's full WETH balance torecipient
:WethGateway#withdrawUnderlying
If the gateway contract has an existing WETH balance, (for example, if a third party has accidentally or intentionally transferred WETH to the contract address), the caller may receive more ETH than expected.
Suggestion: use the return value of
IAlchemistV2(alchemist).withdrawUnderlyingFrom
.Noncritical
Staking pools are incompatible with fee-on-transfer tokens
The internal accounting in
StakingPools
credits users with an amount equal to the_depositAmount
argument:[
StakingPools#_deposit
[(https://github.com/code-423n4/2022-05-alchemix/blob/de65c34c7b6e4e94662bf508e214dcbf327984f4/contracts-full/StakingPools.sol#L375-L385)However, if the pool token is a fee-on-transfer ERC20, the amount received from
safeTransferFrom
on line 382 may be less than_depositAmount
. This could impact reward accounting and potentially cause attempted withdrawals to revert for unlucky users.Since the supported tokens are permissioned, this is a noncritical issue, but be aware of this incompatibility.
QA
Restrict
_isLocked
mutability toview
Mutex#_isLocked
can be declared aview
:Missing address input validations
Address inputs are validated in most locations, but are not validated in a few places. Consider whether a zero or invalid address would impact these contracts.
TransmuterBuffer#initialize
Unlocked version pragmas
It's a best practice to lock version pragmas for concrete contracts. Many contracts are using a fixed
0.8.13
pragma, but the following contracts have a floating^0.8.11
pragma:AlchemicTokenV1
AlchemicTokenV2
AlchemicTokenV2Base
AlchemistV2
AutoleverageBase
AutoleverageCurveFactoryethpool
AutoleverageCurveMetapool
CrossChainCanonicalAlchemicTokenV2
CrossChainCanonicalBase
CrossChainCanonicalGALCX
gALCX
StakingPools
TransmuterBuffer
TransmuterV2
WETHGateway
YearnTokenAdapter
Multicall
Unused imports
Math.sol
inTransmuterBuffer
LiquidityMath.sol
inTransmuterBuffer
Incorrect comments
TransmuterV2#notPaused
Typos
normalizedAmount
inTransmuterV2#exchange
for
in comment forTransmuterBuffer#lastFlowrateUpdate