mirror of
https://github.com/vacp2p/minime.git
synced 2026-01-09 21:48:02 -05:00
add tests for permit
This commit is contained in:
committed by
r4bbit
parent
a522ebb09d
commit
d7f0d85921
25
.gas-report
25
.gas-report
@@ -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 |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user