From 9487742e0769d2c0954500582dfafc885d155731 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 5 Jan 2023 18:38:53 -0500 Subject: [PATCH 01/27] allowance & sig transfer tokenId support --- src/ERC721/AllowanceTransfer.sol | 46 ++++++++-------- src/ERC721/EIP712.sol | 1 + src/ERC721/Permit2.sol | 1 + src/ERC721/SignatureTransfer.sol | 21 ++++---- src/ERC721/interfaces/IAllowanceTransfer.sol | 57 ++++++++++---------- src/ERC721/interfaces/ISignatureTransfer.sol | 34 ++++++------ src/ERC721/libraries/Allowance.sol | 14 ++--- src/ERC721/libraries/Permit2Lib.sol | 1 + src/ERC721/libraries/PermitHash.sol | 14 ++--- 9 files changed, 97 insertions(+), 92 deletions(-) diff --git a/src/ERC721/AllowanceTransfer.sol b/src/ERC721/AllowanceTransfer.sol index 56c4cce0..c21d5caa 100644 --- a/src/ERC721/AllowanceTransfer.sol +++ b/src/ERC721/AllowanceTransfer.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import {ERC20} from "solmate/src/tokens/ERC20.sol"; +import {ERC721} from "solmate/src/tokens/ERC721.sol"; import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; import {PermitHash} from "./libraries/PermitHash.sol"; import {SignatureVerification} from "./libraries/SignatureVerification.sol"; @@ -12,21 +12,20 @@ import {Allowance} from "./libraries/Allowance.sol"; contract AllowanceTransfer is IAllowanceTransfer, EIP712 { using SignatureVerification for bytes; - using SafeTransferLib for ERC20; using PermitHash for PermitSingle; using PermitHash for PermitBatch; using Allowance for PackedAllowance; /// @notice Maps users to tokens to spender addresses and information about the approval on the token /// @dev Indexed in the order of token owner address, token address, spender address - /// @dev The stored word saves the allowed amount, expiration on the allowance, and nonce + /// @dev The stored word saves the allowed tokenId, expiration on the allowance, and nonce mapping(address => mapping(address => mapping(address => PackedAllowance))) public allowance; /// @inheritdoc IAllowanceTransfer - function approve(address token, address spender, uint160 amount, uint48 expiration) external { + function approve(address token, address spender, uint160 tokenId, uint48 expiration) external { PackedAllowance storage allowed = allowance[msg.sender][token][spender]; - allowed.updateAmountAndExpiration(amount, expiration); - emit Approval(msg.sender, token, spender, amount, expiration); + allowed.updateTokenIdAndExpiration(tokenId, expiration); + emit Approval(msg.sender, token, spender, tokenId, expiration); } /// @inheritdoc IAllowanceTransfer @@ -56,8 +55,8 @@ contract AllowanceTransfer is IAllowanceTransfer, EIP712 { } /// @inheritdoc IAllowanceTransfer - function transferFrom(address from, address to, uint160 amount, address token) external { - _transfer(from, to, amount, token); + function transferFrom(address from, address to, uint160 tokenId, address token) external { + _transfer(from, to, tokenId, token); } /// @inheritdoc IAllowanceTransfer @@ -66,31 +65,30 @@ contract AllowanceTransfer is IAllowanceTransfer, EIP712 { uint256 length = transferDetails.length; for (uint256 i = 0; i < length; ++i) { AllowanceTransferDetails memory transferDetail = transferDetails[i]; - _transfer(transferDetail.from, transferDetail.to, transferDetail.amount, transferDetail.token); + _transfer(transferDetail.from, transferDetail.to, transferDetail.tokenId, transferDetail.token); } } } /// @notice Internal function for transferring tokens using stored allowances /// @dev Will fail if the allowed timeframe has passed - function _transfer(address from, address to, uint160 amount, address token) private { + function _transfer(address from, address to, uint160 tokenId, address token) private { PackedAllowance storage allowed = allowance[from][token][msg.sender]; if (block.timestamp > allowed.expiration) revert AllowanceExpired(allowed.expiration); - uint256 maxAmount = allowed.amount; - if (maxAmount != type(uint160).max) { - if (amount > maxAmount) { - revert InsufficientAllowance(maxAmount); + uint256 permittedTokenId = allowed.tokenId; + if (permittedTokenId != type(uint160).max) { + if (permittedTokenId != tokenId) { + revert InsufficientAllowance(token, tokenId); } else { - unchecked { - allowed.amount = uint160(maxAmount) - amount; - } + // If a single tokenId has been approved, reset the permissions on the tokenId to be transferred + allowed.tokenId = 0; } } - // Transfer the tokens from the from address to the recipient. - ERC20(token).safeTransferFrom(from, to, amount); + // Transfer the token from the from address to the recipient. + ERC721(token).safeTransferFrom(from, to, tokenId); } /// @inheritdoc IAllowanceTransfer @@ -103,7 +101,7 @@ contract AllowanceTransfer is IAllowanceTransfer, EIP712 { address token = approvals[i].token; address spender = approvals[i].spender; - allowance[owner][token][spender].amount = 0; + allowance[owner][token][spender].tokenId = 0; emit Lockdown(owner, token, spender); } } @@ -125,19 +123,19 @@ contract AllowanceTransfer is IAllowanceTransfer, EIP712 { emit NonceInvalidation(msg.sender, token, spender, newNonce, oldNonce); } - /// @notice Sets the new values for amount, expiration, and nonce. + /// @notice Sets the new values for tokenId, expiration, and nonce. /// @dev Will check that the signed nonce is equal to the current nonce and then incrememnt the nonce value by 1. /// @dev Emits a Permit event. function _updateApproval(PermitDetails memory details, address owner, address spender) private { uint48 nonce = details.nonce; address token = details.token; - uint160 amount = details.amount; + uint160 tokenId = details.tokenId; uint48 expiration = details.expiration; PackedAllowance storage allowed = allowance[owner][token][spender]; if (allowed.nonce != nonce) revert InvalidNonce(); - allowed.updateAll(amount, expiration, nonce); - emit Permit(owner, token, spender, amount, expiration, nonce); + allowed.updateAll(tokenId, expiration, nonce); + emit Permit(owner, token, spender, tokenId, expiration, nonce); } } diff --git a/src/ERC721/EIP712.sol b/src/ERC721/EIP712.sol index bbd77f07..a4b171e9 100644 --- a/src/ERC721/EIP712.sol +++ b/src/ERC721/EIP712.sol @@ -10,6 +10,7 @@ contract EIP712 { bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; uint256 private immutable _CACHED_CHAIN_ID; + // TODO Update naming bytes32 private constant _HASHED_NAME = keccak256("Permit2"); bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); diff --git a/src/ERC721/Permit2.sol b/src/ERC721/Permit2.sol index 7249e40a..b0e4d945 100644 --- a/src/ERC721/Permit2.sol +++ b/src/ERC721/Permit2.sol @@ -6,6 +6,7 @@ import {AllowanceTransfer} from "./AllowanceTransfer.sol"; /// @notice Permit2 handles signature-based transfers in SignatureTransfer and allowance-based transfers in AllowanceTransfer. /// @dev Users must approve Permit2 before calling any of the transfer functions. +/// @dev It is recommended that you set operator permissions on Permit2 by calling `setApprovalForAll` for any underlying ERC721 token. contract Permit2 is SignatureTransfer, AllowanceTransfer { // Permit2 unifies the two contracts so users have maximal flexibility with their approval. } diff --git a/src/ERC721/SignatureTransfer.sol b/src/ERC721/SignatureTransfer.sol index c026553a..a97a2385 100644 --- a/src/ERC721/SignatureTransfer.sol +++ b/src/ERC721/SignatureTransfer.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.17; import {ISignatureTransfer} from "./interfaces/ISignatureTransfer.sol"; import {SignatureExpired, InvalidNonce} from "./PermitErrors.sol"; -import {ERC20} from "solmate/src/tokens/ERC20.sol"; +import {ERC721} from "solmate/src/tokens/ERC721.sol"; import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; import {SignatureVerification} from "./libraries/SignatureVerification.sol"; import {PermitHash} from "./libraries/PermitHash.sol"; @@ -11,7 +11,6 @@ import {EIP712} from "./EIP712.sol"; contract SignatureTransfer is ISignatureTransfer, EIP712 { using SignatureVerification for bytes; - using SafeTransferLib for ERC20; using PermitHash for PermitTransferFrom; using PermitHash for PermitBatchTransferFrom; @@ -55,16 +54,18 @@ contract SignatureTransfer is ISignatureTransfer, EIP712 { bytes32 dataHash, bytes calldata signature ) private { - uint256 requestedAmount = transferDetails.requestedAmount; + uint256 requestedTokenId = transferDetails.requestedTokenId; if (block.timestamp > permit.deadline) revert SignatureExpired(permit.deadline); - if (requestedAmount > permit.permitted.amount) revert InvalidAmount(permit.permitted.amount); + if (permit.permitted.tokenId != type(uint160).max && requestedTokenId != permit.permitted.tokenId) { + revert InvalidTokenId(permit.permitted.tokenId); + } _useUnorderedNonce(owner, permit.nonce); signature.verify(_hashTypedData(dataHash), owner); - ERC20(permit.permitted.token).safeTransferFrom(owner, transferDetails.to, requestedAmount); + ERC721(permit.permitted.token).safeTransferFrom(owner, transferDetails.to, requestedTokenId); } /// @inheritdoc ISignatureTransfer @@ -114,13 +115,15 @@ contract SignatureTransfer is ISignatureTransfer, EIP712 { unchecked { for (uint256 i = 0; i < numPermitted; ++i) { TokenPermissions memory permitted = permit.permitted[i]; - uint256 requestedAmount = transferDetails[i].requestedAmount; + uint256 requestedTokenId = transferDetails[i].requestedTokenId; - if (requestedAmount > permitted.amount) revert InvalidAmount(permitted.amount); + if (permitted.tokenId != type(uint160).max && requestedTokenId != permitted.tokenId) { + revert InvalidTokenId(permitted.tokenId); + } - if (requestedAmount != 0) { + if (requestedTokenId != 0) { // allow spender to specify which of the permitted tokens should be transferred - ERC20(permitted.token).safeTransferFrom(owner, transferDetails[i].to, requestedAmount); + ERC721(permitted.token).safeTransferFrom(owner, transferDetails[i].to, requestedTokenId); } } } diff --git a/src/ERC721/interfaces/IAllowanceTransfer.sol b/src/ERC721/interfaces/IAllowanceTransfer.sol index c3739b9d..e3312c06 100644 --- a/src/ERC721/interfaces/IAllowanceTransfer.sol +++ b/src/ERC721/interfaces/IAllowanceTransfer.sol @@ -2,16 +2,16 @@ pragma solidity ^0.8.17; /// @title AllowanceTransfer -/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts +/// @notice Handles ERC721 token permissions through signature based allowance setting and ERC721 token transfers by checking stored permissions /// @dev Requires user's token approval on the Permit2 contract interface IAllowanceTransfer { /// @notice Thrown when an allowance on a token has expired. - /// @param deadline The timestamp at which the allowed amount is no longer valid + /// @param deadline The timestamp at which the permissions on the token are no longer valid error AllowanceExpired(uint256 deadline); - /// @notice Thrown when an allowance on a token has been depleted. - /// @param amount The maximum amount allowed - error InsufficientAllowance(uint256 amount); + /// @notice Thrown when there is no allowance for a token. + /// @param token The address of the token and tokenId + error InsufficientAllowance(address token, uint256 tokenId); /// @notice Thrown when too many nonces are invalidated. error ExcessiveInvalidation(); @@ -23,7 +23,7 @@ interface IAllowanceTransfer { /// @notice Emits an event when the owner successfully sets permissions on a token for the spender. event Approval( - address indexed owner, address indexed token, address indexed spender, uint160 amount, uint48 expiration + address indexed owner, address indexed token, address indexed spender, uint160 tokenId, uint48 expiration ); /// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender. @@ -31,7 +31,7 @@ interface IAllowanceTransfer { address indexed owner, address indexed token, address indexed spender, - uint160 amount, + uint160 tokenId, uint48 expiration, uint48 nonce ); @@ -40,11 +40,12 @@ interface IAllowanceTransfer { event Lockdown(address indexed owner, address token, address spender); /// @notice The permit data for a token + /// @dev Setting tokenId to type(uint160).max would set operator approval on the spender. struct PermitDetails { // ERC20 token address address token; - // the maximum amount allowed to spend - uint160 amount; + // the tokenId allowed to spend + uint160 tokenId; // timestamp at which a spender's token allowances become invalid uint48 expiration; // an incrementing value indexed per owner,token,and spender for each signature @@ -73,10 +74,10 @@ interface IAllowanceTransfer { /// @notice The saved permissions /// @dev This info is saved per owner, per token, per spender and all signed over in the permit message - /// @dev Setting amount to type(uint160).max sets an unlimited approval + /// @dev Setting tokenId to type(uint160).max sets an operator approval. This means that the spender can transfer any tokenId you own in the collection. struct PackedAllowance { - // amount allowed - uint160 amount; + // tokenId allowed + uint160 tokenId; // permission expiry uint48 expiration; // an incrementing value indexed per owner,token,and spender for each signature @@ -97,34 +98,34 @@ interface IAllowanceTransfer { address from; // the recipient of the token address to; - // the amount of the token - uint160 amount; + // the tokenId of the token + uint160 tokenId; // the token to be transferred address token; } /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval. /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress] - /// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals. + /// @dev The packed slot holds the allowed tokenId, expiration at which the permissions on the tokenId is no longer valid, and current nonce thats updated on any signature based approvals. function allowance(address, address, address) external view returns (uint160, uint48, uint48); - /// @notice Approves the spender to use up to amount of the specified token up until the expiration + /// @notice Approves the spender to transfer the tokenId of the specified token up until the expiration /// @param token The token to approve /// @param spender The spender address to approve - /// @param amount The approved amount of the token + /// @param tokenId The approved tokenId of the token /// @param expiration The timestamp at which the approval is no longer valid /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve - /// @dev Setting amount to type(uint160).max sets an unlimited approval - function approve(address token, address spender, uint160 amount, uint48 expiration) external; + /// @dev Setting tokenId to type(uint160).max sets an operator approval + function approve(address token, address spender, uint160 tokenId, uint48 expiration) external; - /// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature + /// @notice Permit a spender to a given tokenId of the owners token via the owner's EIP-712 signature /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce /// @param owner The owner of the tokens being approved /// @param permitSingle Data signed over by the owner specifying the terms of approval /// @param signature The owner's signature over the permit data function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external; - /// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature + /// @notice Permit a spender to the signed tokenIds of the owners tokens via the owner's EIP-712 signature /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce /// @param owner The owner of the tokens being approved /// @param permitBatch Data signed over by the owner specifying the terms of approval @@ -134,16 +135,16 @@ interface IAllowanceTransfer { /// @notice Transfer approved tokens from one address to another /// @param from The address to transfer from /// @param to The address of the recipient - /// @param amount The amount of the token to transfer + /// @param tokenId The tokenId of the token to transfer /// @param token The token address to transfer - /// @dev Requires the from address to have approved at least the desired amount - /// of tokens to msg.sender. - function transferFrom(address from, address to, uint160 amount, address token) external; + /// @dev Requires the from address to have approved the desired tokenId or be an operator + /// of the token to msg.sender. + function transferFrom(address from, address to, uint160 tokenId, address token) external; /// @notice Transfer approved tokens in a batch - /// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers - /// @dev Requires the from addresses to have approved at least the desired amount - /// of tokens to msg.sender. + /// @param transferDetails Array of owners, recipients, tokenIds, and tokens for the transfers + /// @dev Requires the from addresses to have approved the desired tokenIds or be an operator + /// of the tokens to msg.sender. function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external; /// @notice Enables performing a "lockdown" of the sender's Permit2 identity diff --git a/src/ERC721/interfaces/ISignatureTransfer.sol b/src/ERC721/interfaces/ISignatureTransfer.sol index 8b76a3eb..3790b909 100644 --- a/src/ERC721/interfaces/ISignatureTransfer.sol +++ b/src/ERC721/interfaces/ISignatureTransfer.sol @@ -2,26 +2,26 @@ pragma solidity ^0.8.17; /// @title SignatureTransfer -/// @notice Handles ERC20 token transfers through signature based actions +/// @notice Handles ERC721 token transfers through signature based actions /// @dev Requires user's token approval on the Permit2 contract interface ISignatureTransfer { - /// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount - /// @param maxAmount The maximum amount a spender can request to transfer - error InvalidAmount(uint256 maxAmount); + /// @notice Thrown when the requested tokenId for a transfer is not the permitted tokenId + /// @param tokenId The valid tokenId a spender can request to transfer + error InvalidTokenId(uint256 tokenId); /// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred - /// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred + /// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request tokenId 0 to be transferred error LengthMismatch(); /// @notice Emits an event when the owner successfully invalidates an unordered nonce. event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask); - /// @notice The token and amount details for a transfer signed in the permit transfer signature + /// @notice The token and tokenId details for a transfer signed in the permit transfer signature struct TokenPermissions { // ERC20 token address address token; - // the maximum amount that can be spent - uint256 amount; + // the tokenId to transfer + uint256 tokenId; } /// @notice The signed permit message for a single token transfer @@ -34,20 +34,20 @@ interface ISignatureTransfer { } /// @notice Specifies the recipient address and amount for batched transfers. - /// @dev Recipients and amounts correspond to the index of the signed token permissions array. - /// @dev Reverts if the requested amount is greater than the permitted signed amount. + /// @dev Recipients and tokenIds correspond to the index of the signed token permissions array. + /// @dev Reverts if the requested tokenId is not the signed tokenId or if the user did not sign operator permissions. struct SignatureTransferDetails { // recipient address address to; - // spender requested amount - uint256 requestedAmount; + // spender requested tokenId + uint256 requestedTokenId; } /// @notice Used to reconstruct the signed permit message for multiple token transfers /// @dev Do not need to pass in spender address as it is required that it is msg.sender /// @dev Note that a user still signs over a spender address struct PermitBatchTransferFrom { - // the tokens and corresponding amounts permitted for a transfer + // the tokens and corresponding tokenIds permitted for a transfer TokenPermissions[] permitted; // a unique value for every token owner's signature to prevent signature replays uint256 nonce; @@ -63,7 +63,7 @@ interface ISignatureTransfer { function nonceBitmap(address, uint256) external view returns (uint256); /// @notice Transfers a token using a signed permit message - /// @dev Reverts if the requested amount is greater than the permitted signed amount + /// @dev Reverts if the requested tokenId is not the permitted signed tokenId or if the permitted signed tokenId is not the maximum /// @param permit The permit data signed over by the owner /// @param owner The owner of the tokens to transfer /// @param transferDetails The spender's requested transfer details for the permitted token @@ -78,7 +78,7 @@ interface ISignatureTransfer { /// @notice Transfers a token using a signed permit message /// @notice Includes extra data provided by the caller to verify signature over /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition - /// @dev Reverts if the requested amount is greater than the permitted signed amount + /// @dev Reverts if the requested tokenId is not the permitted signed tokenId or if the permitted signed tokenId is not the maximum /// @param permit The permit data signed over by the owner /// @param owner The owner of the tokens to transfer /// @param transferDetails The spender's requested transfer details for the permitted token @@ -97,7 +97,7 @@ interface ISignatureTransfer { /// @notice Transfers multiple tokens using a signed permit message /// @param permit The permit data signed over by the owner /// @param owner The owner of the tokens to transfer - /// @param transferDetails Specifies the recipient and requested amount for the token transfer + /// @param transferDetails Specifies the recipient and requested tokenId for the token transfer /// @param signature The signature to verify function permitTransferFrom( PermitBatchTransferFrom memory permit, @@ -111,7 +111,7 @@ interface ISignatureTransfer { /// @notice Includes extra data provided by the caller to verify signature over /// @param permit The permit data signed over by the owner /// @param owner The owner of the tokens to transfer - /// @param transferDetails Specifies the recipient and requested amount for the token transfer + /// @param transferDetails Specifies the recipient and requested tokenId for the token transfer /// @param witness Extra data to include when checking the user signature /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash /// @param signature The signature to verify diff --git a/src/ERC721/libraries/Allowance.sol b/src/ERC721/libraries/Allowance.sol index 671c9724..50134393 100644 --- a/src/ERC721/libraries/Allowance.sol +++ b/src/ERC721/libraries/Allowance.sol @@ -12,7 +12,7 @@ library Allowance { /// @dev If the inputted expiration is 0, the stored expiration is set to block.timestamp function updateAll( IAllowanceTransfer.PackedAllowance storage allowed, - uint160 amount, + uint160 tokenId, uint48 expiration, uint48 nonce ) internal { @@ -23,7 +23,7 @@ library Allowance { uint48 storedExpiration = expiration == BLOCK_TIMESTAMP_EXPIRATION ? uint48(block.timestamp) : expiration; - uint256 word = pack(amount, storedExpiration, storedNonce); + uint256 word = pack(tokenId, storedExpiration, storedNonce); assembly { sstore(allowed.slot, word) } @@ -31,18 +31,18 @@ library Allowance { /// @notice Sets the allowed amount and expiry of the spender's permissions on owner's token. /// @dev Nonce does not need to be incremented. - function updateAmountAndExpiration( + function updateTokenIdAndExpiration( IAllowanceTransfer.PackedAllowance storage allowed, - uint160 amount, + uint160 tokenId, uint48 expiration ) internal { // If the inputted expiration is 0, the allowance only lasts the duration of the block. allowed.expiration = expiration == 0 ? uint48(block.timestamp) : expiration; - allowed.amount = amount; + allowed.tokenId = tokenId; } /// @notice Computes the packed slot of the amount, expiration, and nonce that make up PackedAllowance - function pack(uint160 amount, uint48 expiration, uint48 nonce) internal pure returns (uint256 word) { - word = (uint256(nonce) << 208) | uint256(expiration) << 160 | amount; + function pack(uint160 tokenId, uint48 expiration, uint48 nonce) internal pure returns (uint256 word) { + word = (uint256(nonce) << 208) | uint256(expiration) << 160 | tokenId; } } diff --git a/src/ERC721/libraries/Permit2Lib.sol b/src/ERC721/libraries/Permit2Lib.sol index d3d9b9c7..6549bc67 100644 --- a/src/ERC721/libraries/Permit2Lib.sol +++ b/src/ERC721/libraries/Permit2Lib.sol @@ -11,6 +11,7 @@ import {SafeCast160} from "./SafeCast160.sol"; /// @title Permit2Lib /// @notice Enables efficient transfers and EIP-2612/DAI /// permits for any token by falling back to Permit2. +// TODO enable for erc721 library Permit2Lib { using SafeCast160 for uint256; /*////////////////////////////////////////////////////////////// diff --git a/src/ERC721/libraries/PermitHash.sol b/src/ERC721/libraries/PermitHash.sol index 32d4a83f..be46f43e 100644 --- a/src/ERC721/libraries/PermitHash.sol +++ b/src/ERC721/libraries/PermitHash.sol @@ -6,27 +6,27 @@ import {ISignatureTransfer} from "../interfaces/ISignatureTransfer.sol"; library PermitHash { bytes32 public constant _PERMIT_DETAILS_TYPEHASH = - keccak256("PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)"); + keccak256("PermitDetails(address token,uint160 tokenId,uint48 expiration,uint48 nonce)"); bytes32 public constant _PERMIT_SINGLE_TYPEHASH = keccak256( - "PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)" + "PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 tokenId,uint48 expiration,uint48 nonce)" ); bytes32 public constant _PERMIT_BATCH_TYPEHASH = keccak256( - "PermitBatch(PermitDetails[] details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)" + "PermitBatch(PermitDetails[] details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 tokenId,uint48 expiration,uint48 nonce)" ); - bytes32 public constant _TOKEN_PERMISSIONS_TYPEHASH = keccak256("TokenPermissions(address token,uint256 amount)"); + bytes32 public constant _TOKEN_PERMISSIONS_TYPEHASH = keccak256("TokenPermissions(address token,uint256 tokenId)"); bytes32 public constant _PERMIT_TRANSFER_FROM_TYPEHASH = keccak256( - "PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)" + "PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 tokenId)" ); bytes32 public constant _PERMIT_BATCH_TRANSFER_FROM_TYPEHASH = keccak256( - "PermitBatchTransferFrom(TokenPermissions[] permitted,address spender,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)" + "PermitBatchTransferFrom(TokenPermissions[] permitted,address spender,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 tokenId)" ); - string public constant _TOKEN_PERMISSIONS_TYPESTRING = "TokenPermissions(address token,uint256 amount)"; + string public constant _TOKEN_PERMISSIONS_TYPESTRING = "TokenPermissions(address token,uint256 tokenId)"; string public constant _PERMIT_TRANSFER_FROM_WITNESS_TYPEHASH_STUB = "PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,"; From b6e89ca38aa9ced24765f5593bdf253e03648eb8 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 6 Jan 2023 14:57:13 -0500 Subject: [PATCH 02/27] file cleanup --- src/ERC20/AllowanceTransfer.sol | 4 +- src/ERC20/SignatureTransfer.sol | 4 +- src/ERC721/AllowanceTransfer.sol | 4 +- src/ERC721/PermitErrors.sol | 11 -- src/ERC721/SignatureTransfer.sol | 4 +- src/ERC721/interfaces/IDAIPermit.sol | 23 --- src/ERC721/libraries/Permit2Lib.sol | 144 ------------------ src/ERC721/libraries/SafeCast160.sol | 14 -- .../libraries/SignatureVerification.sol | 47 ------ .../interfaces => shared}/IERC1271.sol | 0 src/{ERC20 => shared}/PermitErrors.sol | 0 .../SignatureVerification.sol | 2 +- test/AllowanceTransferInvariants.t.sol | 2 +- test/AllowanceTransferTest.t.sol | 4 +- test/NonceBitmap.t.sol | 2 +- test/Permit2Lib.t.sol | 2 +- test/SignatureTransfer.t.sol | 5 +- test/TypehashGeneration.t.sol | 2 +- test/mocks/MockSignatureVerification.sol | 2 +- 19 files changed, 18 insertions(+), 258 deletions(-) delete mode 100644 src/ERC721/PermitErrors.sol delete mode 100644 src/ERC721/interfaces/IDAIPermit.sol delete mode 100644 src/ERC721/libraries/Permit2Lib.sol delete mode 100644 src/ERC721/libraries/SafeCast160.sol delete mode 100644 src/ERC721/libraries/SignatureVerification.sol rename src/{ERC721/interfaces => shared}/IERC1271.sol (100%) rename src/{ERC20 => shared}/PermitErrors.sol (100%) rename src/{ERC20/libraries => shared}/SignatureVerification.sol (97%) diff --git a/src/ERC20/AllowanceTransfer.sol b/src/ERC20/AllowanceTransfer.sol index 56c4cce0..c1339957 100644 --- a/src/ERC20/AllowanceTransfer.sol +++ b/src/ERC20/AllowanceTransfer.sol @@ -4,10 +4,10 @@ pragma solidity 0.8.17; import {ERC20} from "solmate/src/tokens/ERC20.sol"; import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; import {PermitHash} from "./libraries/PermitHash.sol"; -import {SignatureVerification} from "./libraries/SignatureVerification.sol"; +import {SignatureVerification} from "../shared/SignatureVerification.sol"; import {EIP712} from "./EIP712.sol"; import {IAllowanceTransfer} from "./interfaces/IAllowanceTransfer.sol"; -import {SignatureExpired, InvalidNonce} from "./PermitErrors.sol"; +import {SignatureExpired, InvalidNonce} from "../shared/PermitErrors.sol"; import {Allowance} from "./libraries/Allowance.sol"; contract AllowanceTransfer is IAllowanceTransfer, EIP712 { diff --git a/src/ERC20/SignatureTransfer.sol b/src/ERC20/SignatureTransfer.sol index c026553a..14d69e86 100644 --- a/src/ERC20/SignatureTransfer.sol +++ b/src/ERC20/SignatureTransfer.sol @@ -2,10 +2,10 @@ pragma solidity 0.8.17; import {ISignatureTransfer} from "./interfaces/ISignatureTransfer.sol"; -import {SignatureExpired, InvalidNonce} from "./PermitErrors.sol"; +import {SignatureExpired, InvalidNonce} from "../shared/PermitErrors.sol"; import {ERC20} from "solmate/src/tokens/ERC20.sol"; import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; -import {SignatureVerification} from "./libraries/SignatureVerification.sol"; +import {SignatureVerification} from "../shared/SignatureVerification.sol"; import {PermitHash} from "./libraries/PermitHash.sol"; import {EIP712} from "./EIP712.sol"; diff --git a/src/ERC721/AllowanceTransfer.sol b/src/ERC721/AllowanceTransfer.sol index c21d5caa..77d42936 100644 --- a/src/ERC721/AllowanceTransfer.sol +++ b/src/ERC721/AllowanceTransfer.sol @@ -4,10 +4,10 @@ pragma solidity 0.8.17; import {ERC721} from "solmate/src/tokens/ERC721.sol"; import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; import {PermitHash} from "./libraries/PermitHash.sol"; -import {SignatureVerification} from "./libraries/SignatureVerification.sol"; +import {SignatureVerification} from "../shared/SignatureVerification.sol"; import {EIP712} from "./EIP712.sol"; import {IAllowanceTransfer} from "./interfaces/IAllowanceTransfer.sol"; -import {SignatureExpired, InvalidNonce} from "./PermitErrors.sol"; +import {SignatureExpired, InvalidNonce} from "../shared/PermitErrors.sol"; import {Allowance} from "./libraries/Allowance.sol"; contract AllowanceTransfer is IAllowanceTransfer, EIP712 { diff --git a/src/ERC721/PermitErrors.sol b/src/ERC721/PermitErrors.sol deleted file mode 100644 index 2c42e2d1..00000000 --- a/src/ERC721/PermitErrors.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -/// @notice Shared errors between signature based transfers and allowance based transfers. - -/// @notice Thrown when validating an inputted signature that is stale -/// @param signatureDeadline The timestamp at which a signature is no longer valid -error SignatureExpired(uint256 signatureDeadline); - -/// @notice Thrown when validating that the inputted nonce has not been used -error InvalidNonce(); diff --git a/src/ERC721/SignatureTransfer.sol b/src/ERC721/SignatureTransfer.sol index a97a2385..e1b14af8 100644 --- a/src/ERC721/SignatureTransfer.sol +++ b/src/ERC721/SignatureTransfer.sol @@ -2,10 +2,10 @@ pragma solidity 0.8.17; import {ISignatureTransfer} from "./interfaces/ISignatureTransfer.sol"; -import {SignatureExpired, InvalidNonce} from "./PermitErrors.sol"; +import {SignatureExpired, InvalidNonce} from "../shared/PermitErrors.sol"; import {ERC721} from "solmate/src/tokens/ERC721.sol"; import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; -import {SignatureVerification} from "./libraries/SignatureVerification.sol"; +import {SignatureVerification} from "../shared/SignatureVerification.sol"; import {PermitHash} from "./libraries/PermitHash.sol"; import {EIP712} from "./EIP712.sol"; diff --git a/src/ERC721/interfaces/IDAIPermit.sol b/src/ERC721/interfaces/IDAIPermit.sol deleted file mode 100644 index 912b7817..00000000 --- a/src/ERC721/interfaces/IDAIPermit.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -interface IDAIPermit { - /// @param holder The address of the token owner. - /// @param spender The address of the token spender. - /// @param nonce The owner's nonce, increases at each call to permit. - /// @param expiry The timestamp at which the permit is no longer valid. - /// @param allowed Boolean that sets approval amount, true for type(uint256).max and false for 0. - /// @param v Must produce valid secp256k1 signature from the owner along with r and s. - /// @param r Must produce valid secp256k1 signature from the owner along with v and s. - /// @param s Must produce valid secp256k1 signature from the owner along with r and v. - function permit( - address holder, - address spender, - uint256 nonce, - uint256 expiry, - bool allowed, - uint8 v, - bytes32 r, - bytes32 s - ) external; -} diff --git a/src/ERC721/libraries/Permit2Lib.sol b/src/ERC721/libraries/Permit2Lib.sol deleted file mode 100644 index 6549bc67..00000000 --- a/src/ERC721/libraries/Permit2Lib.sol +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import {ERC20} from "solmate/src/tokens/ERC20.sol"; - -import {Permit2} from "../Permit2.sol"; -import {IDAIPermit} from "../interfaces/IDAIPermit.sol"; -import {IAllowanceTransfer} from "../interfaces/IAllowanceTransfer.sol"; -import {SafeCast160} from "./SafeCast160.sol"; - -/// @title Permit2Lib -/// @notice Enables efficient transfers and EIP-2612/DAI -/// permits for any token by falling back to Permit2. -// TODO enable for erc721 -library Permit2Lib { - using SafeCast160 for uint256; - /*////////////////////////////////////////////////////////////// - CONSTANTS - //////////////////////////////////////////////////////////////*/ - - /// @dev The unique EIP-712 domain domain separator for the DAI token contract. - bytes32 internal constant DAI_DOMAIN_SEPARATOR = 0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7; - - /// @dev The address for the WETH9 contract on Ethereum mainnet, encoded as a bytes32. - bytes32 internal constant WETH9_ADDRESS = 0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2; - - /// @dev The address of the Permit2 contract the library will use. - Permit2 internal constant PERMIT2 = Permit2(address(0x000000000022D473030F116dDEE9F6B43aC78BA3)); - - /// @notice Transfer a given amount of tokens from one user to another. - /// @param token The token to transfer. - /// @param from The user to transfer from. - /// @param to The user to transfer to. - /// @param amount The amount to transfer. - function transferFrom2(ERC20 token, address from, address to, uint256 amount) internal { - // Generate calldata for a standard transferFrom call. - bytes memory inputData = abi.encodeCall(ERC20.transferFrom, (from, to, amount)); - - bool success; // Call the token contract as normal, capturing whether it succeeded. - assembly { - success := - and( - // Set success to whether the call reverted, if not we check it either - // returned exactly 1 (can't just be non-zero data), or had no return data. - or(eq(mload(0), 1), iszero(returndatasize())), - // Counterintuitively, this call() must be positioned after the or() in the - // surrounding and() because and() evaluates its arguments from right to left. - // We use 0 and 32 to copy up to 32 bytes of return data into the first slot of scratch space. - call(gas(), token, 0, add(inputData, 32), mload(inputData), 0, 32) - ) - } - - // We'll fall back to using Permit2 if calling transferFrom on the token directly reverted. - if (!success) PERMIT2.transferFrom(from, to, amount.toUint160(), address(token)); - } - - /*////////////////////////////////////////////////////////////// - PERMIT LOGIC - //////////////////////////////////////////////////////////////*/ - - /// @notice Permit a user to spend a given amount of - /// another user's tokens via the owner's EIP-712 signature. - /// @param token The token to permit spending. - /// @param owner The user to permit spending from. - /// @param spender The user to permit spending to. - /// @param amount The amount to permit spending. - /// @param deadline The timestamp after which the signature is no longer valid. - /// @param v Must produce valid secp256k1 signature from the owner along with r and s. - /// @param r Must produce valid secp256k1 signature from the owner along with v and s. - /// @param s Must produce valid secp256k1 signature from the owner along with r and v. - function permit2( - ERC20 token, - address owner, - address spender, - uint256 amount, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) internal { - // Generate calldata for a call to DOMAIN_SEPARATOR on the token. - bytes memory inputData = abi.encodeWithSelector(ERC20.DOMAIN_SEPARATOR.selector); - - bool success; // Call the token contract as normal, capturing whether it succeeded. - bytes32 domainSeparator; // If the call succeeded, we'll capture the return value here. - - assembly { - // If the token is WETH9, we know it doesn't have a DOMAIN_SEPARATOR, and we'll skip this step. - // We make sure to mask the token address as its higher order bits aren't guaranteed to be clean. - if iszero(eq(and(token, 0xffffffffffffffffffffffffffffffffffffffff), WETH9_ADDRESS)) { - success := - and( - // Should resolve false if its not 32 bytes or its first word is 0. - and(iszero(iszero(mload(0))), eq(returndatasize(), 32)), - // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. - // Counterintuitively, this call must be positioned second to the and() call in the - // surrounding and() call or else returndatasize() will be zero during the computation. - // We send a maximum of 5000 gas to prevent tokens with fallbacks from using a ton of gas. - // which should be plenty to allow tokens to fetch their DOMAIN_SEPARATOR from storage, etc. - staticcall(5000, token, add(inputData, 32), mload(inputData), 0, 32) - ) - - domainSeparator := mload(0) // Copy the return value into the domainSeparator variable. - } - } - - // If the call to DOMAIN_SEPARATOR succeeded, try using permit on the token. - if (success) { - // We'll use DAI's special permit if it's DOMAIN_SEPARATOR matches, - // otherwise we'll just encode a call to the standard permit function. - inputData = domainSeparator == DAI_DOMAIN_SEPARATOR - ? abi.encodeCall(IDAIPermit.permit, (owner, spender, token.nonces(owner), deadline, true, v, r, s)) - : abi.encodeCall(ERC20.permit, (owner, spender, amount, deadline, v, r, s)); - - assembly { - success := call(gas(), token, 0, add(inputData, 32), mload(inputData), 0, 0) - } - } - - if (!success) { - // If the initial DOMAIN_SEPARATOR call on the token failed or a - // subsequent call to permit failed, fall back to using Permit2. - - (,, uint48 nonce) = PERMIT2.allowance(owner, address(token), spender); - - PERMIT2.permit( - owner, - IAllowanceTransfer.PermitSingle({ - details: IAllowanceTransfer.PermitDetails({ - token: address(token), - amount: amount.toUint160(), - // Use an unlimited expiration because it most - // closely mimics how a standard approval works. - expiration: type(uint48).max, - nonce: nonce - }), - spender: spender, - sigDeadline: deadline - }), - bytes.concat(r, s, bytes1(v)) - ); - } - } -} diff --git a/src/ERC721/libraries/SafeCast160.sol b/src/ERC721/libraries/SafeCast160.sol deleted file mode 100644 index 5926036a..00000000 --- a/src/ERC721/libraries/SafeCast160.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -library SafeCast160 { - /// @notice Thrown when a valude greater than type(uint160).max is cast to uint160 - error UnsafeCast(); - - /// @notice Safely casts uint256 to uint160 - /// @param value The uint256 to be cast - function toUint160(uint256 value) internal pure returns (uint160) { - if (value > type(uint160).max) revert UnsafeCast(); - return uint160(value); - } -} diff --git a/src/ERC721/libraries/SignatureVerification.sol b/src/ERC721/libraries/SignatureVerification.sol deleted file mode 100644 index 904dfcd2..00000000 --- a/src/ERC721/libraries/SignatureVerification.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import {IERC1271} from "../interfaces/IERC1271.sol"; - -library SignatureVerification { - /// @notice Thrown when the passed in signature is not a valid length - error InvalidSignatureLength(); - - /// @notice Thrown when the recovered signer is equal to the zero address - error InvalidSignature(); - - /// @notice Thrown when the recovered signer does not equal the claimedSigner - error InvalidSigner(); - - /// @notice Thrown when the recovered contract signature is incorrect - error InvalidContractSignature(); - - bytes32 constant UPPER_BIT_MASK = (0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); - - function verify(bytes calldata signature, bytes32 hash, address claimedSigner) internal view { - bytes32 r; - bytes32 s; - uint8 v; - - if (claimedSigner.code.length == 0) { - if (signature.length == 65) { - (r, s) = abi.decode(signature, (bytes32, bytes32)); - v = uint8(signature[64]); - } else if (signature.length == 64) { - // EIP-2098 - bytes32 vs; - (r, vs) = abi.decode(signature, (bytes32, bytes32)); - s = vs & UPPER_BIT_MASK; - v = uint8(uint256(vs >> 255)) + 27; - } else { - revert InvalidSignatureLength(); - } - address signer = ecrecover(hash, v, r, s); - if (signer == address(0)) revert InvalidSignature(); - if (signer != claimedSigner) revert InvalidSigner(); - } else { - bytes4 magicValue = IERC1271(claimedSigner).isValidSignature(hash, signature); - if (magicValue != IERC1271.isValidSignature.selector) revert InvalidContractSignature(); - } - } -} diff --git a/src/ERC721/interfaces/IERC1271.sol b/src/shared/IERC1271.sol similarity index 100% rename from src/ERC721/interfaces/IERC1271.sol rename to src/shared/IERC1271.sol diff --git a/src/ERC20/PermitErrors.sol b/src/shared/PermitErrors.sol similarity index 100% rename from src/ERC20/PermitErrors.sol rename to src/shared/PermitErrors.sol diff --git a/src/ERC20/libraries/SignatureVerification.sol b/src/shared/SignatureVerification.sol similarity index 97% rename from src/ERC20/libraries/SignatureVerification.sol rename to src/shared/SignatureVerification.sol index 904dfcd2..12d0b542 100644 --- a/src/ERC20/libraries/SignatureVerification.sol +++ b/src/shared/SignatureVerification.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {IERC1271} from "../interfaces/IERC1271.sol"; +import {IERC1271} from "./IERC1271.sol"; library SignatureVerification { /// @notice Thrown when the passed in signature is not a valid length diff --git a/test/AllowanceTransferInvariants.t.sol b/test/AllowanceTransferInvariants.t.sol index dd6d111e..80224bb8 100644 --- a/test/AllowanceTransferInvariants.t.sol +++ b/test/AllowanceTransferInvariants.t.sol @@ -4,7 +4,7 @@ import "forge-std/Test.sol"; import {TokenProvider} from "./utils/TokenProvider.sol"; import {Permit2} from "../src/ERC20/Permit2.sol"; import {IAllowanceTransfer} from "../src/ERC20/interfaces/IAllowanceTransfer.sol"; -import {SignatureVerification} from "../src/ERC20/libraries/SignatureVerification.sol"; +import {SignatureVerification} from "../src/shared/SignatureVerification.sol"; import {PermitSignature} from "./utils/PermitSignature.sol"; import {InvariantTest} from "./utils/InvariantTest.sol"; import {MockERC20} from "./mocks/MockERC20.sol"; diff --git a/test/AllowanceTransferTest.t.sol b/test/AllowanceTransferTest.t.sol index df827197..aa79a2a0 100644 --- a/test/AllowanceTransferTest.t.sol +++ b/test/AllowanceTransferTest.t.sol @@ -5,12 +5,12 @@ import "forge-std/Test.sol"; import {TokenProvider} from "./utils/TokenProvider.sol"; import {Permit2} from "../src/ERC20/Permit2.sol"; import {PermitSignature} from "./utils/PermitSignature.sol"; -import {SignatureVerification} from "../src/ERC20/libraries/SignatureVerification.sol"; +import {SignatureVerification} from "../src/shared/SignatureVerification.sol"; import {AddressBuilder} from "./utils/AddressBuilder.sol"; import {StructBuilder} from "./utils/StructBuilder.sol"; import {AmountBuilder} from "./utils/AmountBuilder.sol"; import {AllowanceTransfer} from "../src/ERC20/AllowanceTransfer.sol"; -import {SignatureExpired, InvalidNonce} from "../src/ERC20/PermitErrors.sol"; +import {SignatureExpired, InvalidNonce} from "../src/shared/PermitErrors.sol"; import {IAllowanceTransfer} from "../src/ERC20/interfaces/IAllowanceTransfer.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; diff --git a/test/NonceBitmap.t.sol b/test/NonceBitmap.t.sol index 2f37f7f7..6984e9f6 100644 --- a/test/NonceBitmap.t.sol +++ b/test/NonceBitmap.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {SafeERC20, IERC20, IERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import {MockPermit2} from "./mocks/MockPermit2.sol"; -import {InvalidNonce} from "../src/ERC20/PermitErrors.sol"; +import {InvalidNonce} from "../src/shared/PermitErrors.sol"; contract NonceBitmapTest is Test { MockPermit2 permit2; diff --git a/test/Permit2Lib.t.sol b/test/Permit2Lib.t.sol index a74143ce..48cb6173 100644 --- a/test/Permit2Lib.t.sol +++ b/test/Permit2Lib.t.sol @@ -16,7 +16,7 @@ import {MockPermit2Lib} from "./mocks/MockPermit2Lib.sol"; import {SafeCast160} from "../src/ERC20/libraries/SafeCast160.sol"; import {MockPermitWithSmallDS, MockPermitWithLargerDS} from "./mocks/MockPermitWithDS.sol"; import {MockNonPermitNonERC20WithDS} from "./mocks/MockNonPermitNonERC20WithDS.sol"; -import {SignatureVerification} from "../src/ERC20/libraries/SignatureVerification.sol"; +import {SignatureVerification} from "../src/shared/SignatureVerification.sol"; import {MockFallbackERC20} from "./mocks/MockFallbackERC20.sol"; contract Permit2LibTest is Test, PermitSignature, GasSnapshot { diff --git a/test/SignatureTransfer.t.sol b/test/SignatureTransfer.t.sol index 8952a1c2..e1aa66ec 100644 --- a/test/SignatureTransfer.t.sol +++ b/test/SignatureTransfer.t.sol @@ -3,9 +3,8 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {SafeERC20, IERC20, IERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; -import {SignatureVerification} from "../src/ERC20/libraries/SignatureVerification.sol"; +import {SignatureVerification} from "../src/shared/SignatureVerification.sol"; import {TokenProvider} from "./utils/TokenProvider.sol"; -import {SignatureVerification} from "../src/ERC20/libraries/SignatureVerification.sol"; import {PermitSignature} from "./utils/PermitSignature.sol"; import {AddressBuilder} from "./utils/AddressBuilder.sol"; import {AmountBuilder} from "./utils/AmountBuilder.sol"; @@ -14,7 +13,7 @@ import {Permit2} from "../src/ERC20/Permit2.sol"; import {SignatureTransfer} from "../src/ERC20/SignatureTransfer.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {ISignatureTransfer} from "../src/ERC20/interfaces/ISignatureTransfer.sol"; -import {InvalidNonce, SignatureExpired} from "../src/ERC20/PermitErrors.sol"; +import {InvalidNonce, SignatureExpired} from "../src/shared/PermitErrors.sol"; contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnapshot { using AddressBuilder for address[]; diff --git a/test/TypehashGeneration.t.sol b/test/TypehashGeneration.t.sol index 724f7345..a02b049b 100644 --- a/test/TypehashGeneration.t.sol +++ b/test/TypehashGeneration.t.sol @@ -9,7 +9,7 @@ import {ISignatureTransfer} from "../src/ERC20/interfaces/ISignatureTransfer.sol import {MockSignatureVerification} from "./mocks/MockSignatureVerification.sol"; import {MockHash} from "./mocks/MockHash.sol"; import {AddressBuilder} from "./utils/AddressBuilder.sol"; -import {SignatureVerification} from "../src/ERC20/libraries/SignatureVerification.sol"; +import {SignatureVerification} from "../src/shared/SignatureVerification.sol"; contract TypehashGeneration is Test, PermitSignature { using PermitHash for *; diff --git a/test/mocks/MockSignatureVerification.sol b/test/mocks/MockSignatureVerification.sol index 718eff5f..a27c75ad 100644 --- a/test/mocks/MockSignatureVerification.sol +++ b/test/mocks/MockSignatureVerification.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {SignatureVerification} from "../../src/ERC20/libraries/SignatureVerification.sol"; +import {SignatureVerification} from "../../src/shared/SignatureVerification.sol"; contract MockSignatureVerification { function verify(bytes calldata sig, bytes32 hashed, address signer) public view { From f8f0a19c3e73746576cb68d6b35b1189be6fbe7b Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 6 Jan 2023 15:13:35 -0500 Subject: [PATCH 03/27] include _ERC721 naming --- ...nsfer.sol => AllowanceTransfer_ERC721.sol} | 31 +++++++++---------- src/ERC721/{EIP712.sol => EIP712_ERC721.sol} | 7 ++--- src/ERC721/Permit2.sol | 6 ++-- ...nsfer.sol => SignatureTransfer_ERC721.sol} | 24 +++++++------- ...sfer.sol => IAllowanceTransfer_ERC721.sol} | 2 +- ...sfer.sol => ISignatureTransfer_ERC721.sol} | 2 +- .../{Allowance.sol => Allowance_ERC721.sol} | 8 ++--- .../{PermitHash.sol => PermitHash_ERC721.sol} | 26 +++++++++------- 8 files changed, 54 insertions(+), 52 deletions(-) rename src/ERC721/{AllowanceTransfer.sol => AllowanceTransfer_ERC721.sol} (87%) rename src/ERC721/{EIP712.sol => EIP712_ERC721.sol} (92%) rename src/ERC721/{SignatureTransfer.sol => SignatureTransfer_ERC721.sol} (90%) rename src/ERC721/interfaces/{IAllowanceTransfer.sol => IAllowanceTransfer_ERC721.sol} (99%) rename src/ERC721/interfaces/{ISignatureTransfer.sol => ISignatureTransfer_ERC721.sol} (99%) rename src/ERC721/libraries/{Allowance.sol => Allowance_ERC721.sol} (87%) rename src/ERC721/libraries/{PermitHash.sol => PermitHash_ERC721.sol} (82%) diff --git a/src/ERC721/AllowanceTransfer.sol b/src/ERC721/AllowanceTransfer_ERC721.sol similarity index 87% rename from src/ERC721/AllowanceTransfer.sol rename to src/ERC721/AllowanceTransfer_ERC721.sol index 77d42936..a12a62bc 100644 --- a/src/ERC721/AllowanceTransfer.sol +++ b/src/ERC721/AllowanceTransfer_ERC721.sol @@ -2,33 +2,32 @@ pragma solidity 0.8.17; import {ERC721} from "solmate/src/tokens/ERC721.sol"; -import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; -import {PermitHash} from "./libraries/PermitHash.sol"; +import {PermitHash_ERC721} from "./libraries/PermitHash_ERC721.sol"; import {SignatureVerification} from "../shared/SignatureVerification.sol"; -import {EIP712} from "./EIP712.sol"; -import {IAllowanceTransfer} from "./interfaces/IAllowanceTransfer.sol"; +import {EIP712_ERC721} from "./EIP712_ERC721.sol"; +import {IAllowanceTransfer_ERC721} from "./interfaces/IAllowanceTransfer_ERC721.sol"; import {SignatureExpired, InvalidNonce} from "../shared/PermitErrors.sol"; -import {Allowance} from "./libraries/Allowance.sol"; +import {Allowance_ERC721} from "./libraries/Allowance_ERC721.sol"; -contract AllowanceTransfer is IAllowanceTransfer, EIP712 { +contract AllowanceTransfer_ERC721 is IAllowanceTransfer_ERC721, EIP712_ERC721 { using SignatureVerification for bytes; - using PermitHash for PermitSingle; - using PermitHash for PermitBatch; - using Allowance for PackedAllowance; + using PermitHash_ERC721 for PermitSingle; + using PermitHash_ERC721 for PermitBatch; + using Allowance_ERC721 for PackedAllowance; /// @notice Maps users to tokens to spender addresses and information about the approval on the token /// @dev Indexed in the order of token owner address, token address, spender address /// @dev The stored word saves the allowed tokenId, expiration on the allowance, and nonce mapping(address => mapping(address => mapping(address => PackedAllowance))) public allowance; - /// @inheritdoc IAllowanceTransfer + /// @inheritdoc IAllowanceTransfer_ERC721 function approve(address token, address spender, uint160 tokenId, uint48 expiration) external { PackedAllowance storage allowed = allowance[msg.sender][token][spender]; allowed.updateTokenIdAndExpiration(tokenId, expiration); emit Approval(msg.sender, token, spender, tokenId, expiration); } - /// @inheritdoc IAllowanceTransfer + /// @inheritdoc IAllowanceTransfer_ERC721 function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external { if (block.timestamp > permitSingle.sigDeadline) revert SignatureExpired(permitSingle.sigDeadline); @@ -38,7 +37,7 @@ contract AllowanceTransfer is IAllowanceTransfer, EIP712 { _updateApproval(permitSingle.details, owner, permitSingle.spender); } - /// @inheritdoc IAllowanceTransfer + /// @inheritdoc IAllowanceTransfer_ERC721 function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external { if (block.timestamp > permitBatch.sigDeadline) revert SignatureExpired(permitBatch.sigDeadline); @@ -54,12 +53,12 @@ contract AllowanceTransfer is IAllowanceTransfer, EIP712 { } } - /// @inheritdoc IAllowanceTransfer + /// @inheritdoc IAllowanceTransfer_ERC721 function transferFrom(address from, address to, uint160 tokenId, address token) external { _transfer(from, to, tokenId, token); } - /// @inheritdoc IAllowanceTransfer + /// @inheritdoc IAllowanceTransfer_ERC721 function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external { unchecked { uint256 length = transferDetails.length; @@ -91,7 +90,7 @@ contract AllowanceTransfer is IAllowanceTransfer, EIP712 { ERC721(token).safeTransferFrom(from, to, tokenId); } - /// @inheritdoc IAllowanceTransfer + /// @inheritdoc IAllowanceTransfer_ERC721 function lockdown(TokenSpenderPair[] calldata approvals) external { address owner = msg.sender; // Revoke allowances for each pair of spenders and tokens. @@ -107,7 +106,7 @@ contract AllowanceTransfer is IAllowanceTransfer, EIP712 { } } - /// @inheritdoc IAllowanceTransfer + /// @inheritdoc IAllowanceTransfer_ERC721 function invalidateNonces(address token, address spender, uint48 newNonce) external { uint48 oldNonce = allowance[msg.sender][token][spender].nonce; diff --git a/src/ERC721/EIP712.sol b/src/ERC721/EIP712_ERC721.sol similarity index 92% rename from src/ERC721/EIP712.sol rename to src/ERC721/EIP712_ERC721.sol index a4b171e9..62741a4e 100644 --- a/src/ERC721/EIP712.sol +++ b/src/ERC721/EIP712_ERC721.sol @@ -1,17 +1,16 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -/// @notice EIP712 helpers for permit2 +/// @notice EIP712 helpers for Permit2 for ERC721s /// @dev Maintains cross-chain replay protection in the event of a fork /// @dev Reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol -contract EIP712 { +contract EIP712_ERC721 { // Cache the domain separator as an immutable value, but also store the chain id that it // corresponds to, in order to invalidate the cached domain separator if the chain id changes. bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; uint256 private immutable _CACHED_CHAIN_ID; - // TODO Update naming - bytes32 private constant _HASHED_NAME = keccak256("Permit2"); + bytes32 private constant _HASHED_NAME = keccak256("Permit2_ERC721"); bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); diff --git a/src/ERC721/Permit2.sol b/src/ERC721/Permit2.sol index b0e4d945..60463121 100644 --- a/src/ERC721/Permit2.sol +++ b/src/ERC721/Permit2.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import {SignatureTransfer} from "./SignatureTransfer.sol"; -import {AllowanceTransfer} from "./AllowanceTransfer.sol"; +import {SignatureTransfer_ERC721} from "./SignatureTransfer_ERC721.sol"; +import {AllowanceTransfer_ERC721} from "./AllowanceTransfer_ERC721.sol"; /// @notice Permit2 handles signature-based transfers in SignatureTransfer and allowance-based transfers in AllowanceTransfer. /// @dev Users must approve Permit2 before calling any of the transfer functions. /// @dev It is recommended that you set operator permissions on Permit2 by calling `setApprovalForAll` for any underlying ERC721 token. -contract Permit2 is SignatureTransfer, AllowanceTransfer { +contract Permit2_ERC721 is SignatureTransfer_ERC721, AllowanceTransfer_ERC721 { // Permit2 unifies the two contracts so users have maximal flexibility with their approval. } diff --git a/src/ERC721/SignatureTransfer.sol b/src/ERC721/SignatureTransfer_ERC721.sol similarity index 90% rename from src/ERC721/SignatureTransfer.sol rename to src/ERC721/SignatureTransfer_ERC721.sol index e1b14af8..bb982654 100644 --- a/src/ERC721/SignatureTransfer.sol +++ b/src/ERC721/SignatureTransfer_ERC721.sol @@ -1,23 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import {ISignatureTransfer} from "./interfaces/ISignatureTransfer.sol"; +import {ISignatureTransfer_ERC721} from "./interfaces/ISignatureTransfer_ERC721.sol"; import {SignatureExpired, InvalidNonce} from "../shared/PermitErrors.sol"; import {ERC721} from "solmate/src/tokens/ERC721.sol"; import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; import {SignatureVerification} from "../shared/SignatureVerification.sol"; -import {PermitHash} from "./libraries/PermitHash.sol"; -import {EIP712} from "./EIP712.sol"; +import {PermitHash_ERC721} from "./libraries/PermitHash_ERC721.sol"; +import {EIP712_ERC721} from "./EIP712_ERC721.sol"; -contract SignatureTransfer is ISignatureTransfer, EIP712 { +contract SignatureTransfer_ERC721 is ISignatureTransfer_ERC721, EIP712_ERC721 { using SignatureVerification for bytes; - using PermitHash for PermitTransferFrom; - using PermitHash for PermitBatchTransferFrom; + using PermitHash_ERC721 for PermitTransferFrom; + using PermitHash_ERC721 for PermitBatchTransferFrom; - /// @inheritdoc ISignatureTransfer + /// @inheritdoc ISignatureTransfer_ERC721 mapping(address => mapping(uint256 => uint256)) public nonceBitmap; - /// @inheritdoc ISignatureTransfer + /// @inheritdoc ISignatureTransfer_ERC721 function permitTransferFrom( PermitTransferFrom memory permit, SignatureTransferDetails calldata transferDetails, @@ -27,7 +27,7 @@ contract SignatureTransfer is ISignatureTransfer, EIP712 { _permitTransferFrom(permit, transferDetails, owner, permit.hash(), signature); } - /// @inheritdoc ISignatureTransfer + /// @inheritdoc ISignatureTransfer_ERC721 function permitWitnessTransferFrom( PermitTransferFrom memory permit, SignatureTransferDetails calldata transferDetails, @@ -68,7 +68,7 @@ contract SignatureTransfer is ISignatureTransfer, EIP712 { ERC721(permit.permitted.token).safeTransferFrom(owner, transferDetails.to, requestedTokenId); } - /// @inheritdoc ISignatureTransfer + /// @inheritdoc ISignatureTransfer_ERC721 function permitTransferFrom( PermitBatchTransferFrom memory permit, SignatureTransferDetails[] calldata transferDetails, @@ -78,7 +78,7 @@ contract SignatureTransfer is ISignatureTransfer, EIP712 { _permitTransferFrom(permit, transferDetails, owner, permit.hash(), signature); } - /// @inheritdoc ISignatureTransfer + /// @inheritdoc ISignatureTransfer_ERC721 function permitWitnessTransferFrom( PermitBatchTransferFrom memory permit, SignatureTransferDetails[] calldata transferDetails, @@ -129,7 +129,7 @@ contract SignatureTransfer is ISignatureTransfer, EIP712 { } } - /// @inheritdoc ISignatureTransfer + /// @inheritdoc ISignatureTransfer_ERC721 function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external { nonceBitmap[msg.sender][wordPos] |= mask; diff --git a/src/ERC721/interfaces/IAllowanceTransfer.sol b/src/ERC721/interfaces/IAllowanceTransfer_ERC721.sol similarity index 99% rename from src/ERC721/interfaces/IAllowanceTransfer.sol rename to src/ERC721/interfaces/IAllowanceTransfer_ERC721.sol index e3312c06..f9bfc1fa 100644 --- a/src/ERC721/interfaces/IAllowanceTransfer.sol +++ b/src/ERC721/interfaces/IAllowanceTransfer_ERC721.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.17; /// @title AllowanceTransfer /// @notice Handles ERC721 token permissions through signature based allowance setting and ERC721 token transfers by checking stored permissions /// @dev Requires user's token approval on the Permit2 contract -interface IAllowanceTransfer { +interface IAllowanceTransfer_ERC721 { /// @notice Thrown when an allowance on a token has expired. /// @param deadline The timestamp at which the permissions on the token are no longer valid error AllowanceExpired(uint256 deadline); diff --git a/src/ERC721/interfaces/ISignatureTransfer.sol b/src/ERC721/interfaces/ISignatureTransfer_ERC721.sol similarity index 99% rename from src/ERC721/interfaces/ISignatureTransfer.sol rename to src/ERC721/interfaces/ISignatureTransfer_ERC721.sol index 3790b909..36e2f4d4 100644 --- a/src/ERC721/interfaces/ISignatureTransfer.sol +++ b/src/ERC721/interfaces/ISignatureTransfer_ERC721.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.17; /// @title SignatureTransfer /// @notice Handles ERC721 token transfers through signature based actions /// @dev Requires user's token approval on the Permit2 contract -interface ISignatureTransfer { +interface ISignatureTransfer_ERC721 { /// @notice Thrown when the requested tokenId for a transfer is not the permitted tokenId /// @param tokenId The valid tokenId a spender can request to transfer error InvalidTokenId(uint256 tokenId); diff --git a/src/ERC721/libraries/Allowance.sol b/src/ERC721/libraries/Allowance_ERC721.sol similarity index 87% rename from src/ERC721/libraries/Allowance.sol rename to src/ERC721/libraries/Allowance_ERC721.sol index 50134393..4a5135a5 100644 --- a/src/ERC721/libraries/Allowance.sol +++ b/src/ERC721/libraries/Allowance_ERC721.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {IAllowanceTransfer} from "../interfaces/IAllowanceTransfer.sol"; +import {IAllowanceTransfer_ERC721} from "../interfaces/IAllowanceTransfer_ERC721.sol"; -library Allowance { +library Allowance_ERC721 { // note if the expiration passed is 0, then it the approval set to the block.timestamp uint256 private constant BLOCK_TIMESTAMP_EXPIRATION = 0; @@ -11,7 +11,7 @@ library Allowance { /// @dev Nonce is incremented. /// @dev If the inputted expiration is 0, the stored expiration is set to block.timestamp function updateAll( - IAllowanceTransfer.PackedAllowance storage allowed, + IAllowanceTransfer_ERC721.PackedAllowance storage allowed, uint160 tokenId, uint48 expiration, uint48 nonce @@ -32,7 +32,7 @@ library Allowance { /// @notice Sets the allowed amount and expiry of the spender's permissions on owner's token. /// @dev Nonce does not need to be incremented. function updateTokenIdAndExpiration( - IAllowanceTransfer.PackedAllowance storage allowed, + IAllowanceTransfer_ERC721.PackedAllowance storage allowed, uint160 tokenId, uint48 expiration ) internal { diff --git a/src/ERC721/libraries/PermitHash.sol b/src/ERC721/libraries/PermitHash_ERC721.sol similarity index 82% rename from src/ERC721/libraries/PermitHash.sol rename to src/ERC721/libraries/PermitHash_ERC721.sol index be46f43e..ed3fb7ac 100644 --- a/src/ERC721/libraries/PermitHash.sol +++ b/src/ERC721/libraries/PermitHash_ERC721.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {IAllowanceTransfer} from "../interfaces/IAllowanceTransfer.sol"; -import {ISignatureTransfer} from "../interfaces/ISignatureTransfer.sol"; +import {IAllowanceTransfer_ERC721} from "../interfaces/IAllowanceTransfer_ERC721.sol"; +import {ISignatureTransfer_ERC721} from "../interfaces/ISignatureTransfer_ERC721.sol"; -library PermitHash { +library PermitHash_ERC721 { bytes32 public constant _PERMIT_DETAILS_TYPEHASH = keccak256("PermitDetails(address token,uint160 tokenId,uint48 expiration,uint48 nonce)"); @@ -34,13 +34,13 @@ library PermitHash { string public constant _PERMIT_BATCH_WITNESS_TRANSFER_FROM_TYPEHASH_STUB = "PermitBatchWitnessTransferFrom(TokenPermissions[] permitted,address spender,uint256 nonce,uint256 deadline,"; - function hash(IAllowanceTransfer.PermitSingle memory permitSingle) internal pure returns (bytes32) { + function hash(IAllowanceTransfer_ERC721.PermitSingle memory permitSingle) internal pure returns (bytes32) { bytes32 permitHash = _hashPermitDetails(permitSingle.details); return keccak256(abi.encode(_PERMIT_SINGLE_TYPEHASH, permitHash, permitSingle.spender, permitSingle.sigDeadline)); } - function hash(IAllowanceTransfer.PermitBatch memory permitBatch) internal pure returns (bytes32) { + function hash(IAllowanceTransfer_ERC721.PermitBatch memory permitBatch) internal pure returns (bytes32) { uint256 numPermits = permitBatch.details.length; bytes32[] memory permitHashes = new bytes32[](numPermits); for (uint256 i = 0; i < numPermits; ++i) { @@ -56,14 +56,14 @@ library PermitHash { ); } - function hash(ISignatureTransfer.PermitTransferFrom memory permit) internal view returns (bytes32) { + function hash(ISignatureTransfer_ERC721.PermitTransferFrom memory permit) internal view returns (bytes32) { bytes32 tokenPermissionsHash = _hashTokenPermissions(permit.permitted); return keccak256( abi.encode(_PERMIT_TRANSFER_FROM_TYPEHASH, tokenPermissionsHash, msg.sender, permit.nonce, permit.deadline) ); } - function hash(ISignatureTransfer.PermitBatchTransferFrom memory permit) internal view returns (bytes32) { + function hash(ISignatureTransfer_ERC721.PermitBatchTransferFrom memory permit) internal view returns (bytes32) { uint256 numPermitted = permit.permitted.length; bytes32[] memory tokenPermissionHashes = new bytes32[](numPermitted); @@ -83,7 +83,7 @@ library PermitHash { } function hashWithWitness( - ISignatureTransfer.PermitTransferFrom memory permit, + ISignatureTransfer_ERC721.PermitTransferFrom memory permit, bytes32 witness, string calldata witnessTypeString ) internal view returns (bytes32) { @@ -94,7 +94,7 @@ library PermitHash { } function hashWithWitness( - ISignatureTransfer.PermitBatchTransferFrom memory permit, + ISignatureTransfer_ERC721.PermitBatchTransferFrom memory permit, bytes32 witness, string calldata witnessTypeString ) internal view returns (bytes32) { @@ -120,11 +120,15 @@ library PermitHash { ); } - function _hashPermitDetails(IAllowanceTransfer.PermitDetails memory details) private pure returns (bytes32) { + function _hashPermitDetails(IAllowanceTransfer_ERC721.PermitDetails memory details) + private + pure + returns (bytes32) + { return keccak256(abi.encode(_PERMIT_DETAILS_TYPEHASH, details)); } - function _hashTokenPermissions(ISignatureTransfer.TokenPermissions memory permitted) + function _hashTokenPermissions(ISignatureTransfer_ERC721.TokenPermissions memory permitted) private pure returns (bytes32) From e0c2dbe44b3119b11248e2c2b26a09c4246f11b6 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 12 Jan 2023 14:57:04 -0700 Subject: [PATCH 04/27] rename' --- ...ERC721.sol => AllowanceTransferERC721.sol} | 20 ++++++++-------- .../{EIP712_ERC721.sol => EIP712ERC721.sol} | 4 ++-- src/ERC721/Permit2.sol | 6 ++--- ...ERC721.sol => SignatureTransferERC721.sol} | 18 +++++++------- ...RC721.sol => IAllowanceTransferERC721.sol} | 2 +- ...RC721.sol => ISignatureTransferERC721.sol} | 2 +- src/ERC721/libraries/Allowance_ERC721.sol | 6 ++--- src/ERC721/libraries/PermitHash_ERC721.sol | 24 ++++++++----------- 8 files changed, 39 insertions(+), 43 deletions(-) rename src/ERC721/{AllowanceTransfer_ERC721.sol => AllowanceTransferERC721.sol} (91%) rename src/ERC721/{EIP712_ERC721.sol => EIP712ERC721.sol} (94%) rename src/ERC721/{SignatureTransfer_ERC721.sol => SignatureTransferERC721.sol} (92%) rename src/ERC721/interfaces/{IAllowanceTransfer_ERC721.sol => IAllowanceTransferERC721.sol} (99%) rename src/ERC721/interfaces/{ISignatureTransfer_ERC721.sol => ISignatureTransferERC721.sol} (99%) diff --git a/src/ERC721/AllowanceTransfer_ERC721.sol b/src/ERC721/AllowanceTransferERC721.sol similarity index 91% rename from src/ERC721/AllowanceTransfer_ERC721.sol rename to src/ERC721/AllowanceTransferERC721.sol index a12a62bc..4aac057a 100644 --- a/src/ERC721/AllowanceTransfer_ERC721.sol +++ b/src/ERC721/AllowanceTransferERC721.sol @@ -4,12 +4,12 @@ pragma solidity 0.8.17; import {ERC721} from "solmate/src/tokens/ERC721.sol"; import {PermitHash_ERC721} from "./libraries/PermitHash_ERC721.sol"; import {SignatureVerification} from "../shared/SignatureVerification.sol"; -import {EIP712_ERC721} from "./EIP712_ERC721.sol"; -import {IAllowanceTransfer_ERC721} from "./interfaces/IAllowanceTransfer_ERC721.sol"; +import {EIP712ERC721} from "./EIP712ERC721.sol"; +import {IAllowanceTransferERC721} from "./interfaces/IAllowanceTransferERC721.sol"; import {SignatureExpired, InvalidNonce} from "../shared/PermitErrors.sol"; import {Allowance_ERC721} from "./libraries/Allowance_ERC721.sol"; -contract AllowanceTransfer_ERC721 is IAllowanceTransfer_ERC721, EIP712_ERC721 { +contract AllowanceTransferERC721 is IAllowanceTransferERC721, EIP712ERC721 { using SignatureVerification for bytes; using PermitHash_ERC721 for PermitSingle; using PermitHash_ERC721 for PermitBatch; @@ -20,14 +20,14 @@ contract AllowanceTransfer_ERC721 is IAllowanceTransfer_ERC721, EIP712_ERC721 { /// @dev The stored word saves the allowed tokenId, expiration on the allowance, and nonce mapping(address => mapping(address => mapping(address => PackedAllowance))) public allowance; - /// @inheritdoc IAllowanceTransfer_ERC721 + /// @inheritdoc IAllowanceTransferERC721 function approve(address token, address spender, uint160 tokenId, uint48 expiration) external { PackedAllowance storage allowed = allowance[msg.sender][token][spender]; allowed.updateTokenIdAndExpiration(tokenId, expiration); emit Approval(msg.sender, token, spender, tokenId, expiration); } - /// @inheritdoc IAllowanceTransfer_ERC721 + /// @inheritdoc IAllowanceTransferERC721 function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external { if (block.timestamp > permitSingle.sigDeadline) revert SignatureExpired(permitSingle.sigDeadline); @@ -37,7 +37,7 @@ contract AllowanceTransfer_ERC721 is IAllowanceTransfer_ERC721, EIP712_ERC721 { _updateApproval(permitSingle.details, owner, permitSingle.spender); } - /// @inheritdoc IAllowanceTransfer_ERC721 + /// @inheritdoc IAllowanceTransferERC721 function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external { if (block.timestamp > permitBatch.sigDeadline) revert SignatureExpired(permitBatch.sigDeadline); @@ -53,12 +53,12 @@ contract AllowanceTransfer_ERC721 is IAllowanceTransfer_ERC721, EIP712_ERC721 { } } - /// @inheritdoc IAllowanceTransfer_ERC721 + /// @inheritdoc IAllowanceTransferERC721 function transferFrom(address from, address to, uint160 tokenId, address token) external { _transfer(from, to, tokenId, token); } - /// @inheritdoc IAllowanceTransfer_ERC721 + /// @inheritdoc IAllowanceTransferERC721 function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external { unchecked { uint256 length = transferDetails.length; @@ -90,7 +90,7 @@ contract AllowanceTransfer_ERC721 is IAllowanceTransfer_ERC721, EIP712_ERC721 { ERC721(token).safeTransferFrom(from, to, tokenId); } - /// @inheritdoc IAllowanceTransfer_ERC721 + /// @inheritdoc IAllowanceTransferERC721 function lockdown(TokenSpenderPair[] calldata approvals) external { address owner = msg.sender; // Revoke allowances for each pair of spenders and tokens. @@ -106,7 +106,7 @@ contract AllowanceTransfer_ERC721 is IAllowanceTransfer_ERC721, EIP712_ERC721 { } } - /// @inheritdoc IAllowanceTransfer_ERC721 + /// @inheritdoc IAllowanceTransferERC721 function invalidateNonces(address token, address spender, uint48 newNonce) external { uint48 oldNonce = allowance[msg.sender][token][spender].nonce; diff --git a/src/ERC721/EIP712_ERC721.sol b/src/ERC721/EIP712ERC721.sol similarity index 94% rename from src/ERC721/EIP712_ERC721.sol rename to src/ERC721/EIP712ERC721.sol index 62741a4e..ac008be0 100644 --- a/src/ERC721/EIP712_ERC721.sol +++ b/src/ERC721/EIP712ERC721.sol @@ -4,13 +4,13 @@ pragma solidity 0.8.17; /// @notice EIP712 helpers for Permit2 for ERC721s /// @dev Maintains cross-chain replay protection in the event of a fork /// @dev Reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol -contract EIP712_ERC721 { +contract EIP712ERC721 { // Cache the domain separator as an immutable value, but also store the chain id that it // corresponds to, in order to invalidate the cached domain separator if the chain id changes. bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; uint256 private immutable _CACHED_CHAIN_ID; - bytes32 private constant _HASHED_NAME = keccak256("Permit2_ERC721"); + bytes32 private constant _HASHED_NAME = keccak256("Permit2ERC721"); bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); diff --git a/src/ERC721/Permit2.sol b/src/ERC721/Permit2.sol index 60463121..5f75099e 100644 --- a/src/ERC721/Permit2.sol +++ b/src/ERC721/Permit2.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import {SignatureTransfer_ERC721} from "./SignatureTransfer_ERC721.sol"; -import {AllowanceTransfer_ERC721} from "./AllowanceTransfer_ERC721.sol"; +import {SignatureTransferERC721} from "./SignatureTransferERC721.sol"; +import {AllowanceTransferERC721} from "./AllowanceTransferERC721.sol"; /// @notice Permit2 handles signature-based transfers in SignatureTransfer and allowance-based transfers in AllowanceTransfer. /// @dev Users must approve Permit2 before calling any of the transfer functions. /// @dev It is recommended that you set operator permissions on Permit2 by calling `setApprovalForAll` for any underlying ERC721 token. -contract Permit2_ERC721 is SignatureTransfer_ERC721, AllowanceTransfer_ERC721 { +contract Permit2ERC721 is SignatureTransferERC721, AllowanceTransferERC721 { // Permit2 unifies the two contracts so users have maximal flexibility with their approval. } diff --git a/src/ERC721/SignatureTransfer_ERC721.sol b/src/ERC721/SignatureTransferERC721.sol similarity index 92% rename from src/ERC721/SignatureTransfer_ERC721.sol rename to src/ERC721/SignatureTransferERC721.sol index bb982654..da510ec4 100644 --- a/src/ERC721/SignatureTransfer_ERC721.sol +++ b/src/ERC721/SignatureTransferERC721.sol @@ -1,23 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import {ISignatureTransfer_ERC721} from "./interfaces/ISignatureTransfer_ERC721.sol"; +import {ISignatureTransferERC721} from "./interfaces/ISignatureTransferERC721.sol"; import {SignatureExpired, InvalidNonce} from "../shared/PermitErrors.sol"; import {ERC721} from "solmate/src/tokens/ERC721.sol"; import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; import {SignatureVerification} from "../shared/SignatureVerification.sol"; import {PermitHash_ERC721} from "./libraries/PermitHash_ERC721.sol"; -import {EIP712_ERC721} from "./EIP712_ERC721.sol"; +import {EIP712ERC721} from "./EIP712ERC721.sol"; -contract SignatureTransfer_ERC721 is ISignatureTransfer_ERC721, EIP712_ERC721 { +contract SignatureTransferERC721 is ISignatureTransferERC721, EIP712ERC721 { using SignatureVerification for bytes; using PermitHash_ERC721 for PermitTransferFrom; using PermitHash_ERC721 for PermitBatchTransferFrom; - /// @inheritdoc ISignatureTransfer_ERC721 + /// @inheritdoc ISignatureTransferERC721 mapping(address => mapping(uint256 => uint256)) public nonceBitmap; - /// @inheritdoc ISignatureTransfer_ERC721 + /// @inheritdoc ISignatureTransferERC721 function permitTransferFrom( PermitTransferFrom memory permit, SignatureTransferDetails calldata transferDetails, @@ -27,7 +27,7 @@ contract SignatureTransfer_ERC721 is ISignatureTransfer_ERC721, EIP712_ERC721 { _permitTransferFrom(permit, transferDetails, owner, permit.hash(), signature); } - /// @inheritdoc ISignatureTransfer_ERC721 + /// @inheritdoc ISignatureTransferERC721 function permitWitnessTransferFrom( PermitTransferFrom memory permit, SignatureTransferDetails calldata transferDetails, @@ -68,7 +68,7 @@ contract SignatureTransfer_ERC721 is ISignatureTransfer_ERC721, EIP712_ERC721 { ERC721(permit.permitted.token).safeTransferFrom(owner, transferDetails.to, requestedTokenId); } - /// @inheritdoc ISignatureTransfer_ERC721 + /// @inheritdoc ISignatureTransferERC721 function permitTransferFrom( PermitBatchTransferFrom memory permit, SignatureTransferDetails[] calldata transferDetails, @@ -78,7 +78,7 @@ contract SignatureTransfer_ERC721 is ISignatureTransfer_ERC721, EIP712_ERC721 { _permitTransferFrom(permit, transferDetails, owner, permit.hash(), signature); } - /// @inheritdoc ISignatureTransfer_ERC721 + /// @inheritdoc ISignatureTransferERC721 function permitWitnessTransferFrom( PermitBatchTransferFrom memory permit, SignatureTransferDetails[] calldata transferDetails, @@ -129,7 +129,7 @@ contract SignatureTransfer_ERC721 is ISignatureTransfer_ERC721, EIP712_ERC721 { } } - /// @inheritdoc ISignatureTransfer_ERC721 + /// @inheritdoc ISignatureTransferERC721 function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external { nonceBitmap[msg.sender][wordPos] |= mask; diff --git a/src/ERC721/interfaces/IAllowanceTransfer_ERC721.sol b/src/ERC721/interfaces/IAllowanceTransferERC721.sol similarity index 99% rename from src/ERC721/interfaces/IAllowanceTransfer_ERC721.sol rename to src/ERC721/interfaces/IAllowanceTransferERC721.sol index f9bfc1fa..9a08b43f 100644 --- a/src/ERC721/interfaces/IAllowanceTransfer_ERC721.sol +++ b/src/ERC721/interfaces/IAllowanceTransferERC721.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.17; /// @title AllowanceTransfer /// @notice Handles ERC721 token permissions through signature based allowance setting and ERC721 token transfers by checking stored permissions /// @dev Requires user's token approval on the Permit2 contract -interface IAllowanceTransfer_ERC721 { +interface IAllowanceTransferERC721 { /// @notice Thrown when an allowance on a token has expired. /// @param deadline The timestamp at which the permissions on the token are no longer valid error AllowanceExpired(uint256 deadline); diff --git a/src/ERC721/interfaces/ISignatureTransfer_ERC721.sol b/src/ERC721/interfaces/ISignatureTransferERC721.sol similarity index 99% rename from src/ERC721/interfaces/ISignatureTransfer_ERC721.sol rename to src/ERC721/interfaces/ISignatureTransferERC721.sol index 36e2f4d4..a90db50d 100644 --- a/src/ERC721/interfaces/ISignatureTransfer_ERC721.sol +++ b/src/ERC721/interfaces/ISignatureTransferERC721.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.17; /// @title SignatureTransfer /// @notice Handles ERC721 token transfers through signature based actions /// @dev Requires user's token approval on the Permit2 contract -interface ISignatureTransfer_ERC721 { +interface ISignatureTransferERC721 { /// @notice Thrown when the requested tokenId for a transfer is not the permitted tokenId /// @param tokenId The valid tokenId a spender can request to transfer error InvalidTokenId(uint256 tokenId); diff --git a/src/ERC721/libraries/Allowance_ERC721.sol b/src/ERC721/libraries/Allowance_ERC721.sol index 4a5135a5..0af7fa60 100644 --- a/src/ERC721/libraries/Allowance_ERC721.sol +++ b/src/ERC721/libraries/Allowance_ERC721.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {IAllowanceTransfer_ERC721} from "../interfaces/IAllowanceTransfer_ERC721.sol"; +import {IAllowanceTransferERC721} from "../interfaces/IAllowanceTransferERC721.sol"; library Allowance_ERC721 { // note if the expiration passed is 0, then it the approval set to the block.timestamp @@ -11,7 +11,7 @@ library Allowance_ERC721 { /// @dev Nonce is incremented. /// @dev If the inputted expiration is 0, the stored expiration is set to block.timestamp function updateAll( - IAllowanceTransfer_ERC721.PackedAllowance storage allowed, + IAllowanceTransferERC721.PackedAllowance storage allowed, uint160 tokenId, uint48 expiration, uint48 nonce @@ -32,7 +32,7 @@ library Allowance_ERC721 { /// @notice Sets the allowed amount and expiry of the spender's permissions on owner's token. /// @dev Nonce does not need to be incremented. function updateTokenIdAndExpiration( - IAllowanceTransfer_ERC721.PackedAllowance storage allowed, + IAllowanceTransferERC721.PackedAllowance storage allowed, uint160 tokenId, uint48 expiration ) internal { diff --git a/src/ERC721/libraries/PermitHash_ERC721.sol b/src/ERC721/libraries/PermitHash_ERC721.sol index ed3fb7ac..63b091af 100644 --- a/src/ERC721/libraries/PermitHash_ERC721.sol +++ b/src/ERC721/libraries/PermitHash_ERC721.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {IAllowanceTransfer_ERC721} from "../interfaces/IAllowanceTransfer_ERC721.sol"; -import {ISignatureTransfer_ERC721} from "../interfaces/ISignatureTransfer_ERC721.sol"; +import {IAllowanceTransferERC721} from "../interfaces/IAllowanceTransferERC721.sol"; +import {ISignatureTransferERC721} from "../interfaces/ISignatureTransferERC721.sol"; library PermitHash_ERC721 { bytes32 public constant _PERMIT_DETAILS_TYPEHASH = @@ -34,13 +34,13 @@ library PermitHash_ERC721 { string public constant _PERMIT_BATCH_WITNESS_TRANSFER_FROM_TYPEHASH_STUB = "PermitBatchWitnessTransferFrom(TokenPermissions[] permitted,address spender,uint256 nonce,uint256 deadline,"; - function hash(IAllowanceTransfer_ERC721.PermitSingle memory permitSingle) internal pure returns (bytes32) { + function hash(IAllowanceTransferERC721.PermitSingle memory permitSingle) internal pure returns (bytes32) { bytes32 permitHash = _hashPermitDetails(permitSingle.details); return keccak256(abi.encode(_PERMIT_SINGLE_TYPEHASH, permitHash, permitSingle.spender, permitSingle.sigDeadline)); } - function hash(IAllowanceTransfer_ERC721.PermitBatch memory permitBatch) internal pure returns (bytes32) { + function hash(IAllowanceTransferERC721.PermitBatch memory permitBatch) internal pure returns (bytes32) { uint256 numPermits = permitBatch.details.length; bytes32[] memory permitHashes = new bytes32[](numPermits); for (uint256 i = 0; i < numPermits; ++i) { @@ -56,14 +56,14 @@ library PermitHash_ERC721 { ); } - function hash(ISignatureTransfer_ERC721.PermitTransferFrom memory permit) internal view returns (bytes32) { + function hash(ISignatureTransferERC721.PermitTransferFrom memory permit) internal view returns (bytes32) { bytes32 tokenPermissionsHash = _hashTokenPermissions(permit.permitted); return keccak256( abi.encode(_PERMIT_TRANSFER_FROM_TYPEHASH, tokenPermissionsHash, msg.sender, permit.nonce, permit.deadline) ); } - function hash(ISignatureTransfer_ERC721.PermitBatchTransferFrom memory permit) internal view returns (bytes32) { + function hash(ISignatureTransferERC721.PermitBatchTransferFrom memory permit) internal view returns (bytes32) { uint256 numPermitted = permit.permitted.length; bytes32[] memory tokenPermissionHashes = new bytes32[](numPermitted); @@ -83,7 +83,7 @@ library PermitHash_ERC721 { } function hashWithWitness( - ISignatureTransfer_ERC721.PermitTransferFrom memory permit, + ISignatureTransferERC721.PermitTransferFrom memory permit, bytes32 witness, string calldata witnessTypeString ) internal view returns (bytes32) { @@ -94,7 +94,7 @@ library PermitHash_ERC721 { } function hashWithWitness( - ISignatureTransfer_ERC721.PermitBatchTransferFrom memory permit, + ISignatureTransferERC721.PermitBatchTransferFrom memory permit, bytes32 witness, string calldata witnessTypeString ) internal view returns (bytes32) { @@ -120,15 +120,11 @@ library PermitHash_ERC721 { ); } - function _hashPermitDetails(IAllowanceTransfer_ERC721.PermitDetails memory details) - private - pure - returns (bytes32) - { + function _hashPermitDetails(IAllowanceTransferERC721.PermitDetails memory details) private pure returns (bytes32) { return keccak256(abi.encode(_PERMIT_DETAILS_TYPEHASH, details)); } - function _hashTokenPermissions(ISignatureTransfer_ERC721.TokenPermissions memory permitted) + function _hashTokenPermissions(ISignatureTransferERC721.TokenPermissions memory permitted) private pure returns (bytes32) From 78bc9bf616f6de9f4821e91863c6079bb1483703 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 12 Jan 2023 18:37:23 -0700 Subject: [PATCH 05/27] use tokenId inside allowance mapping, add operator approvals --- src/ERC721/AllowanceTransferERC721.sol | 130 +++++++++++++----- src/ERC721/SignatureTransferERC721.sol | 6 +- .../interfaces/IAllowanceTransferERC721.sol | 111 +++++++++++---- ...lowance_ERC721.sol => AllowanceERC721.sol} | 18 +-- ...itHash_ERC721.sol => PermitHashERC721.sol} | 24 +++- 5 files changed, 215 insertions(+), 74 deletions(-) rename src/ERC721/libraries/{Allowance_ERC721.sol => AllowanceERC721.sol} (79%) rename src/ERC721/libraries/{PermitHash_ERC721.sol => PermitHashERC721.sol} (87%) diff --git a/src/ERC721/AllowanceTransferERC721.sol b/src/ERC721/AllowanceTransferERC721.sol index 4aac057a..725b5050 100644 --- a/src/ERC721/AllowanceTransferERC721.sol +++ b/src/ERC721/AllowanceTransferERC721.sol @@ -2,31 +2,43 @@ pragma solidity 0.8.17; import {ERC721} from "solmate/src/tokens/ERC721.sol"; -import {PermitHash_ERC721} from "./libraries/PermitHash_ERC721.sol"; +import {PermitHashERC721} from "./libraries/PermitHashERC721.sol"; import {SignatureVerification} from "../shared/SignatureVerification.sol"; import {EIP712ERC721} from "./EIP712ERC721.sol"; import {IAllowanceTransferERC721} from "./interfaces/IAllowanceTransferERC721.sol"; import {SignatureExpired, InvalidNonce} from "../shared/PermitErrors.sol"; -import {Allowance_ERC721} from "./libraries/Allowance_ERC721.sol"; +import {AllowanceERC721} from "./libraries/AllowanceERC721.sol"; contract AllowanceTransferERC721 is IAllowanceTransferERC721, EIP712ERC721 { using SignatureVerification for bytes; - using PermitHash_ERC721 for PermitSingle; - using PermitHash_ERC721 for PermitBatch; - using Allowance_ERC721 for PackedAllowance; + using PermitHashERC721 for PermitSingle; + using PermitHashERC721 for PermitBatch; + using PermitHashERC721 for PermitAll; + using AllowanceERC721 for PackedAllowance; - /// @notice Maps users to tokens to spender addresses and information about the approval on the token - /// @dev Indexed in the order of token owner address, token address, spender address - /// @dev The stored word saves the allowed tokenId, expiration on the allowance, and nonce - mapping(address => mapping(address => mapping(address => PackedAllowance))) public allowance; + /// @notice Maps users to tokens to tokenId and information about the approval, including the approved spender, on the token + /// @dev Indexed in the order of token owner address, token address, and tokenId + /// @dev The stored word saves the allowed spender, expiration on the allowance, and nonce + mapping(address => mapping(address => mapping(uint256 => PackedAllowance))) public allowance; + + /// @notice Maps users to tokens to spender and sets whether or not the spender has operator status on an entire token collection. + /// @dev Indexed in the order of token owner address, token address, then spender address. + /// @dev Sets a timestamp at which the spender no longer has operator status. Max expiration is type(uint48).max + mapping(address => mapping(address => mapping(address => PackedOperatorAllowance))) public operators; /// @inheritdoc IAllowanceTransferERC721 - function approve(address token, address spender, uint160 tokenId, uint48 expiration) external { - PackedAllowance storage allowed = allowance[msg.sender][token][spender]; - allowed.updateTokenIdAndExpiration(tokenId, expiration); + function approve(address token, address spender, uint256 tokenId, uint48 expiration) external { + PackedAllowance storage allowed = allowance[msg.sender][token][tokenId]; + allowed.updateSpenderAndExpiration(spender, expiration); emit Approval(msg.sender, token, spender, tokenId, expiration); } + /// @inheritdoc IAllowanceTransferERC721 + function setApprovalForAll(address token, address spender, uint48 expiration) external { + operators[msg.sender][token][spender].expiration = expiration; + emit ApprovalForAll(msg.sender, token, spender, expiration); + } + /// @inheritdoc IAllowanceTransferERC721 function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external { if (block.timestamp > permitSingle.sigDeadline) revert SignatureExpired(permitSingle.sigDeadline); @@ -54,7 +66,21 @@ contract AllowanceTransferERC721 is IAllowanceTransferERC721, EIP712ERC721 { } /// @inheritdoc IAllowanceTransferERC721 - function transferFrom(address from, address to, uint160 tokenId, address token) external { + function permit(address owner, PermitAll memory permitAll, bytes calldata signature) external { + if (block.timestamp > permitAll.sigDeadline) revert SignatureExpired(permitAll.sigDeadline); + + // Verify the signer address from the signature. + signature.verify(_hashTypedData(permitAll.hash()), owner); + + PackedOperatorAllowance storage operator = operators[msg.sender][permitAll.token][permitAll.spender]; + unchecked { + operator.nonce += 1; + } + operator.expiration = permitAll.expiration; + } + + /// @inheritdoc IAllowanceTransferERC721 + function transferFrom(address from, address to, uint256 tokenId, address token) external { _transfer(from, to, tokenId, token); } @@ -70,20 +96,27 @@ contract AllowanceTransferERC721 is IAllowanceTransferERC721, EIP712ERC721 { } /// @notice Internal function for transferring tokens using stored allowances + /// @dev msg.sender must have tokenId level permissions through the `allowance` mapping OR operator permissions through the `operators` mapping. /// @dev Will fail if the allowed timeframe has passed - function _transfer(address from, address to, uint160 tokenId, address token) private { - PackedAllowance storage allowed = allowance[from][token][msg.sender]; - - if (block.timestamp > allowed.expiration) revert AllowanceExpired(allowed.expiration); + function _transfer(address from, address to, uint256 tokenId, address token) private { + PackedAllowance storage allowed = allowance[from][token][tokenId]; + uint48 operatorExpiration = operators[from][token][msg.sender].expiration; + bool operatorExpired = block.timestamp > operatorExpiration; + + // At least one of the approval methods must not be expired. + if (block.timestamp > allowed.expiration && operatorExpired) { + revert AllowanceExpired(allowed.expiration, operatorExpiration); + } - uint256 permittedTokenId = allowed.tokenId; - if (permittedTokenId != type(uint160).max) { - if (permittedTokenId != tokenId) { + if (allowed.spender != msg.sender) { + if (operatorExpired) { + // If there is no tokenId permissions and no operator permissions on msg.sender + // then the msg.sender has insufficient allowance. revert InsufficientAllowance(token, tokenId); - } else { - // If a single tokenId has been approved, reset the permissions on the tokenId to be transferred - allowed.tokenId = 0; } + } else { + // Since msg.sender has given tokenId permissions to the spender, reset the tokenId permissions before the transfer. + allowed.spender = address(0); } // Transfer the token from the from address to the recipient. @@ -91,24 +124,51 @@ contract AllowanceTransferERC721 is IAllowanceTransferERC721, EIP712ERC721 { } /// @inheritdoc IAllowanceTransferERC721 - function lockdown(TokenSpenderPair[] calldata approvals) external { + function lockdown(TokenSpenderPair[] calldata operatorApprovals, TokenAndIdPair[] calldata tokenIdApprovals) + external + { address owner = msg.sender; - // Revoke allowances for each pair of spenders and tokens. + // Revoke operator allowances for each pair of spenders and tokens. unchecked { - uint256 length = approvals.length; + uint256 length = operatorApprovals.length; for (uint256 i = 0; i < length; ++i) { - address token = approvals[i].token; - address spender = approvals[i].spender; + address token = operatorApprovals[i].token; + address spender = operatorApprovals[i].spender; - allowance[owner][token][spender].tokenId = 0; + operators[owner][token][spender].expiration = 0; emit Lockdown(owner, token, spender); } } + // Revoke tokenId allowances for each pair of token and tokenId. + unchecked { + uint256 length = tokenIdApprovals.length; + for (uint256 i = 0; i < length; ++i) { + address token = tokenIdApprovals[i].token; + uint256 tokenId = tokenIdApprovals[i].tokenId; + allowance[owner][token][tokenId].expiration = 0; + } + } } /// @inheritdoc IAllowanceTransferERC721 function invalidateNonces(address token, address spender, uint48 newNonce) external { - uint48 oldNonce = allowance[msg.sender][token][spender].nonce; + uint48 oldNonce = operators[msg.sender][token][spender].nonce; + + if (newNonce <= oldNonce) revert InvalidNonce(); + + // Limit the amount of nonces that can be invalidated in one transaction. + unchecked { + uint48 delta = newNonce - oldNonce; + if (delta > type(uint16).max) revert ExcessiveInvalidation(); + } + + operators[msg.sender][token][spender].nonce = newNonce; + emit NonceInvalidation(msg.sender, token, uint256(uint160(spender)), newNonce, oldNonce); + } + + /// @inheritdoc IAllowanceTransferERC721 + function invalidateNonces(address token, uint256 tokenId, uint48 newNonce) external { + uint48 oldNonce = allowance[msg.sender][token][tokenId].nonce; if (newNonce <= oldNonce) revert InvalidNonce(); @@ -118,8 +178,8 @@ contract AllowanceTransferERC721 is IAllowanceTransferERC721, EIP712ERC721 { if (delta > type(uint16).max) revert ExcessiveInvalidation(); } - allowance[msg.sender][token][spender].nonce = newNonce; - emit NonceInvalidation(msg.sender, token, spender, newNonce, oldNonce); + allowance[msg.sender][token][tokenId].nonce = newNonce; + emit NonceInvalidation(msg.sender, token, tokenId, newNonce, oldNonce); } /// @notice Sets the new values for tokenId, expiration, and nonce. @@ -128,13 +188,13 @@ contract AllowanceTransferERC721 is IAllowanceTransferERC721, EIP712ERC721 { function _updateApproval(PermitDetails memory details, address owner, address spender) private { uint48 nonce = details.nonce; address token = details.token; - uint160 tokenId = details.tokenId; + uint256 tokenId = details.tokenId; uint48 expiration = details.expiration; - PackedAllowance storage allowed = allowance[owner][token][spender]; + PackedAllowance storage allowed = allowance[owner][token][tokenId]; if (allowed.nonce != nonce) revert InvalidNonce(); - allowed.updateAll(tokenId, expiration, nonce); + allowed.updateAll(spender, expiration, nonce); emit Permit(owner, token, spender, tokenId, expiration, nonce); } } diff --git a/src/ERC721/SignatureTransferERC721.sol b/src/ERC721/SignatureTransferERC721.sol index da510ec4..e2b7ae00 100644 --- a/src/ERC721/SignatureTransferERC721.sol +++ b/src/ERC721/SignatureTransferERC721.sol @@ -6,13 +6,13 @@ import {SignatureExpired, InvalidNonce} from "../shared/PermitErrors.sol"; import {ERC721} from "solmate/src/tokens/ERC721.sol"; import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; import {SignatureVerification} from "../shared/SignatureVerification.sol"; -import {PermitHash_ERC721} from "./libraries/PermitHash_ERC721.sol"; +import {PermitHashERC721} from "./libraries/PermitHashERC721.sol"; import {EIP712ERC721} from "./EIP712ERC721.sol"; contract SignatureTransferERC721 is ISignatureTransferERC721, EIP712ERC721 { using SignatureVerification for bytes; - using PermitHash_ERC721 for PermitTransferFrom; - using PermitHash_ERC721 for PermitBatchTransferFrom; + using PermitHashERC721 for PermitTransferFrom; + using PermitHashERC721 for PermitBatchTransferFrom; /// @inheritdoc ISignatureTransferERC721 mapping(address => mapping(uint256 => uint256)) public nonceBitmap; diff --git a/src/ERC721/interfaces/IAllowanceTransferERC721.sol b/src/ERC721/interfaces/IAllowanceTransferERC721.sol index 9a08b43f..946e3088 100644 --- a/src/ERC721/interfaces/IAllowanceTransferERC721.sol +++ b/src/ERC721/interfaces/IAllowanceTransferERC721.sol @@ -6,8 +6,9 @@ pragma solidity ^0.8.17; /// @dev Requires user's token approval on the Permit2 contract interface IAllowanceTransferERC721 { /// @notice Thrown when an allowance on a token has expired. - /// @param deadline The timestamp at which the permissions on the token are no longer valid - error AllowanceExpired(uint256 deadline); + /// @param allowanceDeadline The timestamp at which the permissions on the token for a specific tokenId are no longer valid + /// @param operatorDeadline The timestamp at which the permissions given to an operator of an entire collection are no longer valid. + error AllowanceExpired(uint256 allowanceDeadline, uint256 operatorDeadline); /// @notice Thrown when there is no allowance for a token. /// @param token The address of the token and tokenId @@ -16,22 +17,30 @@ interface IAllowanceTransferERC721 { /// @notice Thrown when too many nonces are invalidated. error ExcessiveInvalidation(); - /// @notice Emits an event when the owner successfully invalidates an ordered nonce. + /// @notice Emits an event when the owner successfully invalidates an ordered nonce on the operator mapping. event NonceInvalidation( address indexed owner, address indexed token, address indexed spender, uint48 newNonce, uint48 oldNonce ); + /// @notice Emits an event when the owner successfully invalidates an ordered nonce on the allowance mapping. + event NonceInvalidation( + address indexed owner, address indexed token, uint256 indexed tokenId, uint48 newNonce, uint48 oldNonce + ); + /// @notice Emits an event when the owner successfully sets permissions on a token for the spender. event Approval( - address indexed owner, address indexed token, address indexed spender, uint160 tokenId, uint48 expiration + address indexed owner, address indexed token, address indexed spender, uint256 tokenId, uint48 expiration ); + /// @notice Emits an event when the owner successfully gives a spender operator permissions on a token. + event ApprovalForAll(address indexed owner, address indexed token, address indexed spender, uint48 expiration); + /// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender. event Permit( address indexed owner, address indexed token, address indexed spender, - uint160 tokenId, + uint256 tokenId, uint48 expiration, uint48 nonce ); @@ -40,15 +49,14 @@ interface IAllowanceTransferERC721 { event Lockdown(address indexed owner, address token, address spender); /// @notice The permit data for a token - /// @dev Setting tokenId to type(uint160).max would set operator approval on the spender. struct PermitDetails { // ERC20 token address address token; // the tokenId allowed to spend - uint160 tokenId; + uint256 tokenId; // timestamp at which a spender's token allowances become invalid uint48 expiration; - // an incrementing value indexed per owner,token,and spender for each signature + // an incrementing value indexed per owner,token,and tokenId for each signature uint48 nonce; } @@ -72,18 +80,38 @@ interface IAllowanceTransferERC721 { uint256 sigDeadline; } - /// @notice The saved permissions - /// @dev This info is saved per owner, per token, per spender and all signed over in the permit message - /// @dev Setting tokenId to type(uint160).max sets an operator approval. This means that the spender can transfer any tokenId you own in the collection. + /// @notice The permit message signed to set an operator for the token. + struct PermitAll { + // address of the token collection + address token; + // address of the spender who will act as an operator on all tokenIds owned by the signer for the token collection + address spender; + // expiration of the operator permissions + uint48 expiration; + // an incrementing value indexed per owner, per token, per spender + uint48 nonce; + // deadline on the permit signature + uint256 sigDeadline; + } + + /// @notice The saved permissions on the allowance mapping + /// @dev This info is saved per owner, per token, per tokenId and all signed over in the permit message struct PackedAllowance { - // tokenId allowed - uint160 tokenId; + // spender allowed + address spender; // permission expiry uint48 expiration; // an incrementing value indexed per owner,token,and spender for each signature uint48 nonce; } + /// @notice The saved expiration on the operator. + /// @dev Holds a nonce value to prevent replay protection. + struct PackedOperatorAllowance { + uint48 expiration; + uint48 nonce; + } + /// @notice A token spender pair. struct TokenSpenderPair { // the token the spender is approved @@ -92,6 +120,14 @@ interface IAllowanceTransferERC721 { address spender; } + /// @notice A token and tokenId pair. + struct TokenAndIdPair { + // the token collection address + address token; + // the tokenId + uint256 tokenId; + } + /// @notice Details for a token transfer. struct AllowanceTransferDetails { // the owner of the token @@ -99,15 +135,19 @@ interface IAllowanceTransferERC721 { // the recipient of the token address to; // the tokenId of the token - uint160 tokenId; + uint256 tokenId; // the token to be transferred address token; } - /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval. - /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress] - /// @dev The packed slot holds the allowed tokenId, expiration at which the permissions on the tokenId is no longer valid, and current nonce thats updated on any signature based approvals. - function allowance(address, address, address) external view returns (uint160, uint48, uint48); + /// @notice A mapping from owner address to token address to tokenId to PackedAllowance struct, which contains details and conditions of the approval. + /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][tokenId] + /// @dev The packed slot holds the allowed spender, expiration at which the permissions on the tokenId is no longer valid, and current nonce thats updated on any signature based approvals. + function allowance(address, address, uint256) external view returns (address, uint48, uint48); + + /// @notice A mapping from owner address to token address to spender address to a PackedOperatorAllowance struct, which contains the expiration of the operator approval. + /// @notice The mapping is indexed in the above order see: operator[ownerAddress][tokenAddress][spenderAddress] + function operators(address, address, address) external view returns (uint48, uint48); /// @notice Approves the spender to transfer the tokenId of the specified token up until the expiration /// @param token The token to approve @@ -115,8 +155,16 @@ interface IAllowanceTransferERC721 { /// @param tokenId The approved tokenId of the token /// @param expiration The timestamp at which the approval is no longer valid /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve - /// @dev Setting tokenId to type(uint160).max sets an operator approval - function approve(address token, address spender, uint160 tokenId, uint48 expiration) external; + /// @dev Passing in expiration as 0 sets the expiration to the block.timestamp + function approve(address token, address spender, uint256 tokenId, uint48 expiration) external; + + /// @notice Approves the spender to be an operator of the specified token up until the expiration + /// @param token The token to approve + /// @param spender The spender address to approve + /// @param expiration The timestamp at which the operator approval is no longer valid + /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve + /// @dev Passing in expiration as 0 DOES NOT set the expiration to the block.timestamp unlike `approve`. + function setApprovalForAll(address token, address spender, uint48 expiration) external; /// @notice Permit a spender to a given tokenId of the owners token via the owner's EIP-712 signature /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce @@ -132,6 +180,13 @@ interface IAllowanceTransferERC721 { /// @param signature The owner's signature over the permit data function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external; + /// @notice Permit a spender to be an operator of the owners tokens via the owner's EIP-712 signature + /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce + /// @param owner The owner of the tokens being approved + /// @param permitAll Data signed over by the owner specifying the terms of approval + /// @param signature The owner's signature over the permit data + function permit(address owner, PermitAll memory permitAll, bytes calldata signature) external; + /// @notice Transfer approved tokens from one address to another /// @param from The address to transfer from /// @param to The address of the recipient @@ -139,7 +194,7 @@ interface IAllowanceTransferERC721 { /// @param token The token address to transfer /// @dev Requires the from address to have approved the desired tokenId or be an operator /// of the token to msg.sender. - function transferFrom(address from, address to, uint160 tokenId, address token) external; + function transferFrom(address from, address to, uint256 tokenId, address token) external; /// @notice Transfer approved tokens in a batch /// @param transferDetails Array of owners, recipients, tokenIds, and tokens for the transfers @@ -149,8 +204,11 @@ interface IAllowanceTransferERC721 { /// @notice Enables performing a "lockdown" of the sender's Permit2 identity /// by batch revoking approvals - /// @param approvals Array of approvals to revoke. - function lockdown(TokenSpenderPair[] calldata approvals) external; + /// @param operatorApprovals Array of operator approvals to revoke. + /// @param tokenIdApprovals Array of tokenId approvals to revoke. + /// @dev Expires the allowances on each of the approval mappings, the operator and allowance mappings respectively. + function lockdown(TokenSpenderPair[] calldata operatorApprovals, TokenAndIdPair[] calldata tokenIdApprovals) + external; /// @notice Invalidate nonces for a given (token, spender) pair /// @param token The token to invalidate nonces for @@ -158,4 +216,11 @@ interface IAllowanceTransferERC721 { /// @param newNonce The new nonce to set. Invalidates all nonces less than it. /// @dev Can't invalidate more than 2**16 nonces per transaction. function invalidateNonces(address token, address spender, uint48 newNonce) external; + + /// @notice Invalidate nonces for a given (token, tokenId) pair + /// @param token The token to invalidate nonces for + /// @param tokenId The tokenId to invalidate nonces for + /// @param newNonce The new nonce to set. Invalidates all nonces less than it. + /// @dev Can't invalidate more than 2**16 nonces per transaction. + function invalidateNonces(address token, uint256 tokenId, uint48 newNonce) external; } diff --git a/src/ERC721/libraries/Allowance_ERC721.sol b/src/ERC721/libraries/AllowanceERC721.sol similarity index 79% rename from src/ERC721/libraries/Allowance_ERC721.sol rename to src/ERC721/libraries/AllowanceERC721.sol index 0af7fa60..fae16a02 100644 --- a/src/ERC721/libraries/Allowance_ERC721.sol +++ b/src/ERC721/libraries/AllowanceERC721.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.17; import {IAllowanceTransferERC721} from "../interfaces/IAllowanceTransferERC721.sol"; -library Allowance_ERC721 { +library AllowanceERC721 { // note if the expiration passed is 0, then it the approval set to the block.timestamp uint256 private constant BLOCK_TIMESTAMP_EXPIRATION = 0; @@ -12,7 +12,7 @@ library Allowance_ERC721 { /// @dev If the inputted expiration is 0, the stored expiration is set to block.timestamp function updateAll( IAllowanceTransferERC721.PackedAllowance storage allowed, - uint160 tokenId, + address spender, uint48 expiration, uint48 nonce ) internal { @@ -23,26 +23,26 @@ library Allowance_ERC721 { uint48 storedExpiration = expiration == BLOCK_TIMESTAMP_EXPIRATION ? uint48(block.timestamp) : expiration; - uint256 word = pack(tokenId, storedExpiration, storedNonce); + uint256 word = pack(spender, storedExpiration, storedNonce); assembly { sstore(allowed.slot, word) } } - /// @notice Sets the allowed amount and expiry of the spender's permissions on owner's token. + /// @notice Sets the expiry of the spender's permissions on owner's token. /// @dev Nonce does not need to be incremented. - function updateTokenIdAndExpiration( + function updateSpenderAndExpiration( IAllowanceTransferERC721.PackedAllowance storage allowed, - uint160 tokenId, + address spender, uint48 expiration ) internal { // If the inputted expiration is 0, the allowance only lasts the duration of the block. allowed.expiration = expiration == 0 ? uint48(block.timestamp) : expiration; - allowed.tokenId = tokenId; + allowed.spender = spender; } /// @notice Computes the packed slot of the amount, expiration, and nonce that make up PackedAllowance - function pack(uint160 tokenId, uint48 expiration, uint48 nonce) internal pure returns (uint256 word) { - word = (uint256(nonce) << 208) | uint256(expiration) << 160 | tokenId; + function pack(address spender, uint48 expiration, uint48 nonce) internal pure returns (uint256 word) { + word = (uint256(nonce) << 208) | uint256(expiration) << 160 | uint160(spender); } } diff --git a/src/ERC721/libraries/PermitHash_ERC721.sol b/src/ERC721/libraries/PermitHashERC721.sol similarity index 87% rename from src/ERC721/libraries/PermitHash_ERC721.sol rename to src/ERC721/libraries/PermitHashERC721.sol index 63b091af..9fbba466 100644 --- a/src/ERC721/libraries/PermitHash_ERC721.sol +++ b/src/ERC721/libraries/PermitHashERC721.sol @@ -4,18 +4,21 @@ pragma solidity ^0.8.17; import {IAllowanceTransferERC721} from "../interfaces/IAllowanceTransferERC721.sol"; import {ISignatureTransferERC721} from "../interfaces/ISignatureTransferERC721.sol"; -library PermitHash_ERC721 { +library PermitHashERC721 { bytes32 public constant _PERMIT_DETAILS_TYPEHASH = - keccak256("PermitDetails(address token,uint160 tokenId,uint48 expiration,uint48 nonce)"); + keccak256("PermitDetails(address token,uint256 tokenId,uint48 expiration,uint48 nonce)"); bytes32 public constant _PERMIT_SINGLE_TYPEHASH = keccak256( - "PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 tokenId,uint48 expiration,uint48 nonce)" + "PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint256 tokenId,uint48 expiration,uint48 nonce)" ); bytes32 public constant _PERMIT_BATCH_TYPEHASH = keccak256( - "PermitBatch(PermitDetails[] details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 tokenId,uint48 expiration,uint48 nonce)" + "PermitBatch(PermitDetails[] details,address spender,uint256 sigDeadline)PermitDetails(address token,uint256 tokenId,uint48 expiration,uint48 nonce)" ); + bytes32 public constant _PERMIT_ALL_TYPEHASH = + keccak256("PermitAll(address token,address spender,uint48 expiration,uint48 nonce,uint256 sigDeadline)"); + bytes32 public constant _TOKEN_PERMISSIONS_TYPEHASH = keccak256("TokenPermissions(address token,uint256 tokenId)"); bytes32 public constant _PERMIT_TRANSFER_FROM_TYPEHASH = keccak256( @@ -56,6 +59,19 @@ library PermitHash_ERC721 { ); } + function hash(IAllowanceTransferERC721.PermitAll memory permitAll) internal pure returns (bytes32) { + return keccak256( + abi.encode( + _PERMIT_ALL_TYPEHASH, + permitAll.token, + permitAll.spender, + permitAll.expiration, + permitAll.nonce, + permitAll.sigDeadline + ) + ); + } + function hash(ISignatureTransferERC721.PermitTransferFrom memory permit) internal view returns (bytes32) { bytes32 tokenPermissionsHash = _hashTokenPermissions(permit.permitted); return keccak256( From 3efe5a8561ab22457b300a4b32e97d9ea4e5259e Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 6 Jan 2023 17:19:16 -0500 Subject: [PATCH 06/27] try to share unit bitmap tests --- .../{Permit2.sol => Permit2_ERC721.sol} | 0 test/AllowanceUnitTest.sol | 2 +- test/mocks/IMockPermit2.sol | 28 ++++++++++++ test/mocks/MockPermit2.sol | 36 +++++++-------- test/mocks/MockPermit2_ERC721.sol | 44 +++++++++++++++++++ .../BaseNonceBitmapTest.t.sol} | 12 +++-- test/shared/NonceBitmapTest_ERC20.t.sol | 12 +++++ test/shared/NonceBitmapTest_ERC721.t.sol | 12 +++++ 8 files changed, 117 insertions(+), 29 deletions(-) rename src/ERC721/{Permit2.sol => Permit2_ERC721.sol} (100%) create mode 100644 test/mocks/IMockPermit2.sol create mode 100644 test/mocks/MockPermit2_ERC721.sol rename test/{NonceBitmap.t.sol => shared/BaseNonceBitmapTest.t.sol} (94%) create mode 100644 test/shared/NonceBitmapTest_ERC20.t.sol create mode 100644 test/shared/NonceBitmapTest_ERC721.t.sol diff --git a/src/ERC721/Permit2.sol b/src/ERC721/Permit2_ERC721.sol similarity index 100% rename from src/ERC721/Permit2.sol rename to src/ERC721/Permit2_ERC721.sol diff --git a/test/AllowanceUnitTest.sol b/test/AllowanceUnitTest.sol index 2aa0d170..03e05f34 100644 --- a/test/AllowanceUnitTest.sol +++ b/test/AllowanceUnitTest.sol @@ -21,7 +21,7 @@ contract AllowanceUnitTest is Test, TokenProvider { (,, uint48 nonce) = permit2.allowance(from, token, spender); - permit2.mockUpdateAmountAndExpiration(from, token, spender, amount, expiration); + permit2.mockUpdateSome(from, token, spender, amount, expiration); uint48 timestampAfterUpdate = expiration == 0 ? uint48(block.timestamp) : expiration; diff --git a/test/mocks/IMockPermit2.sol b/test/mocks/IMockPermit2.sol new file mode 100644 index 00000000..9c295b52 --- /dev/null +++ b/test/mocks/IMockPermit2.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {Permit2} from "../../src/ERC20/Permit2.sol"; +import {IAllowanceTransfer} from "../../src/ERC20/interfaces/IAllowanceTransfer.sol"; +import {ISignatureTransfer} from "../../src/ERC20/interfaces/ISignatureTransfer.sol"; +import {Allowance} from "../../src/ERC20/libraries/Allowance.sol"; + +abstract contract IMockPermit2 { + function doStore(address from, address token, address spender, uint256 word) public virtual {} + + function getStore(address from, address token, address spender) public view virtual returns (uint256 word) {} + + function mockUpdateSome(address from, address token, address spender, uint160 data, uint48 expiration) + public + virtual + {} + + function mockUpdateAll(address from, address token, address spender, uint160 data, uint48 expiration, uint48 nonce) + public + virtual + {} + + function useUnorderedNonce(address from, uint256 nonce) public virtual {} + + // stuck here bc this is defined at Permit2 but not the interface + // function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external {} +} diff --git a/test/mocks/MockPermit2.sol b/test/mocks/MockPermit2.sol index b1e26f34..002acea4 100644 --- a/test/mocks/MockPermit2.sol +++ b/test/mocks/MockPermit2.sol @@ -4,46 +4,40 @@ pragma solidity ^0.8.17; import {Permit2} from "../../src/ERC20/Permit2.sol"; import {IAllowanceTransfer} from "../../src/ERC20/interfaces/IAllowanceTransfer.sol"; import {Allowance} from "../../src/ERC20/libraries/Allowance.sol"; +import {IMockPermit2} from "../mocks/IMockPermit2.sol"; -contract MockPermit2 is Permit2 { - function doStore(address from, address token, address spender, uint256 word) public { +contract MockPermit2 is IMockPermit2, Permit2 { + function doStore(address from, address token, address spender, uint256 word) public override { IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; assembly { sstore(allowed.slot, word) } } - function getStore(address from, address token, address spender) public view returns (uint256 word) { + function getStore(address from, address token, address spender) public view override returns (uint256 word) { IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; assembly { word := sload(allowed.slot) } } - function mockUpdateAmountAndExpiration( - address from, - address token, - address spender, - uint160 amount, - uint48 expiration - ) public { + function mockUpdateSome(address from, address token, address spender, uint160 data, uint48 expiration) + public + override + { IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; - Allowance.updateAmountAndExpiration(allowed, amount, expiration); + Allowance.updateAmountAndExpiration(allowed, data, expiration); } - function mockUpdateAll( - address from, - address token, - address spender, - uint160 amount, - uint48 expiration, - uint48 nonce - ) public { + function mockUpdateAll(address from, address token, address spender, uint160 data, uint48 expiration, uint48 nonce) + public + override + { IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; - Allowance.updateAll(allowed, amount, expiration, nonce); + Allowance.updateAll(allowed, data, expiration, nonce); } - function useUnorderedNonce(address from, uint256 nonce) public { + function useUnorderedNonce(address from, uint256 nonce) public override { _useUnorderedNonce(from, nonce); } } diff --git a/test/mocks/MockPermit2_ERC721.sol b/test/mocks/MockPermit2_ERC721.sol new file mode 100644 index 00000000..27d38185 --- /dev/null +++ b/test/mocks/MockPermit2_ERC721.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {Permit2_ERC721} from "../../src/ERC721/Permit2_ERC721.sol"; +import {IAllowanceTransfer_ERC721} from "../../src/ERC721/interfaces/IAllowanceTransfer_ERC721.sol"; +import {SignatureTransfer_ERC721} from "../../src/ERC721/SignatureTransfer_ERC721.sol"; +import {Allowance_ERC721} from "../../src/ERC721/libraries/Allowance_ERC721.sol"; +import {IMockPermit2} from "../mocks/IMockPermit2.sol"; + +contract MockPermit2_ERC721 is IMockPermit2, Permit2_ERC721 { + function doStore(address from, address token, address spender, uint256 word) public override { + IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + assembly { + sstore(allowed.slot, word) + } + } + + function getStore(address from, address token, address spender) public view override returns (uint256 word) { + IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + assembly { + word := sload(allowed.slot) + } + } + + function mockUpdateSome(address from, address token, address spender, uint160 data, uint48 expiration) + public + override + { + IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + Allowance_ERC721.updateTokenIdAndExpiration(allowed, data, expiration); + } + + function mockUpdateAll(address from, address token, address spender, uint160 data, uint48 expiration, uint48 nonce) + public + override + { + IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + Allowance_ERC721.updateAll(allowed, data, expiration, nonce); + } + + function useUnorderedNonce(address from, uint256 nonce) public override { + _useUnorderedNonce(from, nonce); + } +} diff --git a/test/NonceBitmap.t.sol b/test/shared/BaseNonceBitmapTest.t.sol similarity index 94% rename from test/NonceBitmap.t.sol rename to test/shared/BaseNonceBitmapTest.t.sol index 6984e9f6..91c37b2f 100644 --- a/test/NonceBitmap.t.sol +++ b/test/shared/BaseNonceBitmapTest.t.sol @@ -3,15 +3,13 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {SafeERC20, IERC20, IERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; -import {MockPermit2} from "./mocks/MockPermit2.sol"; -import {InvalidNonce} from "../src/shared/PermitErrors.sol"; +import {IMockPermit2} from "../mocks/MockPermit2.sol"; +import {InvalidNonce} from "../../src/shared/PermitErrors.sol"; -contract NonceBitmapTest is Test { - MockPermit2 permit2; +contract BaseNonceBitmapTest is Test { + IMockPermit2 permit2; - function setUp() public { - permit2 = new MockPermit2(); - } + function setUp() public virtual {} function testLowNonces() public { permit2.useUnorderedNonce(address(this), 5); diff --git a/test/shared/NonceBitmapTest_ERC20.t.sol b/test/shared/NonceBitmapTest_ERC20.t.sol new file mode 100644 index 00000000..09d311f8 --- /dev/null +++ b/test/shared/NonceBitmapTest_ERC20.t.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {BaseNonceBitmapTest} from "./BaseNonceBitmapTest.t.sol"; +import {MockPermit2} from "../mocks/MockPermit2.sol"; + +contract NonceBitmapTest_ERC20 is BaseNonceBitmapTest { + function setUp() public override { + permit2 = new MockPermit2(); + } +} diff --git a/test/shared/NonceBitmapTest_ERC721.t.sol b/test/shared/NonceBitmapTest_ERC721.t.sol new file mode 100644 index 00000000..e39ad348 --- /dev/null +++ b/test/shared/NonceBitmapTest_ERC721.t.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {BaseNonceBitmapTest} from "./BaseNonceBitmapTest.t.sol"; +import {MockPermit2_ERC721} from "../mocks/MockPermit2_ERC721.sol"; + +contract NonceBitmapTest_ERC721 is BaseNonceBitmapTest { + function setUp() public override { + permit2 = new MockPermit2_ERC721(); + } +} From 1ea812cac45bb11628a739c9f59e8ed1b7f1df5e Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 6 Jan 2023 17:42:15 -0500 Subject: [PATCH 07/27] 721 bitmap tests --- .gas-snapshot | 33 +++++++++++++++--------- test/shared/BaseNonceBitmapTest.t.sol | 25 ++++++++++-------- test/shared/NonceBitmapTest_ERC20.t.sol | 8 ++++++ test/shared/NonceBitmapTest_ERC721.t.sol | 8 ++++++ 4 files changed, 51 insertions(+), 23 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 463ca7d9..8829d0d5 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -29,22 +29,13 @@ AllowanceTransferTest:testSetAllowanceInvalidSignature() (gas: 64071) AllowanceTransferTest:testSetAllowanceTransfer() (gas: 103161) AllowanceTransferTest:testSetAllowanceTransferDirtyNonceDirtyTransfer() (gas: 97432) AllowanceTransferTest:testTransferFromWithGasSnapshot() (gas: 133004) -AllowanceUnitTest:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38998, ~: 39076) -AllowanceUnitTest:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40222, ~: 40223) -AllowanceUnitTest:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39224, ~: 39225) +AllowanceUnitTest:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38976, ~: 39054) +AllowanceUnitTest:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40200, ~: 40201) +AllowanceUnitTest:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39290, ~: 39291) CompactSignature:testCompactSignature27() (gas: 253) CompactSignature:testCompactSignature28() (gas: 141) EIP712Test:testDomainSeparator() (gas: 5804) EIP712Test:testDomainSeparatorAfterFork() (gas: 10787) -NonceBitmapTest:testHighNonces() (gas: 36186) -NonceBitmapTest:testInvalidateFullWord() (gas: 63125) -NonceBitmapTest:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30308, ~: 31008) -NonceBitmapTest:testInvalidateNonzeroWord() (gas: 85665) -NonceBitmapTest:testInvalidateTwoNoncesRandomly(uint248,uint256,uint256) (runs: 256, μ: 39187, ~: 39187) -NonceBitmapTest:testLowNonces() (gas: 41004) -NonceBitmapTest:testNonceWordBoundary() (gas: 42203) -NonceBitmapTest:testUseTwoRandomNonces(uint256,uint256) (runs: 256, μ: 49205, ~: 51640) -NonceBitmapTest:testUsingNonceTwiceFails(uint256) (runs: 256, μ: 21866, ~: 21889) Permit2LibTest:testOZSafePermit() (gas: 24509) Permit2LibTest:testOZSafePermitPlusOZSafeTransferFrom() (gas: 129197) Permit2LibTest:testOZSafeTransferFrom() (gas: 38919) @@ -107,3 +98,21 @@ TypehashGeneration:testPermitTransferFromWithWitness() (gas: 43369) TypehashGeneration:testPermitTransferFromWithWitnessIncorrectPermitData() (gas: 43430) TypehashGeneration:testPermitTransferFromWithWitnessIncorrectTypehashStub() (gas: 43833) MockPermit2Lib:testPermit2Code(address):(bool) (runs: 256, μ: 3025, ~: 3016) +NonceBitmapTest_ERC20:testHighNonces() (gas: 36142) +NonceBitmapTest_ERC20:testInvalidateFullWord() (gas: 63031) +NonceBitmapTest_ERC20:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30335, ~: 31035) +NonceBitmapTest_ERC20:testInvalidateNonzeroWord() (gas: 85489) +NonceBitmapTest_ERC20:testInvalidateTwoNoncesRandomly(uint248,uint256,uint256) (runs: 256, μ: 39067, ~: 39067) +NonceBitmapTest_ERC20:testLowNonces() (gas: 40894) +NonceBitmapTest_ERC20:testNonceWordBoundary() (gas: 42168) +NonceBitmapTest_ERC20:testUseTwoRandomNonces(uint256,uint256) (runs: 256, μ: 49018, ~: 51624) +NonceBitmapTest_ERC20:testUsingNonceTwiceFails(uint256) (runs: 256, μ: 21866, ~: 21889) +NonceBitmapTest_ERC721:testHighNonces() (gas: 36142) +NonceBitmapTest_ERC721:testInvalidateFullWord() (gas: 63031) +NonceBitmapTest_ERC721:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30335, ~: 31035) +NonceBitmapTest_ERC721:testInvalidateNonzeroWord() (gas: 85489) +NonceBitmapTest_ERC721:testInvalidateTwoNoncesRandomly(uint248,uint256,uint256) (runs: 256, μ: 39067, ~: 39067) +NonceBitmapTest_ERC721:testLowNonces() (gas: 40894) +NonceBitmapTest_ERC721:testNonceWordBoundary() (gas: 42168) +NonceBitmapTest_ERC721:testUseTwoRandomNonces(uint256,uint256) (runs: 256, μ: 49189, ~: 51624) +NonceBitmapTest_ERC721:testUsingNonceTwiceFails(uint256) (runs: 256, μ: 21866, ~: 21889) diff --git a/test/shared/BaseNonceBitmapTest.t.sol b/test/shared/BaseNonceBitmapTest.t.sol index 91c37b2f..a15af389 100644 --- a/test/shared/BaseNonceBitmapTest.t.sol +++ b/test/shared/BaseNonceBitmapTest.t.sol @@ -6,7 +6,7 @@ import {SafeERC20, IERC20, IERC20Permit} from "openzeppelin-contracts/contracts/ import {IMockPermit2} from "../mocks/MockPermit2.sol"; import {InvalidNonce} from "../../src/shared/PermitErrors.sol"; -contract BaseNonceBitmapTest is Test { +abstract contract BaseNonceBitmapTest is Test { IMockPermit2 permit2; function setUp() public virtual {} @@ -46,7 +46,7 @@ contract BaseNonceBitmapTest is Test { } function testInvalidateFullWord() public { - permit2.invalidateUnorderedNonces(0, 2 ** 256 - 1); + invalidateUnorderedNonces(0, 2 ** 256 - 1); vm.expectRevert(InvalidNonce.selector); permit2.useUnorderedNonce(address(this), 0); @@ -60,7 +60,7 @@ contract BaseNonceBitmapTest is Test { } function testInvalidateNonzeroWord() public { - permit2.invalidateUnorderedNonces(1, 2 ** 256 - 1); + invalidateUnorderedNonces(1, 2 ** 256 - 1); permit2.useUnorderedNonce(address(this), 0); permit2.useUnorderedNonce(address(this), 254); @@ -89,22 +89,25 @@ contract BaseNonceBitmapTest is Test { } function testInvalidateNoncesRandomly(uint248 wordPos, uint256 mask) public { - permit2.invalidateUnorderedNonces(wordPos, mask); - assertEq(mask, permit2.nonceBitmap(address(this), wordPos)); + invalidateUnorderedNonces(wordPos, mask); + assertEq(mask, nonceBitmap(address(this), wordPos)); } function testInvalidateTwoNoncesRandomly(uint248 wordPos, uint256 startBitmap, uint256 mask) public { - permit2.invalidateUnorderedNonces(wordPos, startBitmap); - assertEq(startBitmap, permit2.nonceBitmap(address(this), wordPos)); + invalidateUnorderedNonces(wordPos, startBitmap); + assertEq(startBitmap, nonceBitmap(address(this), wordPos)); // invalidating with the mask changes the original bitmap uint256 finalBitmap = startBitmap | mask; - permit2.invalidateUnorderedNonces(wordPos, mask); - uint256 savedBitmap = permit2.nonceBitmap(address(this), wordPos); + invalidateUnorderedNonces(wordPos, mask); + uint256 savedBitmap = nonceBitmap(address(this), wordPos); assertEq(finalBitmap, savedBitmap); // invalidating with the same mask should do nothing - permit2.invalidateUnorderedNonces(wordPos, mask); - assertEq(savedBitmap, permit2.nonceBitmap(address(this), wordPos)); + invalidateUnorderedNonces(wordPos, mask); + assertEq(savedBitmap, nonceBitmap(address(this), wordPos)); } + + function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) public virtual; + function nonceBitmap(address addr, uint256 wordPos) public virtual returns (uint256); } diff --git a/test/shared/NonceBitmapTest_ERC20.t.sol b/test/shared/NonceBitmapTest_ERC20.t.sol index 09d311f8..37796130 100644 --- a/test/shared/NonceBitmapTest_ERC20.t.sol +++ b/test/shared/NonceBitmapTest_ERC20.t.sol @@ -9,4 +9,12 @@ contract NonceBitmapTest_ERC20 is BaseNonceBitmapTest { function setUp() public override { permit2 = new MockPermit2(); } + + function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) public override { + MockPermit2(address(permit2)).invalidateUnorderedNonces(wordPos, mask); + } + + function nonceBitmap(address addr, uint256 wordPos) public override returns (uint256) { + return MockPermit2(address(permit2)).nonceBitmap(addr, wordPos); + } } diff --git a/test/shared/NonceBitmapTest_ERC721.t.sol b/test/shared/NonceBitmapTest_ERC721.t.sol index e39ad348..980b3b45 100644 --- a/test/shared/NonceBitmapTest_ERC721.t.sol +++ b/test/shared/NonceBitmapTest_ERC721.t.sol @@ -9,4 +9,12 @@ contract NonceBitmapTest_ERC721 is BaseNonceBitmapTest { function setUp() public override { permit2 = new MockPermit2_ERC721(); } + + function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) public override { + MockPermit2_ERC721(address(permit2)).invalidateUnorderedNonces(wordPos, mask); + } + + function nonceBitmap(address addr, uint256 wordPos) public override returns (uint256) { + return MockPermit2_ERC721(address(permit2)).nonceBitmap(addr, wordPos); + } } From 57a9ae8f9268c9c1fae9cde3f237d73b1bd29614 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 6 Jan 2023 17:47:07 -0500 Subject: [PATCH 08/27] remove comment --- test/mocks/IMockPermit2.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/mocks/IMockPermit2.sol b/test/mocks/IMockPermit2.sol index 9c295b52..8db774c3 100644 --- a/test/mocks/IMockPermit2.sol +++ b/test/mocks/IMockPermit2.sol @@ -22,7 +22,4 @@ abstract contract IMockPermit2 { {} function useUnorderedNonce(address from, uint256 nonce) public virtual {} - - // stuck here bc this is defined at Permit2 but not the interface - // function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external {} } From d68dd545a2068148fef9e168fa80b739b5260951 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Mon, 9 Jan 2023 08:26:40 -0700 Subject: [PATCH 09/27] shared allowance test --- test/shared/AllowanceUnitTest_ERC20.t.sol | 27 ++++++++++++++ test/shared/AllowanceUnitTest_ERC721.t.sol | 27 ++++++++++++++ .../BaseAllowanceUnitTest.sol} | 35 ++++++++++--------- 3 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 test/shared/AllowanceUnitTest_ERC20.t.sol create mode 100644 test/shared/AllowanceUnitTest_ERC721.t.sol rename test/{AllowanceUnitTest.sol => shared/BaseAllowanceUnitTest.sol} (63%) diff --git a/test/shared/AllowanceUnitTest_ERC20.t.sol b/test/shared/AllowanceUnitTest_ERC20.t.sol new file mode 100644 index 00000000..51e59338 --- /dev/null +++ b/test/shared/AllowanceUnitTest_ERC20.t.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "../mocks/MockPermit2.sol"; +import {BaseAllowanceUnitTest} from "./BaseAllowanceUnitTest.sol"; +import {TokenProvider} from "../utils/TokenProvider.sol"; + +contract AllowanceUnitTest_ERC20 is BaseAllowanceUnitTest { + function setUp() public override { + permit2 = new MockPermit2(); + initializeERC20Tokens(); + } + + function allowance(address from, address token, address spender) + public + view + override + returns (uint160, uint48, uint48) + { + return MockPermit2(address(permit2)).allowance(from, token, spender); + } + + function token() public view override returns (address) { + return address(token1); + } +} diff --git a/test/shared/AllowanceUnitTest_ERC721.t.sol b/test/shared/AllowanceUnitTest_ERC721.t.sol new file mode 100644 index 00000000..98c0368e --- /dev/null +++ b/test/shared/AllowanceUnitTest_ERC721.t.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "../mocks/MockPermit2_ERC721.sol"; +import {BaseAllowanceUnitTest} from "./BaseAllowanceUnitTest.sol"; +import {TokenProvider} from "../utils/TokenProvider.sol"; + +contract AllowanceUnitTest_ERC721 is BaseAllowanceUnitTest { + function setUp() public override { + permit2 = new MockPermit2_ERC721(); + initializeNFTTokens(); + } + + function allowance(address from, address token, address spender) + public + view + override + returns (uint160, uint48, uint48) + { + return MockPermit2_ERC721(address(permit2)).allowance(from, token, spender); + } + + function token() public view override returns (address) { + return address(nft1); + } +} diff --git a/test/AllowanceUnitTest.sol b/test/shared/BaseAllowanceUnitTest.sol similarity index 63% rename from test/AllowanceUnitTest.sol rename to test/shared/BaseAllowanceUnitTest.sol index 03e05f34..a2319f66 100644 --- a/test/AllowanceUnitTest.sol +++ b/test/shared/BaseAllowanceUnitTest.sol @@ -2,30 +2,31 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; -import "./mocks/MockPermit2.sol"; -import {TokenProvider} from "./utils/TokenProvider.sol"; +import "../mocks/IMockPermit2.sol"; +import {TokenProvider} from "../utils/TokenProvider.sol"; -contract AllowanceUnitTest is Test, TokenProvider { - MockPermit2 permit2; +abstract contract BaseAllowanceUnitTest is Test, TokenProvider { + IMockPermit2 permit2; address from = address(0xBEEE); address spender = address(0xBBBB); - function setUp() public { - permit2 = new MockPermit2(); - initializeERC20Tokens(); - } + function setUp() public virtual {} + + function allowance(address from, address token, address spender) public virtual returns (uint160, uint48, uint48); + + function token() public virtual returns (address); function testUpdateAmountExpirationRandomly(uint160 amount, uint48 expiration) public { - address token = address(token1); + address token = token(); - (,, uint48 nonce) = permit2.allowance(from, token, spender); + (,, uint48 nonce) = allowance(from, token, spender); permit2.mockUpdateSome(from, token, spender, amount, expiration); uint48 timestampAfterUpdate = expiration == 0 ? uint48(block.timestamp) : expiration; - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token, spender); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender); assertEq(amount, amount1); assertEq(timestampAfterUpdate, expiration1); /// nonce shouldnt change @@ -37,14 +38,14 @@ contract AllowanceUnitTest is Test, TokenProvider { // we assume we will never be able to reach 2**48 vm.assume(nonce < type(uint48).max); - address token = address(token1); + address token = token(); permit2.mockUpdateAll(from, token, spender, amount, expiration, nonce); uint48 nonceAfterUpdate = nonce + 1; uint48 timestampAfterUpdate = expiration == 0 ? uint48(block.timestamp) : expiration; - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token, spender); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender); assertEq(amount, amount1); assertEq(timestampAfterUpdate, expiration1); @@ -54,18 +55,18 @@ contract AllowanceUnitTest is Test, TokenProvider { function testPackAndUnpack(uint160 amount, uint48 expiration, uint48 nonce) public { // pack some numbers uint256 word = Allowance.pack(amount, expiration, nonce); - + address token = token(); // store the raw word - permit2.doStore(from, address(token1), spender, word); + permit2.doStore(from, token, spender, word); // load it as a packed allowance - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), spender); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender); assertEq(amount, amount1); assertEq(expiration, expiration1); assertEq(nonce, nonce1); // get the stored word - uint256 word1 = permit2.getStore(from, address(token1), spender); + uint256 word1 = permit2.getStore(from, token, spender); assertEq(word, word1); } } From 875f6c9f6254067dee56bfe3eaa0f264df882977 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Mon, 9 Jan 2023 15:26:15 -0700 Subject: [PATCH 10/27] base allowance, erc20 passing: --- test/AllowanceTransferTest_ERC20.t.sol | 41 ++++ ....t.sol => BaseAllowanceTransferTest.t.sol} | 195 ++++++++---------- 2 files changed, 125 insertions(+), 111 deletions(-) create mode 100644 test/AllowanceTransferTest_ERC20.t.sol rename test/{AllowanceTransferTest.t.sol => BaseAllowanceTransferTest.t.sol} (86%) diff --git a/test/AllowanceTransferTest_ERC20.t.sol b/test/AllowanceTransferTest_ERC20.t.sol new file mode 100644 index 00000000..d1bed12c --- /dev/null +++ b/test/AllowanceTransferTest_ERC20.t.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {BaseAllowanceTransferTest} from "./BaseAllowanceTransferTest.t.sol"; +import {Permit2} from "../src/ERC20/Permit2.sol"; + +contract AllowanceTransferTest_ERC20 is BaseAllowanceTransferTest { + function setUp() public override { + // setUp overridden in ERC specific testsg + permit2 = new Permit2(); + DOMAIN_SEPARATOR = permit2.DOMAIN_SEPARATOR(); + + // amount for ERC20s + defaultAmountOrId = 10 ** 18; + + fromPrivateKey = 0x12341234; + from = vm.addr(fromPrivateKey); + + // Use this address to gas test dirty writes later. + fromPrivateKeyDirty = 0x56785678; + fromDirty = vm.addr(fromPrivateKeyDirty); + + initializeERC20Tokens(); + + setERC20TestTokens(from); + setERC20TestTokenApprovals(vm, from, address(permit2)); + + setERC20TestTokens(fromDirty); + setERC20TestTokenApprovals(vm, fromDirty, address(permit2)); + + // dirty the nonce for fromDirty address on token0 and token1 + vm.startPrank(fromDirty); + permit2.invalidateNonces(address(token0), address(this), 1); + permit2.invalidateNonces(address(token1), address(this), 1); + vm.stopPrank(); + // ensure address3 has some balance of token0 and token1 for dirty sstore on transfer + token0.mint(address3, defaultAmountOrId); + token1.mint(address3, defaultAmountOrId); + } +} diff --git a/test/AllowanceTransferTest.t.sol b/test/BaseAllowanceTransferTest.t.sol similarity index 86% rename from test/AllowanceTransferTest.t.sol rename to test/BaseAllowanceTransferTest.t.sol index aa79a2a0..6171c97d 100644 --- a/test/AllowanceTransferTest.t.sol +++ b/test/BaseAllowanceTransferTest.t.sol @@ -14,7 +14,7 @@ import {SignatureExpired, InvalidNonce} from "../src/shared/PermitErrors.sol"; import {IAllowanceTransfer} from "../src/ERC20/interfaces/IAllowanceTransfer.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; -contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnapshot { +abstract contract BaseAllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnapshot { using AddressBuilder for address[]; using stdStorage for StdStorage; @@ -45,7 +45,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps address address0 = address(0); address address2 = address(2); - uint160 defaultAmount = 10 ** 18; + uint160 defaultAmountOrId = 10 ** 18; uint48 defaultNonce = 0; uint32 dirtyNonce = 1; uint48 defaultExpiration = uint48(block.timestamp + 5); @@ -55,50 +55,23 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps bytes32 DOMAIN_SEPARATOR; - function setUp() public { - permit2 = new Permit2(); - DOMAIN_SEPARATOR = permit2.DOMAIN_SEPARATOR(); - - fromPrivateKey = 0x12341234; - from = vm.addr(fromPrivateKey); - - // Use this address to gas test dirty writes later. - fromPrivateKeyDirty = 0x56785678; - fromDirty = vm.addr(fromPrivateKeyDirty); - - initializeERC20Tokens(); - - setERC20TestTokens(from); - setERC20TestTokenApprovals(vm, from, address(permit2)); - - setERC20TestTokens(fromDirty); - setERC20TestTokenApprovals(vm, fromDirty, address(permit2)); - - // dirty the nonce for fromDirty address on token0 and token1 - vm.startPrank(fromDirty); - permit2.invalidateNonces(address(token0), address(this), 1); - permit2.invalidateNonces(address(token1), address(this), 1); - vm.stopPrank(); - // ensure address3 has some balance of token0 and token1 for dirty sstore on transfer - token0.mint(address3, defaultAmount); - token1.mint(address3, defaultAmount); - } + function setUp() public virtual {} function testApprove() public { vm.prank(from); vm.expectEmit(true, true, true, true); - emit Approval(from, address(token0), address(this), defaultAmount, defaultExpiration); - permit2.approve(address(token0), address(this), defaultAmount, defaultExpiration); + emit Approval(from, address(token0), address(this), defaultAmountOrId, defaultExpiration); + permit2.approve(address(token0), address(this), defaultAmountOrId, defaultExpiration); (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 0); } function testSetAllowance() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); snapStart("permitCleanWrite"); @@ -106,14 +79,14 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps snapEnd(); (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); } function testSetAllowanceCompactSig() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getCompactPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); assertEq(sig.length, 64); @@ -122,14 +95,14 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps snapEnd(); (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); } function testSetAllowanceIncorrectSigLength() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); bytes memory sigExtra = bytes.concat(sig, bytes1(uint8(1))); assertEq(sigExtra.length, 66); @@ -140,7 +113,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps function testSetAllowanceDirtyWrite() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, dirtyNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, dirtyNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); snapStart("permitDirtyWrite"); @@ -148,26 +121,26 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps snapEnd(); (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(fromDirty, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 2); } function testSetAllowanceBatchDifferentNonces() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); permit2.permit(from, permit, sig); (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); IAllowanceTransfer.PermitBatch memory permitBatch = - defaultERC20PermitBatchAllowance(tokens, defaultAmount, defaultExpiration, 1); + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, 1); // first token nonce is 1, second token nonce is 0 permitBatch.details[1].nonce = 0; bytes memory sig1 = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); @@ -175,11 +148,11 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps permit2.permit(from, permitBatch, sig1); (amount, expiration, nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 2); (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, defaultAmount); + assertEq(amount1, defaultAmountOrId); assertEq(expiration1, defaultExpiration); assertEq(nonce1, 1); } @@ -187,7 +160,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps function testSetAllowanceBatch() public { address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); snapStart("permitBatchCleanWrite"); @@ -195,21 +168,21 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps snapEnd(); (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, defaultAmount); + assertEq(amount1, defaultAmountOrId); assertEq(expiration1, defaultExpiration); assertEq(nonce1, 1); } function testSetAllowanceBatchEvent() public { address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); - uint160[] memory amounts = AmountBuilder.fillUInt160(2, defaultAmount); + uint160[] memory amounts = AmountBuilder.fillUInt160(2, defaultAmountOrId); IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); vm.expectEmit(true, true, true, true); @@ -219,11 +192,11 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps permit2.permit(from, permit, sig); (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, defaultAmount); + assertEq(amount1, defaultAmountOrId); assertEq(expiration1, defaultExpiration); assertEq(nonce1, 1); } @@ -231,7 +204,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps function testSetAllowanceBatchDirtyWrite() public { address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmount, defaultExpiration, dirtyNonce); + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, dirtyNonce); bytes memory sig = getPermitBatchSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); snapStart("permitBatchDirtyWrite"); @@ -239,12 +212,12 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps snapEnd(); (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(fromDirty, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 2); (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(fromDirty, address(token1), address(this)); - assertEq(amount1, defaultAmount); + assertEq(amount1, defaultAmountOrId); assertEq(expiration1, defaultExpiration); assertEq(nonce1, 2); } @@ -252,7 +225,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps // test setting allowance with ordered nonce and transfer function testSetAllowanceTransfer() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); uint256 startBalanceFrom = token0.balanceOf(from); @@ -262,17 +235,17 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); - permit2.transferFrom(from, address0, defaultAmount, address(token0)); + permit2.transferFrom(from, address0, defaultAmountOrId, address(token0)); - assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmount); - assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmount); + assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); + assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); } function testTransferFromWithGasSnapshot() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); uint256 startBalanceFrom = token0.balanceOf(from); @@ -282,19 +255,19 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); snapStart("transferFrom"); - permit2.transferFrom(from, address0, defaultAmount, address(token0)); + permit2.transferFrom(from, address0, defaultAmountOrId, address(token0)); snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmount); - assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmount); + assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); + assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); } function testBatchTransferFromWithGasSnapshot() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); uint256 startBalanceFrom = token0.balanceOf(from); @@ -303,7 +276,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps permit2.permit(from, permit, sig); (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); // permit token0 for 1 ** 18 address[] memory owners = AddressBuilder.fill(3, from); @@ -315,36 +288,36 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps assertEq(token0.balanceOf(from), startBalanceFrom - 3 * 1 ** 18); assertEq(token0.balanceOf(address0), startBalanceTo + 3 * 1 ** 18); (amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount - 3 * 1 ** 18); + assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); } // dirty sstore on nonce, dirty sstore on transfer function testSetAllowanceTransferDirtyNonceDirtyTransfer() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, dirtyNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, dirtyNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); uint256 startBalanceFrom = token0.balanceOf(fromDirty); uint256 startBalanceTo = token0.balanceOf(address3); // ensure its a dirty store for the recipient address - assertEq(startBalanceTo, defaultAmount); + assertEq(startBalanceTo, defaultAmountOrId); snapStart("permitDirtyNonce"); permit2.permit(fromDirty, permit, sig); snapEnd(); (uint160 amount,,) = permit2.allowance(fromDirty, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); - permit2.transferFrom(fromDirty, address3, defaultAmount, address(token0)); + permit2.transferFrom(fromDirty, address3, defaultAmountOrId, address(token0)); - assertEq(token0.balanceOf(fromDirty), startBalanceFrom - defaultAmount); - assertEq(token0.balanceOf(address3), startBalanceTo + defaultAmount); + assertEq(token0.balanceOf(fromDirty), startBalanceFrom - defaultAmountOrId); + assertEq(token0.balanceOf(address3), startBalanceTo + defaultAmountOrId); } function testSetAllowanceInvalidSignature() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); snapStart("permitInvalidSigner"); vm.expectRevert(SignatureVerification.InvalidSigner.selector); @@ -355,7 +328,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps function testSetAllowanceDeadlinePassed() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); uint256 sigDeadline = block.timestamp + 100; @@ -383,13 +356,13 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps (uint160 startAllowedAmount0,,) = permit2.allowance(from, address(token0), address(this)); assertEq(startAllowedAmount0, type(uint160).max); - permit2.transferFrom(from, address0, defaultAmount, address(token0)); + permit2.transferFrom(from, address0, defaultAmountOrId, address(token0)); (uint160 endAllowedAmount0,,) = permit2.allowance(from, address(token0), address(this)); assertEq(endAllowedAmount0, type(uint160).max); - assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmount); - assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmount); + assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); + assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); } function testMaxAllowanceDirtyWrite() public { @@ -408,18 +381,18 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps (uint160 startAllowedAmount0,,) = permit2.allowance(fromDirty, address(token0), address(this)); assertEq(startAllowedAmount0, type(uint160).max); - permit2.transferFrom(fromDirty, address0, defaultAmount, address(token0)); + permit2.transferFrom(fromDirty, address0, defaultAmountOrId, address(token0)); (uint160 endAllowedAmount0,,) = permit2.allowance(fromDirty, address(token0), address(this)); assertEq(endAllowedAmount0, type(uint160).max); - assertEq(token0.balanceOf(fromDirty), startBalanceFrom - defaultAmount); - assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmount); + assertEq(token0.balanceOf(fromDirty), startBalanceFrom - defaultAmountOrId); + assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); } function testPartialAllowance() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); uint256 startBalanceFrom = token0.balanceOf(from); @@ -428,13 +401,13 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps permit2.permit(from, permit, sig); (uint160 startAllowedAmount0,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(startAllowedAmount0, defaultAmount); + assertEq(startAllowedAmount0, defaultAmountOrId); uint160 transferAmount = 5 ** 18; permit2.transferFrom(from, address0, transferAmount, address(token0)); (uint160 endAllowedAmount0,,) = permit2.allowance(from, address(token0), address(this)); // ensure the allowance was deducted - assertEq(endAllowedAmount0, defaultAmount - transferAmount); + assertEq(endAllowedAmount0, defaultAmountOrId - transferAmount); assertEq(token0.balanceOf(from), startBalanceFrom - transferAmount); assertEq(token0.balanceOf(address0), startBalanceTo + transferAmount); @@ -442,7 +415,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps function testReuseOrderedNonceInvalid() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); permit2.permit(from, permit, sig); @@ -450,7 +423,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps assertEq(nonce, 1); (uint160 amount, uint48 expiration,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); vm.expectRevert(InvalidNonce.selector); @@ -459,7 +432,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps function testInvalidateNonces() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); // Invalidates the 0th nonce by setting the new nonce to 1. @@ -476,7 +449,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps function testInvalidateMultipleNonces() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); // Valid permit, uses nonce 0. @@ -484,7 +457,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps (,, uint48 nonce1) = permit2.allowance(from, address(token0), address(this)); assertEq(nonce1, 1); - permit = defaultERC20PermitAllowance(address(token1), defaultAmount, defaultExpiration, nonce1); + permit = defaultERC20PermitAllowance(address(token1), defaultAmountOrId, defaultExpiration, nonce1); sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); // Invalidates the 9 nonces by setting the new nonce to 33. @@ -510,7 +483,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps function testExcessiveInvalidation() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); uint32 numInvalidate = type(uint16).max; @@ -526,7 +499,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps function testBatchTransferFrom() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); uint256 startBalanceFrom = token0.balanceOf(from); @@ -535,7 +508,7 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps permit2.permit(from, permit, sig); (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); // permit token0 for 1 ** 18 address[] memory owners = AddressBuilder.fill(3, from); @@ -547,13 +520,13 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps assertEq(token0.balanceOf(from), startBalanceFrom - 3 * 1 ** 18); assertEq(token0.balanceOf(address0), startBalanceTo + 3 * 1 ** 18); (amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount - 3 * 1 ** 18); + assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); } function testBatchTransferFromMultiToken() public { address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); IAllowanceTransfer.PermitBatch memory permitBatch = - defaultERC20PermitBatchAllowance(tokens, defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); uint256 startBalanceFrom0 = token0.balanceOf(from); @@ -564,9 +537,9 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps permit2.permit(from, permitBatch, sig); (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); (amount,,) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); // permit token0 for 1 ** 18 address[] memory owners = AddressBuilder.fill(2, from); @@ -580,18 +553,18 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps assertEq(token0.balanceOf(address0), startBalanceTo0 + 1 ** 18); assertEq(token1.balanceOf(address0), startBalanceTo1 + 1 ** 18); (amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount - 1 ** 18); + assertEq(amount, defaultAmountOrId - 1 ** 18); (amount,,) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount, defaultAmount - 1 ** 18); + assertEq(amount, defaultAmountOrId - 1 ** 18); } function testBatchTransferFromDifferentOwners() public { IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); IAllowanceTransfer.PermitSingle memory permitDirty = - defaultERC20PermitAllowance(address(token0), defaultAmount, defaultExpiration, dirtyNonce); + defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, dirtyNonce); bytes memory sigDirty = getPermitSignature(permitDirty, fromPrivateKeyDirty, DOMAIN_SEPARATOR); uint256 startBalanceFrom = token0.balanceOf(from); @@ -603,9 +576,9 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps permit2.permit(fromDirty, permitDirty, sigDirty); (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); (uint160 amount1,,) = permit2.allowance(fromDirty, address(token0), address(this)); - assertEq(amount1, defaultAmount); + assertEq(amount1, defaultAmountOrId); address[] memory owners = AddressBuilder.fill(1, from).push(fromDirty); IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = @@ -618,25 +591,25 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps assertEq(token0.balanceOf(fromDirty), startBalanceFromDirty - 1 ** 18); assertEq(token0.balanceOf(address(this)), startBalanceTo + 2 * 1 ** 18); (amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount - 1 ** 18); + assertEq(amount, defaultAmountOrId - 1 ** 18); (amount,,) = permit2.allowance(fromDirty, address(token0), address(this)); - assertEq(amount, defaultAmount - 1 ** 18); + assertEq(amount, defaultAmountOrId - 1 ** 18); } function testLockdown() public { address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); permit2.permit(from, permit, sig); (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, defaultAmount); + assertEq(amount1, defaultAmountOrId); assertEq(expiration1, defaultExpiration); assertEq(nonce1, 1); @@ -662,17 +635,17 @@ contract AllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnaps function testLockdownEvent() public { address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmount, defaultExpiration, defaultNonce); + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); permit2.permit(from, permit, sig); (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmount); + assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, defaultAmount); + assertEq(amount1, defaultAmountOrId); assertEq(expiration1, defaultExpiration); assertEq(nonce1, 1); From ae055ba669a3547717e566c41a4583c96a8c5d61 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Tue, 10 Jan 2023 11:11:42 -0700 Subject: [PATCH 11/27] partial working refactor --- .forge-snapshots/permitCleanWrite.snap | 2 +- test/AllowanceTransferInvariants.t.sol | 2 +- test/AllowanceTransferTest_ERC20.t.sol | 92 +- test/BaseAllowanceTransferTest.t.sol | 1195 ++++++++++---------- test/SignatureTransfer.t.sol | 232 ++-- test/shared/AllowanceUnitTest_ERC20.t.sol | 8 +- test/shared/AllowanceUnitTest_ERC721.t.sol | 10 +- test/shared/BaseAllowanceUnitTest.sol | 11 +- test/utils/PermitSignature.sol | 39 +- test/utils/TokenProvider.sol | 69 -- test/utils/TokenProvider_ERC20.sol | 73 ++ test/utils/TokenProvider_ERC721.sol | 56 + 12 files changed, 986 insertions(+), 803 deletions(-) delete mode 100644 test/utils/TokenProvider.sol create mode 100644 test/utils/TokenProvider_ERC20.sol create mode 100644 test/utils/TokenProvider_ERC721.sol diff --git a/.forge-snapshots/permitCleanWrite.snap b/.forge-snapshots/permitCleanWrite.snap index c49bc2ab..5c565761 100644 --- a/.forge-snapshots/permitCleanWrite.snap +++ b/.forge-snapshots/permitCleanWrite.snap @@ -1 +1 @@ -63119 \ No newline at end of file +63362 \ No newline at end of file diff --git a/test/AllowanceTransferInvariants.t.sol b/test/AllowanceTransferInvariants.t.sol index 80224bb8..7b4527e9 100644 --- a/test/AllowanceTransferInvariants.t.sol +++ b/test/AllowanceTransferInvariants.t.sol @@ -1,7 +1,7 @@ pragma solidity 0.8.17; import "forge-std/Test.sol"; -import {TokenProvider} from "./utils/TokenProvider.sol"; +import {TokenProvider_ERC20} from "./utils/TokenProvider_ERC20.sol"; import {Permit2} from "../src/ERC20/Permit2.sol"; import {IAllowanceTransfer} from "../src/ERC20/interfaces/IAllowanceTransfer.sol"; import {SignatureVerification} from "../src/shared/SignatureVerification.sol"; diff --git a/test/AllowanceTransferTest_ERC20.t.sol b/test/AllowanceTransferTest_ERC20.t.sol index d1bed12c..98f61905 100644 --- a/test/AllowanceTransferTest_ERC20.t.sol +++ b/test/AllowanceTransferTest_ERC20.t.sol @@ -4,12 +4,16 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {BaseAllowanceTransferTest} from "./BaseAllowanceTransferTest.t.sol"; import {Permit2} from "../src/ERC20/Permit2.sol"; +import {TokenProvider_ERC20} from "./utils/TokenProvider_ERC20.sol"; +import {PermitHash} from "../src/ERC20/libraries/PermitHash.sol"; +import {PermitSignature} from "./utils/PermitSignature.sol"; +import {IAllowanceTransfer} from "../src/ERC20/interfaces/IAllowanceTransfer.sol"; +import {MockERC20} from "./mocks/MockERC20.sol"; -contract AllowanceTransferTest_ERC20 is BaseAllowanceTransferTest { +contract AllowanceTransferTest_ERC20 is TokenProvider_ERC20, BaseAllowanceTransferTest { function setUp() public override { - // setUp overridden in ERC specific testsg - permit2 = new Permit2(); - DOMAIN_SEPARATOR = permit2.DOMAIN_SEPARATOR(); + permit2 = address(new Permit2()); + DOMAIN_SEPARATOR = Permit2(permit2).DOMAIN_SEPARATOR(); // amount for ERC20s defaultAmountOrId = 10 ** 18; @@ -31,11 +35,83 @@ contract AllowanceTransferTest_ERC20 is BaseAllowanceTransferTest { // dirty the nonce for fromDirty address on token0 and token1 vm.startPrank(fromDirty); - permit2.invalidateNonces(address(token0), address(this), 1); - permit2.invalidateNonces(address(token1), address(this), 1); + Permit2(permit2).invalidateNonces(token0(), address(this), 1); + Permit2(permit2).invalidateNonces(token1(), address(this), 1); vm.stopPrank(); // ensure address3 has some balance of token0 and token1 for dirty sstore on transfer - token0.mint(address3, defaultAmountOrId); - token1.mint(address3, defaultAmountOrId); + MockERC20(token0()).mint(address3, defaultAmountOrId); + MockERC20(token1()).mint(address3, defaultAmountOrId); + } + + function permit2Approve(address token, address spender, uint160 amountOrId, uint48 expiration) public override { + Permit2(permit2).approve(token, spender, amountOrId, expiration); + } + + function permit2Allowance(address from, address token, address spender) + public + override + returns (uint160, uint48, uint48) + { + return Permit2(permit2).allowance(from, token, spender); + } + + function permit2Permit(address from, PermitSignature.IPermitSingle memory permit, bytes memory sig) + public + override + { + // convert IPermitSingle to AllowanceTransfer.PermitSingle + IAllowanceTransfer.PermitSingle memory parsedPermit = IAllowanceTransfer.PermitSingle({ + details: IAllowanceTransfer.PermitDetails({ + token: permit.token, + amount: permit.amountOrId, + expiration: permit.expiration, + nonce: permit.nonce + }), + spender: permit.spender, + sigDeadline: permit.sigDeadline + }); + + Permit2(permit2).permit(from, parsedPermit, sig); + } + + function token0() public view override returns (address) { + return address(_token0); + } + + function token1() public view override returns (address) { + return address(_token1); + } + + function getPermitSignature(IPermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator) + public + override + returns (bytes memory sig) + { + (uint8 v, bytes32 r, bytes32 s) = getPermitSignatureRaw(permit, privateKey, domainSeparator); + return bytes.concat(r, s, bytes1(v)); + } + + function getPermitSignatureRaw(IPermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator) + internal + returns (uint8 v, bytes32 r, bytes32 s) + { + // convert IPermitSingle to permit details & hash + bytes32 permitHash = keccak256( + abi.encode( + PermitHash._PERMIT_DETAILS_TYPEHASH, permit.token, permit.amountOrId, permit.expiration, permit.nonce + ) + ); + + bytes32 msgHash = keccak256( + abi.encodePacked( + "\x19\x01", + domainSeparator, + keccak256( + abi.encode(PermitHash._PERMIT_SINGLE_TYPEHASH, permitHash, permit.spender, permit.sigDeadline) + ) + ) + ); + + (v, r, s) = vm.sign(privateKey, msgHash); } } diff --git a/test/BaseAllowanceTransferTest.t.sol b/test/BaseAllowanceTransferTest.t.sol index 6171c97d..670a8538 100644 --- a/test/BaseAllowanceTransferTest.t.sol +++ b/test/BaseAllowanceTransferTest.t.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; -import {TokenProvider} from "./utils/TokenProvider.sol"; import {Permit2} from "../src/ERC20/Permit2.sol"; import {PermitSignature} from "./utils/PermitSignature.sol"; import {SignatureVerification} from "../src/shared/SignatureVerification.sol"; @@ -14,7 +13,7 @@ import {SignatureExpired, InvalidNonce} from "../src/shared/PermitErrors.sol"; import {IAllowanceTransfer} from "../src/ERC20/interfaces/IAllowanceTransfer.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; -abstract contract BaseAllowanceTransferTest is Test, TokenProvider, PermitSignature, GasSnapshot { +abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapshot { using AddressBuilder for address[]; using stdStorage for StdStorage; @@ -34,7 +33,7 @@ abstract contract BaseAllowanceTransferTest is Test, TokenProvider, PermitSignat ); event Lockdown(address indexed owner, address token, address spender); - Permit2 permit2; + address permit2; address from; uint256 fromPrivateKey; @@ -55,617 +54,639 @@ abstract contract BaseAllowanceTransferTest is Test, TokenProvider, PermitSignat bytes32 DOMAIN_SEPARATOR; - function setUp() public virtual {} + function setUp() public virtual; - function testApprove() public { - vm.prank(from); - vm.expectEmit(true, true, true, true); - emit Approval(from, address(token0), address(this), defaultAmountOrId, defaultExpiration); - permit2.approve(address(token0), address(this), defaultAmountOrId, defaultExpiration); - - (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 0); - } - - function testSetAllowance() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - snapStart("permitCleanWrite"); - permit2.permit(from, permit, sig); - snapEnd(); + function token0() public virtual returns (address); + function token1() public virtual returns (address); - (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - } - - function testSetAllowanceCompactSig() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getCompactPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - assertEq(sig.length, 64); - - snapStart("permitCompactSig"); - permit2.permit(from, permit, sig); - snapEnd(); - - (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - } - - function testSetAllowanceIncorrectSigLength() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - bytes memory sigExtra = bytes.concat(sig, bytes1(uint8(1))); - assertEq(sigExtra.length, 66); + function getPermitSignature(IPermitSingle memory permit, uint256 privKey, bytes32 domainSeparator) + public + virtual + returns (bytes memory); - vm.expectRevert(SignatureVerification.InvalidSignatureLength.selector); - permit2.permit(from, permit, sigExtra); - } - - function testSetAllowanceDirtyWrite() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, dirtyNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); - - snapStart("permitDirtyWrite"); - permit2.permit(fromDirty, permit, sig); - snapEnd(); - - (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(fromDirty, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 2); - } - - function testSetAllowanceBatchDifferentNonces() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - permit2.permit(from, permit, sig); - - (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); - IAllowanceTransfer.PermitBatch memory permitBatch = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, 1); - // first token nonce is 1, second token nonce is 0 - permitBatch.details[1].nonce = 0; - bytes memory sig1 = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); - - permit2.permit(from, permitBatch, sig1); - - (amount, expiration, nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 2); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, defaultAmountOrId); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 1); - } - - function testSetAllowanceBatch() public { - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); - IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - snapStart("permitBatchCleanWrite"); - permit2.permit(from, permit, sig); - snapEnd(); - - (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, defaultAmountOrId); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 1); - } + function permit2Approve(address token, address spender, uint160 amountOrId, uint48 expiration) public virtual; - function testSetAllowanceBatchEvent() public { - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); - uint160[] memory amounts = AmountBuilder.fillUInt160(2, defaultAmountOrId); + function permit2Allowance(address from, address token, address spender) + public + virtual + returns (uint160, uint48, uint48); - IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + function permit2Permit(address from, PermitSignature.IPermitSingle memory permit, bytes memory sig) + public + virtual; + function testApprove() public { + vm.prank(from); vm.expectEmit(true, true, true, true); - emit Permit(from, tokens[0], address(this), amounts[0], defaultExpiration, defaultNonce); - vm.expectEmit(true, true, true, true); - emit Permit(from, tokens[1], address(this), amounts[1], defaultExpiration, defaultNonce); - permit2.permit(from, permit, sig); - - (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, defaultAmountOrId); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 1); - } - - function testSetAllowanceBatchDirtyWrite() public { - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); - IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, dirtyNonce); - bytes memory sig = getPermitBatchSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); + emit Approval(from, token0(), address(this), defaultAmountOrId, defaultExpiration); + permit2Approve(token0(), address(this), defaultAmountOrId, defaultExpiration); - snapStart("permitBatchDirtyWrite"); - permit2.permit(fromDirty, permit, sig); - snapEnd(); - - (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(fromDirty, address(token0), address(this)); + (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); - assertEq(nonce, 2); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = - permit2.allowance(fromDirty, address(token1), address(this)); - assertEq(amount1, defaultAmountOrId); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 2); - } - - // test setting allowance with ordered nonce and transfer - function testSetAllowanceTransfer() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address0); - - permit2.permit(from, permit, sig); - - (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - - assertEq(amount, defaultAmountOrId); - - permit2.transferFrom(from, address0, defaultAmountOrId, address(token0)); - - assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); - assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); - } - - function testTransferFromWithGasSnapshot() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address0); - - permit2.permit(from, permit, sig); - - (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - - assertEq(amount, defaultAmountOrId); - - snapStart("transferFrom"); - permit2.transferFrom(from, address0, defaultAmountOrId, address(token0)); - - snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); - assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); - } - - function testBatchTransferFromWithGasSnapshot() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address0); - - permit2.permit(from, permit, sig); - - (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - - // permit token0 for 1 ** 18 - address[] memory owners = AddressBuilder.fill(3, from); - IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - StructBuilder.fillAllowanceTransferDetail(3, address(token0), 1 ** 18, address0, owners); - snapStart("batchTransferFrom"); - permit2.transferFrom(transferDetails); - snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom - 3 * 1 ** 18); - assertEq(token0.balanceOf(address0), startBalanceTo + 3 * 1 ** 18); - (amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); - } - - // dirty sstore on nonce, dirty sstore on transfer - function testSetAllowanceTransferDirtyNonceDirtyTransfer() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, dirtyNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom = token0.balanceOf(fromDirty); - uint256 startBalanceTo = token0.balanceOf(address3); - // ensure its a dirty store for the recipient address - assertEq(startBalanceTo, defaultAmountOrId); - - snapStart("permitDirtyNonce"); - permit2.permit(fromDirty, permit, sig); - snapEnd(); - - (uint160 amount,,) = permit2.allowance(fromDirty, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - - permit2.transferFrom(fromDirty, address3, defaultAmountOrId, address(token0)); - - assertEq(token0.balanceOf(fromDirty), startBalanceFrom - defaultAmountOrId); - assertEq(token0.balanceOf(address3), startBalanceTo + defaultAmountOrId); - } - - function testSetAllowanceInvalidSignature() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - snapStart("permitInvalidSigner"); - vm.expectRevert(SignatureVerification.InvalidSigner.selector); - permit.spender = address0; - permit2.permit(from, permit, sig); - snapEnd(); - } - - function testSetAllowanceDeadlinePassed() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - uint256 sigDeadline = block.timestamp + 100; - - vm.warp(block.timestamp + 101); - snapStart("permitSignatureExpired"); - vm.expectRevert(abi.encodeWithSelector(SignatureExpired.selector, sigDeadline)); - permit2.permit(from, permit, sig); - snapEnd(); + assertEq(nonce, 0); } - function testMaxAllowance() public { - uint160 maxAllowance = type(uint160).max; - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), maxAllowance, defaultExpiration, defaultNonce); + function testSetAllowance() public { + // create a function defaultPermitAllowance() which will be overridden by ERC20, ERC721, and ERC1155 inherited classes + // IPermitSingle + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address0); - - snapStart("permitSetMaxAllowanceCleanWrite"); - permit2.permit(from, permit, sig); - snapEnd(); - - (uint160 startAllowedAmount0,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(startAllowedAmount0, type(uint160).max); - - permit2.transferFrom(from, address0, defaultAmountOrId, address(token0)); - - (uint160 endAllowedAmount0,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(endAllowedAmount0, type(uint160).max); - - assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); - assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); - } - - function testMaxAllowanceDirtyWrite() public { - uint160 maxAllowance = type(uint160).max; - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), maxAllowance, defaultExpiration, dirtyNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom = token0.balanceOf(fromDirty); - uint256 startBalanceTo = token0.balanceOf(address0); - - snapStart("permitSetMaxAllowanceDirtyWrite"); - permit2.permit(fromDirty, permit, sig); + snapStart("permitCleanWrite"); + permit2Permit(from, permit, sig); snapEnd(); - (uint160 startAllowedAmount0,,) = permit2.allowance(fromDirty, address(token0), address(this)); - assertEq(startAllowedAmount0, type(uint160).max); - - permit2.transferFrom(fromDirty, address0, defaultAmountOrId, address(token0)); - - (uint160 endAllowedAmount0,,) = permit2.allowance(fromDirty, address(token0), address(this)); - assertEq(endAllowedAmount0, type(uint160).max); - - assertEq(token0.balanceOf(fromDirty), startBalanceFrom - defaultAmountOrId); - assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); - } - - function testPartialAllowance() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address0); - - permit2.permit(from, permit, sig); - - (uint160 startAllowedAmount0,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(startAllowedAmount0, defaultAmountOrId); - - uint160 transferAmount = 5 ** 18; - permit2.transferFrom(from, address0, transferAmount, address(token0)); - (uint160 endAllowedAmount0,,) = permit2.allowance(from, address(token0), address(this)); - // ensure the allowance was deducted - assertEq(endAllowedAmount0, defaultAmountOrId - transferAmount); - - assertEq(token0.balanceOf(from), startBalanceFrom - transferAmount); - assertEq(token0.balanceOf(address0), startBalanceTo + transferAmount); - } - - function testReuseOrderedNonceInvalid() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - permit2.permit(from, permit, sig); - (,, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(nonce, 1); - - (uint160 amount, uint48 expiration,) = permit2.allowance(from, address(token0), address(this)); + (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); - - vm.expectRevert(InvalidNonce.selector); - permit2.permit(from, permit, sig); - } - - function testInvalidateNonces() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // Invalidates the 0th nonce by setting the new nonce to 1. - vm.prank(from); - vm.expectEmit(true, true, true, true); - emit NonceInvalidation(from, address(token0), address(this), 1, defaultNonce); - permit2.invalidateNonces(address(token0), address(this), 1); - (,, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); assertEq(nonce, 1); - - vm.expectRevert(InvalidNonce.selector); - permit2.permit(from, permit, sig); } - function testInvalidateMultipleNonces() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + // function testSetAllowanceCompactSig() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getCompactPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + // assertEq(sig.length, 64); + + // snapStart("permitCompactSig"); + // permit2.permit(from, permit, sig); + // snapEnd(); + + // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + // } + + // function testSetAllowanceIncorrectSigLength() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + // bytes memory sigExtra = bytes.concat(sig, bytes1(uint8(1))); + // assertEq(sigExtra.length, 66); + + // vm.expectRevert(SignatureVerification.InvalidSignatureLength.selector); + // permit2.permit(from, permit, sigExtra); + // } + + // function testSetAllowanceDirtyWrite() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); + + // snapStart("permitDirtyWrite"); + // permit2.permit(fromDirty, permit, sig); + // snapEnd(); + + // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(fromDirty, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 2); + // } + + // function testSetAllowanceBatchDifferentNonces() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // permit2.permit(from, permit, sig); + + // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + + // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + // IAllowanceTransfer.PermitBatch memory permitBatch = + // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, 1); + // // first token nonce is 1, second token nonce is 0 + // permitBatch.details[1].nonce = 0; + // bytes memory sig1 = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); + + // permit2.permit(from, permitBatch, sig1); + + // (amount, expiration, nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 2); + // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token1(), address(this)); + // assertEq(amount1, defaultAmountOrId); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 1); + // } + + // function testSetAllowanceBatch() public { + // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + // IAllowanceTransfer.PermitBatch memory permit = + // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // snapStart("permitBatchCleanWrite"); + // permit2.permit(from, permit, sig); + // snapEnd(); + + // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token1(), address(this)); + // assertEq(amount1, defaultAmountOrId); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 1); + // } + + // function testSetAllowanceBatchEvent() public { + // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + // uint160[] memory amounts = AmountBuilder.fillUInt160(2, defaultAmountOrId); + + // IAllowanceTransfer.PermitBatch memory permit = + // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // vm.expectEmit(true, true, true, true); + // emit Permit(from, tokens[0], address(this), amounts[0], defaultExpiration, defaultNonce); + // vm.expectEmit(true, true, true, true); + // emit Permit(from, tokens[1], address(this), amounts[1], defaultExpiration, defaultNonce); + // permit2.permit(from, permit, sig); + + // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token1(), address(this)); + // assertEq(amount1, defaultAmountOrId); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 1); + // } + + // function testSetAllowanceBatchDirtyWrite() public { + // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + // IAllowanceTransfer.PermitBatch memory permit = + // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, dirtyNonce); + // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); + + // snapStart("permitBatchDirtyWrite"); + // permit2.permit(fromDirty, permit, sig); + // snapEnd(); + + // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(fromDirty, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 2); + // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(fromDirty, token1(), address(this)); + // assertEq(amount1, defaultAmountOrId); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 2); + // } + + // // test setting allowance with ordered nonce and transfer + // function testSetAllowanceTransfer() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom = token0.balanceOf(from); + // uint256 startBalanceTo = token0.balanceOf(address0); + + // permit2.permit(from, permit, sig); + + // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); + + // assertEq(amount, defaultAmountOrId); + + // permit2.transferFrom(from, address0, defaultAmountOrId, token0()); + + // assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); + // assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); + // } + + // function testTransferFromWithGasSnapshot() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom = token0.balanceOf(from); + // uint256 startBalanceTo = token0.balanceOf(address0); + + // permit2.permit(from, permit, sig); + + // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); + + // assertEq(amount, defaultAmountOrId); + + // snapStart("transferFrom"); + // permit2.transferFrom(from, address0, defaultAmountOrId, token0()); + + // snapEnd(); + // assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); + // assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); + // } + + // function testBatchTransferFromWithGasSnapshot() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom = token0.balanceOf(from); + // uint256 startBalanceTo = token0.balanceOf(address0); + + // permit2.permit(from, permit, sig); + + // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + + // // permit token0 for 1 ** 18 + // address[] memory owners = AddressBuilder.fill(3, from); + // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + // StructBuilder.fillAllowanceTransferDetail(3, token0(), 1 ** 18, address0, owners); + // snapStart("batchTransferFrom"); + // permit2.transferFrom(transferDetails); + // snapEnd(); + // assertEq(token0.balanceOf(from), startBalanceFrom - 3 * 1 ** 18); + // assertEq(token0.balanceOf(address0), startBalanceTo + 3 * 1 ** 18); + // (amount,,) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); + // } + + // // dirty sstore on nonce, dirty sstore on transfer + // function testSetAllowanceTransferDirtyNonceDirtyTransfer() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom = token0.balanceOf(fromDirty); + // uint256 startBalanceTo = token0.balanceOf(address3); + // // ensure its a dirty store for the recipient address + // assertEq(startBalanceTo, defaultAmountOrId); - // Valid permit, uses nonce 0. - permit2.permit(from, permit, sig); - (,, uint48 nonce1) = permit2.allowance(from, address(token0), address(this)); - assertEq(nonce1, 1); + // snapStart("permitDirtyNonce"); + // permit2.permit(fromDirty, permit, sig); + // snapEnd(); - permit = defaultERC20PermitAllowance(address(token1), defaultAmountOrId, defaultExpiration, nonce1); - sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + // (uint160 amount,,) = permit2.allowance(fromDirty, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); - // Invalidates the 9 nonces by setting the new nonce to 33. - vm.prank(from); - vm.expectEmit(true, true, true, true); + // permit2.transferFrom(fromDirty, address3, defaultAmountOrId, token0()); - emit NonceInvalidation(from, address(token0), address(this), 33, nonce1); - permit2.invalidateNonces(address(token0), address(this), 33); - (,, uint48 nonce2) = permit2.allowance(from, address(token0), address(this)); - assertEq(nonce2, 33); + // assertEq(token0.balanceOf(fromDirty), startBalanceFrom - defaultAmountOrId); + // assertEq(token0.balanceOf(address3), startBalanceTo + defaultAmountOrId); + // } - vm.expectRevert(InvalidNonce.selector); - permit2.permit(from, permit, sig); - } + // function testSetAllowanceInvalidSignature() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + // snapStart("permitInvalidSigner"); + // vm.expectRevert(SignatureVerification.InvalidSigner.selector); + // permit.spender = address0; + // permit2.permit(from, permit, sig); + // snapEnd(); + // } - function testInvalidateNoncesInvalid() public { - // fromDirty nonce is 1 - vm.prank(fromDirty); - vm.expectRevert(InvalidNonce.selector); - // setting nonce to 0 should revert - permit2.invalidateNonces(address(token0), address(this), 0); - } - - function testExcessiveInvalidation() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + // function testSetAllowanceDeadlinePassed() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - uint32 numInvalidate = type(uint16).max; - vm.startPrank(from); - vm.expectRevert(IAllowanceTransfer.ExcessiveInvalidation.selector); - permit2.invalidateNonces(address(token0), address(this), numInvalidate + 1); - vm.stopPrank(); - - permit2.permit(from, permit, sig); - (,, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(nonce, 1); - } + // uint256 sigDeadline = block.timestamp + 100; - function testBatchTransferFrom() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address0); - - permit2.permit(from, permit, sig); - - (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - - // permit token0 for 1 ** 18 - address[] memory owners = AddressBuilder.fill(3, from); - IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - StructBuilder.fillAllowanceTransferDetail(3, address(token0), 1 ** 18, address0, owners); - snapStart("batchTransferFrom"); - permit2.transferFrom(transferDetails); - snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom - 3 * 1 ** 18); - assertEq(token0.balanceOf(address0), startBalanceTo + 3 * 1 ** 18); - (amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); - } - - function testBatchTransferFromMultiToken() public { - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); - IAllowanceTransfer.PermitBatch memory permitBatch = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom0 = token0.balanceOf(from); - uint256 startBalanceFrom1 = token1.balanceOf(from); - uint256 startBalanceTo0 = token0.balanceOf(address0); - uint256 startBalanceTo1 = token1.balanceOf(address0); - - permit2.permit(from, permitBatch, sig); - - (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - (amount,,) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount, defaultAmountOrId); - - // permit token0 for 1 ** 18 - address[] memory owners = AddressBuilder.fill(2, from); - IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - StructBuilder.fillAllowanceTransferDetail(2, tokens, 1 ** 18, address0, owners); - snapStart("batchTransferFromMultiToken"); - permit2.transferFrom(transferDetails); - snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom0 - 1 ** 18); - assertEq(token1.balanceOf(from), startBalanceFrom1 - 1 ** 18); - assertEq(token0.balanceOf(address0), startBalanceTo0 + 1 ** 18); - assertEq(token1.balanceOf(address0), startBalanceTo1 + 1 ** 18); - (amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId - 1 ** 18); - (amount,,) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount, defaultAmountOrId - 1 ** 18); - } - - function testBatchTransferFromDifferentOwners() public { - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - IAllowanceTransfer.PermitSingle memory permitDirty = - defaultERC20PermitAllowance(address(token0), defaultAmountOrId, defaultExpiration, dirtyNonce); - bytes memory sigDirty = getPermitSignature(permitDirty, fromPrivateKeyDirty, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address(this)); - uint256 startBalanceFromDirty = token0.balanceOf(fromDirty); - - // from and fromDirty approve address(this) as spender - permit2.permit(from, permit, sig); - permit2.permit(fromDirty, permitDirty, sigDirty); - - (uint160 amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - (uint160 amount1,,) = permit2.allowance(fromDirty, address(token0), address(this)); - assertEq(amount1, defaultAmountOrId); - - address[] memory owners = AddressBuilder.fill(1, from).push(fromDirty); - IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - StructBuilder.fillAllowanceTransferDetail(2, address(token0), 1 ** 18, address(this), owners); - snapStart("transferFrom with different owners"); - permit2.transferFrom(transferDetails); - snapEnd(); - - assertEq(token0.balanceOf(from), startBalanceFrom - 1 ** 18); - assertEq(token0.balanceOf(fromDirty), startBalanceFromDirty - 1 ** 18); - assertEq(token0.balanceOf(address(this)), startBalanceTo + 2 * 1 ** 18); - (amount,,) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId - 1 ** 18); - (amount,,) = permit2.allowance(fromDirty, address(token0), address(this)); - assertEq(amount, defaultAmountOrId - 1 ** 18); - } - - function testLockdown() public { - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); - IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - permit2.permit(from, permit, sig); - - (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, defaultAmountOrId); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 1); - - IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); - approvals[0] = IAllowanceTransfer.TokenSpenderPair(address(token0), address(this)); - approvals[1] = IAllowanceTransfer.TokenSpenderPair(address(token1), address(this)); - - vm.prank(from); - snapStart("lockdown"); - permit2.lockdown(approvals); - snapEnd(); - - (amount, expiration, nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, 0); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - (amount1, expiration1, nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, 0); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 1); - } - - function testLockdownEvent() public { - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); - IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - permit2.permit(from, permit, sig); - - (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, defaultAmountOrId); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 1); - - IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); - approvals[0] = IAllowanceTransfer.TokenSpenderPair(address(token0), address(this)); - approvals[1] = IAllowanceTransfer.TokenSpenderPair(address(token1), address(this)); - - //TODO :fix expecting multiple events, can only check for 1 - vm.prank(from); - vm.expectEmit(true, false, false, false); - emit Lockdown(from, address(token0), address(this)); - permit2.lockdown(approvals); - - (amount, expiration, nonce) = permit2.allowance(from, address(token0), address(this)); - assertEq(amount, 0); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - (amount1, expiration1, nonce1) = permit2.allowance(from, address(token1), address(this)); - assertEq(amount1, 0); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 1); - } + // vm.warp(block.timestamp + 101); + // snapStart("permitSignatureExpired"); + // vm.expectRevert(abi.encodeWithSelector(SignatureExpired.selector, sigDeadline)); + // permit2.permit(from, permit, sig); + // snapEnd(); + // } + + // function testMaxAllowance() public { + // uint160 maxAllowance = type(uint160).max; + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), maxAllowance, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom = token0.balanceOf(from); + // uint256 startBalanceTo = token0.balanceOf(address0); + + // snapStart("permitSetMaxAllowanceCleanWrite"); + // permit2.permit(from, permit, sig); + // snapEnd(); + + // (uint160 startAllowedAmount0,,) = permit2.allowance(from, token0(), address(this)); + // assertEq(startAllowedAmount0, type(uint160).max); + + // permit2.transferFrom(from, address0, defaultAmountOrId, token0()); + + // (uint160 endAllowedAmount0,,) = permit2.allowance(from, token0(), address(this)); + // assertEq(endAllowedAmount0, type(uint160).max); + + // assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); + // assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); + // } + + // function testMaxAllowanceDirtyWrite() public { + // uint160 maxAllowance = type(uint160).max; + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), maxAllowance, defaultExpiration, dirtyNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom = token0.balanceOf(fromDirty); + // uint256 startBalanceTo = token0.balanceOf(address0); + + // snapStart("permitSetMaxAllowanceDirtyWrite"); + // permit2.permit(fromDirty, permit, sig); + // snapEnd(); + + // (uint160 startAllowedAmount0,,) = permit2.allowance(fromDirty, token0(), address(this)); + // assertEq(startAllowedAmount0, type(uint160).max); + + // permit2.transferFrom(fromDirty, address0, defaultAmountOrId, token0()); + + // (uint160 endAllowedAmount0,,) = permit2.allowance(fromDirty, token0(), address(this)); + // assertEq(endAllowedAmount0, type(uint160).max); + + // assertEq(token0.balanceOf(fromDirty), startBalanceFrom - defaultAmountOrId); + // assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); + // } + + // function testPartialAllowance() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom = token0.balanceOf(from); + // uint256 startBalanceTo = token0.balanceOf(address0); + + // permit2.permit(from, permit, sig); + + // (uint160 startAllowedAmount0,,) = permit2.allowance(from, token0(), address(this)); + // assertEq(startAllowedAmount0, defaultAmountOrId); + + // uint160 transferAmount = 5 ** 18; + // permit2.transferFrom(from, address0, transferAmount, token0()); + // (uint160 endAllowedAmount0,,) = permit2.allowance(from, token0(), address(this)); + // // ensure the allowance was deducted + // assertEq(endAllowedAmount0, defaultAmountOrId - transferAmount); + + // assertEq(token0.balanceOf(from), startBalanceFrom - transferAmount); + // assertEq(token0.balanceOf(address0), startBalanceTo + transferAmount); + // } + + // function testReuseOrderedNonceInvalid() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // permit2.permit(from, permit, sig); + // (,, uint48 nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(nonce, 1); + + // (uint160 amount, uint48 expiration,) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + + // vm.expectRevert(InvalidNonce.selector); + // permit2.permit(from, permit, sig); + // } + + // function testInvalidateNonces() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // // Invalidates the 0th nonce by setting the new nonce to 1. + // vm.prank(from); + // vm.expectEmit(true, true, true, true); + // emit NonceInvalidation(from, token0(), address(this), 1, defaultNonce); + // permit2.invalidateNonces(token0(), address(this), 1); + // (,, uint48 nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(nonce, 1); + + // vm.expectRevert(InvalidNonce.selector); + // permit2.permit(from, permit, sig); + // } + + // function testInvalidateMultipleNonces() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // // Valid permit, uses nonce 0. + // permit2.permit(from, permit, sig); + // (,, uint48 nonce1) = permit2.allowance(from, token0(), address(this)); + // assertEq(nonce1, 1); + + // permit = defaultERC20PermitAllowance(token1(), defaultAmountOrId, defaultExpiration, nonce1); + // sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // // Invalidates the 9 nonces by setting the new nonce to 33. + // vm.prank(from); + // vm.expectEmit(true, true, true, true); + + // emit NonceInvalidation(from, token0(), address(this), 33, nonce1); + // permit2.invalidateNonces(token0(), address(this), 33); + // (,, uint48 nonce2) = permit2.allowance(from, token0(), address(this)); + // assertEq(nonce2, 33); + + // vm.expectRevert(InvalidNonce.selector); + // permit2.permit(from, permit, sig); + // } + + // function testInvalidateNoncesInvalid() public { + // // fromDirty nonce is 1 + // vm.prank(fromDirty); + // vm.expectRevert(InvalidNonce.selector); + // // setting nonce to 0 should revert + // permit2.invalidateNonces(token0(), address(this), 0); + // } + + // function testExcessiveInvalidation() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // uint32 numInvalidate = type(uint16).max; + // vm.startPrank(from); + // vm.expectRevert(IAllowanceTransfer.ExcessiveInvalidation.selector); + // permit2.invalidateNonces(token0(), address(this), numInvalidate + 1); + // vm.stopPrank(); + + // permit2.permit(from, permit, sig); + // (,, uint48 nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(nonce, 1); + // } + + // function testBatchTransferFrom() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom = token0.balanceOf(from); + // uint256 startBalanceTo = token0.balanceOf(address0); + + // permit2.permit(from, permit, sig); + + // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + + // // permit token0 for 1 ** 18 + // address[] memory owners = AddressBuilder.fill(3, from); + // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + // StructBuilder.fillAllowanceTransferDetail(3, token0(), 1 ** 18, address0, owners); + // snapStart("batchTransferFrom"); + // permit2.transferFrom(transferDetails); + // snapEnd(); + // assertEq(token0.balanceOf(from), startBalanceFrom - 3 * 1 ** 18); + // assertEq(token0.balanceOf(address0), startBalanceTo + 3 * 1 ** 18); + // (amount,,) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); + // } + + // function testBatchTransferFromMultiToken() public { + // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + // IAllowanceTransfer.PermitBatch memory permitBatch = + // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom0 = token0.balanceOf(from); + // uint256 startBalanceFrom1 = token1.balanceOf(from); + // uint256 startBalanceTo0 = token0.balanceOf(address0); + // uint256 startBalanceTo1 = token1.balanceOf(address0); + + // permit2.permit(from, permitBatch, sig); + + // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // (amount,,) = permit2.allowance(from, token1(), address(this)); + // assertEq(amount, defaultAmountOrId); + + // // permit token0 for 1 ** 18 + // address[] memory owners = AddressBuilder.fill(2, from); + // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + // StructBuilder.fillAllowanceTransferDetail(2, tokens, 1 ** 18, address0, owners); + // snapStart("batchTransferFromMultiToken"); + // permit2.transferFrom(transferDetails); + // snapEnd(); + // assertEq(token0.balanceOf(from), startBalanceFrom0 - 1 ** 18); + // assertEq(token1.balanceOf(from), startBalanceFrom1 - 1 ** 18); + // assertEq(token0.balanceOf(address0), startBalanceTo0 + 1 ** 18); + // assertEq(token1.balanceOf(address0), startBalanceTo1 + 1 ** 18); + // (amount,,) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId - 1 ** 18); + // (amount,,) = permit2.allowance(from, token1(), address(this)); + // assertEq(amount, defaultAmountOrId - 1 ** 18); + // } + + // function testBatchTransferFromDifferentOwners() public { + // IAllowanceTransfer.PermitSingle memory permit = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // IAllowanceTransfer.PermitSingle memory permitDirty = + // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); + // bytes memory sigDirty = getPermitSignature(permitDirty, fromPrivateKeyDirty, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom = token0.balanceOf(from); + // uint256 startBalanceTo = token0.balanceOf(address(this)); + // uint256 startBalanceFromDirty = token0.balanceOf(fromDirty); + + // // from and fromDirty approve address(this) as spender + // permit2.permit(from, permit, sig); + // permit2.permit(fromDirty, permitDirty, sigDirty); + + // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // (uint160 amount1,,) = permit2.allowance(fromDirty, token0(), address(this)); + // assertEq(amount1, defaultAmountOrId); + + // address[] memory owners = AddressBuilder.fill(1, from).push(fromDirty); + // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + // StructBuilder.fillAllowanceTransferDetail(2, token0(), 1 ** 18, address(this), owners); + // snapStart("transferFrom with different owners"); + // permit2.transferFrom(transferDetails); + // snapEnd(); + + // assertEq(token0.balanceOf(from), startBalanceFrom - 1 ** 18); + // assertEq(token0.balanceOf(fromDirty), startBalanceFromDirty - 1 ** 18); + // assertEq(token0.balanceOf(address(this)), startBalanceTo + 2 * 1 ** 18); + // (amount,,) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId - 1 ** 18); + // (amount,,) = permit2.allowance(fromDirty, token0(), address(this)); + // assertEq(amount, defaultAmountOrId - 1 ** 18); + // } + + // function testLockdown() public { + // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + // IAllowanceTransfer.PermitBatch memory permit = + // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // permit2.permit(from, permit, sig); + + // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token1(), address(this)); + // assertEq(amount1, defaultAmountOrId); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 1); + + // IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); + // approvals[0] = IAllowanceTransfer.TokenSpenderPair(token0(), address(this)); + // approvals[1] = IAllowanceTransfer.TokenSpenderPair(token1(), address(this)); + + // vm.prank(from); + // snapStart("lockdown"); + // permit2.lockdown(approvals); + // snapEnd(); + + // (amount, expiration, nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, 0); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + // (amount1, expiration1, nonce1) = permit2.allowance(from, token1(), address(this)); + // assertEq(amount1, 0); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 1); + // } + + // function testLockdownEvent() public { + // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + // IAllowanceTransfer.PermitBatch memory permit = + // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // permit2.permit(from, permit, sig); + + // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token1(), address(this)); + // assertEq(amount1, defaultAmountOrId); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 1); + + // IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); + // approvals[0] = IAllowanceTransfer.TokenSpenderPair(token0(), address(this)); + // approvals[1] = IAllowanceTransfer.TokenSpenderPair(token1(), address(this)); + + // //TODO :fix expecting multiple events, can only check for 1 + // vm.prank(from); + // vm.expectEmit(true, false, false, false); + // emit Lockdown(from, token0(), address(this)); + // permit2.lockdown(approvals); + + // (amount, expiration, nonce) = permit2.allowance(from, token0(), address(this)); + // assertEq(amount, 0); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + // (amount1, expiration1, nonce1) = permit2.allowance(from, token1(), address(this)); + // assertEq(amount1, 0); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 1); + // } } diff --git a/test/SignatureTransfer.t.sol b/test/SignatureTransfer.t.sol index e1aa66ec..332bc2a6 100644 --- a/test/SignatureTransfer.t.sol +++ b/test/SignatureTransfer.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {SafeERC20, IERC20, IERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import {SignatureVerification} from "../src/shared/SignatureVerification.sol"; -import {TokenProvider} from "./utils/TokenProvider.sol"; +import {TokenProvider_ERC20} from "./utils/TokenProvider_ERC20.sol"; import {PermitSignature} from "./utils/PermitSignature.sol"; import {AddressBuilder} from "./utils/AddressBuilder.sol"; import {AmountBuilder} from "./utils/AmountBuilder.sol"; @@ -15,7 +15,7 @@ import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {ISignatureTransfer} from "../src/ERC20/interfaces/ISignatureTransfer.sol"; import {InvalidNonce, SignatureExpired} from "../src/shared/PermitErrors.sol"; -contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnapshot { +contract SignatureTransferTest is Test, PermitSignature, TokenProvider_ERC20, GasSnapshot { using AddressBuilder for address[]; using AmountBuilder for uint256[]; @@ -94,28 +94,28 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps function testPermitTransferFrom() public { uint256 nonce = 0; - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), nonce); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(_token0), nonce); bytes memory sig = getPermitTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address2); + uint256 startBalanceFrom = _token0.balanceOf(from); + uint256 startBalanceTo = _token0.balanceOf(address2); ISignatureTransfer.SignatureTransferDetails memory transferDetails = getTransferDetails(address2, defaultAmount); permit2.permitTransferFrom(permit, transferDetails, from, sig); - assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmount); - assertEq(token0.balanceOf(address2), startBalanceTo + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom - defaultAmount); + assertEq(_token0.balanceOf(address2), startBalanceTo + defaultAmount); } function testPermitTransferFromCompactSig() public { uint256 nonce = 0; - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), nonce); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(_token0), nonce); bytes memory sig = getCompactPermitTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); assertEq(sig.length, 64); - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address2); + uint256 startBalanceFrom = _token0.balanceOf(from); + uint256 startBalanceTo = _token0.balanceOf(address2); ISignatureTransfer.SignatureTransferDetails memory transferDetails = getTransferDetails(address2, defaultAmount); @@ -123,13 +123,13 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps permit2.permitTransferFrom(permit, transferDetails, from, sig); snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmount); - assertEq(token0.balanceOf(address2), startBalanceTo + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom - defaultAmount); + assertEq(_token0.balanceOf(address2), startBalanceTo + defaultAmount); } function testPermitTransferFromIncorrectSigLength() public { uint256 nonce = 0; - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), nonce); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(_token0), nonce); bytes memory sig = getPermitTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); bytes memory sigExtra = bytes.concat(sig, bytes1(uint8(0))); assertEq(sigExtra.length, 66); @@ -143,23 +143,23 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps function testPermitTransferFromToSpender() public { uint256 nonce = 0; // signed spender is address(this) - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), nonce); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(_token0), nonce); bytes memory sig = getPermitTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address0); + uint256 startBalanceFrom = _token0.balanceOf(from); + uint256 startBalanceTo = _token0.balanceOf(address0); ISignatureTransfer.SignatureTransferDetails memory transferDetails = getTransferDetails(address0, defaultAmount); permit2.permitTransferFrom(permit, transferDetails, from, sig); - assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmount); - assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom - defaultAmount); + assertEq(_token0.balanceOf(address0), startBalanceTo + defaultAmount); } function testPermitTransferFromInvalidNonce() public { uint256 nonce = 0; - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), nonce); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(_token0), nonce); bytes memory sig = getPermitTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); ISignatureTransfer.SignatureTransferDetails memory transferDetails = getTransferDetails(address2, defaultAmount); @@ -170,41 +170,41 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps } function testPermitTransferFromRandomNonceAndAmount(uint256 nonce, uint128 amount) public { - token0.mint(address(from), amount); - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), nonce); + _token0.mint(address(from), amount); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(_token0), nonce); permit.permitted.amount = amount; bytes memory sig = getPermitTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address2); + uint256 startBalanceFrom = _token0.balanceOf(from); + uint256 startBalanceTo = _token0.balanceOf(address2); ISignatureTransfer.SignatureTransferDetails memory transferDetails = getTransferDetails(address2, amount); permit2.permitTransferFrom(permit, transferDetails, from, sig); - assertEq(token0.balanceOf(from), startBalanceFrom - amount); - assertEq(token0.balanceOf(address2), startBalanceTo + amount); + assertEq(_token0.balanceOf(from), startBalanceFrom - amount); + assertEq(_token0.balanceOf(address2), startBalanceTo + amount); } function testPermitTransferSpendLessThanFull(uint256 nonce, uint128 amount) public { - token0.mint(address(from), amount); - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), nonce); + _token0.mint(address(from), amount); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(_token0), nonce); permit.permitted.amount = amount; bytes memory sig = getPermitTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address2); + uint256 startBalanceFrom = _token0.balanceOf(from); + uint256 startBalanceTo = _token0.balanceOf(address2); uint256 amountToSpend = amount / 2; ISignatureTransfer.SignatureTransferDetails memory transferDetails = getTransferDetails(address2, amountToSpend); permit2.permitTransferFrom(permit, transferDetails, from, sig); - assertEq(token0.balanceOf(from), startBalanceFrom - amountToSpend); - assertEq(token0.balanceOf(address2), startBalanceTo + amountToSpend); + assertEq(_token0.balanceOf(from), startBalanceFrom - amountToSpend); + assertEq(_token0.balanceOf(address2), startBalanceTo + amountToSpend); } function testPermitBatchTransferFrom() public { uint256 nonce = 0; - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); + address[] memory tokens = AddressBuilder.fill(1, address(_token0)).push(address(_token1)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); @@ -212,104 +212,104 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps ISignatureTransfer.SignatureTransferDetails[] memory toAmountPairs = StructBuilder.fillSigTransferDetails(defaultAmount, to); - uint256 startBalanceFrom0 = token0.balanceOf(from); - uint256 startBalanceFrom1 = token1.balanceOf(from); - uint256 startBalanceTo0 = token0.balanceOf(address2); - uint256 startBalanceTo1 = token1.balanceOf(address0); + uint256 startBalanceFrom0 = _token0.balanceOf(from); + uint256 startBalanceFrom1 = _token1.balanceOf(from); + uint256 startBalanceTo0 = _token0.balanceOf(address2); + uint256 startBalanceTo1 = _token1.balanceOf(address0); permit2.permitTransferFrom(permit, toAmountPairs, from, sig); - assertEq(token0.balanceOf(from), startBalanceFrom0 - defaultAmount); - assertEq(token1.balanceOf(from), startBalanceFrom1 - defaultAmount); - assertEq(token0.balanceOf(address2), startBalanceTo0 + defaultAmount); - assertEq(token1.balanceOf(address0), startBalanceTo1 + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom0 - defaultAmount); + assertEq(_token1.balanceOf(from), startBalanceFrom1 - defaultAmount); + assertEq(_token0.balanceOf(address2), startBalanceTo0 + defaultAmount); + assertEq(_token1.balanceOf(address0), startBalanceTo1 + defaultAmount); } function testPermitBatchMultiPermitSingleTransfer() public { uint256 nonce = 0; - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); + address[] memory tokens = AddressBuilder.fill(1, address(_token0)).push(address(_token1)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - // must fill address to even though token0 wont get sent. + // must fill address to even though _token0 wont get sent. // transfer details must be lenght of permit address[] memory to = AddressBuilder.fill(1, address(address0)).push(address(address0)); ISignatureTransfer.SignatureTransferDetails[] memory toAmountPairs = StructBuilder.fillSigTransferDetails(defaultAmount, to); - // spender doesnt need token0 even though user permitted it + // spender doesnt need _token0 even though user permitted it toAmountPairs[0].requestedAmount = 0; - uint256 startBalanceFrom0 = token0.balanceOf(from); - uint256 startBalanceFrom1 = token1.balanceOf(from); - uint256 startBalanceTo0 = token0.balanceOf(address2); - uint256 startBalanceTo1 = token1.balanceOf(address0); + uint256 startBalanceFrom0 = _token0.balanceOf(from); + uint256 startBalanceFrom1 = _token1.balanceOf(from); + uint256 startBalanceTo0 = _token0.balanceOf(address2); + uint256 startBalanceTo1 = _token1.balanceOf(address0); permit2.permitTransferFrom(permit, toAmountPairs, from, sig); - assertEq(token0.balanceOf(from), startBalanceFrom0); - assertEq(token1.balanceOf(from), startBalanceFrom1 - defaultAmount); - assertEq(token0.balanceOf(address2), startBalanceTo0); - assertEq(token1.balanceOf(address0), startBalanceTo1 + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom0); + assertEq(_token1.balanceOf(from), startBalanceFrom1 - defaultAmount); + assertEq(_token0.balanceOf(address2), startBalanceTo0); + assertEq(_token1.balanceOf(address0), startBalanceTo1 + defaultAmount); } function testPermitBatchTransferFromSingleRecipient() public { uint256 nonce = 0; - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); + address[] memory tokens = AddressBuilder.fill(1, address(_token0)).push(address(_token1)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); ISignatureTransfer.SignatureTransferDetails[] memory toAmountPairs = StructBuilder.fillSigTransferDetails(2, defaultAmount, address(address2)); - uint256 startBalanceFrom0 = token0.balanceOf(from); - uint256 startBalanceFrom1 = token1.balanceOf(from); - uint256 startBalanceTo0 = token0.balanceOf(address2); - uint256 startBalanceTo1 = token1.balanceOf(address2); + uint256 startBalanceFrom0 = _token0.balanceOf(from); + uint256 startBalanceFrom1 = _token1.balanceOf(from); + uint256 startBalanceTo0 = _token0.balanceOf(address2); + uint256 startBalanceTo1 = _token1.balanceOf(address2); snapStart("single recipient 2 tokens"); permit2.permitTransferFrom(permit, toAmountPairs, from, sig); snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom0 - defaultAmount); - assertEq(token1.balanceOf(from), startBalanceFrom1 - defaultAmount); - assertEq(token0.balanceOf(address2), startBalanceTo0 + defaultAmount); - assertEq(token1.balanceOf(address2), startBalanceTo1 + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom0 - defaultAmount); + assertEq(_token1.balanceOf(from), startBalanceFrom1 - defaultAmount); + assertEq(_token0.balanceOf(address2), startBalanceTo0 + defaultAmount); + assertEq(_token1.balanceOf(address2), startBalanceTo1 + defaultAmount); } function testPermitBatchTransferMultiAddr() public { uint256 nonce = 0; // signed spender is address(this) - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); + address[] memory tokens = AddressBuilder.fill(1, address(_token0)).push(address(_token1)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - uint256 startBalanceFrom0 = token0.balanceOf(from); - uint256 startBalanceFrom1 = token1.balanceOf(from); - uint256 startBalanceTo0 = token0.balanceOf(address(this)); - uint256 startBalanceTo1 = token1.balanceOf(address2); + uint256 startBalanceFrom0 = _token0.balanceOf(from); + uint256 startBalanceFrom1 = _token1.balanceOf(from); + uint256 startBalanceTo0 = _token0.balanceOf(address(this)); + uint256 startBalanceTo1 = _token1.balanceOf(address2); address[] memory to = AddressBuilder.fill(1, address(this)).push(address2); ISignatureTransfer.SignatureTransferDetails[] memory toAmountPairs = StructBuilder.fillSigTransferDetails(defaultAmount, to); permit2.permitTransferFrom(permit, toAmountPairs, from, sig); - assertEq(token0.balanceOf(from), startBalanceFrom0 - defaultAmount); - assertEq(token0.balanceOf(address(this)), startBalanceTo0 + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom0 - defaultAmount); + assertEq(_token0.balanceOf(address(this)), startBalanceTo0 + defaultAmount); - assertEq(token1.balanceOf(from), startBalanceFrom1 - defaultAmount); - assertEq(token1.balanceOf(address2), startBalanceTo1 + defaultAmount); + assertEq(_token1.balanceOf(from), startBalanceFrom1 - defaultAmount); + assertEq(_token1.balanceOf(address2), startBalanceTo1 + defaultAmount); } function testPermitBatchTransferSingleRecipientManyTokens() public { uint256 nonce = 0; - address[] memory tokens = AddressBuilder.fill(10, address(token0)); + address[] memory tokens = AddressBuilder.fill(10, address(_token0)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - uint256 startBalanceFrom0 = token0.balanceOf(from); - uint256 startBalanceTo0 = token0.balanceOf(address(this)); + uint256 startBalanceFrom0 = _token0.balanceOf(from); + uint256 startBalanceTo0 = _token0.balanceOf(address(this)); ISignatureTransfer.SignatureTransferDetails[] memory toAmountPairs = StructBuilder.fillSigTransferDetails(10, defaultAmount, address(this)); @@ -318,14 +318,14 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps permit2.permitTransferFrom(permit, toAmountPairs, from, sig); snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom0 - 10 * defaultAmount); - assertEq(token0.balanceOf(address(this)), startBalanceTo0 + 10 * defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom0 - 10 * defaultAmount); + assertEq(_token0.balanceOf(address(this)), startBalanceTo0 + 10 * defaultAmount); } function testPermitBatchTransferInvalidAmountsLengthMismatch() public { uint256 nonce = 0; - address[] memory tokens = AddressBuilder.fill(2, address(token0)); + address[] memory tokens = AddressBuilder.fill(2, address(_token0)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); @@ -338,11 +338,11 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps function testGasSinglePermitTransferFrom() public { uint256 nonce = 0; - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), nonce); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(_token0), nonce); bytes memory sig = getPermitTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address2); + uint256 startBalanceFrom = _token0.balanceOf(from); + uint256 startBalanceTo = _token0.balanceOf(address2); ISignatureTransfer.SignatureTransferDetails memory transferDetails = getTransferDetails(address2, defaultAmount); @@ -350,33 +350,33 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps permit2.permitTransferFrom(permit, transferDetails, from, sig); snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmount); - assertEq(token0.balanceOf(address2), startBalanceTo + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom - defaultAmount); + assertEq(_token0.balanceOf(address2), startBalanceTo + defaultAmount); } function testGasSinglePermitBatchTransferFrom() public { uint256 nonce = 0; - address[] memory tokens = AddressBuilder.fill(1, address(token0)); + address[] memory tokens = AddressBuilder.fill(1, address(_token0)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); ISignatureTransfer.SignatureTransferDetails[] memory toAmountPairs = StructBuilder.fillSigTransferDetails(1, defaultAmount, address(address2)); - uint256 startBalanceFrom0 = token0.balanceOf(from); - uint256 startBalanceTo0 = token0.balanceOf(address2); + uint256 startBalanceFrom0 = _token0.balanceOf(from); + uint256 startBalanceTo0 = _token0.balanceOf(address2); snapStart("permitBatchTransferFromSingleToken"); permit2.permitTransferFrom(permit, toAmountPairs, from, sig); snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom0 - defaultAmount); - assertEq(token0.balanceOf(address2), startBalanceTo0 + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom0 - defaultAmount); + assertEq(_token0.balanceOf(address2), startBalanceTo0 + defaultAmount); } function testGasMultiplePermitBatchTransferFrom() public { uint256 nonce = 0; - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)).push(address(token1)); + address[] memory tokens = AddressBuilder.fill(1, address(_token0)).push(address(_token1)).push(address(_token1)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); @@ -384,28 +384,28 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps ISignatureTransfer.SignatureTransferDetails[] memory toAmountPairs = StructBuilder.fillSigTransferDetails(defaultAmount, to); - uint256 startBalanceFrom0 = token0.balanceOf(from); - uint256 startBalanceFrom1 = token1.balanceOf(from); - uint256 startBalanceTo0 = token0.balanceOf(address(address2)); - uint256 startBalanceTo1 = token1.balanceOf(address(address2)); - uint256 startBalanceToThis1 = token1.balanceOf(address(this)); + uint256 startBalanceFrom0 = _token0.balanceOf(from); + uint256 startBalanceFrom1 = _token1.balanceOf(from); + uint256 startBalanceTo0 = _token0.balanceOf(address(address2)); + uint256 startBalanceTo1 = _token1.balanceOf(address(address2)); + uint256 startBalanceToThis1 = _token1.balanceOf(address(this)); snapStart("permitBatchTransferFromMultipleTokens"); permit2.permitTransferFrom(permit, toAmountPairs, from, sig); snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom0 - defaultAmount); - assertEq(token0.balanceOf(address2), startBalanceTo0 + defaultAmount); - assertEq(token1.balanceOf(from), startBalanceFrom1 - 2 * defaultAmount); - assertEq(token1.balanceOf(address2), startBalanceTo1 + defaultAmount); - assertEq(token1.balanceOf(address(this)), startBalanceToThis1 + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom0 - defaultAmount); + assertEq(_token0.balanceOf(address2), startBalanceTo0 + defaultAmount); + assertEq(_token1.balanceOf(from), startBalanceFrom1 - 2 * defaultAmount); + assertEq(_token1.balanceOf(address2), startBalanceTo1 + defaultAmount); + assertEq(_token1.balanceOf(address(this)), startBalanceToThis1 + defaultAmount); } function testPermitBatchTransferFromTypedWitness() public { uint256 nonce = 0; MockWitness memory witnessData = MockWitness(10000000, address(5), true); bytes32 witness = keccak256(abi.encode(witnessData)); - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); + address[] memory tokens = AddressBuilder.fill(1, address(_token0)).push(address(_token1)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchWitnessSignature( @@ -416,26 +416,26 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps ISignatureTransfer.SignatureTransferDetails[] memory toAmountPairs = StructBuilder.fillSigTransferDetails(defaultAmount, to); - uint256 startBalanceFrom0 = token0.balanceOf(from); - uint256 startBalanceFrom1 = token1.balanceOf(from); - uint256 startBalanceTo0 = token0.balanceOf(address2); - uint256 startBalanceTo1 = token1.balanceOf(address0); + uint256 startBalanceFrom0 = _token0.balanceOf(from); + uint256 startBalanceFrom1 = _token1.balanceOf(from); + uint256 startBalanceTo0 = _token0.balanceOf(address2); + uint256 startBalanceTo1 = _token1.balanceOf(address0); snapStart("permitTransferFromBatchTypedWitness"); permit2.permitWitnessTransferFrom(permit, toAmountPairs, from, witness, WITNESS_TYPE_STRING, sig); snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom0 - defaultAmount); - assertEq(token1.balanceOf(from), startBalanceFrom1 - defaultAmount); - assertEq(token0.balanceOf(address2), startBalanceTo0 + defaultAmount); - assertEq(token1.balanceOf(address0), startBalanceTo1 + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom0 - defaultAmount); + assertEq(_token1.balanceOf(from), startBalanceFrom1 - defaultAmount); + assertEq(_token0.balanceOf(address2), startBalanceTo0 + defaultAmount); + assertEq(_token1.balanceOf(address0), startBalanceTo1 + defaultAmount); } function testPermitBatchTransferFromTypedWitnessInvalidType() public { uint256 nonce = 0; MockWitness memory witnessData = MockWitness(10000000, address(5), true); bytes32 witness = keccak256(abi.encode(witnessData)); - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); + address[] memory tokens = AddressBuilder.fill(1, address(_token0)).push(address(_token1)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchWitnessSignature( permit, fromPrivateKey, FULL_EXAMPLE_WITNESS_BATCH_TYPEHASH, witness, DOMAIN_SEPARATOR @@ -453,7 +453,7 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps uint256 nonce = 0; MockWitness memory witnessData = MockWitness(10000000, address(5), true); bytes32 witness = keccak256(abi.encode(witnessData)); - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); + address[] memory tokens = AddressBuilder.fill(1, address(_token0)).push(address(_token1)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchWitnessSignature(permit, fromPrivateKey, "fake typehash", witness, DOMAIN_SEPARATOR); @@ -470,7 +470,7 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps uint256 nonce = 0; MockWitness memory witnessData = MockWitness(10000000, address(5), true); bytes32 witness = keccak256(abi.encode(witnessData)); - address[] memory tokens = AddressBuilder.fill(1, address(token0)).push(address(token1)); + address[] memory tokens = AddressBuilder.fill(1, address(_token0)).push(address(_token1)); ISignatureTransfer.PermitBatchTransferFrom memory permit = defaultERC20PermitMultiple(tokens, nonce); bytes memory sig = getPermitBatchWitnessSignature( permit, fromPrivateKey, FULL_EXAMPLE_WITNESS_BATCH_TYPEHASH, witness, DOMAIN_SEPARATOR @@ -487,7 +487,7 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps } function testInvalidateUnorderedNonces() public { - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), 0); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(_token0), 0); bytes memory sig = getPermitTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); uint256 bitmap = permit2.nonceBitmap(from, 0); @@ -510,13 +510,13 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps uint256 nonce = 0; MockWitness memory witnessData = MockWitness(10000000, address(5), true); bytes32 witness = keccak256(abi.encode(witnessData)); - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitWitnessTransfer(address(token0), nonce); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitWitnessTransfer(address(_token0), nonce); bytes memory sig = getPermitWitnessTransferSignature( permit, fromPrivateKey, FULL_EXAMPLE_WITNESS_TYPEHASH, witness, DOMAIN_SEPARATOR ); - uint256 startBalanceFrom = token0.balanceOf(from); - uint256 startBalanceTo = token0.balanceOf(address2); + uint256 startBalanceFrom = _token0.balanceOf(from); + uint256 startBalanceTo = _token0.balanceOf(address2); ISignatureTransfer.SignatureTransferDetails memory transferDetails = getTransferDetails(address2, defaultAmount); @@ -524,15 +524,15 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps permit2.permitWitnessTransferFrom(permit, transferDetails, from, witness, WITNESS_TYPE_STRING, sig); snapEnd(); - assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmount); - assertEq(token0.balanceOf(address2), startBalanceTo + defaultAmount); + assertEq(_token0.balanceOf(from), startBalanceFrom - defaultAmount); + assertEq(_token0.balanceOf(address2), startBalanceTo + defaultAmount); } function testPermitTransferFromTypedWitnessInvalidType() public { uint256 nonce = 0; MockWitness memory witnessData = MockWitness(10000000, address(5), true); bytes32 witness = keccak256(abi.encode(witnessData)); - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitWitnessTransfer(address(token0), nonce); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitWitnessTransfer(address(_token0), nonce); bytes memory sig = getPermitWitnessTransferSignature( permit, fromPrivateKey, FULL_EXAMPLE_WITNESS_TYPEHASH, witness, DOMAIN_SEPARATOR ); @@ -547,7 +547,7 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps uint256 nonce = 0; MockWitness memory witnessData = MockWitness(10000000, address(5), true); bytes32 witness = keccak256(abi.encode(witnessData)); - ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitWitnessTransfer(address(token0), nonce); + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitWitnessTransfer(address(_token0), nonce); bytes memory sig = getPermitWitnessTransferSignature(permit, fromPrivateKey, "fake typehash", witness, DOMAIN_SEPARATOR); diff --git a/test/shared/AllowanceUnitTest_ERC20.t.sol b/test/shared/AllowanceUnitTest_ERC20.t.sol index 51e59338..7631de06 100644 --- a/test/shared/AllowanceUnitTest_ERC20.t.sol +++ b/test/shared/AllowanceUnitTest_ERC20.t.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import "../mocks/MockPermit2.sol"; import {BaseAllowanceUnitTest} from "./BaseAllowanceUnitTest.sol"; -import {TokenProvider} from "../utils/TokenProvider.sol"; +import {TokenProvider_ERC20} from "../utils/TokenProvider_ERC20.sol"; -contract AllowanceUnitTest_ERC20 is BaseAllowanceUnitTest { +contract AllowanceUnitTest_ERC20 is TokenProvider_ERC20, BaseAllowanceUnitTest { function setUp() public override { permit2 = new MockPermit2(); initializeERC20Tokens(); @@ -21,7 +21,7 @@ contract AllowanceUnitTest_ERC20 is BaseAllowanceUnitTest { return MockPermit2(address(permit2)).allowance(from, token, spender); } - function token() public view override returns (address) { - return address(token1); + function token1() public view override returns (address) { + return address(_token1); } } diff --git a/test/shared/AllowanceUnitTest_ERC721.t.sol b/test/shared/AllowanceUnitTest_ERC721.t.sol index 98c0368e..d4fa7e71 100644 --- a/test/shared/AllowanceUnitTest_ERC721.t.sol +++ b/test/shared/AllowanceUnitTest_ERC721.t.sol @@ -4,12 +4,12 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import "../mocks/MockPermit2_ERC721.sol"; import {BaseAllowanceUnitTest} from "./BaseAllowanceUnitTest.sol"; -import {TokenProvider} from "../utils/TokenProvider.sol"; +import {TokenProvider_ERC721} from "../utils/TokenProvider_ERC721.sol"; -contract AllowanceUnitTest_ERC721 is BaseAllowanceUnitTest { +contract AllowanceUnitTest_ERC721 is TokenProvider_ERC721, BaseAllowanceUnitTest { function setUp() public override { permit2 = new MockPermit2_ERC721(); - initializeNFTTokens(); + initializeERC721TestTokens(); } function allowance(address from, address token, address spender) @@ -21,7 +21,7 @@ contract AllowanceUnitTest_ERC721 is BaseAllowanceUnitTest { return MockPermit2_ERC721(address(permit2)).allowance(from, token, spender); } - function token() public view override returns (address) { - return address(nft1); + function token1() public view override returns (address) { + return address(_token1); } } diff --git a/test/shared/BaseAllowanceUnitTest.sol b/test/shared/BaseAllowanceUnitTest.sol index a2319f66..5b052c27 100644 --- a/test/shared/BaseAllowanceUnitTest.sol +++ b/test/shared/BaseAllowanceUnitTest.sol @@ -3,9 +3,8 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import "../mocks/IMockPermit2.sol"; -import {TokenProvider} from "../utils/TokenProvider.sol"; -abstract contract BaseAllowanceUnitTest is Test, TokenProvider { +abstract contract BaseAllowanceUnitTest is Test { IMockPermit2 permit2; address from = address(0xBEEE); @@ -15,10 +14,10 @@ abstract contract BaseAllowanceUnitTest is Test, TokenProvider { function allowance(address from, address token, address spender) public virtual returns (uint160, uint48, uint48); - function token() public virtual returns (address); + function token1() public virtual returns (address); function testUpdateAmountExpirationRandomly(uint160 amount, uint48 expiration) public { - address token = token(); + address token = token1(); (,, uint48 nonce) = allowance(from, token, spender); @@ -38,7 +37,7 @@ abstract contract BaseAllowanceUnitTest is Test, TokenProvider { // we assume we will never be able to reach 2**48 vm.assume(nonce < type(uint48).max); - address token = token(); + address token = token1(); permit2.mockUpdateAll(from, token, spender, amount, expiration, nonce); @@ -55,7 +54,7 @@ abstract contract BaseAllowanceUnitTest is Test, TokenProvider { function testPackAndUnpack(uint160 amount, uint48 expiration, uint48 nonce) public { // pack some numbers uint256 word = Allowance.pack(amount, expiration, nonce); - address token = token(); + address token = token1(); // store the raw word permit2.doStore(from, token, spender, word); diff --git a/test/utils/PermitSignature.sol b/test/utils/PermitSignature.sol index f556b478..192cf173 100644 --- a/test/utils/PermitSignature.sol +++ b/test/utils/PermitSignature.sol @@ -30,6 +30,16 @@ contract PermitSignature is Test { "PermitBatchTransferFrom(TokenPermissions[] permitted,address spender,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)" ); + // used for testing abstraction + struct IPermitSingle { + address token; + uint160 amountOrId; + uint48 expiration; + uint48 nonce; + address spender; + uint256 sigDeadline; + } + function getPermitSignatureRaw( IAllowanceTransfer.PermitSingle memory permit, uint256 privateKey, @@ -235,13 +245,30 @@ contract PermitSignature is Test { return bytes.concat(r, s, bytes1(v)); } - function defaultERC20PermitAllowance(address token0, uint160 amount, uint48 expiration, uint48 nonce) + function defaultPermitAllowance(address token, uint160 amountOrId, uint48 expiration, uint48 nonce) + public + view + returns (IPermitSingle memory) + { + IAllowanceTransfer.PermitDetails memory details = + IAllowanceTransfer.PermitDetails({token: token, amount: amountOrId, expiration: expiration, nonce: nonce}); + return IPermitSingle({ + token: token, + amountOrId: amountOrId, + expiration: expiration, + nonce: nonce, + spender: address(this), + sigDeadline: block.timestamp + 100 + }); + } + + function defaultERC20PermitAllowance(address token, uint160 amount, uint48 expiration, uint48 nonce) internal view returns (IAllowanceTransfer.PermitSingle memory) { IAllowanceTransfer.PermitDetails memory details = - IAllowanceTransfer.PermitDetails({token: token0, amount: amount, expiration: expiration, nonce: nonce}); + IAllowanceTransfer.PermitDetails({token: token, amount: amount, expiration: expiration, nonce: nonce}); return IAllowanceTransfer.PermitSingle({ details: details, spender: address(this), @@ -272,25 +299,25 @@ contract PermitSignature is Test { }); } - function defaultERC20PermitTransfer(address token0, uint256 nonce) + function defaultERC20PermitTransfer(address token, uint256 nonce) internal view returns (ISignatureTransfer.PermitTransferFrom memory) { return ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({token: token0, amount: 10 ** 18}), + permitted: ISignatureTransfer.TokenPermissions({token: token, amount: 10 ** 18}), nonce: nonce, deadline: block.timestamp + 100 }); } - function defaultERC20PermitWitnessTransfer(address token0, uint256 nonce) + function defaultERC20PermitWitnessTransfer(address token, uint256 nonce) internal view returns (ISignatureTransfer.PermitTransferFrom memory) { return ISignatureTransfer.PermitTransferFrom({ - permitted: ISignatureTransfer.TokenPermissions({token: token0, amount: 10 ** 18}), + permitted: ISignatureTransfer.TokenPermissions({token: token, amount: 10 ** 18}), nonce: nonce, deadline: block.timestamp + 100 }); diff --git a/test/utils/TokenProvider.sol b/test/utils/TokenProvider.sol deleted file mode 100644 index a240fa40..00000000 --- a/test/utils/TokenProvider.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import "forge-std/Test.sol"; -import {MockERC20} from "../mocks/MockERC20.sol"; -import {MockERC721} from "../mocks/MockERC721.sol"; -import {MockERC1155} from "../mocks/MockERC1155.sol"; - -contract TokenProvider { - uint256 public constant MINT_AMOUNT_ERC20 = 100 ** 18; - uint256 public constant MINT_AMOUNT_ERC1155 = 100; - - uint256 public constant TRANSFER_AMOUNT_ERC20 = 30 ** 18; - uint256 public constant TRANSFER_AMOUNT_ERC1155 = 10; - - MockERC20 token0; - MockERC20 token1; - MockERC721 nft1; - MockERC721 nft2; - MockERC1155 nft3; - MockERC1155 nft4; - - address faucet = address(0x98765); - - function initializeERC20Tokens() public { - token0 = new MockERC20("Test0", "TEST0", 18); - token1 = new MockERC20("Test1", "TEST1", 18); - } - - function setERC20TestTokens(address from) public { - token0.mint(from, MINT_AMOUNT_ERC20); - token1.mint(from, MINT_AMOUNT_ERC20); - } - - function setERC20TestTokenApprovals(Vm vm, address owner, address spender) public { - vm.startPrank(owner); - token0.approve(spender, type(uint256).max); - token1.approve(spender, type(uint256).max); - vm.stopPrank(); - } - - function initializeNFTTokens() public { - nft1 = new MockERC721("TestNFT1", "NFT1"); - nft2 = new MockERC721("TestNFT2", "NFT2"); - nft3 = new MockERC1155(); - nft4 = new MockERC1155(); - } - - // 721s - function setNFTTestTokens(address from) public { - // mint with id 1 - nft1.mint(from, 1); - // mint with id 2 - nft2.mint(from, 2); - // mint 10 with id 1 - nft3.mint(from, 1, MINT_AMOUNT_ERC1155); - // mint 10 with id 2 - nft4.mint(from, 2, MINT_AMOUNT_ERC1155); - } - - function setNFTTestTokenApprovals(Vm vm, address owner, address spender) public { - vm.startPrank(owner); - nft1.approve(spender, 1); - nft2.approve(spender, 2); - nft3.setApprovalForAll(spender, true); - nft4.setApprovalForAll(spender, true); - vm.stopPrank(); - } -} diff --git a/test/utils/TokenProvider_ERC20.sol b/test/utils/TokenProvider_ERC20.sol new file mode 100644 index 00000000..e38503fd --- /dev/null +++ b/test/utils/TokenProvider_ERC20.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {MockERC20} from "../mocks/MockERC20.sol"; + +contract TokenProvider_ERC20 { + uint256 public constant MINT_AMOUNT_ERC20 = 100 ** 18; + uint256 public constant MINT_AMOUNT_ERC1155 = 100; + + uint256 public constant TRANSFER_AMOUNT_ERC20 = 30 ** 18; + uint256 public constant TRANSFER_AMOUNT_ERC1155 = 10; + + MockERC20 _token0; + MockERC20 _token1; + + address faucet = address(0x98765); + + function initializeERC20Tokens() public { + _token0 = new MockERC20("Test0", "TEST0", 18); + _token1 = new MockERC20("Test1", "TEST1", 18); + } + + function setERC20TestTokens(address from) public { + _token0.mint(from, MINT_AMOUNT_ERC20); + _token1.mint(from, MINT_AMOUNT_ERC20); + } + + function setERC20TestTokenApprovals(Vm vm, address owner, address spender) public { + vm.startPrank(owner); + _token0.approve(spender, type(uint256).max); + _token1.approve(spender, type(uint256).max); + vm.stopPrank(); + } + + // function initializeERC721TestTokens() public { + // token_erc721_0 = new MockERC721("TestNFT1", "NFT1"); + // token_erc721_1 = new MockERC721("TestNFT2", "NFT2"); + // } + + // function setERC721TestTokens(address from) public { + // // mint with id 1 + // token_erc721_0.mint(from, 1); + // // mint with id 2 + // token_erc721_1.mint(from, 2); + // } + + // function setERC721TestTokenApprovals(Vm vm, address owner, address spender) public { + // vm.startPrank(owner); + // token_erc721_0.approve(spender, 1); + // token_erc721_1.approve(spender, 2); + // vm.stopPrank(); + // } + + // function initializeERC1155TestTokens() public { + // token_erc1155_0 = new MockERC1155(); + // token_erc1155_1 = new MockERC1155(); + // } + + // function setERC1155TestTokens(address from) public { + // // mint 10 with id 1 + // token_erc1155_0.mint(from, 1, MINT_AMOUNT_ERC1155); + // // mint 10 with id 2 + // token_erc1155_1.mint(from, 2, MINT_AMOUNT_ERC1155); + // } + + // function setERC1155TestTokenApprovals(Vm vm, address owner, address spender) public { + // vm.startPrank(owner); + // token_erc1155_0.setApprovalForAll(spender, true); + // token_erc1155_1.setApprovalForAll(spender, true); + // vm.stopPrank(); + // } +} diff --git a/test/utils/TokenProvider_ERC721.sol b/test/utils/TokenProvider_ERC721.sol new file mode 100644 index 00000000..630891b9 --- /dev/null +++ b/test/utils/TokenProvider_ERC721.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {MockERC721} from "../mocks/MockERC721.sol"; + +contract TokenProvider_ERC721 { + uint256 public constant MINT_AMOUNT_ERC20 = 100 ** 18; + uint256 public constant MINT_AMOUNT_ERC1155 = 100; + + uint256 public constant TRANSFER_AMOUNT_ERC20 = 30 ** 18; + uint256 public constant TRANSFER_AMOUNT_ERC1155 = 10; + + MockERC721 _token0; + MockERC721 _token1; + + address faucet = address(0x98765); + + function initializeERC721TestTokens() public { + _token0 = new MockERC721("TestNFT1", "NFT1"); + _token1 = new MockERC721("TestNFT2", "NFT2"); + } + + function setERC721TestTokens(address from) public { + // mint with id 1 + _token0.mint(from, 1); + // mint with id 2 + _token1.mint(from, 2); + } + + function setERC721TestTokenApprovals(Vm vm, address owner, address spender) public { + vm.startPrank(owner); + _token0.approve(spender, 1); + _token1.approve(spender, 2); + vm.stopPrank(); + } + + // function initializeERC1155TestTokens() public { + // token_erc1155_0 = new MockERC1155(); + // token_erc1155_1 = new MockERC1155(); + // } + + // function setERC1155TestTokens(address from) public { + // // mint 10 with id 1 + // token_erc1155_0.mint(from, 1, MINT_AMOUNT_ERC1155); + // // mint 10 with id 2 + // token_erc1155_1.mint(from, 2, MINT_AMOUNT_ERC1155); + // } + + // function setERC1155TestTokenApprovals(Vm vm, address owner, address spender) public { + // vm.startPrank(owner); + // token_erc1155_0.setApprovalForAll(spender, true); + // token_erc1155_1.setApprovalForAll(spender, true); + // vm.stopPrank(); + // } +} From d41aaf27b0a1e03a7973f3cbdb20b8d925d09eb8 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Tue, 10 Jan 2023 11:35:10 -0700 Subject: [PATCH 12/27] permit single refactor done --- .forge-snapshots/permitCompactSig.snap | 2 +- test/AllowanceTransferTest_ERC20.t.sol | 15 + test/BaseAllowanceTransferTest.t.sol | 1161 ++++++++++++------------ 3 files changed, 598 insertions(+), 580 deletions(-) diff --git a/.forge-snapshots/permitCompactSig.snap b/.forge-snapshots/permitCompactSig.snap index eb882987..18ba881f 100644 --- a/.forge-snapshots/permitCompactSig.snap +++ b/.forge-snapshots/permitCompactSig.snap @@ -1 +1 @@ -63094 \ No newline at end of file +63360 \ No newline at end of file diff --git a/test/AllowanceTransferTest_ERC20.t.sol b/test/AllowanceTransferTest_ERC20.t.sol index 98f61905..bbe2bad5 100644 --- a/test/AllowanceTransferTest_ERC20.t.sol +++ b/test/AllowanceTransferTest_ERC20.t.sol @@ -82,6 +82,10 @@ contract AllowanceTransferTest_ERC20 is TokenProvider_ERC20, BaseAllowanceTransf return address(_token1); } + function balanceOf(address token, address from) public override returns (uint256) { + return MockERC20(token).balanceOf(from); + } + function getPermitSignature(IPermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator) public override @@ -91,6 +95,17 @@ contract AllowanceTransferTest_ERC20 is TokenProvider_ERC20, BaseAllowanceTransf return bytes.concat(r, s, bytes1(v)); } + function getCompactPermitSignature(IPermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator) + public + override + returns (bytes memory sig) + { + (uint8 v, bytes32 r, bytes32 s) = getPermitSignatureRaw(permit, privateKey, domainSeparator); + bytes32 vs; + (r, vs) = _getCompactSignature(v, r, s); + return bytes.concat(r, vs); + } + function getPermitSignatureRaw(IPermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator) internal returns (uint8 v, bytes32 r, bytes32 s) diff --git a/test/BaseAllowanceTransferTest.t.sol b/test/BaseAllowanceTransferTest.t.sol index 670a8538..6954ff76 100644 --- a/test/BaseAllowanceTransferTest.t.sol +++ b/test/BaseAllowanceTransferTest.t.sol @@ -59,11 +59,18 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho function token0() public virtual returns (address); function token1() public virtual returns (address); + function balanceOf(address token, address from) public virtual returns (uint256); + function getPermitSignature(IPermitSingle memory permit, uint256 privKey, bytes32 domainSeparator) public virtual returns (bytes memory); + function getCompactPermitSignature(IPermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator) + public + virtual + returns (bytes memory sig); + function permit2Approve(address token, address spender, uint160 amountOrId, uint48 expiration) public virtual; function permit2Allowance(address from, address token, address spender) @@ -88,12 +95,8 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho } function testSetAllowance() public { - // create a function defaultPermitAllowance() which will be overridden by ERC20, ERC721, and ERC1155 inherited classes - // IPermitSingle PermitSignature.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); snapStart("permitCleanWrite"); @@ -106,587 +109,587 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho assertEq(nonce, 1); } - // function testSetAllowanceCompactSig() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getCompactPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - // assertEq(sig.length, 64); - - // snapStart("permitCompactSig"); - // permit2.permit(from, permit, sig); - // snapEnd(); - - // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // assertEq(expiration, defaultExpiration); - // assertEq(nonce, 1); - // } - - // function testSetAllowanceIncorrectSigLength() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - // bytes memory sigExtra = bytes.concat(sig, bytes1(uint8(1))); - // assertEq(sigExtra.length, 66); - - // vm.expectRevert(SignatureVerification.InvalidSignatureLength.selector); - // permit2.permit(from, permit, sigExtra); - // } - - // function testSetAllowanceDirtyWrite() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); - - // snapStart("permitDirtyWrite"); - // permit2.permit(fromDirty, permit, sig); - // snapEnd(); - - // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(fromDirty, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // assertEq(expiration, defaultExpiration); - // assertEq(nonce, 2); - // } - - // function testSetAllowanceBatchDifferentNonces() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // permit2.permit(from, permit, sig); - - // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // assertEq(expiration, defaultExpiration); - // assertEq(nonce, 1); - - // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - // IAllowanceTransfer.PermitBatch memory permitBatch = - // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, 1); - // // first token nonce is 1, second token nonce is 0 - // permitBatch.details[1].nonce = 0; - // bytes memory sig1 = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); - - // permit2.permit(from, permitBatch, sig1); - - // (amount, expiration, nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // assertEq(expiration, defaultExpiration); - // assertEq(nonce, 2); - // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token1(), address(this)); - // assertEq(amount1, defaultAmountOrId); - // assertEq(expiration1, defaultExpiration); - // assertEq(nonce1, 1); - // } - - // function testSetAllowanceBatch() public { - // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - // IAllowanceTransfer.PermitBatch memory permit = - // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // snapStart("permitBatchCleanWrite"); - // permit2.permit(from, permit, sig); - // snapEnd(); - - // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // assertEq(expiration, defaultExpiration); - // assertEq(nonce, 1); - // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token1(), address(this)); - // assertEq(amount1, defaultAmountOrId); - // assertEq(expiration1, defaultExpiration); - // assertEq(nonce1, 1); - // } - - // function testSetAllowanceBatchEvent() public { - // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - // uint160[] memory amounts = AmountBuilder.fillUInt160(2, defaultAmountOrId); - - // IAllowanceTransfer.PermitBatch memory permit = - // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // vm.expectEmit(true, true, true, true); - // emit Permit(from, tokens[0], address(this), amounts[0], defaultExpiration, defaultNonce); - // vm.expectEmit(true, true, true, true); - // emit Permit(from, tokens[1], address(this), amounts[1], defaultExpiration, defaultNonce); - // permit2.permit(from, permit, sig); - - // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // assertEq(expiration, defaultExpiration); - // assertEq(nonce, 1); - // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token1(), address(this)); - // assertEq(amount1, defaultAmountOrId); - // assertEq(expiration1, defaultExpiration); - // assertEq(nonce1, 1); - // } - - // function testSetAllowanceBatchDirtyWrite() public { - // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - // IAllowanceTransfer.PermitBatch memory permit = - // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, dirtyNonce); - // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); - - // snapStart("permitBatchDirtyWrite"); - // permit2.permit(fromDirty, permit, sig); - // snapEnd(); - - // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(fromDirty, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // assertEq(expiration, defaultExpiration); - // assertEq(nonce, 2); - // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(fromDirty, token1(), address(this)); - // assertEq(amount1, defaultAmountOrId); - // assertEq(expiration1, defaultExpiration); - // assertEq(nonce1, 2); - // } - - // // test setting allowance with ordered nonce and transfer - // function testSetAllowanceTransfer() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // uint256 startBalanceFrom = token0.balanceOf(from); - // uint256 startBalanceTo = token0.balanceOf(address0); - - // permit2.permit(from, permit, sig); - - // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); - - // assertEq(amount, defaultAmountOrId); - - // permit2.transferFrom(from, address0, defaultAmountOrId, token0()); - - // assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); - // assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); - // } - - // function testTransferFromWithGasSnapshot() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // uint256 startBalanceFrom = token0.balanceOf(from); - // uint256 startBalanceTo = token0.balanceOf(address0); - - // permit2.permit(from, permit, sig); - - // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); - - // assertEq(amount, defaultAmountOrId); - - // snapStart("transferFrom"); - // permit2.transferFrom(from, address0, defaultAmountOrId, token0()); - - // snapEnd(); - // assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); - // assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); - // } - - // function testBatchTransferFromWithGasSnapshot() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // uint256 startBalanceFrom = token0.balanceOf(from); - // uint256 startBalanceTo = token0.balanceOf(address0); - - // permit2.permit(from, permit, sig); - - // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - - // // permit token0 for 1 ** 18 - // address[] memory owners = AddressBuilder.fill(3, from); - // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - // StructBuilder.fillAllowanceTransferDetail(3, token0(), 1 ** 18, address0, owners); - // snapStart("batchTransferFrom"); - // permit2.transferFrom(transferDetails); - // snapEnd(); - // assertEq(token0.balanceOf(from), startBalanceFrom - 3 * 1 ** 18); - // assertEq(token0.balanceOf(address0), startBalanceTo + 3 * 1 ** 18); - // (amount,,) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); - // } - - // // dirty sstore on nonce, dirty sstore on transfer - // function testSetAllowanceTransferDirtyNonceDirtyTransfer() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); - - // uint256 startBalanceFrom = token0.balanceOf(fromDirty); - // uint256 startBalanceTo = token0.balanceOf(address3); - // // ensure its a dirty store for the recipient address - // assertEq(startBalanceTo, defaultAmountOrId); + function testSetAllowanceCompactSig() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getCompactPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + assertEq(sig.length, 64); + + snapStart("permitCompactSig"); + permit2Permit(from, permit, sig); + snapEnd(); + + (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + assertEq(expiration, defaultExpiration); + assertEq(nonce, 1); + } + + function testSetAllowanceIncorrectSigLength() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + bytes memory sigExtra = bytes.concat(sig, bytes1(uint8(1))); + assertEq(sigExtra.length, 66); + + vm.expectRevert(SignatureVerification.InvalidSignatureLength.selector); + permit2Permit(from, permit, sigExtra); + } + + function testSetAllowanceDirtyWrite() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); + + snapStart("permitDirtyWrite"); + permit2Permit(fromDirty, permit, sig); + snapEnd(); + + (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(fromDirty, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + assertEq(expiration, defaultExpiration); + assertEq(nonce, 2); + } + + function testSetAllowanceBatchDifferentNonces() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + permit2Permit(from, permit, sig); + + (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + assertEq(expiration, defaultExpiration); + assertEq(nonce, 1); + + address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + IAllowanceTransfer.PermitBatch memory permitBatch = + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, 1); + // first token nonce is 1, second token nonce is 0 + permitBatch.details[1].nonce = 0; + bytes memory sig1 = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); + + permit2Permit(from, permitBatch, sig1); + + (amount, expiration, nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + assertEq(expiration, defaultExpiration); + assertEq(nonce, 2); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); + assertEq(amount1, defaultAmountOrId); + assertEq(expiration1, defaultExpiration); + assertEq(nonce1, 1); + } + + function testSetAllowanceBatch() public { + address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + IAllowanceTransfer.PermitBatch memory permit = + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + snapStart("permitBatchCleanWrite"); + permit2Permit(from, permit, sig); + snapEnd(); + + (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + assertEq(expiration, defaultExpiration); + assertEq(nonce, 1); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); + assertEq(amount1, defaultAmountOrId); + assertEq(expiration1, defaultExpiration); + assertEq(nonce1, 1); + } + + function testSetAllowanceBatchEvent() public { + address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + uint160[] memory amounts = AmountBuilder.fillUInt160(2, defaultAmountOrId); + + IAllowanceTransfer.PermitBatch memory permit = + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + vm.expectEmit(true, true, true, true); + emit Permit(from, tokens[0], address(this), amounts[0], defaultExpiration, defaultNonce); + vm.expectEmit(true, true, true, true); + emit Permit(from, tokens[1], address(this), amounts[1], defaultExpiration, defaultNonce); + permit2Permit(from, permit, sig); + + (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + assertEq(expiration, defaultExpiration); + assertEq(nonce, 1); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); + assertEq(amount1, defaultAmountOrId); + assertEq(expiration1, defaultExpiration); + assertEq(nonce1, 1); + } + + function testSetAllowanceBatchDirtyWrite() public { + address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + IAllowanceTransfer.PermitBatch memory permit = + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, dirtyNonce); + bytes memory sig = getPermitBatchSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); + + snapStart("permitBatchDirtyWrite"); + permit2Permit(fromDirty, permit, sig); + snapEnd(); + + (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(fromDirty, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + assertEq(expiration, defaultExpiration); + assertEq(nonce, 2); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(fromDirty, token1(), address(this)); + assertEq(amount1, defaultAmountOrId); + assertEq(expiration1, defaultExpiration); + assertEq(nonce1, 2); + } + + // test setting allowance with ordered nonce and transfer + function testSetAllowanceTransfer() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + uint256 startBalanceFrom = balanceOf(token0(), from); + uint256 startBalanceTo = balanceOf(token0(), address0); + + permit2Permit(from, permit, sig); + + (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + + assertEq(amount, defaultAmountOrId); + + permit2.transferFrom(from, address0, defaultAmountOrId, token0()); + + assertEq(balanceOf(token0(), from), startBalanceFrom - defaultAmountOrId); + assertEq(balanceOf(token0(), address0), startBalanceTo + defaultAmountOrId); + } + + function testTransferFromWithGasSnapshot() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + uint256 startBalanceFrom = balanceOf(token0(), from); + uint256 startBalanceTo = balanceOf(token0(), address0); + + permit2Permit(from, permit, sig); + + (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + + assertEq(amount, defaultAmountOrId); + + snapStart("transferFrom"); + permit2.transferFrom(from, address0, defaultAmountOrId, token0()); + + snapEnd(); + assertEq(balanceOf(token0(), from), startBalanceFrom - defaultAmountOrId); + assertEq(balanceOf(token0(), address0), startBalanceTo + defaultAmountOrId); + } + + function testBatchTransferFromWithGasSnapshot() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + uint256 startBalanceFrom = balanceOf(token0(), from); + uint256 startBalanceTo = balanceOf(token0(), address0); + + permit2Permit(from, permit, sig); + + (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + + // permit token0 for 1 ** 18 + address[] memory owners = AddressBuilder.fill(3, from); + IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + StructBuilder.fillAllowanceTransferDetail(3, token0(), 1 ** 18, address0, owners); + snapStart("batchTransferFrom"); + permit2.transferFrom(transferDetails); + snapEnd(); + assertEq(balanceOf(token0(), from), startBalanceFrom - 3 * 1 ** 18); + assertEq(balanceOf(token0(), address0), startBalanceTo + 3 * 1 ** 18); + (amount,,) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); + } + + // dirty sstore on nonce, dirty sstore on transfer + function testSetAllowanceTransferDirtyNonceDirtyTransfer() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); - // snapStart("permitDirtyNonce"); - // permit2.permit(fromDirty, permit, sig); - // snapEnd(); + uint256 startBalanceFrom = balanceOf(token0(), fromDirty); + uint256 startBalanceTo = balanceOf(token0(), address3); + // ensure its a dirty store for the recipient address + assertEq(startBalanceTo, defaultAmountOrId); - // (uint160 amount,,) = permit2.allowance(fromDirty, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); + snapStart("permitDirtyNonce"); + permit2Permit(fromDirty, permit, sig); + snapEnd(); - // permit2.transferFrom(fromDirty, address3, defaultAmountOrId, token0()); + (uint160 amount,,) = permit2Allowance(fromDirty, token0(), address(this)); + assertEq(amount, defaultAmountOrId); - // assertEq(token0.balanceOf(fromDirty), startBalanceFrom - defaultAmountOrId); - // assertEq(token0.balanceOf(address3), startBalanceTo + defaultAmountOrId); - // } + permit2.transferFrom(fromDirty, address3, defaultAmountOrId, token0()); - // function testSetAllowanceInvalidSignature() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - // snapStart("permitInvalidSigner"); - // vm.expectRevert(SignatureVerification.InvalidSigner.selector); - // permit.spender = address0; - // permit2.permit(from, permit, sig); - // snapEnd(); - // } + assertEq(balanceOf(token0(), fromDirty), startBalanceFrom - defaultAmountOrId); + assertEq(balanceOf(token0(), address3), startBalanceTo + defaultAmountOrId); + } - // function testSetAllowanceDeadlinePassed() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + function testSetAllowanceInvalidSignature() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + snapStart("permitInvalidSigner"); + vm.expectRevert(SignatureVerification.InvalidSigner.selector); + permit.spender = address0; + permit2Permit(from, permit, sig); + snapEnd(); + } - // uint256 sigDeadline = block.timestamp + 100; + function testSetAllowanceDeadlinePassed() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + uint256 sigDeadline = block.timestamp + 100; + + vm.warp(block.timestamp + 101); + snapStart("permitSignatureExpired"); + vm.expectRevert(abi.encodeWithSelector(SignatureExpired.selector, sigDeadline)); + permit2Permit(from, permit, sig); + snapEnd(); + } + + function testMaxAllowance() public { + uint160 maxAllowance = type(uint160).max; + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), maxAllowance, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + uint256 startBalanceFrom = balanceOf(token0(), from); + uint256 startBalanceTo = balanceOf(token0(), address0); + + snapStart("permitSetMaxAllowanceCleanWrite"); + permit2Permit(from, permit, sig); + snapEnd(); - // vm.warp(block.timestamp + 101); - // snapStart("permitSignatureExpired"); - // vm.expectRevert(abi.encodeWithSelector(SignatureExpired.selector, sigDeadline)); - // permit2.permit(from, permit, sig); - // snapEnd(); - // } - - // function testMaxAllowance() public { - // uint160 maxAllowance = type(uint160).max; - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), maxAllowance, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // uint256 startBalanceFrom = token0.balanceOf(from); - // uint256 startBalanceTo = token0.balanceOf(address0); - - // snapStart("permitSetMaxAllowanceCleanWrite"); - // permit2.permit(from, permit, sig); - // snapEnd(); - - // (uint160 startAllowedAmount0,,) = permit2.allowance(from, token0(), address(this)); - // assertEq(startAllowedAmount0, type(uint160).max); - - // permit2.transferFrom(from, address0, defaultAmountOrId, token0()); - - // (uint160 endAllowedAmount0,,) = permit2.allowance(from, token0(), address(this)); - // assertEq(endAllowedAmount0, type(uint160).max); - - // assertEq(token0.balanceOf(from), startBalanceFrom - defaultAmountOrId); - // assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); - // } - - // function testMaxAllowanceDirtyWrite() public { - // uint160 maxAllowance = type(uint160).max; - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), maxAllowance, defaultExpiration, dirtyNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); - - // uint256 startBalanceFrom = token0.balanceOf(fromDirty); - // uint256 startBalanceTo = token0.balanceOf(address0); - - // snapStart("permitSetMaxAllowanceDirtyWrite"); - // permit2.permit(fromDirty, permit, sig); - // snapEnd(); - - // (uint160 startAllowedAmount0,,) = permit2.allowance(fromDirty, token0(), address(this)); - // assertEq(startAllowedAmount0, type(uint160).max); - - // permit2.transferFrom(fromDirty, address0, defaultAmountOrId, token0()); - - // (uint160 endAllowedAmount0,,) = permit2.allowance(fromDirty, token0(), address(this)); - // assertEq(endAllowedAmount0, type(uint160).max); - - // assertEq(token0.balanceOf(fromDirty), startBalanceFrom - defaultAmountOrId); - // assertEq(token0.balanceOf(address0), startBalanceTo + defaultAmountOrId); - // } - - // function testPartialAllowance() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // uint256 startBalanceFrom = token0.balanceOf(from); - // uint256 startBalanceTo = token0.balanceOf(address0); - - // permit2.permit(from, permit, sig); - - // (uint160 startAllowedAmount0,,) = permit2.allowance(from, token0(), address(this)); - // assertEq(startAllowedAmount0, defaultAmountOrId); - - // uint160 transferAmount = 5 ** 18; - // permit2.transferFrom(from, address0, transferAmount, token0()); - // (uint160 endAllowedAmount0,,) = permit2.allowance(from, token0(), address(this)); - // // ensure the allowance was deducted - // assertEq(endAllowedAmount0, defaultAmountOrId - transferAmount); - - // assertEq(token0.balanceOf(from), startBalanceFrom - transferAmount); - // assertEq(token0.balanceOf(address0), startBalanceTo + transferAmount); - // } - - // function testReuseOrderedNonceInvalid() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // permit2.permit(from, permit, sig); - // (,, uint48 nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(nonce, 1); - - // (uint160 amount, uint48 expiration,) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // assertEq(expiration, defaultExpiration); - - // vm.expectRevert(InvalidNonce.selector); - // permit2.permit(from, permit, sig); - // } - - // function testInvalidateNonces() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // // Invalidates the 0th nonce by setting the new nonce to 1. - // vm.prank(from); - // vm.expectEmit(true, true, true, true); - // emit NonceInvalidation(from, token0(), address(this), 1, defaultNonce); - // permit2.invalidateNonces(token0(), address(this), 1); - // (,, uint48 nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(nonce, 1); - - // vm.expectRevert(InvalidNonce.selector); - // permit2.permit(from, permit, sig); - // } - - // function testInvalidateMultipleNonces() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // // Valid permit, uses nonce 0. - // permit2.permit(from, permit, sig); - // (,, uint48 nonce1) = permit2.allowance(from, token0(), address(this)); - // assertEq(nonce1, 1); - - // permit = defaultERC20PermitAllowance(token1(), defaultAmountOrId, defaultExpiration, nonce1); - // sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // // Invalidates the 9 nonces by setting the new nonce to 33. - // vm.prank(from); - // vm.expectEmit(true, true, true, true); - - // emit NonceInvalidation(from, token0(), address(this), 33, nonce1); - // permit2.invalidateNonces(token0(), address(this), 33); - // (,, uint48 nonce2) = permit2.allowance(from, token0(), address(this)); - // assertEq(nonce2, 33); - - // vm.expectRevert(InvalidNonce.selector); - // permit2.permit(from, permit, sig); - // } - - // function testInvalidateNoncesInvalid() public { - // // fromDirty nonce is 1 - // vm.prank(fromDirty); - // vm.expectRevert(InvalidNonce.selector); - // // setting nonce to 0 should revert - // permit2.invalidateNonces(token0(), address(this), 0); - // } - - // function testExcessiveInvalidation() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // uint32 numInvalidate = type(uint16).max; - // vm.startPrank(from); - // vm.expectRevert(IAllowanceTransfer.ExcessiveInvalidation.selector); - // permit2.invalidateNonces(token0(), address(this), numInvalidate + 1); - // vm.stopPrank(); - - // permit2.permit(from, permit, sig); - // (,, uint48 nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(nonce, 1); - // } - - // function testBatchTransferFrom() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // uint256 startBalanceFrom = token0.balanceOf(from); - // uint256 startBalanceTo = token0.balanceOf(address0); - - // permit2.permit(from, permit, sig); - - // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - - // // permit token0 for 1 ** 18 - // address[] memory owners = AddressBuilder.fill(3, from); - // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - // StructBuilder.fillAllowanceTransferDetail(3, token0(), 1 ** 18, address0, owners); - // snapStart("batchTransferFrom"); - // permit2.transferFrom(transferDetails); - // snapEnd(); - // assertEq(token0.balanceOf(from), startBalanceFrom - 3 * 1 ** 18); - // assertEq(token0.balanceOf(address0), startBalanceTo + 3 * 1 ** 18); - // (amount,,) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); - // } - - // function testBatchTransferFromMultiToken() public { - // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - // IAllowanceTransfer.PermitBatch memory permitBatch = - // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); - - // uint256 startBalanceFrom0 = token0.balanceOf(from); - // uint256 startBalanceFrom1 = token1.balanceOf(from); - // uint256 startBalanceTo0 = token0.balanceOf(address0); - // uint256 startBalanceTo1 = token1.balanceOf(address0); - - // permit2.permit(from, permitBatch, sig); - - // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // (amount,,) = permit2.allowance(from, token1(), address(this)); - // assertEq(amount, defaultAmountOrId); - - // // permit token0 for 1 ** 18 - // address[] memory owners = AddressBuilder.fill(2, from); - // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - // StructBuilder.fillAllowanceTransferDetail(2, tokens, 1 ** 18, address0, owners); - // snapStart("batchTransferFromMultiToken"); - // permit2.transferFrom(transferDetails); - // snapEnd(); - // assertEq(token0.balanceOf(from), startBalanceFrom0 - 1 ** 18); - // assertEq(token1.balanceOf(from), startBalanceFrom1 - 1 ** 18); - // assertEq(token0.balanceOf(address0), startBalanceTo0 + 1 ** 18); - // assertEq(token1.balanceOf(address0), startBalanceTo1 + 1 ** 18); - // (amount,,) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId - 1 ** 18); - // (amount,,) = permit2.allowance(from, token1(), address(this)); - // assertEq(amount, defaultAmountOrId - 1 ** 18); - // } - - // function testBatchTransferFromDifferentOwners() public { - // IAllowanceTransfer.PermitSingle memory permit = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // IAllowanceTransfer.PermitSingle memory permitDirty = - // defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); - // bytes memory sigDirty = getPermitSignature(permitDirty, fromPrivateKeyDirty, DOMAIN_SEPARATOR); - - // uint256 startBalanceFrom = token0.balanceOf(from); - // uint256 startBalanceTo = token0.balanceOf(address(this)); - // uint256 startBalanceFromDirty = token0.balanceOf(fromDirty); - - // // from and fromDirty approve address(this) as spender - // permit2.permit(from, permit, sig); - // permit2.permit(fromDirty, permitDirty, sigDirty); - - // (uint160 amount,,) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // (uint160 amount1,,) = permit2.allowance(fromDirty, token0(), address(this)); - // assertEq(amount1, defaultAmountOrId); - - // address[] memory owners = AddressBuilder.fill(1, from).push(fromDirty); - // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - // StructBuilder.fillAllowanceTransferDetail(2, token0(), 1 ** 18, address(this), owners); - // snapStart("transferFrom with different owners"); - // permit2.transferFrom(transferDetails); - // snapEnd(); - - // assertEq(token0.balanceOf(from), startBalanceFrom - 1 ** 18); - // assertEq(token0.balanceOf(fromDirty), startBalanceFromDirty - 1 ** 18); - // assertEq(token0.balanceOf(address(this)), startBalanceTo + 2 * 1 ** 18); - // (amount,,) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId - 1 ** 18); - // (amount,,) = permit2.allowance(fromDirty, token0(), address(this)); - // assertEq(amount, defaultAmountOrId - 1 ** 18); - // } - - // function testLockdown() public { - // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - // IAllowanceTransfer.PermitBatch memory permit = - // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // permit2.permit(from, permit, sig); - - // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // assertEq(expiration, defaultExpiration); - // assertEq(nonce, 1); - // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token1(), address(this)); - // assertEq(amount1, defaultAmountOrId); - // assertEq(expiration1, defaultExpiration); - // assertEq(nonce1, 1); - - // IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); - // approvals[0] = IAllowanceTransfer.TokenSpenderPair(token0(), address(this)); - // approvals[1] = IAllowanceTransfer.TokenSpenderPair(token1(), address(this)); - - // vm.prank(from); - // snapStart("lockdown"); - // permit2.lockdown(approvals); - // snapEnd(); - - // (amount, expiration, nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, 0); - // assertEq(expiration, defaultExpiration); - // assertEq(nonce, 1); - // (amount1, expiration1, nonce1) = permit2.allowance(from, token1(), address(this)); - // assertEq(amount1, 0); - // assertEq(expiration1, defaultExpiration); - // assertEq(nonce1, 1); - // } - - // function testLockdownEvent() public { - // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - // IAllowanceTransfer.PermitBatch memory permit = - // defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // permit2.permit(from, permit, sig); - - // (uint160 amount, uint48 expiration, uint48 nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, defaultAmountOrId); - // assertEq(expiration, defaultExpiration); - // assertEq(nonce, 1); - // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token1(), address(this)); - // assertEq(amount1, defaultAmountOrId); - // assertEq(expiration1, defaultExpiration); - // assertEq(nonce1, 1); - - // IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); - // approvals[0] = IAllowanceTransfer.TokenSpenderPair(token0(), address(this)); - // approvals[1] = IAllowanceTransfer.TokenSpenderPair(token1(), address(this)); - - // //TODO :fix expecting multiple events, can only check for 1 - // vm.prank(from); - // vm.expectEmit(true, false, false, false); - // emit Lockdown(from, token0(), address(this)); - // permit2.lockdown(approvals); - - // (amount, expiration, nonce) = permit2.allowance(from, token0(), address(this)); - // assertEq(amount, 0); - // assertEq(expiration, defaultExpiration); - // assertEq(nonce, 1); - // (amount1, expiration1, nonce1) = permit2.allowance(from, token1(), address(this)); - // assertEq(amount1, 0); - // assertEq(expiration1, defaultExpiration); - // assertEq(nonce1, 1); - // } + (uint160 startAllowedAmount0,,) = permit2Allowance(from, token0(), address(this)); + assertEq(startAllowedAmount0, type(uint160).max); + + permit2.transferFrom(from, address0, defaultAmountOrId, token0()); + + (uint160 endAllowedAmount0,,) = permit2Allowance(from, token0(), address(this)); + assertEq(endAllowedAmount0, type(uint160).max); + + assertEq(balanceOf(token0(), from), startBalanceFrom - defaultAmountOrId); + assertEq(balanceOf(token0(), address0), startBalanceTo + defaultAmountOrId); + } + + function testMaxAllowanceDirtyWrite() public { + uint160 maxAllowance = type(uint160).max; + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), maxAllowance, defaultExpiration, dirtyNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); + + uint256 startBalanceFrom = balanceOf(token0(), fromDirty); + uint256 startBalanceTo = balanceOf(token0(), address0); + + snapStart("permitSetMaxAllowanceDirtyWrite"); + permit2Permit(fromDirty, permit, sig); + snapEnd(); + + (uint160 startAllowedAmount0,,) = permit2Allowance(fromDirty, token0(), address(this)); + assertEq(startAllowedAmount0, type(uint160).max); + + permit2.transferFrom(fromDirty, address0, defaultAmountOrId, token0()); + + (uint160 endAllowedAmount0,,) = permit2Allowance(fromDirty, token0(), address(this)); + assertEq(endAllowedAmount0, type(uint160).max); + + assertEq(balanceOf(token0(), fromDirty), startBalanceFrom - defaultAmountOrId); + assertEq(balanceOf(token0(), address0), startBalanceTo + defaultAmountOrId); + } + + function testPartialAllowance() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + uint256 startBalanceFrom = balanceOf(token0(), from); + uint256 startBalanceTo = balanceOf(token0(), address0); + + permit2Permit(from, permit, sig); + + (uint160 startAllowedAmount0,,) = permit2Allowance(from, token0(), address(this)); + assertEq(startAllowedAmount0, defaultAmountOrId); + + uint160 transferAmount = 5 ** 18; + permit2.transferFrom(from, address0, transferAmount, token0()); + (uint160 endAllowedAmount0,,) = permit2Allowance(from, token0(), address(this)); + // ensure the allowance was deducted + assertEq(endAllowedAmount0, defaultAmountOrId - transferAmount); + + assertEq(balanceOf(token0(), from), startBalanceFrom - transferAmount); + assertEq(balanceOf(token0(), address0), startBalanceTo + transferAmount); + } + + function testReuseOrderedNonceInvalid() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + permit2Permit(from, permit, sig); + (,, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(nonce, 1); + + (uint160 amount, uint48 expiration,) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + assertEq(expiration, defaultExpiration); + + vm.expectRevert(InvalidNonce.selector); + permit2Permit(from, permit, sig); + } + + function testInvalidateNonces() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // Invalidates the 0th nonce by setting the new nonce to 1. + vm.prank(from); + vm.expectEmit(true, true, true, true); + emit NonceInvalidation(from, token0(), address(this), 1, defaultNonce); + permit2.invalidateNonces(token0(), address(this), 1); + (,, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(nonce, 1); + + vm.expectRevert(InvalidNonce.selector); + permit2Permit(from, permit, sig); + } + + function testInvalidateMultipleNonces() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // Valid permit, uses nonce 0. + permit2Permit(from, permit, sig); + (,, uint48 nonce1) = permit2Allowance(from, token0(), address(this)); + assertEq(nonce1, 1); + + permit = defaultERC20PermitAllowance(token1(), defaultAmountOrId, defaultExpiration, nonce1); + sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // Invalidates the 9 nonces by setting the new nonce to 33. + vm.prank(from); + vm.expectEmit(true, true, true, true); + + emit NonceInvalidation(from, token0(), address(this), 33, nonce1); + permit2.invalidateNonces(token0(), address(this), 33); + (,, uint48 nonce2) = permit2Allowance(from, token0(), address(this)); + assertEq(nonce2, 33); + + vm.expectRevert(InvalidNonce.selector); + permit2Permit(from, permit, sig); + } + + function testInvalidateNoncesInvalid() public { + // fromDirty nonce is 1 + vm.prank(fromDirty); + vm.expectRevert(InvalidNonce.selector); + // setting nonce to 0 should revert + permit2.invalidateNonces(token0(), address(this), 0); + } + + function testExcessiveInvalidation() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + uint32 numInvalidate = type(uint16).max; + vm.startPrank(from); + vm.expectRevert(IAllowanceTransfer.ExcessiveInvalidation.selector); + permit2.invalidateNonces(token0(), address(this), numInvalidate + 1); + vm.stopPrank(); + + permit2Permit(from, permit, sig); + (,, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(nonce, 1); + } + + function testBatchTransferFrom() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + uint256 startBalanceFrom = balanceOf(token0(), from); + uint256 startBalanceTo = balanceOf(token0(), address0); + + permit2Permit(from, permit, sig); + + (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + + // permit token0 for 1 ** 18 + address[] memory owners = AddressBuilder.fill(3, from); + IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + StructBuilder.fillAllowanceTransferDetail(3, token0(), 1 ** 18, address0, owners); + snapStart("batchTransferFrom"); + permit2.transferFrom(transferDetails); + snapEnd(); + assertEq(balanceOf(token0(), from), startBalanceFrom - 3 * 1 ** 18); + assertEq(balanceOf(token0(), address0), startBalanceTo + 3 * 1 ** 18); + (amount,,) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); + } + + function testBatchTransferFromMultiToken() public { + address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + IAllowanceTransfer.PermitBatch memory permitBatch = + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); + + uint256 startBalanceFrom0 = balanceOf(token0(), from); + uint256 startBalanceFrom1 = balanceOf(token1(), from); + uint256 startBalanceTo0 = balanceOf(token0(), address0); + uint256 startBalanceTo1 = balanceOf(token1(), address0); + + permit2Permit(from, permitBatch, sig); + + (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + (amount,,) = permit2Allowance(from, token1(), address(this)); + assertEq(amount, defaultAmountOrId); + + // permit token0 for 1 ** 18 + address[] memory owners = AddressBuilder.fill(2, from); + IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + StructBuilder.fillAllowanceTransferDetail(2, tokens, 1 ** 18, address0, owners); + snapStart("batchTransferFromMultiToken"); + permit2.transferFrom(transferDetails); + snapEnd(); + assertEq(balanceOf(token0(), from), startBalanceFrom0 - 1 ** 18); + assertEq(balanceOf(token1(), from), startBalanceFrom1 - 1 ** 18); + assertEq(balanceOf(token0(), address0), startBalanceTo0 + 1 ** 18); + assertEq(balanceOf(token1(), address0), startBalanceTo1 + 1 ** 18); + (amount,,) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId - 1 ** 18); + (amount,,) = permit2Allowance(from, token1(), address(this)); + assertEq(amount, defaultAmountOrId - 1 ** 18); + } + + function testBatchTransferFromDifferentOwners() public { + PermitSignature.IPermitSingle memory permit = + defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + IAllowanceTransfer.PermitSingle memory permitDirty = + defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); + bytes memory sigDirty = getPermitSignature(permitDirty, fromPrivateKeyDirty, DOMAIN_SEPARATOR); + + uint256 startBalanceFrom = balanceOf(token0(), from); + uint256 startBalanceTo = balanceOf(token0(), address(this)); + uint256 startBalanceFromDirty = balanceOf(token0(), fromDirty); + + // from and fromDirty approve address(this) as spender + permit2Permit(from, permit, sig); + permit2Permit(fromDirty, permitDirty, sigDirty); + + (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + (uint160 amount1,,) = permit2Allowance(fromDirty, token0(), address(this)); + assertEq(amount1, defaultAmountOrId); + + address[] memory owners = AddressBuilder.fill(1, from).push(fromDirty); + IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + StructBuilder.fillAllowanceTransferDetail(2, token0(), 1 ** 18, address(this), owners); + snapStart("transferFrom with different owners"); + permit2.transferFrom(transferDetails); + snapEnd(); + + assertEq(balanceOf(token0(), from), startBalanceFrom - 1 ** 18); + assertEq(balanceOf(token0(), fromDirty), startBalanceFromDirty - 1 ** 18); + assertEq(balanceOf(token0(), address(this)), startBalanceTo + 2 * 1 ** 18); + (amount,,) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId - 1 ** 18); + (amount,,) = permit2Allowance(fromDirty, token0(), address(this)); + assertEq(amount, defaultAmountOrId - 1 ** 18); + } + + function testLockdown() public { + address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + IAllowanceTransfer.PermitBatch memory permit = + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + permit2Permit(from, permit, sig); + + (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + assertEq(expiration, defaultExpiration); + assertEq(nonce, 1); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); + assertEq(amount1, defaultAmountOrId); + assertEq(expiration1, defaultExpiration); + assertEq(nonce1, 1); + + IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); + approvals[0] = IAllowanceTransfer.TokenSpenderPair(token0(), address(this)); + approvals[1] = IAllowanceTransfer.TokenSpenderPair(token1(), address(this)); + + vm.prank(from); + snapStart("lockdown"); + permit2.lockdown(approvals); + snapEnd(); + + (amount, expiration, nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, 0); + assertEq(expiration, defaultExpiration); + assertEq(nonce, 1); + (amount1, expiration1, nonce1) = permit2Allowance(from, token1(), address(this)); + assertEq(amount1, 0); + assertEq(expiration1, defaultExpiration); + assertEq(nonce1, 1); + } + + function testLockdownEvent() public { + address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + IAllowanceTransfer.PermitBatch memory permit = + defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + permit2Permit(from, permit, sig); + + (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, defaultAmountOrId); + assertEq(expiration, defaultExpiration); + assertEq(nonce, 1); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); + assertEq(amount1, defaultAmountOrId); + assertEq(expiration1, defaultExpiration); + assertEq(nonce1, 1); + + IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); + approvals[0] = IAllowanceTransfer.TokenSpenderPair(token0(), address(this)); + approvals[1] = IAllowanceTransfer.TokenSpenderPair(token1(), address(this)); + + //TODO :fix expecting multiple events, can only check for 1 + vm.prank(from); + vm.expectEmit(true, false, false, false); + emit Lockdown(from, token0(), address(this)); + permit2.lockdown(approvals); + + (amount, expiration, nonce) = permit2Allowance(from, token0(), address(this)); + assertEq(amount, 0); + assertEq(expiration, defaultExpiration); + assertEq(nonce, 1); + (amount1, expiration1, nonce1) = permit2Allowance(from, token1(), address(this)); + assertEq(amount1, 0); + assertEq(expiration1, defaultExpiration); + assertEq(nonce1, 1); + } } From d412d026229baf1c48389b4f063184c0b9b67a56 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Tue, 10 Jan 2023 17:50:49 -0700 Subject: [PATCH 13/27] some more refactor compiling --- .forge-snapshots/permitBatchCleanWrite.snap | 2 +- .forge-snapshots/permitBatchDirtyWrite.snap | 2 +- .forge-snapshots/permitCleanWrite.snap | 2 +- .forge-snapshots/permitCompactSig.snap | 2 +- .forge-snapshots/permitDirtyNonce.snap | 2 +- .forge-snapshots/permitDirtyWrite.snap | 2 +- .forge-snapshots/permitInvalidSigner.snap | 2 +- .../permitSetMaxAllowanceCleanWrite.snap | 2 +- .../permitSetMaxAllowanceDirtyWrite.snap | 2 +- .forge-snapshots/permitSignatureExpired.snap | 2 +- .forge-snapshots/transferFrom.snap | 2 +- test/AllowanceTransferTest_ERC20.t.sol | 71 ++- test/BaseAllowanceTransferTest.t.sol | 592 +++++++++--------- test/CompactSignature.t.sol | 4 +- test/utils/AmountBuilder.sol | 4 +- test/utils/PermitAbstraction.sol | 61 ++ test/utils/PermitSignature.sol | 37 +- 17 files changed, 450 insertions(+), 341 deletions(-) create mode 100644 test/utils/PermitAbstraction.sol diff --git a/.forge-snapshots/permitBatchCleanWrite.snap b/.forge-snapshots/permitBatchCleanWrite.snap index ff36c94f..99332dc5 100644 --- a/.forge-snapshots/permitBatchCleanWrite.snap +++ b/.forge-snapshots/permitBatchCleanWrite.snap @@ -1 +1 @@ -91924 \ No newline at end of file +94536 \ No newline at end of file diff --git a/.forge-snapshots/permitBatchDirtyWrite.snap b/.forge-snapshots/permitBatchDirtyWrite.snap index 2020125e..082cd07a 100644 --- a/.forge-snapshots/permitBatchDirtyWrite.snap +++ b/.forge-snapshots/permitBatchDirtyWrite.snap @@ -1 +1 @@ -57724 \ No newline at end of file +60336 \ No newline at end of file diff --git a/.forge-snapshots/permitCleanWrite.snap b/.forge-snapshots/permitCleanWrite.snap index 5c565761..d439ef4e 100644 --- a/.forge-snapshots/permitCleanWrite.snap +++ b/.forge-snapshots/permitCleanWrite.snap @@ -1 +1 @@ -63362 \ No newline at end of file +63449 \ No newline at end of file diff --git a/.forge-snapshots/permitCompactSig.snap b/.forge-snapshots/permitCompactSig.snap index 18ba881f..c02900fb 100644 --- a/.forge-snapshots/permitCompactSig.snap +++ b/.forge-snapshots/permitCompactSig.snap @@ -1 +1 @@ -63360 \ No newline at end of file +63423 \ No newline at end of file diff --git a/.forge-snapshots/permitDirtyNonce.snap b/.forge-snapshots/permitDirtyNonce.snap index a909d364..8c2da0ca 100644 --- a/.forge-snapshots/permitDirtyNonce.snap +++ b/.forge-snapshots/permitDirtyNonce.snap @@ -1 +1 @@ -44014 \ No newline at end of file +44348 \ No newline at end of file diff --git a/.forge-snapshots/permitDirtyWrite.snap b/.forge-snapshots/permitDirtyWrite.snap index 451c9a87..7581d9ee 100644 --- a/.forge-snapshots/permitDirtyWrite.snap +++ b/.forge-snapshots/permitDirtyWrite.snap @@ -1 +1 @@ -46019 \ No newline at end of file +46349 \ No newline at end of file diff --git a/.forge-snapshots/permitInvalidSigner.snap b/.forge-snapshots/permitInvalidSigner.snap index 73ab357b..1aae61b3 100644 --- a/.forge-snapshots/permitInvalidSigner.snap +++ b/.forge-snapshots/permitInvalidSigner.snap @@ -1 +1 @@ -40301 \ No newline at end of file +40647 \ No newline at end of file diff --git a/.forge-snapshots/permitSetMaxAllowanceCleanWrite.snap b/.forge-snapshots/permitSetMaxAllowanceCleanWrite.snap index 6e633733..4d339286 100644 --- a/.forge-snapshots/permitSetMaxAllowanceCleanWrite.snap +++ b/.forge-snapshots/permitSetMaxAllowanceCleanWrite.snap @@ -1 +1 @@ -61114 \ No newline at end of file +61448 \ No newline at end of file diff --git a/.forge-snapshots/permitSetMaxAllowanceDirtyWrite.snap b/.forge-snapshots/permitSetMaxAllowanceDirtyWrite.snap index a909d364..8c2da0ca 100644 --- a/.forge-snapshots/permitSetMaxAllowanceDirtyWrite.snap +++ b/.forge-snapshots/permitSetMaxAllowanceDirtyWrite.snap @@ -1 +1 @@ -44014 \ No newline at end of file +44348 \ No newline at end of file diff --git a/.forge-snapshots/permitSignatureExpired.snap b/.forge-snapshots/permitSignatureExpired.snap index 309c6d2b..6b16f5d7 100644 --- a/.forge-snapshots/permitSignatureExpired.snap +++ b/.forge-snapshots/permitSignatureExpired.snap @@ -1 +1 @@ -31700 \ No newline at end of file +32039 \ No newline at end of file diff --git a/.forge-snapshots/transferFrom.snap b/.forge-snapshots/transferFrom.snap index ebbbd605..5f17e434 100644 --- a/.forge-snapshots/transferFrom.snap +++ b/.forge-snapshots/transferFrom.snap @@ -1 +1 @@ -52197 \ No newline at end of file +52178 \ No newline at end of file diff --git a/test/AllowanceTransferTest_ERC20.t.sol b/test/AllowanceTransferTest_ERC20.t.sol index bbe2bad5..05edeebd 100644 --- a/test/AllowanceTransferTest_ERC20.t.sol +++ b/test/AllowanceTransferTest_ERC20.t.sol @@ -6,6 +6,7 @@ import {BaseAllowanceTransferTest} from "./BaseAllowanceTransferTest.t.sol"; import {Permit2} from "../src/ERC20/Permit2.sol"; import {TokenProvider_ERC20} from "./utils/TokenProvider_ERC20.sol"; import {PermitHash} from "../src/ERC20/libraries/PermitHash.sol"; +import {PermitAbstraction} from "./utils/PermitAbstraction.sol"; import {PermitSignature} from "./utils/PermitSignature.sol"; import {IAllowanceTransfer} from "../src/ERC20/interfaces/IAllowanceTransfer.sol"; import {MockERC20} from "./mocks/MockERC20.sol"; @@ -55,7 +56,7 @@ contract AllowanceTransferTest_ERC20 is TokenProvider_ERC20, BaseAllowanceTransf return Permit2(permit2).allowance(from, token, spender); } - function permit2Permit(address from, PermitSignature.IPermitSingle memory permit, bytes memory sig) + function permit2Permit(address from, PermitAbstraction.IPermitSingle memory permit, bytes memory sig) public override { @@ -74,6 +75,35 @@ contract AllowanceTransferTest_ERC20 is TokenProvider_ERC20, BaseAllowanceTransf Permit2(permit2).permit(from, parsedPermit, sig); } + function permit2Permit(address from, PermitAbstraction.IPermitBatch memory permitBatch, bytes memory sig) + public + override + { + // convert IPermitBatch to IAllowanceTransfer.PermitBatch + + IAllowanceTransfer.PermitDetails[] memory details = + new IAllowanceTransfer.PermitDetails[](permitBatch.tokens.length); + for (uint256 i = 0; i < details.length; i++) { + details[i] = IAllowanceTransfer.PermitDetails({ + token: permitBatch.tokens[i], + amount: permitBatch.amountOrIds[i], + expiration: permitBatch.expirations[i], + nonce: permitBatch.nonces[i] + }); + } + IAllowanceTransfer.PermitBatch memory parsedPermitBatch = IAllowanceTransfer.PermitBatch({ + details: details, + spender: permitBatch.spender, + sigDeadline: permitBatch.sigDeadline + }); + + Permit2(permit2).permit(from, parsedPermitBatch, sig); + } + + function permit2TransferFrom(address from, address to, uint160 amountOrId, address token) public override { + Permit2(permit2).transferFrom(from, to, amountOrId, token); + } + function token0() public view override returns (address) { return address(_token0); } @@ -102,7 +132,7 @@ contract AllowanceTransferTest_ERC20 is TokenProvider_ERC20, BaseAllowanceTransf { (uint8 v, bytes32 r, bytes32 s) = getPermitSignatureRaw(permit, privateKey, domainSeparator); bytes32 vs; - (r, vs) = _getCompactSignature(v, r, s); + (r, vs) = getCompactSignature(v, r, s); return bytes.concat(r, vs); } @@ -129,4 +159,41 @@ contract AllowanceTransferTest_ERC20 is TokenProvider_ERC20, BaseAllowanceTransf (v, r, s) = vm.sign(privateKey, msgHash); } + + function getPermitBatchSignature(IPermitBatch memory permit, uint256 privateKey, bytes32 domainSeparator) + public + override + returns (bytes memory) + { + bytes32[] memory permitHashes = new bytes32[](permit.tokens.length); + for (uint256 i = 0; i < permit.tokens.length; ++i) { + permitHashes[i] = keccak256( + abi.encode( + PermitHash._PERMIT_DETAILS_TYPEHASH, + permit.tokens[i], + permit.amountOrIds[i], + permit.expirations[i], + permit.nonces[i] + ) + ); + } + + bytes32 msgHash = keccak256( + abi.encodePacked( + "\x19\x01", + domainSeparator, + keccak256( + abi.encode( + PermitHash._PERMIT_BATCH_TYPEHASH, + keccak256(abi.encodePacked(permitHashes)), + permit.spender, + permit.sigDeadline + ) + ) + ) + ); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, msgHash); + return bytes.concat(r, s, bytes1(v)); + } } diff --git a/test/BaseAllowanceTransferTest.t.sol b/test/BaseAllowanceTransferTest.t.sol index 6954ff76..4fe96933 100644 --- a/test/BaseAllowanceTransferTest.t.sol +++ b/test/BaseAllowanceTransferTest.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {Permit2} from "../src/ERC20/Permit2.sol"; +import {PermitAbstraction} from "./utils/PermitAbstraction.sol"; import {PermitSignature} from "./utils/PermitSignature.sol"; import {SignatureVerification} from "../src/shared/SignatureVerification.sol"; import {AddressBuilder} from "./utils/AddressBuilder.sol"; @@ -13,7 +14,7 @@ import {SignatureExpired, InvalidNonce} from "../src/shared/PermitErrors.sol"; import {IAllowanceTransfer} from "../src/ERC20/interfaces/IAllowanceTransfer.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; -abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapshot { +abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbstraction, GasSnapshot { using AddressBuilder for address[]; using stdStorage for StdStorage; @@ -71,6 +72,11 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho virtual returns (bytes memory sig); + function getPermitBatchSignature(IPermitBatch memory permit, uint256 privKey, bytes32 domainSeparator) + public + virtual + returns (bytes memory); + function permit2Approve(address token, address spender, uint160 amountOrId, uint48 expiration) public virtual; function permit2Allowance(address from, address token, address spender) @@ -78,10 +84,16 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho virtual returns (uint160, uint48, uint48); - function permit2Permit(address from, PermitSignature.IPermitSingle memory permit, bytes memory sig) + function permit2Permit(address from, PermitAbstraction.IPermitSingle memory permit, bytes memory sig) + public + virtual; + + function permit2Permit(address from, PermitAbstraction.IPermitBatch memory permit, bytes memory sig) public virtual; + function permit2TransferFrom(address from, address to, uint160 amount, address token) public virtual; + function testApprove() public { vm.prank(from); vm.expectEmit(true, true, true, true); @@ -95,7 +107,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho } function testSetAllowance() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); @@ -110,7 +122,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho } function testSetAllowanceCompactSig() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getCompactPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); assertEq(sig.length, 64); @@ -126,7 +138,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho } function testSetAllowanceIncorrectSigLength() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); bytes memory sigExtra = bytes.concat(sig, bytes1(uint8(1))); @@ -137,7 +149,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho } function testSetAllowanceDirtyWrite() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); @@ -152,7 +164,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho } function testSetAllowanceBatchDifferentNonces() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); @@ -164,10 +176,10 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho assertEq(nonce, 1); address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - IAllowanceTransfer.PermitBatch memory permitBatch = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, 1); + PermitAbstraction.IPermitBatch memory permitBatch = + defaultPermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, 1); // first token nonce is 1, second token nonce is 0 - permitBatch.details[1].nonce = 0; + permitBatch.nonces[1] = 0; bytes memory sig1 = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); permit2Permit(from, permitBatch, sig1); @@ -184,8 +196,8 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho function testSetAllowanceBatch() public { address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + PermitAbstraction.IPermitBatch memory permit = + defaultPermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); snapStart("permitBatchCleanWrite"); @@ -206,8 +218,8 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); uint160[] memory amounts = AmountBuilder.fillUInt160(2, defaultAmountOrId); - IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + PermitAbstraction.IPermitBatch memory permit = + defaultPermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); vm.expectEmit(true, true, true, true); @@ -228,8 +240,8 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho function testSetAllowanceBatchDirtyWrite() public { address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, dirtyNonce); + PermitAbstraction.IPermitBatch memory permit = + defaultPermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, dirtyNonce); bytes memory sig = getPermitBatchSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); snapStart("permitBatchDirtyWrite"); @@ -248,7 +260,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho // test setting allowance with ordered nonce and transfer function testSetAllowanceTransfer() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); @@ -261,14 +273,14 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho assertEq(amount, defaultAmountOrId); - permit2.transferFrom(from, address0, defaultAmountOrId, token0()); + permit2TransferFrom(from, address0, defaultAmountOrId, token0()); assertEq(balanceOf(token0(), from), startBalanceFrom - defaultAmountOrId); assertEq(balanceOf(token0(), address0), startBalanceTo + defaultAmountOrId); } function testTransferFromWithGasSnapshot() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); @@ -282,42 +294,42 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho assertEq(amount, defaultAmountOrId); snapStart("transferFrom"); - permit2.transferFrom(from, address0, defaultAmountOrId, token0()); + permit2TransferFrom(from, address0, defaultAmountOrId, token0()); snapEnd(); assertEq(balanceOf(token0(), from), startBalanceFrom - defaultAmountOrId); assertEq(balanceOf(token0(), address0), startBalanceTo + defaultAmountOrId); } - function testBatchTransferFromWithGasSnapshot() public { - PermitSignature.IPermitSingle memory permit = - defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + // function testBatchTransferFromWithGasSnapshot() public { + // PermitAbstraction.IPermitSingle memory permit = + // defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - uint256 startBalanceFrom = balanceOf(token0(), from); - uint256 startBalanceTo = balanceOf(token0(), address0); + // uint256 startBalanceFrom = balanceOf(token0(), from); + // uint256 startBalanceTo = balanceOf(token0(), address0); - permit2Permit(from, permit, sig); + // permit2Permit(from, permit, sig); - (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, defaultAmountOrId); + // (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); - // permit token0 for 1 ** 18 - address[] memory owners = AddressBuilder.fill(3, from); - IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - StructBuilder.fillAllowanceTransferDetail(3, token0(), 1 ** 18, address0, owners); - snapStart("batchTransferFrom"); - permit2.transferFrom(transferDetails); - snapEnd(); - assertEq(balanceOf(token0(), from), startBalanceFrom - 3 * 1 ** 18); - assertEq(balanceOf(token0(), address0), startBalanceTo + 3 * 1 ** 18); - (amount,,) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); - } + // // permit token0 for 1 ** 18 + // address[] memory owners = AddressBuilder.fill(3, from); + // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + // StructBuilder.fillAllowanceTransferDetail(3, token0(), 1 ** 18, address0, owners); + // snapStart("batchTransferFrom"); + // permit2.transferFrom(transferDetails); + // snapEnd(); + // assertEq(balanceOf(token0(), from), startBalanceFrom - 3 * 1 ** 18); + // assertEq(balanceOf(token0(), address0), startBalanceTo + 3 * 1 ** 18); + // (amount,,) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); + // } // dirty sstore on nonce, dirty sstore on transfer function testSetAllowanceTransferDirtyNonceDirtyTransfer() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); @@ -333,14 +345,14 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho (uint160 amount,,) = permit2Allowance(fromDirty, token0(), address(this)); assertEq(amount, defaultAmountOrId); - permit2.transferFrom(fromDirty, address3, defaultAmountOrId, token0()); + permit2TransferFrom(fromDirty, address3, defaultAmountOrId, token0()); assertEq(balanceOf(token0(), fromDirty), startBalanceFrom - defaultAmountOrId); assertEq(balanceOf(token0(), address3), startBalanceTo + defaultAmountOrId); } function testSetAllowanceInvalidSignature() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); snapStart("permitInvalidSigner"); @@ -351,7 +363,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho } function testSetAllowanceDeadlinePassed() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); @@ -366,7 +378,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho function testMaxAllowance() public { uint160 maxAllowance = type(uint160).max; - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), maxAllowance, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); @@ -380,7 +392,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho (uint160 startAllowedAmount0,,) = permit2Allowance(from, token0(), address(this)); assertEq(startAllowedAmount0, type(uint160).max); - permit2.transferFrom(from, address0, defaultAmountOrId, token0()); + permit2TransferFrom(from, address0, defaultAmountOrId, token0()); (uint160 endAllowedAmount0,,) = permit2Allowance(from, token0(), address(this)); assertEq(endAllowedAmount0, type(uint160).max); @@ -391,7 +403,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho function testMaxAllowanceDirtyWrite() public { uint160 maxAllowance = type(uint160).max; - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), maxAllowance, defaultExpiration, dirtyNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKeyDirty, DOMAIN_SEPARATOR); @@ -405,7 +417,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho (uint160 startAllowedAmount0,,) = permit2Allowance(fromDirty, token0(), address(this)); assertEq(startAllowedAmount0, type(uint160).max); - permit2.transferFrom(fromDirty, address0, defaultAmountOrId, token0()); + permit2TransferFrom(fromDirty, address0, defaultAmountOrId, token0()); (uint160 endAllowedAmount0,,) = permit2Allowance(fromDirty, token0(), address(this)); assertEq(endAllowedAmount0, type(uint160).max); @@ -415,7 +427,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho } function testPartialAllowance() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); @@ -428,7 +440,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho assertEq(startAllowedAmount0, defaultAmountOrId); uint160 transferAmount = 5 ** 18; - permit2.transferFrom(from, address0, transferAmount, token0()); + permit2TransferFrom(from, address0, transferAmount, token0()); (uint160 endAllowedAmount0,,) = permit2Allowance(from, token0(), address(this)); // ensure the allowance was deducted assertEq(endAllowedAmount0, defaultAmountOrId - transferAmount); @@ -438,7 +450,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho } function testReuseOrderedNonceInvalid() public { - PermitSignature.IPermitSingle memory permit = + PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); @@ -454,242 +466,242 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, GasSnapsho permit2Permit(from, permit, sig); } - function testInvalidateNonces() public { - PermitSignature.IPermitSingle memory permit = - defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // Invalidates the 0th nonce by setting the new nonce to 1. - vm.prank(from); - vm.expectEmit(true, true, true, true); - emit NonceInvalidation(from, token0(), address(this), 1, defaultNonce); - permit2.invalidateNonces(token0(), address(this), 1); - (,, uint48 nonce) = permit2Allowance(from, token0(), address(this)); - assertEq(nonce, 1); - - vm.expectRevert(InvalidNonce.selector); - permit2Permit(from, permit, sig); - } - - function testInvalidateMultipleNonces() public { - PermitSignature.IPermitSingle memory permit = - defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // Valid permit, uses nonce 0. - permit2Permit(from, permit, sig); - (,, uint48 nonce1) = permit2Allowance(from, token0(), address(this)); - assertEq(nonce1, 1); - - permit = defaultERC20PermitAllowance(token1(), defaultAmountOrId, defaultExpiration, nonce1); - sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - // Invalidates the 9 nonces by setting the new nonce to 33. - vm.prank(from); - vm.expectEmit(true, true, true, true); - - emit NonceInvalidation(from, token0(), address(this), 33, nonce1); - permit2.invalidateNonces(token0(), address(this), 33); - (,, uint48 nonce2) = permit2Allowance(from, token0(), address(this)); - assertEq(nonce2, 33); - - vm.expectRevert(InvalidNonce.selector); - permit2Permit(from, permit, sig); - } - - function testInvalidateNoncesInvalid() public { - // fromDirty nonce is 1 - vm.prank(fromDirty); - vm.expectRevert(InvalidNonce.selector); - // setting nonce to 0 should revert - permit2.invalidateNonces(token0(), address(this), 0); - } - - function testExcessiveInvalidation() public { - PermitSignature.IPermitSingle memory permit = - defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - uint32 numInvalidate = type(uint16).max; - vm.startPrank(from); - vm.expectRevert(IAllowanceTransfer.ExcessiveInvalidation.selector); - permit2.invalidateNonces(token0(), address(this), numInvalidate + 1); - vm.stopPrank(); - - permit2Permit(from, permit, sig); - (,, uint48 nonce) = permit2Allowance(from, token0(), address(this)); - assertEq(nonce, 1); - } - - function testBatchTransferFrom() public { - PermitSignature.IPermitSingle memory permit = - defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom = balanceOf(token0(), from); - uint256 startBalanceTo = balanceOf(token0(), address0); - - permit2Permit(from, permit, sig); - - (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, defaultAmountOrId); - - // permit token0 for 1 ** 18 - address[] memory owners = AddressBuilder.fill(3, from); - IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - StructBuilder.fillAllowanceTransferDetail(3, token0(), 1 ** 18, address0, owners); - snapStart("batchTransferFrom"); - permit2.transferFrom(transferDetails); - snapEnd(); - assertEq(balanceOf(token0(), from), startBalanceFrom - 3 * 1 ** 18); - assertEq(balanceOf(token0(), address0), startBalanceTo + 3 * 1 ** 18); - (amount,,) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); - } - - function testBatchTransferFromMultiToken() public { - address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - IAllowanceTransfer.PermitBatch memory permitBatch = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom0 = balanceOf(token0(), from); - uint256 startBalanceFrom1 = balanceOf(token1(), from); - uint256 startBalanceTo0 = balanceOf(token0(), address0); - uint256 startBalanceTo1 = balanceOf(token1(), address0); - - permit2Permit(from, permitBatch, sig); - - (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, defaultAmountOrId); - (amount,,) = permit2Allowance(from, token1(), address(this)); - assertEq(amount, defaultAmountOrId); - - // permit token0 for 1 ** 18 - address[] memory owners = AddressBuilder.fill(2, from); - IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - StructBuilder.fillAllowanceTransferDetail(2, tokens, 1 ** 18, address0, owners); - snapStart("batchTransferFromMultiToken"); - permit2.transferFrom(transferDetails); - snapEnd(); - assertEq(balanceOf(token0(), from), startBalanceFrom0 - 1 ** 18); - assertEq(balanceOf(token1(), from), startBalanceFrom1 - 1 ** 18); - assertEq(balanceOf(token0(), address0), startBalanceTo0 + 1 ** 18); - assertEq(balanceOf(token1(), address0), startBalanceTo1 + 1 ** 18); - (amount,,) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, defaultAmountOrId - 1 ** 18); - (amount,,) = permit2Allowance(from, token1(), address(this)); - assertEq(amount, defaultAmountOrId - 1 ** 18); - } - - function testBatchTransferFromDifferentOwners() public { - PermitSignature.IPermitSingle memory permit = - defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - IAllowanceTransfer.PermitSingle memory permitDirty = - defaultERC20PermitAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); - bytes memory sigDirty = getPermitSignature(permitDirty, fromPrivateKeyDirty, DOMAIN_SEPARATOR); - - uint256 startBalanceFrom = balanceOf(token0(), from); - uint256 startBalanceTo = balanceOf(token0(), address(this)); - uint256 startBalanceFromDirty = balanceOf(token0(), fromDirty); - - // from and fromDirty approve address(this) as spender - permit2Permit(from, permit, sig); - permit2Permit(fromDirty, permitDirty, sigDirty); - - (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, defaultAmountOrId); - (uint160 amount1,,) = permit2Allowance(fromDirty, token0(), address(this)); - assertEq(amount1, defaultAmountOrId); - - address[] memory owners = AddressBuilder.fill(1, from).push(fromDirty); - IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = - StructBuilder.fillAllowanceTransferDetail(2, token0(), 1 ** 18, address(this), owners); - snapStart("transferFrom with different owners"); - permit2.transferFrom(transferDetails); - snapEnd(); - - assertEq(balanceOf(token0(), from), startBalanceFrom - 1 ** 18); - assertEq(balanceOf(token0(), fromDirty), startBalanceFromDirty - 1 ** 18); - assertEq(balanceOf(token0(), address(this)), startBalanceTo + 2 * 1 ** 18); - (amount,,) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, defaultAmountOrId - 1 ** 18); - (amount,,) = permit2Allowance(fromDirty, token0(), address(this)); - assertEq(amount, defaultAmountOrId - 1 ** 18); - } - - function testLockdown() public { - address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - permit2Permit(from, permit, sig); - - (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); - assertEq(amount1, defaultAmountOrId); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 1); - - IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); - approvals[0] = IAllowanceTransfer.TokenSpenderPair(token0(), address(this)); - approvals[1] = IAllowanceTransfer.TokenSpenderPair(token1(), address(this)); - - vm.prank(from); - snapStart("lockdown"); - permit2.lockdown(approvals); - snapEnd(); - - (amount, expiration, nonce) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, 0); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - (amount1, expiration1, nonce1) = permit2Allowance(from, token1(), address(this)); - assertEq(amount1, 0); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 1); - } - - function testLockdownEvent() public { - address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); - IAllowanceTransfer.PermitBatch memory permit = - defaultERC20PermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); - bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); - - permit2Permit(from, permit, sig); - - (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, defaultAmountOrId); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); - assertEq(amount1, defaultAmountOrId); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 1); - - IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); - approvals[0] = IAllowanceTransfer.TokenSpenderPair(token0(), address(this)); - approvals[1] = IAllowanceTransfer.TokenSpenderPair(token1(), address(this)); - - //TODO :fix expecting multiple events, can only check for 1 - vm.prank(from); - vm.expectEmit(true, false, false, false); - emit Lockdown(from, token0(), address(this)); - permit2.lockdown(approvals); - - (amount, expiration, nonce) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, 0); - assertEq(expiration, defaultExpiration); - assertEq(nonce, 1); - (amount1, expiration1, nonce1) = permit2Allowance(from, token1(), address(this)); - assertEq(amount1, 0); - assertEq(expiration1, defaultExpiration); - assertEq(nonce1, 1); - } + // function testInvalidateNonces() public { + // PermitAbstraction.IPermitSingle memory permit = + // defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // // Invalidates the 0th nonce by setting the new nonce to 1. + // vm.prank(from); + // vm.expectEmit(true, true, true, true); + // emit NonceInvalidation(from, token0(), address(this), 1, defaultNonce); + // permit2.invalidateNonces(token0(), address(this), 1); + // (,, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + // assertEq(nonce, 1); + + // vm.expectRevert(InvalidNonce.selector); + // permit2Permit(from, permit, sig); + // } + + // function testInvalidateMultipleNonces() public { + // PermitAbstraction.IPermitSingle memory permit = + // defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // // Valid permit, uses nonce 0. + // permit2Permit(from, permit, sig); + // (,, uint48 nonce1) = permit2Allowance(from, token0(), address(this)); + // assertEq(nonce1, 1); + + // permit = defaultPermitAllowance(token1(), defaultAmountOrId, defaultExpiration, nonce1); + // sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // // Invalidates the 9 nonces by setting the new nonce to 33. + // vm.prank(from); + // vm.expectEmit(true, true, true, true); + + // emit NonceInvalidation(from, token0(), address(this), 33, nonce1); + // permit2.invalidateNonces(token0(), address(this), 33); + // (,, uint48 nonce2) = permit2Allowance(from, token0(), address(this)); + // assertEq(nonce2, 33); + + // vm.expectRevert(InvalidNonce.selector); + // permit2Permit(from, permit, sig); + // } + + // function testInvalidateNoncesInvalid() public { + // // fromDirty nonce is 1 + // vm.prank(fromDirty); + // vm.expectRevert(InvalidNonce.selector); + // // setting nonce to 0 should revert + // permit2.invalidateNonces(token0(), address(this), 0); + // } + + // function testExcessiveInvalidation() public { + // PermitAbstraction.IPermitSingle memory permit = + // defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // uint32 numInvalidate = type(uint16).max; + // vm.startPrank(from); + // vm.expectRevert(IAllowanceTransfer.ExcessiveInvalidation.selector); + // permit2.invalidateNonces(token0(), address(this), numInvalidate + 1); + // vm.stopPrank(); + + // permit2Permit(from, permit, sig); + // (,, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + // assertEq(nonce, 1); + // } + + // function testBatchTransferFrom() public { + // PermitAbstraction.IPermitSingle memory permit = + // defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom = balanceOf(token0(), from); + // uint256 startBalanceTo = balanceOf(token0(), address0); + + // permit2Permit(from, permit, sig); + + // (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + + // // permit token0 for 1 ** 18 + // address[] memory owners = AddressBuilder.fill(3, from); + // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + // StructBuilder.fillAllowanceTransferDetail(3, token0(), 1 ** 18, address0, owners); + // snapStart("batchTransferFrom"); + // permit2.transferFrom(transferDetails); + // snapEnd(); + // assertEq(balanceOf(token0(), from), startBalanceFrom - 3 * 1 ** 18); + // assertEq(balanceOf(token0(), address0), startBalanceTo + 3 * 1 ** 18); + // (amount,,) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId - 3 * 1 ** 18); + // } + + // function testBatchTransferFromMultiToken() public { + // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + // PermitAbstraction.IPermitBatch memory permitBatch = + // defaultPermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitBatchSignature(permitBatch, fromPrivateKey, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom0 = balanceOf(token0(), from); + // uint256 startBalanceFrom1 = balanceOf(token1(), from); + // uint256 startBalanceTo0 = balanceOf(token0(), address0); + // uint256 startBalanceTo1 = balanceOf(token1(), address0); + + // permit2Permit(from, permitBatch, sig); + + // (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // (amount,,) = permit2Allowance(from, token1(), address(this)); + // assertEq(amount, defaultAmountOrId); + + // // permit token0 for 1 ** 18 + // address[] memory owners = AddressBuilder.fill(2, from); + // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + // StructBuilder.fillAllowanceTransferDetail(2, tokens, 1 ** 18, address0, owners); + // snapStart("batchTransferFromMultiToken"); + // permit2.transferFrom(transferDetails); + // snapEnd(); + // assertEq(balanceOf(token0(), from), startBalanceFrom0 - 1 ** 18); + // assertEq(balanceOf(token1(), from), startBalanceFrom1 - 1 ** 18); + // assertEq(balanceOf(token0(), address0), startBalanceTo0 + 1 ** 18); + // assertEq(balanceOf(token1(), address0), startBalanceTo1 + 1 ** 18); + // (amount,,) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId - 1 ** 18); + // (amount,,) = permit2Allowance(from, token1(), address(this)); + // assertEq(amount, defaultAmountOrId - 1 ** 18); + // } + + // function testBatchTransferFromDifferentOwners() public { + // PermitAbstraction.IPermitSingle memory permit = + // defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // PermitAbstraction.IPermitBatch memory permitDirty = + // defaultPermitBatchAllowance(token0(), defaultAmountOrId, defaultExpiration, dirtyNonce); + // bytes memory sigDirty = getPermitSignature(permitDirty, fromPrivateKeyDirty, DOMAIN_SEPARATOR); + + // uint256 startBalanceFrom = balanceOf(token0(), from); + // uint256 startBalanceTo = balanceOf(token0(), address(this)); + // uint256 startBalanceFromDirty = balanceOf(token0(), fromDirty); + + // // from and fromDirty approve address(this) as spender + // permit2Permit(from, permit, sig); + // permit2Permit(fromDirty, permitDirty, sigDirty); + + // (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // (uint160 amount1,,) = permit2Allowance(fromDirty, token0(), address(this)); + // assertEq(amount1, defaultAmountOrId); + + // address[] memory owners = AddressBuilder.fill(1, from).push(fromDirty); + // IAllowanceTransfer.AllowanceTransferDetails[] memory transferDetails = + // StructBuilder.fillAllowanceTransferDetail(2, token0(), 1 ** 18, address(this), owners); + // snapStart("transferFrom with different owners"); + // permit2.transferFrom(transferDetails); + // snapEnd(); + + // assertEq(balanceOf(token0(), from), startBalanceFrom - 1 ** 18); + // assertEq(balanceOf(token0(), fromDirty), startBalanceFromDirty - 1 ** 18); + // assertEq(balanceOf(token0(), address(this)), startBalanceTo + 2 * 1 ** 18); + // (amount,,) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId - 1 ** 18); + // (amount,,) = permit2Allowance(fromDirty, token0(), address(this)); + // assertEq(amount, defaultAmountOrId - 1 ** 18); + // } + + // function testLockdown() public { + // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + // PermitAbstraction.IPermitBatch memory permit = + // defaultPermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // permit2Permit(from, permit, sig); + + // (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); + // assertEq(amount1, defaultAmountOrId); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 1); + + // IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); + // approvals[0] = IAllowanceTransfer.TokenSpenderPair(token0(), address(this)); + // approvals[1] = IAllowanceTransfer.TokenSpenderPair(token1(), address(this)); + + // vm.prank(from); + // snapStart("lockdown"); + // permit2.lockdown(approvals); + // snapEnd(); + + // (amount, expiration, nonce) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, 0); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + // (amount1, expiration1, nonce1) = permit2Allowance(from, token1(), address(this)); + // assertEq(amount1, 0); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 1); + // } + + // function testLockdownEvent() public { + // address[] memory tokens = AddressBuilder.fill(1, token0()).push(token1()); + // PermitAbstraction.IPermitBatch memory permit = + // defaultPermitBatchAllowance(tokens, defaultAmountOrId, defaultExpiration, defaultNonce); + // bytes memory sig = getPermitBatchSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + // permit2Permit(from, permit, sig); + + // (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, defaultAmountOrId); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + // (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); + // assertEq(amount1, defaultAmountOrId); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 1); + + // IAllowanceTransfer.TokenSpenderPair[] memory approvals = new IAllowanceTransfer.TokenSpenderPair[](2); + // approvals[0] = IAllowanceTransfer.TokenSpenderPair(token0(), address(this)); + // approvals[1] = IAllowanceTransfer.TokenSpenderPair(token1(), address(this)); + + // //TODO :fix expecting multiple events, can only check for 1 + // vm.prank(from); + // vm.expectEmit(true, false, false, false); + // emit Lockdown(from, token0(), address(this)); + // permit2.lockdown(approvals); + + // (amount, expiration, nonce) = permit2Allowance(from, token0(), address(this)); + // assertEq(amount, 0); + // assertEq(expiration, defaultExpiration); + // assertEq(nonce, 1); + // (amount1, expiration1, nonce1) = permit2Allowance(from, token1(), address(this)); + // assertEq(amount1, 0); + // assertEq(expiration1, defaultExpiration); + // assertEq(nonce1, 1); + // } } diff --git a/test/CompactSignature.t.sol b/test/CompactSignature.t.sol index 0f6cbc91..8f04bd30 100644 --- a/test/CompactSignature.t.sol +++ b/test/CompactSignature.t.sol @@ -12,7 +12,7 @@ contract CompactSignature is PermitSignature { uint8 v = 27; bytes32 vs; - (r, vs) = _getCompactSignature(v, r, s); + (r, vs) = getCompactSignature(v, r, s); assertEq(r, 0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b90); assertEq(vs, 0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064); @@ -24,7 +24,7 @@ contract CompactSignature is PermitSignature { uint8 v = 28; bytes32 vs; - (r, vs) = _getCompactSignature(v, r, s); + (r, vs) = getCompactSignature(v, r, s); assertEq(r, 0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76); assertEq(vs, 0x939c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793); diff --git a/test/utils/AmountBuilder.sol b/test/utils/AmountBuilder.sol index efcb832b..8b763b7e 100644 --- a/test/utils/AmountBuilder.sol +++ b/test/utils/AmountBuilder.sol @@ -23,8 +23,8 @@ library AmountBuilder { } } - function fillUInt64(uint256 length, uint64 exp) external pure returns (uint64[] memory exps) { - exps = new uint64[](length); + function fillUInt48(uint256 length, uint48 exp) external pure returns (uint48[] memory exps) { + exps = new uint48[](length); for (uint256 i = 0; i < length; ++i) { exps[i] = exp; } diff --git a/test/utils/PermitAbstraction.sol b/test/utils/PermitAbstraction.sol new file mode 100644 index 00000000..b437e361 --- /dev/null +++ b/test/utils/PermitAbstraction.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {IAllowanceTransfer} from "../../src/ERC20/interfaces/IAllowanceTransfer.sol"; +import {AmountBuilder} from "./AmountBuilder.sol"; + +// Structs are used for type abstraction in the tests and later converted to the respective permit2 structs +contract PermitAbstraction { + struct IPermitSingle { + address token; + uint160 amountOrId; + uint48 expiration; + uint48 nonce; + address spender; + uint256 sigDeadline; + } + + struct IPermitBatch { + address[] tokens; + uint160[] amountOrIds; + uint48[] expirations; + uint48[] nonces; + address spender; + uint256 sigDeadline; + } + + function defaultPermitAllowance(address token, uint160 amountOrId, uint48 expiration, uint48 nonce) + public + view + returns (IPermitSingle memory) + { + IAllowanceTransfer.PermitDetails memory details = + IAllowanceTransfer.PermitDetails({token: token, amount: amountOrId, expiration: expiration, nonce: nonce}); + return IPermitSingle({ + token: token, + amountOrId: amountOrId, + expiration: expiration, + nonce: nonce, + spender: address(this), + sigDeadline: block.timestamp + 100 + }); + } + + function defaultPermitBatchAllowance(address[] memory tokens, uint160 amountOrId, uint48 expiration, uint48 nonce) + public + view + returns (IPermitBatch memory) + { + uint160[] memory amountOrIds = AmountBuilder.fillUInt160(tokens.length, amountOrId); + uint48[] memory expirations = AmountBuilder.fillUInt48(tokens.length, expiration); + uint48[] memory nonces = AmountBuilder.fillUInt48(tokens.length, nonce); + return IPermitBatch({ + tokens: tokens, + amountOrIds: amountOrIds, + expirations: expirations, + nonces: nonces, + spender: address(this), + sigDeadline: block.timestamp + 100 + }); + } +} diff --git a/test/utils/PermitSignature.sol b/test/utils/PermitSignature.sol index 192cf173..dc634d84 100644 --- a/test/utils/PermitSignature.sol +++ b/test/utils/PermitSignature.sol @@ -30,16 +30,6 @@ contract PermitSignature is Test { "PermitBatchTransferFrom(TokenPermissions[] permitted,address spender,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)" ); - // used for testing abstraction - struct IPermitSingle { - address token; - uint160 amountOrId; - uint48 expiration; - uint48 nonce; - address spender; - uint256 sigDeadline; - } - function getPermitSignatureRaw( IAllowanceTransfer.PermitSingle memory permit, uint256 privateKey, @@ -74,7 +64,7 @@ contract PermitSignature is Test { ) internal returns (bytes memory sig) { (uint8 v, bytes32 r, bytes32 s) = getPermitSignatureRaw(permit, privateKey, domainSeparator); bytes32 vs; - (r, vs) = _getCompactSignature(v, r, s); + (r, vs) = getCompactSignature(v, r, s); return bytes.concat(r, vs); } @@ -98,15 +88,11 @@ contract PermitSignature is Test { (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, msgHash); bytes32 vs; - (r, vs) = _getCompactSignature(v, r, s); + (r, vs) = getCompactSignature(v, r, s); return bytes.concat(r, vs); } - function _getCompactSignature(uint8 vRaw, bytes32 rRaw, bytes32 sRaw) - internal - pure - returns (bytes32 r, bytes32 vs) - { + function getCompactSignature(uint8 vRaw, bytes32 rRaw, bytes32 sRaw) public pure returns (bytes32 r, bytes32 vs) { uint8 v = vRaw - 27; // 27 is 0, 28 is 1 vs = bytes32(uint256(v) << 255) | sRaw; return (rRaw, vs); @@ -245,23 +231,6 @@ contract PermitSignature is Test { return bytes.concat(r, s, bytes1(v)); } - function defaultPermitAllowance(address token, uint160 amountOrId, uint48 expiration, uint48 nonce) - public - view - returns (IPermitSingle memory) - { - IAllowanceTransfer.PermitDetails memory details = - IAllowanceTransfer.PermitDetails({token: token, amount: amountOrId, expiration: expiration, nonce: nonce}); - return IPermitSingle({ - token: token, - amountOrId: amountOrId, - expiration: expiration, - nonce: nonce, - spender: address(this), - sigDeadline: block.timestamp + 100 - }); - } - function defaultERC20PermitAllowance(address token, uint160 amount, uint48 expiration, uint48 nonce) internal view From 6ba10b59d1779ec0302ee5b93cddefe43a1f3b19 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Tue, 10 Jan 2023 18:02:55 -0700 Subject: [PATCH 14/27] gas --- .gas-snapshot | 155 ++++++++++++++++++++++++-------------------------- 1 file changed, 74 insertions(+), 81 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 8829d0d5..2a857647 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,103 +1,96 @@ -AllowanceTransferInvariants:invariant_balanceEqualsSpent() (runs: 256, calls: 3840, reverts: 886) -AllowanceTransferInvariants:invariant_permit2NeverHoldsBalance() (runs: 256, calls: 3840, reverts: 886) -AllowanceTransferInvariants:invariant_spendNeverExceedsPermit() (runs: 256, calls: 3840, reverts: 886) -AllowanceTransferTest:testApprove() (gas: 47561) -AllowanceTransferTest:testBatchTransferFrom() (gas: 159268) -AllowanceTransferTest:testBatchTransferFromDifferentOwners() (gas: 235508) -AllowanceTransferTest:testBatchTransferFromMultiToken() (gas: 231828) -AllowanceTransferTest:testBatchTransferFromWithGasSnapshot() (gas: 159818) -AllowanceTransferTest:testExcessiveInvalidation() (gas: 64136) -AllowanceTransferTest:testInvalidateMultipleNonces() (gas: 83139) -AllowanceTransferTest:testInvalidateNonces() (gas: 62679) -AllowanceTransferTest:testInvalidateNoncesInvalid() (gas: 16261) -AllowanceTransferTest:testLockdown() (gas: 145952) -AllowanceTransferTest:testLockdownEvent() (gas: 117758) -AllowanceTransferTest:testMaxAllowance() (gas: 134993) -AllowanceTransferTest:testMaxAllowanceDirtyWrite() (gas: 117582) -AllowanceTransferTest:testPartialAllowance() (gas: 105067) -AllowanceTransferTest:testReuseOrderedNonceInvalid() (gas: 69095) -AllowanceTransferTest:testSetAllowance() (gas: 89583) -AllowanceTransferTest:testSetAllowanceBatch() (gas: 133608) -AllowanceTransferTest:testSetAllowanceBatchDifferentNonces() (gas: 118583) -AllowanceTransferTest:testSetAllowanceBatchDirtyWrite() (gas: 99144) -AllowanceTransferTest:testSetAllowanceBatchEvent() (gas: 115892) -AllowanceTransferTest:testSetAllowanceCompactSig() (gas: 89543) -AllowanceTransferTest:testSetAllowanceDeadlinePassed() (gas: 56500) -AllowanceTransferTest:testSetAllowanceDirtyWrite() (gas: 72175) -AllowanceTransferTest:testSetAllowanceIncorrectSigLength() (gas: 29157) -AllowanceTransferTest:testSetAllowanceInvalidSignature() (gas: 64071) -AllowanceTransferTest:testSetAllowanceTransfer() (gas: 103161) -AllowanceTransferTest:testSetAllowanceTransferDirtyNonceDirtyTransfer() (gas: 97432) -AllowanceTransferTest:testTransferFromWithGasSnapshot() (gas: 133004) -AllowanceUnitTest:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38976, ~: 39054) -AllowanceUnitTest:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40200, ~: 40201) -AllowanceUnitTest:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39290, ~: 39291) -CompactSignature:testCompactSignature27() (gas: 253) +AllowanceTransferInvariants:invariant_balanceEqualsSpent() (runs: 256, calls: 3840, reverts: 887) +AllowanceTransferInvariants:invariant_permit2NeverHoldsBalance() (runs: 256, calls: 3840, reverts: 887) +AllowanceTransferInvariants:invariant_spendNeverExceedsPermit() (runs: 256, calls: 3840, reverts: 887) +AllowanceTransferTest_ERC20:testApprove() (gas: 47610) +AllowanceTransferTest_ERC20:testMaxAllowance() (gas: 135412) +AllowanceTransferTest_ERC20:testMaxAllowanceDirtyWrite() (gas: 118045) +AllowanceTransferTest_ERC20:testPartialAllowance() (gas: 105603) +AllowanceTransferTest_ERC20:testReuseOrderedNonceInvalid() (gas: 69865) +AllowanceTransferTest_ERC20:testSetAllowance() (gas: 89936) +AllowanceTransferTest_ERC20:testSetAllowanceBatch() (gas: 144498) +AllowanceTransferTest_ERC20:testSetAllowanceBatchDifferentNonces() (gas: 129658) +AllowanceTransferTest_ERC20:testSetAllowanceBatchDirtyWrite() (gas: 110034) +AllowanceTransferTest_ERC20:testSetAllowanceBatchEvent() (gas: 124271) +AllowanceTransferTest_ERC20:testSetAllowanceCompactSig() (gas: 89950) +AllowanceTransferTest_ERC20:testSetAllowanceDeadlinePassed() (gas: 56816) +AllowanceTransferTest_ERC20:testSetAllowanceDirtyWrite() (gas: 72528) +AllowanceTransferTest_ERC20:testSetAllowanceIncorrectSigLength() (gas: 29552) +AllowanceTransferTest_ERC20:testSetAllowanceInvalidSignature() (gas: 64467) +AllowanceTransferTest_ERC20:testSetAllowanceTransfer() (gas: 103558) +AllowanceTransferTest_ERC20:testSetAllowanceTransferDirtyNonceDirtyTransfer() (gas: 97849) +AllowanceTransferTest_ERC20:testTransferFromWithGasSnapshot() (gas: 133453) +CompactSignature:testCompactSignature27() (gas: 275) CompactSignature:testCompactSignature28() (gas: 141) EIP712Test:testDomainSeparator() (gas: 5804) EIP712Test:testDomainSeparatorAfterFork() (gas: 10787) -Permit2LibTest:testOZSafePermit() (gas: 24509) -Permit2LibTest:testOZSafePermitPlusOZSafeTransferFrom() (gas: 129197) -Permit2LibTest:testOZSafeTransferFrom() (gas: 38919) -Permit2LibTest:testPermit2() (gas: 22776) -Permit2LibTest:testPermit2DSLessToken() (gas: 6989) -Permit2LibTest:testPermit2DSMore32Token() (gas: 7076) -Permit2LibTest:testPermit2DSMoreToken() (gas: 6957) -Permit2LibTest:testPermit2Full() (gas: 42196) -Permit2LibTest:testPermit2InvalidAmount() (gas: 20619) -Permit2LibTest:testPermit2LargerDS() (gas: 51226) -Permit2LibTest:testPermit2LargerDSRevert() (gas: 32650) -Permit2LibTest:testPermit2NonPermitFallback() (gas: 37048) -Permit2LibTest:testPermit2NonPermitToken() (gas: 32011) -Permit2LibTest:testPermit2PlusTransferFrom2() (gas: 126893) -Permit2LibTest:testPermit2PlusTransferFrom2WithNonPermit() (gas: 147999) -Permit2LibTest:testPermit2PlusTransferFrom2WithNonPermitFallback() (gas: 174659) -Permit2LibTest:testPermit2PlusTransferFrom2WithWETH9Mainnet() (gas: 147693) -Permit2LibTest:testPermit2SmallerDS() (gas: 77619) -Permit2LibTest:testPermit2SmallerDSNoRevert() (gas: 59269) -Permit2LibTest:testPermit2WETH9Mainnet() (gas: 28712) -Permit2LibTest:testStandardPermit() (gas: 22340) -Permit2LibTest:testStandardTransferFrom() (gas: 38121) -Permit2LibTest:testTransferFrom2() (gas: 38580) -Permit2LibTest:testTransferFrom2Full() (gas: 53258) +Permit2LibTest:testOZSafePermit() (gas: 24555) +Permit2LibTest:testOZSafePermitPlusOZSafeTransferFrom() (gas: 129243) +Permit2LibTest:testOZSafeTransferFrom() (gas: 38941) +Permit2LibTest:testPermit2() (gas: 22822) +Permit2LibTest:testPermit2DSLessToken() (gas: 7011) +Permit2LibTest:testPermit2DSMore32Token() (gas: 7098) +Permit2LibTest:testPermit2DSMoreToken() (gas: 6979) +Permit2LibTest:testPermit2Full() (gas: 42242) +Permit2LibTest:testPermit2InvalidAmount() (gas: 20665) +Permit2LibTest:testPermit2LargerDS() (gas: 51272) +Permit2LibTest:testPermit2LargerDSRevert() (gas: 32696) +Permit2LibTest:testPermit2NonPermitFallback() (gas: 37094) +Permit2LibTest:testPermit2NonPermitToken() (gas: 32057) +Permit2LibTest:testPermit2PlusTransferFrom2() (gas: 126939) +Permit2LibTest:testPermit2PlusTransferFrom2WithNonPermit() (gas: 148045) +Permit2LibTest:testPermit2PlusTransferFrom2WithNonPermitFallback() (gas: 174683) +Permit2LibTest:testPermit2PlusTransferFrom2WithWETH9Mainnet() (gas: 147739) +Permit2LibTest:testPermit2SmallerDS() (gas: 77643) +Permit2LibTest:testPermit2SmallerDSNoRevert() (gas: 59293) +Permit2LibTest:testPermit2WETH9Mainnet() (gas: 28736) +Permit2LibTest:testStandardPermit() (gas: 22386) +Permit2LibTest:testStandardTransferFrom() (gas: 38143) +Permit2LibTest:testTransferFrom2() (gas: 38602) +Permit2LibTest:testTransferFrom2Full() (gas: 53280) Permit2LibTest:testTransferFrom2InvalidAmount() (gas: 12710) -Permit2LibTest:testTransferFrom2NonPermitToken() (gas: 53104) +Permit2LibTest:testTransferFrom2NonPermitToken() (gas: 53126) SignatureTransferTest:testCorrectWitnessTypehashes() (gas: 3075) -SignatureTransferTest:testGasMultiplePermitBatchTransferFrom() (gas: 270919) -SignatureTransferTest:testGasSinglePermitBatchTransferFrom() (gas: 186316) -SignatureTransferTest:testGasSinglePermitTransferFrom() (gas: 123850) -SignatureTransferTest:testInvalidateUnorderedNonces() (gas: 41268) -SignatureTransferTest:testPermitBatchMultiPermitSingleTransfer() (gas: 133644) +SignatureTransferTest:testGasMultiplePermitBatchTransferFrom() (gas: 270943) +SignatureTransferTest:testGasSinglePermitBatchTransferFrom() (gas: 186340) +SignatureTransferTest:testGasSinglePermitTransferFrom() (gas: 123852) +SignatureTransferTest:testInvalidateUnorderedNonces() (gas: 41233) +SignatureTransferTest:testPermitBatchMultiPermitSingleTransfer() (gas: 133666) SignatureTransferTest:testPermitBatchTransferFrom() (gas: 162010) -SignatureTransferTest:testPermitBatchTransferFromSingleRecipient() (gas: 190319) -SignatureTransferTest:testPermitBatchTransferFromTypedWitness() (gas: 239854) +SignatureTransferTest:testPermitBatchTransferFromSingleRecipient() (gas: 190277) +SignatureTransferTest:testPermitBatchTransferFromTypedWitness() (gas: 239856) SignatureTransferTest:testPermitBatchTransferFromTypedWitnessInvalidType() (gas: 84467) -SignatureTransferTest:testPermitBatchTransferFromTypedWitnessInvalidTypeHash() (gas: 85864) +SignatureTransferTest:testPermitBatchTransferFromTypedWitnessInvalidTypeHash() (gas: 85842) SignatureTransferTest:testPermitBatchTransferFromTypedWitnessInvalidWitness() (gas: 85688) SignatureTransferTest:testPermitBatchTransferInvalidAmountsLengthMismatch() (gas: 43967) -SignatureTransferTest:testPermitBatchTransferMultiAddr() (gas: 160406) -SignatureTransferTest:testPermitBatchTransferSingleRecipientManyTokens() (gas: 211834) +SignatureTransferTest:testPermitBatchTransferMultiAddr() (gas: 160384) +SignatureTransferTest:testPermitBatchTransferSingleRecipientManyTokens() (gas: 211836) SignatureTransferTest:testPermitTransferFrom() (gas: 93012) SignatureTransferTest:testPermitTransferFromCompactSig() (gas: 123927) -SignatureTransferTest:testPermitTransferFromIncorrectSigLength() (gas: 51327) -SignatureTransferTest:testPermitTransferFromInvalidNonce() (gas: 72799) +SignatureTransferTest:testPermitTransferFromIncorrectSigLength() (gas: 51349) +SignatureTransferTest:testPermitTransferFromInvalidNonce() (gas: 72755) SignatureTransferTest:testPermitTransferFromRandomNonceAndAmount(uint256,uint128) (runs: 256, μ: 95754, ~: 96730) -SignatureTransferTest:testPermitTransferFromToSpender() (gas: 93342) -SignatureTransferTest:testPermitTransferFromTypedWitness() (gas: 125271) -SignatureTransferTest:testPermitTransferFromTypedWitnessInvalidType() (gas: 55906) +SignatureTransferTest:testPermitTransferFromToSpender() (gas: 93364) +SignatureTransferTest:testPermitTransferFromTypedWitness() (gas: 125273) +SignatureTransferTest:testPermitTransferFromTypedWitnessInvalidType() (gas: 55928) SignatureTransferTest:testPermitTransferFromTypedWitnessInvalidTypehash() (gas: 56794) -SignatureTransferTest:testPermitTransferSpendLessThanFull(uint256,uint128) (runs: 256, μ: 97989, ~: 99707) -TypehashGeneration:testPermitBatch() (gas: 40493) +SignatureTransferTest:testPermitTransferSpendLessThanFull(uint256,uint128) (runs: 256, μ: 97967, ~: 99685) +TypehashGeneration:testPermitBatch() (gas: 40515) TypehashGeneration:testPermitBatchTransferFrom() (gas: 49854) -TypehashGeneration:testPermitBatchTransferFromWithWitness() (gas: 56587) +TypehashGeneration:testPermitBatchTransferFromWithWitness() (gas: 56609) TypehashGeneration:testPermitBatchTransferFromWithWitnessIncorrectPermitData() (gas: 56744) -TypehashGeneration:testPermitBatchTransferFromWithWitnessIncorrectTypehashStub() (gas: 57229) +TypehashGeneration:testPermitBatchTransferFromWithWitnessIncorrectTypehashStub() (gas: 57251) TypehashGeneration:testPermitSingle() (gas: 28117) TypehashGeneration:testPermitTransferFrom() (gas: 36520) -TypehashGeneration:testPermitTransferFromWithWitness() (gas: 43369) +TypehashGeneration:testPermitTransferFromWithWitness() (gas: 43391) TypehashGeneration:testPermitTransferFromWithWitnessIncorrectPermitData() (gas: 43430) -TypehashGeneration:testPermitTransferFromWithWitnessIncorrectTypehashStub() (gas: 43833) +TypehashGeneration:testPermitTransferFromWithWitnessIncorrectTypehashStub() (gas: 43855) MockPermit2Lib:testPermit2Code(address):(bool) (runs: 256, μ: 3025, ~: 3016) +AllowanceUnitTest_ERC20:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38727, ~: 38805) +AllowanceUnitTest_ERC20:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40210, ~: 40211) +AllowanceUnitTest_ERC20:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39330, ~: 39331) +AllowanceUnitTest_ERC721:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38727, ~: 38805) +AllowanceUnitTest_ERC721:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40210, ~: 40211) +AllowanceUnitTest_ERC721:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39330, ~: 39331) NonceBitmapTest_ERC20:testHighNonces() (gas: 36142) NonceBitmapTest_ERC20:testInvalidateFullWord() (gas: 63031) NonceBitmapTest_ERC20:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30335, ~: 31035) From d2f227ecd48fee4aa1a58e355a8546a1c37b3d59 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 13 Jan 2023 11:28:54 -0700 Subject: [PATCH 15/27] rename --- .../{Permit2_ERC721.sol => Permit2ERC721.sol} | 0 test/AllowanceTransferInvariants.t.sol | 2 +- ...t.sol => AllowanceTransferTestERC20.t.sol} | 4 +- test/SignatureTransfer.t.sol | 4 +- test/mocks/MockPermit2ERC721.sol | 44 +++++++++++++++++++ test/mocks/MockPermit2_ERC721.sol | 44 ------------------- ...C20.t.sol => AllowanceUnitTestERC20.t.sol} | 4 +- ...21.t.sol => AllowanceUnitTestERC721.t.sol} | 10 ++--- ...C721.t.sol => NonceBitmapTestERC721.t.sol} | 10 ++--- ...vider_ERC20.sol => TokenProviderERC20.sol} | 2 +- ...der_ERC721.sol => TokenProviderERC721.sol} | 2 +- 11 files changed, 63 insertions(+), 63 deletions(-) rename src/ERC721/{Permit2_ERC721.sol => Permit2ERC721.sol} (100%) rename test/{AllowanceTransferTest_ERC20.t.sol => AllowanceTransferTestERC20.t.sol} (97%) create mode 100644 test/mocks/MockPermit2ERC721.sol delete mode 100644 test/mocks/MockPermit2_ERC721.sol rename test/shared/{AllowanceUnitTest_ERC20.t.sol => AllowanceUnitTestERC20.t.sol} (81%) rename test/shared/{AllowanceUnitTest_ERC721.t.sol => AllowanceUnitTestERC721.t.sol} (61%) rename test/shared/{NonceBitmapTest_ERC721.t.sol => NonceBitmapTestERC721.t.sol} (53%) rename test/utils/{TokenProvider_ERC20.sol => TokenProviderERC20.sol} (98%) rename test/utils/{TokenProvider_ERC721.sol => TokenProviderERC721.sol} (98%) diff --git a/src/ERC721/Permit2_ERC721.sol b/src/ERC721/Permit2ERC721.sol similarity index 100% rename from src/ERC721/Permit2_ERC721.sol rename to src/ERC721/Permit2ERC721.sol diff --git a/test/AllowanceTransferInvariants.t.sol b/test/AllowanceTransferInvariants.t.sol index 7b4527e9..d44049ad 100644 --- a/test/AllowanceTransferInvariants.t.sol +++ b/test/AllowanceTransferInvariants.t.sol @@ -1,7 +1,7 @@ pragma solidity 0.8.17; import "forge-std/Test.sol"; -import {TokenProvider_ERC20} from "./utils/TokenProvider_ERC20.sol"; +import {TokenProviderERC20} from "./utils/TokenProviderERC20.sol"; import {Permit2} from "../src/ERC20/Permit2.sol"; import {IAllowanceTransfer} from "../src/ERC20/interfaces/IAllowanceTransfer.sol"; import {SignatureVerification} from "../src/shared/SignatureVerification.sol"; diff --git a/test/AllowanceTransferTest_ERC20.t.sol b/test/AllowanceTransferTestERC20.t.sol similarity index 97% rename from test/AllowanceTransferTest_ERC20.t.sol rename to test/AllowanceTransferTestERC20.t.sol index 05edeebd..ad21a0ad 100644 --- a/test/AllowanceTransferTest_ERC20.t.sol +++ b/test/AllowanceTransferTestERC20.t.sol @@ -4,14 +4,14 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {BaseAllowanceTransferTest} from "./BaseAllowanceTransferTest.t.sol"; import {Permit2} from "../src/ERC20/Permit2.sol"; -import {TokenProvider_ERC20} from "./utils/TokenProvider_ERC20.sol"; +import {TokenProviderERC20} from "./utils/TokenProviderERC20.sol"; import {PermitHash} from "../src/ERC20/libraries/PermitHash.sol"; import {PermitAbstraction} from "./utils/PermitAbstraction.sol"; import {PermitSignature} from "./utils/PermitSignature.sol"; import {IAllowanceTransfer} from "../src/ERC20/interfaces/IAllowanceTransfer.sol"; import {MockERC20} from "./mocks/MockERC20.sol"; -contract AllowanceTransferTest_ERC20 is TokenProvider_ERC20, BaseAllowanceTransferTest { +contract AllowanceTransferTestERC20 is TokenProviderERC20, BaseAllowanceTransferTest { function setUp() public override { permit2 = address(new Permit2()); DOMAIN_SEPARATOR = Permit2(permit2).DOMAIN_SEPARATOR(); diff --git a/test/SignatureTransfer.t.sol b/test/SignatureTransfer.t.sol index 332bc2a6..fbe51ddc 100644 --- a/test/SignatureTransfer.t.sol +++ b/test/SignatureTransfer.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {SafeERC20, IERC20, IERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import {SignatureVerification} from "../src/shared/SignatureVerification.sol"; -import {TokenProvider_ERC20} from "./utils/TokenProvider_ERC20.sol"; +import {TokenProviderERC20} from "./utils/TokenProviderERC20.sol"; import {PermitSignature} from "./utils/PermitSignature.sol"; import {AddressBuilder} from "./utils/AddressBuilder.sol"; import {AmountBuilder} from "./utils/AmountBuilder.sol"; @@ -15,7 +15,7 @@ import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {ISignatureTransfer} from "../src/ERC20/interfaces/ISignatureTransfer.sol"; import {InvalidNonce, SignatureExpired} from "../src/shared/PermitErrors.sol"; -contract SignatureTransferTest is Test, PermitSignature, TokenProvider_ERC20, GasSnapshot { +contract SignatureTransferTest is Test, PermitSignature, TokenProviderERC20, GasSnapshot { using AddressBuilder for address[]; using AmountBuilder for uint256[]; diff --git a/test/mocks/MockPermit2ERC721.sol b/test/mocks/MockPermit2ERC721.sol new file mode 100644 index 00000000..d88276dc --- /dev/null +++ b/test/mocks/MockPermit2ERC721.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {Permit2ERC721} from "../../src/ERC721/Permit2ERC721.sol"; +import {IAllowanceTransferERC721} from "../../src/ERC721/interfaces/IAllowanceTransferERC721.sol"; +import {SignatureTransferERC721} from "../../src/ERC721/SignatureTransferERC721.sol"; +import {AllowanceERC721} from "../../src/ERC721/libraries/AllowanceERC721.sol"; +import {IMockPermit2} from "../mocks/IMockPermit2.sol"; + +contract MockPermit2ERC721 is IMockPermit2, Permit2ERC721 { + function doStore(address from, address token, address spender, uint256 word) public override { + IAllowanceTransferERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + assembly { + sstore(allowed.slot, word) + } + } + + function getStore(address from, address token, address spender) public view override returns (uint256 word) { + IAllowanceTransferERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + assembly { + word := sload(allowed.slot) + } + } + + function mockUpdateSome(address from, address token, address spender, uint160 data, uint48 expiration) + public + override + { + IAllowanceTransferERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + AllowanceERC721.updateTokenIdAndExpiration(allowed, data, expiration); + } + + function mockUpdateAll(address from, address token, address spender, uint160 data, uint48 expiration, uint48 nonce) + public + override + { + IAllowanceTransferERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + AllowanceERC721.updateAll(allowed, data, expiration, nonce); + } + + function useUnorderedNonce(address from, uint256 nonce) public override { + _useUnorderedNonce(from, nonce); + } +} diff --git a/test/mocks/MockPermit2_ERC721.sol b/test/mocks/MockPermit2_ERC721.sol deleted file mode 100644 index 27d38185..00000000 --- a/test/mocks/MockPermit2_ERC721.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import {Permit2_ERC721} from "../../src/ERC721/Permit2_ERC721.sol"; -import {IAllowanceTransfer_ERC721} from "../../src/ERC721/interfaces/IAllowanceTransfer_ERC721.sol"; -import {SignatureTransfer_ERC721} from "../../src/ERC721/SignatureTransfer_ERC721.sol"; -import {Allowance_ERC721} from "../../src/ERC721/libraries/Allowance_ERC721.sol"; -import {IMockPermit2} from "../mocks/IMockPermit2.sol"; - -contract MockPermit2_ERC721 is IMockPermit2, Permit2_ERC721 { - function doStore(address from, address token, address spender, uint256 word) public override { - IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; - assembly { - sstore(allowed.slot, word) - } - } - - function getStore(address from, address token, address spender) public view override returns (uint256 word) { - IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; - assembly { - word := sload(allowed.slot) - } - } - - function mockUpdateSome(address from, address token, address spender, uint160 data, uint48 expiration) - public - override - { - IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; - Allowance_ERC721.updateTokenIdAndExpiration(allowed, data, expiration); - } - - function mockUpdateAll(address from, address token, address spender, uint160 data, uint48 expiration, uint48 nonce) - public - override - { - IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; - Allowance_ERC721.updateAll(allowed, data, expiration, nonce); - } - - function useUnorderedNonce(address from, uint256 nonce) public override { - _useUnorderedNonce(from, nonce); - } -} diff --git a/test/shared/AllowanceUnitTest_ERC20.t.sol b/test/shared/AllowanceUnitTestERC20.t.sol similarity index 81% rename from test/shared/AllowanceUnitTest_ERC20.t.sol rename to test/shared/AllowanceUnitTestERC20.t.sol index 7631de06..b5046e2c 100644 --- a/test/shared/AllowanceUnitTest_ERC20.t.sol +++ b/test/shared/AllowanceUnitTestERC20.t.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import "../mocks/MockPermit2.sol"; import {BaseAllowanceUnitTest} from "./BaseAllowanceUnitTest.sol"; -import {TokenProvider_ERC20} from "../utils/TokenProvider_ERC20.sol"; +import {TokenProviderERC20} from "../utils/TokenProviderERC20.sol"; -contract AllowanceUnitTest_ERC20 is TokenProvider_ERC20, BaseAllowanceUnitTest { +contract AllowanceUnitTest_ERC20 is TokenProviderERC20, BaseAllowanceUnitTest { function setUp() public override { permit2 = new MockPermit2(); initializeERC20Tokens(); diff --git a/test/shared/AllowanceUnitTest_ERC721.t.sol b/test/shared/AllowanceUnitTestERC721.t.sol similarity index 61% rename from test/shared/AllowanceUnitTest_ERC721.t.sol rename to test/shared/AllowanceUnitTestERC721.t.sol index d4fa7e71..3d302bf0 100644 --- a/test/shared/AllowanceUnitTest_ERC721.t.sol +++ b/test/shared/AllowanceUnitTestERC721.t.sol @@ -2,13 +2,13 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; -import "../mocks/MockPermit2_ERC721.sol"; +import "../mocks/MockPermit2ERC721.sol"; import {BaseAllowanceUnitTest} from "./BaseAllowanceUnitTest.sol"; -import {TokenProvider_ERC721} from "../utils/TokenProvider_ERC721.sol"; +import {TokenProviderERC721} from "../utils/TokenProviderERC721.sol"; -contract AllowanceUnitTest_ERC721 is TokenProvider_ERC721, BaseAllowanceUnitTest { +contract AllowanceUnitTestERC721 is TokenProviderERC721, BaseAllowanceUnitTest { function setUp() public override { - permit2 = new MockPermit2_ERC721(); + permit2 = new MockPermit2ERC721(); initializeERC721TestTokens(); } @@ -18,7 +18,7 @@ contract AllowanceUnitTest_ERC721 is TokenProvider_ERC721, BaseAllowanceUnitTest override returns (uint160, uint48, uint48) { - return MockPermit2_ERC721(address(permit2)).allowance(from, token, spender); + return MockPermit2ERC721(address(permit2)).allowance(from, token, spender); } function token1() public view override returns (address) { diff --git a/test/shared/NonceBitmapTest_ERC721.t.sol b/test/shared/NonceBitmapTestERC721.t.sol similarity index 53% rename from test/shared/NonceBitmapTest_ERC721.t.sol rename to test/shared/NonceBitmapTestERC721.t.sol index 980b3b45..effc7af6 100644 --- a/test/shared/NonceBitmapTest_ERC721.t.sol +++ b/test/shared/NonceBitmapTestERC721.t.sol @@ -3,18 +3,18 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {BaseNonceBitmapTest} from "./BaseNonceBitmapTest.t.sol"; -import {MockPermit2_ERC721} from "../mocks/MockPermit2_ERC721.sol"; +import {MockPermit2ERC721} from "../mocks/MockPermit2ERC721.sol"; -contract NonceBitmapTest_ERC721 is BaseNonceBitmapTest { +contract NonceBitmapTestERC721 is BaseNonceBitmapTest { function setUp() public override { - permit2 = new MockPermit2_ERC721(); + permit2 = new MockPermit2ERC721(); } function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) public override { - MockPermit2_ERC721(address(permit2)).invalidateUnorderedNonces(wordPos, mask); + MockPermit2ERC721(address(permit2)).invalidateUnorderedNonces(wordPos, mask); } function nonceBitmap(address addr, uint256 wordPos) public override returns (uint256) { - return MockPermit2_ERC721(address(permit2)).nonceBitmap(addr, wordPos); + return MockPermit2ERC721(address(permit2)).nonceBitmap(addr, wordPos); } } diff --git a/test/utils/TokenProvider_ERC20.sol b/test/utils/TokenProviderERC20.sol similarity index 98% rename from test/utils/TokenProvider_ERC20.sol rename to test/utils/TokenProviderERC20.sol index e38503fd..24ece69d 100644 --- a/test/utils/TokenProvider_ERC20.sol +++ b/test/utils/TokenProviderERC20.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {MockERC20} from "../mocks/MockERC20.sol"; -contract TokenProvider_ERC20 { +contract TokenProviderERC20 { uint256 public constant MINT_AMOUNT_ERC20 = 100 ** 18; uint256 public constant MINT_AMOUNT_ERC1155 = 100; diff --git a/test/utils/TokenProvider_ERC721.sol b/test/utils/TokenProviderERC721.sol similarity index 98% rename from test/utils/TokenProvider_ERC721.sol rename to test/utils/TokenProviderERC721.sol index 630891b9..681f02d5 100644 --- a/test/utils/TokenProvider_ERC721.sol +++ b/test/utils/TokenProviderERC721.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {MockERC721} from "../mocks/MockERC721.sol"; -contract TokenProvider_ERC721 { +contract TokenProviderERC721 { uint256 public constant MINT_AMOUNT_ERC20 = 100 ** 18; uint256 public constant MINT_AMOUNT_ERC1155 = 100; From 5ff81ccd520a446e8a98cb87cc4517e335b3a694 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 13 Jan 2023 12:30:47 -0700 Subject: [PATCH 16/27] fix: check current nonce before setting new nonce & expiration --- src/ERC721/AllowanceTransferERC721.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ERC721/AllowanceTransferERC721.sol b/src/ERC721/AllowanceTransferERC721.sol index 725b5050..db475266 100644 --- a/src/ERC721/AllowanceTransferERC721.sol +++ b/src/ERC721/AllowanceTransferERC721.sol @@ -73,6 +73,9 @@ contract AllowanceTransferERC721 is IAllowanceTransferERC721, EIP712ERC721 { signature.verify(_hashTypedData(permitAll.hash()), owner); PackedOperatorAllowance storage operator = operators[msg.sender][permitAll.token][permitAll.spender]; + + if (permitAll.nonce != operator.nonce) revert InvalidNonce(); + unchecked { operator.nonce += 1; } From 96261d0b8660e6e0211b6770aadd0c90054e24e1 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 13 Jan 2023 15:15:04 -0700 Subject: [PATCH 17/27] fix: use owner --- src/ERC721/AllowanceTransferERC721.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ERC721/AllowanceTransferERC721.sol b/src/ERC721/AllowanceTransferERC721.sol index db475266..374c596a 100644 --- a/src/ERC721/AllowanceTransferERC721.sol +++ b/src/ERC721/AllowanceTransferERC721.sol @@ -72,7 +72,7 @@ contract AllowanceTransferERC721 is IAllowanceTransferERC721, EIP712ERC721 { // Verify the signer address from the signature. signature.verify(_hashTypedData(permitAll.hash()), owner); - PackedOperatorAllowance storage operator = operators[msg.sender][permitAll.token][permitAll.spender]; + PackedOperatorAllowance storage operator = operators[owner][permitAll.token][permitAll.spender]; if (permitAll.nonce != operator.nonce) revert InvalidNonce(); From 6bb7b77c353703abc56b04f67f953a1ac4c53555 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 13 Jan 2023 15:18:42 -0700 Subject: [PATCH 18/27] fix: nesting logic --- src/ERC721/AllowanceTransferERC721.sol | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/ERC721/AllowanceTransferERC721.sol b/src/ERC721/AllowanceTransferERC721.sol index 374c596a..392ff5c2 100644 --- a/src/ERC721/AllowanceTransferERC721.sol +++ b/src/ERC721/AllowanceTransferERC721.sol @@ -111,15 +111,13 @@ contract AllowanceTransferERC721 is IAllowanceTransferERC721, EIP712ERC721 { revert AllowanceExpired(allowed.expiration, operatorExpiration); } - if (allowed.spender != msg.sender) { - if (operatorExpired) { - // If there is no tokenId permissions and no operator permissions on msg.sender - // then the msg.sender has insufficient allowance. - revert InsufficientAllowance(token, tokenId); - } - } else { - // Since msg.sender has given tokenId permissions to the spender, reset the tokenId permissions before the transfer. + if (allowed.spender == msg.sender) { + // Reset permissions before transfer. allowed.spender = address(0); + } else if (operatorExpired) { + // If there is no tokenId permissions and no operator permissions on msg.sender + // then the msg.sender has insufficient allowance. + revert InsufficientAllowance(token, tokenId); } // Transfer the token from the from address to the recipient. From aec2bd82781a5333755d73c3198379847187b9e1 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Tue, 31 Jan 2023 14:43:20 -0500 Subject: [PATCH 19/27] comment update --- src/ERC721/interfaces/IAllowanceTransferERC721.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ERC721/interfaces/IAllowanceTransferERC721.sol b/src/ERC721/interfaces/IAllowanceTransferERC721.sol index 946e3088..c2f87fd1 100644 --- a/src/ERC721/interfaces/IAllowanceTransferERC721.sol +++ b/src/ERC721/interfaces/IAllowanceTransferERC721.sol @@ -106,7 +106,7 @@ interface IAllowanceTransferERC721 { } /// @notice The saved expiration on the operator. - /// @dev Holds a nonce value to prevent replay protection. + /// @dev Holds a nonce value to provide replay protection. struct PackedOperatorAllowance { uint48 expiration; uint48 nonce; From 1d45915d896c421a41c0afae2676d33a9d985a45 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 6 Jan 2023 17:19:16 -0500 Subject: [PATCH 20/27] try to share unit bitmap tests --- .../{Permit2.sol => Permit2_ERC721.sol} | 0 test/AllowanceUnitTest.sol | 2 +- test/mocks/IMockPermit2.sol | 28 ++++++++++++ test/mocks/MockPermit2.sol | 36 +++++++-------- test/mocks/MockPermit2_ERC721.sol | 44 +++++++++++++++++++ .../BaseNonceBitmapTest.t.sol} | 12 +++-- test/shared/NonceBitmapTest_ERC20.t.sol | 12 +++++ test/shared/NonceBitmapTest_ERC721.t.sol | 12 +++++ 8 files changed, 117 insertions(+), 29 deletions(-) rename src/ERC721/{Permit2.sol => Permit2_ERC721.sol} (100%) create mode 100644 test/mocks/IMockPermit2.sol create mode 100644 test/mocks/MockPermit2_ERC721.sol rename test/{NonceBitmap.t.sol => shared/BaseNonceBitmapTest.t.sol} (94%) create mode 100644 test/shared/NonceBitmapTest_ERC20.t.sol create mode 100644 test/shared/NonceBitmapTest_ERC721.t.sol diff --git a/src/ERC721/Permit2.sol b/src/ERC721/Permit2_ERC721.sol similarity index 100% rename from src/ERC721/Permit2.sol rename to src/ERC721/Permit2_ERC721.sol diff --git a/test/AllowanceUnitTest.sol b/test/AllowanceUnitTest.sol index 2aa0d170..03e05f34 100644 --- a/test/AllowanceUnitTest.sol +++ b/test/AllowanceUnitTest.sol @@ -21,7 +21,7 @@ contract AllowanceUnitTest is Test, TokenProvider { (,, uint48 nonce) = permit2.allowance(from, token, spender); - permit2.mockUpdateAmountAndExpiration(from, token, spender, amount, expiration); + permit2.mockUpdateSome(from, token, spender, amount, expiration); uint48 timestampAfterUpdate = expiration == 0 ? uint48(block.timestamp) : expiration; diff --git a/test/mocks/IMockPermit2.sol b/test/mocks/IMockPermit2.sol new file mode 100644 index 00000000..9c295b52 --- /dev/null +++ b/test/mocks/IMockPermit2.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {Permit2} from "../../src/ERC20/Permit2.sol"; +import {IAllowanceTransfer} from "../../src/ERC20/interfaces/IAllowanceTransfer.sol"; +import {ISignatureTransfer} from "../../src/ERC20/interfaces/ISignatureTransfer.sol"; +import {Allowance} from "../../src/ERC20/libraries/Allowance.sol"; + +abstract contract IMockPermit2 { + function doStore(address from, address token, address spender, uint256 word) public virtual {} + + function getStore(address from, address token, address spender) public view virtual returns (uint256 word) {} + + function mockUpdateSome(address from, address token, address spender, uint160 data, uint48 expiration) + public + virtual + {} + + function mockUpdateAll(address from, address token, address spender, uint160 data, uint48 expiration, uint48 nonce) + public + virtual + {} + + function useUnorderedNonce(address from, uint256 nonce) public virtual {} + + // stuck here bc this is defined at Permit2 but not the interface + // function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external {} +} diff --git a/test/mocks/MockPermit2.sol b/test/mocks/MockPermit2.sol index b1e26f34..002acea4 100644 --- a/test/mocks/MockPermit2.sol +++ b/test/mocks/MockPermit2.sol @@ -4,46 +4,40 @@ pragma solidity ^0.8.17; import {Permit2} from "../../src/ERC20/Permit2.sol"; import {IAllowanceTransfer} from "../../src/ERC20/interfaces/IAllowanceTransfer.sol"; import {Allowance} from "../../src/ERC20/libraries/Allowance.sol"; +import {IMockPermit2} from "../mocks/IMockPermit2.sol"; -contract MockPermit2 is Permit2 { - function doStore(address from, address token, address spender, uint256 word) public { +contract MockPermit2 is IMockPermit2, Permit2 { + function doStore(address from, address token, address spender, uint256 word) public override { IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; assembly { sstore(allowed.slot, word) } } - function getStore(address from, address token, address spender) public view returns (uint256 word) { + function getStore(address from, address token, address spender) public view override returns (uint256 word) { IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; assembly { word := sload(allowed.slot) } } - function mockUpdateAmountAndExpiration( - address from, - address token, - address spender, - uint160 amount, - uint48 expiration - ) public { + function mockUpdateSome(address from, address token, address spender, uint160 data, uint48 expiration) + public + override + { IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; - Allowance.updateAmountAndExpiration(allowed, amount, expiration); + Allowance.updateAmountAndExpiration(allowed, data, expiration); } - function mockUpdateAll( - address from, - address token, - address spender, - uint160 amount, - uint48 expiration, - uint48 nonce - ) public { + function mockUpdateAll(address from, address token, address spender, uint160 data, uint48 expiration, uint48 nonce) + public + override + { IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; - Allowance.updateAll(allowed, amount, expiration, nonce); + Allowance.updateAll(allowed, data, expiration, nonce); } - function useUnorderedNonce(address from, uint256 nonce) public { + function useUnorderedNonce(address from, uint256 nonce) public override { _useUnorderedNonce(from, nonce); } } diff --git a/test/mocks/MockPermit2_ERC721.sol b/test/mocks/MockPermit2_ERC721.sol new file mode 100644 index 00000000..27d38185 --- /dev/null +++ b/test/mocks/MockPermit2_ERC721.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {Permit2_ERC721} from "../../src/ERC721/Permit2_ERC721.sol"; +import {IAllowanceTransfer_ERC721} from "../../src/ERC721/interfaces/IAllowanceTransfer_ERC721.sol"; +import {SignatureTransfer_ERC721} from "../../src/ERC721/SignatureTransfer_ERC721.sol"; +import {Allowance_ERC721} from "../../src/ERC721/libraries/Allowance_ERC721.sol"; +import {IMockPermit2} from "../mocks/IMockPermit2.sol"; + +contract MockPermit2_ERC721 is IMockPermit2, Permit2_ERC721 { + function doStore(address from, address token, address spender, uint256 word) public override { + IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + assembly { + sstore(allowed.slot, word) + } + } + + function getStore(address from, address token, address spender) public view override returns (uint256 word) { + IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + assembly { + word := sload(allowed.slot) + } + } + + function mockUpdateSome(address from, address token, address spender, uint160 data, uint48 expiration) + public + override + { + IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + Allowance_ERC721.updateTokenIdAndExpiration(allowed, data, expiration); + } + + function mockUpdateAll(address from, address token, address spender, uint160 data, uint48 expiration, uint48 nonce) + public + override + { + IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; + Allowance_ERC721.updateAll(allowed, data, expiration, nonce); + } + + function useUnorderedNonce(address from, uint256 nonce) public override { + _useUnorderedNonce(from, nonce); + } +} diff --git a/test/NonceBitmap.t.sol b/test/shared/BaseNonceBitmapTest.t.sol similarity index 94% rename from test/NonceBitmap.t.sol rename to test/shared/BaseNonceBitmapTest.t.sol index 6984e9f6..91c37b2f 100644 --- a/test/NonceBitmap.t.sol +++ b/test/shared/BaseNonceBitmapTest.t.sol @@ -3,15 +3,13 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {SafeERC20, IERC20, IERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; -import {MockPermit2} from "./mocks/MockPermit2.sol"; -import {InvalidNonce} from "../src/shared/PermitErrors.sol"; +import {IMockPermit2} from "../mocks/MockPermit2.sol"; +import {InvalidNonce} from "../../src/shared/PermitErrors.sol"; -contract NonceBitmapTest is Test { - MockPermit2 permit2; +contract BaseNonceBitmapTest is Test { + IMockPermit2 permit2; - function setUp() public { - permit2 = new MockPermit2(); - } + function setUp() public virtual {} function testLowNonces() public { permit2.useUnorderedNonce(address(this), 5); diff --git a/test/shared/NonceBitmapTest_ERC20.t.sol b/test/shared/NonceBitmapTest_ERC20.t.sol new file mode 100644 index 00000000..09d311f8 --- /dev/null +++ b/test/shared/NonceBitmapTest_ERC20.t.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {BaseNonceBitmapTest} from "./BaseNonceBitmapTest.t.sol"; +import {MockPermit2} from "../mocks/MockPermit2.sol"; + +contract NonceBitmapTest_ERC20 is BaseNonceBitmapTest { + function setUp() public override { + permit2 = new MockPermit2(); + } +} diff --git a/test/shared/NonceBitmapTest_ERC721.t.sol b/test/shared/NonceBitmapTest_ERC721.t.sol new file mode 100644 index 00000000..e39ad348 --- /dev/null +++ b/test/shared/NonceBitmapTest_ERC721.t.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {BaseNonceBitmapTest} from "./BaseNonceBitmapTest.t.sol"; +import {MockPermit2_ERC721} from "../mocks/MockPermit2_ERC721.sol"; + +contract NonceBitmapTest_ERC721 is BaseNonceBitmapTest { + function setUp() public override { + permit2 = new MockPermit2_ERC721(); + } +} From 443c86c6e800a29a6bf9fb705aa4a220ed843fb0 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 6 Jan 2023 17:42:15 -0500 Subject: [PATCH 21/27] 721 bitmap tests --- .gas-snapshot | 33 +++++++++++++++--------- test/shared/BaseNonceBitmapTest.t.sol | 25 ++++++++++-------- test/shared/NonceBitmapTest_ERC20.t.sol | 8 ++++++ test/shared/NonceBitmapTest_ERC721.t.sol | 8 ++++++ 4 files changed, 51 insertions(+), 23 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 463ca7d9..8829d0d5 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -29,22 +29,13 @@ AllowanceTransferTest:testSetAllowanceInvalidSignature() (gas: 64071) AllowanceTransferTest:testSetAllowanceTransfer() (gas: 103161) AllowanceTransferTest:testSetAllowanceTransferDirtyNonceDirtyTransfer() (gas: 97432) AllowanceTransferTest:testTransferFromWithGasSnapshot() (gas: 133004) -AllowanceUnitTest:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38998, ~: 39076) -AllowanceUnitTest:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40222, ~: 40223) -AllowanceUnitTest:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39224, ~: 39225) +AllowanceUnitTest:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38976, ~: 39054) +AllowanceUnitTest:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40200, ~: 40201) +AllowanceUnitTest:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39290, ~: 39291) CompactSignature:testCompactSignature27() (gas: 253) CompactSignature:testCompactSignature28() (gas: 141) EIP712Test:testDomainSeparator() (gas: 5804) EIP712Test:testDomainSeparatorAfterFork() (gas: 10787) -NonceBitmapTest:testHighNonces() (gas: 36186) -NonceBitmapTest:testInvalidateFullWord() (gas: 63125) -NonceBitmapTest:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30308, ~: 31008) -NonceBitmapTest:testInvalidateNonzeroWord() (gas: 85665) -NonceBitmapTest:testInvalidateTwoNoncesRandomly(uint248,uint256,uint256) (runs: 256, μ: 39187, ~: 39187) -NonceBitmapTest:testLowNonces() (gas: 41004) -NonceBitmapTest:testNonceWordBoundary() (gas: 42203) -NonceBitmapTest:testUseTwoRandomNonces(uint256,uint256) (runs: 256, μ: 49205, ~: 51640) -NonceBitmapTest:testUsingNonceTwiceFails(uint256) (runs: 256, μ: 21866, ~: 21889) Permit2LibTest:testOZSafePermit() (gas: 24509) Permit2LibTest:testOZSafePermitPlusOZSafeTransferFrom() (gas: 129197) Permit2LibTest:testOZSafeTransferFrom() (gas: 38919) @@ -107,3 +98,21 @@ TypehashGeneration:testPermitTransferFromWithWitness() (gas: 43369) TypehashGeneration:testPermitTransferFromWithWitnessIncorrectPermitData() (gas: 43430) TypehashGeneration:testPermitTransferFromWithWitnessIncorrectTypehashStub() (gas: 43833) MockPermit2Lib:testPermit2Code(address):(bool) (runs: 256, μ: 3025, ~: 3016) +NonceBitmapTest_ERC20:testHighNonces() (gas: 36142) +NonceBitmapTest_ERC20:testInvalidateFullWord() (gas: 63031) +NonceBitmapTest_ERC20:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30335, ~: 31035) +NonceBitmapTest_ERC20:testInvalidateNonzeroWord() (gas: 85489) +NonceBitmapTest_ERC20:testInvalidateTwoNoncesRandomly(uint248,uint256,uint256) (runs: 256, μ: 39067, ~: 39067) +NonceBitmapTest_ERC20:testLowNonces() (gas: 40894) +NonceBitmapTest_ERC20:testNonceWordBoundary() (gas: 42168) +NonceBitmapTest_ERC20:testUseTwoRandomNonces(uint256,uint256) (runs: 256, μ: 49018, ~: 51624) +NonceBitmapTest_ERC20:testUsingNonceTwiceFails(uint256) (runs: 256, μ: 21866, ~: 21889) +NonceBitmapTest_ERC721:testHighNonces() (gas: 36142) +NonceBitmapTest_ERC721:testInvalidateFullWord() (gas: 63031) +NonceBitmapTest_ERC721:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30335, ~: 31035) +NonceBitmapTest_ERC721:testInvalidateNonzeroWord() (gas: 85489) +NonceBitmapTest_ERC721:testInvalidateTwoNoncesRandomly(uint248,uint256,uint256) (runs: 256, μ: 39067, ~: 39067) +NonceBitmapTest_ERC721:testLowNonces() (gas: 40894) +NonceBitmapTest_ERC721:testNonceWordBoundary() (gas: 42168) +NonceBitmapTest_ERC721:testUseTwoRandomNonces(uint256,uint256) (runs: 256, μ: 49189, ~: 51624) +NonceBitmapTest_ERC721:testUsingNonceTwiceFails(uint256) (runs: 256, μ: 21866, ~: 21889) diff --git a/test/shared/BaseNonceBitmapTest.t.sol b/test/shared/BaseNonceBitmapTest.t.sol index 91c37b2f..a15af389 100644 --- a/test/shared/BaseNonceBitmapTest.t.sol +++ b/test/shared/BaseNonceBitmapTest.t.sol @@ -6,7 +6,7 @@ import {SafeERC20, IERC20, IERC20Permit} from "openzeppelin-contracts/contracts/ import {IMockPermit2} from "../mocks/MockPermit2.sol"; import {InvalidNonce} from "../../src/shared/PermitErrors.sol"; -contract BaseNonceBitmapTest is Test { +abstract contract BaseNonceBitmapTest is Test { IMockPermit2 permit2; function setUp() public virtual {} @@ -46,7 +46,7 @@ contract BaseNonceBitmapTest is Test { } function testInvalidateFullWord() public { - permit2.invalidateUnorderedNonces(0, 2 ** 256 - 1); + invalidateUnorderedNonces(0, 2 ** 256 - 1); vm.expectRevert(InvalidNonce.selector); permit2.useUnorderedNonce(address(this), 0); @@ -60,7 +60,7 @@ contract BaseNonceBitmapTest is Test { } function testInvalidateNonzeroWord() public { - permit2.invalidateUnorderedNonces(1, 2 ** 256 - 1); + invalidateUnorderedNonces(1, 2 ** 256 - 1); permit2.useUnorderedNonce(address(this), 0); permit2.useUnorderedNonce(address(this), 254); @@ -89,22 +89,25 @@ contract BaseNonceBitmapTest is Test { } function testInvalidateNoncesRandomly(uint248 wordPos, uint256 mask) public { - permit2.invalidateUnorderedNonces(wordPos, mask); - assertEq(mask, permit2.nonceBitmap(address(this), wordPos)); + invalidateUnorderedNonces(wordPos, mask); + assertEq(mask, nonceBitmap(address(this), wordPos)); } function testInvalidateTwoNoncesRandomly(uint248 wordPos, uint256 startBitmap, uint256 mask) public { - permit2.invalidateUnorderedNonces(wordPos, startBitmap); - assertEq(startBitmap, permit2.nonceBitmap(address(this), wordPos)); + invalidateUnorderedNonces(wordPos, startBitmap); + assertEq(startBitmap, nonceBitmap(address(this), wordPos)); // invalidating with the mask changes the original bitmap uint256 finalBitmap = startBitmap | mask; - permit2.invalidateUnorderedNonces(wordPos, mask); - uint256 savedBitmap = permit2.nonceBitmap(address(this), wordPos); + invalidateUnorderedNonces(wordPos, mask); + uint256 savedBitmap = nonceBitmap(address(this), wordPos); assertEq(finalBitmap, savedBitmap); // invalidating with the same mask should do nothing - permit2.invalidateUnorderedNonces(wordPos, mask); - assertEq(savedBitmap, permit2.nonceBitmap(address(this), wordPos)); + invalidateUnorderedNonces(wordPos, mask); + assertEq(savedBitmap, nonceBitmap(address(this), wordPos)); } + + function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) public virtual; + function nonceBitmap(address addr, uint256 wordPos) public virtual returns (uint256); } diff --git a/test/shared/NonceBitmapTest_ERC20.t.sol b/test/shared/NonceBitmapTest_ERC20.t.sol index 09d311f8..37796130 100644 --- a/test/shared/NonceBitmapTest_ERC20.t.sol +++ b/test/shared/NonceBitmapTest_ERC20.t.sol @@ -9,4 +9,12 @@ contract NonceBitmapTest_ERC20 is BaseNonceBitmapTest { function setUp() public override { permit2 = new MockPermit2(); } + + function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) public override { + MockPermit2(address(permit2)).invalidateUnorderedNonces(wordPos, mask); + } + + function nonceBitmap(address addr, uint256 wordPos) public override returns (uint256) { + return MockPermit2(address(permit2)).nonceBitmap(addr, wordPos); + } } diff --git a/test/shared/NonceBitmapTest_ERC721.t.sol b/test/shared/NonceBitmapTest_ERC721.t.sol index e39ad348..980b3b45 100644 --- a/test/shared/NonceBitmapTest_ERC721.t.sol +++ b/test/shared/NonceBitmapTest_ERC721.t.sol @@ -9,4 +9,12 @@ contract NonceBitmapTest_ERC721 is BaseNonceBitmapTest { function setUp() public override { permit2 = new MockPermit2_ERC721(); } + + function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) public override { + MockPermit2_ERC721(address(permit2)).invalidateUnorderedNonces(wordPos, mask); + } + + function nonceBitmap(address addr, uint256 wordPos) public override returns (uint256) { + return MockPermit2_ERC721(address(permit2)).nonceBitmap(addr, wordPos); + } } From e3d41f2f44ea8942fd00ada40acb7fa2fca9444c Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 6 Jan 2023 17:47:07 -0500 Subject: [PATCH 22/27] remove comment --- test/mocks/IMockPermit2.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/mocks/IMockPermit2.sol b/test/mocks/IMockPermit2.sol index 9c295b52..8db774c3 100644 --- a/test/mocks/IMockPermit2.sol +++ b/test/mocks/IMockPermit2.sol @@ -22,7 +22,4 @@ abstract contract IMockPermit2 { {} function useUnorderedNonce(address from, uint256 nonce) public virtual {} - - // stuck here bc this is defined at Permit2 but not the interface - // function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external {} } From ed80cdb3aff9686f5dcb4bbe8d41d34e68e04bdd Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Mon, 9 Jan 2023 08:26:40 -0700 Subject: [PATCH 23/27] shared allowance test --- test/shared/AllowanceUnitTest_ERC20.t.sol | 27 ++++++++++++++ test/shared/AllowanceUnitTest_ERC721.t.sol | 27 ++++++++++++++ .../BaseAllowanceUnitTest.sol} | 35 ++++++++++--------- 3 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 test/shared/AllowanceUnitTest_ERC20.t.sol create mode 100644 test/shared/AllowanceUnitTest_ERC721.t.sol rename test/{AllowanceUnitTest.sol => shared/BaseAllowanceUnitTest.sol} (63%) diff --git a/test/shared/AllowanceUnitTest_ERC20.t.sol b/test/shared/AllowanceUnitTest_ERC20.t.sol new file mode 100644 index 00000000..51e59338 --- /dev/null +++ b/test/shared/AllowanceUnitTest_ERC20.t.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "../mocks/MockPermit2.sol"; +import {BaseAllowanceUnitTest} from "./BaseAllowanceUnitTest.sol"; +import {TokenProvider} from "../utils/TokenProvider.sol"; + +contract AllowanceUnitTest_ERC20 is BaseAllowanceUnitTest { + function setUp() public override { + permit2 = new MockPermit2(); + initializeERC20Tokens(); + } + + function allowance(address from, address token, address spender) + public + view + override + returns (uint160, uint48, uint48) + { + return MockPermit2(address(permit2)).allowance(from, token, spender); + } + + function token() public view override returns (address) { + return address(token1); + } +} diff --git a/test/shared/AllowanceUnitTest_ERC721.t.sol b/test/shared/AllowanceUnitTest_ERC721.t.sol new file mode 100644 index 00000000..98c0368e --- /dev/null +++ b/test/shared/AllowanceUnitTest_ERC721.t.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "../mocks/MockPermit2_ERC721.sol"; +import {BaseAllowanceUnitTest} from "./BaseAllowanceUnitTest.sol"; +import {TokenProvider} from "../utils/TokenProvider.sol"; + +contract AllowanceUnitTest_ERC721 is BaseAllowanceUnitTest { + function setUp() public override { + permit2 = new MockPermit2_ERC721(); + initializeNFTTokens(); + } + + function allowance(address from, address token, address spender) + public + view + override + returns (uint160, uint48, uint48) + { + return MockPermit2_ERC721(address(permit2)).allowance(from, token, spender); + } + + function token() public view override returns (address) { + return address(nft1); + } +} diff --git a/test/AllowanceUnitTest.sol b/test/shared/BaseAllowanceUnitTest.sol similarity index 63% rename from test/AllowanceUnitTest.sol rename to test/shared/BaseAllowanceUnitTest.sol index 03e05f34..a2319f66 100644 --- a/test/AllowanceUnitTest.sol +++ b/test/shared/BaseAllowanceUnitTest.sol @@ -2,30 +2,31 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; -import "./mocks/MockPermit2.sol"; -import {TokenProvider} from "./utils/TokenProvider.sol"; +import "../mocks/IMockPermit2.sol"; +import {TokenProvider} from "../utils/TokenProvider.sol"; -contract AllowanceUnitTest is Test, TokenProvider { - MockPermit2 permit2; +abstract contract BaseAllowanceUnitTest is Test, TokenProvider { + IMockPermit2 permit2; address from = address(0xBEEE); address spender = address(0xBBBB); - function setUp() public { - permit2 = new MockPermit2(); - initializeERC20Tokens(); - } + function setUp() public virtual {} + + function allowance(address from, address token, address spender) public virtual returns (uint160, uint48, uint48); + + function token() public virtual returns (address); function testUpdateAmountExpirationRandomly(uint160 amount, uint48 expiration) public { - address token = address(token1); + address token = token(); - (,, uint48 nonce) = permit2.allowance(from, token, spender); + (,, uint48 nonce) = allowance(from, token, spender); permit2.mockUpdateSome(from, token, spender, amount, expiration); uint48 timestampAfterUpdate = expiration == 0 ? uint48(block.timestamp) : expiration; - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token, spender); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender); assertEq(amount, amount1); assertEq(timestampAfterUpdate, expiration1); /// nonce shouldnt change @@ -37,14 +38,14 @@ contract AllowanceUnitTest is Test, TokenProvider { // we assume we will never be able to reach 2**48 vm.assume(nonce < type(uint48).max); - address token = address(token1); + address token = token(); permit2.mockUpdateAll(from, token, spender, amount, expiration, nonce); uint48 nonceAfterUpdate = nonce + 1; uint48 timestampAfterUpdate = expiration == 0 ? uint48(block.timestamp) : expiration; - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, token, spender); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender); assertEq(amount, amount1); assertEq(timestampAfterUpdate, expiration1); @@ -54,18 +55,18 @@ contract AllowanceUnitTest is Test, TokenProvider { function testPackAndUnpack(uint160 amount, uint48 expiration, uint48 nonce) public { // pack some numbers uint256 word = Allowance.pack(amount, expiration, nonce); - + address token = token(); // store the raw word - permit2.doStore(from, address(token1), spender, word); + permit2.doStore(from, token, spender, word); // load it as a packed allowance - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2.allowance(from, address(token1), spender); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender); assertEq(amount, amount1); assertEq(expiration, expiration1); assertEq(nonce, nonce1); // get the stored word - uint256 word1 = permit2.getStore(from, address(token1), spender); + uint256 word1 = permit2.getStore(from, token, spender); assertEq(word, word1); } } From a377753d1a5f1a88f37aa6ef631980a260f1dfc6 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Tue, 10 Jan 2023 17:59:57 -0700 Subject: [PATCH 24/27] gas --- .gas-snapshot | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 8829d0d5..35c090cf 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -29,9 +29,6 @@ AllowanceTransferTest:testSetAllowanceInvalidSignature() (gas: 64071) AllowanceTransferTest:testSetAllowanceTransfer() (gas: 103161) AllowanceTransferTest:testSetAllowanceTransferDirtyNonceDirtyTransfer() (gas: 97432) AllowanceTransferTest:testTransferFromWithGasSnapshot() (gas: 133004) -AllowanceUnitTest:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38976, ~: 39054) -AllowanceUnitTest:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40200, ~: 40201) -AllowanceUnitTest:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39290, ~: 39291) CompactSignature:testCompactSignature27() (gas: 253) CompactSignature:testCompactSignature28() (gas: 141) EIP712Test:testDomainSeparator() (gas: 5804) @@ -98,6 +95,12 @@ TypehashGeneration:testPermitTransferFromWithWitness() (gas: 43369) TypehashGeneration:testPermitTransferFromWithWitnessIncorrectPermitData() (gas: 43430) TypehashGeneration:testPermitTransferFromWithWitnessIncorrectTypehashStub() (gas: 43833) MockPermit2Lib:testPermit2Code(address):(bool) (runs: 256, μ: 3025, ~: 3016) +AllowanceUnitTest_ERC20:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38723, ~: 38801) +AllowanceUnitTest_ERC20:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40213, ~: 40214) +AllowanceUnitTest_ERC20:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39326, ~: 39327) +AllowanceUnitTest_ERC721:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38723, ~: 38801) +AllowanceUnitTest_ERC721:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40213, ~: 40214) +AllowanceUnitTest_ERC721:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39326, ~: 39327) NonceBitmapTest_ERC20:testHighNonces() (gas: 36142) NonceBitmapTest_ERC20:testInvalidateFullWord() (gas: 63031) NonceBitmapTest_ERC20:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30335, ~: 31035) From 195c179630d1efb90b16644a7fcb057419fe2e1a Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Tue, 31 Jan 2023 18:56:33 -0500 Subject: [PATCH 25/27] rebase and cleanup --- .../{Permit2_ERC721.sol => Permit2ERC721.sol} | 0 .../interfaces/IAllowanceTransferERC721.sol | 2 + test/mocks/IMockPermit2.sol | 30 +++++++--- test/mocks/MockPermit2.sol | 39 ++++++++---- test/mocks/MockPermit2ERC721.sol | 59 +++++++++++++++++++ test/mocks/MockPermit2_ERC721.sol | 44 -------------- ...C20.t.sol => AllowanceUnitTestERC20.t.sol} | 5 +- ...21.t.sol => AllowanceUnitTestERC721.t.sol} | 14 +++-- test/shared/BaseAllowanceUnitTest.sol | 31 ++++++---- ...ERC20.t.sol => NonceBitmapTestERC20.t.sol} | 0 ...C721.t.sol => NonceBitmapTestERC721.t.sol} | 10 ++-- 11 files changed, 146 insertions(+), 88 deletions(-) rename src/ERC721/{Permit2_ERC721.sol => Permit2ERC721.sol} (100%) create mode 100644 test/mocks/MockPermit2ERC721.sol delete mode 100644 test/mocks/MockPermit2_ERC721.sol rename test/shared/{AllowanceUnitTest_ERC20.t.sol => AllowanceUnitTestERC20.t.sol} (82%) rename test/shared/{AllowanceUnitTest_ERC721.t.sol => AllowanceUnitTestERC721.t.sol} (56%) rename test/shared/{NonceBitmapTest_ERC20.t.sol => NonceBitmapTestERC20.t.sol} (100%) rename test/shared/{NonceBitmapTest_ERC721.t.sol => NonceBitmapTestERC721.t.sol} (53%) diff --git a/src/ERC721/Permit2_ERC721.sol b/src/ERC721/Permit2ERC721.sol similarity index 100% rename from src/ERC721/Permit2_ERC721.sol rename to src/ERC721/Permit2ERC721.sol diff --git a/src/ERC721/interfaces/IAllowanceTransferERC721.sol b/src/ERC721/interfaces/IAllowanceTransferERC721.sol index c2f87fd1..ea0a5ada 100644 --- a/src/ERC721/interfaces/IAllowanceTransferERC721.sol +++ b/src/ERC721/interfaces/IAllowanceTransferERC721.sol @@ -143,10 +143,12 @@ interface IAllowanceTransferERC721 { /// @notice A mapping from owner address to token address to tokenId to PackedAllowance struct, which contains details and conditions of the approval. /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][tokenId] /// @dev The packed slot holds the allowed spender, expiration at which the permissions on the tokenId is no longer valid, and current nonce thats updated on any signature based approvals. + /// @dev Setting the expiration to 0, sets the expiration to block.timestamp so the approval only lasts for the duration of the block. function allowance(address, address, uint256) external view returns (address, uint48, uint48); /// @notice A mapping from owner address to token address to spender address to a PackedOperatorAllowance struct, which contains the expiration of the operator approval. /// @notice The mapping is indexed in the above order see: operator[ownerAddress][tokenAddress][spenderAddress] + /// @dev Unlike the allowance mappings, setting the expiration to 0 just invalidates the operator allowance. It does NOT set the allowance to block.timestamp. function operators(address, address, address) external view returns (uint48, uint48); /// @notice Approves the spender to transfer the tokenId of the specified token up until the expiration diff --git a/test/mocks/IMockPermit2.sol b/test/mocks/IMockPermit2.sol index 8db774c3..87667827 100644 --- a/test/mocks/IMockPermit2.sol +++ b/test/mocks/IMockPermit2.sol @@ -7,19 +7,35 @@ import {ISignatureTransfer} from "../../src/ERC20/interfaces/ISignatureTransfer. import {Allowance} from "../../src/ERC20/libraries/Allowance.sol"; abstract contract IMockPermit2 { - function doStore(address from, address token, address spender, uint256 word) public virtual {} + // note that some parameters are unused in the erc20 and erc721 case but with this interface they can share some base tests - function getStore(address from, address token, address spender) public view virtual returns (uint256 word) {} + function doStore(address from, address token, address spender, uint256 tokenId, uint256 word) public virtual {} - function mockUpdateSome(address from, address token, address spender, uint160 data, uint48 expiration) + function getStore(address from, address token, address spender, uint256 tokenId) public + view virtual + returns (uint256 word) {} - function mockUpdateAll(address from, address token, address spender, uint160 data, uint48 expiration, uint48 nonce) - public - virtual - {} + function mockUpdateSome( + address from, + address token, + address spender, + uint160 updateData, + uint256 tokenId, + uint48 expiration + ) public virtual {} + + function mockUpdateAll( + address from, + address token, + address spender, + uint160 updateData, + uint256 tokenId, + uint48 expiration, + uint48 nonce + ) public virtual {} function useUnorderedNonce(address from, uint256 nonce) public virtual {} } diff --git a/test/mocks/MockPermit2.sol b/test/mocks/MockPermit2.sol index 002acea4..d1ecadb0 100644 --- a/test/mocks/MockPermit2.sol +++ b/test/mocks/MockPermit2.sol @@ -7,34 +7,49 @@ import {Allowance} from "../../src/ERC20/libraries/Allowance.sol"; import {IMockPermit2} from "../mocks/IMockPermit2.sol"; contract MockPermit2 is IMockPermit2, Permit2 { - function doStore(address from, address token, address spender, uint256 word) public override { + function doStore(address from, address token, address spender, uint256 tokenId, uint256 word) public override { IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; assembly { sstore(allowed.slot, word) } } - function getStore(address from, address token, address spender) public view override returns (uint256 word) { + function getStore(address from, address token, address spender, uint256 tokenId) + public + view + override + returns (uint256 word) + { IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; assembly { word := sload(allowed.slot) } } - function mockUpdateSome(address from, address token, address spender, uint160 data, uint48 expiration) - public - override - { + function mockUpdateSome( + address from, + address token, + address spender, + uint160 updateData, + uint256 tokenId, + uint48 expiration + ) public override { + // uint256 tokenId unused IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; - Allowance.updateAmountAndExpiration(allowed, data, expiration); + Allowance.updateAmountAndExpiration(allowed, updateData, expiration); } - function mockUpdateAll(address from, address token, address spender, uint160 data, uint48 expiration, uint48 nonce) - public - override - { + function mockUpdateAll( + address from, + address token, + address spender, + uint160 updateData, + uint256 tokenId, + uint48 expiration, + uint48 nonce + ) public override { IAllowanceTransfer.PackedAllowance storage allowed = allowance[from][token][spender]; - Allowance.updateAll(allowed, data, expiration, nonce); + Allowance.updateAll(allowed, updateData, expiration, nonce); } function useUnorderedNonce(address from, uint256 nonce) public override { diff --git a/test/mocks/MockPermit2ERC721.sol b/test/mocks/MockPermit2ERC721.sol new file mode 100644 index 00000000..01fe93b0 --- /dev/null +++ b/test/mocks/MockPermit2ERC721.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {Permit2ERC721} from "../../src/ERC721/Permit2ERC721.sol"; +import {IAllowanceTransferERC721} from "../../src/ERC721/interfaces/IAllowanceTransferERC721.sol"; +import {SignatureTransferERC721} from "../../src/ERC721/SignatureTransferERC721.sol"; +import {AllowanceERC721} from "../../src/ERC721/libraries/AllowanceERC721.sol"; +import {IMockPermit2} from "../mocks/IMockPermit2.sol"; + +contract MockPermit2ERC721 is IMockPermit2, Permit2ERC721 { + function doStore(address from, address token, address spender, uint256 tokenId, uint256 word) public override { + IAllowanceTransferERC721.PackedAllowance storage allowed = allowance[from][token][tokenId]; + assembly { + sstore(allowed.slot, word) + } + } + + function getStore(address from, address token, address spender, uint256 tokenId) + public + view + override + returns (uint256 word) + { + IAllowanceTransferERC721.PackedAllowance storage allowed = allowance[from][token][tokenId]; + assembly { + word := sload(allowed.slot) + } + } + + function mockUpdateSome( + address from, + address token, + address spender, + uint160 updateData, + uint256 tokenId, + uint48 expiration + ) public override { + // spender input unused in 721 case + IAllowanceTransferERC721.PackedAllowance storage allowed = allowance[from][token][tokenId]; + AllowanceERC721.updateSpenderAndExpiration(allowed, address(updateData), expiration); + } + + function mockUpdateAll( + address from, + address token, + address spender, + uint160 updateData, + uint256 tokenId, + uint48 expiration, + uint48 nonce + ) public override { + IAllowanceTransferERC721.PackedAllowance storage allowed = allowance[from][token][tokenId]; + AllowanceERC721.updateAll(allowed, address(updateData), expiration, nonce); + } + + function useUnorderedNonce(address from, uint256 nonce) public override { + _useUnorderedNonce(from, nonce); + } +} diff --git a/test/mocks/MockPermit2_ERC721.sol b/test/mocks/MockPermit2_ERC721.sol deleted file mode 100644 index 27d38185..00000000 --- a/test/mocks/MockPermit2_ERC721.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import {Permit2_ERC721} from "../../src/ERC721/Permit2_ERC721.sol"; -import {IAllowanceTransfer_ERC721} from "../../src/ERC721/interfaces/IAllowanceTransfer_ERC721.sol"; -import {SignatureTransfer_ERC721} from "../../src/ERC721/SignatureTransfer_ERC721.sol"; -import {Allowance_ERC721} from "../../src/ERC721/libraries/Allowance_ERC721.sol"; -import {IMockPermit2} from "../mocks/IMockPermit2.sol"; - -contract MockPermit2_ERC721 is IMockPermit2, Permit2_ERC721 { - function doStore(address from, address token, address spender, uint256 word) public override { - IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; - assembly { - sstore(allowed.slot, word) - } - } - - function getStore(address from, address token, address spender) public view override returns (uint256 word) { - IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; - assembly { - word := sload(allowed.slot) - } - } - - function mockUpdateSome(address from, address token, address spender, uint160 data, uint48 expiration) - public - override - { - IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; - Allowance_ERC721.updateTokenIdAndExpiration(allowed, data, expiration); - } - - function mockUpdateAll(address from, address token, address spender, uint160 data, uint48 expiration, uint48 nonce) - public - override - { - IAllowanceTransfer_ERC721.PackedAllowance storage allowed = allowance[from][token][spender]; - Allowance_ERC721.updateAll(allowed, data, expiration, nonce); - } - - function useUnorderedNonce(address from, uint256 nonce) public override { - _useUnorderedNonce(from, nonce); - } -} diff --git a/test/shared/AllowanceUnitTest_ERC20.t.sol b/test/shared/AllowanceUnitTestERC20.t.sol similarity index 82% rename from test/shared/AllowanceUnitTest_ERC20.t.sol rename to test/shared/AllowanceUnitTestERC20.t.sol index 51e59338..03896fad 100644 --- a/test/shared/AllowanceUnitTest_ERC20.t.sol +++ b/test/shared/AllowanceUnitTestERC20.t.sol @@ -5,14 +5,15 @@ import "forge-std/Test.sol"; import "../mocks/MockPermit2.sol"; import {BaseAllowanceUnitTest} from "./BaseAllowanceUnitTest.sol"; import {TokenProvider} from "../utils/TokenProvider.sol"; +import {Allowance} from "../../src/ERC20/libraries/Allowance.sol"; -contract AllowanceUnitTest_ERC20 is BaseAllowanceUnitTest { +contract AllowanceUnitTestERC20 is BaseAllowanceUnitTest { function setUp() public override { permit2 = new MockPermit2(); initializeERC20Tokens(); } - function allowance(address from, address token, address spender) + function allowance(address from, address token, address spender, uint256 tokenId) public view override diff --git a/test/shared/AllowanceUnitTest_ERC721.t.sol b/test/shared/AllowanceUnitTestERC721.t.sol similarity index 56% rename from test/shared/AllowanceUnitTest_ERC721.t.sol rename to test/shared/AllowanceUnitTestERC721.t.sol index 98c0368e..c10ae37e 100644 --- a/test/shared/AllowanceUnitTest_ERC721.t.sol +++ b/test/shared/AllowanceUnitTestERC721.t.sol @@ -1,24 +1,26 @@ -// SPDX-License-Identifier: MIT +// // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "forge-std/Test.sol"; -import "../mocks/MockPermit2_ERC721.sol"; +import "../mocks/MockPermit2ERC721.sol"; import {BaseAllowanceUnitTest} from "./BaseAllowanceUnitTest.sol"; import {TokenProvider} from "../utils/TokenProvider.sol"; -contract AllowanceUnitTest_ERC721 is BaseAllowanceUnitTest { +contract AllowanceUnitTestERC721 is BaseAllowanceUnitTest { function setUp() public override { - permit2 = new MockPermit2_ERC721(); + permit2 = new MockPermit2ERC721(); initializeNFTTokens(); } - function allowance(address from, address token, address spender) + function allowance(address from, address token, address spender, uint256 tokenId) public view override returns (uint160, uint48, uint48) { - return MockPermit2_ERC721(address(permit2)).allowance(from, token, spender); + (address spender1, uint48 expiration1, uint48 nonce1) = + MockPermit2ERC721(address(permit2)).allowance(from, token, tokenId); + return (uint160(spender1), expiration1, nonce1); } function token() public view override returns (address) { diff --git a/test/shared/BaseAllowanceUnitTest.sol b/test/shared/BaseAllowanceUnitTest.sol index a2319f66..2aa0fb6e 100644 --- a/test/shared/BaseAllowanceUnitTest.sol +++ b/test/shared/BaseAllowanceUnitTest.sol @@ -10,23 +10,28 @@ abstract contract BaseAllowanceUnitTest is Test, TokenProvider { address from = address(0xBEEE); address spender = address(0xBBBB); + uint256 tokenId = 0; function setUp() public virtual {} - function allowance(address from, address token, address spender) public virtual returns (uint160, uint48, uint48); + function allowance(address from, address token, address spender, uint256 tokenId) + public + virtual + returns (uint160, uint48, uint48); function token() public virtual returns (address); - function testUpdateAmountExpirationRandomly(uint160 amount, uint48 expiration) public { + function testUpdateSomeRandomly(uint160 amount, uint48 expiration) public { address token = token(); - (,, uint48 nonce) = allowance(from, token, spender); + (,, uint48 nonce) = allowance(from, token, spender, tokenId); - permit2.mockUpdateSome(from, token, spender, amount, expiration); + // erc721s will update the spender field to the amount and will not use the spender input + permit2.mockUpdateSome(from, token, spender, amount, tokenId, expiration); uint48 timestampAfterUpdate = expiration == 0 ? uint48(block.timestamp) : expiration; - (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender, tokenId); assertEq(amount, amount1); assertEq(timestampAfterUpdate, expiration1); /// nonce shouldnt change @@ -40,12 +45,12 @@ abstract contract BaseAllowanceUnitTest is Test, TokenProvider { address token = token(); - permit2.mockUpdateAll(from, token, spender, amount, expiration, nonce); + permit2.mockUpdateAll(from, token, spender, amount, tokenId, expiration, nonce); uint48 nonceAfterUpdate = nonce + 1; uint48 timestampAfterUpdate = expiration == 0 ? uint48(block.timestamp) : expiration; - (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender, tokenId); assertEq(amount, amount1); assertEq(timestampAfterUpdate, expiration1); @@ -54,19 +59,21 @@ abstract contract BaseAllowanceUnitTest is Test, TokenProvider { function testPackAndUnpack(uint160 amount, uint48 expiration, uint48 nonce) public { // pack some numbers - uint256 word = Allowance.pack(amount, expiration, nonce); address token = token(); + uint256 word = Allowance.pack(amount, expiration, nonce); + // store the raw word - permit2.doStore(from, token, spender, word); + permit2.doStore(from, token, spender, tokenId, word); + uint256 word1 = permit2.getStore(from, token, spender, tokenId); // load it as a packed allowance - (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = allowance(from, token, spender, tokenId); assertEq(amount, amount1); assertEq(expiration, expiration1); assertEq(nonce, nonce1); // get the stored word - uint256 word1 = permit2.getStore(from, token, spender); - assertEq(word, word1); + uint256 word2 = permit2.getStore(from, token, spender, tokenId); + assertEq(word, word2); } } diff --git a/test/shared/NonceBitmapTest_ERC20.t.sol b/test/shared/NonceBitmapTestERC20.t.sol similarity index 100% rename from test/shared/NonceBitmapTest_ERC20.t.sol rename to test/shared/NonceBitmapTestERC20.t.sol diff --git a/test/shared/NonceBitmapTest_ERC721.t.sol b/test/shared/NonceBitmapTestERC721.t.sol similarity index 53% rename from test/shared/NonceBitmapTest_ERC721.t.sol rename to test/shared/NonceBitmapTestERC721.t.sol index 980b3b45..effc7af6 100644 --- a/test/shared/NonceBitmapTest_ERC721.t.sol +++ b/test/shared/NonceBitmapTestERC721.t.sol @@ -3,18 +3,18 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {BaseNonceBitmapTest} from "./BaseNonceBitmapTest.t.sol"; -import {MockPermit2_ERC721} from "../mocks/MockPermit2_ERC721.sol"; +import {MockPermit2ERC721} from "../mocks/MockPermit2ERC721.sol"; -contract NonceBitmapTest_ERC721 is BaseNonceBitmapTest { +contract NonceBitmapTestERC721 is BaseNonceBitmapTest { function setUp() public override { - permit2 = new MockPermit2_ERC721(); + permit2 = new MockPermit2ERC721(); } function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) public override { - MockPermit2_ERC721(address(permit2)).invalidateUnorderedNonces(wordPos, mask); + MockPermit2ERC721(address(permit2)).invalidateUnorderedNonces(wordPos, mask); } function nonceBitmap(address addr, uint256 wordPos) public override returns (uint256) { - return MockPermit2_ERC721(address(permit2)).nonceBitmap(addr, wordPos); + return MockPermit2ERC721(address(permit2)).nonceBitmap(addr, wordPos); } } From cce91d844585ac3f5a1ab817efd61b721b4a527e Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Tue, 31 Jan 2023 20:03:17 -0500 Subject: [PATCH 26/27] gas --- .gas-snapshot | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 35c090cf..9ee3a719 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -95,27 +95,27 @@ TypehashGeneration:testPermitTransferFromWithWitness() (gas: 43369) TypehashGeneration:testPermitTransferFromWithWitnessIncorrectPermitData() (gas: 43430) TypehashGeneration:testPermitTransferFromWithWitnessIncorrectTypehashStub() (gas: 43833) MockPermit2Lib:testPermit2Code(address):(bool) (runs: 256, μ: 3025, ~: 3016) -AllowanceUnitTest_ERC20:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38723, ~: 38801) -AllowanceUnitTest_ERC20:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40213, ~: 40214) -AllowanceUnitTest_ERC20:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39326, ~: 39327) -AllowanceUnitTest_ERC721:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 38723, ~: 38801) -AllowanceUnitTest_ERC721:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 40213, ~: 40214) -AllowanceUnitTest_ERC721:testUpdateAmountExpirationRandomly(uint160,uint48) (runs: 256, μ: 39326, ~: 39327) +AllowanceUnitTestERC20:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 42090, ~: 42168) +AllowanceUnitTestERC20:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 42620, ~: 42621) +AllowanceUnitTestERC20:testUpdateSomeRandomly(uint160,uint48) (runs: 256, μ: 41234, ~: 41235) +AllowanceUnitTestERC721:testPackAndUnpack(uint160,uint48,uint48) (runs: 256, μ: 41651, ~: 41729) +AllowanceUnitTestERC721:testUpdateAllRandomly(uint160,uint48,uint48) (runs: 256, μ: 42456, ~: 42457) +AllowanceUnitTestERC721:testUpdateSomeRandomly(uint160,uint48) (runs: 256, μ: 40723, ~: 40724) NonceBitmapTest_ERC20:testHighNonces() (gas: 36142) NonceBitmapTest_ERC20:testInvalidateFullWord() (gas: 63031) -NonceBitmapTest_ERC20:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30335, ~: 31035) +NonceBitmapTest_ERC20:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30313, ~: 31013) NonceBitmapTest_ERC20:testInvalidateNonzeroWord() (gas: 85489) -NonceBitmapTest_ERC20:testInvalidateTwoNoncesRandomly(uint248,uint256,uint256) (runs: 256, μ: 39067, ~: 39067) +NonceBitmapTest_ERC20:testInvalidateTwoNoncesRandomly(uint248,uint256,uint256) (runs: 256, μ: 39001, ~: 39001) NonceBitmapTest_ERC20:testLowNonces() (gas: 40894) NonceBitmapTest_ERC20:testNonceWordBoundary() (gas: 42168) -NonceBitmapTest_ERC20:testUseTwoRandomNonces(uint256,uint256) (runs: 256, μ: 49018, ~: 51624) +NonceBitmapTest_ERC20:testUseTwoRandomNonces(uint256,uint256) (runs: 256, μ: 49104, ~: 51624) NonceBitmapTest_ERC20:testUsingNonceTwiceFails(uint256) (runs: 256, μ: 21866, ~: 21889) -NonceBitmapTest_ERC721:testHighNonces() (gas: 36142) -NonceBitmapTest_ERC721:testInvalidateFullWord() (gas: 63031) -NonceBitmapTest_ERC721:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30335, ~: 31035) -NonceBitmapTest_ERC721:testInvalidateNonzeroWord() (gas: 85489) -NonceBitmapTest_ERC721:testInvalidateTwoNoncesRandomly(uint248,uint256,uint256) (runs: 256, μ: 39067, ~: 39067) -NonceBitmapTest_ERC721:testLowNonces() (gas: 40894) -NonceBitmapTest_ERC721:testNonceWordBoundary() (gas: 42168) -NonceBitmapTest_ERC721:testUseTwoRandomNonces(uint256,uint256) (runs: 256, μ: 49189, ~: 51624) -NonceBitmapTest_ERC721:testUsingNonceTwiceFails(uint256) (runs: 256, μ: 21866, ~: 21889) +NonceBitmapTestERC721:testHighNonces() (gas: 36362) +NonceBitmapTestERC721:testInvalidateFullWord() (gas: 63251) +NonceBitmapTestERC721:testInvalidateNoncesRandomly(uint248,uint256) (runs: 256, μ: 30203, ~: 30903) +NonceBitmapTestERC721:testInvalidateNonzeroWord() (gas: 85764) +NonceBitmapTestERC721:testInvalidateTwoNoncesRandomly(uint248,uint256,uint256) (runs: 256, μ: 38671, ~: 38671) +NonceBitmapTestERC721:testLowNonces() (gas: 41279) +NonceBitmapTestERC721:testNonceWordBoundary() (gas: 42344) +NonceBitmapTestERC721:testUseTwoRandomNonces(uint256,uint256) (runs: 256, μ: 49299, ~: 51734) +NonceBitmapTestERC721:testUsingNonceTwiceFails(uint256) (runs: 256, μ: 21954, ~: 21977) From 3d90fbf402a0f2de72f4cd45631bd41d2148d6e8 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Fri, 3 Feb 2023 15:19:00 -0500 Subject: [PATCH 27/27] some shared structure, 721 not workin tho rn --- .forge-snapshots/permitBatchCleanWrite.snap | 2 +- .forge-snapshots/permitCleanWrite.snap | 2 +- .forge-snapshots/permitCompactSig.snap | 2 +- .forge-snapshots/permitInvalidSigner.snap | 2 +- .forge-snapshots/permitSignatureExpired.snap | 2 +- src/ERC721/AllowanceTransferERC721.sol | 1 + test/AllowanceTransferTestERC20.t.sol | 28 ++- test/AllowanceTransferTestERC721.t.sol | 219 +++++++++++++++++++ test/BaseAllowanceTransferTest.t.sol | 86 +++++--- test/SignatureTransfer.t.sol | 6 +- test/shared/AllowanceUnitTestERC20.t.sol | 2 +- test/shared/AllowanceUnitTestERC721.t.sol | 2 +- test/utils/TokenProviderERC20.sol | 6 +- test/utils/TokenProviderERC721.sol | 14 +- 14 files changed, 317 insertions(+), 57 deletions(-) create mode 100644 test/AllowanceTransferTestERC721.t.sol diff --git a/.forge-snapshots/permitBatchCleanWrite.snap b/.forge-snapshots/permitBatchCleanWrite.snap index 99332dc5..2f15d30a 100644 --- a/.forge-snapshots/permitBatchCleanWrite.snap +++ b/.forge-snapshots/permitBatchCleanWrite.snap @@ -1 +1 @@ -94536 \ No newline at end of file +94475 \ No newline at end of file diff --git a/.forge-snapshots/permitCleanWrite.snap b/.forge-snapshots/permitCleanWrite.snap index d439ef4e..e7954ab8 100644 --- a/.forge-snapshots/permitCleanWrite.snap +++ b/.forge-snapshots/permitCleanWrite.snap @@ -1 +1 @@ -63449 \ No newline at end of file +63299 \ No newline at end of file diff --git a/.forge-snapshots/permitCompactSig.snap b/.forge-snapshots/permitCompactSig.snap index c02900fb..af02b0f3 100644 --- a/.forge-snapshots/permitCompactSig.snap +++ b/.forge-snapshots/permitCompactSig.snap @@ -1 +1 @@ -63423 \ No newline at end of file +63273 \ No newline at end of file diff --git a/.forge-snapshots/permitInvalidSigner.snap b/.forge-snapshots/permitInvalidSigner.snap index 1aae61b3..2a49a1b7 100644 --- a/.forge-snapshots/permitInvalidSigner.snap +++ b/.forge-snapshots/permitInvalidSigner.snap @@ -1 +1 @@ -40647 \ No newline at end of file +40554 \ No newline at end of file diff --git a/.forge-snapshots/permitSignatureExpired.snap b/.forge-snapshots/permitSignatureExpired.snap index 6b16f5d7..232a1d35 100644 --- a/.forge-snapshots/permitSignatureExpired.snap +++ b/.forge-snapshots/permitSignatureExpired.snap @@ -1 +1 @@ -32039 \ No newline at end of file +31952 \ No newline at end of file diff --git a/src/ERC721/AllowanceTransferERC721.sol b/src/ERC721/AllowanceTransferERC721.sol index 392ff5c2..24433678 100644 --- a/src/ERC721/AllowanceTransferERC721.sol +++ b/src/ERC721/AllowanceTransferERC721.sol @@ -8,6 +8,7 @@ import {EIP712ERC721} from "./EIP712ERC721.sol"; import {IAllowanceTransferERC721} from "./interfaces/IAllowanceTransferERC721.sol"; import {SignatureExpired, InvalidNonce} from "../shared/PermitErrors.sol"; import {AllowanceERC721} from "./libraries/AllowanceERC721.sol"; +import "forge-std/console2.sol"; contract AllowanceTransferERC721 is IAllowanceTransferERC721, EIP712ERC721 { using SignatureVerification for bytes; diff --git a/test/AllowanceTransferTestERC20.t.sol b/test/AllowanceTransferTestERC20.t.sol index ad21a0ad..812b38d8 100644 --- a/test/AllowanceTransferTestERC20.t.sol +++ b/test/AllowanceTransferTestERC20.t.sol @@ -26,13 +26,13 @@ contract AllowanceTransferTestERC20 is TokenProviderERC20, BaseAllowanceTransfer fromPrivateKeyDirty = 0x56785678; fromDirty = vm.addr(fromPrivateKeyDirty); - initializeERC20Tokens(); + initializeTokens(); - setERC20TestTokens(from); - setERC20TestTokenApprovals(vm, from, address(permit2)); + setTokens(from); + setTokenApprovals(vm, from, address(permit2)); - setERC20TestTokens(fromDirty); - setERC20TestTokenApprovals(vm, fromDirty, address(permit2)); + setTokens(fromDirty); + setTokenApprovals(vm, fromDirty, address(permit2)); // dirty the nonce for fromDirty address on token0 and token1 vm.startPrank(fromDirty); @@ -44,16 +44,26 @@ contract AllowanceTransferTestERC20 is TokenProviderERC20, BaseAllowanceTransfer MockERC20(token1()).mint(address3, defaultAmountOrId); } - function permit2Approve(address token, address spender, uint160 amountOrId, uint48 expiration) public override { - Permit2(permit2).approve(token, spender, amountOrId, expiration); + function getAmountOrId() public override returns (uint256) { + // default amount + return defaultAmountOrId; } - function permit2Allowance(address from, address token, address spender) + function getExpectedAmountOrSpender() public override returns (uint160) { + // expected amount is the defaultAmountOrId + return uint160(defaultAmountOrId); + } + + function permit2Approve(address token, address spender, uint256 amountOrId, uint48 expiration) public override { + Permit2(permit2).approve(token, spender, uint160(amountOrId), expiration); + } + + function permit2Allowance(address from, address token, uint256 tokenIdOrSpender) public override returns (uint160, uint48, uint48) { - return Permit2(permit2).allowance(from, token, spender); + return Permit2(permit2).allowance(from, token, address(uint160(tokenIdOrSpender))); } function permit2Permit(address from, PermitAbstraction.IPermitSingle memory permit, bytes memory sig) diff --git a/test/AllowanceTransferTestERC721.t.sol b/test/AllowanceTransferTestERC721.t.sol new file mode 100644 index 00000000..eacc2229 --- /dev/null +++ b/test/AllowanceTransferTestERC721.t.sol @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {BaseAllowanceTransferTest} from "./BaseAllowanceTransferTest.t.sol"; +import {Permit2ERC721} from "../src/ERC721/Permit2ERC721.sol"; +import {TokenProviderERC721} from "./utils/TokenProviderERC721.sol"; +import {PermitHashERC721} from "../src/ERC721/libraries/PermitHashERC721.sol"; +import {PermitAbstraction} from "./utils/PermitAbstraction.sol"; +import {PermitSignature} from "./utils/PermitSignature.sol"; +import {IAllowanceTransferERC721} from "../src/ERC721/interfaces/IAllowanceTransferERC721.sol"; +import {MockERC20} from "./mocks/MockERC20.sol"; + +contract AllowanceTransferTestERC721 is TokenProviderERC721, BaseAllowanceTransferTest { + uint256 currentId = 1; + + function setUp() public override { + permit2 = address(new Permit2ERC721()); + DOMAIN_SEPARATOR = Permit2ERC721(permit2).DOMAIN_SEPARATOR(); + + uint256 mintAmount = 10 ** 18; + // amount for ERC20s + defaultAmountOrId = 0; + + fromPrivateKey = 0x12341234; + from = vm.addr(fromPrivateKey); + + // Use this address to gas test dirty writes later. + fromPrivateKeyDirty = 0x56785678; + fromDirty = vm.addr(fromPrivateKeyDirty); + + initializeTokens(); + + setToken0(from); + setTokenApprovals0(vm, from, address(permit2)); + + setToken1(fromDirty); + setTokenApprovals1(vm, fromDirty, address(permit2)); + + // dirty the nonce for fromDirty address on token0 and token1 + // use token1 for fromDirty tests + vm.startPrank(fromDirty); + Permit2ERC721(permit2).invalidateNonces(token1(), address(this), 1); + vm.stopPrank(); + } + + function getExpectedAmountOrSpender() public override returns (uint160) { + // spender is this address + return uint160(address(this)); + } + + function setAmountOrId(uint256 id) public override { + currentId = id; + } + + function getAmountOrId() public override returns (uint256) { + // tokenId for token0 is 1 + return currentId; + } + + function permit2Approve(address token, address spender, uint256 amountOrId, uint48 expiration) public override { + Permit2ERC721(permit2).approve(token, spender, amountOrId, expiration); + } + + function permit2Allowance(address from, address token, uint256 tokenIdOrSpender) + public + override + returns (uint160, uint48, uint48) + { + (address spender1, uint48 expiration1, uint48 nonce1) = + Permit2ERC721(address(permit2)).allowance(from, token, getAmountOrId()); + return (uint160(spender1), expiration1, nonce1); + } + + function permit2Permit(address from, PermitAbstraction.IPermitSingle memory permit, bytes memory sig) + public + override + { + // convert IPermitSingle to AllowanceTransfer.PermitSingle + IAllowanceTransferERC721.PermitSingle memory parsedPermit = IAllowanceTransferERC721.PermitSingle({ + details: IAllowanceTransferERC721.PermitDetails({ + token: permit.token, + tokenId: permit.amountOrId, + expiration: permit.expiration, + nonce: permit.nonce + }), + spender: permit.spender, + sigDeadline: permit.sigDeadline + }); + + Permit2ERC721(permit2).permit(from, parsedPermit, sig); + } + + function permit2Permit(address from, PermitAbstraction.IPermitBatch memory permitBatch, bytes memory sig) + public + override + { + // convert IPermitBatch to IAllowanceTransferERC721.PermitBatch + + IAllowanceTransferERC721.PermitDetails[] memory details = + new IAllowanceTransferERC721.PermitDetails[](permitBatch.tokens.length); + for (uint256 i = 0; i < details.length; i++) { + details[i] = IAllowanceTransferERC721.PermitDetails({ + token: permitBatch.tokens[i], + tokenId: permitBatch.amountOrIds[i], + expiration: permitBatch.expirations[i], + nonce: permitBatch.nonces[i] + }); + } + IAllowanceTransferERC721.PermitBatch memory parsedPermitBatch = IAllowanceTransferERC721.PermitBatch({ + details: details, + spender: permitBatch.spender, + sigDeadline: permitBatch.sigDeadline + }); + + Permit2ERC721(permit2).permit(from, parsedPermitBatch, sig); + } + + function permit2TransferFrom(address from, address to, uint160 amountOrId, address token) public override { + Permit2ERC721(permit2).transferFrom(from, to, amountOrId, token); + } + + function token0() public view override returns (address) { + return address(_token0); + } + + function token1() public view override returns (address) { + return address(_token1); + } + + function balanceOf(address token, address from) public override returns (uint256) { + return MockERC20(token).balanceOf(from); + } + + function getPermitSignature(IPermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator) + public + override + returns (bytes memory sig) + { + (uint8 v, bytes32 r, bytes32 s) = getPermitSignatureRaw(permit, privateKey, domainSeparator); + return bytes.concat(r, s, bytes1(v)); + } + + function getCompactPermitSignature(IPermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator) + public + override + returns (bytes memory sig) + { + (uint8 v, bytes32 r, bytes32 s) = getPermitSignatureRaw(permit, privateKey, domainSeparator); + bytes32 vs; + (r, vs) = getCompactSignature(v, r, s); + return bytes.concat(r, vs); + } + + function getPermitSignatureRaw(IPermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator) + internal + returns (uint8 v, bytes32 r, bytes32 s) + { + // convert IPermitSingle to permit details & hash + bytes32 permitHash = keccak256( + abi.encode( + PermitHashERC721._PERMIT_DETAILS_TYPEHASH, + permit.token, + permit.amountOrId, + permit.expiration, + permit.nonce + ) + ); + + bytes32 msgHash = keccak256( + abi.encodePacked( + "\x19\x01", + domainSeparator, + keccak256( + abi.encode(PermitHashERC721._PERMIT_SINGLE_TYPEHASH, permitHash, permit.spender, permit.sigDeadline) + ) + ) + ); + + (v, r, s) = vm.sign(privateKey, msgHash); + } + + function getPermitBatchSignature(IPermitBatch memory permit, uint256 privateKey, bytes32 domainSeparator) + public + override + returns (bytes memory) + { + bytes32[] memory permitHashes = new bytes32[](permit.tokens.length); + for (uint256 i = 0; i < permit.tokens.length; ++i) { + permitHashes[i] = keccak256( + abi.encode( + PermitHashERC721._PERMIT_DETAILS_TYPEHASH, + permit.tokens[i], + permit.amountOrIds[i], + permit.expirations[i], + permit.nonces[i] + ) + ); + } + + bytes32 msgHash = keccak256( + abi.encodePacked( + "\x19\x01", + domainSeparator, + keccak256( + abi.encode( + PermitHashERC721._PERMIT_BATCH_TYPEHASH, + keccak256(abi.encodePacked(permitHashes)), + permit.spender, + permit.sigDeadline + ) + ) + ) + ); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, msgHash); + return bytes.concat(r, s, bytes1(v)); + } +} diff --git a/test/BaseAllowanceTransferTest.t.sol b/test/BaseAllowanceTransferTest.t.sol index 4fe96933..00fe53ce 100644 --- a/test/BaseAllowanceTransferTest.t.sol +++ b/test/BaseAllowanceTransferTest.t.sol @@ -53,6 +53,8 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst // has some balance of token0 address address3 = address(3); + address spender = address(this); + bytes32 DOMAIN_SEPARATOR; function setUp() public virtual; @@ -77,9 +79,9 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst virtual returns (bytes memory); - function permit2Approve(address token, address spender, uint160 amountOrId, uint48 expiration) public virtual; + function permit2Approve(address token, address spender, uint256 amountOrId, uint48 expiration) public virtual; - function permit2Allowance(address from, address token, address spender) + function permit2Allowance(address from, address token, uint256 tokenIdOrSpender) public virtual returns (uint160, uint48, uint48); @@ -94,18 +96,27 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst function permit2TransferFrom(address from, address to, uint160 amount, address token) public virtual; + function getExpectedAmountOrSpender() public virtual returns (uint160); + function getAmountOrId() public virtual returns (uint256); + function testApprove() public { vm.prank(from); - vm.expectEmit(true, true, true, true); - emit Approval(from, token0(), address(this), defaultAmountOrId, defaultExpiration); - permit2Approve(token0(), address(this), defaultAmountOrId, defaultExpiration); + // vm.expectEmit(true, true, true, true); + // emit Approval(from, token0(), address(this), getAmountOrId(), defaultExpiration); + permit2Approve(token0(), address(this), getAmountOrId(), defaultExpiration); - (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); - assertEq(amount, defaultAmountOrId); + (uint160 amountOrSpender, uint48 expiration, uint48 nonce) = + permit2Allowance(from, token0(), uint256(uint160(address(this)))); + + // for erc20s the amountOrSpender should be compared to defaultAmountOrId + // for erc721s the amountOrSpender should be compared to spender + assertEq(amountOrSpender, getExpectedAmountOrSpender()); assertEq(expiration, defaultExpiration); assertEq(nonce, 0); } + function setAmountOrId(uint256 id) public virtual {} + function testSetAllowance() public { PermitAbstraction.IPermitSingle memory permit = defaultPermitAllowance(token0(), defaultAmountOrId, defaultExpiration, defaultNonce); @@ -115,7 +126,8 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(from, permit, sig); snapEnd(); - (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + (uint160 amount, uint48 expiration, uint48 nonce) = + permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); @@ -131,7 +143,8 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(from, permit, sig); snapEnd(); - (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + (uint160 amount, uint48 expiration, uint48 nonce) = + permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); @@ -157,7 +170,8 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(fromDirty, permit, sig); snapEnd(); - (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(fromDirty, token0(), address(this)); + (uint160 amount, uint48 expiration, uint48 nonce) = + permit2Allowance(fromDirty, token1(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 2); @@ -170,7 +184,8 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(from, permit, sig); - (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + (uint160 amount, uint48 expiration, uint48 nonce) = + permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); @@ -184,11 +199,12 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(from, permitBatch, sig1); - (amount, expiration, nonce) = permit2Allowance(from, token0(), address(this)); + (amount, expiration, nonce) = permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 2); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = + permit2Allowance(from, token1(), uint256(uint160(address(this)))); assertEq(amount1, defaultAmountOrId); assertEq(expiration1, defaultExpiration); assertEq(nonce1, 1); @@ -204,11 +220,13 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(from, permit, sig); snapEnd(); - (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + (uint160 amount, uint48 expiration, uint48 nonce) = + permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = + permit2Allowance(from, token1(), uint256(uint160(address(this)))); assertEq(amount1, defaultAmountOrId); assertEq(expiration1, defaultExpiration); assertEq(nonce1, 1); @@ -228,11 +246,13 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst emit Permit(from, tokens[1], address(this), amounts[1], defaultExpiration, defaultNonce); permit2Permit(from, permit, sig); - (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + (uint160 amount, uint48 expiration, uint48 nonce) = + permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 1); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(from, token1(), address(this)); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = + permit2Allowance(from, token1(), uint256(uint160(address(this)))); assertEq(amount1, defaultAmountOrId); assertEq(expiration1, defaultExpiration); assertEq(nonce1, 1); @@ -248,11 +268,13 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(fromDirty, permit, sig); snapEnd(); - (uint160 amount, uint48 expiration, uint48 nonce) = permit2Allowance(fromDirty, token0(), address(this)); + (uint160 amount, uint48 expiration, uint48 nonce) = + permit2Allowance(fromDirty, token1(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); assertEq(nonce, 2); - (uint160 amount1, uint48 expiration1, uint48 nonce1) = permit2Allowance(fromDirty, token1(), address(this)); + (uint160 amount1, uint48 expiration1, uint48 nonce1) = + permit2Allowance(fromDirty, token1(), uint256(uint160(address(this)))); assertEq(amount1, defaultAmountOrId); assertEq(expiration1, defaultExpiration); assertEq(nonce1, 2); @@ -269,7 +291,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(from, permit, sig); - (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + (uint160 amount,,) = permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); @@ -289,7 +311,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(from, permit, sig); - (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); + (uint160 amount,,) = permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); @@ -342,7 +364,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(fromDirty, permit, sig); snapEnd(); - (uint160 amount,,) = permit2Allowance(fromDirty, token0(), address(this)); + (uint160 amount,,) = permit2Allowance(fromDirty, token1(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); permit2TransferFrom(fromDirty, address3, defaultAmountOrId, token0()); @@ -389,12 +411,12 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(from, permit, sig); snapEnd(); - (uint160 startAllowedAmount0,,) = permit2Allowance(from, token0(), address(this)); + (uint160 startAllowedAmount0,,) = permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(startAllowedAmount0, type(uint160).max); permit2TransferFrom(from, address0, defaultAmountOrId, token0()); - (uint160 endAllowedAmount0,,) = permit2Allowance(from, token0(), address(this)); + (uint160 endAllowedAmount0,,) = permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(endAllowedAmount0, type(uint160).max); assertEq(balanceOf(token0(), from), startBalanceFrom - defaultAmountOrId); @@ -414,12 +436,12 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(fromDirty, permit, sig); snapEnd(); - (uint160 startAllowedAmount0,,) = permit2Allowance(fromDirty, token0(), address(this)); + (uint160 startAllowedAmount0,,) = permit2Allowance(fromDirty, token1(), uint256(uint160(address(this)))); assertEq(startAllowedAmount0, type(uint160).max); permit2TransferFrom(fromDirty, address0, defaultAmountOrId, token0()); - (uint160 endAllowedAmount0,,) = permit2Allowance(fromDirty, token0(), address(this)); + (uint160 endAllowedAmount0,,) = permit2Allowance(fromDirty, token1(), uint256(uint160(address(this)))); assertEq(endAllowedAmount0, type(uint160).max); assertEq(balanceOf(token0(), fromDirty), startBalanceFrom - defaultAmountOrId); @@ -436,12 +458,12 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst permit2Permit(from, permit, sig); - (uint160 startAllowedAmount0,,) = permit2Allowance(from, token0(), address(this)); + (uint160 startAllowedAmount0,,) = permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(startAllowedAmount0, defaultAmountOrId); uint160 transferAmount = 5 ** 18; permit2TransferFrom(from, address0, transferAmount, token0()); - (uint160 endAllowedAmount0,,) = permit2Allowance(from, token0(), address(this)); + (uint160 endAllowedAmount0,,) = permit2Allowance(from, token0(), uint256(uint160(address(this)))); // ensure the allowance was deducted assertEq(endAllowedAmount0, defaultAmountOrId - transferAmount); @@ -455,10 +477,10 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst bytes memory sig = getPermitSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); permit2Permit(from, permit, sig); - (,, uint48 nonce) = permit2Allowance(from, token0(), address(this)); + (,, uint48 nonce) = permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(nonce, 1); - (uint160 amount, uint48 expiration,) = permit2Allowance(from, token0(), address(this)); + (uint160 amount, uint48 expiration,) = permit2Allowance(from, token0(), uint256(uint160(address(this)))); assertEq(amount, defaultAmountOrId); assertEq(expiration, defaultExpiration); @@ -613,7 +635,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst // (uint160 amount,,) = permit2Allowance(from, token0(), address(this)); // assertEq(amount, defaultAmountOrId); - // (uint160 amount1,,) = permit2Allowance(fromDirty, token0(), address(this)); + // (uint160 amount1,,) = permit2Allowance(fromDirty, token1(), address(this)); // assertEq(amount1, defaultAmountOrId); // address[] memory owners = AddressBuilder.fill(1, from).push(fromDirty); @@ -628,7 +650,7 @@ abstract contract BaseAllowanceTransferTest is Test, PermitSignature, PermitAbst // assertEq(balanceOf(token0(), address(this)), startBalanceTo + 2 * 1 ** 18); // (amount,,) = permit2Allowance(from, token0(), address(this)); // assertEq(amount, defaultAmountOrId - 1 ** 18); - // (amount,,) = permit2Allowance(fromDirty, token0(), address(this)); + // (amount,,) = permit2Allowance(fromDirty, token1(), address(this)); // assertEq(amount, defaultAmountOrId - 1 ** 18); // } diff --git a/test/SignatureTransfer.t.sol b/test/SignatureTransfer.t.sol index fbe51ddc..2ee81ef9 100644 --- a/test/SignatureTransfer.t.sol +++ b/test/SignatureTransfer.t.sol @@ -67,10 +67,10 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProviderERC20, Gas fromPrivateKey = 0x12341234; from = vm.addr(fromPrivateKey); - initializeERC20Tokens(); + initializeTokens(); - setERC20TestTokens(from); - setERC20TestTokenApprovals(vm, from, address(permit2)); + setTokens(from); + setTokenApprovals(vm, from, address(permit2)); } function testCorrectWitnessTypehashes() public { diff --git a/test/shared/AllowanceUnitTestERC20.t.sol b/test/shared/AllowanceUnitTestERC20.t.sol index 1a4dba80..b9a5d532 100644 --- a/test/shared/AllowanceUnitTestERC20.t.sol +++ b/test/shared/AllowanceUnitTestERC20.t.sol @@ -10,7 +10,7 @@ import {Allowance} from "../../src/ERC20/libraries/Allowance.sol"; contract AllowanceUnitTestERC20 is BaseAllowanceUnitTest, TokenProviderERC20 { function setUp() public override { permit2 = new MockPermit2(); - initializeERC20Tokens(); + initializeTokens(); } function allowance(address from, address token, address spender, uint256 tokenId) diff --git a/test/shared/AllowanceUnitTestERC721.t.sol b/test/shared/AllowanceUnitTestERC721.t.sol index 2d5e0f7d..f1009e81 100644 --- a/test/shared/AllowanceUnitTestERC721.t.sol +++ b/test/shared/AllowanceUnitTestERC721.t.sol @@ -9,7 +9,7 @@ import {TokenProviderERC721} from "../utils/TokenProviderERC721.sol"; contract AllowanceUnitTestERC721 is BaseAllowanceUnitTest, TokenProviderERC721 { function setUp() public override { permit2 = new MockPermit2ERC721(); - initializeERC721TestTokens(); + initializeTokens(); } function allowance(address from, address token, address spender, uint256 tokenId) diff --git a/test/utils/TokenProviderERC20.sol b/test/utils/TokenProviderERC20.sol index 24ece69d..f3090a14 100644 --- a/test/utils/TokenProviderERC20.sol +++ b/test/utils/TokenProviderERC20.sol @@ -16,17 +16,17 @@ contract TokenProviderERC20 { address faucet = address(0x98765); - function initializeERC20Tokens() public { + function initializeTokens() public { _token0 = new MockERC20("Test0", "TEST0", 18); _token1 = new MockERC20("Test1", "TEST1", 18); } - function setERC20TestTokens(address from) public { + function setTokens(address from) public { _token0.mint(from, MINT_AMOUNT_ERC20); _token1.mint(from, MINT_AMOUNT_ERC20); } - function setERC20TestTokenApprovals(Vm vm, address owner, address spender) public { + function setTokenApprovals(Vm vm, address owner, address spender) public { vm.startPrank(owner); _token0.approve(spender, type(uint256).max); _token1.approve(spender, type(uint256).max); diff --git a/test/utils/TokenProviderERC721.sol b/test/utils/TokenProviderERC721.sol index 681f02d5..c874e3ee 100644 --- a/test/utils/TokenProviderERC721.sol +++ b/test/utils/TokenProviderERC721.sol @@ -16,21 +16,29 @@ contract TokenProviderERC721 { address faucet = address(0x98765); - function initializeERC721TestTokens() public { + function initializeTokens() public { _token0 = new MockERC721("TestNFT1", "NFT1"); _token1 = new MockERC721("TestNFT2", "NFT2"); } - function setERC721TestTokens(address from) public { + function setToken0(address from) public { // mint with id 1 _token0.mint(from, 1); + } + + function setToken1(address from) public { // mint with id 2 _token1.mint(from, 2); } - function setERC721TestTokenApprovals(Vm vm, address owner, address spender) public { + function setTokenApprovals0(Vm vm, address owner, address spender) public { vm.startPrank(owner); _token0.approve(spender, 1); + vm.stopPrank(); + } + + function setTokenApprovals1(Vm vm, address owner, address spender) public { + vm.startPrank(owner); _token1.approve(spender, 2); vm.stopPrank(); }