mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
- Added basic flow test of StaticAToken + template of other tests needed
This commit is contained in:
parent
7c555ba55a
commit
a3e11800a4
|
@ -115,3 +115,9 @@ export const notFalsyOrZeroAddress = (address: tEthereumAddress | null | undefin
|
|||
}
|
||||
return isAddress(address) && !isZeroAddress(address);
|
||||
};
|
||||
|
||||
export const impersonateAccountsHardhat = async (accounts: tEthereumAddress[]) => {
|
||||
for (const account of accounts) {
|
||||
await DRE.network.provider.send('hardhat_impersonateAccount', [account]);
|
||||
}
|
||||
};
|
||||
|
|
73
helpers/ray-math.ts
Normal file
73
helpers/ray-math.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export type BigNumberValue = string | number | BigNumber;
|
||||
|
||||
export const BigNumberZD = BigNumber.clone({
|
||||
DECIMAL_PLACES: 0,
|
||||
ROUNDING_MODE: BigNumber.ROUND_DOWN,
|
||||
});
|
||||
|
||||
export function valueToBigNumber(amount: BigNumberValue): BigNumber {
|
||||
return new BigNumber(amount);
|
||||
}
|
||||
|
||||
export function valueToZDBigNumber(amount: BigNumberValue): BigNumber {
|
||||
return new BigNumberZD(amount);
|
||||
}
|
||||
|
||||
export const WAD = valueToZDBigNumber(10).pow(18);
|
||||
export const HALF_WAD = WAD.dividedBy(2);
|
||||
|
||||
export const RAY = valueToZDBigNumber(10).pow(27);
|
||||
export const HALF_RAY = RAY.dividedBy(2);
|
||||
|
||||
export const WAD_RAY_RATIO = valueToZDBigNumber(10).pow(9);
|
||||
|
||||
export function wadMul(a: BigNumberValue, b: BigNumberValue): BigNumber {
|
||||
return HALF_WAD.plus(valueToZDBigNumber(a).multipliedBy(b)).div(WAD);
|
||||
}
|
||||
|
||||
export function wadDiv(a: BigNumberValue, b: BigNumberValue): BigNumber {
|
||||
const halfB = valueToZDBigNumber(b).div(2);
|
||||
|
||||
return halfB.plus(valueToZDBigNumber(a).multipliedBy(WAD)).div(b);
|
||||
}
|
||||
|
||||
export function rayMul(a: BigNumberValue, b: BigNumberValue): BigNumber {
|
||||
return HALF_RAY.plus(valueToZDBigNumber(a).multipliedBy(b)).div(RAY);
|
||||
}
|
||||
|
||||
export function rayDiv(a: BigNumberValue, b: BigNumberValue): BigNumber {
|
||||
const halfB = valueToZDBigNumber(b).div(2);
|
||||
|
||||
return halfB.plus(valueToZDBigNumber(a).multipliedBy(RAY)).div(b);
|
||||
}
|
||||
|
||||
export function rayToWad(a: BigNumberValue): BigNumber {
|
||||
const halfRatio = valueToZDBigNumber(WAD_RAY_RATIO).div(2);
|
||||
|
||||
return halfRatio.plus(a).div(WAD_RAY_RATIO);
|
||||
}
|
||||
|
||||
export function wadToRay(a: BigNumberValue): BigNumber {
|
||||
return valueToZDBigNumber(a).multipliedBy(WAD_RAY_RATIO).decimalPlaces(0);
|
||||
}
|
||||
|
||||
export function rayPow(a: BigNumberValue, p: BigNumberValue): BigNumber {
|
||||
let x = valueToZDBigNumber(a);
|
||||
let n = valueToZDBigNumber(p);
|
||||
let z = !n.modulo(2).eq(0) ? x : valueToZDBigNumber(RAY);
|
||||
|
||||
for (n = n.div(2); !n.eq(0); n = n.div(2)) {
|
||||
x = rayMul(x, x);
|
||||
|
||||
if (!n.modulo(2).eq(0)) {
|
||||
z = rayMul(z, x);
|
||||
}
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
export function rayToDecimal(a: BigNumberValue): BigNumber {
|
||||
return valueToZDBigNumber(a).dividedBy(RAY);
|
||||
}
|
|
@ -33,6 +33,7 @@
|
|||
"test-weth": "hardhat test test/__setup.spec.ts test/weth-gateway.spec.ts",
|
||||
"test-uniswap": "hardhat test test/__setup.spec.ts test/uniswapAdapters*.spec.ts",
|
||||
"test:main:check-list": "MAINNET_FORK=true TS_NODE_TRANSPILE_ONLY=1 hardhat test test/__setup.spec.ts test/mainnet/check-list.spec.ts",
|
||||
"test:main:staticAToken": "MAINNET_FORK=true TS_NODE_TRANSPILE_ONLY=1 hardhat test test/mainnet/static-atoken.spec.ts",
|
||||
"dev:coverage": "buidler compile --force && buidler coverage --network coverage",
|
||||
"aave:evm:dev:migration": "npm run compile && hardhat aave:dev",
|
||||
"aave:docker:full:migration": "npm run compile && npm run hardhat:docker -- aave:mainnet",
|
||||
|
|
224
test/mainnet/static-atoken.spec.ts
Normal file
224
test/mainnet/static-atoken.spec.ts
Normal file
|
@ -0,0 +1,224 @@
|
|||
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 () => {});
|
||||
});
|
Loading…
Reference in New Issue
Block a user