@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,123 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {ITrustedIssuerRegistry} from "../src/interfaces/ITrustedIssuerRegistry.sol";
5
+ import {IPolicyEngine} from "@chainlink/policy-management/interfaces/IPolicyEngine.sol";
6
+ import {TrustedIssuerRegistry} from "../src/TrustedIssuerRegistry.sol";
7
+ import {PolicyEngine} from "@chainlink/policy-management/core/PolicyEngine.sol";
8
+ import {BaseProxyTest} from "./helpers/BaseProxyTest.sol";
9
+
10
+ contract TrustedIssuerRegistryTest is BaseProxyTest {
11
+ PolicyEngine internal s_policyEngine;
12
+ TrustedIssuerRegistry internal s_trustedIssuerRegistry;
13
+ address internal s_owner;
14
+
15
+ function setUp() public {
16
+ s_owner = makeAddr("owner");
17
+
18
+ vm.startPrank(s_owner);
19
+ s_policyEngine = _deployPolicyEngine(true, address(this));
20
+ s_trustedIssuerRegistry = _deployTrustedIssuerRegistry(address(s_policyEngine));
21
+ vm.stopPrank();
22
+ }
23
+
24
+ // -------------------------
25
+ // Add trusted issuer
26
+ // -------------------------
27
+
28
+ function test_addTrustedIssuer_success() public {
29
+ string memory issuerId = "did:example:issuer1";
30
+ bytes32 didHash = keccak256(abi.encodePacked(issuerId));
31
+
32
+ s_trustedIssuerRegistry.addTrustedIssuer(issuerId, "");
33
+
34
+ bool isTrusted = s_trustedIssuerRegistry.isTrustedIssuer(issuerId);
35
+ assertTrue(isTrusted);
36
+
37
+ bytes32[] memory issuers = s_trustedIssuerRegistry.getTrustedIssuers();
38
+ assertEq(issuers.length, 1);
39
+ assertEq(issuers[0], didHash);
40
+ }
41
+
42
+ function test_addTrustedIssuer_emptyDid_failure() public {
43
+ vm.expectRevert("issuerId cannot be empty");
44
+ s_trustedIssuerRegistry.addTrustedIssuer("", "");
45
+ }
46
+
47
+ function test_addTrustedIssuer_duplicate_failure() public {
48
+ string memory issuerId = "did:example:issuer1";
49
+
50
+ s_trustedIssuerRegistry.addTrustedIssuer(issuerId, "");
51
+
52
+ vm.expectRevert("Issuer already trusted");
53
+ s_trustedIssuerRegistry.addTrustedIssuer(issuerId, "");
54
+ }
55
+
56
+ // -------------------------
57
+ // Remove trusted issuer
58
+ // -------------------------
59
+
60
+ function test_removeTrustedIssuer_success() public {
61
+ string memory issuerId = "did:example:issuer1";
62
+
63
+ s_trustedIssuerRegistry.addTrustedIssuer(issuerId, "");
64
+ assertTrue(s_trustedIssuerRegistry.isTrustedIssuer(issuerId));
65
+
66
+ s_trustedIssuerRegistry.removeTrustedIssuer(issuerId, "");
67
+
68
+ assertFalse(s_trustedIssuerRegistry.isTrustedIssuer(issuerId));
69
+
70
+ bytes32[] memory issuers = s_trustedIssuerRegistry.getTrustedIssuers();
71
+ assertEq(issuers.length, 0);
72
+ }
73
+
74
+ function test_removeTrustedIssuer_notTrusted_failure() public {
75
+ string memory issuerId = "did:example:nonexistent";
76
+
77
+ vm.expectRevert("Issuer not trusted");
78
+ s_trustedIssuerRegistry.removeTrustedIssuer(issuerId, "");
79
+ }
80
+
81
+ // -------------------------
82
+ // List management behavior
83
+ // -------------------------
84
+
85
+ function test_multipleIssuers_addAndRemove_listUpdatedCorrectly() public {
86
+ string memory did1 = "did:example:issuer1";
87
+ string memory did2 = "did:example:issuer2";
88
+ string memory did3 = "did:example:issuer3";
89
+
90
+ s_trustedIssuerRegistry.addTrustedIssuer(did1, "");
91
+ s_trustedIssuerRegistry.addTrustedIssuer(did2, "");
92
+ s_trustedIssuerRegistry.addTrustedIssuer(did3, "");
93
+
94
+ bytes32[] memory beforeRemoval = s_trustedIssuerRegistry.getTrustedIssuers();
95
+ assertEq(beforeRemoval.length, 3);
96
+
97
+ s_trustedIssuerRegistry.removeTrustedIssuer(did2, "");
98
+
99
+ bytes32[] memory afterRemoval = s_trustedIssuerRegistry.getTrustedIssuers();
100
+ assertEq(afterRemoval.length, 2);
101
+
102
+ bytes32 hash1 = keccak256(abi.encodePacked(did1));
103
+ bytes32 hash3 = keccak256(abi.encodePacked(did3));
104
+
105
+ assertTrue(afterRemoval[0] == hash1 || afterRemoval[1] == hash1);
106
+ assertTrue(afterRemoval[0] == hash3 || afterRemoval[1] == hash3);
107
+ }
108
+
109
+ // -------------------------
110
+ // View functions
111
+ // -------------------------
112
+
113
+ function test_isTrustedIssuer_returnsFalseWhenNotAdded() public {
114
+ string memory issuerId = "did:example:nonexistent";
115
+ bool isTrusted = s_trustedIssuerRegistry.isTrustedIssuer(issuerId);
116
+ assertFalse(isTrusted);
117
+ }
118
+
119
+ function test_getTrustedIssuers_emptyInitially() public {
120
+ bytes32[] memory issuers = s_trustedIssuerRegistry.getTrustedIssuers();
121
+ assertEq(issuers.length, 0);
122
+ }
123
+ }
@@ -0,0 +1,112 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {Test} from "forge-std/Test.sol";
5
+ import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
6
+ import {IPolicyEngine} from "@chainlink/policy-management/interfaces/IPolicyEngine.sol";
7
+ import {PolicyEngine} from "@chainlink/policy-management/core/PolicyEngine.sol";
8
+ import {Policy} from "@chainlink/policy-management/core/Policy.sol";
9
+ import {IdentityRegistry} from "../../src/IdentityRegistry.sol";
10
+ import {CredentialRegistry} from "../../src/CredentialRegistry.sol";
11
+ import {CredentialRegistryIdentityValidator} from "../../src/CredentialRegistryIdentityValidator.sol";
12
+ import {CredentialRegistryIdentityValidatorPolicy} from "../../src/CredentialRegistryIdentityValidatorPolicy.sol";
13
+ import {ICredentialRequirements} from "../../src/interfaces/ICredentialRequirements.sol";
14
+ import {TrustedIssuerRegistry} from "../../src/TrustedIssuerRegistry.sol";
15
+ /**
16
+ * @title BaseProxyTest
17
+ * @notice Base contract for tests that need to deploy upgradeable contracts through proxies
18
+ * @dev Provides helper functions to deploy common contracts with proper proxy pattern
19
+ */
20
+
21
+ abstract contract BaseProxyTest is Test {
22
+ /**
23
+ * @notice Deploy PolicyEngine through proxy
24
+ * @param defaultAllow Whether the default policy engine rule will allow or reject the transaction
25
+ * @param initialOwner The address of the initial owner of the policy engine
26
+ * @return The deployed PolicyEngine proxy instance
27
+ */
28
+ function _deployPolicyEngine(bool defaultAllow, address initialOwner) internal returns (PolicyEngine) {
29
+ PolicyEngine policyEngineImpl = new PolicyEngine();
30
+ bytes memory policyEngineData = abi.encodeWithSelector(PolicyEngine.initialize.selector, defaultAllow, initialOwner);
31
+ ERC1967Proxy policyEngineProxy = new ERC1967Proxy(address(policyEngineImpl), policyEngineData);
32
+ return PolicyEngine(address(policyEngineProxy));
33
+ }
34
+
35
+ /**
36
+ * @notice Deploy IdentityRegistry through proxy
37
+ * @param policyEngine The address of the policy engine contract
38
+ * @return The deployed IdentityRegistry proxy instance
39
+ */
40
+ function _deployIdentityRegistry(address policyEngine) internal returns (IdentityRegistry) {
41
+ IdentityRegistry identityRegistryImpl = new IdentityRegistry();
42
+ bytes memory identityRegistryData =
43
+ abi.encodeWithSelector(IdentityRegistry.initialize.selector, policyEngine, address(this));
44
+ ERC1967Proxy identityRegistryProxy = new ERC1967Proxy(address(identityRegistryImpl), identityRegistryData);
45
+ return IdentityRegistry(address(identityRegistryProxy));
46
+ }
47
+
48
+ /**
49
+ * @notice Deploy CredentialRegistry through proxy
50
+ * @param policyEngine The address of the policy engine contract
51
+ * @return The deployed CredentialRegistry proxy instance
52
+ */
53
+ function _deployCredentialRegistry(address policyEngine) internal returns (CredentialRegistry) {
54
+ CredentialRegistry credentialRegistryImpl = new CredentialRegistry();
55
+ bytes memory credentialRegistryData =
56
+ abi.encodeWithSelector(CredentialRegistry.initialize.selector, policyEngine, address(this));
57
+ ERC1967Proxy credentialRegistryProxy = new ERC1967Proxy(address(credentialRegistryImpl), credentialRegistryData);
58
+ return CredentialRegistry(address(credentialRegistryProxy));
59
+ }
60
+
61
+ function _deployTrustedIssuerRegistry(address policyEngine) internal returns (TrustedIssuerRegistry) {
62
+ TrustedIssuerRegistry impl = new TrustedIssuerRegistry();
63
+ bytes memory data = abi.encodeWithSelector(TrustedIssuerRegistry.initialize.selector, policyEngine, address(this));
64
+ ERC1967Proxy proxy = new ERC1967Proxy(address(impl), data);
65
+ return TrustedIssuerRegistry(address(proxy));
66
+ }
67
+
68
+ /**
69
+ * @notice Deploy IdentityValidator through proxy
70
+ * @param credentialSources Initial credential sources
71
+ * @param credentialRequirements Initial credential requirements
72
+ * @return The deployed IdentityValidator proxy instance
73
+ */
74
+ function _deployCredentialRegistryIdentityValidator(
75
+ ICredentialRequirements.CredentialSourceInput[] memory credentialSources,
76
+ ICredentialRequirements.CredentialRequirementInput[] memory credentialRequirements
77
+ )
78
+ internal
79
+ returns (CredentialRegistryIdentityValidator)
80
+ {
81
+ CredentialRegistryIdentityValidator identityValidatorImpl = new CredentialRegistryIdentityValidator();
82
+ bytes memory identityValidatorData = abi.encodeWithSelector(
83
+ CredentialRegistryIdentityValidator.initialize.selector, credentialSources, credentialRequirements
84
+ );
85
+ ERC1967Proxy identityValidatorProxy = new ERC1967Proxy(address(identityValidatorImpl), identityValidatorData);
86
+ return CredentialRegistryIdentityValidator(address(identityValidatorProxy));
87
+ }
88
+
89
+ /**
90
+ * @notice Deploy CredentialRegistryIdentityValidatorPolicy through proxy
91
+ * @param policyEngine The address of the policy engine contract
92
+ * @param owner The address of the policy owner
93
+ * @param parameters ABI-encoded parameters for policy initialization
94
+ * @return The deployed CredentialRegistryIdentityValidatorPolicy proxy instance
95
+ */
96
+ function _deployCredentialRegistryCredentialRegistryIdentityValidatorPolicy(
97
+ address policyEngine,
98
+ address owner,
99
+ bytes memory parameters
100
+ )
101
+ internal
102
+ returns (CredentialRegistryIdentityValidatorPolicy)
103
+ {
104
+ CredentialRegistryIdentityValidatorPolicy identityValidatorPolicyImpl =
105
+ new CredentialRegistryIdentityValidatorPolicy();
106
+ bytes memory identityValidatorPolicyData =
107
+ abi.encodeWithSelector(Policy.initialize.selector, policyEngine, owner, parameters);
108
+ ERC1967Proxy identityValidatorPolicyProxy =
109
+ new ERC1967Proxy(address(identityValidatorPolicyImpl), identityValidatorPolicyData);
110
+ return CredentialRegistryIdentityValidatorPolicy(address(identityValidatorPolicyProxy));
111
+ }
112
+ }
@@ -0,0 +1,26 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {ICredentialDataValidator} from "../../src/interfaces/ICredentialDataValidator.sol";
5
+
6
+ contract MockCredentialDataValidator is ICredentialDataValidator {
7
+ bool private s_dataValid;
8
+
9
+ function setDataValid(bool dataValid) public {
10
+ s_dataValid = dataValid;
11
+ }
12
+
13
+ function validateCredentialData(
14
+ bytes32, /*ccid*/
15
+ address, /*account*/
16
+ bytes32, /*credentialTypeId*/
17
+ bytes calldata, /*credentialData*/
18
+ bytes calldata /*context*/
19
+ )
20
+ external
21
+ view
22
+ returns (bool)
23
+ {
24
+ return s_dataValid;
25
+ }
26
+ }
@@ -0,0 +1,131 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {ICredentialRegistry} from "../../src/interfaces/ICredentialRegistry.sol";
5
+
6
+ /**
7
+ * @title MockCredentialRegistryReverting
8
+ * @notice Mock credential registry that can be configured to revert on validate() calls
9
+ * @dev Used for testing error handling in CredentialRegistryIdentityValidator
10
+ */
11
+ contract MockCredentialRegistryReverting is ICredentialRegistry {
12
+ bool private s_shouldRevert;
13
+ string private s_revertMessage;
14
+
15
+ function setShouldRevert(bool shouldRevert) public {
16
+ s_shouldRevert = shouldRevert;
17
+ }
18
+
19
+ function setRevertMessage(string memory message) public {
20
+ s_revertMessage = message;
21
+ }
22
+
23
+ function validate(
24
+ bytes32, /*ccid*/
25
+ bytes32, /*credentialTypeId*/
26
+ bytes calldata /*context*/
27
+ )
28
+ external
29
+ view
30
+ returns (bool)
31
+ {
32
+ if (s_shouldRevert) {
33
+ revert(s_revertMessage);
34
+ }
35
+ return false; // Default behavior when not reverting
36
+ }
37
+
38
+ // Minimal implementation of required interface methods
39
+ function registerCredential(
40
+ bytes32, /*ccid*/
41
+ bytes32, /*credentialTypeId*/
42
+ uint40, /*expiresAt*/
43
+ bytes calldata, /*credentialData*/
44
+ bytes calldata /*context*/
45
+ )
46
+ external
47
+ {
48
+ revert("Not implemented");
49
+ }
50
+
51
+ function registerCredentials(
52
+ bytes32, /*ccid*/
53
+ bytes32[] calldata, /*credentialTypeIds*/
54
+ uint40, /*expiresAt*/
55
+ bytes[] calldata, /*credentialDatas*/
56
+ bytes calldata /*context*/
57
+ )
58
+ external
59
+ {
60
+ revert("Not implemented");
61
+ }
62
+
63
+ function renewCredential(
64
+ bytes32, /*ccid*/
65
+ bytes32, /*credentialTypeId*/
66
+ uint40, /*expiresAt*/
67
+ bytes calldata /*context*/
68
+ )
69
+ external
70
+ {
71
+ revert("Not implemented");
72
+ }
73
+
74
+ function revokeCredential(bytes32, /*ccid*/ bytes32, /*credentialTypeId*/ bytes calldata /*context*/ ) external {
75
+ revert("Not implemented");
76
+ }
77
+
78
+ function removeCredential(bytes32, /*ccid*/ bytes32, /*credentialTypeId*/ bytes calldata /*context*/ ) external {
79
+ revert("Not implemented");
80
+ }
81
+
82
+ function getCredential(
83
+ bytes32, /*ccid*/
84
+ bytes32 /*credentialTypeId*/
85
+ )
86
+ external
87
+ view
88
+ returns (ICredentialRegistry.Credential memory)
89
+ {
90
+ revert("Not implemented");
91
+ }
92
+
93
+ function getCredentials(
94
+ bytes32, /*ccid*/
95
+ bytes32[] calldata /*credentialTypeIds*/
96
+ )
97
+ external
98
+ view
99
+ returns (ICredentialRegistry.Credential[] memory)
100
+ {
101
+ revert("Not implemented");
102
+ }
103
+
104
+ function getCredentialTypes(bytes32 /*ccid*/ ) external view returns (bytes32[] memory) {
105
+ revert("Not implemented");
106
+ }
107
+
108
+ function hasCredential(bytes32, /*ccid*/ bytes32 /*credentialTypeId*/ ) external view returns (bool) {
109
+ revert("Not implemented");
110
+ }
111
+
112
+ function isCredentialExpired(bytes32, /*ccid*/ bytes32 /*credentialTypeId*/ ) external view returns (bool) {
113
+ revert("Not implemented");
114
+ }
115
+
116
+ function validateAll(
117
+ bytes32, /*ccid*/
118
+ bytes32[] calldata, /*credentialTypeIds*/
119
+ bytes calldata /*context*/
120
+ )
121
+ external
122
+ view
123
+ returns (bool)
124
+ {
125
+ revert("Not implemented");
126
+ }
127
+
128
+ function supportsInterface(bytes4 /*interfaceId*/ ) external pure returns (bool) {
129
+ return true;
130
+ }
131
+ }
@@ -0,0 +1,197 @@
1
+ # Policy Management
2
+
3
+ **A dynamic engine to create and enforce onchain rules. Update compliance logic without redeploying your core contracts.**
4
+
5
+ ## Why This Matters
6
+
7
+ Hardcoding enforcement logic makes your contracts rigid and difficult to audit. With the Policy Management component, you can separate your application's business logic from its compliance rules, allowing you to adapt to new regulations or security practices on the fly.
8
+
9
+ A contract might start with simple rules, but as it evolves, more complex logic gets added directly into the function, making it brittle and hard to maintain.
10
+
11
+ ```solidity
12
+ // ❌ Before: All rules are hard-coded, making the function complex and inflexible.
13
+ contract MyToken {
14
+ address public owner;
15
+ bool public paused;
16
+
17
+ function mint(uint256 amount) public {
18
+ require(!paused, "Contract is paused");
19
+ require(msg.sender == owner, "Only owner can mint");
20
+ // ...
21
+ }
22
+ }
23
+
24
+ // ✅ After: The function is only concerned with its core logic.
25
+ // All rules (pausable, owner-only, volume limits, etc.) are now separate,
26
+ // composable policies attached to the Policy Engine.
27
+ import { PolicyProtected } from "@chainlink/policy-management/core/PolicyProtected.sol";
28
+
29
+ contract MyToken is PolicyProtected {
30
+ function mint(uint256 amount) public runPolicy {
31
+ // ...
32
+ }
33
+ }
34
+ ```
35
+
36
+ ## How It Works: An Overview
37
+
38
+ Policy Management is built on a few core components that work together to protect your contracts:
39
+
40
+ - **`PolicyProtected`**: An abstract contract you inherit from. It provides the `runPolicy` modifier that acts as the "hook" into the policy system and includes an optional, advanced **[`context`](docs/CONCEPTS.md#the-context-parameter-a-flexible-data-channel)** for passing arbitrary data like off-chain signatures.
41
+ - **`PolicyEngine`**: The central orchestrator. It holds the registry of all policies and executes them in the correct order for any protected function.
42
+ - **`Policy`**: A self-contained contract that holds a single rule. It uses a `run()` function for read-only checks and has an optional [`postRun()`](docs/CONCEPTS.md#policy-flow-diagram) function that can modify state _after_ a check has passed.
43
+ - **`Extractor`**: A helper contract that parses transaction data for the policies. For most common use cases, the `PolicyEngine` handles the underlying parameter mapping automatically. You can learn more about the [Extractor and Mapper pattern in our Concepts guide](docs/CONCEPTS.md#the-extractor-and-mapper-pattern).
44
+
45
+ A `Policy` contract's `run()` function can either return a result or revert to reject the transaction:
46
+
47
+ - **`revert IPolicyEngine.PolicyRejected(reason)`**: Immediately blocks the transaction with a descriptive error message. This is a final "no" that bypasses all subsequent policies and provides clear feedback about why the transaction was rejected.
48
+ - **`Allowed`**: Immediately approves the transaction and also bypasses all subsequent policies in the chain. This is a final "yes".
49
+ - **`Continue`**: The check passed, but the decision is deferred. The engine proceeds to the next policy in the chain, or applies its default result if none remain.
50
+
51
+ This diagram illustrates the primary flow: a dApp calls a protected function, and the `PolicyEngine` intercepts the call to check the relevant policies before allowing execution to proceed.
52
+
53
+ ```mermaid
54
+ sequenceDiagram
55
+ participant User
56
+ participant PolicyProtectedContract as Policy-Protected Contract
57
+ participant PolicyEngine as Policy Engine
58
+ participant Policy1 as Policy
59
+
60
+ User->>PolicyProtectedContract: 1. Calls a protected function()
61
+ PolicyProtectedContract->>PolicyEngine: 2. Asks "Is this call allowed?"
62
+ Note over PolicyProtectedContract: The `runPolicy` modifier triggers this.
63
+
64
+ PolicyEngine->>Policy1: 3. run()
65
+
66
+ alt Policy Rejects
67
+ Policy1-->>PolicyEngine: 4a. revert PolicyRejected(reason)
68
+ Note over PolicyEngine: Skips any subsequent policies
69
+ PolicyEngine-->>PolicyProtectedContract: 5a. REVERT with reason
70
+ else Policy Allows
71
+ Policy1-->>PolicyEngine: 4b. Returns `Allowed`
72
+ Note over PolicyEngine: Skips any subsequent policies
73
+ PolicyEngine-->>PolicyProtectedContract: 5b. SUCCESS
74
+ else Policy Continues
75
+ Policy1-->>PolicyEngine: 4c. Returns `Continue`
76
+ Note over PolicyEngine: Proceeds to next policy (if any)<br/>or applies engine's default result.
77
+ PolicyEngine-->>PolicyProtectedContract: 5c. Allows or Reverts (default result)
78
+ end
79
+ ```
80
+
81
+ **Important note**: The `PolicyEngine` executes policies **in the order they are added**. For a detailed guide on how to add, remove, and reorder policies, see the **[Policy Ordering Guide](docs/POLICY_ORDERING_GUIDE.md)**. For a more detailed breakdown of the execution flow, see the [Policy Flow Diagram in our Concepts guide](docs/CONCEPTS.md#policy-flow-diagram).
82
+
83
+ **Key Benefits**:
84
+
85
+ - **Future-Proof**: Adapt to new regulations or security needs by adding, removing, or updating policies without changing your core contracts.
86
+ - **Composable & Reusable**: Chain multiple policies together to create sophisticated rulesets. Reuse the same policies across different contracts and functions.
87
+ - **Audit Friendly**: Isolate complex compliance logic into individual policy contracts, making them easier to audit and reason about.
88
+ - **Ready-to-Use Library**: Leverage a suite of pre-built, audited policies for common use cases like authorization, volume limits, and more.
89
+
90
+ ## How to Use
91
+
92
+ The following example shows how to protect a `pause` function so that only a designated Security Administrator can call it. This demonstrates a clear separation of roles, where one address owns the main contract but a different, specialized address is given a specific permission.
93
+
94
+ ### 1. Inherit from `PolicyProtected`
95
+
96
+ First, make your contract inherit from `PolicyProtected` and add the `runPolicy` modifier to the function you want to secure.
97
+
98
+ ```solidity
99
+ import { PolicyProtected } from "@chainlink/policy-management/core/PolicyProtected.sol";
100
+
101
+ contract MyPausableContract is PolicyProtected {
102
+ bool public paused;
103
+
104
+ function pause() public virtual runPolicy {
105
+ paused = true;
106
+ }
107
+ }
108
+ ```
109
+
110
+ ### 2. Deploy and Connect the Components
111
+
112
+ > **Note:** All Policy Management components must be deployed behind a proxy because they use OpenZeppelin's upgradeable contracts pattern (disabled constructors with initializers). This typically uses `ERC1967Proxy` for upgradeability, though minimal proxies (clones) may be used for immutable components. See the **[Getting Started Guide: Deployment Script](../../getting_started/GETTING_STARTED.md#the-deployment-script)** for the complete proxy deployment pattern.
113
+
114
+ Once your components are deployed, here are the key configuration steps:
115
+
116
+ ```solidity
117
+ // 1. Authorize who can trigger the policy
118
+ onlyAuthorizedSenderPolicy.authorizeSender(securityAdmin);
119
+
120
+ // 2. Register the policy with the engine
121
+ policyEngine.addPolicy(
122
+ address(myContract),
123
+ myContract.pause.selector,
124
+ address(onlyAuthorizedSenderPolicy),
125
+ new bytes32[](0)
126
+ );
127
+ ```
128
+
129
+ These two calls establish the relationship: the `OnlyAuthorizedSenderPolicy` is now enforced on `myContract.pause()`, and only `securityAdmin` can satisfy it. The `contractOwner` cannot call it directly, demonstrating a clear and powerful separation of roles that is difficult to achieve with simple ownership patterns.
130
+
131
+ This design also makes the contract adaptable to future requirements. For instance, if a new rule dictates that the `pause` function can only be called after a majority of partners have submitted offchain approvals, you wouldn't need to change `MyPausableContract`. You could simply deploy a new `PartnerApprovalPolicy`, and add it to the policy chain for the `pause` selector in the `PolicyEngine`. The `runPolicy` modifier ensures both the original `onlyAuthorizedSenderPolicy` and the new approval policy are checked automatically, without any changes to the core contract.
132
+
133
+ ## Working with Policies
134
+
135
+ The Policy Management component offers a flexible approach, allowing you to use our library of audited, pre-built policies or to create your own for custom logic.
136
+
137
+ ### Use Our Library of Pre-Built Policies
138
+
139
+ This component includes a rich library of pre-built, audited policies to accelerate development. These policies cover a wide range of common compliance and access control scenarios.
140
+
141
+ - **Access Control**: `AllowPolicy`, `BypassPolicy`, `RejectPolicy`, `OnlyOwnerPolicy`, `OnlyAuthorizedSenderPolicy`, `RoleBasedAccessControlPolicy`.
142
+ - **Transaction Limits**: `MaxPolicy`, `VolumePolicy`, `VolumeRatePolicy`, `SecureMintPolicy`.
143
+ - **Time-Based Rules**: `IntervalPolicy`, `PausePolicy`.
144
+
145
+ → **[Explore the Full List and Configuration Guides](./src/policies/README.md)**
146
+
147
+ ### Build Your Own Custom Policies
148
+
149
+ The true power of the engine comes from its extensibility. You can easily write your own custom policies to enforce any onchain rule imaginable.
150
+
151
+ - **[Read the Tutorial →](docs/CUSTOM_POLICIES_TUTORIAL.md)** A step-by-step guide to building a policy from scratch.
152
+ - **[Get the Boilerplate →](docs/CUSTOM_POLICIES_TUTORIAL.md#boilerplate-template-for-custom-policies)** A clean, commented template to use as a starting point.
153
+
154
+ ## Integration with Other Components
155
+
156
+ Policy Management is the engine that drives the functionality of the other Chainlink ACE core contracts, allowing you to create powerful, cross-component rules.
157
+
158
+ ### With Cross-Chain Identity
159
+
160
+ Use identity verification as a dynamic policy condition. The `CredentialRegistryIdentityValidatorPolicy` is a powerful example that checks a user's onchain credentials before allowing an action.
161
+
162
+ ```solidity
163
+ // In your deployment script:
164
+ CredentialRegistryIdentityValidatorPolicy kycPolicy = new CredentialRegistryIdentityValidatorPolicy();
165
+ // ... initialize kycPolicy with your identity validator ...
166
+
167
+ // Apply the KYC check to the 'transfer' function
168
+ policyEngine.addPolicy(
169
+ address(myToken),
170
+ myToken.transfer.selector,
171
+ address(kycPolicy),
172
+ // ... specify parameters ...
173
+ );
174
+ ```
175
+
176
+ Learn more about [Cross-Chain Identity](../cross-chain-identity/README.md).
177
+
178
+ ## Security Considerations
179
+
180
+ The Policy Management component is incredibly powerful, but its security depends on correct configuration and management.
181
+
182
+ - **Policy Management is Critical**: Only authorized roles MUST be able to add, remove, or reorder policies in the `PolicyEngine`. Malicious updates could disable or circumvent all compliance rules.
183
+ - **Policy Order Matters**: Policies are executed in the order they are added. A policy that returns `Allowed` will bypass all subsequent policies. Ensure that high-priority checks and restrictive policies are ordered first.
184
+ - **Trust in Policies**: A malicious or poorly written policy can introduce vulnerabilities. Only install trusted, audited policies. The `postRun` function, in particular, can modify state and must be treated with extreme caution.
185
+ - **External Call Risks**: Policies that make external calls (e.g., to data sources, identity registries, or oracles) can introduce risks like denial of service or gas exhaustion. Since most policy `run()` functions are `view` (read-only), traditional reentrancy attacks are not possible, but **policy administrators** must ensure that external contracts are trusted and reliable when composing policies dynamically.
186
+ - **Extractor/Mapper Trust**: The `PolicyEngine` relies on `Extractors` to correctly parse transaction data. If an extractor is compromised or misrepresents data, policies may make decisions based on false information, potentially leading to bypasses.
187
+
188
+ ## Next Steps
189
+
190
+ - **[Core Concepts](docs/CONCEPTS.md)**: A deep dive into the architecture and design rationale.
191
+ - **[Creating Custom Policies](docs/CUSTOM_POLICIES_TUTORIAL.md)**: A step-by-step guide to building your own policies.
192
+ - **[Get the Boilerplate](docs/CUSTOM_POLICIES_TUTORIAL.md#boilerplate-template-for-custom-policies)**: A clean, commented template to use as a starting point.
193
+ - **[Policy Ordering Guide](docs/POLICY_ORDERING_GUIDE.md)**: Learn how to add, remove, and reorder policies in the Policy Engine.
194
+ - **[API Guide](docs/API_GUIDE.md)**: A developer-focused guide for common tasks.
195
+ - **[API Reference](docs/API_REFERENCE.md)**: Complete interface specifications.
196
+ - **[Security Deep Dive](docs/SECURITY.md)**: A full checklist of security principles for a robust implementation.
197
+ - **[Implementation](./src/) & [Tests](./test/)**: Examine the reference implementation in `/src` and see it in action in the `/test` folder.