Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/external/forwarder/TransactionForwarder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ contract TransactionForwarder is
// Constructor
constructor(string memory name) ERC2771Forwarder(name) {}

//--------------------------------------------------------------------------
// Metatransaction Helper Functions

/// @notice Creates a digest for the given ForwardRequestData
/// @dev The signature field of the given ForwardRequestData can be empty
/// @param req The ForwardRequest you want to get the digest from
/// @return digest The digest needed to create a signature for the request
function createDigest(ForwardRequestData memory req)
external
view
returns (bytes32 digest)
{
return _hashTypedDataV4(_getStructHash(req));
}

//--------------------------------------------------------------------------
// Multicall Functions

Expand Down Expand Up @@ -61,6 +76,28 @@ contract TransactionForwarder is
}
}

//--------------------------------------------------------------------------
// Internal

function _getStructHash(ERC2771Forwarder.ForwardRequestData memory req)
internal
view
returns (bytes32)
{
return keccak256(
abi.encode(
_FORWARD_REQUEST_TYPEHASH,
req.from,
req.to,
req.value,
req.gas,
nonces(req.from),
req.deadline,
keccak256(req.data)
)
);
}

// Copied from the ERC2771Forwarder as it isnt declared internal ಠ_ಠ
// Just added a _ because i cant override it
/**
Expand Down
131 changes: 0 additions & 131 deletions test/e2e/module/ForwarderSignatureHelper.sol

This file was deleted.

51 changes: 31 additions & 20 deletions test/e2e/module/MetaTxAndMulticallE2E.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ pragma solidity ^0.8.0;

import "forge-std/console.sol";

import {ForwarderSignatureHelper} from "./ForwarderSignatureHelper.sol";

// SuT
import {RoleAuthorizer} from "src/modules/authorizer/RoleAuthorizer.sol";

Expand All @@ -22,7 +20,8 @@ import {
} from "src/modules/logicModule/BountyManager.sol";
import {
TransactionForwarder,
ITransactionForwarder
ITransactionForwarder,
ERC2771Forwarder
} from "src/external/forwarder/TransactionForwarder.sol";

contract MetaTxAndMulticallE2E is E2ETest {
Expand Down Expand Up @@ -121,29 +120,33 @@ contract MetaTxAndMulticallE2E is E2ETest {
vm.prank(signer);
token.approve(fundingManager, depositAmount);

//Because the creation of the signature and the ForwardRequestData is kind of messy I created a Helper that handles that
ForwarderSignatureHelper signatureHelper =
new ForwarderSignatureHelper(address(forwarder));

//We create a simplyfied ForwardRequest without the signature
ForwarderSignatureHelper.HelperForwardRequest memory req =
ForwarderSignatureHelper.HelperForwardRequest({
ERC2771Forwarder.ForwardRequestData memory req = ERC2771Forwarder
.ForwardRequestData({
from: signer,
to: fundingManager,
value: 0,
//This should be approximately be the gas value of the called function in this case the deposit function
gas: 1_000_000,
//This is the timestamp after which the request is not executable anymore.
deadline: uint48(block.timestamp + 1 weeks),
data: abi.encodeWithSignature("deposit(uint256)", depositAmount)
data: abi.encodeWithSignature("deposit(uint256)", depositAmount),
//This has to be empty until we create the signature
signature: bytes("")
});

//We use the helper to create the sinature
TransactionForwarder.ForwardRequestData memory finalReq =
signatureHelper.getForwardRequestData(req, signer, signerPrivateKey);
//Create the digest needed to create the signature
bytes32 digest = forwarder.createDigest(req);

//Create Signature with digest (This has to be handled by the frontend)
vm.prank(signer);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, digest);
bytes memory signature = abi.encodePacked(r, s, v);

req.signature = signature;

//Do call
forwarder.execute(finalReq);
forwarder.execute(req);

//Check if successful
assertEq(
Expand Down Expand Up @@ -178,7 +181,7 @@ contract MetaTxAndMulticallE2E is E2ETest {
);

//Then we need to create the ForwardRequest
req = ForwarderSignatureHelper.HelperForwardRequest({
req = ERC2771Forwarder.ForwardRequestData({
from: signer,
to: address(bountyManager),
value: 0,
Expand All @@ -190,15 +193,23 @@ contract MetaTxAndMulticallE2E is E2ETest {
100e18, //minimumPayoutAmount
500e18, //maximumPayoutAmount
bytes("This is a test bounty") //details
)
),
//This has to be empty until we create the signature
signature: bytes("")
});

//We use the helper to create the sinature
finalReq =
signatureHelper.getForwardRequestData(req, signer, signerPrivateKey);
//Create the digest needed to create the signature
digest = forwarder.createDigest(req);

//Create Signature with digest (This has to be handled by the frontend)
vm.prank(signer);
(v, r, s) = vm.sign(signerPrivateKey, digest);
signature = abi.encodePacked(r, s, v);

req.signature = signature;

//Do call
forwarder.execute(finalReq);
forwarder.execute(req);

//Check if successful
assertTrue(bountyManager.isExistingBountyId(1));
Expand Down
41 changes: 39 additions & 2 deletions test/external/TransactionForwarder.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,53 @@ import {
ERC2771Forwarder
} from "src/external/forwarder/TransactionForwarder.sol";

import {TransactionForwarderAccessMock} from
"test/utils/mocks/external/TransactionForwarderAccessMock.sol";

import {CallIntercepter} from "test/utils/mocks/external/CallIntercepter.sol";

contract TransactionForwarderTest is Test {
// SuT
TransactionForwarder forwarder;
TransactionForwarderAccessMock forwarder;

event CallReceived(address intercepterAddress, bytes data, address sender);

function setUp() public {
forwarder = new TransactionForwarder("TransactionForwarder");
forwarder = new TransactionForwarderAccessMock("TransactionForwarder");
}

//--------------------------------------------------------------------------
// Test: createDigest

function testCreateDigest(
ERC2771Forwarder.ForwardRequestData memory req,
uint signerPrivateKey
) public {
//Restrict the signerKey to a space where it still should work
signerPrivateKey = bound(signerPrivateKey, 1, 2 ^ 128);

//Derive signer from signerkey
address signer = vm.addr(signerPrivateKey);

//set from in req to be the signer
req.from = signer;

//Create the digest needed to create the signature
bytes32 digest = forwarder.createDigest(req);

//Create Signature with digest (This has to be handled by the frontend)
vm.prank(signer);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, digest);
bytes memory signature = abi.encodePacked(r, s, v);

//Set correct signature in request
req.signature = signature;

(,, bool signerMatch, address signerResult) =
forwarder.original_validate(req);

assertTrue(signerMatch);
assertEq(signer, signerResult);
}

//--------------------------------------------------------------------------------
Expand Down
22 changes: 22 additions & 0 deletions test/utils/mocks/external/TransactionForwarderAccessMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.0;

import {TransactionForwarder} from
"src/external/forwarder/TransactionForwarder.sol";

contract TransactionForwarderAccessMock is TransactionForwarder {
constructor(string memory name) TransactionForwarder(name) {}

function original_validate(ForwardRequestData calldata request)
external
view
returns (
bool isTrustedForwarder,
bool active,
bool signerMatch,
address signer
)
{
return _validate(request);
}
}