Skip to content

Commit db233dd

Browse files
committed
feat: adds dynamic protocol fee
1 parent b86ae27 commit db233dd

File tree

3 files changed

+34
-62
lines changed

3 files changed

+34
-62
lines changed

src/UniversalBridgeProxy.sol

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,7 @@ contract UniversalBridgeProxy {
1010
bytes32 private constant _ERC1967_IMPLEMENTATION_SLOT =
1111
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
1212

13-
constructor(
14-
address _implementation,
15-
address _owner,
16-
address _operator,
17-
address payable _protocolFeeRecipient,
18-
uint256 _protocolFeeBps
19-
) {
13+
constructor(address _implementation, address _owner, address _operator, address payable _protocolFeeRecipient) {
2014
if (_implementation == address(0)) {
2115
revert ImplementationZeroAddress();
2216
}
@@ -34,11 +28,10 @@ contract UniversalBridgeProxy {
3428
}
3529

3630
bytes memory data = abi.encodeWithSignature(
37-
"initialize(address,address,address,uint256)",
31+
"initialize(address,address,address)",
3832
_owner,
3933
_operator,
40-
_protocolFeeRecipient,
41-
_protocolFeeBps
34+
_protocolFeeRecipient
4235
);
4336
(bool success, ) = _implementation.delegatecall(data);
4437

src/UniversalBridgeV1.sol

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ library UniversalBridgeStorage {
2222
mapping(bytes32 => bool) processed;
2323
/// @dev Mapping from forward address or token address => whether restricted.
2424
mapping(address => bool) isRestricted;
25-
/// @dev protocol fee bps, capped at 300 bps (3%)
26-
uint256 protocolFeeBps;
2725
/// @dev protocol fee recipient address
2826
address protocolFeeRecipient;
2927
/// @dev whether the bridge is paused
@@ -55,7 +53,7 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
5553
uint256 tokenAmount;
5654
address payable forwardAddress;
5755
address payable spenderAddress;
58-
uint256 expirationTimestamp;
56+
uint256 protocolFeeBps;
5957
address payable developerFeeRecipient;
6058
uint256 developerFeeBps;
6159
bytes callData;
@@ -96,22 +94,16 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
9694
error UniversalBridgePaused(); // 0x46ecd2c9
9795
error UniversalBridgeRestrictedAddress(); // 0xec7d39b3
9896
error UniversalBridgeVerificationFailed(); // 0x1573f645
99-
error UniversalBridgeRequestExpired(uint256 expirationTimestamp); // 0xb4ebecd8
10097
error UniversalBridgeTransactionAlreadyProcessed(); // 0x7d21ae4b
10198

10299
constructor() {
103100
_disableInitializers();
104101
}
105102

106-
function initialize(
107-
address _owner,
108-
address _operator,
109-
address payable _protocolFeeRecipient,
110-
uint256 _protocolFeeBps
111-
) external initializer {
103+
function initialize(address _owner, address _operator, address payable _protocolFeeRecipient) external initializer {
112104
_initializeOwner(_owner);
113105
_grantRoles(_operator, _OPERATOR_ROLE);
114-
_setProtocolFeeInfo(_protocolFeeRecipient, _protocolFeeBps);
106+
_setProtocolFeeInfo(_protocolFeeRecipient);
115107
}
116108

117109
/*///////////////////////////////////////////////////////////////
@@ -136,8 +128,8 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
136128
}
137129
}
138130

139-
function setProtocolFeeInfo(address payable feeRecipient, uint256 feeBps) external onlyOwner {
140-
_setProtocolFeeInfo(feeRecipient, feeBps);
131+
function setProtocolFeeInfo(address payable feeRecipient) external onlyOwner {
132+
_setProtocolFeeInfo(feeRecipient);
141133
}
142134

143135
function pause(bool _pause) external onlyOwner {
@@ -148,9 +140,8 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
148140
_universalBridgeStorage().isRestricted[_target] = _restrict;
149141
}
150142

151-
function getProtocolFeeInfo() external view returns (address feeRecipient, uint256 feeBps) {
143+
function getProtocolFeeInfo() external view returns (address feeRecipient) {
152144
feeRecipient = _universalBridgeStorage().protocolFeeRecipient;
153-
feeBps = _universalBridgeStorage().protocolFeeBps;
154145
}
155146

156147
function isPaused() external view returns (bool) {
@@ -188,6 +179,10 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
188179
revert UniversalBridgeRestrictedAddress();
189180
}
190181

182+
if (req.protocolFeeBps > MAX_PROTOCOL_FEE_BPS) {
183+
revert UniversalBridgeInvalidFeeBps();
184+
}
185+
191186
// verify amount
192187
if (req.tokenAmount == 0) {
193188
revert UniversalBridgeInvalidAmount(req.tokenAmount);
@@ -200,7 +195,8 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
200195
req.tokenAddress,
201196
req.tokenAmount,
202197
req.developerFeeRecipient,
203-
req.developerFeeBps
198+
req.developerFeeBps,
199+
req.protocolFeeBps
204200
);
205201

206202
if (_isNativeToken(req.tokenAddress)) {
@@ -282,10 +278,6 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
282278
TransactionRequest calldata req,
283279
bytes calldata signature
284280
) private view returns (bool) {
285-
if (req.expirationTimestamp < block.timestamp) {
286-
revert UniversalBridgeRequestExpired(req.expirationTimestamp);
287-
}
288-
289281
bool processed = _universalBridgeStorage().processed[req.transactionId];
290282

291283
if (processed) {
@@ -300,7 +292,6 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
300292
req.tokenAmount,
301293
req.forwardAddress,
302294
req.spenderAddress,
303-
req.expirationTimestamp,
304295
req.developerFeeRecipient,
305296
req.developerFeeBps,
306297
keccak256(req.callData),
@@ -319,10 +310,10 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
319310
address tokenAddress,
320311
uint256 tokenAmount,
321312
address developerFeeRecipient,
322-
uint256 developerFeeBps
313+
uint256 developerFeeBps,
314+
uint256 protocolFeeBps
323315
) private returns (uint256, uint256) {
324316
address protocolFeeRecipient = _universalBridgeStorage().protocolFeeRecipient;
325-
uint256 protocolFeeBps = _universalBridgeStorage().protocolFeeBps;
326317

327318
uint256 protocolFee = (tokenAmount * protocolFeeBps) / 10_000;
328319
uint256 developerFee = (tokenAmount * developerFeeBps) / 10_000;
@@ -353,17 +344,12 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
353344
version = "1";
354345
}
355346

356-
function _setProtocolFeeInfo(address payable feeRecipient, uint256 feeBps) internal {
347+
function _setProtocolFeeInfo(address payable feeRecipient) internal {
357348
if (feeRecipient == address(0)) {
358349
revert UniversalBridgeZeroAddress();
359350
}
360351

361-
if (feeBps > MAX_PROTOCOL_FEE_BPS) {
362-
revert UniversalBridgeInvalidFeeBps();
363-
}
364-
365352
_universalBridgeStorage().protocolFeeRecipient = feeRecipient;
366-
_universalBridgeStorage().protocolFeeBps = feeBps;
367353
}
368354

369355
function _isNativeToken(address tokenAddress) private pure returns (bool) {
@@ -372,7 +358,7 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
372358

373359
function _authorizeUpgrade(address) internal override onlyOwner {}
374360

375-
function _universalBridgeStorage() internal view returns (UniversalBridgeStorage.Data storage) {
361+
function _universalBridgeStorage() internal pure returns (UniversalBridgeStorage.Data storage) {
376362
return UniversalBridgeStorage.data();
377363
}
378364
}

test/UniversalBridgeV1.t.sol

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ contract UniversalBridgeTest is Test {
2020
bytes32 indexed transactionId,
2121
address tokenAddress,
2222
uint256 tokenAmount,
23+
uint256 protocolFee,
2324
address developerFeeRecipient,
2425
uint256 developerFeeBps,
2526
uint256 developerFee,
26-
uint256 protocolFee,
2727
bytes extraData
2828
);
2929

@@ -73,9 +73,7 @@ contract UniversalBridgeTest is Test {
7373

7474
// deploy impl and proxy
7575
address impl = address(new UniversalBridgeV1());
76-
bridge = UniversalBridgeV1(
77-
address(new UniversalBridgeProxy(impl, owner, operator, protocolFeeRecipient, protocolFeeBps))
78-
);
76+
bridge = UniversalBridgeV1(address(new UniversalBridgeProxy(impl, owner, operator, protocolFeeRecipient)));
7977

8078
mockERC20 = new MockERC20("Token", "TKN");
8179
mockTarget = new MockTarget();
@@ -137,7 +135,7 @@ contract UniversalBridgeTest is Test {
137135
req.tokenAmount,
138136
req.forwardAddress,
139137
req.spenderAddress,
140-
req.expirationTimestamp,
138+
req.protocolFeeBps,
141139
req.developerFeeRecipient,
142140
req.developerFeeBps,
143141
keccak256(req.callData),
@@ -174,9 +172,9 @@ contract UniversalBridgeTest is Test {
174172
req.tokenAmount = sendValue;
175173
req.forwardAddress = payable(address(mockTarget));
176174
req.spenderAddress = payable(address(mockTarget));
177-
req.expirationTimestamp = 1000;
178175
req.developerFeeRecipient = developer;
179176
req.developerFeeBps = developerFeeBps;
177+
req.protocolFeeBps = protocolFeeBps;
180178
req.callData = targetCalldata;
181179

182180
// generate signature
@@ -218,9 +216,9 @@ contract UniversalBridgeTest is Test {
218216
req.tokenAmount = sendValue;
219217
req.forwardAddress = payable(address(mockTargetNonSpender));
220218
req.spenderAddress = payable(address(mockSpender));
221-
req.expirationTimestamp = 1000;
222219
req.developerFeeRecipient = developer;
223220
req.developerFeeBps = developerFeeBps;
221+
req.protocolFeeBps = protocolFeeBps;
224222
req.callData = targetCalldata;
225223

226224
// generate signature
@@ -260,9 +258,9 @@ contract UniversalBridgeTest is Test {
260258
req.tokenAmount = sendValue;
261259
req.forwardAddress = payable(address(receiver));
262260
req.spenderAddress = payable(address(0));
263-
req.expirationTimestamp = 1000;
264261
req.developerFeeRecipient = developer;
265262
req.developerFeeBps = developerFeeBps;
263+
req.protocolFeeBps = protocolFeeBps;
266264

267265
// generate signature
268266
bytes memory _signature = _prepareAndSignData(
@@ -305,9 +303,9 @@ contract UniversalBridgeTest is Test {
305303
req.tokenAmount = sendValue;
306304
req.forwardAddress = payable(address(mockTarget));
307305
req.spenderAddress = payable(address(mockTarget));
308-
req.expirationTimestamp = 1000;
309306
req.developerFeeRecipient = developer;
310307
req.developerFeeBps = developerFeeBps;
308+
req.protocolFeeBps = protocolFeeBps;
311309
req.callData = targetCalldata;
312310

313311
// generate signature
@@ -351,9 +349,9 @@ contract UniversalBridgeTest is Test {
351349
req.tokenAmount = sendValue;
352350
req.forwardAddress = payable(address(mockTargetNonSpender));
353351
req.spenderAddress = payable(address(mockSpender));
354-
req.expirationTimestamp = 1000;
355352
req.developerFeeRecipient = developer;
356353
req.developerFeeBps = developerFeeBps;
354+
req.protocolFeeBps = protocolFeeBps;
357355
req.callData = targetCalldata;
358356

359357
// generate signature
@@ -391,9 +389,9 @@ contract UniversalBridgeTest is Test {
391389
req.tokenAmount = sendValue;
392390
req.forwardAddress = payable(address(receiver));
393391
req.spenderAddress = payable(address(0));
394-
req.expirationTimestamp = 1000;
395392
req.developerFeeRecipient = developer;
396393
req.developerFeeBps = developerFeeBps;
394+
req.protocolFeeBps = protocolFeeBps;
397395
req.callData = targetCalldata;
398396

399397
// generate signature
@@ -435,9 +433,9 @@ contract UniversalBridgeTest is Test {
435433
req.tokenAmount = sendValue;
436434
req.forwardAddress = payable(address(mockTarget));
437435
req.spenderAddress = payable(address(mockTarget));
438-
req.expirationTimestamp = 1000;
439436
req.developerFeeRecipient = developer;
440437
req.developerFeeBps = developerFeeBps;
438+
req.protocolFeeBps = protocolFeeBps;
441439
req.callData = targetCalldata;
442440

443441
// generate signature
@@ -454,10 +452,10 @@ contract UniversalBridgeTest is Test {
454452
_transactionId,
455453
address(mockERC20),
456454
sendValue,
455+
expectedProtocolFee,
457456
developer,
458457
developerFeeBps,
459458
expectedDeveloperFee,
460-
expectedProtocolFee,
461459
""
462460
);
463461
bridge.initiateTransaction(req, _signature);
@@ -471,7 +469,6 @@ contract UniversalBridgeTest is Test {
471469
req.transactionId = _transactionId;
472470
req.tokenAddress = address(mockERC20);
473471
req.tokenAmount = 0;
474-
req.expirationTimestamp = 1000;
475472

476473
// generate signature
477474
bytes memory _signature = _prepareAndSignData(
@@ -497,9 +494,9 @@ contract UniversalBridgeTest is Test {
497494
req.tokenAmount = sendValue;
498495
req.forwardAddress = payable(address(receiver));
499496
req.spenderAddress = payable(address(0));
500-
req.expirationTimestamp = 1000;
501497
req.developerFeeRecipient = developer;
502498
req.developerFeeBps = developerFeeBps;
499+
req.protocolFeeBps = protocolFeeBps;
503500
req.callData = targetCalldata;
504501

505502
// generate signature
@@ -530,9 +527,9 @@ contract UniversalBridgeTest is Test {
530527
req.tokenAmount = sendValue;
531528
req.forwardAddress = payable(address(receiver));
532529
req.spenderAddress = payable(address(0));
533-
req.expirationTimestamp = 1000;
534530
req.developerFeeRecipient = developer;
535531
req.developerFeeBps = developerFeeBps;
532+
req.protocolFeeBps = protocolFeeBps;
536533
req.callData = "";
537534

538535
// generate signature
@@ -553,7 +550,6 @@ contract UniversalBridgeTest is Test {
553550
bytes32 _transactionId = keccak256("transaction ID");
554551

555552
req.transactionId = _transactionId;
556-
req.expirationTimestamp = 1000;
557553

558554
// generate signature
559555
bytes memory _signature = _prepareAndSignData(
@@ -576,7 +572,6 @@ contract UniversalBridgeTest is Test {
576572

577573
req.transactionId = _transactionId;
578574
req.forwardAddress = payable(address(receiver));
579-
req.expirationTimestamp = 1000;
580575

581576
// generate signature
582577
bytes memory _signature = _prepareAndSignData(
@@ -600,7 +595,6 @@ contract UniversalBridgeTest is Test {
600595
req.transactionId = _transactionId;
601596
req.tokenAddress = address(mockERC20);
602597
req.forwardAddress = payable(address(receiver));
603-
req.expirationTimestamp = 1000;
604598

605599
// generate signature
606600
bytes memory _signature = _prepareAndSignData(
@@ -640,9 +634,9 @@ contract UniversalBridgeTest is Test {
640634
req.tokenAmount = sendValue;
641635
req.forwardAddress = payable(address(mockRefundTarget));
642636
req.spenderAddress = payable(address(mockRefundTarget));
643-
req.expirationTimestamp = 1000;
644637
req.developerFeeRecipient = developer;
645638
req.developerFeeBps = developerFeeBps;
639+
req.protocolFeeBps = protocolFeeBps;
646640
req.callData = targetCalldata;
647641

648642
// generate signature
@@ -684,7 +678,6 @@ contract UniversalBridgeTest is Test {
684678
vm.prank(sender);
685679
mockERC20.approve(address(bridge), sendValueWithFees);
686680

687-
688681
// create transaction request
689682
UniversalBridgeV1.TransactionRequest memory req;
690683
bytes32 _transactionId = keccak256("erc20 refund transaction ID");
@@ -694,9 +687,9 @@ contract UniversalBridgeTest is Test {
694687
req.tokenAmount = sendValue;
695688
req.forwardAddress = payable(address(mockRefundTarget));
696689
req.spenderAddress = payable(address(mockRefundTarget));
697-
req.expirationTimestamp = 1000;
698690
req.developerFeeRecipient = developer;
699691
req.developerFeeBps = developerFeeBps;
692+
req.protocolFeeBps = protocolFeeBps;
700693
req.callData = targetCalldata;
701694

702695
// generate signature

0 commit comments

Comments
 (0)