Skip to content

Commit

Permalink
gas benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
kumaryash90 committed Jul 31, 2024
1 parent e1a21d6 commit d78ad75
Show file tree
Hide file tree
Showing 2 changed files with 243 additions and 21 deletions.
31 changes: 10 additions & 21 deletions gasreport.txt
Original file line number Diff line number Diff line change
@@ -1,26 +1,15 @@
No files changed, compilation skipped

Running 2 tests for test/benchmarks/BenchmarkPaymentsGatewaySplit.t.sol:BenchmarkPaymentsGatewaySplitTest
[PASS] test_startTransfer_erc20() (gas: 131301)
[PASS] test_startTransfer_nativeToken() (gas: 141479)
Test result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.69ms
Ran 2 tests for test/benchmarks/BenchmarkModularPaymentsGateway.t.sol:BenchmarkModularPaymentsGatewayTest
[PASS] test_initiateTokenPurchase_erc20() (gas: 181350)
[PASS] test_initiateTokenPurchase_nativeToken() (gas: 246176)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.83ms (777.63µs CPU time)

Running 2 tests for test/benchmarks/BenchmarkPaymentsGateway.t.sol:BenchmarkPaymentsGatewayTest
[PASS] test_startTransfer_erc20() (gas: 158244)
[PASS] test_startTransfer_nativeToken() (gas: 183194)
Test result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.94ms
Ran 2 tests for test/benchmarks/BenchmarkPaymentsGateway.t.sol:BenchmarkPaymentsGatewayTest
[PASS] test_initiateTokenPurchase_erc20() (gas: 150635)
[PASS] test_initiateTokenPurchase_nativeToken() (gas: 209936)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.83ms (1.01ms CPU time)

Running 2 tests for test/benchmarks/BenchmarkPaymentsGatewayPull.t.sol:BenchmarkPaymentsGatewayPullTest
[PASS] test_startTransfer_erc20() (gas: 184188)
[PASS] test_startTransfer_nativeToken() (gas: 181962)
Test result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.81ms


Ran 3 test suites: 6 tests passed, 0 failed, 0 skipped (6 total tests)
test_startTransfer_erc20() (gas: 0 (0.000%))
test_startTransfer_nativeToken() (gas: 0 (0.000%))
test_startTransfer_erc20() (gas: 0 (0.000%))
test_startTransfer_nativeToken() (gas: 0 (0.000%))
test_startTransfer_erc20() (gas: 0 (0.000%))
test_startTransfer_nativeToken() (gas: 0 (0.000%))
Overall gas change: 0 (0.000%)
Ran 2 test suites in 2.70ms (3.65ms CPU time): 4 tests passed, 0 failed, 0 skipped (4 total tests)
Overall gas change: 0 (NaN%)
233 changes: 233 additions & 0 deletions test/benchmarks/BenchmarkModularPaymentsGateway.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import { Test, console } from "forge-std/Test.sol";
import { ModularPaymentsGateway } from "src/ModularPaymentsGateway.sol";
import { PaymentsGatewayExtension } from "src/PaymentsGatewayExtension.sol";
import { LibClone } from "lib/solady/src/utils/LibClone.sol";
import { MockERC20 } from "../utils/MockERC20.sol";
import { MockTarget } from "../utils/MockTarget.sol";

contract BenchmarkModularPaymentsGatewayTest is Test {
PaymentsGatewayExtension internal gateway;
MockERC20 internal mockERC20;
MockTarget internal mockTarget;

address payable internal owner;
address payable internal operator;
address payable internal sender;
address payable internal receiver;
address payable internal client;

bytes32 internal ownerClientId;
bytes32 internal clientId;

uint256 internal ownerFeeAmount;
uint256 internal clientFeeAmount;
uint256 internal totalFeeAmount;

PaymentsGatewayExtension.PayoutInfo[] internal payouts;

bytes32 internal typehashPayRequest;
bytes32 internal typehashPayoutInfo;
bytes32 internal nameHash;
bytes32 internal versionHash;
bytes32 internal typehashEip712;
bytes32 internal domainSeparator;

function setUp() public {
owner = payable(vm.addr(1));
operator = payable(vm.addr(2));
sender = payable(vm.addr(3));
receiver = payable(vm.addr(4));
client = payable(vm.addr(5));

ownerClientId = keccak256("owner");
clientId = keccak256("client");

ownerFeeAmount = 20;
clientFeeAmount = 10;

// deploy and install extension
address impl = address(new ModularPaymentsGateway());
address extension = address(new PaymentsGatewayExtension());

address[] memory extensions = new address[](1);
bytes[] memory extensionData = new bytes[](1);
extensions[0] = address(extension);
extensionData[0] = "";

gateway = PaymentsGatewayExtension(LibClone.clone(impl));
ModularPaymentsGateway(payable(address(gateway))).initialize(operator, extensions, extensionData);

mockERC20 = new MockERC20("Token", "TKN");
mockTarget = new MockTarget();

// fund the sender
mockERC20.mint(sender, 10 ether);
vm.deal(sender, 10 ether);

// build payout info
payouts.push(
PaymentsGatewayExtension.PayoutInfo({
clientId: ownerClientId,
payoutAddress: owner,
feeAmount: ownerFeeAmount
})
);
payouts.push(
PaymentsGatewayExtension.PayoutInfo({
clientId: clientId,
payoutAddress: client,
feeAmount: clientFeeAmount
})
);

for (uint256 i = 0; i < payouts.length; i++) {
totalFeeAmount += payouts[i].feeAmount;
}

// EIP712
typehashPayoutInfo = keccak256("PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeAmount)");
typehashPayRequest = keccak256(
"PayRequest(bytes32 clientId,bytes32 transactionId,address tokenAddress,uint256 tokenAmount,uint256 expirationTimestamp,PayoutInfo[] payouts,address forwardAddress,bytes data)PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeAmount)"
);
nameHash = keccak256(bytes("PaymentsGateway"));
versionHash = keccak256(bytes("1"));
typehashEip712 = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(gateway)));
}

