mirror of
				https://github.com/Instadapp/aave-protocol-v2.git
				synced 2024-07-29 21:47:30 +00:00 
			
		
		
		
	- Added permit() to aToken.
This commit is contained in:
		
							parent
							
								
									f3856bac12
								
							
						
					
					
						commit
						748312cf20
					
				| 
						 | 
				
			
			@ -2,13 +2,13 @@ import {usePlugin, BuidlerConfig} from '@nomiclabs/buidler/config';
 | 
			
		|||
// @ts-ignore
 | 
			
		||||
import {accounts} from './test-wallets.js';
 | 
			
		||||
import {eEthereumNetwork} from './helpers/types';
 | 
			
		||||
import { BUIDLEREVM_CHAINID, COVERAGE_CHAINID } from './helpers/constants';
 | 
			
		||||
 | 
			
		||||
usePlugin('@nomiclabs/buidler-ethers');
 | 
			
		||||
usePlugin('buidler-typechain');
 | 
			
		||||
usePlugin('solidity-coverage');
 | 
			
		||||
usePlugin('@nomiclabs/buidler-waffle');
 | 
			
		||||
usePlugin('@nomiclabs/buidler-etherscan');
 | 
			
		||||
usePlugin('buidler-gas-reporter');
 | 
			
		||||
 | 
			
		||||
const DEFAULT_BLOCK_GAS_LIMIT = 10000000;
 | 
			
		||||
const DEFAULT_GAS_PRICE = 10;
 | 
			
		||||
