import { MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../../helpers/constants'; import { BUIDLEREVM_CHAINID } from '../../helpers/buidler-constants'; import { buildPermitParams, getSignatureFromTypedData } from '../../helpers/contracts-helpers'; import { expect } from 'chai'; import { ethers } from 'ethers'; import { makeSuite, TestEnv } from './helpers/make-suite'; import { DRE } from '../../helpers/misc-utils'; import { waitForTx } from '../../helpers/misc-utils'; import { _TypedDataEncoder } from 'ethers/lib/utils'; const { parseEther } = ethers.utils; makeSuite('AToken: Permit', (testEnv: TestEnv) => { it('Checks the domain separator', async () => { const { aDai } = testEnv; const separator = await aDai.DOMAIN_SEPARATOR(); const domain = { name: await aDai.name(), version: '1', chainId: DRE.network.config.chainId, verifyingContract: aDai.address, }; const domainSeparator = _TypedDataEncoder.hashDomain(domain); expect(separator).to.be.equal(domainSeparator, 'Invalid domain separator'); }); it('Get aDAI for tests', async () => { const { dai, pool, deployer } = 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 = DRE.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 = DRE.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 = DRE.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 = DRE.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 = DRE.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 = DRE.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 = DRE.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'); }); });