@chainlink/ace 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/.foundry-version +1 -0
  2. package/.github/CODEOWNERS +1 -0
  3. package/.github/workflows/auto-release-version.yml +107 -0
  4. package/.github/workflows/create-version-pr.yml +95 -0
  5. package/.github/workflows/forge-docs.yml +90 -0
  6. package/.github/workflows/forge-test.yml +59 -0
  7. package/.solhint-test.json +18 -0
  8. package/.solhint.json +16 -0
  9. package/.solhintignore +3 -0
  10. package/.solhintignore-test +2 -0
  11. package/Glossary.md +141 -0
  12. package/LICENSE +59 -0
  13. package/README.md +218 -0
  14. package/assets/chainlink-logo.svg +21 -0
  15. package/chainlink-ace-License-grants +2 -0
  16. package/foundry.toml +33 -0
  17. package/getting_started/GETTING_STARTED.md +477 -0
  18. package/getting_started/MyVault.sol +48 -0
  19. package/getting_started/advanced/.env.example +36 -0
  20. package/getting_started/advanced/GETTING_STARTED_ADVANCED.md +431 -0
  21. package/getting_started/advanced/SanctionsList.sol +25 -0
  22. package/getting_started/advanced/SanctionsPolicy.sol +58 -0
  23. package/package.json +41 -0
  24. package/packages/cross-chain-identity/README.md +148 -0
  25. package/packages/cross-chain-identity/docs/API_GUIDE.md +120 -0
  26. package/packages/cross-chain-identity/docs/API_REFERENCE.md +271 -0
  27. package/packages/cross-chain-identity/docs/CONCEPTS.md +253 -0
  28. package/packages/cross-chain-identity/docs/CREDENTIAL_FLOW.md +195 -0
  29. package/packages/cross-chain-identity/docs/SECURITY.md +70 -0
  30. package/packages/cross-chain-identity/src/CredentialRegistry.sol +245 -0
  31. package/packages/cross-chain-identity/src/CredentialRegistryIdentityValidator.sol +339 -0
  32. package/packages/cross-chain-identity/src/CredentialRegistryIdentityValidatorPolicy.sol +71 -0
  33. package/packages/cross-chain-identity/src/IdentityRegistry.sol +123 -0
  34. package/packages/cross-chain-identity/src/TrustedIssuerRegistry.sol +140 -0
  35. package/packages/cross-chain-identity/src/interfaces/ICredentialDataValidator.sol +30 -0
  36. package/packages/cross-chain-identity/src/interfaces/ICredentialRegistry.sol +170 -0
  37. package/packages/cross-chain-identity/src/interfaces/ICredentialRequirements.sol +192 -0
  38. package/packages/cross-chain-identity/src/interfaces/ICredentialValidator.sol +37 -0
  39. package/packages/cross-chain-identity/src/interfaces/IIdentityRegistry.sol +85 -0
  40. package/packages/cross-chain-identity/src/interfaces/IIdentityValidator.sol +18 -0
  41. package/packages/cross-chain-identity/src/interfaces/ITrustedIssuerRegistry.sol +61 -0
  42. package/packages/cross-chain-identity/test/CredentialRegistry.t.sol +220 -0
  43. package/packages/cross-chain-identity/test/CredentialRegistryIdentityValidator.t.sol +554 -0
  44. package/packages/cross-chain-identity/test/CredentialRegistryIdentityValidatorPolicy.t.sol +114 -0
  45. package/packages/cross-chain-identity/test/IdentityRegistry.t.sol +106 -0
  46. package/packages/cross-chain-identity/test/IdentityValidator.t.sol +969 -0
  47. package/packages/cross-chain-identity/test/TrustedIssuerRegistry.t.sol +123 -0
  48. package/packages/cross-chain-identity/test/helpers/BaseProxyTest.sol +112 -0
  49. package/packages/cross-chain-identity/test/helpers/MockCredentialDataValidator.sol +26 -0
  50. package/packages/cross-chain-identity/test/helpers/MockCredentialRegistryReverting.sol +131 -0
  51. package/packages/policy-management/README.md +197 -0
  52. package/packages/policy-management/docs/API_GUIDE.md +290 -0
  53. package/packages/policy-management/docs/API_REFERENCE.md +173 -0
  54. package/packages/policy-management/docs/CONCEPTS.md +156 -0
  55. package/packages/policy-management/docs/CUSTOM_POLICIES_TUTORIAL.md +195 -0
  56. package/packages/policy-management/docs/POLICY_ORDERING_GUIDE.md +91 -0
  57. package/packages/policy-management/docs/SECURITY.md +57 -0
  58. package/packages/policy-management/src/core/Policy.sol +124 -0
  59. package/packages/policy-management/src/core/PolicyEngine.sol +382 -0
  60. package/packages/policy-management/src/core/PolicyFactory.sol +92 -0
  61. package/packages/policy-management/src/core/PolicyProtected.sol +126 -0
  62. package/packages/policy-management/src/extractors/ComplianceTokenForceTransferExtractor.sol +57 -0
  63. package/packages/policy-management/src/extractors/ComplianceTokenFreezeUnfreezeExtractor.sol +54 -0
  64. package/packages/policy-management/src/extractors/ComplianceTokenMintBurnExtractor.sol +61 -0
  65. package/packages/policy-management/src/extractors/ERC20ApproveExtractor.sol +57 -0
  66. package/packages/policy-management/src/extractors/ERC20TransferExtractor.sol +62 -0
  67. package/packages/policy-management/src/extractors/ERC3643ForcedTransferExtractor.sol +56 -0
  68. package/packages/policy-management/src/extractors/ERC3643FreezeUnfreezeExtractor.sol +55 -0
  69. package/packages/policy-management/src/extractors/ERC3643MintBurnExtractor.sol +51 -0
  70. package/packages/policy-management/src/extractors/ERC3643SetAddressFrozenExtractor.sol +51 -0
  71. package/packages/policy-management/src/interfaces/IExtractor.sol +17 -0
  72. package/packages/policy-management/src/interfaces/IMapper.sol +17 -0
  73. package/packages/policy-management/src/interfaces/IPolicy.sol +61 -0
  74. package/packages/policy-management/src/interfaces/IPolicyEngine.sol +264 -0
  75. package/packages/policy-management/src/interfaces/IPolicyProtected.sol +48 -0
  76. package/packages/policy-management/src/policies/AllowPolicy.sol +104 -0
  77. package/packages/policy-management/src/policies/BypassPolicy.sol +90 -0
  78. package/packages/policy-management/src/policies/IntervalPolicy.sol +223 -0
  79. package/packages/policy-management/src/policies/MaxPolicy.sol +73 -0
  80. package/packages/policy-management/src/policies/OnlyAuthorizedSenderPolicy.sol +84 -0
  81. package/packages/policy-management/src/policies/OnlyOwnerPolicy.sol +35 -0
  82. package/packages/policy-management/src/policies/PausePolicy.sol +82 -0
  83. package/packages/policy-management/src/policies/README.md +632 -0
  84. package/packages/policy-management/src/policies/RejectPolicy.sol +89 -0
  85. package/packages/policy-management/src/policies/RoleBasedAccessControlPolicy.sol +162 -0
  86. package/packages/policy-management/src/policies/SecureMintPolicy.sol +271 -0
  87. package/packages/policy-management/src/policies/VolumePolicy.sol +133 -0
  88. package/packages/policy-management/src/policies/VolumeRatePolicy.sol +192 -0
  89. package/packages/policy-management/test/PolicyEngine.t.sol +368 -0
  90. package/packages/policy-management/test/PolicyFactory.t.sol +114 -0
  91. package/packages/policy-management/test/PolicyProtectedToken.t.sol +75 -0
  92. package/packages/policy-management/test/extractors/ComplianceTokenForceTransferExtractor.t.sol +59 -0
  93. package/packages/policy-management/test/extractors/ComplianceTokenFreezeUnfreezeExtractor.t.sol +74 -0
  94. package/packages/policy-management/test/extractors/ComplianceTokenMintBurnExtractor.t.sol +92 -0
  95. package/packages/policy-management/test/extractors/ERC20ApproveExtractor.t.sol +58 -0
  96. package/packages/policy-management/test/extractors/ERC3643ForcedTransferExtractor.t.sol +59 -0
  97. package/packages/policy-management/test/extractors/ERC3643FreezeUnfreezeExtractor.t.sol +74 -0
  98. package/packages/policy-management/test/extractors/ERC3643MintBurnExtractor.t.sol +73 -0
  99. package/packages/policy-management/test/extractors/ERC3643SetAddressFrozenExtractor.t.sol +56 -0
  100. package/packages/policy-management/test/helpers/BaseProxyTest.sol +75 -0
  101. package/packages/policy-management/test/helpers/CustomMapper.sol +26 -0
  102. package/packages/policy-management/test/helpers/DummyExtractor.sol +11 -0
  103. package/packages/policy-management/test/helpers/ExpectedParameterPolicy.sol +39 -0
  104. package/packages/policy-management/test/helpers/MockAggregatorV3.sol +51 -0
  105. package/packages/policy-management/test/helpers/MockToken.sol +66 -0
  106. package/packages/policy-management/test/helpers/MockTokenExtractor.sol +34 -0
  107. package/packages/policy-management/test/helpers/PolicyAlwaysAllowed.sol +45 -0
  108. package/packages/policy-management/test/helpers/PolicyAlwaysContinue.sol +23 -0
  109. package/packages/policy-management/test/helpers/PolicyAlwaysRejected.sol +23 -0
  110. package/packages/policy-management/test/helpers/PolicyFailingRun.sol +22 -0
  111. package/packages/policy-management/test/policies/AllowPolicy.t.sol +174 -0
  112. package/packages/policy-management/test/policies/BypassPolicy.t.sol +159 -0
  113. package/packages/policy-management/test/policies/IntervalPolicy.t.sol +307 -0
  114. package/packages/policy-management/test/policies/MaxPolicy.t.sol +54 -0
  115. package/packages/policy-management/test/policies/OnlyAuthorizedSenderPolicy.t.sol +95 -0
  116. package/packages/policy-management/test/policies/OnlyOwnerPolicy.t.sol +47 -0
  117. package/packages/policy-management/test/policies/PausePolicy.t.sol +75 -0
  118. package/packages/policy-management/test/policies/RejectPolicy.t.sol +182 -0
  119. package/packages/policy-management/test/policies/RoleBasedAccessControlPolicy.t.sol +223 -0
  120. package/packages/policy-management/test/policies/SecureMintPolicy.t.sol +442 -0
  121. package/packages/policy-management/test/policies/VolumePolicy.t.sol +158 -0
  122. package/packages/policy-management/test/policies/VolumeRatePolicy.t.sol +165 -0
  123. package/packages/tokens/erc-20/src/ComplianceTokenERC20.sol +345 -0
  124. package/packages/tokens/erc-20/src/ComplianceTokenStoreERC20.sol +29 -0
  125. package/packages/tokens/erc-20/test/ComplianceTokenERC20.t.sol +556 -0
  126. package/packages/tokens/erc-20/test/helpers/BaseProxyTest.sol +75 -0
  127. package/packages/tokens/erc-3643/README.md +24 -0
  128. package/packages/tokens/erc-3643/src/ComplianceTokenERC3643.sol +564 -0
  129. package/packages/tokens/erc-3643/src/ComplianceTokenStoreERC3643.sol +30 -0
  130. package/packages/tokens/erc-3643/test/ComplianceTokenERC3643.t.sol +815 -0
  131. package/packages/tokens/erc-3643/test/helpers/BaseProxyTest.sol +76 -0
  132. package/packages/tokens/erc-3643/test/helpers/ExpectedContextPolicy.sol +32 -0
  133. package/packages/vendor/erc-3643/compliance/modular/IModularCompliance.sol +220 -0
  134. package/packages/vendor/erc-3643/registry/interface/IClaimTopicsRegistry.sol +101 -0
  135. package/packages/vendor/erc-3643/registry/interface/IIdentityRegistry.sol +251 -0
  136. package/packages/vendor/erc-3643/registry/interface/IIdentityRegistryStorage.sol +191 -0
  137. package/packages/vendor/erc-3643/registry/interface/ITrustedIssuersRegistry.sol +161 -0
  138. package/packages/vendor/erc-3643/token/IToken.sol +457 -0
  139. package/packages/vendor/onchain-id/interface/IClaimIssuer.sol +53 -0
  140. package/packages/vendor/onchain-id/interface/IERC734.sol +110 -0
  141. package/packages/vendor/onchain-id/interface/IERC735.sol +105 -0
  142. package/packages/vendor/onchain-id/interface/IIdentity.sol +26 -0
  143. package/packages/vendor/onchain-id/interface/IImplementationAuthority.sol +21 -0
  144. package/remappings.txt +6 -0
  145. package/script/DeployComplianceTokenERC20.s.sol +191 -0
  146. package/script/DeployComplianceTokenERC3643.s.sol +208 -0
  147. package/script/DeploySimpleComplianceToken.s.sol +38 -0
  148. package/script/getting_started/DeployGettingStarted.s.sol +74 -0
  149. package/script/getting_started/advanced/DeployAdvancedGettingStarted.s.sol +332 -0
  150. package/script/getting_started/advanced/DeploySanctionsList.s.sol +26 -0
