add tests for permit

This commit is contained in:
Ricardo Guilherme Schmidt
2023-09-26 02:28:17 -03:00
committed by r4bbit
parent a522ebb09d
commit d7f0d85921
3 changed files with 276 additions and 13 deletions

View File

@@ -3,27 +3,30 @@
| Deployment Cost | Deployment Size | | | | |
| 2149485 | 12251 | | | | |
| Function Name | min | avg | median | max | # calls |
| allowance | 0 | 62 | 0 | 808 | 13 |
| DOMAIN_SEPARATOR | 365 | 365 | 365 | 365 | 10 |
| allowance | 0 | 202 | 0 | 808 | 16 |
| approve | 0 | 15064 | 14767 | 31771 | 10 |
| approveAndCall | 0 | 31232 | 0 | 93698 | 3 |
| balanceOf | 0 | 550 | 0 | 2731 | 63 |
| balanceOf | 0 | 657 | 0 | 2731 | 67 |
| balanceOfAt | 0 | 905 | 0 | 4027 | 78 |
| changeController | 0 | 593 | 0 | 3558 | 6 |
| claimTokens | 9537 | 41267 | 57132 | 57132 | 3 |
| controller | 0 | 0 | 0 | 0 | 9 |
| decimals | 0 | 0 | 0 | 0 | 9 |
| controller | 0 | 0 | 0 | 0 | 10 |
| decimals | 0 | 0 | 0 | 0 | 10 |
| destroyTokens | 2286 | 5185 | 4288 | 8983 | 3 |
| enableTransfers | 0 | 0 | 0 | 0 | 3 |
| generateTokens | 0 | 14015 | 0 | 95885 | 62 |
| name | 0 | 0 | 0 | 0 | 9 |
| parentSnapShotBlock | 0 | 0 | 0 | 0 | 10 |
| parentToken | 0 | 0 | 0 | 0 | 10 |
| generateTokens | 0 | 16532 | 0 | 95885 | 64 |
| name | 0 | 0 | 0 | 0 | 10 |
| nonces | 632 | 1832 | 2632 | 2632 | 5 |
| parentSnapShotBlock | 0 | 0 | 0 | 0 | 11 |
| parentToken | 0 | 0 | 0 | 0 | 11 |
| permit | 689 | 39800 | 53934 | 58434 | 10 |
| receive | 7960 | 7979 | 7979 | 7998 | 2 |
| symbol | 0 | 0 | 0 | 0 | 9 |
| totalSupply | 0 | 286 | 0 | 2480 | 22 |
| symbol | 0 | 0 | 0 | 0 | 10 |
| totalSupply | 0 | 274 | 0 | 2480 | 23 |
| totalSupplyAt | 0 | 873 | 0 | 3659 | 17 |
| transfer | 526 | 39283 | 50395 | 93092 | 20 |
| transferFrom | 0 | 16834 | 3495 | 66574 | 7 |
| transferFrom | 0 | 19678 | 3495 | 66574 | 11 |

View File

