From 7688637c12672730b6f53c50690fc50b04f88e44 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Fri, 19 Jan 2024 12:31:57 -0500 Subject: [PATCH 1/2] helper function to get a signature with a specified spender --- test/utils/PermitSignature.sol | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/test/utils/PermitSignature.sol b/test/utils/PermitSignature.sol index 0cb0c3d2..4162cb1a 100644 --- a/test/utils/PermitSignature.sol +++ b/test/utils/PermitSignature.sol @@ -131,20 +131,38 @@ contract PermitSignature { return bytes.concat(r, s, bytes1(v)); } + /// @notice Get the permitted signature for a transferFrom to the current contract (address(this)) + /// @param permit The permit to sign + /// @param privateKey The private key to sign with, provided to vm.sign + /// @param domainSeparator The domain separator for the permit + /// @return sig The concatenated signature, authorizing a transfer from signer to the current contract function getPermitTransferSignature( ISignatureTransfer.PermitTransferFrom memory permit, uint256 privateKey, bytes32 domainSeparator ) internal view returns (bytes memory sig) { + return getPermitTransferSignature(permit, address(this), privateKey, domainSeparator); + } + + /// @notice Get the permitted signature for a transferFrom to a designated address + /// @param permit The permit to sign + /// @param to The recipient of the transferFrom + /// @param privateKey The private key to sign with, provided to vm.sign + /// @param domainSeparator The domain separator for the permit + /// @return sig The concatenated signature, authorizing a transfer from signer to the provided address + function getPermitTransferSignature( + ISignatureTransfer.PermitTransferFrom memory permit, + address to, + uint256 privateKey, + bytes32 domainSeparator + ) internal pure returns (bytes memory sig) { bytes32 tokenPermissions = keccak256(abi.encode(_TOKEN_PERMISSIONS_TYPEHASH, permit.permitted)); bytes32 msgHash = keccak256( abi.encodePacked( "\x19\x01", domainSeparator, keccak256( - abi.encode( - _PERMIT_TRANSFER_FROM_TYPEHASH, tokenPermissions, address(this), permit.nonce, permit.deadline - ) + abi.encode(_PERMIT_TRANSFER_FROM_TYPEHASH, tokenPermissions, to, permit.nonce, permit.deadline) ) ) ); From 881b44231c4f6dc3f7d00d3233ec0d9f7f831751 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Fri, 19 Jan 2024 12:34:13 -0500 Subject: [PATCH 2/2] testing the new helper function --- test/SignatureTransfer.t.sol | 33 +++++++++++++++++++++++++++++++++ test/mocks/MockContract.sol | 24 ++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 test/mocks/MockContract.sol diff --git a/test/SignatureTransfer.t.sol b/test/SignatureTransfer.t.sol index 0d6b710e..9e29b3b1 100644 --- a/test/SignatureTransfer.t.sol +++ b/test/SignatureTransfer.t.sol @@ -15,6 +15,8 @@ import {SignatureTransfer} from "../src/SignatureTransfer.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {ISignatureTransfer} from "../src/interfaces/ISignatureTransfer.sol"; import {InvalidNonce, SignatureExpired} from "../src/PermitErrors.sol"; +import {IPermit2} from "../src/interfaces/IPermit2.sol"; +import {MockContract} from "./mocks/MockContract.sol"; contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnapshot { using AddressBuilder for address[]; @@ -58,12 +60,14 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps address address0 = address(0x0); address address2 = address(0x2); + MockContract mockContract; bytes32 DOMAIN_SEPARATOR; function setUp() public { permit2 = new Permit2(); DOMAIN_SEPARATOR = permit2.DOMAIN_SEPARATOR(); + mockContract = new MockContract(IPermit2(address(permit2))); fromPrivateKey = 0x12341234; from = vm.addr(fromPrivateKey); @@ -109,6 +113,35 @@ contract SignatureTransferTest is Test, PermitSignature, TokenProvider, GasSnaps assertEq(token0.balanceOf(address2), startBalanceTo + defaultAmount); } + function testPermitTransferFromContract() public { + uint256 nonce = 0; + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), nonce); + bytes memory sig = getPermitTransferSignature(permit, address(mockContract), fromPrivateKey, DOMAIN_SEPARATOR); + + uint256 startBalance = token0.balanceOf(from); + + uint256 amount = 1e18; // default amount in defaultERC20PermitTransfer + vm.prank(from); + mockContract.depositWithPermit(address(token0), amount, permit, sig); + + // token0 transferred from `from` to `mockContract` + assertEq(token0.balanceOf(address(mockContract)), amount); + assertEq(token0.balanceOf(address(from)), startBalance - amount); + } + + function test_revert_PermitTransferFromContract() public { + uint256 nonce = 0; + ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), nonce); + + // the default signature, where the signer approves to=address(this) + // signature will fail when a contract (invalid recipient) tries to use it + bytes memory sig = getPermitTransferSignature(permit, fromPrivateKey, DOMAIN_SEPARATOR); + + uint256 amount = 1e18; + vm.expectRevert(); + mockContract.depositWithPermit(address(token0), amount, permit, sig); + } + function testPermitTransferFromCompactSig() public { uint256 nonce = 0; ISignatureTransfer.PermitTransferFrom memory permit = defaultERC20PermitTransfer(address(token0), nonce); diff --git a/test/mocks/MockContract.sol b/test/mocks/MockContract.sol new file mode 100644 index 00000000..8b410869 --- /dev/null +++ b/test/mocks/MockContract.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "../../src/interfaces/IPermit2.sol"; + +contract MockContract { + IPermit2 public immutable permit2; + + constructor(IPermit2 _permit2) { + permit2 = _permit2; + } + + function depositWithPermit( + address token, + uint256 amount, + ISignatureTransfer.PermitTransferFrom calldata permitData, + bytes calldata sig + ) external { + require(token == permitData.permitted.token, "MockContract: token mismatch"); + permit2.permitTransferFrom( + permitData, ISignatureTransfer.SignatureTransferDetails(address(this), amount), msg.sender, sig + ); + } +}