Skip to content

Commit

Permalink
test: permit2 flows
Browse files Browse the repository at this point in the history
  • Loading branch information
anna-carroll committed Jul 19, 2024
1 parent 3ea69d2 commit e3607ac
Show file tree
Hide file tree
Showing 2 changed files with 467 additions and 1 deletion.
126 changes: 125 additions & 1 deletion test/Helpers.t.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {Test, console2} from "forge-std/Test.sol";
import {Zenith} from "../src/Zenith.sol";
import {UsesPermit2} from "../src/permit2/UsesPermit2.sol";

import {Test, console2} from "forge-std/Test.sol";
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {ERC20Burnable} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol";

contract TestERC20 is ERC20Burnable {
constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {}
Expand All @@ -14,6 +17,127 @@ contract TestERC20 is ERC20Burnable {
}
}

contract Permit2Stub {
/// @notice stubbed `permitWitnessTransferFrom` - does not check signature, nonce, or deadline
function permitWitnessTransferFrom(
ISignatureTransfer.PermitTransferFrom memory permit,
ISignatureTransfer.SignatureTransferDetails calldata transferDetails,
address owner,
bytes32, /*witness*/
string calldata, /*witnessTypeString*/
bytes calldata /*signature*/
) external {
ERC20(permit.permitted.token).transferFrom(owner, transferDetails.to, transferDetails.requestedAmount);
}
}

contract BatchPermit2Stub {
function permitWitnessTransferFrom(
ISignatureTransfer.PermitBatchTransferFrom memory permit,
ISignatureTransfer.SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes32, /*witness*/
string calldata, /*witnessTypeString*/
bytes calldata /*signature*/
) external {
for (uint256 i = 0; i < transferDetails.length; i++) {
ERC20(permit.permitted[i].token).transferFrom(
owner, transferDetails[i].to, transferDetails[i].requestedAmount
);
}
}
}

contract Permit2Helpers is Test {
string public constant _PERMIT_TRANSFER_FROM_WITNESS_TYPEHASH_STUB =
"PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,";

bytes32 public constant _TOKEN_PERMISSIONS_TYPEHASH = keccak256("TokenPermissions(address token,uint256 amount)");

string public constant _PERMIT_BATCH_WITNESS_TRANSFER_FROM_TYPEHASH_STUB =
"PermitBatchWitnessTransferFrom(TokenPermissions[] permitted,address spender,uint256 nonce,uint256 deadline,";

/// @notice given a Permit and a Witness, produce a signature from the `owner`
function signPermit(
uint256 signingKey,
address spender,
ISignatureTransfer.PermitTransferFrom memory permit,
UsesPermit2.Witness memory _witness
) internal pure returns (bytes memory signature) {
bytes32 permit2Hash = hashWithWitness(spender, permit, _witness.witnessHash, _witness.witnessTypeString);
uint8 v;
bytes32 r;
bytes32 s;
(v, r, s) = vm.sign(signingKey, permit2Hash);
signature = abi.encodePacked(r, s, v);
}

// this function is private on permit2 contracts but need to port it here for test functionality
function hashWithWitness(
address spender,
ISignatureTransfer.PermitTransferFrom memory _permit,
bytes32 witness,
string memory witnessTypeString
) internal pure returns (bytes32) {
bytes32 typeHash = keccak256(abi.encodePacked(_PERMIT_TRANSFER_FROM_WITNESS_TYPEHASH_STUB, witnessTypeString));

bytes32 tokenPermissionsHash = _hashTokenPermissions(_permit.permitted);
return keccak256(abi.encode(typeHash, tokenPermissionsHash, spender, _permit.nonce, _permit.deadline, witness));
}

/// @notice given a Permit and a Witness, produce a signature from the `owner`
function signPermit(
uint256 signingKey,
address spender,
ISignatureTransfer.PermitBatchTransferFrom memory permit,
UsesPermit2.Witness memory _witness
) internal pure returns (bytes memory signature) {
bytes32 permit2Hash = hashWithWitness(spender, permit, _witness.witnessHash, _witness.witnessTypeString);
uint8 v;
bytes32 r;
bytes32 s;
(v, r, s) = vm.sign(signingKey, permit2Hash);
signature = abi.encodePacked(r, s, v);
}

function hashWithWitness(
address spender,
ISignatureTransfer.PermitBatchTransferFrom memory permit,
bytes32 witness,
string memory witnessTypeString
) internal pure returns (bytes32) {
bytes32 typeHash =
keccak256(abi.encodePacked(_PERMIT_BATCH_WITNESS_TRANSFER_FROM_TYPEHASH_STUB, witnessTypeString));

uint256 numPermitted = permit.permitted.length;
bytes32[] memory tokenPermissionHashes = new bytes32[](numPermitted);

for (uint256 i = 0; i < numPermitted; ++i) {
tokenPermissionHashes[i] = _hashTokenPermissions(permit.permitted[i]);
}

return keccak256(
abi.encode(
typeHash,
keccak256(abi.encodePacked(tokenPermissionHashes)),
spender,
permit.nonce,
permit.deadline,
witness
)
);
}

// this function is private on permit2 contracts but need to port it here for test functionality
function _hashTokenPermissions(ISignatureTransfer.TokenPermissions memory _permitted)
private
pure
returns (bytes32)
{
return keccak256(abi.encode(_TOKEN_PERMISSIONS_TYPEHASH, _permitted));
}
}

contract HelpersTest is Test {
Zenith public target;

Expand Down
Loading

0 comments on commit e3607ac

Please sign in to comment.