ghost-dao-contracts/test/tokens/Permit.t.sol
Uncle Fatso 46b33b4c75
initial push for smart-contracts
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-04-28 14:17:04 +03:00

306 lines
8.1 KiB
Solidity

pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "../../src/mocks/SigUtils.sol";
import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol";
abstract contract ERC20PermitTest is Test {
SigUtils sigUtils;
ERC20Permit permitToken;
address owner;
address spender;
uint256 permitAmount;
uint256 maxPermitAmount;
uint256 constant ownerPrivateKey = 0xA11CE;
uint256 constant spenderPrivateKey = 0xB0B;
function initializePermit(
address token,
uint256 amount,
uint256 maxAmount
) public {
permitAmount = amount;
maxPermitAmount = maxAmount;
permitToken = ERC20Permit(token);
sigUtils = new SigUtils(permitToken.DOMAIN_SEPARATOR());
owner = vm.addr(ownerPrivateKey);
spender = vm.addr(spenderPrivateKey);
}
function test_permit_initialNonceIsZero() public view {
assertEq(permitToken.nonces(owner), 0);
assertEq(permitToken.nonces(spender), 0);
}
function test_permit_acceptsOwnerSignature() public {
_mintPermitTokens(owner, permitAmount);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: permitAmount,
nonce: 0,
deadline: 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
permitToken.permit(
permit.owner,
permit.spender,
permit.value,
permit.deadline,
v,
r,
s
);
assertEq(permitToken.allowance(owner, spender), permitAmount);
assertEq(permitToken.nonces(owner), 1);
}
function test_permit_expiredPermit() public {
_mintPermitTokens(owner, permitAmount);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: permitAmount,
nonce: permitToken.nonces(owner),
deadline: 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
vm.warp(1 days + 1 seconds); // fast forward one second past the deadline
vm.expectRevert();
permitToken.permit(
permit.owner,
permit.spender,
permit.value,
permit.deadline,
v,
r,
s
);
}
function test_permit_invalidSigner() public {
_mintPermitTokens(owner, permitAmount);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: permitAmount,
nonce: permitToken.nonces(owner),
deadline: 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(spenderPrivateKey, digest); // spender signs owner's approval
vm.expectRevert();
permitToken.permit(
permit.owner,
permit.spender,
permit.value,
permit.deadline,
v,
r,
s
);
}
function test_permit_invalidNonce() public {
_mintPermitTokens(owner, permitAmount);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: permitAmount,
nonce: 1, // owner nonce stored on-chain is 0
deadline: 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
vm.expectRevert();
permitToken.permit(
permit.owner,
permit.spender,
permit.value,
permit.deadline,
v,
r,
s
);
}
function test_permit_signatureReplay() public {
_mintPermitTokens(owner, permitAmount);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: permitAmount,
nonce: 0,
deadline: 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
permitToken.permit(
permit.owner,
permit.spender,
permit.value,
permit.deadline,
v,
r,
s
);
vm.expectRevert();
permitToken.permit(
permit.owner,
permit.spender,
permit.value,
permit.deadline,
v,
r,
s
);
}
function test_permit_transferFromLimitedPermit() public {
_mintPermitTokens(owner, permitAmount);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: permitAmount,
nonce: 0,
deadline: 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
permitToken.permit(
permit.owner,
permit.spender,
permit.value,
permit.deadline,
v,
r,
s
);
vm.prank(spender);
permitToken.transferFrom(owner, spender, permitAmount);
assertEq(permitToken.balanceOf(owner), 0);
assertEq(permitToken.balanceOf(spender), permitAmount);
assertEq(permitToken.allowance(owner, spender), 0);
}
function test_permit_transferFromMaxPermit() public {
_mintPermitTokens(owner, permitAmount);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: maxPermitAmount,
nonce: 0,
deadline: 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
permitToken.permit(
permit.owner,
permit.spender,
permit.value,
permit.deadline,
v,
r,
s
);
vm.prank(spender);
permitToken.transferFrom(owner, spender, permitAmount);
assertEq(permitToken.balanceOf(owner), 0);
assertEq(permitToken.balanceOf(spender), permitAmount);
assertEq(permitToken.allowance(owner, spender), maxPermitAmount);
}
function test_permit_invalidAllowance() public {
_mintPermitTokens(owner, permitAmount);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: 1,
nonce: 0,
deadline: 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
permitToken.permit(
permit.owner,
permit.spender,
permit.value,
permit.deadline,
v,
r,
s
);
vm.expectRevert();
vm.prank(spender);
permitToken.transferFrom(owner, spender, permitAmount);
}
function test_permit_ivnalidBalance() public {
_mintPermitTokens(owner, permitAmount);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: 2,
nonce: 0,
deadline: 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
permitToken.permit(
permit.owner,
permit.spender,
permit.value,
permit.deadline,
v,
r,
s
);
vm.expectRevert();
vm.prank(spender);
permitToken.transferFrom(owner, spender, permitAmount);
}
function _mintPermitTokens(address who, uint256 permitAmount) internal virtual;
}