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
|
// @ts-ignore
|
||||||
import {accounts} from './test-wallets.js';
|
import {accounts} from './test-wallets.js';
|
||||||
import {eEthereumNetwork} from './helpers/types';
|
import {eEthereumNetwork} from './helpers/types';
|
||||||
|
import { BUIDLEREVM_CHAINID, COVERAGE_CHAINID } from './helpers/constants';
|
||||||
|
|
||||||
usePlugin('@nomiclabs/buidler-ethers');
|
usePlugin('@nomiclabs/buidler-ethers');
|
||||||
usePlugin('buidler-typechain');
|
usePlugin('buidler-typechain');
|
||||||
usePlugin('solidity-coverage');
|
usePlugin('solidity-coverage');
|
||||||
usePlugin('@nomiclabs/buidler-waffle');
|
usePlugin('@nomiclabs/buidler-waffle');
|
||||||
usePlugin('@nomiclabs/buidler-etherscan');
|
usePlugin('@nomiclabs/buidler-etherscan');
|
||||||
usePlugin('buidler-gas-reporter');
|
|
||||||
|
|
||||||
const DEFAULT_BLOCK_GAS_LIMIT = 10000000;
|
const DEFAULT_BLOCK_GAS_LIMIT = 10000000;
|
||||||
const DEFAULT_GAS_PRICE = 10;
|
const DEFAULT_GAS_PRICE = 10;
|
||||||
|
@ -59,6 +59,7 @@ const config: any = {
|
||||||
networks: {
|
networks: {
|
||||||
coverage: {
|
coverage: {
|
||||||
url: 'http://localhost:8555',
|
url: 'http://localhost:8555',
|
||||||
|
chainId: COVERAGE_CHAINID,
|
||||||
},
|
},
|
||||||
kovan: getCommonNetworkConfig(eEthereumNetwork.kovan, 42),
|
kovan: getCommonNetworkConfig(eEthereumNetwork.kovan, 42),
|
||||||
ropsten: getCommonNetworkConfig(eEthereumNetwork.ropsten, 3),
|
ropsten: getCommonNetworkConfig(eEthereumNetwork.ropsten, 3),
|
||||||
|
@ -68,7 +69,7 @@ const config: any = {
|
||||||
blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT,
|
blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT,
|
||||||
gas: DEFAULT_BLOCK_GAS_LIMIT,
|
gas: DEFAULT_BLOCK_GAS_LIMIT,
|
||||||
gasPrice: 8000000000,
|
gasPrice: 8000000000,
|
||||||
chainId: 31337,
|
chainId: BUIDLEREVM_CHAINID,
|
||||||
throwOnTransactionFailures: true,
|
throwOnTransactionFailures: true,
|
||||||
throwOnCallFailures: true,
|
throwOnCallFailures: true,
|
||||||
accounts: accounts.map(({secretKey, balance}: {secretKey: string; balance: string}) => ({
|
accounts: accounts.map(({secretKey, balance}: {secretKey: string; balance: string}) => ({
|
||||||
|
|
|
@ -29,9 +29,16 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
||||||
|
|
||||||
mapping(address => uint256) private _scaledRedirectedBalances;
|
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;
|
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 {
|
modifier onlyLendingPool {
|
||||||
require(msg.sender == address(POOL), Errors.CALLER_MUST_BE_LENDING_POOL);
|
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 tokenName,
|
||||||
string calldata tokenSymbol
|
string calldata tokenSymbol
|
||||||
) external virtual initializer {
|
) 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);
|
_setName(tokenName);
|
||||||
_setSymbol(tokenSymbol);
|
_setSymbol(tokenSymbol);
|
||||||
_setDecimals(underlyingAssetDecimals);
|
_setDecimals(underlyingAssetDecimals);
|
||||||
|
@ -187,6 +209,42 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
||||||
return amount;
|
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(
|
function _transfer(
|
||||||
address from,
|
address from,
|
||||||
address to,
|
address to,
|
||||||
|
|
|
@ -8,12 +8,16 @@ import {
|
||||||
IReserveParams,
|
IReserveParams,
|
||||||
tEthereumAddress,
|
tEthereumAddress,
|
||||||
iBasicDistributionParams,
|
iBasicDistributionParams,
|
||||||
|
eEthereumNetwork,
|
||||||
} from './types';
|
} from './types';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import {getParamPerPool} from './contracts-helpers';
|
import {getParamPerPool, getParamPerNetwork} from './contracts-helpers';
|
||||||
|
|
||||||
export const TEST_SNAPSHOT_ID = '0x1';
|
export const TEST_SNAPSHOT_ID = '0x1';
|
||||||
|
|
||||||
|
export const BUIDLEREVM_CHAINID = 31337;
|
||||||
|
export const COVERAGE_CHAINID = 1337;
|
||||||
|
|
||||||
// ----------------
|
// ----------------
|
||||||
// MATH
|
// MATH
|
||||||
// ----------------
|
// ----------------
|
||||||
|
@ -531,3 +535,18 @@ export const getFeeDistributionParamsCommon = (
|
||||||
percentages,
|
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 {StableDebtToken} from '../types/StableDebtToken';
|
||||||
import {VariableDebtToken} from '../types/VariableDebtToken';
|
import {VariableDebtToken} from '../types/VariableDebtToken';
|
||||||
import {MockSwapAdapter} from '../types/MockSwapAdapter';
|
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) => {
|
export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => {
|
||||||
const currentNetwork = BRE.network.name;
|
const currentNetwork = BRE.network.name;
|
||||||
|
@ -431,10 +433,14 @@ const linkBytecode = (artifact: Artifact, libraries: any) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getParamPerNetwork = <T>(
|
export const getParamPerNetwork = <T>(
|
||||||
{kovan, ropsten, main}: iParamsPerNetwork<T>,
|
{kovan, ropsten, main, buidlerevm, coverage}: iParamsPerNetwork<T>,
|
||||||
network: eEthereumNetwork
|
network: eEthereumNetwork
|
||||||
) => {
|
) => {
|
||||||
switch (network) {
|
switch (network) {
|
||||||
|
case eEthereumNetwork.coverage:
|
||||||
|
return coverage;
|
||||||
|
case eEthereumNetwork.buidlerevm:
|
||||||
|
return buidlerevm;
|
||||||
case eEthereumNetwork.kovan:
|
case eEthereumNetwork.kovan:
|
||||||
return kovan;
|
return kovan;
|
||||||
case eEthereumNetwork.ropsten:
|
case eEthereumNetwork.ropsten:
|
||||||
|
@ -471,3 +477,59 @@ export const convertToCurrencyUnits = async (tokenAddress: string, amount: strin
|
||||||
const amountInCurrencyUnits = new BigNumber(amount).div(currencyUnit);
|
const amountInCurrencyUnits = new BigNumber(amount).div(currencyUnit);
|
||||||
return amountInCurrencyUnits.toFixed();
|
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',
|
kovan = 'kovan',
|
||||||
ropsten = 'ropsten',
|
ropsten = 'ropsten',
|
||||||
main = 'main',
|
main = 'main',
|
||||||
|
coverage = 'coverage'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AavePools {
|
export enum AavePools {
|
||||||
|
@ -251,6 +252,8 @@ export interface IMarketRates {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface iParamsPerNetwork<T> {
|
export interface iParamsPerNetwork<T> {
|
||||||
|
[eEthereumNetwork.coverage]: T;
|
||||||
|
[eEthereumNetwork.buidlerevm]: T;
|
||||||
[eEthereumNetwork.kovan]: T;
|
[eEthereumNetwork.kovan]: T;
|
||||||
[eEthereumNetwork.ropsten]: T;
|
[eEthereumNetwork.ropsten]: T;
|
||||||
[eEthereumNetwork.main]: 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-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-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-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:coverage": "buidler coverage --network coverage",
|
||||||
"dev:deployment": "buidler dev-deployment",
|
"dev:deployment": "buidler dev-deployment",
|
||||||
"dev:deployExample": "buidler deploy-Example",
|
"dev:deployExample": "buidler deploy-Example",
|
||||||
|
@ -58,7 +59,9 @@
|
||||||
"tslint-config-prettier": "^1.18.0",
|
"tslint-config-prettier": "^1.18.0",
|
||||||
"tslint-plugin-prettier": "^2.3.0",
|
"tslint-plugin-prettier": "^2.3.0",
|
||||||
"typechain": "2.0.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": {
|
"husky": {
|
||||||
"hooks": {
|
"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