Summary
The IAlchemist is designed to be compatible with the Alchemix protocol and it looks like this:
interface IAlchemist {
struct YieldTokenConfig {
address adapter;
uint256 maximumLoss;
uint256 maximumExpectedValue;
uint256 creditUnlockBlocks;
}
function admin() external view returns (address);
function depositUnderlying(address yieldToken, uint256 amount, address rec, uint256 minOut) external;
function mint(uint256 _amount, address _recipient) external;
function repay(address _underlying, uint256 _amount, address _recipient) external;
function whitelist() external view returns (address);
function burn(uint256 _amount, address _recipient) external;
function addYieldToken(address yieldToken, YieldTokenConfig calldata config) external;
function harvest(address yieldToken, uint256 amount) external;
function setYieldTokenEnabled(address yieldToken, bool flag) external ;
}
Vulnerability Details
If we check the following functions depositUnderlying
, repay
, burn
on Alchemix they look like this:
depositUnderlying
function depositUnderlying(
address yieldToken,
uint256 amount,
address recipient,
uint256 minimumAmountOut
) external returns (uint256 sharesIssued);
repay
function repay(
address underlyingToken,
uint256 amount,
address recipient
) external returns (uint256 amountRepaid);
burn
function burn(uint256 amount, address recipient) external returns (uint256 amountBurned);
The difference between them and implementation in IAlchemist
contract is that they have return values that the IAlchemist
does not include, so this can make it not fully compatible with Alchemix protocol, also without including them protocol would not correctly handle the return values.
Impact
Contract is not compatible with Alchemix protocol.
Tools Used
Manual Review
Recommendations
Make the following changes in IAlchemist
interface:
interface IAlchemist {
struct YieldTokenConfig {
// The adapter used by the system to interop with the token.
address adapter;
// The maximum percent loss in expected value that can occur before certain actions are disabled measured in
// units of basis points.
uint256 maximumLoss;
// The maximum value that can be held by the system before certain actions are disabled measured in the
// underlying token.
uint256 maximumExpectedValue;
// The number of blocks that credit will be distributed over to depositors.
uint256 creditUnlockBlocks;
}
function admin() external view returns (address);
+ function depositUnderlying(address yieldToken, uint256 amount, address rec, uint256 minOut) external returns (uint256);
function mint(uint256 _amount, address _recipient) external;
+ function repay(address _underlying, uint256 _amount, address _recipient) external returns (uint256);
function whitelist() external view returns (address);
+ function burn(uint256 _amount, address _recipient) external returns (uint256);
function addYieldToken(address yieldToken, YieldTokenConfig calldata config) external;
function harvest(address yieldToken, uint256 amount) external;
function setYieldTokenEnabled(address yieldToken, bool flag) external;
}