Add ERC7674 (draft) (#5071)
Co-authored-by: Ernesto García <ernestognw@gmail.com> Co-authored-by: cairo <cairoeth@protonmail.com>
This commit is contained in:
142
test/token/ERC20/extensions/draft-ERC20TemporaryApproval.test.js
Normal file
142
test/token/ERC20/extensions/draft-ERC20TemporaryApproval.test.js
Normal file
@ -0,0 +1,142 @@
|
||||
const { ethers } = require('hardhat');
|
||||
const { expect } = require('chai');
|
||||
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
|
||||
const { max, min } = require('../../../helpers/math.js');
|
||||
|
||||
const { shouldBehaveLikeERC20 } = require('../ERC20.behavior.js');
|
||||
|
||||
const name = 'My Token';
|
||||
const symbol = 'MTKN';
|
||||
const initialSupply = 100n;
|
||||
|
||||
async function fixture() {
|
||||
// this.accounts is used by shouldBehaveLikeERC20
|
||||
const accounts = await ethers.getSigners();
|
||||
const [holder, recipient, other] = accounts;
|
||||
|
||||
const token = await ethers.deployContract('$ERC20TemporaryApproval', [name, symbol]);
|
||||
await token.$_mint(holder, initialSupply);
|
||||
|
||||
const spender = await ethers.deployContract('$Address');
|
||||
const batch = await ethers.deployContract('BatchCaller');
|
||||
const getter = await ethers.deployContract('ERC20GetterHelper');
|
||||
|
||||
return { accounts, holder, recipient, other, token, spender, batch, getter };
|
||||
}
|
||||
|
||||
describe('ERC20TemporaryApproval', function () {
|
||||
beforeEach(async function () {
|
||||
Object.assign(this, await loadFixture(fixture));
|
||||
});
|
||||
|
||||
shouldBehaveLikeERC20(initialSupply);
|
||||
|
||||
describe('setting and spending temporary allowance', function () {
|
||||
beforeEach(async function () {
|
||||
await this.token.connect(this.holder).transfer(this.batch, initialSupply);
|
||||
});
|
||||
|
||||
for (let {
|
||||
description,
|
||||
persistentAllowance,
|
||||
temporaryAllowance,
|
||||
amount,
|
||||
temporaryExpected,
|
||||
persistentExpected,
|
||||
} of [
|
||||
{ description: 'can set temporary allowance', temporaryAllowance: 42n },
|
||||
{
|
||||
description: 'can set temporary allowance on top of persistent allowance',
|
||||
temporaryAllowance: 42n,
|
||||
persistentAllowance: 17n,
|
||||
},
|
||||
{ description: 'support allowance overflow', temporaryAllowance: ethers.MaxUint256, persistentAllowance: 17n },
|
||||
{ description: 'consuming temporary allowance alone', temporaryAllowance: 42n, amount: 2n },
|
||||
{
|
||||
description: 'fallback to persistent allowance if temporary allowance is not sufficient',
|
||||
temporaryAllowance: 42n,
|
||||
persistentAllowance: 17n,
|
||||
amount: 50n,
|
||||
},
|
||||
{
|
||||
description: 'do not reduce infinite temporary allowance #1',
|
||||
temporaryAllowance: ethers.MaxUint256,
|
||||
amount: 50n,
|
||||
temporaryExpected: ethers.MaxUint256,
|
||||
},
|
||||
{
|
||||
description: 'do not reduce infinite temporary allowance #2',
|
||||
temporaryAllowance: 17n,
|
||||
persistentAllowance: ethers.MaxUint256,
|
||||
amount: 50n,
|
||||
temporaryExpected: ethers.MaxUint256,
|
||||
persistentExpected: ethers.MaxUint256,
|
||||
},
|
||||
]) {
|
||||
persistentAllowance ??= 0n;
|
||||
temporaryAllowance ??= 0n;
|
||||
amount ??= 0n;
|
||||
temporaryExpected ??= min(persistentAllowance + temporaryAllowance - amount, ethers.MaxUint256);
|
||||
persistentExpected ??= persistentAllowance - max(amount - temporaryAllowance, 0n);
|
||||
|
||||
it(description, async function () {
|
||||
await expect(
|
||||
this.batch.execute(
|
||||
[
|
||||
persistentAllowance && {
|
||||
target: this.token,
|
||||
value: 0n,
|
||||
data: this.token.interface.encodeFunctionData('approve', [this.spender.target, persistentAllowance]),
|
||||
},
|
||||
temporaryAllowance && {
|
||||
target: this.token,
|
||||
value: 0n,
|
||||
data: this.token.interface.encodeFunctionData('temporaryApprove', [
|
||||
this.spender.target,
|
||||
temporaryAllowance,
|
||||
]),
|
||||
},
|
||||
amount && {
|
||||
target: this.spender,
|
||||
value: 0n,
|
||||
data: this.spender.interface.encodeFunctionData('$functionCall', [
|
||||
this.token.target,
|
||||
this.token.interface.encodeFunctionData('transferFrom', [
|
||||
this.batch.target,
|
||||
this.recipient.address,
|
||||
amount,
|
||||
]),
|
||||
]),
|
||||
},
|
||||
{
|
||||
target: this.getter,
|
||||
value: 0n,
|
||||
data: this.getter.interface.encodeFunctionData('allowance', [
|
||||
this.token.target,
|
||||
this.batch.target,
|
||||
this.spender.target,
|
||||
]),
|
||||
},
|
||||
].filter(Boolean),
|
||||
),
|
||||
)
|
||||
.to.emit(this.getter, 'ERC20Allowance')
|
||||
.withArgs(this.token, this.batch, this.spender, temporaryExpected);
|
||||
|
||||
expect(await this.token.allowance(this.batch, this.spender)).to.equal(persistentExpected);
|
||||
});
|
||||
}
|
||||
|
||||
it('reverts when the recipient is the zero address', async function () {
|
||||
await expect(this.token.connect(this.holder).temporaryApprove(ethers.ZeroAddress, 1n))
|
||||
.to.be.revertedWithCustomError(this.token, 'ERC20InvalidSpender')
|
||||
.withArgs(ethers.ZeroAddress);
|
||||
});
|
||||
|
||||
it('reverts when the token owner is the zero address', async function () {
|
||||
await expect(this.token.$_temporaryApprove(ethers.ZeroAddress, this.recipient, 1n))
|
||||
.to.be.revertedWithCustomError(this.token, 'ERC20InvalidApprover')
|
||||
.withArgs(ethers.ZeroAddress);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user