From bd7b7e2c756432da0fe9eda1622efe14a24edb94 Mon Sep 17 00:00:00 2001 From: Philippe Gonday Date: Tue, 17 Dec 2024 11:51:17 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=85(test)=20bt-235=20add=20Permit=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contracts/utils/EIP712Upgradeable.sol | 64 +----------- test/token/token-permit.ts | 137 +++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 65 deletions(-) diff --git a/contracts/utils/EIP712Upgradeable.sol b/contracts/utils/EIP712Upgradeable.sol index 0d46db44..2168a9ba 100644 --- a/contracts/utils/EIP712Upgradeable.sol +++ b/contracts/utils/EIP712Upgradeable.sol @@ -1,65 +1,5 @@ -// SPDX-License-Identifier: GPL-3.0 -// -// :+#####%%%%%%%%%%%%%%+ -// .-*@@@%+.:+%@@@@@%%#***%@@%= -// :=*%@@@#=. :#@@% *@@@%= -// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- -// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. -// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ -// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- -// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: -// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. -// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. -// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ -// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- -// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: -// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- -// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- -// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# -// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- -// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: -// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: -// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. -// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. -// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. -// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. -// @@@@@@+. +@@*. .+@@@@@%=. -// -@@@@@= =@@%: -#@@@@%+. -// +@@@@@. =@@@= .+@@@@@*: -// #@@@@#:%@@#. :*@@@@#- -// @@@@@%@@@= :#@@@@+. -// :@@@@@@@#.:#@@@%- -// +@@@@@@-.*@@@*: -// #@@@@#.=@@@+. -// @@@@+-%@%= -// :@@@#%@%= -// +@@@@%- -// :#%%= -// - -/** - * NOTICE - * - * The T-REX software is licensed under a proprietary license or the GPL v.3. - * If you choose to receive it under the GPL v.3 license, the following applies: - * T-REX is a suite of smart contracts implementing the ERC-3643 standard and - * developed by Tokeny to manage and transfer financial assets on EVM blockchains - * - * Copyright (C) 2023, Tokeny sàrl. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/EIP712.sol) pragma solidity 0.8.27; diff --git a/test/token/token-permit.ts b/test/token/token-permit.ts index ecef610b..66d156a9 100644 --- a/test/token/token-permit.ts +++ b/test/token/token-permit.ts @@ -1,6 +1,137 @@ -import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { loadFixture, time } from '@nomicfoundation/hardhat-network-helpers'; import { expect } from 'chai'; import { ethers } from 'hardhat'; -import { deployFullSuiteFixture, deploySuiteWithModularCompliancesFixture } from '../fixtures/deploy-full-suite.fixture'; +import { deployFullSuiteFixture } from '../fixtures/deploy-full-suite.fixture'; +import { Token } from '../../typechain-types'; +import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers'; -describe('Token - Permit', () => {}); +describe('Token - Permit', () => { + const value = 42n; + const nonce = 0n; + const maxDeadline = ethers.MaxUint256; + + async function getDomain(token: Token) { + return { + chainId: (await ethers.provider.getNetwork()).chainId, + verifyingContract: token.target.toString(), + name: await token.name(), + version: '1', + }; + } + + async function buildData(token: Token, owner: SignerWithAddress, spender: SignerWithAddress, deadline = maxDeadline) { + const domain = await getDomain(token); + const types = { + Permit: [ + { name: 'owner', type: 'address' }, + { name: 'spender', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }; + const message = { + owner: owner.address, + spender: spender.address, + value, + nonce, + deadline, + }; + + return { domain, types, message }; + } + + describe('Initial state', () => { + it('initial nonce is 0', async () => { + const { + suite: { token }, + accounts: { aliceWallet, bobWallet, anotherWallet }, + } = await loadFixture(deployFullSuiteFixture); + + expect(await token.nonces(aliceWallet)).to.equal(0n); + expect(await token.nonces(bobWallet)).to.equal(0n); + expect(await token.nonces(anotherWallet)).to.equal(0n); + }); + + it('domain separator', async () => { + const { + suite: { token }, + } = await loadFixture(deployFullSuiteFixture); + + const hashedDomain = ethers.TypedDataEncoder.hashDomain(await getDomain(token)); + + expect(await token.DOMAIN_SEPARATOR()).to.equal(hashedDomain); + }); + }); + + describe('Permit', () => { + it('accepts owner signature', async () => { + const { + suite: { token }, + accounts: { aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + const { v, r, s } = await buildData(token, aliceWallet, bobWallet) + .then(({ domain, types, message }) => aliceWallet.signTypedData(domain, types, message)) + .then(ethers.Signature.from); + + await token.permit(aliceWallet, bobWallet, value, maxDeadline, v, r, s); + + expect(await token.nonces(aliceWallet)).to.equal(1n); + expect(await token.allowance(aliceWallet, bobWallet)).to.equal(value); + }); + + it('rejects reused signature', async () => { + const { + suite: { token }, + accounts: { aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + const { v, r, s, serialized } = await buildData(token, aliceWallet, bobWallet) + .then(({ domain, types, message }) => aliceWallet.signTypedData(domain, types, message)) + .then(ethers.Signature.from); + + await token.permit(aliceWallet, bobWallet, value, maxDeadline, v, r, s); + + const recovered = await buildData(token, aliceWallet, bobWallet).then(({ domain, types, message }) => + ethers.verifyTypedData(domain, types, { ...message, nonce: nonce + 1n, deadline: maxDeadline }, serialized), + ); + + await expect(token.permit(aliceWallet, bobWallet, value, maxDeadline, v, r, s)) + .to.be.revertedWithCustomError(token, 'ERC2612InvalidSigner') + .withArgs(recovered, aliceWallet); + }); + + it('rejects other signature', async () => { + const { + suite: { token }, + accounts: { aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + const { v, r, s } = await buildData(token, aliceWallet, bobWallet) + .then(({ domain, types, message }) => bobWallet.signTypedData(domain, types, message)) + .then(ethers.Signature.from); + + await expect(token.permit(aliceWallet, bobWallet, value, maxDeadline, v, r, s)) + .to.be.revertedWithCustomError(token, 'ERC2612InvalidSigner') + .withArgs(bobWallet, aliceWallet); + }); + + it('rejects expired permit', async () => { + const { + suite: { token }, + accounts: { aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + const deadline = (await time.latest().then(ethers.toBigInt)) - BigInt(time.duration.weeks(1)); + + const { v, r, s } = await buildData(token, aliceWallet, bobWallet, deadline) + .then(({ domain, types, message }) => aliceWallet.signTypedData(domain, types, message)) + .then(ethers.Signature.from); + + await expect(token.permit(aliceWallet, bobWallet, value, deadline, v, r, s)) + .to.be.revertedWithCustomError(token, 'ERC2612ExpiredSignature') + .withArgs(deadline); + }); + }); +});