@layerzerolabs/oft-upgradeable-evm-contracts 4.0.3 → 4.0.4

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.
@@ -9,7 +9,7 @@ import { OFTCoreExtendedRBACUpgradeable } from "./OFTCoreExtendedRBACUpgradeable
9
9
  /**
10
10
  * @title OFTBurnMintExtendedRBACUpgradeable
11
11
  * @author LayerZero Labs (@TRileySchwarz, tinom.eth)
12
- * @custom:version 1.1.0
12
+ * @custom:version 1.2.0
13
13
  * @notice Upgradeable OFT burn-mint adapter with toggleable pause, fee, and rate limit functionality.
14
14
  * Supports dynamic mint/burn function selectors and configurable approval requirements.
15
15
  * @dev Roles are handled through `AccessControl2StepUpgradeable`.
@@ -8,7 +8,7 @@ import { OFTBurnMintExtendedRBACUpgradeable } from "./OFTBurnMintExtendedRBACUpg
8
8
  /**
9
9
  * @title OFTBurnSelfMintExtendedRBACUpgradeable
10
10
  * @author LayerZero Labs (@TRileySchwarz, tinom.eth)
11
- * @custom:version 1.1.0
11
+ * @custom:version 1.2.0
12
12
  * @notice Upgradeable OFT burn-mint adapter with toggleable pause, fee, and rate limit functionality.
13
13
  * Supports dynamic mint/burn function selectors, assuming that the burn operation happens in 2 steps:
14
14
  * - First, the OFT transfers the tokens to itself.
@@ -25,7 +25,7 @@ import { OFTCoreRBACUpgradeable } from "./../oft/OFTCoreRBACUpgradeable.sol";
25
25
  /**
26
26
  * @title OFTCoreExtendedRBACUpgradeable
27
27
  * @author LayerZero Labs (@TRileySchwarz, tinom.eth)
28
- * @custom:version 1.1.0
28
+ * @custom:version 1.2.0
29
29
  * @notice Abstract upgradeable contract that implements the core functionality of an extended OFT token.
30
30
  * @dev Includes `CreditRedirect`, `Fee`, `RateLimiter`, and `Pause` extensions with RBAC access control.
31
31
  * @dev Roles are handled through `AccessControl2StepUpgradeable`.
@@ -9,7 +9,7 @@ import { OFTCoreExtendedRBACUpgradeable } from "./OFTCoreExtendedRBACUpgradeable
9
9
  /**
10
10
  * @title OFTLockUnlockExtendedRBACUpgradeable
11
11
  * @author LayerZero Labs (@TRileySchwarz, tinom.eth)
12
- * @custom:version 1.1.0
12
+ * @custom:version 1.2.0
13
13
  * @notice Upgradeable OFT lock-unlock adapter with toggleable pause, fee, and rate limit functionality.
14
14
  * @dev Roles are handled through `AccessControl2StepUpgradeable`.
15
15
  */
@@ -15,7 +15,7 @@ import { OFTCoreExtendedRBACUpgradeable } from "./OFTCoreExtendedRBACUpgradeable
15
15
  /**
16
16
  * @title OFTNativeExtendedRBACUpgradeable
17
17
  * @author LayerZero Labs (@TRileySchwarz, tinom.eth)
18
- * @custom:version 1.1.0
18
+ * @custom:version 1.2.0
19
19
  * @notice Upgradeable OFT native adapter with toggleable pause, fee, and rate limit functionality.
20
20
  * @dev Roles are handled through `AccessControl2StepUpgradeable`.
21
21
  */
