Skip to content

Commit 5e16180

Browse files
committed
expiration timestamp
1 parent 7f78661 commit 5e16180

File tree

3 files changed

+60
-22
lines changed

3 files changed

+60
-22
lines changed

src/PaymentsGateway.sol

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ contract PaymentsGateway is EIP712, Ownable, ReentrancyGuard {
2727
error PaymentsGatewayInvalidAmount(uint256 amount);
2828
error PaymentsGatewayVerificationFailed();
2929
error PaymentsGatewayFailedToForward();
30+
error PaymentsGatewayRequestExpired(uint256 expirationTimestamp);
3031

3132
event TransferStart(
3233
bytes32 indexed clientId,
@@ -71,6 +72,7 @@ contract PaymentsGateway is EIP712, Ownable, ReentrancyGuard {
7172
bytes32 transactionId;
7273
address tokenAddress;
7374
uint256 tokenAmount;
75+
uint256 expirationTimestamp;
7476
PayoutInfo[] payouts;
7577
address payable forwardAddress;
7678
bytes data;
@@ -80,7 +82,7 @@ contract PaymentsGateway is EIP712, Ownable, ReentrancyGuard {
8082
keccak256("PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeBPS)");
8183
bytes32 private constant REQUEST_TYPEHASH =
8284
keccak256(
83-
"PayRequest(bytes32 clientId,bytes32 transactionId,address tokenAddress,uint256 tokenAmount,PayoutInfo[] payouts,address forwardAddress,bytes data)PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeBPS)"
85+
"PayRequest(bytes32 clientId,bytes32 transactionId,address tokenAddress,uint256 tokenAmount,uint256 expirationTimestamp,PayoutInfo[] payouts,address forwardAddress,bytes data)PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeBPS)"
8486
);
8587
address private constant THIRDWEB_CLIENT_ID = 0x0000000000000000000000000000000000000000;
8688
address private constant NATIVE_TOKEN_ADDRESS = 0x0000000000000000000000000000000000000000;
@@ -176,6 +178,7 @@ contract PaymentsGateway is EIP712, Ownable, ReentrancyGuard {
176178
req.transactionId,
177179
req.tokenAddress,
178180
req.tokenAmount,
181+
req.expirationTimestamp,
179182
payoutsHash,
180183
req.forwardAddress,
181184
keccak256(req.data)
@@ -206,6 +209,11 @@ contract PaymentsGateway is EIP712, Ownable, ReentrancyGuard {
206209
revert PaymentsGatewayInvalidAmount(req.tokenAmount);
207210
}
208211

212+
// verify expiration timestamp
213+
if (req.expirationTimestamp < block.timestamp) {
214+
revert PaymentsGatewayRequestExpired(req.expirationTimestamp);
215+
}
216+
209217
// verify data
210218
if (!_verifyTransferStart(req, signature)) {
211219
revert PaymentsGatewayVerificationFailed();

test/PaymentsGateway.t.sol

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ contract PaymentsGatewayTest is Test {
6262

6363
function setUp() public {
6464
owner = payable(vm.addr(1));
65-
operator = payable(vm.addr(0x0000000000000000000000000000000000000000000000000000000000000002));
65+
operator = payable(vm.addr(2));
6666
sender = payable(vm.addr(3));
6767
receiver = payable(vm.addr(4));
6868
client = payable(vm.addr(5));
@@ -98,7 +98,7 @@ contract PaymentsGatewayTest is Test {
9898
// EIP712
9999
typehashPayoutInfo = keccak256("PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeBPS)");
100100
typehashPayRequest = keccak256(
101-
"PayRequest(bytes32 clientId,bytes32 transactionId,address tokenAddress,uint256 tokenAmount,PayoutInfo[] payouts,address forwardAddress,bytes data)PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeBPS)"
101+
"PayRequest(bytes32 clientId,bytes32 transactionId,address tokenAddress,uint256 tokenAmount,uint256 expirationTimestamp,PayoutInfo[] payouts,address forwardAddress,bytes data)PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeBPS)"
102102
);
103103
nameHash = keccak256(bytes("PaymentsGateway"));
104104
versionHash = keccak256(bytes("1"));
@@ -125,10 +125,10 @@ contract PaymentsGatewayTest is Test {
125125
function _hashPayoutInfo(PaymentsGateway.PayoutInfo[] memory _payouts) private view returns (bytes32) {
126126
bytes32 payoutHash = typehashPayoutInfo;
127127

128-
bytes32[] memory payoutsHashes = new bytes32[](payouts.length);
128+
bytes32[] memory payoutsHashes = new bytes32[](_payouts.length);
129129
for (uint i = 0; i < payouts.length; i++) {
130130
payoutsHashes[i] = keccak256(
131-
abi.encode(payoutHash, payouts[i].clientId, payouts[i].payoutAddress, payouts[i].feeBPS)
131+
abi.encode(payoutHash, _payouts[i].clientId, _payouts[i].payoutAddress, _payouts[i].feeBPS)
132132
);
133133
}
134134
return keccak256(abi.encodePacked(payoutsHashes));
@@ -147,6 +147,7 @@ contract PaymentsGatewayTest is Test {
147147
req.transactionId,
148148
req.tokenAddress,
149149
req.tokenAmount,
150+
req.expirationTimestamp,
150151
_payoutsHash,
151152
req.forwardAddress,
152153
keccak256(req.data)
@@ -184,12 +185,13 @@ contract PaymentsGatewayTest is Test {
184185
req.tokenAddress = address(mockERC20);
185186
req.tokenAmount = sendValue;
186187
req.forwardAddress = payable(address(mockTarget));
188+
req.expirationTimestamp = 1000;
187189
req.data = targetCalldata;
188190
req.payouts = payouts;
189191

190192
// generate signature
191193
bytes memory _signature = _prepareAndSignData(
192-
0x0000000000000000000000000000000000000000000000000000000000000002, // sign with operator private key, i.e. 2
194+
2, // sign with operator private key, i.e. 2
193195
req
194196
);
195197

@@ -224,6 +226,7 @@ contract PaymentsGatewayTest is Test {
224226
req.tokenAddress = address(0);
225227
req.tokenAmount = sendValue;
226228
req.forwardAddress = payable(address(mockTarget));
229+
req.expirationTimestamp = 1000;
227230
req.data = targetCalldata;
228231
req.payouts = payouts;
229232

@@ -235,7 +238,7 @@ contract PaymentsGatewayTest is Test {
235238

236239
// generate signature
237240
bytes memory _signature = _prepareAndSignData(
238-
0x0000000000000000000000000000000000000000000000000000000000000002, // sign with operator private key, i.e. 2
241+
2, // sign with operator private key, i.e. 2
239242
req
240243
);
241244

@@ -277,12 +280,13 @@ contract PaymentsGatewayTest is Test {
277280
req.tokenAddress = address(mockERC20);
278281
req.tokenAmount = sendValue;
279282
req.forwardAddress = payable(address(mockTarget));
283+
req.expirationTimestamp = 1000;
280284
req.data = targetCalldata;
281285
req.payouts = payouts;
282286

283287
// generate signature
284288
bytes memory _signature = _prepareAndSignData(
285-
0x0000000000000000000000000000000000000000000000000000000000000002, // sign with operator private key, i.e. 2
289+
2, // sign with operator private key, i.e. 2
286290
req
287291
);
288292

@@ -311,6 +315,7 @@ contract PaymentsGatewayTest is Test {
311315
req.tokenAddress = address(mockERC20);
312316
req.tokenAmount = sendValue;
313317
req.forwardAddress = payable(address(mockTarget));
318+
req.expirationTimestamp = 1000;
314319
req.data = targetCalldata;
315320
req.payouts = payouts;
316321

@@ -326,6 +331,35 @@ contract PaymentsGatewayTest is Test {
326331
gateway.startTransfer(req, _signature);
327332
}
328333

334+
function test_revert_startTransfer_requestExpired() public {
335+
uint256 sendValue = 1 ether;
336+
bytes memory targetCalldata = "";
337+
338+
// create pay request
339+
PaymentsGateway.PayRequest memory req;
340+
bytes32 _transactionId = keccak256("transaction ID");
341+
342+
req.clientId = clientId;
343+
req.transactionId = _transactionId;
344+
req.tokenAddress = address(mockERC20);
345+
req.tokenAmount = sendValue;
346+
req.forwardAddress = payable(address(mockTarget));
347+
req.expirationTimestamp = 1000;
348+
req.data = targetCalldata;
349+
req.payouts = payouts;
350+
351+
// generate signature
352+
bytes memory _signature = _prepareAndSignData(2, req);
353+
354+
vm.warp(req.expirationTimestamp + 1);
355+
// send transaction
356+
vm.prank(sender);
357+
vm.expectRevert(
358+
abi.encodeWithSelector(PaymentsGateway.PaymentsGatewayRequestExpired.selector, req.expirationTimestamp)
359+
);
360+
gateway.startTransfer(req, _signature);
361+
}
362+
329363
// /*///////////////////////////////////////////////////////////////
330364
// Test `endTransfer`
331365
// //////////////////////////////////////////////////////////////*/

test/benchmarks/BenchmarkPaymentsGateway.t.sol

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ contract BenchmarkPaymentsGatewayTest is Test {
3535

3636
function setUp() public {
3737
owner = payable(vm.addr(1));
38-
operator = payable(vm.addr(0x0000000000000000000000000000000000000000000000000000000000000002));
38+
operator = payable(vm.addr(2));
3939
sender = payable(vm.addr(3));
4040
receiver = payable(vm.addr(4));
4141
client = payable(vm.addr(5));
@@ -66,7 +66,7 @@ contract BenchmarkPaymentsGatewayTest is Test {
6666
// EIP712
6767
typehashPayoutInfo = keccak256("PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeBPS)");
6868
typehashPayRequest = keccak256(
69-
"PayRequest(bytes32 clientId,bytes32 transactionId,address tokenAddress,uint256 tokenAmount,PayoutInfo[] payouts,address forwardAddress,bytes data)PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeBPS)"
69+
"PayRequest(bytes32 clientId,bytes32 transactionId,address tokenAddress,uint256 tokenAmount,uint256 expirationTimestamp,PayoutInfo[] payouts,address forwardAddress,bytes data)PayoutInfo(bytes32 clientId,address payoutAddress,uint256 feeBPS)"
7070
);
7171
nameHash = keccak256(bytes("PaymentsGateway"));
7272
versionHash = keccak256(bytes("1"));
@@ -91,19 +91,12 @@ contract BenchmarkPaymentsGatewayTest is Test {
9191
}
9292

9393
function _hashPayoutInfo(PaymentsGateway.PayoutInfo[] memory _payouts) private view returns (bytes32) {
94-
// bytes32 payoutHash = keccak256(abi.encodePacked("PayoutInfo"));
9594
bytes32 payoutHash = typehashPayoutInfo;
96-
// for (uint256 i = 0; i < _payouts.length; ++i) {
97-
// payoutHash = keccak256(
98-
// abi.encode(payoutHash, _payouts[i].clientId, _payouts[i].payoutAddress, _payouts[i].feeBPS)
99-
// );
100-
// }
101-
// return payoutHash;
102-
103-
bytes32[] memory payoutsHashes = new bytes32[](payouts.length);
95+
96+
bytes32[] memory payoutsHashes = new bytes32[](_payouts.length);
10497
for (uint i = 0; i < payouts.length; i++) {
10598
payoutsHashes[i] = keccak256(
106-
abi.encode(payoutHash, payouts[i].clientId, payouts[i].payoutAddress, payouts[i].feeBPS)
99+
abi.encode(payoutHash, _payouts[i].clientId, _payouts[i].payoutAddress, _payouts[i].feeBPS)
107100
);
108101
}
109102
return keccak256(abi.encodePacked(payoutsHashes));
@@ -122,6 +115,7 @@ contract BenchmarkPaymentsGatewayTest is Test {
122115
req.transactionId,
123116
req.tokenAddress,
124117
req.tokenAmount,
118+
req.expirationTimestamp,
125119
_payoutsHash,
126120
req.forwardAddress,
127121
keccak256(req.data)
@@ -160,12 +154,13 @@ contract BenchmarkPaymentsGatewayTest is Test {
160154
req.tokenAddress = address(mockERC20);
161155
req.tokenAmount = sendValue;
162156
req.forwardAddress = payable(address(mockTarget));
157+
req.expirationTimestamp = 1000;
163158
req.data = targetCalldata;
164159
req.payouts = payouts;
165160

166161
// generate signature
167162
bytes memory _signature = _prepareAndSignData(
168-
0x0000000000000000000000000000000000000000000000000000000000000002, // sign with operator private key, i.e. 2
163+
2, // sign with operator private key, i.e. 2
169164
req
170165
);
171166

@@ -190,12 +185,13 @@ contract BenchmarkPaymentsGatewayTest is Test {
190185
req.tokenAddress = address(0);
191186
req.tokenAmount = sendValue;
192187
req.forwardAddress = payable(address(mockTarget));
188+
req.expirationTimestamp = 1000;
193189
req.data = targetCalldata;
194190
req.payouts = payouts;
195191

196192
// generate signature
197193
bytes memory _signature = _prepareAndSignData(
198-
0x0000000000000000000000000000000000000000000000000000000000000002, // sign with operator private key, i.e. 2
194+
2, // sign with operator private key, i.e. 2
199195
req
200196
);
201197

0 commit comments

Comments
 (0)