@layerzerolabs/utils-upgradeable-evm-contracts 1.2.19 → 1.2.21

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.
@@ -0,0 +1,103 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { ICreditRedirect } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/ICreditRedirect.sol";
5
+ import { ICreditRedirectAllowlist } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/ICreditRedirectAllowlist.sol";
6
+ import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
7
+
8
+ /**
9
+ * @title CreditRedirectBaseUpgradeable
10
+ * @author LayerZero Labs (tinom.eth)
11
+ * @custom:version 1.0.0
12
+ * @notice Abstract upgradeable contract that implements configuration to redirect credits from non-allowlisted
13
+ * addresses to an escrow address.
14
+ * @dev No public management functions are exposed by this contract, wrappers should be used with access control.
15
+ * Alternatively, refer to `CreditRedirectRBACUpgradeable` for a permissioned implementation.
16
+ */
17
+ abstract contract CreditRedirectBaseUpgradeable is ICreditRedirect, Initializable {
18
+ /// @custom:storage-location erc7201:layerzerov2.storage.creditredirect
19
+ struct CreditRedirectStorage {
20
+ address allowlist;
21
+ address escrow;
22
+ }
23
+
24
+ // keccak256(abi.encode(uint256(keccak256("layerzerov2.storage.creditredirect")) - 1)) & ~bytes32(uint256(0xff))
25
+ bytes32 private constant CREDIT_REDIRECT_STORAGE_LOCATION =
26
+ 0xb868bd7fc06fef88c81262b11fe9b85cff6f00e2736baf361fa258c33ba8cd00;
27
+
28
+ /**
29
+ * @notice Internal function to get the credit redirect storage.
30
+ * @return $ Storage pointer
31
+ */
32
+ function _getCreditRedirectStorage() internal pure returns (CreditRedirectStorage storage $) {
33
+ assembly {
34
+ $.slot := CREDIT_REDIRECT_STORAGE_LOCATION
35
+ }
36
+ }
37
+
38
+ /**
39
+ * @notice Initializes the contract.
40
+ * @dev This function is empty on purpose, as no custom logic is needed.
41
+ */
42
+ function __CreditRedirectBase_init() internal onlyInitializing {}
43
+
44
+ /**
45
+ * @notice Unchained initialization function for the contract.
46
+ * @dev This function is empty on purpose, as no custom logic is needed.
47
+ */
48
+ function __CreditRedirectBase_init_unchained() internal onlyInitializing {}
49
+
50
+ /**
51
+ * @inheritdoc ICreditRedirect
52
+ */
53
+ function creditRedirectConfig() public view virtual returns (CreditRedirectConfig memory config) {
54
+ CreditRedirectStorage storage $ = _getCreditRedirectStorage();
55
+ return CreditRedirectConfig({ allowlist: $.allowlist, escrow: $.escrow });
56
+ }
57
+
58
+ /**
59
+ * @notice Checks whether a user is allowlisted, for credit redirection purposes.
60
+ * @param _user Address to check
61
+ * @return isUserAllowlisted Whether the user is allowlisted
62
+ */
63
+ function _isAllowlisted(address _user) internal view virtual returns (bool isUserAllowlisted) {
64
+ address allowlist = _getCreditRedirectStorage().allowlist;
65
+ if (allowlist == address(0)) return true;
66
+ return ICreditRedirectAllowlist(allowlist).isAllowlisted(_user);
67
+ }
68
+
69
+ /**
70
+ * @notice Redirects a credit to the escrow when the recipient is not allowlisted.
71
+ * @dev Returns `_to` unchanged when credit redirect is disabled or the recipient is allowlisted.
72
+ * @param _to Intended credit recipient
73
+ * @param _amountLD Amount to credit in local decimals
74
+ * @return recipient Effective credit recipient
75
+ */
76
+ function _redirectCredit(address _to, uint256 _amountLD) internal virtual returns (address recipient) {
77
+ if (!_isAllowlisted(_to)) {
78
+ /// @dev `escrow` is guaranteed to be non-zero here.
79
+ address escrow = _getCreditRedirectStorage().escrow;
80
+ emit CreditRedirected(_to, escrow, _amountLD);
81
+ return escrow;
82
+ }
83
+ return _to;
84
+ }
85
+
86
+ // ============ Internal Functions to Wrap with Access Control ============
87
+
88
+ /**
89
+ * @notice Internal function to set the credit redirect configuration.
90
+ * @dev `allowlist` and `escrow` must both be set or both be `address(0)` (disabled).
91
+ * @dev To be wrapped with access control.
92
+ * @param _config New credit redirect configuration
93
+ */
94
+ function _setCreditRedirectConfig(CreditRedirectConfig memory _config) internal virtual {
95
+ if ((_config.allowlist == address(0)) != (_config.escrow == address(0))) {
96
+ revert InvalidCreditRedirectConfig();
97
+ }
98
+ CreditRedirectStorage storage $ = _getCreditRedirectStorage();
99
+ $.allowlist = _config.allowlist;
100
+ $.escrow = _config.escrow;
101
+ emit CreditRedirectConfigSet(_config);
102
+ }
103
+ }
@@ -0,0 +1,37 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { ICreditRedirect } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/ICreditRedirect.sol";
5
+ import { AccessControl2StepUpgradeable } from "./../access/AccessControl2StepUpgradeable.sol";
6
+ import { CreditRedirectBaseUpgradeable } from "./CreditRedirectBaseUpgradeable.sol";
7
+
8
+ /**
9
+ * @title CreditRedirectRBACUpgradeable
10
+ * @author LayerZero Labs (tinom.eth)
11
+ * @custom:version 1.0.0
12
+ * @notice Abstract upgradeable contract that implements configuration to redirect credits from non-allowlisted
13
+ * addresses to an escrow address.
14
+ * @dev Exposes public management functions through `AccessControl2StepUpgradeable`.
15
+ */
16
+ abstract contract CreditRedirectRBACUpgradeable is CreditRedirectBaseUpgradeable, AccessControl2StepUpgradeable {
17
+ /**
18
+ * @notice Initializes the contract.
19
+ * @dev This function is empty on purpose, as no custom logic is needed.
20
+ */
21
+ function __CreditRedirectRBAC_init() internal onlyInitializing {}
22
+
23
+ /**
24
+ * @notice Unchained initialization function for the contract.
25
+ * @dev This function is empty on purpose, as no custom logic is needed.
26
+ */
27
+ function __CreditRedirectRBAC_init_unchained() internal onlyInitializing {}
28
+
29
+ /**
30
+ * @inheritdoc ICreditRedirect
31
+ */
32
+ function setCreditRedirectConfig(
33
+ CreditRedirectConfig calldata _config
34
+ ) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
35
+ _setCreditRedirectConfig(_config);
36
+ }
37
+ }
package/foundry.toml CHANGED
@@ -1,5 +1,5 @@
1
1
  [profile.default]
