@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,382 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {IExtractor} from "../interfaces/IExtractor.sol";
5
+ import {IMapper} from "../interfaces/IMapper.sol";
6
+ import {IPolicy} from "../interfaces/IPolicy.sol";
7
+ import {IPolicyEngine} from "../interfaces/IPolicyEngine.sol";
8
+ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
9
+ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
10
+
11
+ contract PolicyEngine is Initializable, OwnableUpgradeable, IPolicyEngine {
12
+ uint256 private constant MAX_POLICIES = 8;
13
+
14
+ /// @custom:storage-location erc7201:policy-management.PolicyEngine
15
+ struct PolicyEngineStorage {
16
+ bool defaultPolicyAllow;
17
+ mapping(bytes4 selector => address extractor) extractorBySelector;
18
+ mapping(address policy => address mapper) policyMappers;
19
+ mapping(address target => bool attached) targetAttached;
20
+ mapping(address target => mapping(bytes4 selector => address[] policies)) targetPolicies;
21
+ mapping(address target => mapping(bytes4 selector => mapping(address policy => bytes32[] policyParameterNames)))
22
+ targetPolicyParameters;
23
+ mapping(address target => bool hasTargetDefault) targetHasDefault;
24
+ mapping(address target => bool targetDefaultPolicyAllow) targetDefaultPolicyAllow;
25
+ }
26
+
27
+ // keccak256(abi.encode(uint256(keccak256("policy-management.PolicyEngine")) - 1)) &
28
+ // ~bytes32(uint256(0xff))
29
+ // solhint-disable-next-line const-name-snakecase
30
+ bytes32 private constant policyEngineStorageLocation =
31
+ 0xa1f0e32dde2a220dbeed9998863e2afeb333bc7b502562572bef1aa4cf5bf300;
32
+
33
+ function _policyEngineStorage() private pure returns (PolicyEngineStorage storage $) {
34
+ // solhint-disable-next-line no-inline-assembly
35
+ assembly {
36
+ $.slot := policyEngineStorageLocation
37
+ }
38
+ }
39
+
40
+ constructor() {
41
+ _disableInitializers();
42
+ }
43
+
44
+ /**
45
+ * @dev Initializes the policy engine.
46
+ * @param defaultAllow The default policy result. True to allow, false to reject.
47
+ */
48
+ function initialize(bool defaultAllow, address initialOwner) public virtual initializer {
49
+ __PolicyEngine_init(defaultAllow, initialOwner);
50
+ }
51
+
52
+ function __PolicyEngine_init(bool defaultAllow, address initialOwner) internal onlyInitializing {
53
+ __PolicyEngine_init_unchained(defaultAllow);
54
+ __Ownable_init(initialOwner);
55
+ }
56
+
57
+ function __PolicyEngine_init_unchained(bool defaultAllow) internal onlyInitializing {
58
+ _policyEngineStorage().defaultPolicyAllow = defaultAllow;
59
+ }
60
+
61
+ /// @inheritdoc IPolicyEngine
62
+ // TODO: need to review the permissioing of this function
63
+ function attach() public {
64
+ _attachTarget(msg.sender);
65
+ }
66
+
67
+ function _attachTarget(address target) internal {
68
+ if (_policyEngineStorage().targetAttached[target]) {
69
+ revert IPolicyEngine.TargetAlreadyAttached(target);
70
+ }
71
+ _policyEngineStorage().targetAttached[target] = true;
72
+ emit TargetAttached(target);
73
+ }
74
+
75
+ /// @inheritdoc IPolicyEngine
76
+ // TODO: need to review the permissioing of this function
77
+ function detach() public {
78
+ if (!_policyEngineStorage().targetAttached[msg.sender]) {
79
+ revert IPolicyEngine.TargetNotAttached(msg.sender);
80
+ }
81
+ _policyEngineStorage().targetAttached[msg.sender] = false;
82
+ emit TargetDetached(msg.sender);
83
+ }
84
+
85
+ /// @inheritdoc IPolicyEngine
86
+ function setDefaultPolicyAllow(bool defaultAllow) public onlyOwner {
87
+ _policyEngineStorage().defaultPolicyAllow = defaultAllow;
88
+ emit DefaultPolicyAllowSet(defaultAllow);
89
+ }
90
+
91
+ /// @inheritdoc IPolicyEngine
92
+ function setTargetDefaultPolicyAllow(address target, bool defaultAllow) public onlyOwner {
93
+ PolicyEngineStorage storage $ = _policyEngineStorage();
94
+ $.targetHasDefault[target] = true;
95
+ $.targetDefaultPolicyAllow[target] = defaultAllow;
96
+ emit TargetDefaultPolicyAllowSet(target, defaultAllow);
97
+ }
98
+
99
+ /// @inheritdoc IPolicyEngine
100
+ function setPolicyMapper(address policy, address mapper) public onlyOwner {
101
+ _policyEngineStorage().policyMappers[policy] = mapper;
102
+ }
103
+
104
+ /// @inheritdoc IPolicyEngine
105
+ function getPolicyMapper(address policy) external view returns (address) {
106
+ return _policyEngineStorage().policyMappers[policy];
107
+ }
108
+
109
+ /// @inheritdoc IPolicyEngine
110
+ function check(IPolicyEngine.Payload calldata payload) public view virtual override {
111
+ address[] memory policies = _policyEngineStorage().targetPolicies[msg.sender][payload.selector];
112
+
113
+ if (policies.length == 0) {
114
+ _checkDefaultPolicyAllowRevert(msg.sender, payload.selector);
115
+ return;
116
+ }
117
+
118
+ IPolicyEngine.Parameter[] memory extractedParameters = _extractParameters(payload);
119
+ for (uint256 i = 0; i < policies.length; i++) {
120
+ address policy = policies[i];
121
+
122
+ bytes[] memory policyParameterValues = _policyParameterValues(
123
+ policy, _policyEngineStorage().targetPolicyParameters[msg.sender][payload.selector][policy], extractedParameters
124
+ );
125
+ try IPolicy(policy).run(payload.sender, msg.sender, payload.selector, policyParameterValues, payload.context)
126
+ returns (IPolicyEngine.PolicyResult policyResult) {
127
+ if (policyResult == IPolicyEngine.PolicyResult.Allowed) {
128
+ return;
129
+ } // else continue to next policy
130
+ } catch (bytes memory err) {
131
+ _handlePolicyError(payload, policy, err);
132
+ }
133
+ }
134
+
135
+ _checkDefaultPolicyAllowRevert(msg.sender, payload.selector);
136
+ }
137
+
138
+ /// @inheritdoc IPolicyEngine
139
+ function run(IPolicyEngine.Payload calldata payload) public virtual override {
140
+ address[] memory policies = _policyEngineStorage().targetPolicies[msg.sender][payload.selector];
141
+ if (policies.length == 0) {
142
+ _checkDefaultPolicyAllowRevert(msg.sender, payload.selector);
143
+ emit PolicyRunComplete(payload.sender, msg.sender, payload.selector);
144
+ return;
145
+ }
146
+
147
+ IPolicyEngine.Parameter[] memory extractedParameters = _extractParameters(payload);
148
+ for (uint256 i = 0; i < policies.length; i++) {
149
+ address policy = policies[i];
150
+
151
+ bytes[] memory policyParameterValues = _policyParameterValues(
152
+ policy, _policyEngineStorage().targetPolicyParameters[msg.sender][payload.selector][policy], extractedParameters
153
+ );
154
+ try IPolicy(policy).run(payload.sender, msg.sender, payload.selector, policyParameterValues, payload.context)
155
+ returns (IPolicyEngine.PolicyResult policyResult) {
156
+ // solhint-disable-next-line no-empty-blocks
157
+ try IPolicy(policy).postRun(
158
+ payload.sender, msg.sender, payload.selector, policyParameterValues, payload.context
159
+ ) {} catch (bytes memory err) {
160
+ revert IPolicyEngine.PolicyPostRunError(payload.selector, policy, err);
161
+ }
162
+ if (policyResult == IPolicyEngine.PolicyResult.Allowed) {
163
+ emit PolicyRunComplete(payload.sender, msg.sender, payload.selector);
164
+ return;
165
+ }
166
+ } catch (bytes memory err) {
167
+ _handlePolicyError(payload, policy, err);
168
+ }
169
+ }
170
+
171
+ _checkDefaultPolicyAllowRevert(msg.sender, payload.selector);
172
+ emit PolicyRunComplete(payload.sender, msg.sender, payload.selector);
173
+ }
174
+
175
+ /// @inheritdoc IPolicyEngine
176
+ function setExtractor(bytes4 selector, address extractor) public virtual override onlyOwner {
177
+ _policyEngineStorage().extractorBySelector[selector] = extractor;
178
+ emit ExtractorSet(selector, extractor);
179
+ }
180
+
181
+ /// @inheritdoc IPolicyEngine
182
+ function setExtractors(bytes4[] calldata selectors, address extractor) public virtual override onlyOwner {
183
+ for (uint256 i = 0; i < selectors.length; i++) {
184
+ setExtractor(selectors[i], extractor);
185
+ }
186
+ }
187
+
188
+ /// @inheritdoc IPolicyEngine
189
+ function getExtractor(bytes4 selector) public view virtual override returns (address) {
190
+ return _policyEngineStorage().extractorBySelector[selector];
191
+ }
192
+
193
+ /// @inheritdoc IPolicyEngine
194
+ function addPolicy(
195
+ address target,
196
+ bytes4 selector,
197
+ address policy,
198
+ bytes32[] calldata policyParameterNames
199
+ )
200
+ public
201
+ virtual
202
+ override
203
+ onlyOwner
204
+ {
205
+ _checkPolicyConfiguration(target, selector, policy);
206
+ IPolicy(policy).onInstall(selector);
207
+ _policyEngineStorage().targetPolicies[target][selector].push(policy);
208
+ _policyEngineStorage().targetPolicyParameters[target][selector][policy] = policyParameterNames;
209
+ emit PolicyAdded(target, selector, policy);
210
+ }
211
+
212
+ /// @inheritdoc IPolicyEngine
213
+ function addPolicyAt(
214
+ address target,
215
+ bytes4 selector,
216
+ address policy,
217
+ bytes32[] calldata policyParameterNames,
218
+ uint256 position
219
+ )
220
+ public
221
+ virtual
222
+ override
223
+ onlyOwner
224
+ {
225
+ address[] storage policies = _policyEngineStorage().targetPolicies[target][selector];
226
+ if (position > policies.length) {
227
+ revert IPolicyEngine.InvalidConfiguration("Position is greater than the number of policies");
228
+ }
229
+ _checkPolicyConfiguration(target, selector, policy);
230
+ IPolicy(policy).onInstall(selector);
231
+ policies.push();
232
+ for (uint256 i = policies.length - 1; i > position; i--) {
233
+ policies[i] = policies[i - 1];
234
+ }
235
+ policies[position] = policy;
236
+ _policyEngineStorage().targetPolicyParameters[target][selector][policy] = policyParameterNames;
237
+ emit PolicyAdded(target, selector, policy);
238
+ }
239
+
240
+ /// @inheritdoc IPolicyEngine
241
+ function removePolicy(address target, bytes4 selector, address policy) public virtual override onlyOwner {
242
+ address[] storage policies = _policyEngineStorage().targetPolicies[target][selector];
243
+ for (uint256 i = 0; i < policies.length; i++) {
244
+ if (policies[i] == policy) {
245
+ IPolicy(policy).onUninstall(selector);
246
+
247
+ for (uint256 j = i; j < policies.length - 1; j++) {
248
+ policies[j] = policies[j + 1];
249
+ }
250
+
251
+ policies.pop();
252
+ emit PolicyRemoved(target, selector, policy);
253
+ return;
254
+ }
255
+ }
256
+ }
257
+
258
+ /// @inheritdoc IPolicyEngine
259
+ function getPolicies(
260
+ address target,
261
+ bytes4 selector
262
+ )
263
+ public
264
+ view
265
+ virtual
266
+ override
267
+ returns (address[] memory policies)
268
+ {
269
+ return _policyEngineStorage().targetPolicies[target][selector];
270
+ }
271
+
272
+ function _handlePolicyError(Payload memory payload, address policy, bytes memory err) internal pure {
273
+ (bytes4 errorSelector, bytes memory errorData) = _decodeError(err);
274
+ if (errorSelector == IPolicyEngine.PolicyRejected.selector) {
275
+ revert IPolicyEngine.PolicyRunRejected(payload.selector, policy, abi.decode(errorData, (string)));
276
+ } else {
277
+ revert IPolicyEngine.PolicyRunError(payload.selector, policy, err);
278
+ }
279
+ }
280
+
281
+ function _checkDefaultPolicyAllowRevert(address target, bytes4 selector) private view {
282
+ PolicyEngineStorage storage $ = _policyEngineStorage();
283
+ bool defaultAllow = $.defaultPolicyAllow;
284
+ if ($.targetHasDefault[target]) {
285
+ defaultAllow = $.targetDefaultPolicyAllow[target];
286
+ }
287
+ if (!defaultAllow) {
288
+ revert IPolicyEngine.PolicyRunRejected(0, address(0), "no policy allowed the action and default is reject");
289
+ }
290
+ }
291
+
292
+ function _checkPolicyConfiguration(address target, bytes4 selector, address policy) private view {
293
+ if (policy == address(0)) {
294
+ revert IPolicyEngine.InvalidConfiguration("Policy address cannot be zero");
295
+ }
296
+ if (_policyEngineStorage().targetPolicies[target][selector].length >= MAX_POLICIES) {
297
+ revert IPolicyEngine.InvalidConfiguration("Maximum policies reached");
298
+ }
299
+ address[] memory policies = _policyEngineStorage().targetPolicies[target][selector];
300
+ for (uint256 i = 0; i < policies.length; i++) {
301
+ if (policies[i] == policy) {
302
+ revert IPolicyEngine.InvalidConfiguration("Policy already added");
303
+ }
304
+ }
305
+ }
306
+
307
+ function _extractParameters(IPolicyEngine.Payload memory payload)
308
+ private
309
+ view
310
+ returns (IPolicyEngine.Parameter[] memory)
311
+ {
312
+ IExtractor extractor = IExtractor(_policyEngineStorage().extractorBySelector[payload.selector]);
313
+ IPolicyEngine.Parameter[] memory extractedParameters;
314
+
315
+ if (address(extractor) == address(0)) {
316
+ return extractedParameters;
317
+ }
318
+
319
+ try extractor.extract(payload) returns (IPolicyEngine.Parameter[] memory _extractedParameters) {
320
+ extractedParameters = _extractedParameters;
321
+ } catch (bytes memory err) {
322
+ revert IPolicyEngine.ExtractorError(payload.selector, address(extractor), err);
323
+ }
324
+
325
+ return extractedParameters;
326
+ }
327
+
328
+ function _policyParameterValues(
329
+ address policy,
330
+ bytes32[] memory policyParameterNames,
331
+ IPolicyEngine.Parameter[] memory extractedParameters
332
+ )
333
+ private
334
+ view
335
+ returns (bytes[] memory)
336
+ {
337
+ address mapper = _policyEngineStorage().policyMappers[policy];
338
+ // use custom mapper if set
339
+ if (mapper != address(0)) {
340
+ try IMapper(mapper).map(extractedParameters) returns (bytes[] memory mappedParameters) {
341
+ return mappedParameters;
342
+ } catch (bytes memory err) {
343
+ revert IPolicyEngine.PolicyMapperError(policy, err);
344
+ }
345
+ }
346
+
347
+ bytes[] memory policyParameterValues = new bytes[](policyParameterNames.length);
348
+
349
+ uint256 parameterCount = policyParameterNames.length;
350
+ if (parameterCount == 0) {
351
+ return policyParameterValues;
352
+ }
353
+
354
+ uint256 mappedParameterCount = 0;
355
+ for (uint256 i = 0; i < extractedParameters.length; i++) {
356
+ for (uint256 j = 0; j < parameterCount; j++) {
357
+ if (extractedParameters[i].name == policyParameterNames[j]) {
358
+ policyParameterValues[j] = extractedParameters[i].value;
359
+ mappedParameterCount++;
360
+ break;
361
+ }
362
+ }
363
+ if (mappedParameterCount == parameterCount) {
364
+ return policyParameterValues;
365
+ }
366
+ }
367
+ revert IPolicyEngine.InvalidConfiguration("Missing policy parameters");
368
+ }
369
+
370
+ function _decodeError(bytes memory err) internal pure returns (bytes4, bytes memory) {
371
+ // If the error length is less than 4, it is not a valid error
372
+ if (err.length < 4) {
373
+ return (0, err);
374
+ }
375
+ bytes4 selector = bytes4(err);
376
+ bytes memory errorData = new bytes(err.length - 4);
377
+ for (uint256 i = 0; i < err.length - 4; i++) {
378
+ errorData[i] = err[i + 4];
379
+ }
380
+ return (selector, errorData);
381
+ }
382
+ }
@@ -0,0 +1,92 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {Policy} from "./Policy.sol";
5
+ import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
6
+
7
+ /**
8
+ * @title PolicyFactory
9
+ * @notice Factory contract for creating deterministic minimal proxy clones of policy implementations.
10
+ * @dev Uses OpenZeppelin's Clones library to create deterministic minimal proxies (EIP-1167) of policy contracts.
11
+ * Each policy is deployed with a unique salt derived from the creator's address and a unique policy ID,
12
+ * ensuring deterministic addresses and preventing duplicate deployments.
13
+ */
14
+ contract PolicyFactory {
15
+ /// @notice Emitted when a new policy is created
16
+ event PolicyCreated(address policy);
17
+
18
+ /// @notice Emitted when policy initialization fails
19
+ error PolicyInitializationFailed(bytes reason);
20
+
21
+ /**
22
+ * @notice Creates a new policy contract using deterministic minimal proxy cloning.
23
+ * @dev If a policy with the same salt already exists, returns the existing address instead of reverting.
24
+ * Uses CREATE2 for deterministic deployment addresses. The policy is automatically initialized
25
+ * with the provided parameters after deployment.
26
+ * @param implementation The address of the policy implementation contract to clone
27
+ * @param uniquePolicyId A unique identifier for this policy (combined with msg.sender to create salt)
28
+ * @param policyEngine The address of the policy engine that will manage this policy
29
+ * @param initialOwner The address that will own the newly created policy contract
30
+ * @param configData ABI-encoded configuration data specific to the policy implementation
31
+ * @return policyAddress The address of the created (or existing) policy contract
32
+ */
33
+ function createPolicy(
34
+ address implementation,
35
+ bytes32 uniquePolicyId,
36
+ address policyEngine,
37
+ address initialOwner,
38
+ bytes calldata configData
39
+ )
40
+ public
41
+ returns (address policyAddress)
42
+ {
43
+ bytes32 salt = getSalt(msg.sender, uniquePolicyId);
44
+ policyAddress = Clones.predictDeterministicAddress(implementation, salt);
45
+ if (policyAddress.code.length > 0) {
46
+ return policyAddress;
47
+ }
48
+
49
+ policyAddress = Clones.cloneDeterministic(implementation, salt);
50
+
51
+ try Policy(policyAddress).initialize(policyEngine, initialOwner, configData) {
52
+ emit PolicyCreated(policyAddress);
53
+ } catch (bytes memory reason) {
54
+ revert PolicyInitializationFailed(reason);
55
+ }
56
+ }
57
+
58
+ /**
59
+ * @notice Predicts the deterministic address where a policy would be deployed.
60
+ * @dev Useful for calculating policy addresses before deployment or checking if a policy already exists.
61
+ * Uses the same salt generation as createPolicy to ensure address consistency.
62
+ * @param creator The address of the account that would create the policy
63
+ * @param implementation The address of the policy implementation contract
64
+ * @param uniquePolicyId The unique identifier for the policy
65
+ * @return The predicted address where the policy would be deployed
66
+ */
67
+ function predictPolicyAddress(
68
+ address creator,
69
+ address implementation,
70
+ bytes32 uniquePolicyId
71
+ )
72
+ public
73
+ view
74
+ returns (address)
75
+ {
76
+ bytes32 salt = getSalt(creator, uniquePolicyId);
77
+ return Clones.predictDeterministicAddress(implementation, salt);
78
+ }
79
+
80
+ /**
81
+ * @notice Generates a deterministic salt for policy deployment.
82
+ * @dev Combines the sender address and unique policy ID to create a unique salt.
83
+ * This ensures that the same creator cannot deploy multiple policies with the same ID,
84
+ * while allowing different creators to use the same policy ID.
85
+ * @param sender The address of the policy creator
86
+ * @param uniquePolicyId The unique identifier for the policy
87
+ * @return The generated salt for deterministic deployment
88
+ */
89
+ function getSalt(address sender, bytes32 uniquePolicyId) public pure returns (bytes32) {
90
+ return keccak256(abi.encodePacked(sender, uniquePolicyId));
91
+ }
92
+ }
@@ -0,0 +1,126 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {IPolicyEngine} from "../interfaces/IPolicyEngine.sol";
5
+ import {IPolicyProtected} from "../interfaces/IPolicyProtected.sol";
6
+ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
7
+ import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
8
+ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
9
+ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
10
+
11
+ /**
12
+ * @title PolicyProtected.sol
13
+ * @dev Base implementation for attaching a policy engine to a smart contract. Uses ERC-7201 storage
14
+ * to not conflict with other storage slots of extending contracts. Provides modifiers to be attached to methods
15
+ * of the extending contract to run the policy engine before executing the method.
16
+ */
17
+ abstract contract PolicyProtected is Initializable, OwnableUpgradeable, ERC165Upgradeable, IPolicyProtected {
18
+ /// @custom:storage-location erc7201:policy-management.PolicyProtected
19
+ struct PolicyProtectedStorage {
20
+ IPolicyEngine policyEngine;
21
+ mapping(address sender => bytes context) senderContext; // use transient storage eventually
22
+ }
23
+
24
+ // keccak256(abi.encode(uint256(keccak256("policy-management.PolicyProtected")) - 1)) &
25
+ // ~bytes32(uint256(0xff))
26
+ // solhint-disable-next-line const-name-snakecase
27
+ bytes32 private constant policyProtectedStorageLocation =
28
+ 0x381e6510830aa5d1f847c166134370760011d6c9becccc73371e64e18c3c4f00;
29
+
30
+ function _policyProtectedStorage() private pure returns (PolicyProtectedStorage storage $) {
31
+ // solhint-disable-next-line no-inline-assembly
32
+ assembly {
33
+ $.slot := policyProtectedStorageLocation
34
+ }
35
+ }
36
+
37
+ constructor() {
38
+ _disableInitializers();
39
+ }
40
+
41
+ function __PolicyProtected_init(address initialOwner, address policyEngine) internal onlyInitializing {
42
+ __Ownable_init(initialOwner);
43
+ __ERC165_init();
44
+ __PolicyProtected_init_unchained(policyEngine);
45
+ }
46
+
47
+ function __PolicyProtected_init_unchained(address policyEngine) internal onlyInitializing {
48
+ _attachPolicyEngine(policyEngine);
49
+ }
50
+ /**
51
+ * @dev Modifier to run the policy engine on the current method.
52
+ * @notice After the function execution completes, any context that was set will be automatically cleared.
53
+ */
54
+
55
+ modifier runPolicy() {
56
+ if (address(_policyProtectedStorage().policyEngine) == address(0)) {
57
+ revert IPolicyEngine.PolicyEngineUndefined();
58
+ }
59
+ bytes memory context = getContext();
60
+ _policyProtectedStorage().policyEngine.run(
61
+ IPolicyEngine.Payload({selector: msg.sig, sender: msg.sender, data: msg.data[4:], context: context})
62
+ );
63
+ _;
64
+ if (context.length > 0) {
65
+ clearContext();
66
+ }
67
+ }
68
+
69
+ /**
70
+ * @dev Modifier to run the policy engine on the current method with the provided context.
71
+ * @param context Additional information or authorization to perform the operation.
72
+ */
73
+ modifier runPolicyWithContext(bytes calldata context) {
74
+ if (address(_policyProtectedStorage().policyEngine) == address(0)) {
75
+ revert IPolicyEngine.PolicyEngineUndefined();
76
+ }
77
+ _policyProtectedStorage().policyEngine.run(
78
+ IPolicyEngine.Payload({selector: msg.sig, sender: msg.sender, data: msg.data[4:], context: context})
79
+ );
80
+ _;
81
+ }
82
+
83
+ /// @inheritdoc IPolicyProtected
84
+ function attachPolicyEngine(address policyEngine) external virtual override onlyOwner {
85
+ _attachPolicyEngine(policyEngine);
86
+ }
87
+
88
+ function _attachPolicyEngine(address policyEngine) internal {
89
+ _policyProtectedStorage().policyEngine = IPolicyEngine(policyEngine);
90
+ IPolicyEngine(policyEngine).attach();
91
+ emit PolicyEngineAttached(policyEngine);
92
+ }
93
+
94
+ /// @inheritdoc IPolicyProtected
95
+ function getPolicyEngine() public view virtual override returns (address) {
96
+ return address(_policyProtectedStorage().policyEngine);
97
+ }
98
+
99
+ /// @inheritdoc IPolicyProtected
100
+ function setContext(bytes calldata context) public override {
101
+ _policyProtectedStorage().senderContext[msg.sender] = context;
102
+ }
103
+
104
+ /// @inheritdoc IPolicyProtected
105
+ function getContext() public view override returns (bytes memory) {
106
+ return _policyProtectedStorage().senderContext[msg.sender];
107
+ }
108
+
109
+ /// @inheritdoc IPolicyProtected
110
+ function clearContext() public override {
111
+ delete _policyProtectedStorage().senderContext[msg.sender];
112
+ }
113
+
114
+ /**
115
+ * @dev See {IERC165-supportsInterface}.
116
+ */
117
+ function supportsInterface(bytes4 interfaceId)
118
+ public
119
+ view
120
+ virtual
121
+ override(ERC165Upgradeable, IERC165)
122
+ returns (bool)
123
+ {
124
+ return interfaceId == type(IPolicyProtected).interfaceId || super.supportsInterface(interfaceId);
125
+ }
126
+ }
@@ -0,0 +1,57 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {IExtractor} from "@chainlink/policy-management/interfaces/IExtractor.sol";
5
+ import {IPolicyEngine} from "@chainlink/policy-management/interfaces/IPolicyEngine.sol";
6
+ import {ComplianceTokenERC20} from "../../../tokens/erc-20/src/ComplianceTokenERC20.sol";
7
+
8
+ /**
9
+ * @title ComplianceTokenForceTransferExtractor
10
+ * @notice Extracts parameters from Compliance Token ERC20 forced transfer function calls.
11
+ * @dev This extractor supports the forceTransfer() function selector from the ComplianceTokenERC20 contract
12
+ * and extracts the from address, to address, and amount parameters. Force transfers allow
13
+ * authorized agents to move tokens between addresses without approval for compliance purposes.
14
+ */
15
+ contract ComplianceTokenForceTransferExtractor is IExtractor {
16
+ /// @notice Parameter key for the sender/from address in forced transfer operations
17
+ bytes32 public constant PARAM_FROM = keccak256("from");
18
+
19
+ /// @notice Parameter key for the recipient/to address in forced transfer operations
20
+ bytes32 public constant PARAM_TO = keccak256("to");
21
+
22
+ /// @notice Parameter key for the amount being forcefully transferred
23
+ bytes32 public constant PARAM_AMOUNT = keccak256("amount");
24
+
25
+ /**
26
+ * @inheritdoc IExtractor
27
+ * @dev Extracts parameters from ComplianceTokenERC20 forceTransfer(address from, address to, uint256 amount)
28
+ * function calls.
29
+ * @param payload The policy engine payload containing the function selector and calldata
30
+ * @return An array of three parameters: PARAM_FROM, PARAM_TO, and PARAM_AMOUNT
31
+ */
32
+ function extract(IPolicyEngine.Payload calldata payload)
33
+ public
34
+ pure
35
+ virtual
36
+ returns (IPolicyEngine.Parameter[] memory)
37
+ {
38
+ address from = address(0);
39
+ address to = address(0);
40
+ uint256 amount = 0;
41
+
42
+ // Handle forceTransfer(address from, address to, uint256 amount)
43
+ if (payload.selector == ComplianceTokenERC20.forceTransfer.selector) {
44
+ (from, to, amount) = abi.decode(payload.data, (address, address, uint256));
45
+ } else {
46
+ revert IPolicyEngine.UnsupportedSelector(payload.selector);
47
+ }
48
+
49
+ // Build the parameter array with extracted values
50
+ IPolicyEngine.Parameter[] memory result = new IPolicyEngine.Parameter[](3);
51
+ result[0] = IPolicyEngine.Parameter(PARAM_FROM, abi.encode(from));
52
+ result[1] = IPolicyEngine.Parameter(PARAM_TO, abi.encode(to));
53
+ result[2] = IPolicyEngine.Parameter(PARAM_AMOUNT, abi.encode(amount));
54
+
55
+ return result;
56
+ }
57
+ }