/*///////////////////////////////////////////////////////////////
internal util functions
//////////////////////////////////////////////////////////////*/

function _buildMockTargetCalldata(
address _sender,
address _receiver,
address _token,
uint256 _sendValue,
string memory _message
) internal pure returns (bytes memory data) {
data = abi.encode(_sender, _receiver, _token, _sendValue, _message);
}

function _hashPayoutInfo(PaymentsGatewayExtension.PayoutInfo[] memory _payouts) private view returns (bytes32) {
bytes32 payoutHash = typehashPayoutInfo;

bytes32[] memory payoutsHashes = new bytes32[](_payouts.length);
for (uint i = 0; i < payouts.length; i++) {
payoutsHashes[i] = keccak256(
abi.encode(payoutHash, _payouts[i].clientId, _payouts[i].payoutAddress, _payouts[i].feeAmount)
);
}
return keccak256(abi.encodePacked(payoutsHashes));
}

function _prepareAndSignData(
uint256 _operatorPrivateKey,
PaymentsGatewayExtension.PayRequest memory req
) internal view returns (bytes memory signature) {
bytes memory dataToHash;
{
bytes32 _payoutsHash = _hashPayoutInfo(req.payouts);
dataToHash = abi.encode(
typehashPayRequest,
req.clientId,
req.transactionId,
req.tokenAddress,
req.tokenAmount,
req.expirationTimestamp,
_payoutsHash,
req.forwardAddress,
keccak256(req.data)
);
}

{
bytes32 _structHash = keccak256(dataToHash);
bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, _structHash));
(uint8 v, bytes32 r, bytes32 s) = vm.sign(_operatorPrivateKey, typedDataHash);

signature = abi.encodePacked(r, s, v);
}
}

/*///////////////////////////////////////////////////////////////
Test `initiateTokenPurchase`
//////////////////////////////////////////////////////////////*/

function test_initiateTokenPurchase_erc20() public {
vm.pauseGasMetering();
uint256 sendValue = 1 ether;
uint256 sendValueWithFees = sendValue + totalFeeAmount;
bytes memory targetCalldata = _buildMockTargetCalldata(sender, receiver, address(mockERC20), sendValue, "");

// approve amount to gateway contract
vm.prank(sender);
mockERC20.approve(address(gateway), sendValueWithFees);

// create pay request
PaymentsGatewayExtension.PayRequest memory req;
bytes32 _transactionId = keccak256("transaction ID");

req.clientId = clientId;
req.transactionId = _transactionId;
req.tokenAddress = address(mockERC20);
req.tokenAmount = sendValue;
req.forwardAddress = payable(address(mockTarget));
req.expirationTimestamp = 1000;
req.data = targetCalldata;
req.payouts = payouts;

// generate signature
bytes memory _signature = _prepareAndSignData(
2, // sign with operator private key, i.e. 2
req
);

// send transaction
vm.prank(sender);
vm.resumeGasMetering();
gateway.initiateTokenPurchase(req, _signature);
}

function test_initiateTokenPurchase_nativeToken() public {
vm.pauseGasMetering();
uint256 sendValue = 1 ether;
uint256 sendValueWithFees = sendValue + totalFeeAmount;
bytes memory targetCalldata = _buildMockTargetCalldata(
sender,
receiver,
address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE),
sendValue,
""
);

// create pay request
PaymentsGatewayExtension.PayRequest memory req;
bytes32 _transactionId = keccak256("transaction ID");

req.clientId = clientId;
req.transactionId = _transactionId;
req.tokenAddress = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
req.tokenAmount = sendValue;
req.forwardAddress = payable(address(mockTarget));
req.expirationTimestamp = 1000;
req.data = targetCalldata;
req.payouts = payouts;

// generate signature
bytes memory _signature = _prepareAndSignData(
2, // sign with operator private key, i.e. 2
req
);

// send transaction
vm.prank(sender);
vm.resumeGasMetering();
gateway.initiateTokenPurchase{ value: sendValueWithFees }(req, _signature);
}
}

0 comments on commit d78ad75

Please sign in to comment.