2
- solc = '0.8.22'
2
+ solc = '0.8.26'
3
3
  verbosity = 3
4
4
  src = "contracts"
5
5
  test = "test"
@@ -7,7 +7,7 @@ out = "out"
7
7
  cache_path = "cache"
8
8
  libs = ['node_modules', 'node_modules/@layerzerolabs/toolbox-foundry/lib']
9
9
  optimizer = true
10
- optimizer_runs = 200
10
+ optimizer_runs = 20_000
11
11
 
12
12
  remappings = [
13
13
  'ds-test/=node_modules/@layerzerolabs/toolbox-foundry/lib/ds-test/',
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@layerzerolabs/utils-upgradeable-evm-contracts",
3
- "version": "1.2.19",
3
+ "version": "1.2.21",
4
4
  "private": false,
5
5
  "description": "LayerZero Labs reference EVM upgradeable utility implementations",
6
6
  "license": "MIT",
7
7
  "devDependencies": {
8
8
  "@layerzerolabs/test-devtools-evm-foundry": "8.0.1",
9
9
  "@layerzerolabs/toolbox-foundry": "^0.1.13",
10
- "@openzeppelin/contracts": ">=5.0.2 <5.5.0",
11
- "@openzeppelin/contracts-upgradeable": ">=5.0.2 <5.5.0",
10
+ "@openzeppelin/contracts": "^5.0.2",
11
+ "@openzeppelin/contracts-upgradeable": "^5.0.2",
12
12
  "solhint": "^6.0.1",
13
- "@layerzerolabs/utils-evm-contracts": "1.2.19",
14
- "@layerzerolabs/solhint-configuration": "1.2.19",
15
- "@layerzerolabs/vm-tooling-evm": "1.2.19"
13
+ "@layerzerolabs/utils-evm-contracts": "1.2.21",
14
+ "@layerzerolabs/vm-tooling-evm": "1.2.21",
15
+ "@layerzerolabs/solhint-configuration": "1.2.21"
16
16
  },
17
17
  "publishConfig": {
18
18
  "access": "public",
@@ -0,0 +1,175 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { ICreditRedirect } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/ICreditRedirect.sol";
5
+ import { ICreditRedirectAllowlist } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/ICreditRedirectAllowlist.sol";
6
+ import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
7
+ import { Test } from "forge-std/Test.sol";
8
+ import { CreditRedirectBaseUpgradeable } from "./../contracts/credit-redirect/CreditRedirectBaseUpgradeable.sol";
9
+
10
+ contract CreditRedirectAllowlistMock is ICreditRedirectAllowlist {
11
+ mapping(address user => bool isAllowlisted) public allowlisted;
12
+
13
+ function setAllowlisted(address _user, bool _isAllowlisted) public {
14
+ allowlisted[_user] = _isAllowlisted;
15
+ }
16
+
17
+ function isAllowlisted(address _user) public view returns (bool isUserAllowlisted) {
18
+ return allowlisted[_user];
19
+ }
20
+ }
21
+
22
+ contract CreditRedirectBaseUpgradeableHarness is CreditRedirectBaseUpgradeable {
23
+ constructor() {
24
+ _disableInitializers();
25
+ }
26
+
27
+ function initialize() public initializer {
28
+ __CreditRedirectBase_init();
29
+ }
30
+
31
+ function setCreditRedirectConfig(ICreditRedirect.CreditRedirectConfig calldata _config) public {
32
+ _setCreditRedirectConfig(_config);
33
+ }
34
+
35
+ function isAllowlisted(address _user) public view returns (bool isUserAllowlisted) {
36
+ return _isAllowlisted(_user);
37
+ }
38
+
39
+ function redirectCredit(address _to, uint256 _amountLD) public returns (address recipient) {
40
+ return _redirectCredit(_to, _amountLD);
41
+ }
42
+ }
43
+
44
+ contract CreditRedirectBaseUpgradeableTest is Test {
45
+ CreditRedirectBaseUpgradeableHarness public creditRedirect;
46
+
47
+ address alice = makeAddr("alice");
48
+ address bob = makeAddr("bob");
49
+
50
+ function _deployCreditRedirect() internal virtual returns (CreditRedirectBaseUpgradeableHarness) {
51
+ CreditRedirectBaseUpgradeableHarness impl = new CreditRedirectBaseUpgradeableHarness();
52
+ TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
53
+ address(impl),
54
+ address(this),
55
+ abi.encodeWithSelector(CreditRedirectBaseUpgradeableHarness.initialize.selector)
56
+ );
57
+
58
+ return CreditRedirectBaseUpgradeableHarness(address(proxy));
59
+ }
60
+
61
+ function setUp() public virtual {
62
+ creditRedirect = _deployCreditRedirect();
63
+ }
64
+
65
+ function test_initialize() public view {
66
+ ICreditRedirect.CreditRedirectConfig memory config = creditRedirect.creditRedirectConfig();
67
+ assertEq(config.allowlist, address(0));
68
+ assertEq(config.escrow, address(0));
69
+ }
70
+
71
+ function test_setCreditRedirectConfig_Set() public {
72
+ ICreditRedirect.CreditRedirectConfig memory config = ICreditRedirect.CreditRedirectConfig({
73
+ allowlist: bob,
74
+ escrow: alice
75
+ });
76
+
77
+ vm.expectEmit(true, true, true, true, address(creditRedirect));
78
+ emit ICreditRedirect.CreditRedirectConfigSet(config);
79
+ creditRedirect.setCreditRedirectConfig(config);
80
+
81
+ ICreditRedirect.CreditRedirectConfig memory stored = creditRedirect.creditRedirectConfig();
82
+ assertEq(stored.allowlist, bob);
83
+ assertEq(stored.escrow, alice);
84
+ }
85
+
86
+ function test_setCreditRedirectConfig_Unset() public {
87
+ creditRedirect.setCreditRedirectConfig(ICreditRedirect.CreditRedirectConfig({ allowlist: bob, escrow: alice }));
88
+
89
+ ICreditRedirect.CreditRedirectConfig memory config = ICreditRedirect.CreditRedirectConfig({
90
+ allowlist: address(0),
91
+ escrow: address(0)
92
+ });
93
+
94
+ vm.expectEmit(true, true, true, true, address(creditRedirect));
95
+ emit ICreditRedirect.CreditRedirectConfigSet(config);
96
+ creditRedirect.setCreditRedirectConfig(config);
97
+
98
+ ICreditRedirect.CreditRedirectConfig memory stored = creditRedirect.creditRedirectConfig();
99
+ assertEq(stored.allowlist, address(0));
100
+ assertEq(stored.escrow, address(0));
101
+ }
102
+
103
+ function test_setCreditRedirectConfig_Fuzz(address _allowlist, address _escrow) public {
104
+ vm.assume((_allowlist == address(0)) == (_escrow == address(0)));
105
+
106
+ creditRedirect.setCreditRedirectConfig(
107
+ ICreditRedirect.CreditRedirectConfig({ allowlist: _allowlist, escrow: _escrow })
108
+ );
109
+
110
+ ICreditRedirect.CreditRedirectConfig memory stored = creditRedirect.creditRedirectConfig();
111
+ assertEq(stored.allowlist, _allowlist);
112
+ assertEq(stored.escrow, _escrow);
113
+ }
114
+
115
+ function test_setCreditRedirectConfig_Revert_AllowlistOnly() public {
116
+ vm.expectRevert(ICreditRedirect.InvalidCreditRedirectConfig.selector);
117
+ creditRedirect.setCreditRedirectConfig(
118
+ ICreditRedirect.CreditRedirectConfig({ allowlist: bob, escrow: address(0) })
119
+ );
120
+ }
121
+
122
+ function test_setCreditRedirectConfig_Revert_EscrowOnly() public {
123
+ vm.expectRevert(ICreditRedirect.InvalidCreditRedirectConfig.selector);
124
+ creditRedirect.setCreditRedirectConfig(
125
+ ICreditRedirect.CreditRedirectConfig({ allowlist: address(0), escrow: alice })
126
+ );
127
+ }
128
+
129
+ function test_isAllowlisted_NoAllowlist() public view {
130
+ assertTrue(creditRedirect.isAllowlisted(alice));
131
+ assertTrue(creditRedirect.isAllowlisted(bob));
132
+ }
133
+
134
+ function test_isAllowlisted_DelegatesToAllowlist() public {
135
+ CreditRedirectAllowlistMock allowlist = new CreditRedirectAllowlistMock();
136
+ allowlist.setAllowlisted(alice, true);
137
+ creditRedirect.setCreditRedirectConfig(
138
+ ICreditRedirect.CreditRedirectConfig({ allowlist: address(allowlist), escrow: bob })
139
+ );
140
+
141
+ assertTrue(creditRedirect.isAllowlisted(alice));
142
+ assertFalse(creditRedirect.isAllowlisted(bob));
143
+ }
144
+
145
+ function test_redirectCredit_Disabled() public {
146
+ assertEq(creditRedirect.redirectCredit(bob, 1 ether), bob);
147
+ }
148
+
149
+ function test_redirectCredit_Allowlisted() public {
150
+ CreditRedirectAllowlistMock allowlist = new CreditRedirectAllowlistMock();
151
+ allowlist.setAllowlisted(bob, true);
152
+ creditRedirect.setCreditRedirectConfig(
153
+ ICreditRedirect.CreditRedirectConfig({ allowlist: address(allowlist), escrow: alice })
154
+ );
155
+
156
+ assertEq(creditRedirect.redirectCredit(bob, 1 ether), bob);
157
+ }
158
+
159
+ function test_redirectCredit_NotAllowlisted() public {
160
+ CreditRedirectAllowlistMock allowlist = new CreditRedirectAllowlistMock();
161
+ creditRedirect.setCreditRedirectConfig(
162
+ ICreditRedirect.CreditRedirectConfig({ allowlist: address(allowlist), escrow: alice })
163
+ );
164
+
165
+ vm.expectEmit(true, true, true, true, address(creditRedirect));
166
+ emit ICreditRedirect.CreditRedirected(bob, alice, 1 ether);
167
+ assertEq(creditRedirect.redirectCredit(bob, 1 ether), alice);
168
+ }
169
+
170
+ function test_storageHash() public pure {
171
+ bytes32 storageHash = keccak256(abi.encode(uint256(keccak256("layerzerov2.storage.creditredirect")) - 1)) &
172
+ ~bytes32(uint256(0xff));
173
+ assertEq(storageHash, 0xb868bd7fc06fef88c81262b11fe9b85cff6f00e2736baf361fa258c33ba8cd00);
174
+ }
175
+ }
@@ -0,0 +1,63 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.22;
3
+
4
+ import { ICreditRedirect } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/ICreditRedirect.sol";
5
+ import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol";
6
+ import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
7
+ import { CreditRedirectRBACUpgradeable } from "./../contracts/credit-redirect/CreditRedirectRBACUpgradeable.sol";
8
+ import {
9
+ CreditRedirectBaseUpgradeableTest,
10
+ CreditRedirectBaseUpgradeableHarness
11
+ } from "./CreditRedirectBaseUpgradeable.t.sol";
12
+
13
+ contract CreditRedirectRBACUpgradeableHarness is CreditRedirectRBACUpgradeable {
14
+ constructor() {
15
+ _disableInitializers();
16
+ }
17
+
18
+ function initialize(address _initialAdmin) public initializer {
19
+ __AccessControl2Step_init(_initialAdmin);
20
+ __CreditRedirectBase_init();
21
+ }
22
+
23
+ function isAllowlisted(address _user) public view returns (bool isUserAllowlisted) {
24
+ return _isAllowlisted(_user);
25
+ }
26
+
27
+ function redirectCredit(address _to, uint256 _amountLD) public returns (address recipient) {
28
+ return _redirectCredit(_to, _amountLD);
29
+ }
30
+ }
31
+
32
+ contract CreditRedirectRBACUpgradeableTest is CreditRedirectBaseUpgradeableTest {
33
+ address charlie = makeAddr("charlie");
34
+ CreditRedirectRBACUpgradeableHarness creditRedirectRbac;
35
+
36
+ function _deployCreditRedirect() internal virtual override returns (CreditRedirectBaseUpgradeableHarness) {
37
+ CreditRedirectRBACUpgradeableHarness impl = new CreditRedirectRBACUpgradeableHarness();
38
+ TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
39
+ address(impl),
40
+ address(this),
41
+ abi.encodeWithSelector(CreditRedirectRBACUpgradeableHarness.initialize.selector, address(this))
42
+ );
43
+
44
+ return CreditRedirectBaseUpgradeableHarness(address(proxy));
45
+ }
46
+
47
+ function setUp() public override {
48
+ super.setUp();
49
+ creditRedirectRbac = CreditRedirectRBACUpgradeableHarness(address(creditRedirect));
50
+ }
51
+
52
+ function test_setCreditRedirectConfig_Revert_Unauthorized() public {
53
+ vm.expectRevert(
54
+ abi.encodeWithSelector(
55
+ IAccessControl.AccessControlUnauthorizedAccount.selector,
56
+ charlie,
57
+ creditRedirectRbac.DEFAULT_ADMIN_ROLE()
58
+ )
59
+ );
60
+ vm.prank(charlie);
61
+ creditRedirect.setCreditRedirectConfig(ICreditRedirect.CreditRedirectConfig({ allowlist: bob, escrow: alice }));
62
+ }
63
+ }