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);
 | 
						|
      });
 | 
						|
    });
 | 
						|
  });
 | 
						|
});
 |