@@ -0,0 +1,75 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {IPolicyEngine, PolicyEngine} from "@chainlink/policy-management/core/PolicyEngine.sol";
5
+ import {PausePolicy} from "@chainlink/policy-management/policies/PausePolicy.sol";
6
+ import {MockToken} from "../helpers/MockToken.sol";
7
+ import {BaseProxyTest} from "../helpers/BaseProxyTest.sol";
8
+
9
+ contract PausePolicyTest is BaseProxyTest {
10
+ PolicyEngine public policyEngine;
11
+ MockToken public token;
12
+ PausePolicy public pausePolicy;
13
+ address public deployer;
14
+ address public recipient;
15
+
16
+ function setUp() public {
17
+ deployer = makeAddr("deployer");
18
+ recipient = makeAddr("recipient");
19
+
20
+ vm.startPrank(deployer);
21
+
22
+ policyEngine = _deployPolicyEngine(true, deployer);
23
+
24
+ PausePolicy pausePolicyImpl = new PausePolicy();
25
+ bytes memory configParamBytes = abi.encode(false); // Initial paused state is false
26
+ pausePolicy =
27
+ PausePolicy(_deployPolicy(address(pausePolicyImpl), address(policyEngine), deployer, configParamBytes));
28
+
29
+ token = MockToken(_deployMockToken(address(policyEngine)));
30
+
31
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(pausePolicy), new bytes32[](0));
32
+ }
33
+
34
+ function test_transfer_whenPaused_reverts() public {
35
+ vm.startPrank(deployer);
36
+ pausePolicy.pause();
37
+
38
+ vm.expectRevert(_encodeRejectedRevert(MockToken.transfer.selector, address(pausePolicy), "contract is paused"));
39
+ token.transfer(recipient, 100);
40
+ }
41
+
42
+ function test_transfer_whenNotPaused_succeeds() public {
43
+ token.transfer(recipient, 100);
44
+ assert(token.balanceOf(recipient) == 100);
45
+ }
46
+
47
+ function test_transfer_afterUnpause_succeeds() public {
48
+ vm.startPrank(deployer);
49
+ pausePolicy.pause();
50
+ assert(pausePolicy.s_paused() == true);
51
+ pausePolicy.unpause();
52
+
53
+ token.transfer(recipient, 100);
54
+ assert(token.balanceOf(recipient) == 100);
55
+ }
56
+
57
+ function test_pause_whenAlreadyPaused_reverts() public {
58
+ vm.startPrank(deployer);
59
+
60
+ pausePolicy.pause();
61
+ assert(pausePolicy.s_paused() == true);
62
+
63
+ vm.expectRevert("already paused");
64
+ pausePolicy.pause();
65
+ }
66
+
67
+ function test_unpause_whenAlreadyUnpaused_reverts() public {
68
+ vm.startPrank(deployer);
69
+
70
+ assert(pausePolicy.s_paused() == false);
71
+
72
+ vm.expectRevert("already unpaused");
73
+ pausePolicy.unpause();
74
+ }
75
+ }
@@ -0,0 +1,182 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {IPolicyEngine, PolicyEngine} from "@chainlink/policy-management/core/PolicyEngine.sol";
5
+ import {ERC20TransferExtractor} from "@chainlink/policy-management/extractors/ERC20TransferExtractor.sol";
6
+ import {RejectPolicy} from "@chainlink/policy-management/policies/RejectPolicy.sol";
7
+ import {MockToken} from "../helpers/MockToken.sol";
8
+ import {ERC3643MintBurnExtractor} from "@chainlink/policy-management/extractors/ERC3643MintBurnExtractor.sol";
9
+ import {BaseProxyTest} from "../helpers/BaseProxyTest.sol";
10
+
11
+ contract RejectPolicyTest is BaseProxyTest {
12
+ PolicyEngine public policyEngine;
13
+ MockToken public token;
14
+ RejectPolicy public rejectPolicy;
15
+ address public deployer;
16
+ address public account;
17
+ address public recipient;
18
+
19
+ function setUp() public {
20
+ deployer = makeAddr("deployer");
21
+ account = makeAddr("account");
22
+ recipient = makeAddr("recipient");
23
+
24
+ vm.startPrank(deployer, deployer);
25
+
26
+ policyEngine = _deployPolicyEngine(true, deployer);
27
+
28
+ RejectPolicy rejectPolicyImpl = new RejectPolicy();
29
+ rejectPolicy = RejectPolicy(_deployPolicy(address(rejectPolicyImpl), address(policyEngine), deployer, ""));
30
+
31
+ token = MockToken(_deployMockToken(address(policyEngine)));
32
+
33
+ // set up the rejectPolicy to check the recipient and origin address of token transfers
34
+ ERC20TransferExtractor transferExtractor = new ERC20TransferExtractor();
35
+ bytes32[] memory policyParameters = new bytes32[](2);
36
+ policyParameters[0] = transferExtractor.PARAM_TO();
37
+ policyParameters[1] = transferExtractor.PARAM_FROM();
38
+ policyEngine.setExtractor(MockToken.transfer.selector, address(transferExtractor));
39
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(rejectPolicy), policyParameters);
40
+ // set up the rejectPolicy to check the mint account (single account)
41
+ ERC3643MintBurnExtractor mintBurnExtractor = new ERC3643MintBurnExtractor();
42
+ bytes32[] memory mintPolicyParams = new bytes32[](1);
43
+ mintPolicyParams[0] = mintBurnExtractor.PARAM_ACCOUNT();
44
+ policyEngine.setExtractor(MockToken.mint.selector, address(mintBurnExtractor));
45
+ policyEngine.addPolicy(address(token), MockToken.mint.selector, address(rejectPolicy), mintPolicyParams);
46
+ }
47
+
48
+ function test_rejectAddress_succeeds() public {
49
+ vm.startPrank(deployer, deployer);
50
+
51
+ // add the sender to the reject list
52
+ rejectPolicy.rejectAddress(account);
53
+ vm.assertEq(rejectPolicy.addressRejected(account), true);
54
+ }
55
+
56
+ function test_addressRejected_alreadyInList_fails() public {
57
+ vm.startPrank(deployer, deployer);
58
+
59
+ // add the sender to the reject list (setup and sanity check)
60
+ rejectPolicy.rejectAddress(account);
61
+ vm.assertEq(rejectPolicy.addressRejected(account), true);
62
+
63
+ // add the sender to the reject list again (reverts)
64
+ vm.expectRevert("Account already in reject list");
65
+ rejectPolicy.rejectAddress(account);
66
+ }
67
+
68
+ function test_unrejectAddress_succeeds() public {
69
+ vm.startPrank(deployer, deployer);
70
+
71
+ // add the sender to the reject list (setup and sanity check)
72
+ rejectPolicy.rejectAddress(account);
73
+ vm.assertEq(rejectPolicy.addressRejected(account), true);
74
+
75
+ // remove the sender from the reject list
76
+ rejectPolicy.unrejectAddress(account);
77
+ vm.assertEq(rejectPolicy.addressRejected(account), false);
78
+ }
79
+
80
+ function test_unrejectAddress_notInList_fails() public {
81
+ vm.startPrank(deployer, deployer);
82
+
83
+ // remove the sender from the reject list (reverts)
84
+ vm.expectRevert("Account not in reject list");
85
+ rejectPolicy.unrejectAddress(account);
86
+ }
87
+
88
+ function test_transfer_notInList_succeeds() public {
89
+ vm.startPrank(account, account);
90
+
91
+ // transfer from sender to recipient
92
+ token.transfer(recipient, 100);
93
+ vm.assertEq(token.balanceOf(recipient), 100);
94
+ }
95
+
96
+ function test_transfer_inList_reverts() public {
97
+ vm.startPrank(deployer, deployer);
98
+
99
+ // add the recipient to the reject list
100
+ rejectPolicy.rejectAddress(recipient);
101
+ vm.assertEq(rejectPolicy.addressRejected(recipient), true);
102
+
103
+ vm.startPrank(account, account);
104
+
105
+ // transfer from sender to recipient (reverts)
106
+ vm.expectRevert(
107
+ _encodeRejectedRevert(MockToken.transfer.selector, address(rejectPolicy), "address is on reject list")
108
+ );
109
+ token.transfer(recipient, 100);
110
+ }
111
+
112
+ function test_transfer_allInList_reverts() public {
113
+ vm.startPrank(deployer, deployer);
114
+
115
+ // add the account and recipient to the reject list
116
+ rejectPolicy.rejectAddress(recipient);
117
+ vm.assertEq(rejectPolicy.addressRejected(recipient), true);
118
+ rejectPolicy.rejectAddress(account);
119
+ vm.assertEq(rejectPolicy.addressRejected(account), true);
120
+
121
+ vm.startPrank(account, account);
122
+
123
+ // transfer from sender to recipient (reverts)
124
+ vm.expectRevert(
125
+ _encodeRejectedRevert(MockToken.transfer.selector, address(rejectPolicy), "address is on reject list")
126
+ );
127
+ token.transfer(recipient, 100);
128
+ }
129
+
130
+ function test_transfer_removedFromList_succeeds() public {
131
+ // add the address to the reject list (setup)
132
+ vm.startPrank(deployer, deployer);
133
+ rejectPolicy.rejectAddress(recipient);
134
+
135
+ // transfer from address to recipient (sanity check)
136
+ vm.startPrank(account, account);
137
+ vm.expectRevert(
138
+ _encodeRejectedRevert(MockToken.transfer.selector, address(rejectPolicy), "address is on reject list")
139
+ );
140
+ token.transfer(recipient, 100);
141
+
142
+ // remove from the reject list
143
+ vm.startPrank(deployer, deployer);
144
+ rejectPolicy.unrejectAddress(recipient);
145
+
146
+ // transfer from address to recipient
147
+ vm.startPrank(account, account);
148
+ token.transfer(recipient, 100);
149
+ vm.assertEq(token.balanceOf(recipient), 100);
150
+ }
151
+
152
+ function test_mint_notInList_success() public {
153
+ vm.startPrank(deployer, deployer);
154
+ token.mint(account, 100);
155
+ vm.assertEq(token.balanceOf(account), 100);
156
+ }
157
+
158
+ function test_mint_inList_failure() public {
159
+ vm.startPrank(deployer, deployer);
160
+ // add account as rejected
161
+ rejectPolicy.rejectAddress(account);
162
+ vm.assertEq(rejectPolicy.addressRejected(account), true);
163
+ vm.expectRevert(_encodeRejectedRevert(MockToken.mint.selector, address(rejectPolicy), "address is on reject list"));
164
+ token.mint(account, 100);
165
+ }
166
+
167
+ function test_misconfiguration_failure() public {
168
+ vm.startPrank(deployer);
169
+ // misconfigure the rejectPolicy to check mint operations
170
+ ERC3643MintBurnExtractor mintExtractor = new ERC3643MintBurnExtractor();
171
+ policyEngine.setExtractor(MockToken.burn.selector, address(mintExtractor));
172
+ policyEngine.addPolicy(address(token), MockToken.burn.selector, address(rejectPolicy), new bytes32[](0));
173
+
174
+ bytes memory error = abi.encodeWithSignature("Error(string)", "expected at least 1 parameter");
175
+ vm.expectRevert(
176
+ abi.encodeWithSelector(
177
+ IPolicyEngine.PolicyRunError.selector, MockToken.burn.selector, address(rejectPolicy), error
178
+ )
179
+ );
180
+ token.burn(recipient, 100);
181
+ }
182
+ }
@@ -0,0 +1,223 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
5
+ import {RoleBasedAccessControlPolicy} from "@chainlink/policy-management/policies/RoleBasedAccessControlPolicy.sol";
6
+ import {IPolicyEngine} from "@chainlink/policy-management/interfaces/IPolicyEngine.sol";
7
+ import {PolicyEngine} from "@chainlink/policy-management/core/PolicyEngine.sol";
8
+ import {MockToken} from "../helpers/MockToken.sol";
9
+ import {BaseProxyTest} from "../helpers/BaseProxyTest.sol";
10
+
11
+ contract RoleBasedAccessControlPolicyTest is BaseProxyTest {
12
+ RoleBasedAccessControlPolicy policy;
13
+ PolicyEngine public policyEngine;
14
+ MockToken public token;
15
+ address public deployer;
16
+ address public txSender;
17
+ address public recipient;
18
+
19
+ function setUp() public {
20
+ deployer = makeAddr("deployer");
21
+ txSender = makeAddr("txSender");
22
+
23
+ vm.startPrank(deployer);
24
+
25
+ policyEngine = _deployPolicyEngine(true, deployer);
26
+
27
+ token = MockToken(_deployMockToken(address(policyEngine)));
28
+
29
+ RoleBasedAccessControlPolicy policyImpl = new RoleBasedAccessControlPolicy();
30
+ policy = RoleBasedAccessControlPolicy(_deployPolicy(address(policyImpl), address(policyEngine), deployer, ""));
31
+
32
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(policy), new bytes32[](0));
33
+ }
34
+
35
+ function test_grantRoleRevokeRole_deployer_succeeds() public {
36
+ bytes32 someRole = keccak256("someRole");
37
+
38
+ vm.startPrank(deployer);
39
+
40
+ vm.expectEmit();
41
+ emit IAccessControl.RoleGranted(someRole, txSender, deployer);
42
+ policy.grantRole(someRole, txSender);
43
+ assertEq(policy.hasRole(someRole, txSender), true);
44
+ vm.expectEmit();
45
+ emit IAccessControl.RoleRevoked(someRole, txSender, deployer);
46
+ policy.revokeRole(someRole, txSender);
47
+ assertEq(policy.hasRole(someRole, txSender), false);
48
+ }
49
+
50
+ function test_grantRoleRevokeRole_nonDeployer_fails() public {
51
+ bytes32 someRole = keccak256("someRole");
52
+ address nonDeployer = makeAddr("nonDeployer");
53
+
54
+ vm.startPrank(nonDeployer);
55
+
56
+ vm.expectRevert(abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, nonDeployer, 0x00));
57
+ policy.grantRole(someRole, txSender);
58
+ vm.expectRevert(abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, nonDeployer, 0x00));
59
+ policy.revokeRole(someRole, txSender);
60
+ }
61
+
62
+ function test_grantRoleRevokeRole_roleAdmin_succeeds() public {
63
+ bytes32 someRole = keccak256("someRole");
64
+ address admin = makeAddr("admin");
65
+
66
+ // grant admin role of "someRole" to admin
67
+ vm.startPrank(deployer);
68
+ policy.grantRole(policy.getRoleAdmin(someRole), admin);
69
+
70
+ vm.startPrank(admin);
71
+ vm.expectEmit();
72
+ emit IAccessControl.RoleGranted(someRole, txSender, admin);
73
+ policy.grantRole(someRole, txSender);
74
+ assertEq(policy.hasRole(someRole, txSender), true);
75
+ vm.expectEmit();
76
+ emit IAccessControl.RoleRevoked(someRole, txSender, admin);
77
+ policy.revokeRole(someRole, txSender);
78
+ assertEq(policy.hasRole(someRole, txSender), false);
79
+ }
80
+
81
+ function test_grantOperationAllowanceToRole_succeeds() public {
82
+ bytes32 role = keccak256("role");
83
+ vm.startPrank(deployer);
84
+
85
+ // grant operation allowance to role
86
+ vm.expectEmit();
87
+ emit RoleBasedAccessControlPolicy.OperationAllowanceGrantedToRole(MockToken.transfer.selector, role);
88
+ policy.grantOperationAllowanceToRole(MockToken.transfer.selector, role);
89
+ }
90
+
91
+ function test_grantOperationAllowanceToRole_alreadyExist_fails() public {
92
+ bytes32 role = keccak256("role");
93
+ vm.startPrank(deployer);
94
+
95
+ // grant operation allowance to role (sanity check)
96
+ vm.expectEmit();
97
+ emit RoleBasedAccessControlPolicy.OperationAllowanceGrantedToRole(MockToken.transfer.selector, role);
98
+ policy.grantOperationAllowanceToRole(MockToken.transfer.selector, role);
99
+
100
+ // grant again (revert)
101
+ vm.expectRevert("Role already has operation allowance");
102
+ policy.grantOperationAllowanceToRole(MockToken.transfer.selector, role);
103
+ }
104
+
105
+ function test_removeOperationAllowanceFromRole_succeeds() public {
106
+ bytes32 role = keccak256("role");
107
+ vm.startPrank(deployer);
108
+
109
+ // grant operation allowance to role (sanity check)
110
+ vm.expectEmit();
111
+ emit RoleBasedAccessControlPolicy.OperationAllowanceGrantedToRole(MockToken.transfer.selector, role);
112
+ policy.grantOperationAllowanceToRole(MockToken.transfer.selector, role);
113
+
114
+ // remove operation allowance from role
115
+ vm.expectEmit();
116
+ emit RoleBasedAccessControlPolicy.OperationAllowanceRemovedFromRole(MockToken.transfer.selector, role);
117
+ policy.removeOperationAllowanceFromRole(MockToken.transfer.selector, role);
118
+ }
119
+
120
+ function test_removeOperationAllowanceFromRole_invalidOperation_fails() public {
121
+ bytes32 role = keccak256("role");
122
+ vm.startPrank(deployer);
123
+
124
+ // remove invalid operation allowance from role (revert)
125
+ vm.expectRevert("Role does not have operation allowance");
126
+ policy.removeOperationAllowanceFromRole(MockToken.transfer.selector, role);
127
+ }
128
+
129
+ function test_transfer_senderWithoutRole_reverts() public {
130
+ vm.startPrank(txSender);
131
+
132
+ vm.expectRevert(
133
+ _encodeRejectedRevert(MockToken.transfer.selector, address(policy), "caller lacks required role for operation")
134
+ );
135
+ token.transfer(recipient, 100);
136
+ }
137
+
138
+ function test_transfer_withRoleAssociatedToOperation_succeeds() public {
139
+ vm.startPrank(deployer);
140
+ bytes32 allowedRole = keccak256("allowedRole");
141
+ policy.grantOperationAllowanceToRole(MockToken.transfer.selector, allowedRole);
142
+ policy.grantRole(allowedRole, txSender);
143
+
144
+ vm.startPrank(txSender);
145
+
146
+ token.transfer(recipient, 100);
147
+
148
+ assert(token.balanceOf(recipient) == 100);
149
+ }
150
+
151
+ function test_transfer_withRoleAssignedToUserButNotAssociatedToOperation_reverts() public {
152
+ vm.startPrank(deployer);
153
+ bytes32 someRole = keccak256("someRole");
154
+ policy.grantRole(someRole, txSender);
155
+
156
+ vm.startPrank(txSender);
157
+
158
+ vm.expectRevert(
159
+ _encodeRejectedRevert(MockToken.transfer.selector, address(policy), "caller lacks required role for operation")
160
+ );
161
+ token.transfer(recipient, 100);
162
+ }
163
+
164
+ function test_transfer_withRoleAssignedToUserAndRevokedFromOperation_reverts() public {
165
+ vm.startPrank(deployer);
166
+ bytes32 allowedRole = keccak256("allowedRole");
167
+ policy.grantOperationAllowanceToRole(MockToken.transfer.selector, allowedRole);
168
+ policy.grantRole(allowedRole, txSender);
169
+
170
+ // sanity check
171
+ vm.startPrank(txSender);
172
+ token.transfer(recipient, 100);
173
+ assertEq(token.balanceOf(recipient), 100);
174
+
175
+ vm.startPrank(deployer);
176
+ policy.removeOperationAllowanceFromRole(MockToken.transfer.selector, allowedRole);
177
+
178
+ vm.startPrank(txSender);
179
+ vm.expectRevert(
180
+ _encodeRejectedRevert(MockToken.transfer.selector, address(policy), "caller lacks required role for operation")
181
+ );
182
+ token.transfer(recipient, 100);
183
+ assertEq(token.balanceOf(recipient), 100);
184
+ }
185
+
186
+ function test_transfer_senderWithRoleAssociatedToOperationButRevoked_reverts() public {
187
+ vm.startPrank(deployer);
188
+ bytes32 allowedRole = keccak256("allowedRole");
189
+ policy.grantOperationAllowanceToRole(MockToken.transfer.selector, allowedRole);
190
+ policy.grantRole(allowedRole, txSender);
191
+
192
+ // sanity check
193
+ vm.startPrank(txSender);
194
+ token.transfer(recipient, 100);
195
+ assert(token.balanceOf(recipient) == 100);
196
+
197
+ vm.startPrank(deployer);
198
+ policy.revokeRole(allowedRole, txSender);
199
+
200
+ vm.startPrank(txSender);
201
+ vm.expectRevert(
202
+ _encodeRejectedRevert(MockToken.transfer.selector, address(policy), "caller lacks required role for operation")
203
+ );
204
+ token.transfer(recipient, 100);
205
+
206
+ assert(token.balanceOf(recipient) == 100);
207
+ }
208
+
209
+ function test_transfer_senderWithDifferentRole_reverts() public {
210
+ vm.startPrank(deployer);
211
+ bytes32 allowedRole = keccak256("allowedRole");
212
+ policy.grantOperationAllowanceToRole(MockToken.transfer.selector, allowedRole);
213
+
214
+ bytes32 anotherRole = keccak256("anotherRole");
215
+ policy.grantRole(anotherRole, txSender);
216
+
217
+ vm.startPrank(txSender);
218
+ vm.expectRevert(
219
+ _encodeRejectedRevert(MockToken.transfer.selector, address(policy), "caller lacks required role for operation")
220
+ );
221
+ token.transfer(recipient, 100);
222
+ }
223
+ }