mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
522 lines
16 KiB
TypeScript
522 lines
16 KiB
TypeScript
import BigNumberJs from 'bignumber.js';
|
|
import { BigNumber } from 'ethers';
|
|
import { parseEther } from 'ethers/lib/utils';
|
|
import { MAX_UINT_AMOUNT } from '../../helpers/constants';
|
|
import { evmRevert, evmSnapshot, increaseTime } from '../../helpers/misc-utils';
|
|
import { makeSuite, SignerWithAddress, TestEnv } from './helpers/make-suite';
|
|
import { checkRewards } from './helpers/rewards-distribution/verify';
|
|
|
|
const chai = require('chai');
|
|
const { expect } = chai;
|
|
|
|
/**
|
|
* @dev REW is a mocked mintable token named RewardsToken.sol with an emission rate of 1 REW per second that can be deposited using a RewardsAwareAToken implementation named RewardsATokenMock.sol
|
|
* The distribution of REW happens at `claim`, but there is also a `updateMintableEmission` functions that updates the state of the distribution of 1 user but does not claim.
|
|
*/
|
|
|
|
makeSuite('Reward Aware AToken', (testEnv: TestEnv) => {
|
|
let initTimestamp;
|
|
let evmSnapshotId;
|
|
|
|
before('Initializing configuration', async () => {
|
|
// Sets BigNumber for this suite, instead of globally
|
|
BigNumberJs.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumberJs.ROUND_DOWN });
|
|
});
|
|
|
|
after('Reset', () => {
|
|
// Reset BigNumber
|
|
BigNumberJs.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumberJs.ROUND_HALF_UP });
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
initTimestamp = await testEnv.rew.INIT_TIMESTAMP();
|
|
evmSnapshotId = await evmSnapshot();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await evmRevert(evmSnapshotId);
|
|
});
|
|
|
|
const mintAndDeposit = async (
|
|
key: SignerWithAddress,
|
|
shouldReward?: boolean,
|
|
amountSize?: BigNumber
|
|
) => {
|
|
const { rew, aRew, pool } = testEnv;
|
|
const amount = amountSize || parseEther('1');
|
|
const userATokenBalanceBeforeDeposit = await aRew.balanceOf(key.address);
|
|
|
|
// Mint REW to user
|
|
await rew.connect(key.signer).mint(amount);
|
|
|
|
// Approve and Deposit REW to pool and mint
|
|
await rew.connect(key.signer).approve(pool.address, amount);
|
|
|
|
const txDeposit = await pool.connect(key.signer).deposit(rew.address, amount, key.address, '0');
|
|
|
|
expect(Promise.resolve(txDeposit)).emit(aRew, 'Mint');
|
|
|
|
const userBalanceAfterDeposit = await rew.balanceOf(key.address);
|
|
const userATokenBalanceAfterDeposit = await aRew.balanceOf(key.address);
|
|
|
|
expect(userATokenBalanceAfterDeposit)
|
|
.to.be.eq(
|
|
userATokenBalanceBeforeDeposit.add(amount),
|
|
'User aToken balance should be equal the amount deposited'
|
|
)
|
|
.and.gt('0', 'User aToken balance should be greater than zero');
|
|
expect(userBalanceAfterDeposit).to.be.eq('0', 'Token balance should be zero');
|
|
if (!txDeposit.blockNumber) {
|
|
throw 'missing block number';
|
|
}
|
|
// Check all token rewards
|
|
await checkRewards(key, aRew.address, txDeposit.blockNumber, shouldReward);
|
|
};
|
|
|
|
const claim = async (user: SignerWithAddress, skipRewardChecks?: boolean) => {
|
|
skipRewardChecks = true;
|
|
|
|
const { rew, aRew } = testEnv;
|
|
|
|
// Claim all the rewards from aToken
|
|
const txClaim = await aRew.connect(user.signer).claim(rew.address);
|
|
await expect(Promise.resolve(txClaim)).to.emit(aRew, 'Claim');
|
|
|
|
// Calculate expected rewards
|
|
if (!txClaim.blockNumber) {
|
|
throw 'Block number missing from tx';
|
|
}
|
|
|
|
if (!skipRewardChecks) {
|
|
// Check all token rewards
|
|
await checkRewards(user, aRew.address, txClaim.blockNumber, true);
|
|
}
|
|
};
|
|
|
|
describe('Deposits: mints', async () => {
|
|
it('User1 deposits Reward Aware Token to Lending Pool', async () => {
|
|
const {
|
|
users: [user1],
|
|
} = testEnv;
|
|
|
|
await mintAndDeposit(user1);
|
|
});
|
|
|
|
it('Other users deposits Reward Aware Token to Lending Pool', async () => {
|
|
const {
|
|
users: [, user2, user3, user4],
|
|
} = testEnv;
|
|
|
|
await mintAndDeposit(user2);
|
|
await mintAndDeposit(user3);
|
|
await mintAndDeposit(user4);
|
|
});
|
|
|
|
it('User1 deposits multiple time Reward Aware Token to Lending Pool', async () => {
|
|
const {
|
|
users: [user1],
|
|
} = testEnv;
|
|
|
|
await mintAndDeposit(user1, false, parseEther('2'));
|
|
await mintAndDeposit(user1, true, parseEther('12'));
|
|
});
|
|
});
|
|
describe('Withdrawals: burns', async () => {
|
|
it('User1 deposits Reward Aware Token to Lending Pool', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
pool,
|
|
} = testEnv;
|
|
|
|
// Deposits
|
|
await mintAndDeposit(user1);
|
|
|
|
// Burn/Withdraw
|
|
await aRew.approve(pool.address, MAX_UINT_AMOUNT);
|
|
await pool.connect(user1.signer).withdraw(rew.address, MAX_UINT_AMOUNT, user1.address);
|
|
});
|
|
});
|
|
describe('Claim rewards', async () => {
|
|
it('User1 claims 100% portion of REW via the aToken contract', async () => {
|
|
const {
|
|
users: [user1],
|
|
rew,
|
|
aRew,
|
|
} = testEnv;
|
|
// User1 deposits
|
|
await mintAndDeposit(user1);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
// Check rewards for aToken at rew
|
|
const token = await aRew.getRewardsTokenAddress('0');
|
|
const aTokenRewards = await rew.getClaimableRewards(aRew.address);
|
|
// Check rewards for user at aRew
|
|
const userRewards = await aRew.getClaimableRewards(rew.address, user1.address);
|
|
// Expect user rewards to be the same as aToken rewards due 100%
|
|
expect(aTokenRewards).to.be.eq(
|
|
userRewards,
|
|
'Rewards should be the same due user holds 100% of RewardsAware distribution'
|
|
);
|
|
|
|
// Claims and check rewards
|
|
await claim(user1);
|
|
});
|
|
|
|
it('Two users with half the portion of REW via the aToken contract', async () => {
|
|
const {
|
|
users: [user1, user2],
|
|
rew,
|
|
aRew,
|
|
} = testEnv;
|
|
// User1 and user2 deposits
|
|
await mintAndDeposit(user1);
|
|
await mintAndDeposit(user2);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
// Check rewards for aToken at rew
|
|
const aTokenRewards = await rew.getClaimableRewards(aRew.address);
|
|
|
|
// Check rewards for user1 at aRew
|
|
const user1Rewards = await aRew.getClaimableRewards(rew.address, user1.address);
|
|
|
|
// Check rewards for user2 at aRew
|
|
const user2Rewards = await aRew.getClaimableRewards(rew.address, user2.address);
|
|
|
|
// Expect user rewards to be the same as aToken rewards
|
|
expect(aTokenRewards).to.be.eq(
|
|
user1Rewards.add(user2Rewards),
|
|
'Rewards should be the same of all users of RewardsAware distribution'
|
|
);
|
|
|
|
// Claims and check rewards
|
|
await claim(user1);
|
|
await claim(user2);
|
|
});
|
|
|
|
it('Four users with different portions of REW via the aToken contract', async () => {
|
|
const {
|
|
users: [user1, user2, user3, user4],
|
|
rew,
|
|
aRew,
|
|
} = testEnv;
|
|
// Deposits
|
|
await mintAndDeposit(user1, false, parseEther('1'));
|
|
await mintAndDeposit(user2, false, parseEther('2.5'));
|
|
await mintAndDeposit(user3, false, parseEther('4.7'));
|
|
await mintAndDeposit(user4, false, parseEther('0.31'));
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
// Claims and check rewards
|
|
await claim(user1);
|
|
await claim(user2);
|
|
await claim(user3);
|
|
await claim(user4);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(2713);
|
|
|
|
// Claims and check rewards
|
|
await claim(user1);
|
|
await claim(user2);
|
|
await claim(user3);
|
|
await claim(user4);
|
|
});
|
|
|
|
it('Two users with half the portion of REW via the aToken contract, one burns and them claims', async () => {
|
|
const {
|
|
users: [user1, user2],
|
|
rew,
|
|
aRew,
|
|
pool,
|
|
} = testEnv;
|
|
|
|
// User1 and user2 deposits
|
|
await mintAndDeposit(user1, false, parseEther('1000'));
|
|
await mintAndDeposit(user2, false, parseEther('1000'));
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
// Claims and check rewards
|
|
await claim(user1);
|
|
await claim(user2);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
// Burn/Withdraw to update current lifetime rewards state
|
|
await aRew.approve(pool.address, MAX_UINT_AMOUNT);
|
|
await pool.connect(user1.signer).withdraw(rew.address, MAX_UINT_AMOUNT, user1.address);
|
|
|
|
// Claims
|
|
await claim(user1);
|
|
await claim(user2);
|
|
});
|
|
});
|
|
describe('Getters', () => {
|
|
describe('getClaimableRewards', () => {
|
|
it('Rewards should be zero if user with no balance', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
|
|
const rewards = await aRew.getClaimableRewards(rew.address, user1.address);
|
|
expect(rewards).eq('0', 'Rewards should be zero');
|
|
});
|
|
|
|
it('Rewards should be available after time travel', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
const rewards = await aRew.getClaimableRewards(rew.address, user1.address);
|
|
expect(rewards).gt('0', 'Rewards should be greater than zero');
|
|
});
|
|
});
|
|
describe('getUserLifetimeRewardsAccrued', () => {
|
|
it('User lifetime rewards should be zero if no deposit', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
|
|
const rewards = await aRew.getUserRewardsAccrued(rew.address, user1.address);
|
|
expect(rewards).eq('0', 'Rewards should be zero');
|
|
});
|
|
|
|
it('User lifetime rewards should be zero if deposit due state is not updated', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
const rewards = await aRew.getUserRewardsAccrued(rew.address, user1.address);
|
|
expect(rewards).eq('0', 'Rewards should be zero');
|
|
});
|
|
|
|
it('User should have some lifetime rewards if deposit again due state is updated', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
await mintAndDeposit(user1, true);
|
|
|
|
const rewards = await aRew.getUserRewardsAccrued(rew.address, user1.address);
|
|
expect(rewards).gt('0', 'Rewards should be greater than zero');
|
|
});
|
|
|
|
it('User should have some lifetime rewards if claims due state is updated', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
await claim(user1);
|
|
|
|
const rewards = await aRew.getUserRewardsAccrued(rew.address, user1.address);
|
|
expect(rewards).gt('0', 'Rewards should be greater than zero');
|
|
});
|
|
});
|
|
|
|
describe('getUserIndex', () => {
|
|
it('User index should be zero if no deposit', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
|
|
const rewards = await aRew.getUserIndex(rew.address, user1.address);
|
|
expect(rewards).eq('0', 'Rewards should be zero');
|
|
});
|
|
|
|
it('User lifetime rewards should be zero if deposit due state is not updated', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
const rewards = await aRew.getUserIndex(rew.address, user1.address);
|
|
expect(rewards).eq('0', 'Rewards should be zero');
|
|
});
|
|
|
|
it('User should have some lifetime rewards if deposit again due state is updated', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
await mintAndDeposit(user1, true);
|
|
|
|
const rewards = await aRew.getUserIndex(rew.address, user1.address);
|
|
expect(rewards).gt('0', 'Rewards should be greater than zero');
|
|
});
|
|
|
|
it('User should have some lifetime rewards if claims due state is updated', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
await claim(user1);
|
|
|
|
const rewards = await aRew.getUserIndex(rew.address, user1.address);
|
|
expect(rewards).gt('0', 'Rewards should be greater than zero');
|
|
});
|
|
});
|
|
describe('getUserClaimedRewards', () => {
|
|
it('User should NOT have claimed rewards if didnt claim', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
const rewards = await aRew.getUserIndex(rew.address, user1.address);
|
|
expect(rewards).eq('0', 'Rewards should be zero');
|
|
});
|
|
|
|
it('User should have claimed rewards if claims', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
await claim(user1);
|
|
|
|
const rewards = await aRew.getUserIndex(rew.address, user1.address);
|
|
expect(rewards).gt('0', 'Rewards should be greater than zero');
|
|
});
|
|
});
|
|
describe('getLifetimeRewards', () => {
|
|
it('The aToken Tifetime rewards should be zero if there is no deposits', async () => {
|
|
const { aRew, rew } = testEnv;
|
|
|
|
const rewards = await aRew.getLifetimeRewards(rew.address);
|
|
expect(rewards).eq('0', 'Rewards should be zero');
|
|
});
|
|
|
|
it('The aToken lifetime rewards should be zero at init', async () => {
|
|
const { aRew, rew } = testEnv;
|
|
|
|
const rewards = await aRew.getLifetimeRewards(rew.address);
|
|
expect(rewards).eq('0', 'Rewards should be zero');
|
|
});
|
|
|
|
it('The aToken lifetime rewards should update if there is further actions: deposit', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
// Deposit again to update current lifetime rewards state
|
|
await mintAndDeposit(user1, true);
|
|
|
|
const rewards = await aRew.getLifetimeRewards(rew.address);
|
|
expect(rewards).gte('0', 'Rewards should be greater than zero');
|
|
});
|
|
|
|
it('The aToken lifetime rewards should update if there is further actions: claim', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
// Claim to update current lifetime rewards state
|
|
await claim(user1);
|
|
|
|
const rewards = await aRew.getLifetimeRewards(rew.address);
|
|
expect(rewards).gte('0', 'Rewards should be greater than zero');
|
|
});
|
|
|
|
it('The aToken lifetime rewards should update if there is further actions: burn', async () => {
|
|
const {
|
|
users: [user1],
|
|
aRew,
|
|
rew,
|
|
pool,
|
|
} = testEnv;
|
|
await mintAndDeposit(user1);
|
|
|
|
// Pass time to generate rewards
|
|
await increaseTime(1000);
|
|
|
|
// Burn/Withdraw to update current lifetime rewards state
|
|
await aRew.approve(pool.address, MAX_UINT_AMOUNT);
|
|
await pool.connect(user1.signer).withdraw(rew.address, MAX_UINT_AMOUNT, user1.address);
|
|
|
|
const rewards = await aRew.getLifetimeRewards(rew.address);
|
|
expect(rewards).gte('0', 'Rewards should be greater than zero');
|
|
});
|
|
});
|
|
describe('getRewardsToken', () => {
|
|
it('The getter should return the current token reward address', async () => {
|
|
const { aRew, rew } = testEnv;
|
|
const rewardToken = await aRew.getRewardsTokenAddress(0);
|
|
expect(rewardToken).to.be.equal(rew.address);
|
|
});
|
|
});
|
|
});
|
|
});
|