@hyperlane-xyz/multicollateral 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contracts/{MultiCollateral.sol → CrossCollateralRouter.sol} +131 -46
- package/contracts/{MultiCollateralRoutingFee.sol → CrossCollateralRoutingFee.sol} +9 -6
- package/contracts/TokenBridgeOft.sol +338 -0
- package/contracts/interfaces/{IMultiCollateralFee.sol → ICrossCollateralFee.sol} +1 -1
- package/contracts/interfaces/layerzero/IOFT.sol +85 -0
- package/dist/typechain/{MultiCollateral.d.ts → CrossCollateralRouter.d.ts} +89 -79
- package/dist/typechain/CrossCollateralRouter.d.ts.map +1 -0
- package/dist/typechain/CrossCollateralRouter.js +2 -0
- package/dist/typechain/CrossCollateralRouter.js.map +1 -0
- package/dist/typechain/{MultiCollateralRoutingFee.d.ts → CrossCollateralRoutingFee.d.ts} +13 -13
- package/dist/typechain/{MultiCollateralRoutingFee.d.ts.map → CrossCollateralRoutingFee.d.ts.map} +1 -1
- package/dist/typechain/CrossCollateralRoutingFee.js +2 -0
- package/dist/typechain/CrossCollateralRoutingFee.js.map +1 -0
- package/dist/typechain/TokenBridgeOft.d.ts +293 -0
- package/dist/typechain/TokenBridgeOft.d.ts.map +1 -0
- package/dist/typechain/TokenBridgeOft.js +2 -0
- package/dist/typechain/TokenBridgeOft.js.map +1 -0
- package/dist/typechain/factories/{MultiCollateral__factory.d.ts → CrossCollateralRouter__factory.d.ts} +97 -87
- package/dist/typechain/factories/{MultiCollateral__factory.d.ts.map → CrossCollateralRouter__factory.d.ts.map} +1 -1
- package/dist/typechain/factories/CrossCollateralRouter__factory.js +1868 -0
- package/dist/typechain/factories/CrossCollateralRouter__factory.js.map +1 -0
- package/dist/typechain/factories/{MultiCollateralRoutingFee__factory.d.ts → CrossCollateralRoutingFee__factory.d.ts} +12 -12
- package/dist/typechain/factories/{MultiCollateralRoutingFee__factory.d.ts.map → CrossCollateralRoutingFee__factory.d.ts.map} +1 -1
- package/dist/typechain/factories/{MultiCollateralRoutingFee__factory.js → CrossCollateralRoutingFee__factory.js} +6 -6
- package/dist/typechain/factories/{MultiCollateralRoutingFee__factory.js.map → CrossCollateralRoutingFee__factory.js.map} +1 -1
- package/dist/typechain/factories/TokenBridgeOft__factory.d.ts +312 -0
- package/dist/typechain/factories/TokenBridgeOft__factory.d.ts.map +1 -0
- package/dist/typechain/factories/TokenBridgeOft__factory.js +417 -0
- package/dist/typechain/factories/TokenBridgeOft__factory.js.map +1 -0
- package/dist/typechain/factories/index.d.ts +3 -2
- package/dist/typechain/factories/index.d.ts.map +1 -1
- package/dist/typechain/factories/index.js +3 -2
- package/dist/typechain/factories/index.js.map +1 -1
- package/dist/typechain/index.d.ts +6 -4
- package/dist/typechain/index.d.ts.map +1 -1
- package/dist/typechain/index.js +3 -2
- package/dist/typechain/index.js.map +1 -1
- package/package.json +7 -6
- package/dist/typechain/MultiCollateral.d.ts.map +0 -1
- package/dist/typechain/MultiCollateral.js +0 -2
- package/dist/typechain/MultiCollateral.js.map +0 -1
- package/dist/typechain/MultiCollateralRoutingFee.js +0 -2
- package/dist/typechain/MultiCollateralRoutingFee.js.map +0 -1
- package/dist/typechain/factories/MultiCollateral__factory.js +0 -1855
- package/dist/typechain/factories/MultiCollateral__factory.js.map +0 -1
|
@@ -24,36 +24,50 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
|
|
|
24
24
|
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
|
25
25
|
|
|
26
26
|
// ============ Local Imports ============
|
|
27
|
-
import {
|
|
27
|
+
import {ICrossCollateralFee} from "./interfaces/ICrossCollateralFee.sol";
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* @title
|
|
30
|
+
* @title CrossCollateralRouter
|
|
31
31
|
* @notice Multi-router collateral: direct 1-message atomic transfers between
|
|
32
32
|
* collateral routers, both cross-chain and same-chain.
|
|
33
33
|
* @dev Extends HypERC20Collateral. Each deployed instance holds collateral for
|
|
34
|
-
* one ERC20. Enrolled routers are other
|
|
34
|
+
* one ERC20. Enrolled routers are other CrossCollateralRouter instances (same or
|
|
35
35
|
* different token) that this instance trusts to send/receive transfers.
|
|
36
|
+
* CrossCollateralRouter assumes standard ERC20 behavior with exact transfer
|
|
37
|
+
* amounts. Rebasing tokens, fee-on-transfer tokens, and ERC777 tokens are not
|
|
38
|
+
* supported due to exact-amount accounting in transfer/handle flows.
|
|
36
39
|
*
|
|
37
40
|
* Overrides:
|
|
38
41
|
* - handle(): accepts messages from the mailbox (cross-chain) or directly
|
|
39
42
|
* from enrolled routers on the same chain.
|
|
40
43
|
*/
|
|
41
|
-
contract
|
|
44
|
+
contract CrossCollateralRouter is HypERC20Collateral, ICrossCollateralFee {
|
|
42
45
|
using TypeCasts for address;
|
|
43
46
|
using TypeCasts for bytes32;
|
|
44
47
|
using SafeERC20 for IERC20;
|
|
45
48
|
using EnumerableSet for EnumerableSet.Bytes32Set;
|
|
49
|
+
using EnumerableSet for EnumerableSet.UintSet;
|
|
46
50
|
|
|
47
51
|
// ============ Events ============
|
|
48
52
|
|
|
49
|
-
event
|
|
50
|
-
|
|
53
|
+
event CrossCollateralRouterEnrolled(
|
|
54
|
+
uint32 indexed domain,
|
|
55
|
+
bytes32 indexed router
|
|
56
|
+
);
|
|
57
|
+
event CrossCollateralRouterUnenrolled(
|
|
58
|
+
uint32 indexed domain,
|
|
59
|
+
bytes32 indexed router
|
|
60
|
+
);
|
|
51
61
|
|
|
52
62
|
// ============ Storage ============
|
|
53
63
|
|
|
54
64
|
/// @notice Additional enrolled routers by domain (beyond the standard
|
|
55
65
|
/// enrolled remote router). Local routers use localDomain as key.
|
|
56
|
-
mapping(uint32 => EnumerableSet.Bytes32Set) private
|
|
66
|
+
mapping(uint32 => EnumerableSet.Bytes32Set) private _crossCollateralRouters;
|
|
67
|
+
|
|
68
|
+
/// @notice Tracks which domains have at least one CrossCollateral-enrolled router,
|
|
69
|
+
/// enabling on-chain enumeration for the SDK reader.
|
|
70
|
+
EnumerableSet.UintSet private _crossCollateralDomains;
|
|
57
71
|
|
|
58
72
|
// ============ Constructor ============
|
|
59
73
|
|
|
@@ -66,43 +80,91 @@ contract MultiCollateral is HypERC20Collateral, IMultiCollateralFee {
|
|
|
66
80
|
|
|
67
81
|
// ============ Router Management (onlyOwner) ============
|
|
68
82
|
|
|
69
|
-
function
|
|
83
|
+
function enrollCrossCollateralRouters(
|
|
70
84
|
uint32[] calldata _domains,
|
|
71
85
|
bytes32[] calldata _routers
|
|
72
86
|
) external onlyOwner {
|
|
73
|
-
require(_domains.length == _routers.length, "
|
|
87
|
+
require(_domains.length == _routers.length, "CCR: length mismatch");
|
|
74
88
|
for (uint256 i = 0; i < _domains.length; i++) {
|
|
75
|
-
if (
|
|
76
|
-
|
|
89
|
+
if (_crossCollateralRouters[_domains[i]].add(_routers[i])) {
|
|
90
|
+
_crossCollateralDomains.add(uint256(_domains[i]));
|
|
91
|
+
emit CrossCollateralRouterEnrolled(_domains[i], _routers[i]);
|
|
77
92
|
}
|
|
78
93
|
}
|
|
79
94
|
}
|
|
80
95
|
|
|
81
|
-
function
|
|
96
|
+
function unenrollCrossCollateralRouters(
|
|
82
97
|
uint32[] calldata _domains,
|
|
83
98
|
bytes32[] calldata _routers
|
|
84
99
|
) external onlyOwner {
|
|
85
|
-
require(_domains.length == _routers.length, "
|
|
100
|
+
require(_domains.length == _routers.length, "CCR: length mismatch");
|
|
86
101
|
for (uint256 i = 0; i < _domains.length; i++) {
|
|
87
|
-
if (
|
|
88
|
-
|
|
102
|
+
if (_crossCollateralRouters[_domains[i]].remove(_routers[i])) {
|
|
103
|
+
if (_crossCollateralRouters[_domains[i]].length() == 0) {
|
|
104
|
+
_crossCollateralDomains.remove(uint256(_domains[i]));
|
|
105
|
+
}
|
|
106
|
+
emit CrossCollateralRouterUnenrolled(_domains[i], _routers[i]);
|
|
89
107
|
}
|
|
90
108
|
}
|
|
91
109
|
}
|
|
92
110
|
|
|
93
|
-
function
|
|
111
|
+
function crossCollateralRouters(
|
|
94
112
|
uint32 _domain,
|
|
95
113
|
bytes32 _router
|
|
96
114
|
) external view returns (bool) {
|
|
97
|
-
return
|
|
115
|
+
return _crossCollateralRouters[_domain].contains(_router);
|
|
98
116
|
}
|
|
99
117
|
|
|
100
118
|
// ============ Enumeration ============
|
|
101
119
|
|
|
102
|
-
function
|
|
120
|
+
function getCrossCollateralRouters(
|
|
103
121
|
uint32 _domain
|
|
104
122
|
) external view returns (bytes32[] memory) {
|
|
105
|
-
return
|
|
123
|
+
return _crossCollateralRouters[_domain].values();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/// @notice Returns all domains that have at least one CrossCollateral-enrolled router.
|
|
127
|
+
function getCrossCollateralDomains()
|
|
128
|
+
external
|
|
129
|
+
view
|
|
130
|
+
returns (uint32[] memory domains)
|
|
131
|
+
{
|
|
132
|
+
uint256 len = _crossCollateralDomains.length();
|
|
133
|
+
domains = new uint32[](len);
|
|
134
|
+
for (uint256 i = 0; i < len; i++) {
|
|
135
|
+
domains[i] = uint32(_crossCollateralDomains.at(i));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ============ Destination Gas Override ============
|
|
140
|
+
|
|
141
|
+
/// @dev Overrides GasRouter._setDestinationGas to also accept CrossCollateral-enrolled
|
|
142
|
+
/// domains (not just default Router._routers). Excludes localDomain since
|
|
143
|
+
/// same-chain transfers skip mailbox dispatch.
|
|
144
|
+
function _setDestinationGas(uint32 domain, uint256 gas) internal override {
|
|
145
|
+
require(domain != localDomain, "CCR: no gas for local domain");
|
|
146
|
+
require(
|
|
147
|
+
routers(domain) != bytes32(0) ||
|
|
148
|
+
_crossCollateralRouters[domain].length() > 0,
|
|
149
|
+
"CCR: domain has no routers"
|
|
150
|
+
);
|
|
151
|
+
destinationGas[domain] = gas;
|
|
152
|
+
emit GasSet(domain, gas);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ============ Internal Helpers ============
|
|
156
|
+
|
|
157
|
+
/// @dev Reverts unless `_router` is enrolled for `_domain` (either via the
|
|
158
|
+
/// standard Router._routers map or via the CrossCollateral-specific _crossCollateralRouters set).
|
|
159
|
+
function _requireAuthorizedRouter(
|
|
160
|
+
uint32 _domain,
|
|
161
|
+
bytes32 _router
|
|
162
|
+
) internal view {
|
|
163
|
+
require(
|
|
164
|
+
_isRemoteRouter(_domain, _router) ||
|
|
165
|
+
_crossCollateralRouters[_domain].contains(_router),
|
|
166
|
+
"CCR: unauthorized router"
|
|
167
|
+
);
|
|
106
168
|
}
|
|
107
169
|
|
|
108
170
|
// ============ Handle Override ============
|
|
@@ -119,18 +181,14 @@ contract MultiCollateral is HypERC20Collateral, IMultiCollateralFee {
|
|
|
119
181
|
) external payable override {
|
|
120
182
|
if (msg.sender == address(mailbox)) {
|
|
121
183
|
// Cross-chain via mailbox: sender must be enrolled
|
|
122
|
-
|
|
123
|
-
_isRemoteRouter(_origin, _sender) ||
|
|
124
|
-
_enrolledRouters[_origin].contains(_sender),
|
|
125
|
-
"MC: unauthorized router"
|
|
126
|
-
);
|
|
184
|
+
_requireAuthorizedRouter(_origin, _sender);
|
|
127
185
|
} else {
|
|
128
186
|
// Same-chain direct call: caller must be an enrolled router
|
|
129
187
|
require(
|
|
130
|
-
|
|
188
|
+
_crossCollateralRouters[localDomain].contains(
|
|
131
189
|
TypeCasts.addressToBytes32(msg.sender)
|
|
132
190
|
),
|
|
133
|
-
"
|
|
191
|
+
"CCR: unauthorized router"
|
|
134
192
|
);
|
|
135
193
|
}
|
|
136
194
|
_handle(_origin, _sender, _message);
|
|
@@ -138,7 +196,7 @@ contract MultiCollateral is HypERC20Collateral, IMultiCollateralFee {
|
|
|
138
196
|
|
|
139
197
|
// ============ Per-Router Fee Lookup ============
|
|
140
198
|
// Mirrors TokenRouter._feeRecipientAndAmount but routes through
|
|
141
|
-
//
|
|
199
|
+
// ICrossCollateralFee.quoteTransferRemoteTo (which includes _targetRouter)
|
|
142
200
|
// instead of ITokenFee.quoteTransferRemote (destination-only).
|
|
143
201
|
|
|
144
202
|
function _feeRecipientAndAmountForRouter(
|
|
@@ -151,7 +209,7 @@ contract MultiCollateral is HypERC20Collateral, IMultiCollateralFee {
|
|
|
151
209
|
if (_feeRecipient == address(0)) return (_feeRecipient, 0);
|
|
152
210
|
|
|
153
211
|
// Only difference from base: quoteTransferRemoteTo with _targetRouter
|
|
154
|
-
Quote[] memory quotes =
|
|
212
|
+
Quote[] memory quotes = ICrossCollateralFee(_feeRecipient)
|
|
155
213
|
.quoteTransferRemoteTo(
|
|
156
214
|
_destination,
|
|
157
215
|
_recipient,
|
|
@@ -162,7 +220,7 @@ contract MultiCollateral is HypERC20Collateral, IMultiCollateralFee {
|
|
|
162
220
|
|
|
163
221
|
require(
|
|
164
222
|
quotes.length == 1 && quotes[0].token == token(),
|
|
165
|
-
"
|
|
223
|
+
"CCR: fee must match token"
|
|
166
224
|
);
|
|
167
225
|
feeAmount = quotes[0].amount;
|
|
168
226
|
}
|
|
@@ -197,11 +255,12 @@ contract MultiCollateral is HypERC20Collateral, IMultiCollateralFee {
|
|
|
197
255
|
// Same-domain transferRemoteTo calls handle() directly and does not dispatch
|
|
198
256
|
// through mailbox hooks, so do not charge hook fees in that path.
|
|
199
257
|
if (_feeHook != address(0) && _destination != localDomain) {
|
|
200
|
-
uint256 hookFee =
|
|
258
|
+
uint256 hookFee = _quoteGasPaymentTo(
|
|
201
259
|
_destination,
|
|
202
260
|
_recipient,
|
|
203
|
-
_amount,
|
|
204
|
-
_token
|
|
261
|
+
_outboundAmount(_amount),
|
|
262
|
+
_token,
|
|
263
|
+
_targetRouter
|
|
205
264
|
);
|
|
206
265
|
if (hookFee > 0) {
|
|
207
266
|
if (_token != address(this)) {
|
|
@@ -231,10 +290,10 @@ contract MultiCollateral is HypERC20Collateral, IMultiCollateralFee {
|
|
|
231
290
|
/**
|
|
232
291
|
* @notice Transfers tokens to the primary enrolled router for `_destination`.
|
|
233
292
|
* @dev Uses the enrolled primary remote router for `_destination` and routes through
|
|
234
|
-
* router-aware fee lookup (`
|
|
293
|
+
* router-aware fee lookup (`ICrossCollateralFee`) via `transferRemoteTo`.
|
|
235
294
|
* @dev This override is required because TokenRouter's `_feeRecipientAndAmount`
|
|
236
295
|
* is non-virtual and hardcodes `ITokenFee`. Delegating through
|
|
237
|
-
* `transferRemoteTo` keeps both transfer paths on `
|
|
296
|
+
* `transferRemoteTo` keeps both transfer paths on `ICrossCollateralFee`.
|
|
238
297
|
*/
|
|
239
298
|
function transferRemote(
|
|
240
299
|
uint32 _destination,
|
|
@@ -263,13 +322,11 @@ contract MultiCollateral is HypERC20Collateral, IMultiCollateralFee {
|
|
|
263
322
|
uint256 _amount,
|
|
264
323
|
bytes32 _targetRouter
|
|
265
324
|
) public payable returns (bytes32 messageId) {
|
|
266
|
-
|
|
267
|
-
_isRemoteRouter(_destination, _targetRouter) ||
|
|
268
|
-
_enrolledRouters[_destination].contains(_targetRouter),
|
|
269
|
-
"MC: unauthorized router"
|
|
270
|
-
);
|
|
325
|
+
_requireAuthorizedRouter(_destination, _targetRouter);
|
|
271
326
|
if (_destination == localDomain) {
|
|
272
|
-
|
|
327
|
+
// Local transfers call handle() directly without mailbox dispatch,
|
|
328
|
+
// so any msg.value would be stuck in this contract permanently.
|
|
329
|
+
require(msg.value == 0, "CCR: local transfer no msg.value");
|
|
273
330
|
}
|
|
274
331
|
|
|
275
332
|
(, uint256 remainingValue) = _calculateFeesAndChargeForRouter(
|
|
@@ -286,8 +343,8 @@ contract MultiCollateral is HypERC20Collateral, IMultiCollateralFee {
|
|
|
286
343
|
if (_destination == localDomain) {
|
|
287
344
|
// Same-domain: call target router's handle directly
|
|
288
345
|
address target = _targetRouter.bytes32ToAddress();
|
|
289
|
-
require(target.code.length > 0, "
|
|
290
|
-
|
|
346
|
+
require(target.code.length > 0, "CCR: target router not contract");
|
|
347
|
+
CrossCollateralRouter(target).handle(
|
|
291
348
|
localDomain,
|
|
292
349
|
TypeCasts.addressToBytes32(address(this)),
|
|
293
350
|
tokenMsg
|
|
@@ -310,24 +367,33 @@ contract MultiCollateral is HypERC20Collateral, IMultiCollateralFee {
|
|
|
310
367
|
// Differences: (1) router-aware fee lookup, (2) same-domain returns 0 gas
|
|
311
368
|
// since handle() is called directly without mailbox dispatch.
|
|
312
369
|
|
|
313
|
-
/// @inheritdoc
|
|
370
|
+
/// @inheritdoc ICrossCollateralFee
|
|
314
371
|
function quoteTransferRemoteTo(
|
|
315
372
|
uint32 _destination,
|
|
316
373
|
bytes32 _recipient,
|
|
317
374
|
uint256 _amount,
|
|
318
375
|
bytes32 _targetRouter
|
|
319
|
-
)
|
|
376
|
+
) public view override returns (Quote[] memory quotes) {
|
|
377
|
+
_requireAuthorizedRouter(_destination, _targetRouter);
|
|
378
|
+
if (_destination == localDomain) {
|
|
379
|
+
require(
|
|
380
|
+
_targetRouter.bytes32ToAddress().code.length > 0,
|
|
381
|
+
"CCR: target router not contract"
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
320
385
|
quotes = new Quote[](3);
|
|
321
386
|
|
|
322
387
|
// Same-domain: handle() called directly, no interchain gas
|
|
323
388
|
uint256 gasQuote = 0;
|
|
324
389
|
address _feeToken = feeToken();
|
|
325
390
|
if (_destination != localDomain) {
|
|
326
|
-
gasQuote =
|
|
391
|
+
gasQuote = _quoteGasPaymentTo(
|
|
327
392
|
_destination,
|
|
328
393
|
_recipient,
|
|
329
394
|
_outboundAmount(_amount),
|
|
330
|
-
_feeToken
|
|
395
|
+
_feeToken,
|
|
396
|
+
_targetRouter
|
|
331
397
|
);
|
|
332
398
|
}
|
|
333
399
|
quotes[0] = Quote({token: _feeToken, amount: gasQuote});
|
|
@@ -346,4 +412,23 @@ contract MultiCollateral is HypERC20Collateral, IMultiCollateralFee {
|
|
|
346
412
|
amount: _externalFeeAmount(_destination, _recipient, _amount)
|
|
347
413
|
});
|
|
348
414
|
}
|
|
415
|
+
|
|
416
|
+
/// @dev Target-router-aware gas quote helper. Avoids Router._mustHaveRemoteRouter().
|
|
417
|
+
/// Caller must validate `_targetRouter` is authorized for `_destination`.
|
|
418
|
+
function _quoteGasPaymentTo(
|
|
419
|
+
uint32 _destination,
|
|
420
|
+
bytes32 _recipient,
|
|
421
|
+
uint256 _amount,
|
|
422
|
+
address _feeToken,
|
|
423
|
+
bytes32 _targetRouter
|
|
424
|
+
) internal view returns (uint256) {
|
|
425
|
+
return
|
|
426
|
+
mailbox.quoteDispatch(
|
|
427
|
+
_destination,
|
|
428
|
+
_targetRouter,
|
|
429
|
+
TokenMessage.format(_recipient, _amount),
|
|
430
|
+
_generateHookMetadata(_destination, _feeToken),
|
|
431
|
+
IPostDispatchHook(address(hook))
|
|
432
|
+
);
|
|
433
|
+
}
|
|
349
434
|
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
2
2
|
pragma solidity >=0.8.0;
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {ICrossCollateralFee} from "./interfaces/ICrossCollateralFee.sol";
|
|
5
5
|
import {ITokenFee, Quote} from "@hyperlane-xyz/core/interfaces/ITokenBridge.sol";
|
|
6
6
|
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
7
7
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
8
8
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* @title
|
|
11
|
+
* @title CrossCollateralRoutingFee
|
|
12
12
|
* @notice Routes fee lookups by destination + target router. Delegates to
|
|
13
13
|
* existing ITokenFee (3-param) fee contracts (LinearFee, ProgressiveFee, etc.).
|
|
14
|
+
* @dev This contract assumes standard ERC20 behavior for fee balances and
|
|
15
|
+
* transfers. Rebasing, fee-on-transfer, and ERC777 tokens are not supported
|
|
16
|
+
* for deterministic fee accounting.
|
|
14
17
|
*/
|
|
15
|
-
contract
|
|
18
|
+
contract CrossCollateralRoutingFee is ICrossCollateralFee, ITokenFee, Ownable {
|
|
16
19
|
using SafeERC20 for IERC20;
|
|
17
20
|
|
|
18
21
|
/// @notice Sentinel key for destination-level default fee contracts.
|
|
@@ -34,7 +37,7 @@ contract MultiCollateralRoutingFee is IMultiCollateralFee, ITokenFee, Ownable {
|
|
|
34
37
|
_transferOwnership(_owner);
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
function
|
|
40
|
+
function setCrossCollateralRouterFeeContracts(
|
|
38
41
|
uint32[] calldata destinations,
|
|
39
42
|
bytes32[] calldata targetRouters,
|
|
40
43
|
address[] calldata _feeContracts
|
|
@@ -42,7 +45,7 @@ contract MultiCollateralRoutingFee is IMultiCollateralFee, ITokenFee, Ownable {
|
|
|
42
45
|
require(
|
|
43
46
|
destinations.length == targetRouters.length &&
|
|
44
47
|
destinations.length == _feeContracts.length,
|
|
45
|
-
"
|
|
48
|
+
"CCRF: length mismatch"
|
|
46
49
|
);
|
|
47
50
|
|
|
48
51
|
for (uint256 i = 0; i < destinations.length; i++) {
|
|
@@ -79,7 +82,7 @@ contract MultiCollateralRoutingFee is IMultiCollateralFee, ITokenFee, Ownable {
|
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
/**
|
|
82
|
-
* @inheritdoc
|
|
85
|
+
* @inheritdoc ICrossCollateralFee
|
|
83
86
|
* @dev Routes: specific router → destination default (DEFAULT_ROUTER).
|
|
84
87
|
*/
|
|
85
88
|
function quoteTransferRemoteTo(
|