@layerzerolabs/utils-upgradeable-evm-contracts 0.2.74

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.
Files changed (37) hide show
  1. package/.turbo/turbo-lint.log +491 -0
  2. package/.turbo/turbo-test.log +1356 -0
  3. package/LICENSE +23 -0
  4. package/contracts/access/AccessControl2StepUpgradeable.sol +144 -0
  5. package/contracts/allowlist/AllowlistBaseUpgradeable.sol +184 -0
  6. package/contracts/allowlist/AllowlistRBACUpgradeable.sol +55 -0
  7. package/contracts/fee-accounting/FeeHandlerBaseUpgradeable.sol +69 -0
  8. package/contracts/fee-accounting/FeeHandlerRBACUpgradeable.sol +34 -0
  9. package/contracts/fee-config/FeeConfigBaseUpgradeable.sol +128 -0
  10. package/contracts/fee-config/FeeConfigRBACUpgradeable.sol +44 -0
  11. package/contracts/libs/EnumerableSetPagination.sol +77 -0
  12. package/contracts/pause/PauseBaseUpgradeable.sol +107 -0
  13. package/contracts/pause/PauseRBACUpgradeable.sol +47 -0
  14. package/contracts/pause-by-id/PauseByIDBaseUpgradeable.sol +127 -0
  15. package/contracts/pause-by-id/PauseByIDRBACUpgradeable.sol +74 -0
  16. package/contracts/rate-limiter/RateLimiterBaseUpgradeable.sol +636 -0
  17. package/contracts/rate-limiter/RateLimiterRBACUpgradeable.sol +89 -0
  18. package/contracts/rate-limiter/libs/RateLimiterUtils.sol +82 -0
  19. package/foundry.toml +27 -0
  20. package/package.json +34 -0
  21. package/solhint.config.js +3 -0
  22. package/test/AccessControl2StepUpgradeable.t.sol +374 -0
  23. package/test/AllowlistBaseUpgradeable.t.sol +380 -0
  24. package/test/AllowlistRBACUpgradeable.t.sol +84 -0
  25. package/test/EnumerableSetPagination.t.sol +445 -0
  26. package/test/FeeConfigBaseUpgradeable.t.sol +232 -0
  27. package/test/FeeConfigRBACUpgradeable.t.sol +59 -0
  28. package/test/FeeHandlerBaseUpgradeable.t.sol +78 -0
  29. package/test/FeeHandlerRBACUpgradeable.t.sol +45 -0
  30. package/test/PauseBaseUpgradeable.t.sol +244 -0
  31. package/test/PauseByIDBaseUpgradeable.t.sol +337 -0
  32. package/test/PauseByIDRBACUpgradeable.t.sol +414 -0
  33. package/test/PauseRBACUpgradeable.t.sol +258 -0
  34. package/test/RateLimiterBaseUpgradeable.t.sol +826 -0
  35. package/test/RateLimiterRBACUpgradeable.t.sol +128 -0
  36. package/test/RateLimiterRBACUpgradeableInvariant.t.sol +444 -0
  37. package/test/RateLimiterUtils.t.sol +137 -0
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2026 - LayerZero Labs Ltd.
2
+
3
+ Permission is hereby granted, free of charge, to any
4
+ person obtaining a copy of this software and associated
5
+ documentation files (the "Software"), to deal in the
6
+ Software without restriction, including without
7
+ limitation the rights to use, copy, modify, merge,
8
+ publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software
10
+ is furnished to do so, subject to the following
11
+ conditions:
12
+ The above copyright notice and this permission notice
13
+ shall be included in all copies or substantial portions
14
+ of the Software.
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19
+ SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
22
+ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23
+ DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,144 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { IAccessControl2Step } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/IAccessControl2Step.sol";
5
+ import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
6
+ import { AccessControlEnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol";
7
+
8
+ /**
9
+ * @title AccessControl2StepUpgradeable
10
+ * @author LayerZero Labs (tinom.eth)
11
+ * @custom:version 1.0.0
12
+ * @notice Abstract upgradeable contract that extends `AccessControlEnumerable` with a two-step transfer mechanism for
13
+ * the `DEFAULT_ADMIN_ROLE`, similar to `AccessControlDefaultAdminRules`. It does not enforce delayed transfers.
14
+ * @dev The `DEFAULT_ADMIN_ROLE` cannot be granted or revoked through the standard `grantRole` and `revokeRole`
15
+ * functions. Instead, a two-step process is enforced:
16
+ * 1. The current admin calls `beginDefaultAdminTransfer(newAdmin)`.
17
+ * 2. The pending admin calls `acceptDefaultAdminTransfer()`.
18
+ * The current default admin can cancel a pending transfer via `beginDefaultAdminTransfer(address(0))`.
19
+ * Renouncing the `DEFAULT_ADMIN_ROLE` is not permitted.
20
+ */
21
+ abstract contract AccessControl2StepUpgradeable is IAccessControl2Step, AccessControlEnumerableUpgradeable {
22
+ /// @custom:storage-location erc7201:layerzerov2.storage.accesscontrol2step
23
+ struct AccessControl2StepStorage {
24
+ address _pendingDefaultAdmin;
25
+ address _currentDefaultAdmin;
26
+ }
27
+
28
+ // keccak256(abi.encode(uint256(keccak256("layerzerov2.storage.accesscontrol2step")) - 1)) & ~bytes32(uint256(0xff))
29
+ bytes32 private constant ACCESS_CONTROL_2_STEP_STORAGE_LOCATION =
30
+ 0x9ab055b8fb8e38b861ceee93a65f20f7a2382a86570a6abe934fe7edfac60400;
31
+
32
+ /**
33
+ * @notice Internal function to get the contract storage.
34
+ * @return $ Storage pointer
35
+ */
36
+ function _getAccessControl2StepStorage() internal pure returns (AccessControl2StepStorage storage $) {
37
+ assembly {
38
+ $.slot := ACCESS_CONTROL_2_STEP_STORAGE_LOCATION
39
+ }
40
+ }
41
+
42
+ /**
43
+ * @notice Initializes the contract.
44
+ * @param _initialDefaultAdmin Default admin address
45
+ */
46
+ function __AccessControl2Step_init(address _initialDefaultAdmin) internal onlyInitializing {
47
+ __AccessControl2Step_init_unchained(_initialDefaultAdmin);
48
+ }
49
+
50
+ /**
51
+ * @notice Unchained initialization function for the contract.
52
+ * @param _initialDefaultAdmin Default admin address
53
+ */
54
+ function __AccessControl2Step_init_unchained(address _initialDefaultAdmin) internal onlyInitializing {
55
+ if (_initialDefaultAdmin == address(0)) {
56
+ revert InvalidDefaultAdmin(address(0));
57
+ }
58
+ AccessControl2StepStorage storage $ = _getAccessControl2StepStorage();
59
+ $._currentDefaultAdmin = _initialDefaultAdmin;
60
+ /// @dev Bypass `DEFAULT_ADMIN_ROLE` check.
61
+ super._grantRole(DEFAULT_ADMIN_ROLE, _initialDefaultAdmin);
62
+ }
63
+
64
+ // ============ Getters ============
65
+
66
+ /**
67
+ * @inheritdoc IAccessControl2Step
68
+ */
69
+ function defaultAdmin() public view virtual returns (address defaultAdminAddress) {
70
+ return _getAccessControl2StepStorage()._currentDefaultAdmin;
71
+ }
72
+
73
+ /**
74
+ * @inheritdoc IAccessControl2Step
75
+ */
76
+ function pendingDefaultAdmin() public view virtual returns (address pendingDefaultAdminAddress) {
77
+ return _getAccessControl2StepStorage()._pendingDefaultAdmin;
78
+ }
79
+
80
+ // ============ Setters ============
81
+
82
+ /**
83
+ * @inheritdoc IAccessControl2Step
84
+ */
85
+ function beginDefaultAdminTransfer(address _newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
86
+ AccessControl2StepStorage storage $ = _getAccessControl2StepStorage();
87
+ $._pendingDefaultAdmin = _newAdmin;
88
+ emit DefaultAdminTransferStarted(_newAdmin);
89
+ }
90
+
91
+ /**
92
+ * @inheritdoc IAccessControl2Step
93
+ */
94
+ function acceptDefaultAdminTransfer() public virtual {
95
+ AccessControl2StepStorage storage $ = _getAccessControl2StepStorage();
96
+
97
+ address pendingAdmin = $._pendingDefaultAdmin;
98
+ if (pendingAdmin != _msgSender()) {
99
+ revert CallerNotPendingAdmin(pendingAdmin);
100
+ }
101
+
102
+ /// @dev Bypass `DEFAULT_ADMIN_ROLE` check.
103
+ super._revokeRole(DEFAULT_ADMIN_ROLE, $._currentDefaultAdmin);
104
+ super._grantRole(DEFAULT_ADMIN_ROLE, pendingAdmin);
105
+
106
+ $._currentDefaultAdmin = pendingAdmin;
107
+ delete $._pendingDefaultAdmin;
108
+ }
109
+
110
+ // ============ Overrides ============
111
+
112
+ /**
113
+ * @dev Override to prevent granting `DEFAULT_ADMIN_ROLE`.
114
+ * @inheritdoc AccessControlEnumerableUpgradeable
115
+ */
116
+ function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
117
+ if (role == DEFAULT_ADMIN_ROLE) {
118
+ revert AccessControlEnforcedDefaultAdminRules();
119
+ }
120
+ return super._grantRole(role, account);
121
+ }
122
+
123
+ /**
124
+ * @dev Override to prevent revoking `DEFAULT_ADMIN_ROLE`.
125
+ * @inheritdoc AccessControlEnumerableUpgradeable
126
+ */
127
+ function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
128
+ if (role == DEFAULT_ADMIN_ROLE) {
129
+ revert AccessControlEnforcedDefaultAdminRules();
130
+ }
131
+ return super._revokeRole(role, account);
132
+ }
133
+
134
+ /**
135
+ * @dev Override to prevent updating the admin of the `DEFAULT_ADMIN_ROLE`.
136
+ * @inheritdoc AccessControlUpgradeable
137
+ */
138
+ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {
139
+ if (role == DEFAULT_ADMIN_ROLE) {
140
+ revert AccessControlEnforcedDefaultAdminRules();
141
+ }
142
+ super._setRoleAdmin(role, adminRole);
143
+ }
144
+ }
@@ -0,0 +1,184 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { IAllowlist } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/IAllowlist.sol";
5
+ import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
6
+ import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
7
+ import { EnumerableSetPagination } from "./../libs/EnumerableSetPagination.sol";
8
+
9
+ /**
10
+ * @title AllowlistBaseUpgradeable
11
+ * @author LayerZero Labs (tinom.eth)
12
+ * @custom:version 1.0.0
13
+ * @notice Abstract upgradeable contract that provides toggleable allowlist functionality between open, blacklist, and
14
+ * whitelist modes.
15
+ * @dev No public management functions are exposed by this contract, wrappers should be used with access control.
16
+ * Alternatively, refer to `AllowlistRBACUpgradeable` for a permissioned implementation.
17
+ */
18
+ abstract contract AllowlistBaseUpgradeable is IAllowlist, Initializable {
19
+ using EnumerableSet for EnumerableSet.AddressSet;
20
+ using EnumerableSetPagination for EnumerableSet.AddressSet;
21
+
22
+ /// @custom:storage-location erc7201:layerzerov2.storage.allowlist
23
+ struct AllowlistStorage {
24
+ AllowlistMode mode;
25
+ EnumerableSet.AddressSet blacklistedSet;
26
+ EnumerableSet.AddressSet whitelistedSet;
27
+ }
28
+
29
+ // keccak256(abi.encode(uint256(keccak256("layerzerov2.storage.allowlist")) - 1)) & ~bytes32(uint256(0xff))
30
+ bytes32 private constant ALLOWLIST_STORAGE_LOCATION =
31
+ 0xa9f917c522fb3fd8673bbc655e92a5c1f1ee3df7bf7ad06a1821b937d0205e00;
32
+
33
+ /**
34
+ * @notice Internal function to get the allowlist storage.
35
+ * @return $ Storage pointer
36
+ */
37
+ function _getAllowlistStorage() internal pure returns (AllowlistStorage storage $) {
38
+ assembly {
39
+ $.slot := ALLOWLIST_STORAGE_LOCATION
40
+ }
41
+ }
42
+
43
+ /**
44
+ * @notice Initializes the contract.
45
+ * @param _mode Initial mode
46
+ */
47
+ function __AllowlistBase_init(AllowlistMode _mode) internal onlyInitializing {
48
+ __AllowlistBase_init_unchained(_mode);
49
+ }
50
+
51
+ /**
52
+ * @notice Unchained initialization function for the contract.
53
+ * @param _mode Initial mode
54
+ */
55
+ function __AllowlistBase_init_unchained(AllowlistMode _mode) internal onlyInitializing {
56
+ /// @dev Not using `_setAllowlistMode` to avoid reverting if the mode is open.
57
+ AllowlistStorage storage $ = _getAllowlistStorage();
58
+ $.mode = _mode;
59
+ emit AllowlistModeUpdated(_mode);
60
+ }
61
+
62
+ /**
63
+ * @notice Modifier that reverts if the user is not allowlisted.
64
+ * @param _user User address
65
+ */
66
+ modifier onlyAllowlisted(address _user) {
67
+ if (!isAllowlisted(_user)) revert NotAllowlisted(_user, allowlistMode());
68
+ _;
69
+ }
70
+
71
+ /**
72
+ * @inheritdoc IAllowlist
73
+ */
74
+ function allowlistMode() public view virtual returns (AllowlistMode mode) {
75
+ return _getAllowlistStorage().mode;
76
+ }
77
+
78
+ /**
79
+ * @inheritdoc IAllowlist
80
+ */
81
+ function isBlacklisted(address _user) public view virtual returns (bool isUserBlacklisted) {
82
+ return _getAllowlistStorage().blacklistedSet.contains(_user);
83
+ }
84
+
85
+ /**
86
+ * @inheritdoc IAllowlist
87
+ */
88
+ function isWhitelisted(address _user) public view virtual returns (bool isUserWhitelisted) {
89
+ return _getAllowlistStorage().whitelistedSet.contains(_user);
90
+ }
91
+
92
+ /**
93
+ * @inheritdoc IAllowlist
94
+ */
95
+ function isAllowlisted(address _user) public view virtual returns (bool isUserAllowlisted) {
96
+ AllowlistMode currentMode = allowlistMode();
97
+ if (currentMode == AllowlistMode.Open) return true;
98
+ if (currentMode == AllowlistMode.Blacklist) return !isBlacklisted(_user);
99
+ /// @dev `currentMode == AllowlistMode.Whitelist`.
100
+ return isWhitelisted(_user);
101
+ }
102
+
103
+ /**
104
+ * @inheritdoc IAllowlist
105
+ */
106
+ function blacklistedCount() public view virtual returns (uint256 count) {
107
+ return _getAllowlistStorage().blacklistedSet.length();
108
+ }
109
+
110
+ /**
111
+ * @inheritdoc IAllowlist
112
+ */
113
+ function whitelistedCount() public view virtual returns (uint256 count) {
114
+ return _getAllowlistStorage().whitelistedSet.length();
115
+ }
116
+
117
+ /**
118
+ * @inheritdoc IAllowlist
119
+ */
120
+ function getBlacklist(uint256 _offset, uint256 _limit) public view virtual returns (address[] memory addresses) {
121
+ return _getAllowlistStorage().blacklistedSet.paginate(_offset, _limit);
122
+ }
123
+
124
+ /**
125
+ * @inheritdoc IAllowlist
126
+ */
127
+ function getWhitelist(uint256 _offset, uint256 _limit) public view virtual returns (address[] memory addresses) {
128
+ return _getAllowlistStorage().whitelistedSet.paginate(_offset, _limit);
129
+ }
130
+
131
+ // ============ Internal Functions to Wrap with Access Control ============
132
+
133
+ /**
134
+ * @notice Internal function to set the allowlist mode.
135
+ * @dev To be wrapped with access control.
136
+ * @param _mode New mode
137
+ */
138
+ function _setAllowlistMode(AllowlistMode _mode) internal virtual {
139
+ AllowlistStorage storage $ = _getAllowlistStorage();
140
+ if ($.mode == _mode) revert ModeAlreadySet(_mode);
141
+ $.mode = _mode;
142
+ emit AllowlistModeUpdated(_mode);
143
+ }
144
+
145
+ /**
146
+ * @notice Internal function to set the whitelist state for an array of users.
147
+ * @dev To be wrapped with access control.
148
+ * @param _params Array of users and whitelist states
149
+ */
150
+ function _setWhitelisted(SetAllowlistParam[] calldata _params) internal virtual {
151
+ AllowlistStorage storage $ = _getAllowlistStorage();
152
+ for (uint256 i = 0; i < _params.length; i++) {
153
+ SetAllowlistParam calldata param = _params[i];
154
+ bool addedOrRemoved;
155
+ if (param.isEnabled) {
156
+ addedOrRemoved = $.whitelistedSet.add(param.user);
157
+ } else {
158
+ addedOrRemoved = $.whitelistedSet.remove(param.user);
159
+ }
160
+ if (!addedOrRemoved) revert AllowlistStateIdempotent(param.user, param.isEnabled);
161
+ emit WhitelistUpdated(param.user, param.isEnabled);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * @notice Internal function to set the blacklist state for an array of users.
167
+ * @dev To be wrapped with access control.
168
+ * @param _params Array of users and blacklist states
169
+ */
170
+ function _setBlacklisted(SetAllowlistParam[] calldata _params) internal virtual {
171
+ AllowlistStorage storage $ = _getAllowlistStorage();
172
+ for (uint256 i = 0; i < _params.length; i++) {
173
+ SetAllowlistParam calldata param = _params[i];
174
+ bool addedOrRemoved;
175
+ if (param.isEnabled) {
176
+ addedOrRemoved = $.blacklistedSet.add(param.user);
177
+ } else {
178
+ addedOrRemoved = $.blacklistedSet.remove(param.user);
179
+ }
180
+ if (!addedOrRemoved) revert AllowlistStateIdempotent(param.user, param.isEnabled);
181
+ emit BlacklistUpdated(param.user, param.isEnabled);
182
+ }
183
+ }
184
+ }
@@ -0,0 +1,55 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { IAllowlist } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/IAllowlist.sol";
5
+ import { AccessControl2StepUpgradeable } from "./../access/AccessControl2StepUpgradeable.sol";
6
+ import { AllowlistBaseUpgradeable } from "./AllowlistBaseUpgradeable.sol";
7
+
8
+ /**
9
+ * @title AllowlistRBACUpgradeable
10
+ * @author LayerZero Labs (tinom.eth)
11
+ * @custom:version 1.0.0
12
+ * @notice Abstract upgradeable contract that provides toggleable allowlist functionality between open, blacklist, and
13
+ * whitelist modes.
14
+ * @dev Exposes public management functions through `AccessControl2StepUpgradeable`.
15
+ */
16
+ abstract contract AllowlistRBACUpgradeable is AllowlistBaseUpgradeable, AccessControl2StepUpgradeable {
17
+ /// @notice Role for setting the blacklist state for users.
18
+ bytes32 public constant BLACKLISTER_ROLE = keccak256("BLACKLISTER_ROLE");
19
+
20
+ /// @notice Role for setting the whitelist state for users.
21
+ bytes32 public constant WHITELISTER_ROLE = keccak256("WHITELISTER_ROLE");
22
+
23
+ /**
24
+ * @notice Initializes the contract.
25
+ * @dev This function is empty on purpose, as no custom logic is needed.
26
+ */
27
+ function __AllowlistRBAC_init() internal onlyInitializing {}
28
+
29
+ /**
30
+ * @notice Unchained initialization function for the contract.
31
+ * @dev This function is empty on purpose, as no custom logic is needed.
32
+ */
33
+ function __AllowlistRBAC_init_unchained() internal onlyInitializing {}
34
+
35
+ /**
36
+ * @inheritdoc IAllowlist
37
+ */
38
+ function setAllowlistMode(AllowlistMode _mode) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
39
+ _setAllowlistMode(_mode);
40
+ }
41
+
42
+ /**
43
+ * @inheritdoc IAllowlist
44
+ */
45
+ function setBlacklisted(SetAllowlistParam[] calldata _params) public virtual onlyRole(BLACKLISTER_ROLE) {
46
+ _setBlacklisted(_params);
47
+ }
48
+
49
+ /**
50
+ * @inheritdoc IAllowlist
51
+ */
52
+ function setWhitelisted(SetAllowlistParam[] calldata _params) public virtual onlyRole(WHITELISTER_ROLE) {
53
+ _setWhitelisted(_params);
54
+ }
55
+ }
@@ -0,0 +1,69 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { IFeeHandler } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/IFeeHandler.sol";
5
+ import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
6
+
7
+ /**
8
+ * @title FeeHandlerBaseUpgradeable
9
+ * @author LayerZero Labs (tinom.eth)
10
+ * @custom:version 1.0.0
11
+ * @notice Abstract upgradeable contract that stores the fee deposit address, for push-based fee handling.
12
+ * @dev Fee transfer logic is handled by the inheriting contract. This contract only manages the `feeDeposit` state.
13
+ * @dev No public management functions are exposed by this contract, wrappers should be used with access control.
14
+ * Alternatively, refer to `FeeHandlerRBACUpgradeable` for a permissioned implementation.
15
+ * @dev Does not include fee configuration (BPS / calculation). See `FeeConfigBaseUpgradeable` for that.
16
+ */
17
+ abstract contract FeeHandlerBaseUpgradeable is IFeeHandler, Initializable {
18
+ /// @custom:storage-location erc7201:layerzerov2.storage.feehandler
19
+ struct FeeHandlerStorage {
20
+ address feeDeposit;
21
+ }
22
+
23
+ // keccak256(abi.encode(uint256(keccak256("layerzerov2.storage.feehandler")) - 1)) & ~bytes32(uint256(0xff))
24
+ bytes32 private constant FEE_HANDLER_STORAGE_LOCATION =
25
+ 0xe32e0c5f1df3081ca85b86156deac82a46e8fb7b21b412e09f7ccdc5fca29900;
26
+
27
+ /**
28
+ * @notice Internal function to get the fee handler storage.
29
+ * @return $ Storage pointer
30
+ */
31
+ function _getFeeHandlerStorage() internal pure returns (FeeHandlerStorage storage $) {
32
+ assembly {
33
+ $.slot := FEE_HANDLER_STORAGE_LOCATION
34
+ }
35
+ }
36
+
37
+ /**
38
+ * @notice Initializes the contract with a fee deposit.
39
+ * @param _feeDeposit Address that will receive any accrued fees
40
+ */
41
+ function __FeeHandlerBase_init(address _feeDeposit) internal onlyInitializing {
42
+ __FeeHandlerBase_init_unchained(_feeDeposit);
43
+ }
44
+
45
+ /**
46
+ * @notice Unchained initialization function for the contract.
47
+ * @param _feeDeposit Address that will receive any accrued fees
48
+ */
49
+ function __FeeHandlerBase_init_unchained(address _feeDeposit) internal onlyInitializing {
50
+ _setFeeDeposit(_feeDeposit);
51
+ }
52
+
53
+ /**
54
+ * @inheritdoc IFeeHandler
55
+ */
56
+ function feeDeposit() public view virtual returns (address deposit) {
57
+ return _getFeeHandlerStorage().feeDeposit;
58
+ }
59
+
60
+ /**
61
+ * @notice Sets the fee deposit address.
62
+ * @param _feeDeposit New fee deposit address
63
+ */
64
+ function _setFeeDeposit(address _feeDeposit) internal virtual {
65
+ if (_feeDeposit == address(0)) revert InvalidFeeDeposit();
66
+ _getFeeHandlerStorage().feeDeposit = _feeDeposit;
67
+ emit FeeDepositSet(_feeDeposit);
68
+ }
69
+ }
@@ -0,0 +1,34 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { IFeeHandler } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/IFeeHandler.sol";
5
+ import { AccessControl2StepUpgradeable } from "./../access/AccessControl2StepUpgradeable.sol";
6
+ import { FeeHandlerBaseUpgradeable } from "./FeeHandlerBaseUpgradeable.sol";
7
+
8
+ /**
9
+ * @title FeeHandlerRBACUpgradeable
10
+ * @author LayerZero Labs (tinom.eth)
11
+ * @custom:version 1.0.0
12
+ * @notice Abstract upgradeable contract that stores the fee deposit address, for push-based fee handling.
13
+ * @dev Exposes public management functions through `AccessControl2StepUpgradeable`.
14
+ */
15
+ abstract contract FeeHandlerRBACUpgradeable is FeeHandlerBaseUpgradeable, AccessControl2StepUpgradeable {
16
+ /**
17
+ * @notice Initializes the contract.
18
+ * @dev This function is empty on purpose, as no custom logic is needed.
19
+ */
20
+ function __FeeHandlerRBAC_init() internal onlyInitializing {}
21
+
22
+ /**
23
+ * @notice Unchained initialization function for the contract.
24
+ * @dev This function is empty on purpose, as no custom logic is needed.
25
+ */
26
+ function __FeeHandlerRBAC_init_unchained() internal onlyInitializing {}
27
+
28
+ /**
29
+ * @inheritdoc IFeeHandler
30
+ */
31
+ function setFeeDeposit(address _feeDeposit) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
32
+ _setFeeDeposit(_feeDeposit);
33
+ }
34
+ }
@@ -0,0 +1,128 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { IFeeConfig } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/IFeeConfig.sol";
5
+ import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
6
+
7
+ /**
8
+ * @title FeeConfigBaseUpgradeable
9
+ * @author LayerZero Labs (@TRileySchwarz, tinom.eth)
10
+ * @custom:version 1.0.0
11
+ * @notice Abstract upgradeable contract that implements fee configuration and calculation.
12
+ * @dev No public management functions are exposed by this contract, wrappers should be used with access control.
13
+ * Alternatively, refer to `FeeConfigRBACUpgradeable` for a permissioned implementation.
14
+ * @dev Does not include fee receiver management. See `FeeHandlerBaseUpgradeable` for that.
15
+ */
16
+ abstract contract FeeConfigBaseUpgradeable is IFeeConfig, Initializable {
17
+ /// @notice Constant with which fee basis points (BPS) are divided to get the fee amount.
18
+ uint16 public constant BPS_DENOMINATOR = 10_000;
19
+
20
+ /// @custom:storage-location erc7201:layerzerov2.storage.feeconfig
21
+ struct FeeConfigStorage {
22
+ uint16 defaultFeeBps;
23
+ mapping(uint256 id => FeeConfig config) feeBps;
24
+ }
25
+
26
+ // keccak256(abi.encode(uint256(keccak256("layerzerov2.storage.feeconfig")) - 1)) & ~bytes32(uint256(0xff))
27
+ bytes32 private constant FEE_CONFIG_STORAGE_LOCATION =
28
+ 0x19c40dd5c7b9d6e4dbe259e67955cccfb75eaf6c218fbfbd413bfcd8248dd800;
29
+
30
+ /**
31
+ * @notice Internal function to get the fee config storage.
32
+ * @return $ Storage pointer
33
+ */
34
+ function _getFeeConfigStorage() internal pure returns (FeeConfigStorage storage $) {
35
+ assembly {
36
+ $.slot := FEE_CONFIG_STORAGE_LOCATION
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @notice Initializes the contract.
42
+ * @dev This function is empty on purpose, as no custom logic is needed.
43
+ */
44
+ function __FeeConfigBase_init() internal onlyInitializing {}
45
+
46
+ /**
47
+ * @notice Unchained initialization function for the contract.
48
+ * @dev This function is empty on purpose, as no custom logic is needed.
49
+ */
50
+ function __FeeConfigBase_init_unchained() internal onlyInitializing {}
51
+
52
+ /**
53
+ * @inheritdoc IFeeConfig
54
+ */
55
+ function getFee(uint256 _id, uint256 _amount) public view virtual returns (uint256 fee) {
56
+ uint16 bps = _getFeeBps(_id);
57
+ /// @dev If `amount * bps < BPS_DENOMINATOR`, there is no fee.
58
+ return bps == 0 ? 0 : (_amount * bps) / BPS_DENOMINATOR;
59
+ }
60
+
61
+ /**
62
+ * @inheritdoc IFeeConfig
63
+ */
64
+ function getAmountBeforeFee(
65
+ uint256 _id,
66
+ uint256 _amountAfterFee
67
+ ) public view virtual returns (uint256 amountBeforeFee) {
68
+ uint16 bps = _getFeeBps(_id);
69
+ if (bps == BPS_DENOMINATOR) return 0;
70
+ if (bps == 0) return _amountAfterFee;
71
+ return (_amountAfterFee * BPS_DENOMINATOR) / (BPS_DENOMINATOR - bps);
72
+ }
73
+
74
+ /**
75
+ * @inheritdoc IFeeConfig
76
+ */
77
+ function defaultFeeBps() public view virtual returns (uint16 fee) {
78
+ FeeConfigStorage storage $ = _getFeeConfigStorage();
79
+ return $.defaultFeeBps;
80
+ }
81
+
82
+ /**
83
+ * @inheritdoc IFeeConfig
84
+ */
85
+ function feeBps(uint256 _id) public view virtual returns (FeeConfig memory config) {
86
+ FeeConfigStorage storage $ = _getFeeConfigStorage();
87
+ return $.feeBps[_id];
88
+ }
89
+
90
+ /**
91
+ * @notice Retrieves the fee basis points (BPS) for a specific destination ID.
92
+ * @param _id Destination ID
93
+ * @return bps Fee basis points (BPS) for the destination ID
94
+ */
95
+ function _getFeeBps(uint256 _id) internal view virtual returns (uint16) {
96
+ FeeConfigStorage storage $ = _getFeeConfigStorage();
97
+ FeeConfig storage config = $.feeBps[_id];
98
+ return config.enabled ? config.feeBps : $.defaultFeeBps;
99
+ }
100
+
101
+ // ============ Internal Functions to Wrap with Access Control ============
102
+
103
+ /**
104
+ * @notice Internal function to set the default fee basis points (BPS) for all destinations.
105
+ * @dev To be wrapped with access control.
106
+ * @param _feeBps New default fee basis points (BPS)
107
+ */
108
+ function _setDefaultFeeBps(uint16 _feeBps) internal virtual {
109
+ if (_feeBps > BPS_DENOMINATOR) revert IFeeConfig.InvalidBps(_feeBps);
110
+ FeeConfigStorage storage $ = _getFeeConfigStorage();
111
+ $.defaultFeeBps = _feeBps;
112
+ emit DefaultFeeBpsSet(_feeBps);
113
+ }
114
+
115
+ /**
116
+ * @notice Internal function to set the fee basis points (BPS) for a specific destination ID.
117
+ * @dev To be wrapped with access control.
118
+ * @param _id Destination ID
119
+ * @param _feeBps New fee basis points (BPS)
120
+ * @param _enabled Whether the fee is enabled for the destination
121
+ */
122
+ function _setFeeBps(uint256 _id, uint16 _feeBps, bool _enabled) internal virtual {
123
+ if (_feeBps > BPS_DENOMINATOR) revert IFeeConfig.InvalidBps(_feeBps);
124
+ FeeConfigStorage storage $ = _getFeeConfigStorage();
125
+ $.feeBps[_id] = FeeConfig(_feeBps, _enabled);
126
+ emit FeeBpsSet(_id, _feeBps, _enabled);
127
+ }
128
+ }
@@ -0,0 +1,44 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { IFeeConfig } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/IFeeConfig.sol";
5
+ import { AccessControl2StepUpgradeable } from "./../access/AccessControl2StepUpgradeable.sol";
6
+ import { FeeConfigBaseUpgradeable } from "./FeeConfigBaseUpgradeable.sol";
7
+
8
+ /**
9
+ * @title FeeConfigRBACUpgradeable
10
+ * @author LayerZero Labs (@TRileySchwarz, tinom.eth)
11
+ * @custom:version 1.0.0
12
+ * @notice Abstract upgradeable contract that implements fee configuration and calculation.
13
+ * @dev Exposes public management functions through `AccessControl2StepUpgradeable`.
14
+ */
15
+ abstract contract FeeConfigRBACUpgradeable is FeeConfigBaseUpgradeable, AccessControl2StepUpgradeable {
16
+ /// @notice Role for setting fee basis points.
17
+ bytes32 public constant FEE_CONFIG_MANAGER_ROLE = keccak256("FEE_CONFIG_MANAGER_ROLE");
18
+
19
+ /**
20
+ * @notice Initializes the contract.
21
+ * @dev This function is empty on purpose, as no custom logic is needed.
22
+ */
23
+ function __FeeConfigRBAC_init() internal onlyInitializing {}
24
+
25
+ /**
26
+ * @notice Unchained initialization function for the contract.
27
+ * @dev This function is empty on purpose, as no custom logic is needed.
28
+ */
29
+ function __FeeConfigRBAC_init_unchained() internal onlyInitializing {}
30
+
31
+ /**
32
+ * @inheritdoc IFeeConfig
33
+ */
34
+ function setDefaultFeeBps(uint16 _feeBps) public virtual onlyRole(FEE_CONFIG_MANAGER_ROLE) {
35
+ _setDefaultFeeBps(_feeBps);
36
+ }
37
+
38
+ /**
39
+ * @inheritdoc IFeeConfig
40
+ */
41
+ function setFeeBps(uint256 _id, uint16 _feeBps, bool _enabled) public virtual onlyRole(FEE_CONFIG_MANAGER_ROLE) {
42
+ _setFeeBps(_id, _feeBps, _enabled);
43
+ }
44
+ }