fix: Replace constructor with initialize function and remove immutables

This commit is contained in:
Lasse Herskind 2021-09-21 13:30:40 +02:00
parent 7f9108d1f8
commit 1bf28602a3
7 changed files with 114 additions and 246 deletions

View File

@ -41,8 +41,8 @@ contract ERC20 is Context, IERC20 {
uint256 private _totalSupply;
string private _name;
string private _symbol;
string internal _name;
string internal _symbol;
uint8 private _decimals;
/**

View File

@ -0,0 +1,40 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
import {ILendingPool} from './ILendingPool.sol';
import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
/**
* @title IInitializableStaticATokenLM
* @notice Interface for the initialize function on StaticATokenLM
* @author Aave
**/
interface IInitializableStaticATokenLM {
/**
* @dev Emitted when a StaticATokenLM is initialized
* @param pool The address of the lending pool where this aToken will be used
* @param aToken The address of the underlying asset of this aToken (aWETH)
* @param staticATokenName The name of the Static aToken
* @param staticATokenSymbol The symbol of the Static aToken
**/
event Initialized(
address indexed pool,
address aToken,
string staticATokenName,
string staticATokenSymbol
);
/**
* @dev Initializes the StaticATokenLM
* @param pool The address of the lending pool where this aToken will be used
* @param aToken The address of the underlying aToken (aWETH)
* @param staticATokenName The name of the stat Static aToken
* @param staticATokenSymbol The symbol of the Static aToken
*/
function initialize(
ILendingPool pool,
address aToken,
string calldata staticATokenName,
string calldata staticATokenSymbol
) external;
}

View File

@ -3,8 +3,11 @@ pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
import {ILendingPool} from './ILendingPool.sol';
import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
import {IInitializableStaticATokenLM} from './IInitializableStaticATokenLM.sol';
interface IStaticATokenLM is IERC20 {
interface IStaticATokenLM is IERC20, IInitializableStaticATokenLM {
struct SignatureParams {
uint8 v;
bytes32 r;
@ -226,4 +229,14 @@ interface IStaticATokenLM is IERC20 {
function getLifetimeRewards() external view returns (uint256);
function getLastRewardBlock() external view returns (uint256);
function LENDING_POOL() external returns (ILendingPool);
function INCENTIVES_CONTROLLER() external returns (IAaveIncentivesController);
function ATOKEN() external returns (IERC20);
function ASSET() external returns (IERC20);
function REWARD_TOKEN() external returns (IERC20);
}

View File

@ -4,9 +4,12 @@ pragma experimental ABIEncoderV2;
import {ILendingPool} from '../../interfaces/ILendingPool.sol';
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
import {IAToken} from '../../interfaces/IAToken.sol';
import {IStaticATokenLM} from '../../interfaces/IStaticATokenLM.sol';
import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
import {IInitializableStaticATokenLM} from '../../interfaces/IInitializableStaticATokenLM.sol';
import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
import {StaticATokenErrors} from '../libraries/helpers/StaticATokenErrors.sol';
@ -23,7 +26,11 @@ import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
* The token support claiming liquidity mining rewards from the Aave system.
* @author Aave
**/
contract StaticATokenLM is IStaticATokenLM, ERC20 {
contract StaticATokenLM is
VersionedInitializable,
ERC20('STATIC_ATOKEN_IMPL', 'STATIC_ATOKEN_IMPL'),
IStaticATokenLM
{
using SafeERC20 for IERC20;
using SafeMath for uint256;
using WadRayMath for uint256;
@ -43,11 +50,13 @@ contract StaticATokenLM is IStaticATokenLM, ERC20 {
'Withdraw(address owner,address recipient,uint256 staticAmount,uint256 dynamicAmount,bool toUnderlying,uint256 nonce,uint256 deadline)'
);
ILendingPool public immutable LENDING_POOL;
IAaveIncentivesController public immutable INCENTIVES_CONTROLLER;
IERC20 public immutable ATOKEN;
IERC20 public immutable ASSET;
IERC20 public immutable REWARD_TOKEN;
uint256 public constant STATIC_ATOKEN_LM_REVISION = 0x1;
ILendingPool public override LENDING_POOL;
IAaveIncentivesController public override INCENTIVES_CONTROLLER;
IERC20 public override ATOKEN;
IERC20 public override ASSET;
IERC20 public override REWARD_TOKEN;
mapping(address => uint256) public _nonces;
@ -61,21 +70,32 @@ contract StaticATokenLM is IStaticATokenLM, ERC20 {
// user => unclaimedRewards (in RAYs)
mapping(address => uint256) private _unclaimedRewards;
constructor(
ILendingPool lendingPool,
///@inheritdoc VersionedInitializable
function getRevision() internal pure virtual override returns (uint256) {
return STATIC_ATOKEN_LM_REVISION;
}
///@inheritdoc IInitializableStaticATokenLM
function initialize(
ILendingPool pool,
address aToken,
string memory wrappedTokenName,
string memory wrappedTokenSymbol
) public ERC20(wrappedTokenName, wrappedTokenSymbol) {
LENDING_POOL = lendingPool;
string calldata staticATokenName,
string calldata staticATokenSymbol
) external override initializer {
LENDING_POOL = pool;
ATOKEN = IERC20(aToken);
IERC20 underlyingAsset = ASSET = IERC20(IAToken(aToken).UNDERLYING_ASSET_ADDRESS());
underlyingAsset.safeApprove(address(lendingPool), type(uint256).max);
_name = staticATokenName;
_symbol = staticATokenSymbol;
_setupDecimals(IERC20Detailed(aToken).decimals());
IAaveIncentivesController incentivesController =
INCENTIVES_CONTROLLER = IAToken(aToken).getIncentivesController();
REWARD_TOKEN = IERC20(incentivesController.REWARD_TOKEN());
ASSET = IERC20(IAToken(aToken).UNDERLYING_ASSET_ADDRESS());
ASSET.safeApprove(address(pool), type(uint256).max);
INCENTIVES_CONTROLLER = IAToken(aToken).getIncentivesController();
REWARD_TOKEN = IERC20(INCENTIVES_CONTROLLER.REWARD_TOKEN());
emit Initialized(address(pool), aToken, staticATokenName, staticATokenSymbol);
}
///@inheritdoc IStaticATokenLM

View File

@ -30,6 +30,7 @@ import { _TypedDataEncoder } from 'ethers/lib/utils';
import { expect, use } from 'chai';
import { getCurrentBlock } from '../../../../helpers/contracts-helpers';
import { stat } from 'fs';
//use(solidity);
@ -99,13 +100,18 @@ describe('StaticATokenLM: aToken wrapper with static balances and liquidity mini
aweth = ATokenFactory.connect(AWETH, userSigner);
stkAave = ERC20Factory.connect(STKAAVE, userSigner);
staticAToken = await new StaticATokenLMFactory(userSigner).deploy(
staticAToken = await new StaticATokenLMFactory(userSigner).deploy();
await staticAToken.initialize(
LENDING_POOL,
AWETH,
'Static Aave Interest Bearing WETH',
'stataAAVE'
);
expect(await staticAToken.decimals()).to.be.eq(18);
expect(await staticAToken.name()).to.be.eq('Static Aave Interest Bearing WETH');
expect(await staticAToken.symbol()).to.be.eq('stataAAVE');
snap = await evmSnapshot();
});
@ -188,6 +194,11 @@ describe('StaticATokenLM: aToken wrapper with static balances and liquidity mini
it('Check getters', async () => {
const amountToDeposit = utils.parseEther('5');
const accRewardsPerTokenPre = await staticAToken.getAccRewardsPerToken();
const lifetimeRewardsClaimedPre = await staticAToken.getLifetimeRewardsClaimed();
const lifetimeRewards = await staticAToken.getLifetimeRewards();
const lastRewardBlock = await staticAToken.getLastRewardBlock();
// Just preparation
await waitForTx(await weth.deposit({ value: amountToDeposit.mul(2) }));
await waitForTx(
@ -207,6 +218,13 @@ describe('StaticATokenLM: aToken wrapper with static balances and liquidity mini
expect(staticBalance).to.be.eq(staticBalanceFromDynamic);
expect(dynamicBalance).to.be.eq(dynamicBalanceFromStatic);
await staticAToken.collectAndUpdateRewards();
expect(await staticAToken.getAccRewardsPerToken()).to.be.gt(accRewardsPerTokenPre);
expect(await staticAToken.getLifetimeRewardsClaimed()).to.be.gt(lifetimeRewardsClaimedPre);
expect(await staticAToken.getLifetimeRewards()).to.be.gt(lifetimeRewards);
expect(await staticAToken.getLastRewardBlock()).to.be.gt(lastRewardBlock);
});
it('Multiple deposits in one block (Breaks if GasReport enabled)', async () => {

View File

@ -162,7 +162,8 @@ describe('StaticATokenLM: aToken wrapper with static balances and liquidity mini
aweth = ATokenFactory.connect(AWETH, userSigner);
stkAave = ERC20Factory.connect(STKAAVE, userSigner);
staticAToken = await new StaticATokenLMFactory(userSigner).deploy(
staticAToken = await new StaticATokenLMFactory(userSigner).deploy();
await staticAToken.initialize(
LENDING_POOL,
AWETH,
'Static Aave Interest Bearing WETH',

View File

@ -1,224 +0,0 @@
import rawDRE from 'hardhat';
import BigNumber from 'bignumber.js';
import {
LendingPoolFactory,
WETH9Factory,
StaticATokenFactory,
ATokenFactory,
ERC20,
LendingPool,
} from '../../types';
import { impersonateAccountsHardhat, DRE, waitForTx } from '../../helpers/misc-utils';
import { utils } from 'ethers';
import { rayMul } from '../../helpers/ray-math';
import { MAX_UINT_AMOUNT } from '../../helpers/constants';
import { tEthereumAddress } from '../../helpers/types';
const { expect } = require('chai');
const DEFAULT_GAS_LIMIT = 10000000;
const DEFAULT_GAS_PRICE = utils.parseUnits('100', 'gwei');
const defaultTxParams = { gasLimit: DEFAULT_GAS_LIMIT, gasPrice: DEFAULT_GAS_PRICE };
const ETHER_BANK = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
const LENDING_POOL = '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9';
const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
const AWETH = '0x030bA81f1c18d280636F32af80b9AAd02Cf0854e';
const TEST_USERS = ['0x0F4ee9631f4be0a63756515141281A3E2B293Bbe'];
type tBalancesInvolved = {
aTokenBalanceStaticAToken: BigNumber;
aTokenBalanceUser: BigNumber;
underlyingBalanceUser: BigNumber;
underlyingBalanceStaticAToken: BigNumber;
userStaticATokenBalance: BigNumber;
userDynamicStaticATokenBalance: BigNumber;
currentRate: BigNumber;
staticATokenSupply: BigNumber;
};
type tContextParams = {
staticAToken: ERC20;
underlying: ERC20;
aToken: ERC20;
user: tEthereumAddress;
lendingPool: LendingPool;
};
const getContext = async ({
staticAToken,
underlying,
aToken,
user,
lendingPool,
}: tContextParams): Promise<tBalancesInvolved> => ({
aTokenBalanceStaticAToken: new BigNumber(
(await aToken.balanceOf(staticAToken.address)).toString()
),
aTokenBalanceUser: new BigNumber((await aToken.balanceOf(user)).toString()),
underlyingBalanceUser: new BigNumber((await underlying.balanceOf(user)).toString()),
underlyingBalanceStaticAToken: new BigNumber(
(await underlying.balanceOf(staticAToken.address)).toString()
),
userStaticATokenBalance: new BigNumber((await staticAToken.balanceOf(user)).toString()),
userDynamicStaticATokenBalance: new BigNumber(
rayMul(
new BigNumber((await staticAToken.balanceOf(user)).toString()),
new BigNumber((await lendingPool.getReserveNormalizedIncome(WETH)).toString())
)
),
currentRate: new BigNumber((await lendingPool.getReserveNormalizedIncome(WETH)).toString()),
staticATokenSupply: new BigNumber((await staticAToken.totalSupply()).toString()),
});
before(async () => {
await rawDRE.run('set-DRE');
// Impersonations
await impersonateAccountsHardhat([ETHER_BANK, ...TEST_USERS]);
const ethHolderSigner = DRE.ethers.provider.getSigner(ETHER_BANK);
for (const recipientOfEth of [...TEST_USERS]) {
await ethHolderSigner.sendTransaction({
from: ethHolderSigner._address,
to: recipientOfEth,
value: utils.parseEther('100'),
...defaultTxParams,
});
}
console.log('\n***************');
console.log('Test setup finished');
console.log('***************\n');
});
describe('StaticAToken: aToken wrapper with static balances', () => {
it('Deposit WETH on stataWETH, then withdraw of the whole balance in underlying', async () => {
const userSigner = DRE.ethers.provider.getSigner(TEST_USERS[0]);
const lendingPool = LendingPoolFactory.connect(LENDING_POOL, userSigner);
const weth = WETH9Factory.connect(WETH, userSigner);
const aweth = ATokenFactory.connect(AWETH, userSigner);
const amountToDeposit = utils.parseEther('5');
await waitForTx(await weth.deposit({ value: amountToDeposit }));
const staticAToken = await new StaticATokenFactory(userSigner).deploy(
LENDING_POOL,
AWETH,
'Static Aave Interest Bearing WETH',
'stataAAVE'
);
const ctxtParams: tContextParams = {
staticAToken: <ERC20>staticAToken,
underlying: <ERC20>(<unknown>weth),
aToken: <ERC20>aweth,
user: userSigner._address,
lendingPool,
};
const ctxtBeforeDeposit = await getContext(ctxtParams);
await waitForTx(await weth.approve(staticAToken.address, amountToDeposit, defaultTxParams));
await waitForTx(
await staticAToken.deposit(userSigner._address, amountToDeposit, 0, true, defaultTxParams)
);
const ctxtAfterDeposit = await getContext(ctxtParams);
expect(ctxtAfterDeposit.aTokenBalanceStaticAToken.toString()).to.be.equal(
ctxtBeforeDeposit.aTokenBalanceStaticAToken
.plus(new BigNumber(amountToDeposit.toString()))
.toString()
);
expect(ctxtAfterDeposit.underlyingBalanceUser.toString()).to.be.equal(
ctxtBeforeDeposit.underlyingBalanceUser
.minus(new BigNumber(amountToDeposit.toString()))
.toString()
);
expect(ctxtAfterDeposit.userDynamicStaticATokenBalance.toString()).to.be.equal(
ctxtBeforeDeposit.userDynamicStaticATokenBalance
.plus(new BigNumber(amountToDeposit.toString()))
.toString()
);
expect(ctxtAfterDeposit.underlyingBalanceStaticAToken.toString()).to.be.equal(
ctxtBeforeDeposit.underlyingBalanceStaticAToken.toString()
);
expect(ctxtBeforeDeposit.aTokenBalanceUser.toString()).to.be.equal(
ctxtAfterDeposit.aTokenBalanceUser.toString()
);
const ctxtBeforeWithdrawal = await getContext(ctxtParams);
const amountToWithdraw = MAX_UINT_AMOUNT;
await waitForTx(
await staticAToken.withdraw(userSigner._address, amountToWithdraw, true, defaultTxParams)
);
const ctxtAfterWithdrawal = await getContext(ctxtParams);
expect(
ctxtAfterWithdrawal.aTokenBalanceStaticAToken.toString(),
'INVALID_ATOKEN_BALANCE_ON_STATICATOKEN_AFTER_WITHDRAW'
).to.be.equal(
rayMul(
ctxtAfterWithdrawal.staticATokenSupply.plus(ctxtBeforeWithdrawal.userStaticATokenBalance),
ctxtAfterWithdrawal.currentRate
)
.minus(
rayMul(ctxtBeforeWithdrawal.userStaticATokenBalance, ctxtAfterWithdrawal.currentRate)
)
.toString()
);
expect(
ctxtAfterWithdrawal.underlyingBalanceUser.toString(),
'INVALID_UNDERLYING_BALANCE_OF_USER_AFTER_WITHDRAWAL'
).to.be.equal(
ctxtBeforeWithdrawal.underlyingBalanceUser
.plus(rayMul(ctxtBeforeWithdrawal.userStaticATokenBalance, ctxtAfterWithdrawal.currentRate))
.toString()
);
expect(
ctxtAfterWithdrawal.userStaticATokenBalance.toString(),
'INVALID_STATICATOKEN_BALANCE_OF_USER_AFTER_WITHDRAWAL'
).to.be.equal('0');
expect(
ctxtAfterDeposit.underlyingBalanceStaticAToken.toString(),
'INVALID_UNDERLYNG_BALANCE_OF_STATICATOKEN_AFTER_WITHDRAWAL'
).to.be.equal(ctxtBeforeDeposit.underlyingBalanceStaticAToken.toString());
expect(
ctxtBeforeDeposit.aTokenBalanceUser.toString(),
'INVALID_ATOKEN_BALANCE_OF_USER_AFTER_WITHDRAWAL'
).to.be.equal(ctxtAfterDeposit.aTokenBalanceUser.toString());
});
it('Deposit WETH on stataWETH and then withdraw some balance in underlying', async () => {});
it('Deposit WETH on stataWETH and then withdraw all the balance in aToken', async () => {});
it('Deposit aWETH on stataWETH and then withdraw some balance in aToken', async () => {});
it('Deposit using metaDeposit()', async () => {});
it('Withdraw using withdrawDynamicAmount()', async () => {});
it('Withdraw using metaWithdraw()', async () => {});
});