@@ -5,6 +5,7 @@ import "@openzeppelin/contracts/access/Ownable.sol";
5
5
import "@openzeppelin/contracts/utils/Context.sol " ;
6
6
import "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
7
7
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol " ;
8
+ import { SafeTransferLib } from "./lib/SafeTransferLib.sol " ;
8
9
9
10
/**
10
11
Requirements
@@ -18,6 +19,13 @@ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
18
19
*/
19
20
20
21
contract PaymentsGateway is Ownable , ReentrancyGuard {
22
+ error PaymentsGatewayInvalidOperator (address operator );
23
+ error PaymentsGatewayNotOwnerOrOperator (address caller );
24
+ error PaymentsGatewayMismatchedValue (uint256 expected , uint256 actual );
25
+ error PaymentsGatewayInvalidAmount (uint256 amount );
26
+ error PaymentsGatewayVerificationFailed ();
27
+ error PaymentsGatewayFailedToForward ();
28
+
21
29
event TransferStart (
22
30
bytes32 indexed clientId ,
23
31
address indexed sender ,
@@ -62,18 +70,24 @@ contract PaymentsGateway is Ownable, ReentrancyGuard {
62
70
address private _operator;
63
71
64
72
constructor (address contractOwner , address initialOperator ) Ownable (contractOwner) {
65
- require (initialOperator != address (0 ), "Operator can't be the zero address " );
73
+ if (initialOperator == address (0 )) {
74
+ revert PaymentsGatewayInvalidOperator (initialOperator);
75
+ }
66
76
_operator = initialOperator;
67
77
emit OperatorChanged (address (0 ), initialOperator);
68
78
}
69
79
70
80
modifier onlyOwnerOrOperator () {
71
- require (msg .sender == owner () || msg .sender == _operator, "Caller is not the owner or operator " );
81
+ if (msg .sender != owner () && msg .sender != _operator) {
82
+ revert PaymentsGatewayNotOwnerOrOperator (msg .sender );
83
+ }
72
84
_;
73
85
}
74
86
75
87
function setOperator (address newOperator ) public onlyOwnerOrOperator {
76
- require (newOperator != address (0 ), "Operator can't be the zero address " );
88
+ if (newOperator == address (0 )) {
89
+ revert PaymentsGatewayInvalidOperator (newOperator);
90
+ }
77
91
emit OperatorChanged (_operator, newOperator);
78
92
_operator = newOperator;
79
93
}
@@ -89,13 +103,9 @@ contract PaymentsGateway is Ownable, ReentrancyGuard {
89
103
address payable receiver
90
104
) public onlyOwnerOrOperator nonReentrant {
91
105
if (_isTokenERC20 (tokenAddress)) {
92
- require (
93
- IERC20 (tokenAddress).transferFrom (address (this ), receiver, tokenAmount),
94
- "Failed to withdraw funds "
95
- );
106
+ SafeTransferLib.safeTransferFrom (tokenAddress, address (this ), receiver, tokenAmount);
96
107
} else {
97
- (bool sent , ) = receiver.call { value: tokenAmount }("" );
98
- require (sent, "Failed to withdraw funds " );
108
+ SafeTransferLib.safeTransferETH (receiver, tokenAmount);
99
109
}
100
110
}
101
111
@@ -136,17 +146,15 @@ contract PaymentsGateway is Ownable, ReentrancyGuard {
136
146
payouts[payeeIdx].feeBPS
137
147
);
138
148
if (_isTokenNative (tokenAddress)) {
139
- (bool sent , ) = payouts[payeeIdx].payoutAddress.call { value: feeAmount }("" );
140
- require (sent, "Failed to distribute fees " );
149
+ SafeTransferLib.safeTransferETH (payouts[payeeIdx].payoutAddress, feeAmount);
141
150
} else {
142
- require (
143
- IERC20 (tokenAddress).transferFrom (msg .sender , payouts[payeeIdx].payoutAddress, feeAmount),
144
- "Token Fee Transfer Failed "
145
- );
151
+ SafeTransferLib.safeTransferFrom (tokenAddress, msg .sender , payouts[payeeIdx].payoutAddress, feeAmount);
146
152
}
147
153
}
148
154
149
- require (totalFeeAmount < tokenAmount, "fees exceeded tokenAmount " );
155
+ if (totalFeeAmount > tokenAmount) {
156
+ revert PaymentsGatewayMismatchedValue (totalFeeAmount, tokenAmount);
157
+ }
150
158
return totalFeeAmount;
151
159
}
152
160
@@ -207,7 +215,6 @@ contract PaymentsGateway is Ownable, ReentrancyGuard {
207
215
return (recovered, valid);
208
216
}
209
217
210
-
211
218
/**
212
219
The purpose of startTransfer is to be the entrypoint for all thirdweb pay swap / bridge
213
220
transactions. This function will allow us to standardize the logging and fee splitting across all providers.
@@ -229,11 +236,13 @@ contract PaymentsGateway is Ownable, ReentrancyGuard {
229
236
bytes calldata signature
230
237
) external payable nonReentrant {
231
238
// verify amount
232
- require (tokenAmount > 0 , "token amount must be greater than zero " );
239
+ if (tokenAmount == 0 ) {
240
+ revert PaymentsGatewayInvalidAmount (tokenAmount);
241
+ }
233
242
234
243
// verify data
235
- require (
236
- _verifyTransferStart (
244
+ if (
245
+ ! _verifyTransferStart (
237
246
clientId,
238
247
transactionId,
239
248
tokenAddress,
@@ -242,39 +251,52 @@ contract PaymentsGateway is Ownable, ReentrancyGuard {
242
251
forwardAddress,
243
252
data,
244
253
signature
245
- ),
246
- "failed to verify transaction "
247
- );
254
+ )
255
+ ) {
256
+ revert PaymentsGatewayVerificationFailed ();
257
+ }
248
258
249
259
if (_isTokenNative (tokenAddress)) {
250
- require (msg .value >= tokenAmount, "msg value must be gte than token amount " );
260
+ if (msg .value < tokenAmount) {
261
+ revert PaymentsGatewayMismatchedValue (tokenAmount, msg .value );
262
+ }
251
263
}
252
264
253
- emit TransferStart (clientId, msg .sender , transactionId, tokenAddress, tokenAmount);
254
-
255
265
// distribute fees
256
266
uint256 totalFeeAmount = _distributeFees (tokenAddress, tokenAmount, payouts);
257
267
258
268
// determine native value to send
259
- uint256 sendValue = msg .value ;
269
+ uint256 sendValue = msg .value ; // includes bridge fee etc. (if any)
260
270
if (_isTokenNative (tokenAddress)) {
261
271
sendValue = msg .value - totalFeeAmount;
262
- require (sendValue <= msg .value , "send value cannot exceed msg value " );
263
- require (sendValue >= tokenAmount, "send value must cover tokenAmount " );
272
+
273
+ if (sendValue < tokenAmount) {
274
+ revert PaymentsGatewayMismatchedValue (sendValue, tokenAmount);
275
+ }
264
276
}
265
277
266
278
if (_isTokenERC20 (tokenAddress)) {
267
279
// pull user funds
268
- require (
269
- IERC20 (tokenAddress).transferFrom (msg .sender , address (this ), tokenAmount),
270
- "Failed to pull user erc20 funds "
271
- );
280
+ SafeTransferLib.safeTransferFrom (tokenAddress, msg .sender , address (this ), tokenAmount);
281
+ SafeTransferLib.safeApprove (tokenAddress, forwardAddress, tokenAmount);
282
+ }
272
283
273
- require (IERC20 (tokenAddress).approve (forwardAddress, tokenAmount), "Failed to approve forwarder " );
284
+ {
285
+ (bool success , bytes memory response ) = forwardAddress.call { value: sendValue }(data);
286
+ if (! success) {
287
+ // If there is return data, the delegate call reverted with a reason or a custom error, which we bubble up.
288
+ if (response.length > 0 ) {
289
+ assembly {
290
+ let returndata_size := mload (response)
291
+ revert (add (32 , response), returndata_size)
292
+ }
293
+ } else {
294
+ revert PaymentsGatewayFailedToForward ();
295
+ }
296
+ }
274
297
}
275
298
276
- (bool success , ) = forwardAddress.call { value: sendValue }(data);
277
- require (success, "Failed to forward " );
299
+ emit TransferStart (clientId, msg .sender , transactionId, tokenAddress, tokenAmount);
278
300
}
279
301
280
302
/**
@@ -294,23 +316,23 @@ contract PaymentsGateway is Ownable, ReentrancyGuard {
294
316
uint256 tokenAmount ,
295
317
address payable receiverAddress
296
318
) external payable nonReentrant {
297
- require (tokenAmount > 0 , "token amount must be greater than zero " );
319
+ if (tokenAmount == 0 ) {
320
+ revert PaymentsGatewayInvalidAmount (tokenAmount);
321
+ }
298
322
299
323
if (_isTokenNative (tokenAddress)) {
300
- require (msg .value >= tokenAmount, "msg value must be gte token amount " );
324
+ if (msg .value < tokenAmount) {
325
+ revert PaymentsGatewayMismatchedValue (tokenAmount, msg .value );
326
+ }
301
327
}
302
328
303
- emit TransferEnd (clientId, receiverAddress, transactionId, tokenAddress, tokenAmount);
304
-
305
329
// pull user funds
306
330
if (_isTokenERC20 (tokenAddress)) {
307
- require (
308
- IERC20 (tokenAddress).transferFrom (msg .sender , receiverAddress, tokenAmount),
309
- "Failed to forward erc20 funds "
310
- );
331
+ SafeTransferLib.safeTransferFrom (tokenAddress, msg .sender , receiverAddress, tokenAmount);
311
332
} else {
312
- (bool success , ) = receiverAddress.call { value: tokenAmount }("" );
313
- require (success, "Failed to send to reciever " );
333
+ SafeTransferLib.safeTransferETH (receiverAddress, tokenAmount);
314
334
}
335
+
336
+ emit TransferEnd (clientId, receiverAddress, transactionId, tokenAddress, tokenAmount);
315
337
}
316
338
}
0 commit comments