// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import {Vm} from "forge-std/Vm.sol"; import {EIP712} from "openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol"; import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol"; import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol"; /// taken from permit2 utils PermitSignature files contract Permit2SignatureHelpers { Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); bytes32 public constant _PERMIT_DETAILS_TYPEHASH = keccak256("PermitDetails(address token,uint160 amount,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)" ); bytes32 public constant _PERMIT_BATCH_TYPEHASH = keccak256( "PermitBatch(PermitDetails[] details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)" ); function getPermitSignatureRaw( IAllowanceTransfer.PermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator ) internal pure returns (uint8 v, bytes32 r, bytes32 s) { bytes32 permitHash = keccak256(abi.encode(_PERMIT_DETAILS_TYPEHASH, permit.details)); bytes32 msgHash = keccak256( abi.encodePacked( "\x19\x01", domainSeparator, keccak256(abi.encode(_PERMIT_SINGLE_TYPEHASH, permitHash, permit.spender, permit.sigDeadline)) ) ); (v, r, s) = vm.sign(privateKey, msgHash); } function getPermitSignature( IAllowanceTransfer.PermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator ) internal pure returns (bytes memory sig) { (uint8 v, bytes32 r, bytes32 s) = getPermitSignatureRaw(permit, privateKey, domainSeparator); return bytes.concat(r, s, bytes1(v)); } function getCompactPermitSignature( IAllowanceTransfer.PermitSingle memory permit, uint256 privateKey, bytes32 domainSeparator ) internal pure 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 _getCompactSignature(uint8 vRaw, bytes32 rRaw, bytes32 sRaw) internal 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); } function getPermitBatchSignature( IAllowanceTransfer.PermitBatch memory permit, uint256 privateKey, bytes32 domainSeparator ) internal pure returns (bytes memory sig) { bytes32[] memory permitHashes = new bytes32[](permit.details.length); for (uint256 i = 0; i < permit.details.length; ++i) { permitHashes[i] = keccak256(abi.encode(_PERMIT_DETAILS_TYPEHASH, permit.details[i])); } bytes32 msgHash = keccak256( abi.encodePacked( "\x19\x01", domainSeparator, keccak256( abi.encode( _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)); } function defaultERC20PermitAllowance(address token0, 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}); return IAllowanceTransfer.PermitSingle({ details: details, spender: address(this), sigDeadline: block.timestamp + 100 }); } function defaultERC20PermitBatchAllowance(address[] memory tokens, uint160 amount, uint48 expiration, uint48 nonce) internal view returns (IAllowanceTransfer.PermitBatch memory) { IAllowanceTransfer.PermitDetails[] memory details = new IAllowanceTransfer.PermitDetails[](tokens.length); for (uint256 i = 0; i < tokens.length; ++i) { details[i] = IAllowanceTransfer.PermitDetails({ token: tokens[i], amount: amount, expiration: expiration, nonce: nonce }); } return IAllowanceTransfer.PermitBatch({ details: details, spender: address(this), sigDeadline: block.timestamp + 100 }); } }