| 
						 | 
				
			
			@ -59,6 +59,7 @@ const config: any = {
 | 
			
		|||
  networks: {
 | 
			
		||||
    coverage: {
 | 
			
		||||
      url: 'http://localhost:8555',
 | 
			
		||||
      chainId: COVERAGE_CHAINID,
 | 
			
		||||
    },
 | 
			
		||||
    kovan: getCommonNetworkConfig(eEthereumNetwork.kovan, 42),
 | 
			
		||||
    ropsten: getCommonNetworkConfig(eEthereumNetwork.ropsten, 3),
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +69,7 @@ const config: any = {
 | 
			
		|||
      blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT,
 | 
			
		||||
      gas: DEFAULT_BLOCK_GAS_LIMIT,
 | 
			
		||||
      gasPrice: 8000000000,
 | 
			
		||||
      chainId: 31337,
 | 
			
		||||
      chainId: BUIDLEREVM_CHAINID,
 | 
			
		||||
      throwOnTransactionFailures: true,
 | 
			
		||||
      throwOnCallFailures: true,
 | 
			
		||||
      accounts: accounts.map(({secretKey, balance}: {secretKey: string; balance: string}) => ({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,8 +29,15 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
 | 
			
		|||
 | 
			
		||||
  mapping(address => uint256) private _scaledRedirectedBalances;
 | 
			
		||||
 | 
			
		||||
  /// @dev owner => next valid nonce to submit with permit()
 | 
			
		||||
  mapping (address => uint256) public _nonces;
 | 
			
		||||
 | 
			
		||||
  uint256 public constant ATOKEN_REVISION = 0x1;
 | 
			
		||||
  
 | 
			
		||||
  bytes32 public DOMAIN_SEPARATOR;
 | 
			
		||||
  bytes public constant EIP712_REVISION = bytes("1");
 | 
			
		||||
  bytes32 internal constant EIP712_DOMAIN = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
 | 
			
		||||
  bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
 | 
			
		||||
 | 
			
		||||
  modifier onlyLendingPool {
 | 
			
		||||
    require(msg.sender == address(POOL), Errors.CALLER_MUST_BE_LENDING_POOL);
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +63,21 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
 | 
			
		|||
    string calldata tokenName,
 | 
			
		||||
    string calldata tokenSymbol
 | 
			
		||||
  ) external virtual initializer {
 | 
			
		||||
    uint256 chainId;
 | 
			
		||||
 | 
			
		||||
    //solium-disable-next-line
 | 
			
		||||
    assembly {
 | 
			
		||||
        chainId := chainid()
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    DOMAIN_SEPARATOR = keccak256(abi.encode(
 | 
			
		||||
        EIP712_DOMAIN,
 | 
			
		||||
        keccak256(bytes(tokenName)),
 | 
			
		||||
        keccak256(EIP712_REVISION),
 | 
			
		||||
        chainId,
 | 
			
		||||
        address(this)
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    _setName(tokenName);
 | 
			
		||||
    _setSymbol(tokenSymbol);
 | 
			
		||||
    _setDecimals(underlyingAssetDecimals);
 | 
			
		||||
| 
						 | 
				
			
			@ -187,6 +209,42 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
 | 
			
		|||
    return amount;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
  * @dev implements the permit function as for https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
 | 
			
		||||
  * @param owner the owner of the funds
 | 
			
		||||
  * @param spender the spender
 | 
			
		||||
  * @param value the amount
 | 
			
		||||
  * @param deadline the deadline timestamp, 0 for no deadline
 | 
			
		||||
  * @param v signature param
 | 
			
		||||
  * @param s signature param
 | 
			
		||||
  * @param r signature param
 | 
			
		||||
  */
 | 
			
		||||
  function permit(
 | 
			
		||||
      address owner,
 | 
			
		||||
      address spender,
 | 
			
		||||
      uint256 value,
 | 
			
		||||
      uint256 deadline,
 | 
			
		||||
      uint8 v,
 | 
			
		||||
      bytes32 r,
 | 
			
		||||
      bytes32 s
 | 
			
		||||
  ) external {
 | 
			
		||||
      require(owner != address(0), "INVALID_OWNER");
 | 
			
		||||
      //solium-disable-next-line
 | 
			
		||||
      require(block.timestamp <= deadline, "INVALID_EXPIRATION");
 | 
			
		||||
      uint256 currentValidNonce = _nonces[owner];
 | 
			
		||||
      bytes32 digest = keccak256(
 | 
			
		||||
              abi.encodePacked(
 | 
			
		||||
                  "\x19\x01",
 | 
			
		||||
                  DOMAIN_SEPARATOR,
 | 
			
		||||
                  keccak256(
 | 
			
		||||
                      abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
 | 
			
		||||
                  )
 | 
			
		||||
      );
 | 
			
		||||
      require(owner == ecrecover(digest, v, r, s), "INVALID_SIGNATURE");
 | 
			
		||||
      _nonces[owner] = currentValidNonce.add(1);
 | 
			
		||||
      _approve(owner, spender, value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function _transfer(
 | 
			
		||||
    address from,
 | 
			
		||||
    address to,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,12 +8,16 @@ import {
 | 
			
		|||
  IReserveParams,
 | 
			
		||||
  tEthereumAddress,
 | 
			
		||||
  iBasicDistributionParams,
 | 
			
		||||
  eEthereumNetwork,
 | 
			
		||||
} from './types';
 | 
			
		||||
import BigNumber from 'bignumber.js';
 | 
			
		||||
import {getParamPerPool} from './contracts-helpers';
 | 
			
		||||
import {getParamPerPool, getParamPerNetwork} from './contracts-helpers';
 | 
			
		||||
 | 
			
		||||
export const TEST_SNAPSHOT_ID = '0x1';
 | 
			
		||||
 | 
			
		||||
export const BUIDLEREVM_CHAINID = 31337;
 | 
			
		||||
export const COVERAGE_CHAINID = 1337;
 | 
			
		||||
 | 
			
		||||
// ----------------
 | 
			
		||||
// MATH
 | 
			
		||||
// ----------------
 | 
			
		||||
| 
						 | 
				
			
			@ -531,3 +535,18 @@ export const getFeeDistributionParamsCommon = (
 | 
			
		|||
    percentages,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getATokenDomainSeparatorPerNetwork = (
 | 
			
		||||
  network: eEthereumNetwork
 | 
			
		||||
): tEthereumAddress =>
 | 
			
		||||
  getParamPerNetwork<tEthereumAddress>(
 | 
			
		||||
    {
 | 
			
		||||
      [eEthereumNetwork.coverage]: "0x95b73a72c6ecf4ccbbba5178800023260bad8e75cdccdb8e4827a2977a37c820",
 | 
			
		||||
      [eEthereumNetwork.buidlerevm]:
 | 
			
		||||
        "0x76cbbf8aa4b11a7c207dd79ccf8c394f59475301598c9a083f8258b4fafcfa86",
 | 
			
		||||
      [eEthereumNetwork.kovan]: "",
 | 
			
		||||
      [eEthereumNetwork.ropsten]: "",
 | 
			
		||||
      [eEthereumNetwork.main]: "",
 | 
			
		||||
    },
 | 
			
		||||
    network
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +32,8 @@ import {Ierc20Detailed} from '../types/Ierc20Detailed';
 | 
			
		|||
import {StableDebtToken} from '../types/StableDebtToken';
 | 
			
		||||
import {VariableDebtToken} from '../types/VariableDebtToken';
 | 
			
		||||
import {MockSwapAdapter} from '../types/MockSwapAdapter';
 | 
			
		||||
import { signTypedData_v4, TypedData } from "eth-sig-util";
 | 
			
		||||
import { fromRpcSig, ECDSASignature } from "ethereumjs-util";
 | 
			
		||||
 | 
			
		||||
export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => {
 | 
			
		||||
  const currentNetwork = BRE.network.name;
 | 
			
		||||
| 
						 | 
				
			
			@ -431,10 +433,14 @@ const linkBytecode = (artifact: Artifact, libraries: any) => {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
export const getParamPerNetwork = <T>(
 | 
			
		||||
  {kovan, ropsten, main}: iParamsPerNetwork<T>,
 | 
			
		||||
  {kovan, ropsten, main, buidlerevm, coverage}: iParamsPerNetwork<T>,
 | 
			
		||||
  network: eEthereumNetwork
 | 
			
		||||
) => {
 | 
			
		||||
  switch (network) {
 | 
			
		||||
    case eEthereumNetwork.coverage:
 | 
			
		||||
      return coverage;
 | 
			
		||||
    case eEthereumNetwork.buidlerevm:
 | 
			
		||||
      return buidlerevm;
 | 
			
		||||
    case eEthereumNetwork.kovan:
 | 
			
		||||
      return kovan;
 | 
			
		||||
    case eEthereumNetwork.ropsten:
 | 
			
		||||
| 
						 | 
				
			
			@ -471,3 +477,59 @@ export const convertToCurrencyUnits = async (tokenAddress: string, amount: strin
 | 
			
		|||
  const amountInCurrencyUnits = new BigNumber(amount).div(currencyUnit);
 | 
			
		||||
  return amountInCurrencyUnits.toFixed();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const buildPermitParams = (
 | 
			
		||||
  chainId: number,
 | 
			
		||||
  token: tEthereumAddress,
 | 
			
		||||
  revision: string,
 | 
			
		||||
  tokenName: string,
 | 
			
		||||
  owner: tEthereumAddress,
 | 
			
		||||
  spender: tEthereumAddress,
 | 
			
		||||
  nonce: number,
 | 
			
		||||
  deadline: string,
 | 
			
		||||
  value: tStringTokenSmallUnits
 | 
			
		||||
) => ({
 | 
			
		||||
  types: {
 | 
			
		||||
    EIP712Domain: [
 | 
			
		||||
      { name: "name", type: "string" },
 | 
			
		||||
      { name: "version", type: "string" },
 | 
			
		||||
      { name: "chainId", type: "uint256" },
 | 
			
		||||
      { name: "verifyingContract", type: "address" },
 | 
			
		||||
    ],
 | 
			
		||||
    Permit: [
 | 
			
		||||
      { name: "owner", type: "address" },
 | 
			
		||||
      { name: "spender", type: "address" },
 | 
			
		||||
      { name: "value", type: "uint256" },
 | 
			
		||||
      { name: "nonce", type: "uint256" },
 | 
			
		||||
      { name: "deadline", type: "uint256" },
 | 
			
		||||
    ],
 | 
			
		||||
  },
 | 
			
		||||
  primaryType: "Permit" as const,
 | 
			
		||||
  domain: {
 | 
			
		||||
    name: tokenName,
 | 
			
		||||
    version: revision,
 | 
			
		||||
    chainId: chainId,
 | 
			
		||||
    verifyingContract: token,
 | 
			
		||||
  },
 | 
			
		||||
  message: {
 | 
			
		||||
    owner,
 | 
			
		||||
    spender,
 | 
			
		||||
    value,
 | 
			
		||||
    nonce,
 | 
			
		||||
    deadline,
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const getSignatureFromTypedData = (
 | 
			
		||||
  privateKey: string,
 | 
			
		||||
  typedData: any // TODO: should be TypedData, from eth-sig-utils, but TS doesn't accept it
 | 
			
		||||
): ECDSASignature => {
 | 
			
		||||
  const signature = signTypedData_v4(
 | 
			
		||||
    Buffer.from(privateKey.substring(2, 66), "hex"),
 | 
			
		||||
    {
 | 
			
		||||
      data: typedData,
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
  return fromRpcSig(signature);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ export enum eEthereumNetwork {
 | 
			
		|||
  kovan = 'kovan',
 | 
			
		||||
  ropsten = 'ropsten',
 | 
			
		||||
  main = 'main',
 | 
			
		||||
  coverage = 'coverage'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum AavePools {
 | 
			
		||||
| 
						 | 
				
			
			@ -251,6 +252,8 @@ export interface IMarketRates {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
export interface iParamsPerNetwork<T> {
 | 
			
		||||
  [eEthereumNetwork.coverage]: T;
 | 
			
		||||
  [eEthereumNetwork.buidlerevm]: T;
 | 
			
		||||
  [eEthereumNetwork.kovan]: T;
 | 
			
		||||
  [eEthereumNetwork.ropsten]: T;
 | 
			
		||||
  [eEthereumNetwork.main]: T;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,7 @@
 | 
			
		|||
    "test-repay-with-collateral": "buidler test test/__setup.spec.ts test/repay-with-collateral.spec.ts",
 | 
			
		||||
    "test-liquidate-with-collateral": "buidler test test/__setup.spec.ts test/flash-liquidation-with-collateral.spec.ts",
 | 
			
		||||
    "test-flash": "buidler test test/__setup.spec.ts test/flashloan.spec.ts",
 | 
			
		||||
    "test-permit": "buidler test test/__setup.spec.ts test/atoken-permit.spec.ts",
 | 
			
		||||
    "dev:coverage": "buidler coverage --network coverage",
 | 
			
		||||
    "dev:deployment": "buidler dev-deployment",
 | 
			
		||||
    "dev:deployExample": "buidler deploy-Example",
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +59,9 @@
 | 
			
		|||
    "tslint-config-prettier": "^1.18.0",
 | 
			
		||||
    "tslint-plugin-prettier": "^2.3.0",
 | 
			
		||||
    "typechain": "2.0.0",
 | 
			
		||||
    "typescript": "3.9.3"
 | 
			
		||||
    "typescript": "3.9.3",
 | 
			
		||||
    "eth-sig-util": "2.5.3",
 | 
			
		||||
    "ethereumjs-util": "7.0.2"
 | 
			
		||||
  },
 | 
			
		||||
  "husky": {
 | 
			
		||||
    "hooks": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										312
									
								
								test/atoken-permit.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								test/atoken-permit.spec.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,312 @@
 | 
			
		|||
import {
 | 
			
		||||
  MAX_UINT_AMOUNT,
 | 
			
		||||
  ZERO_ADDRESS,
 | 
			
		||||
  getATokenDomainSeparatorPerNetwork,
 | 
			
		||||
  BUIDLEREVM_CHAINID,
 | 
			
		||||
} from '../helpers/constants';
 | 
			
		||||
import {buildPermitParams, getSignatureFromTypedData} from '../helpers/contracts-helpers';
 | 
			
		||||
import {expect} from 'chai';
 | 
			
		||||
import {ethers} from 'ethers';
 | 
			
		||||
import {eEthereumNetwork} from '../helpers/types';
 | 
			
		||||
import {makeSuite, TestEnv} from './helpers/make-suite';
 | 
			
		||||
import {BRE} from '../helpers/misc-utils';
 | 
			
		||||
import {waitForTx} from './__setup.spec';
 | 
			
		||||
 | 
			
		||||
const {parseEther} = ethers.utils;
 | 
			
		||||
 | 
			
		||||
makeSuite('AToken: Permit', (testEnv: TestEnv) => {
 | 
			
		||||
  it('Checks the domain separator', async () => {
 | 
			
		||||
    const DOMAIN_SEPARATOR_ENCODED = getATokenDomainSeparatorPerNetwork(
 | 
			
		||||
      eEthereumNetwork.buidlerevm
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const {aDai} = testEnv;
 | 
			
		||||
 | 
			
		||||
    const separator = await aDai.DOMAIN_SEPARATOR();
 | 
			
		||||
 | 
			
		||||
    expect(separator).to.be.equal(DOMAIN_SEPARATOR_ENCODED, 'Invalid domain separator');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Get aDAI for tests', async () => {
 | 
			
		||||
    const {dai, deployer, pool} = testEnv;
 | 
			
		||||
 | 
			
		||||
    await dai.mint(parseEther('20000'));
 | 
			
		||||
    await dai.approve(pool.address, parseEther('20000'));
 | 
			
		||||
    await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Reverts submitting a permit with 0 expiration', async () => {
 | 
			
		||||
    const {aDai, deployer, users} = testEnv;
 | 
			
		||||
    const owner = deployer;
 | 
			
		||||
    const spender = users[1];
 | 
			
		||||
 | 
			
		||||
    const tokenName = await aDai.name();
 | 
			
		||||
 | 
			
		||||
    const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
 | 
			
		||||
    const expiration = 0;
 | 
			
		||||
    const nonce = (await aDai._nonces(owner.address)).toNumber();
 | 
			
		||||
    const permitAmount = ethers.utils.parseEther('2').toString();
 | 
			
		||||
    const msgParams = buildPermitParams(
 | 
			
		||||
      chainId,
 | 
			
		||||
      aDai.address,
 | 
			
		||||
      '1',
 | 
			
		||||
      tokenName,
 | 
			
		||||
      owner.address,
 | 
			
		||||
      spender.address,
 | 
			
		||||
      nonce,
 | 
			
		||||
      permitAmount,
 | 
			
		||||
      expiration.toFixed()
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
 | 
			
		||||
    if (!ownerPrivateKey) {
 | 
			
		||||
      throw new Error('INVALID_OWNER_PK');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
 | 
			
		||||
      '0',
 | 
			
		||||
      'INVALID_ALLOWANCE_BEFORE_PERMIT'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      aDai
 | 
			
		||||
        .connect(spender.signer)
 | 
			
		||||
        .permit(owner.address, spender.address, permitAmount, expiration, v, r, s)
 | 
			
		||||
    ).to.be.revertedWith('INVALID_EXPIRATION');
 | 
			
		||||
 | 
			
		||||
    expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
 | 
			
		||||
      '0',
 | 
			
		||||
      'INVALID_ALLOWANCE_AFTER_PERMIT'
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Submits a permit with maximum expiration length', async () => {
 | 
			
		||||
    const {aDai, deployer, users} = testEnv;
 | 
			
		||||
    const owner = deployer;
 | 
			
		||||
    const spender = users[1];
 | 
			
		||||
 | 
			
		||||
    const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
 | 
			
		||||
    const deadline = MAX_UINT_AMOUNT;
 | 
			
		||||
    const nonce = (await aDai._nonces(owner.address)).toNumber();
 | 
			
		||||
    const permitAmount = parseEther('2').toString();
 | 
			
		||||
    const msgParams = buildPermitParams(
 | 
			
		||||
      chainId,
 | 
			
		||||
      aDai.address,
 | 
			
		||||
      '1',
 | 
			
		||||
      await aDai.name(),
 | 
			
		||||
      owner.address,
 | 
			
		||||
      spender.address,
 | 
			
		||||
      nonce,
 | 
			
		||||
      deadline,
 | 
			
		||||
      permitAmount
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
 | 
			
		||||
    if (!ownerPrivateKey) {
 | 
			
		||||
      throw new Error('INVALID_OWNER_PK');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
 | 
			
		||||
      '0',
 | 
			
		||||
      'INVALID_ALLOWANCE_BEFORE_PERMIT'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
 | 
			
		||||
 | 
			
		||||
    await waitForTx(
 | 
			
		||||
      await aDai
 | 
			
		||||
        .connect(spender.signer)
 | 
			
		||||
        .permit(owner.address, spender.address, permitAmount, deadline, v, r, s)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    expect((await aDai._nonces(owner.address)).toNumber()).to.be.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Cancels the previous permit', async () => {
 | 
			
		||||
    const {aDai, deployer, users} = testEnv;
 | 
			
		||||
    const owner = deployer;
 | 
			
		||||
    const spender = users[1];
 | 
			
		||||
 | 
			
		||||
    const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
 | 
			
		||||
    const deadline = MAX_UINT_AMOUNT;
 | 
			
		||||
    const nonce = (await aDai._nonces(owner.address)).toNumber();
 | 
			
		||||
    const permitAmount = '0';
 | 
			
		||||
    const msgParams = buildPermitParams(
 | 
			
		||||
      chainId,
 | 
			
		||||
      aDai.address,
 | 
			
		||||
      '1',
 | 
			
		||||
      await aDai.name(),
 | 
			
		||||
      owner.address,
 | 
			
		||||
      spender.address,
 | 
			
		||||
      nonce,
 | 
			
		||||
      deadline,
 | 
			
		||||
      permitAmount
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
 | 
			
		||||
    if (!ownerPrivateKey) {
 | 
			
		||||
      throw new Error('INVALID_OWNER_PK');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
 | 
			
		||||
 | 
			
		||||
    expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
 | 
			
		||||
      ethers.utils.parseEther('2'),
 | 
			
		||||
      'INVALID_ALLOWANCE_BEFORE_PERMIT'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await waitForTx(
 | 
			
		||||
      await aDai
 | 
			
		||||
        .connect(spender.signer)
 | 
			
		||||
        .permit(owner.address, spender.address, permitAmount, deadline, v, r, s)
 | 
			
		||||
    );
 | 
			
		||||
    expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
 | 
			
		||||
      permitAmount,
 | 
			
		||||
      'INVALID_ALLOWANCE_AFTER_PERMIT'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    expect((await aDai._nonces(owner.address)).toNumber()).to.be.equal(2);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Tries to submit a permit with invalid nonce', async () => {
 | 
			
		||||
    const {aDai, deployer, users} = testEnv;
 | 
			
		||||
    const owner = deployer;
 | 
			
		||||
    const spender = users[1];
 | 
			
		||||
 | 
			
		||||
    const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
 | 
			
		||||
    const deadline = MAX_UINT_AMOUNT;
 | 
			
		||||
    const nonce = 1000;
 | 
			
		||||
    const permitAmount = '0';
 | 
			
		||||
    const msgParams = buildPermitParams(
 | 
			
		||||
      chainId,
 | 
			
		||||
      aDai.address,
 | 
			
		||||
      '1',
 | 
			
		||||
      await aDai.name(),
 | 
			
		||||
      owner.address,
 | 
			
		||||
      spender.address,
 | 
			
		||||
      nonce,
 | 
			
		||||
      deadline,
 | 
			
		||||
      permitAmount
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
 | 
			
		||||
    if (!ownerPrivateKey) {
 | 
			
		||||
      throw new Error('INVALID_OWNER_PK');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      aDai
 | 
			
		||||
        .connect(spender.signer)
 | 
			
		||||
        .permit(owner.address, spender.address, permitAmount, deadline, v, r, s)
 | 
			
		||||
    ).to.be.revertedWith('INVALID_SIGNATURE');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Tries to submit a permit with invalid expiration (previous to the current block)', async () => {
 | 
			
		||||
    const {aDai, deployer, users} = testEnv;
 | 
			
		||||
    const owner = deployer;
 | 
			
		||||
    const spender = users[1];
 | 
			
		||||
 | 
			
		||||
    const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
 | 
			
		||||
    const expiration = '1';
 | 
			
		||||
    const nonce = (await aDai._nonces(owner.address)).toNumber();
 | 
			
		||||
    const permitAmount = '0';
 | 
			
		||||
    const msgParams = buildPermitParams(
 | 
			
		||||
      chainId,
 | 
			
		||||
      aDai.address,
 | 
			
		||||
      '1',
 | 
			
		||||
      await aDai.name(),
 | 
			
		||||
      owner.address,
 | 
			
		||||
      spender.address,
 | 
			
		||||
      nonce,
 | 
			
		||||
      expiration,
 | 
			
		||||
      permitAmount
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
 | 
			
		||||
    if (!ownerPrivateKey) {
 | 
			
		||||
      throw new Error('INVALID_OWNER_PK');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      aDai
 | 
			
		||||
        .connect(spender.signer)
 | 
			
		||||
        .permit(owner.address, spender.address, expiration, permitAmount, v, r, s)
 | 
			
		||||
    ).to.be.revertedWith('INVALID_EXPIRATION');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Tries to submit a permit with invalid signature', async () => {
 | 
			
		||||
    const {aDai, deployer, users} = testEnv;
 | 
			
		||||
    const owner = deployer;
 | 
			
		||||
    const spender = users[1];
 | 
			
		||||
 | 
			
		||||
    const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
 | 
			
		||||
    const deadline = MAX_UINT_AMOUNT;
 | 
			
		||||
    const nonce = (await aDai._nonces(owner.address)).toNumber();
 | 
			
		||||
    const permitAmount = '0';
 | 
			
		||||
    const msgParams = buildPermitParams(
 | 
			
		||||
      chainId,
 | 
			
		||||
      aDai.address,
 | 
			
		||||
      '1',
 | 
			
		||||
      await aDai.name(),
 | 
			
		||||
      owner.address,
 | 
			
		||||
      spender.address,
 | 
			
		||||
      nonce,
 | 
			
		||||
      deadline,
 | 
			
		||||
      permitAmount
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
 | 
			
		||||
    if (!ownerPrivateKey) {
 | 
			
		||||
      throw new Error('INVALID_OWNER_PK');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      aDai
 | 
			
		||||
        .connect(spender.signer)
 | 
			
		||||
        .permit(owner.address, ZERO_ADDRESS, permitAmount, deadline, v, r, s)
 | 
			
		||||
    ).to.be.revertedWith('INVALID_SIGNATURE');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Tries to submit a permit with invalid owner', async () => {
 | 
			
		||||
    const {aDai, deployer, users} = testEnv;
 | 
			
		||||
    const owner = deployer;
 | 
			
		||||
    const spender = users[1];
 | 
			
		||||
 | 
			
		||||
    const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
 | 
			
		||||
    const expiration = MAX_UINT_AMOUNT;
 | 
			
		||||
    const nonce = (await aDai._nonces(owner.address)).toNumber();
 | 
			
		||||
    const permitAmount = '0';
 | 
			
		||||
    const msgParams = buildPermitParams(
 | 
			
		||||
      chainId,
 | 
			
		||||
      aDai.address,
 | 
			
		||||
      '1',
 | 
			
		||||
      await aDai.name(),
 | 
			
		||||
      owner.address,
 | 
			
		||||
      spender.address,
 | 
			
		||||
      nonce,
 | 
			
		||||
      expiration,
 | 
			
		||||
      permitAmount
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
 | 
			
		||||
    if (!ownerPrivateKey) {
 | 
			
		||||
      throw new Error('INVALID_OWNER_PK');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      aDai
 | 
			
		||||
        .connect(spender.signer)
 | 
			
		||||
        .permit(ZERO_ADDRESS, spender.address, expiration, permitAmount, v, r, s)
 | 
			
		||||
    ).to.be.revertedWith('INVALID_OWNER');
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user