@@ -110,9 +110,19 @@ DestroyTokensTest:testDeployment() (gas: 45664)
DestroyTokensTest:testDestroyTokens() (gas: 124942)
GenerateTokensTest:testDeployment() (gas: 45619)
GenerateTokensTest:testGenerateTokens() (gas: 114621)
GenerateTokensTest:test_RevertWhen_SenderIsNotController() (gas: 14930)
GenerateTokensTest:testRevertWhen_SenderIsNotController() (gas: 14930)
MiniMeTokenTest:testDeployment() (gas: 45664)
ReentrancyTest:testAttack() (gas: 229482)
TestPermit:testDeployment() (gas: 45858)
TestPermit:testInvalidAllowance() (gas: 84397)
TestPermit:testInvalidBalance() (gas: 67478)
TestPermit:testPermit() (gas: 84644)
TestPermit:testRevertExpiredPermit() (gas: 27953)
TestPermit:testRevertInvalidNonce() (gas: 57362)
TestPermit:testRevertInvalidSigner() (gas: 56145)
TestPermit:testRevertSignatureReplay() (gas: 96746)
TestPermit:testTransferFromLimitedPermit() (gas: 216308)
TestPermit:testTransferFromMaxPermit() (gas: 236247)
TransferTest:testDeployment() (gas: 45880)
TransferTest:testTransfer() (gas: 201303)
>>>>>>> d658aa3 (Make MiniMe implement ERC2612)

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import { Test } from "forge-std/Test.sol";
import { Test, console } from "forge-std/Test.sol";
import { Deploy } from "../script/Deploy.s.sol";
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
@@ -1163,3 +1163,253 @@ contract TestSnapshotReads is MiniMeTokenTest {
);
}
}
import {
ERC2612InvalidSigner,
ERC2612ExpiredSignature,
ECDSA,
NotEnoughBalance,
NotEnoughAllowance
} from "../contracts/MiniMeBase.sol";
contract TestPermit is MiniMeTokenTest {
SigUtils internal sigUtils;
uint256 internal ownerPrivateKey = 0xA11CE;
uint256 internal spenderPrivateKey = 0xB0B;
address internal owner = vm.addr(ownerPrivateKey);
address internal spender = vm.addr(spenderPrivateKey);
function setUp() public virtual override {
MiniMeTokenTest.setUp();
sigUtils = new SigUtils(minimeToken.DOMAIN_SEPARATOR());
}
function testPermit() public {
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: 1e18,
nonce: 0,
deadline: block.timestamp + 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
minimeToken.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
assertEq(minimeToken.allowance(owner, spender), 1e18);
assertEq(minimeToken.nonces(owner), 1);
}
function testRevertExpiredPermit() public {
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: 1e18,
nonce: minimeToken.nonces(owner),
deadline: block.timestamp - 1 seconds
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
vm.expectRevert(abi.encodeWithSelector(ERC2612ExpiredSignature.selector, permit.deadline));
minimeToken.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
}
function testRevertInvalidSigner() public {
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: 1e18,
nonce: minimeToken.nonces(owner),
deadline: block.timestamp + 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(spenderPrivateKey, digest); // spender signs owner's approval
vm.expectRevert(
abi.encodeWithSelector(ERC2612InvalidSigner.selector, ECDSA.recover(digest, v, r, s), permit.owner)
);
minimeToken.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
}
function testRevertInvalidNonce() public {
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: 1e18,
nonce: 1, // owner nonce stored on-chain is 0
deadline: block.timestamp + 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
SigUtils.Permit memory permitNeeded = SigUtils.Permit({
owner: owner,
spender: spender,
value: 1e18,
nonce: 0, // owner nonce stored on-chain is 0
deadline: block.timestamp + 1 days
});
bytes32 digestNeeded = sigUtils.getTypedDataHash(permitNeeded);
vm.expectRevert(
abi.encodeWithSelector(ERC2612InvalidSigner.selector, ECDSA.recover(digestNeeded, v, r, s), permit.owner)
);
minimeToken.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
}
function testRevertSignatureReplay() public {
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: 1e18,
nonce: minimeToken.nonces(owner),
deadline: block.timestamp + 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
minimeToken.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
SigUtils.Permit memory permitNeeded = SigUtils.Permit({
owner: owner,
spender: spender,
value: 1e18,
nonce: minimeToken.nonces(owner),
deadline: block.timestamp + 1 days
});
bytes32 digestNeeded = sigUtils.getTypedDataHash(permitNeeded);
vm.expectRevert(
abi.encodeWithSelector(ERC2612InvalidSigner.selector, ECDSA.recover(digestNeeded, v, r, s), permit.owner)
);
minimeToken.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
}
function testTransferFromLimitedPermit() public {
_generateTokens(owner, 1e18);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: 1e18,
nonce: 0,
deadline: block.timestamp + 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
minimeToken.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
vm.prank(spender);
minimeToken.transferFrom(owner, spender, 1e18);
assertEq(minimeToken.balanceOf(owner), 0);
assertEq(minimeToken.balanceOf(spender), 1e18);
assertEq(minimeToken.allowance(owner, spender), 0);
}
function testTransferFromMaxPermit() public {
_generateTokens(owner, 1e18);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: type(uint256).max,
nonce: 0,
deadline: block.timestamp + 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
minimeToken.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
vm.prank(spender);
minimeToken.transferFrom(owner, spender, 1e18);
assertEq(minimeToken.balanceOf(owner), 0);
assertEq(minimeToken.balanceOf(spender), 1e18);
assertEq(minimeToken.allowance(owner, spender), type(uint256).max - 1e18);
}
function testInvalidAllowance() public {
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: 5e17, // approve only 0.5 tokens
nonce: 0,
deadline: block.timestamp + 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
minimeToken.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
vm.prank(spender);
vm.expectRevert(NotEnoughAllowance.selector);
minimeToken.transferFrom(owner, spender, 1e18); // attempt to transfer 1 token
}
function testInvalidBalance() public {
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: 2e18, // approve 2 tokens
nonce: 0,
deadline: block.timestamp + 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
minimeToken.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
vm.prank(spender);
vm.expectRevert(NotEnoughBalance.selector);
minimeToken.transferFrom(owner, spender, 2e18); // attempt to transfer 2 tokens (owner only owns 1)
}
}
contract SigUtils {
bytes32 internal DOMAIN_SEPARATOR;
constructor(bytes32 _DOMAIN_SEPARATOR) {
DOMAIN_SEPARATOR = _DOMAIN_SEPARATOR;
}
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
struct Permit {
address owner;
address spender;
uint256 value;
uint256 nonce;
uint256 deadline;
}
// computes the hash of a permit
function getStructHash(Permit memory _permit) internal pure returns (bytes32) {
return keccak256(
abi.encode(PERMIT_TYPEHASH, _permit.owner, _permit.spender, _permit.value, _permit.nonce, _permit.deadline)
);
}
// computes the hash of the fully encoded EIP-712 message for the domain, which can be used to recover the signer
function getTypedDataHash(Permit memory _permit) public view returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, getStructHash(_permit)));
}
}