// SPDX-License-Identifier: AGPL-3.0pragmasolidity ^0.8.13;import {ERC20} from"solmate/tokens/ERC20.sol";
import {ERC4626} from"solmate/mixins/ERC4626.sol";
import {SafeTransferLib} from"solmate/utils/SafeTransferLib.sol";
import {IPool} from"./external/IPool.sol";
import {IRewardsController} from"./external/IRewardsController.sol";
/// @title AaveV3ERC4626/// @author zefram.eth/// @notice ERC4626 wrapper for Aave V3/// @dev Important security note: due to Aave using a rebasing model for aTokens,/// this contract cannot independently keep track of the deposited funds, so it is possible/// for an attacker to directly transfer aTokens to this contract, increase the vault share/// price atomically, and then exploit an external lending market that uses this contract/// as collateral.contractAaveV3ERC4626isERC4626{
/// -----------------------------------------------------------------------/// Libraries usage/// -----------------------------------------------------------------------usingSafeTransferLibforERC20;
/// -----------------------------------------------------------------------/// Events/// -----------------------------------------------------------------------eventClaimRewards(uint256 amount);
/// -----------------------------------------------------------------------/// Constants/// -----------------------------------------------------------------------uint256internalconstant DECIMALS_MASK =0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFF;
uint256internalconstant ACTIVE_MASK =0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF;
uint256internalconstant FROZEN_MASK =0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF;
uint256internalconstant PAUSED_MASK =0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFF;
uint256internalconstant SUPPLY_CAP_MASK =0xFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
uint256internalconstant SUPPLY_CAP_START_BIT_POSITION =116;
uint256internalconstant RESERVE_DECIMALS_START_BIT_POSITION =48;
/// -----------------------------------------------------------------------/// Immutable params/// -----------------------------------------------------------------------/// @notice The Aave aToken contract
ERC20 publicimmutable aToken;
/// @notice The Aave Pool contract
IPool publicimmutable lendingPool;
/// @notice The address that will receive the liquidity mining rewards (if any)addresspublicimmutable rewardRecipient;
/// @notice The Aave RewardsController contract
IRewardsController publicimmutable rewardsController;
/// -----------------------------------------------------------------------/// Constructor/// -----------------------------------------------------------------------constructor(
ERC20 asset_,
ERC20 aToken_,
IPool lendingPool_,
address rewardRecipient_,
IRewardsController rewardsController_
) ERC4626(asset_, _vaultName(asset_), _vaultSymbol(asset_)) {
aToken = aToken_;
lendingPool = lendingPool_;
rewardRecipient = rewardRecipient_;
rewardsController = rewardsController_;
}
/// -----------------------------------------------------------------------/// Aave liquidity mining/// -----------------------------------------------------------------------/// @notice Claims liquidity mining rewards from Aave and sends it to rewardRecipientfunctionclaimRewards() external{
address[] memory assets =newaddress[](1);
assets[0] =address(aToken);
(, uint256[] memory claimedAmounts) = rewardsController.claimAllRewards(assets, rewardRecipient);
emit ClaimRewards(claimedAmounts[0]);
}
/// -----------------------------------------------------------------------/// ERC4626 overrides/// -----------------------------------------------------------------------functionwithdraw(uint256 assets, address receiver, address owner)
publicvirtualoverridereturns (uint256 shares)
{
shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) {
allowance[owner][msg.sender] = allowed - shares;
}
}
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
// withdraw assets directly from Aave
lendingPool.withdraw(address(asset), assets, receiver);
}
functionredeem(uint256 shares, address receiver, address owner) publicvirtualoverridereturns (uint256 assets) {
if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) {
allowance[owner][msg.sender] = allowed - shares;
}
}
// Check for rounding error since we round down in previewRedeem.require((assets = previewRedeem(shares)) !=0, "ZERO_ASSETS");
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
// withdraw assets directly from Aave
lendingPool.withdraw(address(asset), assets, receiver);
}
functiontotalAssets() publicviewvirtualoverridereturns (uint256) {
// aTokens use rebasing to accrue interest, so the total assets is just the aToken balancereturn aToken.balanceOf(address(this));
}
functionafterDeposit(uint256 assets, uint256/*shares*/) internalvirtualoverride{
/// -----------------------------------------------------------------------/// Deposit assets into Aave/// -----------------------------------------------------------------------// approve to lendingPool
asset.safeApprove(address(lendingPool), assets);
// deposit into lendingPool
lendingPool.supply(address(asset), assets, address(this), 0);
}
functionmaxDeposit(address) publicviewvirtualoverridereturns (uint256) {
// check if asset is pauseduint256 configData = lendingPool.getReserveData(address(asset)).configuration.data;
if (!(_getActive(configData) &&!_getFrozen(configData) &&!_getPaused(configData))) {
return0;
}
// handle supply capuint256 supplyCapInWholeTokens = _getSupplyCap(configData);
if (supplyCapInWholeTokens ==0) {
returntype(uint256).max;
}
uint8 tokenDecimals = _getDecimals(configData);
uint256 supplyCap = supplyCapInWholeTokens *10** tokenDecimals;
return supplyCap - aToken.totalSupply();
}
functionmaxMint(address) publicviewvirtualoverridereturns (uint256) {
// check if asset is pauseduint256 configData = lendingPool.getReserveData(address(asset)).configuration.data;
if (!(_getActive(configData) &&!_getFrozen(configData) &&!_getPaused(configData))) {
return0;
}
// handle supply capuint256 supplyCapInWholeTokens = _getSupplyCap(configData);
if (supplyCapInWholeTokens ==0) {
returntype(uint256).max;
}
uint8 tokenDecimals = _getDecimals(configData);
uint256 supplyCap = supplyCapInWholeTokens *10** tokenDecimals;
return convertToShares(supplyCap - aToken.totalSupply());
}
functionmaxWithdraw(address owner) publicviewvirtualoverridereturns (uint256) {
// check if asset is pauseduint256 configData = lendingPool.getReserveData(address(asset)).configuration.data;
if (!(_getActive(configData) &&!_getPaused(configData))) {
return0;
}
uint256 cash = asset.balanceOf(address(aToken));
uint256 assetsBalance = convertToAssets(balanceOf[owner]);
return cash < assetsBalance ? cash : assetsBalance;
}
functionmaxRedeem(address owner) publicviewvirtualoverridereturns (uint256) {
// check if asset is pauseduint256 configData = lendingPool.getReserveData(address(asset)).configuration.data;
if (!(_getActive(configData) &&!_getPaused(configData))) {
return0;
}
uint256 cash = asset.balanceOf(address(aToken));
uint256 cashInShares = convertToShares(cash);
uint256 shareBalance = balanceOf[owner];
return cashInShares < shareBalance ? cashInShares : shareBalance;
}
/// -----------------------------------------------------------------------/// ERC20 metadata generation/// -----------------------------------------------------------------------function_vaultName(ERC20 asset_) internalviewvirtualreturns (stringmemory vaultName) {
vaultName =string.concat("ERC4626-Wrapped Aave v3 ", asset_.symbol());
}
function_vaultSymbol(ERC20 asset_) internalviewvirtualreturns (stringmemory vaultSymbol) {
vaultSymbol =string.concat("wa", asset_.symbol());
}
/// -----------------------------------------------------------------------/// Internal functions/// -----------------------------------------------------------------------function_getDecimals(uint256 configData) internalpurereturns (uint8) {
returnuint8((configData &~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION);
}
function_getActive(uint256 configData) internalpurereturns (bool) {
return configData &~ACTIVE_MASK !=0;
}
function_getFrozen(uint256 configData) internalpurereturns (bool) {
return configData &~FROZEN_MASK !=0;
}
function_getPaused(uint256 configData) internalpurereturns (bool) {
return configData &~PAUSED_MASK !=0;
}
function_getSupplyCap(uint256 configData) internalpurereturns (uint256) {
return (configData &~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION;
}
}
Contract Source Code
File 2 of 7: ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.abstractcontractERC20{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
uint8publicimmutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/uint256public totalSupply;
mapping(address=>uint256) public balanceOf;
mapping(address=>mapping(address=>uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/uint256internalimmutable INITIAL_CHAIN_ID;
bytes32internalimmutable INITIAL_DOMAIN_SEPARATOR;
mapping(address=>uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID =block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 amount) publicvirtualreturns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
returntrue;
}
functiontransfer(address to, uint256 amount) publicvirtualreturns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
returntrue;
}
functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualreturns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
returntrue;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) publicvirtual{
require(deadline >=block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing// the owner's nonce which cannot realistically overflow.unchecked {
address recoveredAddress =ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress !=address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
functionDOMAIN_SEPARATOR() publicviewvirtualreturns (bytes32) {
returnblock.chainid== INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
functioncomputeDomainSeparator() internalviewvirtualreturns (bytes32) {
returnkeccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 amount) internalvirtual{
totalSupply += amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function_burn(addressfrom, uint256 amount) internalvirtual{
balanceOf[from] -= amount;
// Cannot underflow because a user's balance// will never be larger than the total supply.unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
Contract Source Code
File 3 of 7: ERC4626.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import {ERC20} from"../tokens/ERC20.sol";
import {SafeTransferLib} from"../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from"../utils/FixedPointMathLib.sol";
/// @notice Minimal ERC4626 tokenized Vault implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol)abstractcontractERC4626isERC20{
usingSafeTransferLibforERC20;
usingFixedPointMathLibforuint256;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventDeposit(addressindexed caller, addressindexed owner, uint256 assets, uint256 shares);
eventWithdraw(addressindexed caller,
addressindexed receiver,
addressindexed owner,
uint256 assets,
uint256 shares
);
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
ERC20 publicimmutable asset;
constructor(
ERC20 _asset,
stringmemory _name,
stringmemory _symbol
) ERC20(_name, _symbol, _asset.decimals()) {
asset = _asset;
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LOGIC
//////////////////////////////////////////////////////////////*/functiondeposit(uint256 assets, address receiver) publicvirtualreturns (uint256 shares) {
// Check for rounding error since we round down in previewDeposit.require((shares = previewDeposit(assets)) !=0, "ZERO_SHARES");
// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
functionmint(uint256 shares, address receiver) publicvirtualreturns (uint256 assets) {
assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
functionwithdraw(uint256 assets,
address receiver,
address owner
) publicvirtualreturns (uint256 shares) {
shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
functionredeem(uint256 shares,
address receiver,
address owner
) publicvirtualreturns (uint256 assets) {
if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
// Check for rounding error since we round down in previewRedeem.require((assets = previewRedeem(shares)) !=0, "ZERO_ASSETS");
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
/*//////////////////////////////////////////////////////////////
ACCOUNTING LOGIC
//////////////////////////////////////////////////////////////*/functiontotalAssets() publicviewvirtualreturns (uint256);
functionconvertToShares(uint256 assets) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? assets : assets.mulDivDown(supply, totalAssets());
}
functionconvertToAssets(uint256 shares) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? shares : shares.mulDivDown(totalAssets(), supply);
}
functionpreviewDeposit(uint256 assets) publicviewvirtualreturns (uint256) {
return convertToShares(assets);
}
functionpreviewMint(uint256 shares) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? shares : shares.mulDivUp(totalAssets(), supply);
}
functionpreviewWithdraw(uint256 assets) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? assets : assets.mulDivUp(supply, totalAssets());
}
functionpreviewRedeem(uint256 shares) publicviewvirtualreturns (uint256) {
return convertToAssets(shares);
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LIMIT LOGIC
//////////////////////////////////////////////////////////////*/functionmaxDeposit(address) publicviewvirtualreturns (uint256) {
returntype(uint256).max;
}
functionmaxMint(address) publicviewvirtualreturns (uint256) {
returntype(uint256).max;
}
functionmaxWithdraw(address owner) publicviewvirtualreturns (uint256) {
return convertToAssets(balanceOf[owner]);
}
functionmaxRedeem(address owner) publicviewvirtualreturns (uint256) {
return balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
INTERNAL HOOKS LOGIC
//////////////////////////////////////////////////////////////*/functionbeforeWithdraw(uint256 assets, uint256 shares) internalvirtual{}
functionafterDeposit(uint256 assets, uint256 shares) internalvirtual{}
}
Contract Source Code
File 4 of 7: FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Arithmetic library with operations for fixed-point numbers./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)libraryFixedPointMathLib{
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/uint256internalconstant WAD =1e18; // The scalar of ETH and most ERC20s.functionmulWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
functionmulWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
functiondivWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
functiondivWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/functionmulDivDown(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
assembly {
// Store x * y in z for now.
z :=mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))ifiszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
revert(0, 0)
}
// Divide z by the denominator.
z :=div(z, denominator)
}
}
functionmulDivUp(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
assembly {
// Store x * y in z for now.
z :=mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))ifiszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
revert(0, 0)
}
// First, divide z - 1 by the denominator and add 1.// We allow z - 1 to underflow if z is 0, because we multiply the// end result by 0 if z is zero, ensuring we return 0 if z is zero.
z :=mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
}
}
functionrpow(uint256 x,
uint256 n,
uint256 scalar
) internalpurereturns (uint256 z) {
assembly {
switch x
case0 {
switch n
case0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z :=0
}
}
default {
switchmod(n, 2)
case0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.let half :=shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n :=shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n :=shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.// Equivalent to iszero(eq(div(xx, x), x)) here.ifshr(128, x) {
revert(0, 0)
}
// Store x squared.let xx :=mul(x, x)
// Round to the nearest number.let xxRound :=add(xx, half)
// Revert if xx + half overflowed.iflt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x :=div(xxRound, scalar)
// If n is even:ifmod(n, 2) {
// Compute z * x.let zx :=mul(z, x)
// If z * x overflowed:ifiszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.ifiszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.let zxRound :=add(zx, half)
// Revert if zx + half overflowed.iflt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z :=div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/functionsqrt(uint256 x) internalpurereturns (uint256 z) {
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z :=181// The "correct" value is 1, but this saves a multiplication later.// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.// We check y >= 2^(k + 8) but shift right by k bits// each branch to ensure that if x >= 256, then y >= 256.ifiszero(lt(y, 0x10000000000000000000000000000000000)) {
y :=shr(128, y)
z :=shl(64, z)
}
ifiszero(lt(y, 0x1000000000000000000)) {
y :=shr(64, y)
z :=shl(32, z)
}
ifiszero(lt(y, 0x10000000000)) {
y :=shr(32, y)
z :=shl(16, z)
}
ifiszero(lt(y, 0x1000000)) {
y :=shr(16, y)
z :=shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could// get y in a tighter range. Currently, we will have y in [256, 256*2^16).// We ensured y >= 256 so that the relative difference between y and y+1 is small.// That's not possible if x < 256 but we can just verify those cases exhaustively.// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.// There is no overflow risk here since y < 2^136 after the first branch above.
z :=shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z :=sub(z, lt(div(x, z), z))
}
}
functionunsafeMod(uint256 x, uint256 y) internalpurereturns (uint256 z) {
assembly {
// Mod x by y. Note this will return// 0 instead of reverting if y is zero.
z :=mod(x, y)
}
}
functionunsafeDiv(uint256 x, uint256 y) internalpurereturns (uint256 r) {
assembly {
// Divide x by y. Note this will return// 0 instead of reverting if y is zero.
r :=div(x, y)
}
}
functionunsafeDivUp(uint256 x, uint256 y) internalpurereturns (uint256 z) {
assembly {
// Add 1 to x * y if x % y > 0. Note this will// return 0 instead of reverting if y is zero.
z :=add(gt(mod(x, y), 0), div(x, y))
}
}
}
Contract Source Code
File 5 of 7: IPool.sol
// SPDX-License-Identifier: AGPL-3.0pragmasolidity ^0.8.4;/**
* @title IPool
* @author Aave
* @notice Defines the basic interface for an Aave Pool.
*
*/interfaceIPool{
structReserveConfigurationMap {
//bit 0-15: LTV//bit 16-31: Liq. threshold//bit 32-47: Liq. bonus//bit 48-55: Decimals//bit 56: reserve is active//bit 57: reserve is frozen//bit 58: borrowing is enabled//bit 59: stable rate borrowing enabled//bit 60: asset is paused//bit 61: borrowing in isolation mode is enabled//bit 62-63: reserved//bit 64-79: reserve factor//bit 80-115 borrow cap in whole tokens, borrowCap == 0 => no cap//bit 116-151 supply cap in whole tokens, supplyCap == 0 => no cap//bit 152-167 liquidation protocol fee//bit 168-175 eMode category//bit 176-211 unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled//bit 212-251 debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals//bit 252-255 unuseduint256 data;
}
structReserveData {
ReserveConfigurationMap configuration;
//the liquidity index. Expressed in rayuint128 liquidityIndex;
//the current supply rate. Expressed in rayuint128 currentLiquidityRate;
//variable borrow index. Expressed in rayuint128 variableBorrowIndex;
//the current variable borrow rate. Expressed in rayuint128 currentVariableBorrowRate;
//the current stable borrow rate. Expressed in rayuint128 currentStableBorrowRate;
//timestamp of last updateuint40 lastUpdateTimestamp;
//the id of the reserve. Represents the position in the list of the active reservesuint16 id;
//aToken addressaddress aTokenAddress;
//stableDebtToken addressaddress stableDebtTokenAddress;
//variableDebtToken addressaddress variableDebtTokenAddress;
//address of the interest rate strategyaddress interestRateStrategyAddress;
//the current treasury balance, scaleduint128 accruedToTreasury;
//the outstanding unbacked aTokens minted through the bridging featureuint128 unbacked;
//the outstanding debt borrowed against this asset in isolation modeuint128 isolationModeTotalDebt;
}
/**
* @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
* - E.g. User supplies 100 USDC and gets in return 100 aUSDC
* @param asset The address of the underlying asset to supply
* @param amount The amount to be supplied
* @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
* wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
* is a different wallet
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
*
*/functionsupply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
/**
* @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
* E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
* @param asset The address of the underlying asset to withdraw
* @param amount The underlying amount to be withdrawn
* - Send the value type(uint256).max in order to withdraw the whole aToken balance
* @param to The address that will receive the underlying, same as msg.sender if the user
* wants to receive it on his own wallet, or a different address if the beneficiary is a
* different wallet
* @return The final amount withdrawn
*
*/functionwithdraw(address asset, uint256 amount, address to) externalreturns (uint256);
/**
* @notice Returns the state and configuration of the reserve
* @param asset The address of the underlying asset of the reserve
* @return The state and configuration data of the reserve
*
*/functiongetReserveData(address asset) externalviewreturns (ReserveData memory);
}
Contract Source Code
File 6 of 7: IRewardsController.sol
// SPDX-License-Identifier: AGPL-3.0pragmasolidity ^0.8.4;/**
* @title IRewardsController
* @author Aave
* @notice Defines the basic interface for a Rewards Controller.
*/interfaceIRewardsController{
/**
* @dev Claims all rewards for a user to the desired address, on all the assets of the pool, accumulating the pending rewards
* @param assets The list of assets to check eligible distributions before claiming rewards
* @param to The address that will be receiving the rewards
* @return rewardsList List of addresses of the reward tokens
* @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardList"
*
*/functionclaimAllRewards(address[] calldata assets, address to)
externalreturns (address[] memory rewardsList, uint256[] memory claimedAmounts);
}
Contract Source Code
File 7 of 7: SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import {ERC20} from"../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer./// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.librarySafeTransferLib{
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferETH(address to, uint256 amount) internal{
bool success;
assembly {
// Transfer the ETH and store if it succeeded or not.
success :=call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferFrom(
ERC20 token,
addressfrom,
address to,
uint256 amount
) internal{
bool success;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
functionsafeTransfer(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
functionsafeApprove(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}