@@ -8,7 +8,7 @@ import { OFTBurnMintExtendedRBACUpgradeable } from "./../OFTBurnMintExtendedRBAC
8
8
  /**
9
9
  * @title OFTBurnMintExtendedRBACAltUpgradeable
10
10
  * @author LayerZero Labs (@TRileySchwarz, tinom.eth)
11
- * @custom:version 1.1.0
11
+ * @custom:version 1.2.0
12
12
  * @notice `OFTBurnMintExtendedRBACUpgradeable` variant that pays native fees using an ERC20 token instead of
13
13
  * `msg.value`.
14
14
  * @dev For chains where gas/native fees are paid via an ERC20 token (e.g., some L2s using `EndpointV2Alt`).
@@ -8,7 +8,7 @@ import { OFTBurnSelfMintExtendedRBACUpgradeable } from "./../OFTBurnSelfMintExte
8
8
  /**
9
9
  * @title OFTBurnSelfMintExtendedRBACAltUpgradeable
10
10
  * @author LayerZero Labs (@TRileySchwarz, tinom.eth)
11
- * @custom:version 1.1.0
11
+ * @custom:version 1.2.0
12
12
  * @notice `OFTBurnSelfMintExtendedRBACUpgradeable` variant that pays native fees using an ERC20 token instead of
13
13
  * `msg.value`.
14
14
  * @dev For chains where gas/native fees are paid via an ERC20 token (e.g., some L2s using `EndpointV2Alt`).
@@ -8,7 +8,7 @@ import { OFTLockUnlockExtendedRBACUpgradeable } from "./../OFTLockUnlockExtended
8
8
  /**
9
9
  * @title OFTLockUnlockExtendedRBACAltUpgradeable
10
10
  * @author LayerZero Labs (@TRileySchwarz, tinom.eth)
11
- * @custom:version 1.1.0
11
+ * @custom:version 1.2.0
12
12
  * @notice `OFTLockUnlockExtendedRBACUpgradeable` variant that pays native fees using an ERC20 token instead of
13
13
  * `msg.value`.
14
14
  * @dev For chains where gas/native fees are paid via an ERC20 token (e.g., some L2s using `EndpointV2Alt`).
package/package.json CHANGED
@@ -1,24 +1,24 @@
1
1
  {
2
2
  "name": "@layerzerolabs/oft-upgradeable-evm-contracts",
3
- "version": "4.0.3",
3
+ "version": "4.0.4",
4
4
  "private": false,
5
5
  "description": "LayerZero Labs reference EVM upgradeable OFT implementation",
6
6
  "license": "MIT",
7
7
  "devDependencies": {
8
- "@layerzerolabs/lz-evm-messagelib-v2": "npm:@layerzerolabs/protocol-v2-messagelib-evm-contracts@4.0.3",
9
- "@layerzerolabs/lz-evm-protocol-v2": "npm:@layerzerolabs/protocol-v2-evm-contracts@4.0.3",
10
- "@layerzerolabs/lz-evm-v1-0.7": "npm:@layerzerolabs/protocol-v1-0.7-evm-contracts@4.0.3",
11
- "@layerzerolabs/oapp-evm-contracts": "4.0.3",
12
- "@layerzerolabs/oapp-upgradeable-evm-contracts": "4.0.3",
13
- "@layerzerolabs/oft-evm-contracts": "4.0.3",
14
- "@layerzerolabs/protocol-v2-messagelib-evm-contracts": "4.0.3",
15
- "@layerzerolabs/solhint-configuration": "4.0.3",
8
+ "@layerzerolabs/lz-evm-messagelib-v2": "npm:@layerzerolabs/protocol-v2-messagelib-evm-contracts@4.0.4",
9
+ "@layerzerolabs/lz-evm-protocol-v2": "npm:@layerzerolabs/protocol-v2-evm-contracts@4.0.4",
10
+ "@layerzerolabs/lz-evm-v1-0.7": "npm:@layerzerolabs/protocol-v1-0.7-evm-contracts@4.0.4",
11
+ "@layerzerolabs/oapp-evm-contracts": "4.0.4",
12
+ "@layerzerolabs/oapp-upgradeable-evm-contracts": "4.0.4",
13
+ "@layerzerolabs/oft-evm-contracts": "4.0.4",
14
+ "@layerzerolabs/protocol-v2-messagelib-evm-contracts": "4.0.4",
15
+ "@layerzerolabs/solhint-configuration": "4.0.4",
16
16
  "@layerzerolabs/test-devtools-evm-foundry": "8.0.1",
17
- "@layerzerolabs/test-utils-evm-contracts": "4.0.3",
17
+ "@layerzerolabs/test-utils-evm-contracts": "4.0.4",
18
18
  "@layerzerolabs/toolbox-foundry": "^0.1.13",
19
- "@layerzerolabs/utils-evm-contracts": "4.0.3",
20
- "@layerzerolabs/utils-upgradeable-evm-contracts": "4.0.3",
21
- "@layerzerolabs/vm-tooling-evm": "4.0.3",
19
+ "@layerzerolabs/utils-evm-contracts": "4.0.4",
20
+ "@layerzerolabs/utils-upgradeable-evm-contracts": "4.0.4",
21
+ "@layerzerolabs/vm-tooling-evm": "4.0.4",
22
22
  "@matterlabs/hardhat-zksync-solc": "^1.4.0",
23
23
  "@openzeppelin/contracts": "^5.0.2",
24
24
  "@openzeppelin/contracts-upgradeable": "^5.0.2",
@@ -0,0 +1,340 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { OptionsBuilder } from "@layerzerolabs/oapp-evm-contracts/contracts/oapp/libs/OptionsBuilder.sol";
5
+ import {
6
+ IOFT,
7
+ SendParam,
8
+ OFTReceipt,
9
+ MessagingFee
10
+ } from "@layerzerolabs/oft-evm-contracts/contracts/interfaces/IOFT.sol";
11
+ import { TestHelperOz5 } from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol";
12
+ import { MockBurnerMinter } from "@layerzerolabs/test-utils-evm-contracts/contracts/mocks/MockBurnerMinter.sol";
13
+ import { MockERC20 } from "@layerzerolabs/test-utils-evm-contracts/contracts/mocks/MockERC20.sol";
14
+ import { IRateLimiter } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/IRateLimiter.sol";
15
+ import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
16
+ import { DoubleEndedQueue } from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol";
17
+ import { OFTBurnMintExtendedRBACUpgradeable } from "./../contracts/extended/OFTBurnMintExtendedRBACUpgradeable.sol";
18
+ import { OFTCoreExtendedRBACUpgradeable } from "./../contracts/extended/OFTCoreExtendedRBACUpgradeable.sol";
19
+ import { OFTLockUnlockExtendedRBACUpgradeable } from "./../contracts/extended/OFTLockUnlockExtendedRBACUpgradeable.sol";
20
+
21
+ /**
22
+ * @notice End-to-end tests for a lock/unlock hub that caps the inflows from each burn/mint spoke at its net outflow.
23
+ * @dev Each spoke rate limit ID on the hub has an inbound net limit `LEDGER_LIMIT` with a no-decay window, and starts
24
+ * with `inboundUsage` equal to `LEDGER_LIMIT`, so the inbound headroom is the net outflow to the spoke.
25
+ */
26
+ contract OFTExtendedRBACHubLedgerTest is TestHelperOz5 {
27
+ using OptionsBuilder for bytes;
28
+
29
+ uint32 constant HUB_EID = 1;
30
+ uint32 constant SPOKE_A_EID = 2;
31
+ uint32 constant SPOKE_B_EID = 3;
32
+ uint32 constant NO_DECAY_WINDOW = type(uint32).max;
33
+ uint96 constant LEDGER_LIMIT = type(uint96).max;
34
+ uint16 constant FEE_BPS = 1_000; // 10%
35
+
36
+ address admin = makeAddr("admin");
37
+ address alice = makeAddr("alice");
38
+ address bob = makeAddr("bob");
39
+
40
+ MockERC20 hubToken;
41
+ OFTLockUnlockExtendedRBACUpgradeable hub;
42
+ mapping(uint32 eid => MockERC20 token) spokeTokens;
43
+ mapping(uint32 eid => OFTBurnMintExtendedRBACUpgradeable oft) spokes;
44
+
45
+ function setUp() public virtual override {
46
+ super.setUp();
47
+ setUpEndpoints(3, LibraryType.SimpleMessageLib);
48
+
49
+ hubToken = new MockERC20(18);
50
+ hub = OFTLockUnlockExtendedRBACUpgradeable(
51
+ _deployProxy(address(new OFTLockUnlockExtendedRBACUpgradeable(address(hubToken), endpoints[HUB_EID], 0)))
52
+ );
53
+ _grantRoles(hub);
54
+
55
+ _deploySpoke(SPOKE_A_EID);
56
+ _deploySpoke(SPOKE_B_EID);
57
+
58
+ vm.startPrank(admin);
59
+ _setUpHubLedger(SPOKE_A_EID, false, 0, 0);
60
+ _setUpHubLedger(SPOKE_B_EID, false, 0, 0);
61
+ vm.stopPrank();
62
+ }
63
+
64
+ // ============ Helper Functions ============
65
+
66
+ function _deployProxy(address _impl) internal returns (address) {
67
+ return
68
+ address(
69
+ new TransparentUpgradeableProxy(
70
+ _impl,
71
+ address(this),
72
+ abi.encodeWithSelector(OFTLockUnlockExtendedRBACUpgradeable.initialize.selector, admin, admin)
73
+ )
74
+ );
75
+ }
76
+
77
+ function _grantRoles(OFTCoreExtendedRBACUpgradeable _oft) internal {
78
+ vm.startPrank(admin);
79
+ _oft.grantRole(_oft.RATE_LIMITER_MANAGER_ROLE(), admin);
80
+ _oft.grantRole(_oft.FEE_CONFIG_MANAGER_ROLE(), admin);
81
+ vm.stopPrank();
82
+ }
83
+
84
+ function _deploySpoke(uint32 _eid) internal {
85
+ MockERC20 token = new MockERC20(18);
86
+ OFTBurnMintExtendedRBACUpgradeable spoke = OFTBurnMintExtendedRBACUpgradeable(
87
+ _deployProxy(
88
+ address(
89
+ new OFTBurnMintExtendedRBACUpgradeable(
90
+ address(token),
91
+ address(new MockBurnerMinter(address(token))),
92
+ endpoints[_eid],
93
+ false,
94
+ bytes4(keccak256("burn(address,uint256)")),
95
+ bytes4(keccak256("mint(address,uint256)")),
96
+ 0
97
+ )
98
+ )
99
+ )
100
+ );
101
+ _grantRoles(spoke);
102
+ spokeTokens[_eid] = token;
103
+ spokes[_eid] = spoke;
104
+
105
+ vm.startPrank(admin);
106
+ spoke.setPeer(HUB_EID, addressToBytes32(address(hub)));
107
+ spoke.setRateLimitGlobalConfig(
108
+ IRateLimiter.RateLimitGlobalConfig({ useGlobalState: false, isGloballyDisabled: true })
109
+ );
110
+ hub.setPeer(_eid, addressToBytes32(address(spoke)));
111
+ vm.stopPrank();
112
+ }
113
+
114
+ function _setUpHubLedger(
115
+ uint32 _spokeEid,
116
+ bool _outboundEnabled,
117
+ uint96 _outboundLimit,
118
+ uint32 _outboundWindow
119
+ ) internal {
120
+ IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](1);
121
+ configs[0] = IRateLimiter.SetRateLimitConfigParam({
122
+ id: _spokeEid,
123
+ config: IRateLimiter.RateLimitConfig({
124
+ active: true,
125
+ outboundEnabled: _outboundEnabled,
126
+ inboundEnabled: true,
127
+ netAccountingEnabled: true,
128
+ addressExemptionEnabled: false,
129
+ outboundLimit: _outboundLimit,
130
+ inboundLimit: LEDGER_LIMIT,
131
+ outboundWindow: _outboundWindow,
132
+ inboundWindow: NO_DECAY_WINDOW
133
+ })
134
+ });
135
+ hub.setRateLimitConfigs(configs);
136
+
137
+ IRateLimiter.SetRateLimitStateParam[] memory states = new IRateLimiter.SetRateLimitStateParam[](1);
138
+ states[0] = IRateLimiter.SetRateLimitStateParam({
139
+ id: _spokeEid,
140
+ state: IRateLimiter.RateLimitState({
141
+ outboundUsage: 0,
142
+ inboundUsage: LEDGER_LIMIT,
143
+ lastUpdated: uint40(block.timestamp)
144
+ })
145
+ });
146
+ hub.setRateLimitStates(states);
147
+ }
148
+
149
+ function _send(
150
+ OFTCoreExtendedRBACUpgradeable _oft,
151
+ uint32 _dstEid,
152
+ address _from,
153
+ address _to,
154
+ uint256 _amountLD
155
+ ) internal returns (OFTReceipt memory receipt) {
156
+ SendParam memory sendParam = SendParam({
157
+ dstEid: _dstEid,
158
+ to: addressToBytes32(_to),
159
+ amountLD: _amountLD,
160
+ minAmountLD: 0,
161
+ extraOptions: OptionsBuilder.newOptions().addExecutorLzReceiveOption(200_000, 0),
162
+ composeMsg: bytes(""),
163
+ oftCmd: bytes("")
164
+ });
165
+ MessagingFee memory fee = _oft.quoteSend(sendParam, false);
166
+ vm.deal(_from, fee.nativeFee);
167
+
168
+ vm.startPrank(_from);
169
+ MockERC20(_oft.token()).approve(address(_oft), _amountLD);
170
+ (, receipt) = _oft.send{ value: fee.nativeFee }(sendParam, fee, _from);
171
+ vm.stopPrank();
172
+ }
173
+
174
+ function _sendToSpoke(uint32 _spokeEid, uint256 _amountLD) internal returns (OFTReceipt memory receipt) {
175
+ hubToken.mint(alice, _amountLD);
176
+ receipt = _send(hub, _spokeEid, alice, bob, _amountLD);
177
+ verifyPackets(_spokeEid, address(spokes[_spokeEid]));
178
+ }
179
+
180
+ function _sendFromSpoke(uint32 _spokeEid, uint256 _amountLD) internal {
181
+ _send(spokes[_spokeEid], HUB_EID, bob, alice, _amountLD);
182
+ }
183
+
184
+ function _hubHeadroom(uint32 _spokeEid) internal view returns (uint256 inAvail) {
185
+ (, , , inAvail) = hub.getRateLimitUsages(_spokeEid);
186
+ }
187
+
188
+ // ============ send Tests ============
189
+
190
+ function test_send_HubLedger_DecayingOutboundLimit() public {
191
+ vm.startPrank(admin);
192
+ _setUpHubLedger(SPOKE_A_EID, true, 100 ether, 1 days);
193
+ vm.stopPrank();
194
+
195
+ _sendToSpoke(SPOKE_A_EID, 100 ether);
196
+ assertEq(_hubHeadroom(SPOKE_A_EID), 100 ether);
197
+
198
+ hubToken.mint(alice, 1 ether);
199
+ vm.expectRevert(abi.encodeWithSelector(IRateLimiter.RateLimitExceeded.selector, 0, 1 ether));
200
+ this.sendExternal(hub, SPOKE_A_EID, alice, bob, 1 ether);
201
+
202
+ vm.warp(block.timestamp + 1 days);
203
+ _sendToSpoke(SPOKE_A_EID, 100 ether);
204
+ assertEq(_hubHeadroom(SPOKE_A_EID), 200 ether);
205
+
206
+ _sendFromSpoke(SPOKE_A_EID, 200 ether);
207
+ verifyPackets(HUB_EID, address(hub));
208
+ assertEq(_hubHeadroom(SPOKE_A_EID), 0);
209
+ }
210
+
211
+ // ============ lzReceive Tests ============
212
+
213
+ function test_lzReceive_HubLedger() public {
214
+ _sendToSpoke(SPOKE_A_EID, 300 ether);
215
+ assertEq(_hubHeadroom(SPOKE_A_EID), 300 ether);
216
+
217
+ _sendFromSpoke(SPOKE_A_EID, 100 ether);
218
+ verifyPackets(HUB_EID, address(hub));
219
+ assertEq(_hubHeadroom(SPOKE_A_EID), 200 ether);
220
+
221
+ _sendFromSpoke(SPOKE_A_EID, 200 ether);
222
+ verifyPackets(HUB_EID, address(hub));
223
+ assertEq(_hubHeadroom(SPOKE_A_EID), 0);
224
+ assertEq(hubToken.balanceOf(alice), 300 ether);
225
+ }
226
+
227
+ function test_lzReceive_HubLedger_LongWarp() public {
228
+ _sendToSpoke(SPOKE_A_EID, 300 ether);
229
+
230
+ vm.warp(block.timestamp + 100 * 365 days);
231
+ assertEq(_hubHeadroom(SPOKE_A_EID), 300 ether);
232
+
233
+ _sendFromSpoke(SPOKE_A_EID, 300 ether);
234
+ verifyPackets(HUB_EID, address(hub));
235
+ assertEq(_hubHeadroom(SPOKE_A_EID), 0);
236
+
237
+ vm.warp(block.timestamp + 100 * 365 days);
238
+ spokeTokens[SPOKE_A_EID].mint(bob, 1e12);
239
+ _sendFromSpoke(SPOKE_A_EID, 1e12);
240
+
241
+ vm.expectRevert(abi.encodeWithSelector(IRateLimiter.RateLimitExceeded.selector, 0, 1e12));
242
+ this.verifyPackets(HUB_EID, address(hub));
243
+ }
244
+
245
+ function test_lzReceive_HubLedger_OutOfOrderReturns() public {
246
+ _sendToSpoke(SPOKE_A_EID, 300 ether);
247
+
248
+ _sendFromSpoke(SPOKE_A_EID, 50 ether);
249
+ _sendFromSpoke(SPOKE_A_EID, 100 ether);
250
+ _sendFromSpoke(SPOKE_A_EID, 150 ether);
251
+ assertEq(spokeTokens[SPOKE_A_EID].totalSupply(), 0);
252
+ assertEq(_hubHeadroom(SPOKE_A_EID), 300 ether);
253
+
254
+ vm.expectEmit(false, true, true, true, address(hub));
255
+ emit IOFT.OFTReceived(bytes32(0), SPOKE_A_EID, alice, 150 ether);
256
+ vm.expectEmit(false, true, true, true, address(hub));
257
+ emit IOFT.OFTReceived(bytes32(0), SPOKE_A_EID, alice, 100 ether);
258
+ vm.expectEmit(false, true, true, true, address(hub));
259
+ emit IOFT.OFTReceived(bytes32(0), SPOKE_A_EID, alice, 50 ether);
260
+ this.deliverToHubInReverse();
261
+ assertEq(_hubHeadroom(SPOKE_A_EID), 0);
262
+ assertEq(hubToken.balanceOf(alice), 300 ether);
263
+ }
264
+
265
+ function test_lzReceive_HubLedger_OutOfOrderReturnsWithHubSendInFlight() public {
266
+ _sendToSpoke(SPOKE_A_EID, 300 ether);
267
+
268
+ _sendFromSpoke(SPOKE_A_EID, 100 ether);
269
+ _sendFromSpoke(SPOKE_A_EID, 200 ether);
270
+
271
+ hubToken.mint(alice, 200 ether);
272
+ _send(hub, SPOKE_A_EID, alice, bob, 200 ether);
273
+ assertEq(_hubHeadroom(SPOKE_A_EID), 500 ether);
274
+
275
+ this.deliverToHubInReverse();
276
+ assertEq(_hubHeadroom(SPOKE_A_EID), 200 ether);
277
+
278
+ verifyPackets(SPOKE_A_EID, address(spokes[SPOKE_A_EID]));
279
+ assertEq(spokeTokens[SPOKE_A_EID].totalSupply(), _hubHeadroom(SPOKE_A_EID));
280
+ }
281
+
282
+ function test_lzReceive_HubLedger_WithFee() public {
283
+ vm.prank(admin);
284
+ hub.setDefaultFeeBps(FEE_BPS);
285
+
286
+ OFTReceipt memory receipt = _sendToSpoke(SPOKE_A_EID, 100 ether);
287
+ assertEq(receipt.amountReceivedLD, 90 ether);
288
+ assertEq(_hubHeadroom(SPOKE_A_EID), receipt.amountReceivedLD);
289
+
290
+ _sendFromSpoke(SPOKE_A_EID, 90 ether);
291
+ verifyPackets(HUB_EID, address(hub));
292
+ assertEq(_hubHeadroom(SPOKE_A_EID), 0);
293
+ }
294
+
295
+ function test_lzReceive_HubLedger_Revert_ExceedsNetOutflow() public {
296
+ _sendToSpoke(SPOKE_A_EID, 300 ether);
297
+
298
+ spokeTokens[SPOKE_A_EID].mint(bob, 1e12);
299
+ _sendFromSpoke(SPOKE_A_EID, 300 ether + 1e12);
300
+
301
+ vm.expectRevert(abi.encodeWithSelector(IRateLimiter.RateLimitExceeded.selector, 300 ether, 300 ether + 1e12));
302
+ this.verifyPackets(HUB_EID, address(hub));
303
+ }
304
+
305
+ function test_lzReceive_HubLedger_Revert_OtherSpoke() public {
306
+ _sendToSpoke(SPOKE_A_EID, 300 ether);
307
+ assertEq(_hubHeadroom(SPOKE_B_EID), 0);
308
+
309
+ spokeTokens[SPOKE_B_EID].mint(bob, 1 ether);
310
+ _sendFromSpoke(SPOKE_B_EID, 1 ether);
311
+
312
+ vm.expectRevert(abi.encodeWithSelector(IRateLimiter.RateLimitExceeded.selector, 0, 1 ether));
313
+ this.verifyPackets(HUB_EID, address(hub));
314
+ assertEq(_hubHeadroom(SPOKE_A_EID), 300 ether);
315
+ }
316
+
317
+ // ============ External Wrappers ============
318
+
319
+ function sendExternal(
320
+ OFTCoreExtendedRBACUpgradeable _oft,
321
+ uint32 _dstEid,
322
+ address _from,
323
+ address _to,
324
+ uint256 _amountLD
325
+ ) external {
326
+ _send(_oft, _dstEid, _from, _to, _amountLD);
327
+ }
328
+
329
+ function deliverToHubInReverse() external {
330
+ DoubleEndedQueue.Bytes32Deque storage queue = packetsQueue[HUB_EID][addressToBytes32(address(hub))];
331
+ bytes32[] memory guids = new bytes32[](DoubleEndedQueue.length(queue));
332
+ for (uint256 i = 0; i < guids.length; i++) {
333
+ guids[i] = DoubleEndedQueue.popBack(queue);
334
+ this.validatePacket(packets[guids[i]], bytes(""));
335
+ }
336
+ for (uint256 i = guids.length; i > 0; i--) {
337
+ this.lzReceive(packets[guids[i - 1]], optionsLookup[guids[i - 1]]);
338
+ }
339
+ }
340
+ }
@@ -142,7 +142,7 @@ abstract contract OFTExtendedRBACTestBase is TestHelperOz5 {
142
142
  configs[0] = IRateLimiter.SetRateLimitConfigParam({
143
143
  id: _id,
144
144
  config: IRateLimiter.RateLimitConfig({
145
- overrideDefaultConfig: true,
145
+ active: true,
146
146
  outboundEnabled: true,
147
147
  inboundEnabled: true,
148
148
  netAccountingEnabled: true,
@@ -166,7 +166,7 @@ abstract contract OFTExtendedRBACTestBase is TestHelperOz5 {
166
166
  configs[0] = IRateLimiter.SetRateLimitConfigParam({
167
167
  id: _id,
168
168
  config: IRateLimiter.RateLimitConfig({
169
- overrideDefaultConfig: true,
169
+ active: true,
170
170
  outboundEnabled: true,
171
171
  inboundEnabled: true,
172
172
  netAccountingEnabled: true,
@@ -254,7 +254,7 @@ abstract contract OFTExtendedRBACTestBase is TestHelperOz5 {
254
254
  configs[0] = IRateLimiter.SetRateLimitConfigParam({
255
255
  id: DST_EID,
256
256
  config: IRateLimiter.RateLimitConfig({
257
- overrideDefaultConfig: true,
257
+ active: true,
258
258
  outboundEnabled: false,
259
259
  inboundEnabled: true,
260
260
  netAccountingEnabled: false,
@@ -619,7 +619,7 @@ abstract contract OFTExtendedRBACTestBase is TestHelperOz5 {
619
619
  configs[0] = IRateLimiter.SetRateLimitConfigParam({
620
620
  id: DST_EID,
621
621
  config: IRateLimiter.RateLimitConfig({
622
- overrideDefaultConfig: true,
622
+ active: true,
623
623
  outboundEnabled: false,
624
624
  inboundEnabled: true,
625
625
  netAccountingEnabled: false,