@layerzerolabs/oft-upgradeable-evm-contracts 4.0.2 → 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.
- package/contracts/extended/OFTBurnMintExtendedRBACUpgradeable.sol +1 -1
- package/contracts/extended/OFTBurnSelfMintExtendedRBACUpgradeable.sol +1 -1
- package/contracts/extended/OFTCoreExtendedRBACUpgradeable.sol +1 -1
- package/contracts/extended/OFTLockUnlockExtendedRBACUpgradeable.sol +1 -1
- package/contracts/extended/OFTNativeExtendedRBACUpgradeable.sol +1 -1
- package/contracts/extended/alt/OFTBurnMintExtendedRBACAltUpgradeable.sol +1 -1
- package/contracts/extended/alt/OFTBurnSelfMintExtendedRBACAltUpgradeable.sol +1 -1
- package/contracts/extended/alt/OFTLockUnlockExtendedRBACAltUpgradeable.sol +1 -1
- package/package.json +13 -13
- package/test/OFTExtendedRBACHubLedger.t.sol +340 -0
- package/test/shared/OFTExtendedRBACTestBase.sol +4 -4
- package/turbo-snapshot.lock.json +330 -330
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
+
"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.
|
|
9
|
-
"@layerzerolabs/lz-evm-protocol-v2": "npm:@layerzerolabs/protocol-v2-evm-contracts@4.0.
|
|
10
|
-
"@layerzerolabs/lz-evm-v1-0.7": "npm:@layerzerolabs/protocol-v1-0.7-evm-contracts@4.0.
|
|
11
|
-
"@layerzerolabs/oapp-evm-contracts": "4.0.
|
|
12
|
-
"@layerzerolabs/oapp-upgradeable-evm-contracts": "4.0.
|
|
13
|
-
"@layerzerolabs/oft-evm-contracts": "4.0.
|
|
14
|
-
"@layerzerolabs/protocol-v2-messagelib-evm-contracts": "4.0.
|
|
15
|
-
"@layerzerolabs/solhint-configuration": "4.0.
|
|
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.
|
|
17
|
+
"@layerzerolabs/test-utils-evm-contracts": "4.0.4",
|
|
18
18
|
"@layerzerolabs/toolbox-foundry": "^0.1.13",
|
|
19
|
-
"@layerzerolabs/utils-evm-contracts": "4.0.
|
|
20
|
-
"@layerzerolabs/utils-upgradeable-evm-contracts": "4.0.
|
|
21
|
-
"@layerzerolabs/vm-tooling-evm": "4.0.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
622
|
+
active: true,
|
|
623
623
|
outboundEnabled: false,
|
|
624
624
|
inboundEnabled: true,
|
|
625
625
|
netAccountingEnabled: false,
|