Skip to content

Commit d78ad75

Browse files
committed
gas benchmarks
1 parent e1a21d6 commit d78ad75

File tree

2 files changed

+243
-21
lines changed

2 files changed

+243
-21
lines changed

gasreport.txt

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,15 @@
11
No files changed, compilation skipped
22

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

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

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

18-
19-
Ran 3 test suites: 6 tests passed, 0 failed, 0 skipped (6 total tests)
20-
test_startTransfer_erc20() (gas: 0 (0.000%))
21-
test_startTransfer_nativeToken() (gas: 0 (0.000%))
22-
test_startTransfer_erc20() (gas: 0 (0.000%))
23-
test_startTransfer_nativeToken() (gas: 0 (0.000%))
24-
test_startTransfer_erc20() (gas: 0 (0.000%))
25-
test_startTransfer_nativeToken() (gas: 0 (0.000%))
26-
Overall gas change: 0 (0.000%)
14+
Ran 2 test suites in 2.70ms (3.65ms CPU time): 4 tests passed, 0 failed, 0 skipped (4 total tests)
15+
Overall gas change: 0 (NaN%)
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.13;
3+
4+
import { Test, console } from "forge-std/Test.sol";
5+
import { ModularPaymentsGateway } from "src/ModularPaymentsGateway.sol";
6+
import { PaymentsGatewayExtension } from "src/PaymentsGatewayExtension.sol";
7+
import { LibClone } from "lib/solady/src/utils/LibClone.sol";
8+
import { MockERC20 } from "../utils/MockERC20.sol";
9+
import { MockTarget } from "../utils/MockTarget.sol";
10+
11+
contract BenchmarkModularPaymentsGatewayTest is Test {
12+
PaymentsGatewayExtension internal gateway;
13+
MockERC20 internal mockERC20;
14+
MockTarget internal mockTarget;
15+
16+
address payable internal owner;
17+
address payable internal operator;
18+
address payable internal sender;
19+
address payable internal receiver;
20+
address payable internal client;
21+
22+
bytes32 internal ownerClientId;
23+
bytes32 internal clientId;
24+
25+
uint256 internal ownerFeeAmount;
26+
uint256 internal clientFeeAmount;
27+
uint256 internal totalFeeAmount;
28+
29+
PaymentsGatewayExtension.PayoutInfo[] internal payouts;
30+
31+
bytes32 internal typehashPayRequest;
32+
bytes32 internal typehashPayoutInfo;
33+
bytes32 internal nameHash;
34+
bytes32 internal versionHash;
35+
bytes32 internal typehashEip712;
36+
bytes32 internal domainSeparator;
37+
38+
function setUp() public {
39+
owner = payable(vm.addr(1));
40+
operator = payable(vm.addr(2));
41+
sender = payable(vm.addr(3));
42+
receiver = payable(vm.addr(4));
43+
client = payable(vm.addr(5));
44+
45+
ownerClientId = keccak256("owner");
46+
clientId = keccak256("client");
47+
48+
ownerFeeAmount = 20;
49+
clientFeeAmount = 10;
50+
51+
// deploy and install extension
52+
address impl = address(new ModularPaymentsGateway());
53+
address extension = address(new PaymentsGatewayExtension());
54+
55+
address[] memory extensions = new address[](1);
56+
bytes[] memory extensionData = new bytes[](1);
57+
extensions[0] = address(extension);
58+
extensionData[0] = "";
59+
60+
gateway = PaymentsGatewayExtension(LibClone.clone(impl));
61+
ModularPaymentsGateway(payable(address(gateway))).initialize(operator, extensions, extensionData);
62+
63+
mockERC20 = new MockERC20("Token", "TKN");
64+
mockTarget = new MockTarget();
65+
66+
// fund the sender
67+
mockERC20.mint(sender, 10 ether);
68+
vm.deal(sender, 10 ether);
69+
70+
// build payout info
71+
payouts.push(
72+
PaymentsGatewayExtension.PayoutInfo({
73+
clientId: ownerClientId,
74+
payoutAddress: owner,
75+
feeAmount: ownerFeeAmount
76+
})
77+
);
78+
payouts.push(
79+
PaymentsGatewayExtension.PayoutInfo({
80+
clientId: clientId,
81+
payoutAddress: client,
82+
feeAmount: clientFeeAmount
83+
})
84+
);
85+
86+
for (uint256 i = 0; i < payouts.length; i++) {
87+
totalFeeAmount += payouts[i].feeAmount;
88+
}
89+
90+
// EIP712
91+
typehashPayoutInfo = keccak256("PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeAmount)");
92+
typehashPayRequest = keccak256(
93+
"PayRequest(bytes32 clientId,bytes32 transactionId,address tokenAddress,uint256 tokenAmount,uint256 expirationTimestamp,PayoutInfo[] payouts,address forwardAddress,bytes data)PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeAmount)"
94+
);
95+
nameHash = keccak256(bytes("PaymentsGateway"));
96+
versionHash = keccak256(bytes("1"));
97+
typehashEip712 = keccak256(
98+
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
99+
);
100+
domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(gateway)));
101+
}
102+
103+
/*///////////////////////////////////////////////////////////////
104+
internal util functions
105+
//////////////////////////////////////////////////////////////*/
106+
107+
function _buildMockTargetCalldata(
108+
address _sender,
109+
address _receiver,
110+
address _token,
111+
uint256 _sendValue,
112+
string memory _message
113+
) internal pure returns (bytes memory data) {
114+
data = abi.encode(_sender, _receiver, _token, _sendValue, _message);
115+
}
116+
117+
function _hashPayoutInfo(PaymentsGatewayExtension.PayoutInfo[] memory _payouts) private view returns (bytes32) {
118+
bytes32 payoutHash = typehashPayoutInfo;
119+
120+
bytes32[] memory payoutsHashes = new bytes32[](_payouts.length);
121+
for (uint i = 0; i < payouts.length; i++) {
122+
payoutsHashes[i] = keccak256(
123+
abi.encode(payoutHash, _payouts[i].clientId, _payouts[i].payoutAddress, _payouts[i].feeAmount)
124+
);
125+
}
126+
return keccak256(abi.encodePacked(payoutsHashes));
127+
}
128+
129+
function _prepareAndSignData(
130+
uint256 _operatorPrivateKey,
131+
PaymentsGatewayExtension.PayRequest memory req
132+
) internal view returns (bytes memory signature) {
133+
bytes memory dataToHash;
134+
{
135+
bytes32 _payoutsHash = _hashPayoutInfo(req.payouts);
136+
dataToHash = abi.encode(
137+
typehashPayRequest,
138+
req.clientId,
139+
req.transactionId,
140+
req.tokenAddress,
141+
req.tokenAmount,
142+
req.expirationTimestamp,
143+
_payoutsHash,
144+
req.forwardAddress,
145+
keccak256(req.data)
146+
);
147+
}
148+
149+
{
150+
bytes32 _structHash = keccak256(dataToHash);
151+
bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, _structHash));
152+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(_operatorPrivateKey, typedDataHash);
153+
154+
signature = abi.encodePacked(r, s, v);
155+
}
156+
}
157+
158+
/*///////////////////////////////////////////////////////////////
159+
Test `initiateTokenPurchase`
160+
//////////////////////////////////////////////////////////////*/
161+
162+
function test_initiateTokenPurchase_erc20() public {
163+
vm.pauseGasMetering();
164+
uint256 sendValue = 1 ether;
165+
uint256 sendValueWithFees = sendValue + totalFeeAmount;
166+
bytes memory targetCalldata = _buildMockTargetCalldata(sender, receiver, address(mockERC20), sendValue, "");
167+
168+
// approve amount to gateway contract
169+
vm.prank(sender);
170+
mockERC20.approve(address(gateway), sendValueWithFees);
171+
172+
// create pay request
173+
PaymentsGatewayExtension.PayRequest memory req;
174+
bytes32 _transactionId = keccak256("transaction ID");
175+
176+
req.clientId = clientId;
177+
req.transactionId = _transactionId;
178+
req.tokenAddress = address(mockERC20);
179+
req.tokenAmount = sendValue;
180+
req.forwardAddress = payable(address(mockTarget));
181+
req.expirationTimestamp = 1000;
182+
req.data = targetCalldata;
183+
req.payouts = payouts;
184+
185+
// generate signature
186+
bytes memory _signature = _prepareAndSignData(
187+
2, // sign with operator private key, i.e. 2
188+
req
189+
);
190+
191+
// send transaction
192+
vm.prank(sender);
193+
vm.resumeGasMetering();
194+
gateway.initiateTokenPurchase(req, _signature);
195+
}
196+
197+
function test_initiateTokenPurchase_nativeToken() public {
198+
vm.pauseGasMetering();
199+
uint256 sendValue = 1 ether;
200+
uint256 sendValueWithFees = sendValue + totalFeeAmount;
201+
bytes memory targetCalldata = _buildMockTargetCalldata(
202+
sender,
203+
receiver,
204+
address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE),
205+
sendValue,
206+
""
207+
);
208+
209+
// create pay request
210+
PaymentsGatewayExtension.PayRequest memory req;
211+
bytes32 _transactionId = keccak256("transaction ID");
212+
213+
req.clientId = clientId;
214+
req.transactionId = _transactionId;
215+
req.tokenAddress = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
216+
req.tokenAmount = sendValue;
217+
req.forwardAddress = payable(address(mockTarget));
218+
req.expirationTimestamp = 1000;
219+
req.data = targetCalldata;
220+
req.payouts = payouts;
221+
222+
// generate signature
223+
bytes memory _signature = _prepareAndSignData(
224+
2, // sign with operator private key, i.e. 2
225+
req
226+
);
227+
228+
// send transaction
229+
vm.prank(sender);
230+
vm.resumeGasMetering();
231+
gateway.initiateTokenPurchase{ value: sendValueWithFees }(req, _signature);
232+
}
233+
}

0 commit comments

Comments
 (0)