@chainlink/ace 0.5.0 → 1.0.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.
- package/.github/workflows/npm-publish.yml +28 -0
- package/README.md +9 -1
- package/UPGRADE_GUIDE.md +754 -0
- package/getting_started/GETTING_STARTED.md +6 -0
- package/getting_started/MyVault.sol +2 -2
- package/getting_started/advanced/SanctionsPolicy.sol +6 -2
- package/package.json +3 -3
- package/packages/cross-chain-identity/docs/API_REFERENCE.md +2 -0
- package/packages/cross-chain-identity/src/CredentialRegistry.sol +9 -7
- package/packages/cross-chain-identity/src/CredentialRegistryFactory.sol +96 -0
- package/packages/cross-chain-identity/src/CredentialRegistryIdentityValidator.sol +15 -4
- package/packages/cross-chain-identity/src/CredentialRegistryIdentityValidatorPolicy.sol +10 -7
- package/packages/cross-chain-identity/src/IdentityRegistry.sol +33 -17
- package/packages/cross-chain-identity/src/IdentityRegistryFactory.sol +96 -0
- package/packages/cross-chain-identity/src/TrustedIssuerRegistry.sol +8 -6
- package/packages/cross-chain-identity/src/TrustedIssuerRegistryFactory.sol +96 -0
- package/packages/cross-chain-identity/src/interfaces/ICredentialRegistry.sol +6 -0
- package/packages/cross-chain-identity/src/interfaces/ICredentialRequirements.sol +2 -1
- package/packages/cross-chain-identity/src/interfaces/IIdentityRegistry.sol +7 -1
- package/packages/cross-chain-identity/src/interfaces/ITrustedIssuerRegistry.sol +6 -0
- package/packages/cross-chain-identity/test/CredentialRegistry.t.sol +1 -1
- package/packages/cross-chain-identity/test/CredentialRegistryFactory.t.sol +105 -0
- package/packages/cross-chain-identity/test/CredentialRegistryIdentityValidator.t.sol +16 -1
- package/packages/cross-chain-identity/test/CredentialRegistryIdentityValidatorPolicy.t.sol +58 -1
- package/packages/cross-chain-identity/test/IdentityRegistry.t.sol +140 -1
- package/packages/cross-chain-identity/test/IdentityRegistryFactory.t.sol +103 -0
- package/packages/cross-chain-identity/test/IdentityValidator.t.sol +1 -1
- package/packages/cross-chain-identity/test/TrustedIssuerRegistry.t.sol +1 -1
- package/packages/cross-chain-identity/test/TrustedIssuerRegistryFactory.t.sol +107 -0
- package/packages/cross-chain-identity/test/helpers/BaseProxyTest.sol +1 -1
- package/packages/cross-chain-identity/test/helpers/MockCredentialDataValidator.sol +1 -1
- package/packages/cross-chain-identity/test/helpers/MockCredentialRegistryReverting.sol +3 -1
- package/packages/policy-management/README.md +1 -0
- package/packages/policy-management/docs/API_GUIDE.md +2 -0
- package/packages/policy-management/docs/API_REFERENCE.md +3 -1
- package/packages/policy-management/docs/CUSTOM_POLICIES_TUTORIAL.md +10 -4
- package/packages/policy-management/src/core/Policy.sol +5 -4
- package/packages/policy-management/src/core/PolicyEngine.sol +113 -57
- package/packages/policy-management/src/core/PolicyEngineFactory.sol +102 -0
- package/packages/policy-management/src/core/PolicyFactory.sol +1 -1
- package/packages/policy-management/src/core/PolicyProtected.sol +28 -55
- package/packages/policy-management/src/core/PolicyProtectedUpgradeable.sol +134 -0
- package/packages/policy-management/src/extractors/ComplianceTokenForceTransferExtractor.sol +4 -1
- package/packages/policy-management/src/extractors/ComplianceTokenFreezeUnfreezeExtractor.sol +4 -1
- package/packages/policy-management/src/extractors/ComplianceTokenMintBurnExtractor.sol +4 -1
- package/packages/policy-management/src/extractors/ERC20ApproveExtractor.sol +4 -1
- package/packages/policy-management/src/extractors/ERC20TransferExtractor.sol +4 -1
- package/packages/policy-management/src/extractors/ERC3643ForcedTransferExtractor.sol +4 -1
- package/packages/policy-management/src/extractors/ERC3643FreezeUnfreezeExtractor.sol +4 -1
- package/packages/policy-management/src/extractors/ERC3643MintBurnExtractor.sol +4 -1
- package/packages/policy-management/src/extractors/ERC3643SetAddressFrozenExtractor.sol +4 -1
- package/packages/policy-management/src/interfaces/ICertifiedActionValidator.sol +110 -0
- package/packages/policy-management/src/interfaces/IExtractor.sol +6 -0
- package/packages/policy-management/src/interfaces/IMapper.sol +6 -0
- package/packages/policy-management/src/interfaces/IPolicy.sol +6 -0
- package/packages/policy-management/src/interfaces/IPolicyEngine.sol +90 -10
- package/packages/policy-management/src/interfaces/IPolicyProtected.sol +6 -0
- package/packages/policy-management/src/libraries/CertifiedActionLib.sol +47 -0
- package/packages/policy-management/src/policies/AllowPolicy.sol +12 -7
- package/packages/policy-management/src/policies/BypassPolicy.sol +23 -5
- package/packages/policy-management/src/policies/CertifiedActionDONValidatorPolicy.sol +156 -0
- package/packages/policy-management/src/policies/CertifiedActionERC20TransferValidatorPolicy.sol +202 -0
- package/packages/policy-management/src/policies/CertifiedActionValidatorPolicy.sol +365 -0
- package/packages/policy-management/src/policies/IntervalPolicy.sol +64 -48
- package/packages/policy-management/src/policies/MaxPolicy.sol +7 -5
- package/packages/policy-management/src/policies/OnlyAuthorizedSenderPolicy.sol +20 -4
- package/packages/policy-management/src/policies/OnlyOwnerPolicy.sol +4 -2
- package/packages/policy-management/src/policies/PausePolicy.sol +16 -15
- package/packages/policy-management/src/policies/RejectPolicy.sol +23 -5
- package/packages/policy-management/src/policies/RoleBasedAccessControlPolicy.sol +7 -5
- package/packages/policy-management/src/policies/SecureMintPolicy.sol +136 -48
- package/packages/policy-management/src/policies/VolumePolicy.sol +9 -5
- package/packages/policy-management/src/policies/VolumeRatePolicy.sol +11 -7
- package/packages/policy-management/test/PolicyEngine.t.sol +131 -23
- package/packages/policy-management/test/PolicyEngineFactory.t.sol +95 -0
- package/packages/policy-management/test/PolicyFactory.t.sol +1 -1
- package/packages/policy-management/test/{PolicyProtectedToken.t.sol → PolicyProtected.t.sol} +54 -8
- package/packages/policy-management/test/PolicyProtectedUpgradeable.t.sol +126 -0
- package/packages/policy-management/test/extractors/ComplianceTokenForceTransferExtractor.t.sol +1 -1
- package/packages/policy-management/test/extractors/ComplianceTokenFreezeUnfreezeExtractor.t.sol +1 -1
- package/packages/policy-management/test/extractors/ComplianceTokenMintBurnExtractor.t.sol +1 -1
- package/packages/policy-management/test/extractors/ERC20ApproveExtractor.t.sol +1 -1
- package/packages/policy-management/test/extractors/ERC3643ForcedTransferExtractor.t.sol +1 -1
- package/packages/policy-management/test/extractors/ERC3643FreezeUnfreezeExtractor.t.sol +1 -1
- package/packages/policy-management/test/extractors/ERC3643MintBurnExtractor.t.sol +1 -1
- package/packages/policy-management/test/extractors/ERC3643SetAddressFrozenExtractor.t.sol +1 -1
- package/packages/policy-management/test/helpers/BaseCertifiedActionTest.sol +44 -0
- package/packages/policy-management/test/helpers/BaseProxyTest.sol +88 -7
- package/packages/policy-management/test/helpers/CustomMapper.sol +3 -1
- package/packages/policy-management/test/helpers/DummyExtractor.sol +3 -1
- package/packages/policy-management/test/helpers/ExpectedParameterPolicy.sol +3 -1
- package/packages/policy-management/test/helpers/FaultyPolicyEngine.sol +54 -0
- package/packages/policy-management/test/helpers/MockCertifiedActionValidatorPolicyExtension.sol +46 -0
- package/packages/policy-management/test/helpers/MockToken.sol +2 -5
- package/packages/policy-management/test/helpers/MockTokenExtractor.sol +9 -4
- package/packages/policy-management/test/helpers/MockTokenUpgradeable.sol +66 -0
- package/packages/policy-management/test/helpers/PolicyAlwaysAllowed.sol +3 -1
- package/packages/policy-management/test/helpers/PolicyAlwaysContinue.sol +3 -1
- package/packages/policy-management/test/helpers/PolicyAlwaysRejected.sol +10 -1
- package/packages/policy-management/test/helpers/PolicyFailingRun.sol +3 -1
- package/packages/policy-management/test/policies/AllowPolicy.t.sol +39 -23
- package/packages/policy-management/test/policies/BypassPolicy.t.sol +48 -20
- package/packages/policy-management/test/policies/CertifiedActionDONValidatorPolicy.t.sol +172 -0
- package/packages/policy-management/test/policies/CertifiedActionERC20TransferValidatorPolicy.t.sol +217 -0
- package/packages/policy-management/test/policies/CertifiedActionValidatorPolicy.t.sol +1083 -0
- package/packages/policy-management/test/policies/IntervalPolicy.t.sol +105 -55
- package/packages/policy-management/test/policies/MaxPolicy.t.sol +15 -7
- package/packages/policy-management/test/policies/OnlyAuthorizedSenderPolicy.t.sol +91 -16
- package/packages/policy-management/test/policies/OnlyOwnerPolicy.t.sol +11 -7
- package/packages/policy-management/test/policies/PausePolicy.t.sol +46 -16
- package/packages/policy-management/test/policies/RejectPolicy.t.sol +52 -24
- package/packages/policy-management/test/policies/RoleBasedAccessControlPolicy.t.sol +50 -30
- package/packages/policy-management/test/policies/SecureMintPolicy.t.sol +211 -32
- package/packages/policy-management/test/policies/VolumePolicy.t.sol +20 -10
- package/packages/policy-management/test/policies/VolumeRatePolicy.t.sol +24 -18
- package/packages/tokens/erc-20/src/ComplianceTokenERC20.sol +4 -7
- package/packages/tokens/erc-20/src/ComplianceTokenStoreERC20.sol +4 -4
- package/packages/tokens/erc-20/test/ComplianceTokenERC20.t.sol +92 -82
- package/packages/tokens/erc-20/test/helpers/BaseProxyTest.sol +74 -1
- package/packages/tokens/erc-3643/src/ComplianceTokenERC3643.sol +12 -7
- package/packages/tokens/erc-3643/src/ComplianceTokenStoreERC3643.sol +4 -4
- package/packages/tokens/erc-3643/test/ComplianceTokenERC3643.t.sol +108 -119
- package/packages/tokens/erc-3643/test/helpers/BaseProxyTest.sol +74 -1
- package/packages/tokens/erc-3643/test/helpers/ExpectedContextPolicy.sol +3 -1
- package/remappings.txt +1 -0
- package/script/DeployCertifiedActionsComplianceTokenERC3643.s.sol +125 -0
|
@@ -436,6 +436,12 @@ You now understand the core ACE integration pattern:
|
|
|
436
436
|
|
|
437
437
|
Choose your path based on what you want to build:
|
|
438
438
|
|
|
439
|
+
#### **Upgrade an existing contract**
|
|
440
|
+
|
|
441
|
+
Already have a deployed contract that needs ACE compliance? Follow our step-by-step upgrade guide:
|
|
442
|
+
|
|
443
|
+
- **[Upgrade Guide](../UPGRADE_GUIDE.md)** - Add ACE to existing deployed contracts without disrupting functionality
|
|
444
|
+
|
|
439
445
|
#### **Learn more about Policy Management**
|
|
440
446
|
|
|
441
447
|
You've seen one policy (`PausePolicy`) on two functions. Ready to level up?
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: BUSL-1.1
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {PolicyProtectedUpgradeable} from "@chainlink/policy-management/core/PolicyProtectedUpgradeable.sol";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @title MyVault
|
|
@@ -10,7 +10,7 @@ import {PolicyProtected} from "@chainlink/policy-management/core/PolicyProtected
|
|
|
10
10
|
* Both the `deposit` and `withdraw` functions are protected with the `runPolicy` modifier.
|
|
11
11
|
* This contract is designed to be deployed behind a proxy for upgradeability.
|
|
12
12
|
*/
|
|
13
|
-
contract MyVault is
|
|
13
|
+
contract MyVault is PolicyProtectedUpgradeable {
|
|
14
14
|
mapping(address => uint256) public deposits;
|
|
15
15
|
|
|
16
16
|
function initialize(address initialOwner, address policyEngine) public initializer {
|
|
@@ -6,7 +6,9 @@ import {IPolicyEngine} from "../../packages/policy-management/src/interfaces/IPo
|
|
|
6
6
|
import {SanctionsList} from "./SanctionsList.sol";
|
|
7
7
|
|
|
8
8
|
contract SanctionsPolicy is Policy {
|
|
9
|
-
|
|
9
|
+
string public constant override typeAndVersion = "SanctionsPolicy 1.0.0";
|
|
10
|
+
|
|
11
|
+
address public sanctionsList;
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* @notice Configures the policy with the sanctions list address.
|
|
@@ -42,7 +44,9 @@ contract SanctionsPolicy is Policy {
|
|
|
42
44
|
override
|
|
43
45
|
returns (IPolicyEngine.PolicyResult)
|
|
44
46
|
{
|
|
45
|
-
|
|
47
|
+
if (parameters.length != 1) {
|
|
48
|
+
revert InvalidParameters("SanctionsPolicy: Expected 1 parameter");
|
|
49
|
+
}
|
|
46
50
|
// This policy expects the "to" address as the first parameter
|
|
47
51
|
address recipient = abi.decode(parameters[0], (address));
|
|
48
52
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chainlink/ace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Chainlink Automated Compliance Engine (ACE) Contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chainlink",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"url": "https://github.com/smartcontractkit/chainlink-ace.git"
|
|
14
14
|
},
|
|
15
15
|
"license": "BUSL-1.1",
|
|
16
|
-
"author": "
|
|
16
|
+
"author": "smartcontractkit",
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "forge build --sizes",
|
|
19
19
|
"clean": "rm -rf cache out",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"wtf": "which forge"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@chainlink/contracts": "1.
|
|
35
|
+
"@chainlink/contracts": "1.5.0",
|
|
36
36
|
"@openzeppelin/contracts": "^5.0.2",
|
|
37
37
|
"@openzeppelin/contracts-upgradeable": "^5.0.2",
|
|
38
38
|
"forge-std": "github:foundry-rs/forge-std#v1.9.4",
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
3
|
|
|
4
4
|
import {ICredentialRegistry} from "./interfaces/ICredentialRegistry.sol";
|
|
5
5
|
import {ICredentialValidator} from "./interfaces/ICredentialValidator.sol";
|
|
6
|
-
import {
|
|
6
|
+
import {PolicyProtectedUpgradeable} from "@chainlink/policy-management/core/PolicyProtectedUpgradeable.sol";
|
|
7
7
|
|
|
8
|
-
contract CredentialRegistry is
|
|
9
|
-
|
|
8
|
+
contract CredentialRegistry is PolicyProtectedUpgradeable, ICredentialRegistry {
|
|
9
|
+
string public constant override typeAndVersion = "CredentialRegistry 1.0.0";
|
|
10
|
+
|
|
11
|
+
/// @custom:storage-location erc7201:chainlink.ace.CredentialRegistry
|
|
10
12
|
struct CredentialRegistryStorage {
|
|
11
13
|
mapping(bytes32 ccid => bytes32[] credentialTypeIds) credentialTypeIdsByCCID;
|
|
12
14
|
mapping(bytes32 ccid => mapping(bytes32 credentialTypeId => Credential credentials)) credentials;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
// keccak256(abi.encode(uint256(keccak256("
|
|
17
|
+
// keccak256(abi.encode(uint256(keccak256("chainlink.ace.CredentialRegistry")) - 1)) &
|
|
16
18
|
// ~bytes32(uint256(0xff))
|
|
17
19
|
// solhint-disable-next-line const-name-snakecase
|
|
18
20
|
bytes32 private constant credentialRegistryStorageLocation =
|
|
19
|
-
|
|
21
|
+
0xa402d759ab0c43f5f6ba3a2cdc5bb0f98c15af3daf1f94f873714cacbe789800;
|
|
20
22
|
|
|
21
23
|
function _credentialRegistryStorage() private pure returns (CredentialRegistryStorage storage $) {
|
|
22
24
|
// solhint-disable-next-line no-inline-assembly
|
|
@@ -168,7 +170,7 @@ contract CredentialRegistry is PolicyProtected, ICredentialRegistry {
|
|
|
168
170
|
view
|
|
169
171
|
returns (Credential[] memory)
|
|
170
172
|
{
|
|
171
|
-
|
|
173
|
+
uint256 length = credentialTypeIds.length;
|
|
172
174
|
Credential[] memory credentials = new Credential[](length);
|
|
173
175
|
for (uint256 i = 0; i < length; i++) {
|
|
174
176
|
credentials[i] = getCredential(ccid, credentialTypeIds[i]);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {CredentialRegistry} from "./CredentialRegistry.sol";
|
|
5
|
+
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @title CredentialRegistryFactory
|
|
9
|
+
* @notice Factory contract for creating deterministic minimal proxy clones of credential registry implementations.
|
|
10
|
+
* @dev Uses OpenZeppelin's Clones library to create deterministic minimal proxies (EIP-1167) of credential
|
|
11
|
+
* registry contracts.
|
|
12
|
+
* Each registry is deployed with a unique salt derived from the creator's address and a unique registry ID,
|
|
13
|
+
* ensuring deterministic addresses and preventing duplicate deployments.
|
|
14
|
+
*/
|
|
15
|
+
contract CredentialRegistryFactory {
|
|
16
|
+
/// @notice Emitted when a new credential registry is created
|
|
17
|
+
event CredentialRegistryCreated(address registry);
|
|
18
|
+
|
|
19
|
+
/// @notice Emitted when registry initialization fails
|
|
20
|
+
error RegistryInitializationFailed(bytes reason);
|
|
21
|
+
|
|
22
|
+
/// @notice Emitted when implementation address is zero
|
|
23
|
+
error ImplementationIsZeroAddress();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @notice Creates a new credential registry contract using deterministic minimal proxy cloning.
|
|
27
|
+
* @dev If a registry with the same salt already exists, returns the existing address instead of reverting.
|
|
28
|
+
* Uses CREATE2 for deterministic deployment addresses. The registry is automatically initialized
|
|
29
|
+
* with the provided parameters after deployment.
|
|
30
|
+
* @param implementation The address of the credential registry implementation contract to clone
|
|
31
|
+
* @param uniqueRegistryId A unique identifier for this registry (combined with msg.sender to create salt)
|
|
32
|
+
* @param policyEngine The address of the policy engine that will manage this registry
|
|
33
|
+
* @param initialOwner The address that will own the newly created registry contract
|
|
34
|
+
* @return registryAddress The address of the created (or existing) registry contract
|
|
35
|
+
*/
|
|
36
|
+
function createCredentialRegistry(
|
|
37
|
+
address implementation,
|
|
38
|
+
bytes32 uniqueRegistryId,
|
|
39
|
+
address policyEngine,
|
|
40
|
+
address initialOwner
|
|
41
|
+
)
|
|
42
|
+
public
|
|
43
|
+
returns (address registryAddress)
|
|
44
|
+
{
|
|
45
|
+
if (implementation == address(0)) revert ImplementationIsZeroAddress();
|
|
46
|
+
|
|
47
|
+
bytes32 salt = getSalt(msg.sender, uniqueRegistryId);
|
|
48
|
+
registryAddress = Clones.predictDeterministicAddress(implementation, salt);
|
|
49
|
+
if (registryAddress.code.length > 0) {
|
|
50
|
+
return registryAddress;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
registryAddress = Clones.cloneDeterministic(implementation, salt);
|
|
54
|
+
|
|
55
|
+
try CredentialRegistry(registryAddress).initialize(policyEngine, initialOwner) {
|
|
56
|
+
emit CredentialRegistryCreated(registryAddress);
|
|
57
|
+
} catch (bytes memory reason) {
|
|
58
|
+
revert RegistryInitializationFailed(reason);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @notice Predicts the deterministic address where a credential registry would be deployed.
|
|
64
|
+
* @dev Useful for calculating registry addresses before deployment or checking if a registry already exists.
|
|
65
|
+
* Uses the same salt generation as createCredentialRegistry to ensure address consistency.
|
|
66
|
+
* @param creator The address of the account that would create the registry
|
|
67
|
+
* @param implementation The address of the credential registry implementation contract
|
|
68
|
+
* @param uniqueRegistryId The unique identifier for the registry
|
|
69
|
+
* @return The predicted address where the registry would be deployed
|
|
70
|
+
*/
|
|
71
|
+
function predictRegistryAddress(
|
|
72
|
+
address creator,
|
|
73
|
+
address implementation,
|
|
74
|
+
bytes32 uniqueRegistryId
|
|
75
|
+
)
|
|
76
|
+
public
|
|
77
|
+
view
|
|
78
|
+
returns (address)
|
|
79
|
+
{
|
|
80
|
+
bytes32 salt = getSalt(creator, uniqueRegistryId);
|
|
81
|
+
return Clones.predictDeterministicAddress(implementation, salt);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @notice Generates a deterministic salt for registry deployment.
|
|
86
|
+
* @dev Combines the sender address and unique registry ID to create a unique salt.
|
|
87
|
+
* This ensures that the same creator cannot deploy multiple registries with the same ID,
|
|
88
|
+
* while allowing different creators to use the same registry ID.
|
|
89
|
+
* @param sender The address of the registry creator
|
|
90
|
+
* @param uniqueRegistryId The unique identifier for the registry
|
|
91
|
+
* @return The generated salt for deterministic deployment
|
|
92
|
+
*/
|
|
93
|
+
function getSalt(address sender, bytes32 uniqueRegistryId) public pure returns (bytes32) {
|
|
94
|
+
return keccak256(abi.encodePacked(sender, uniqueRegistryId));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
3
|
|
|
4
4
|
import {ICredentialRequirements} from "./interfaces/ICredentialRequirements.sol";
|
|
5
5
|
import {IIdentityValidator} from "./interfaces/IIdentityValidator.sol";
|
|
@@ -11,19 +11,20 @@ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Own
|
|
|
11
11
|
contract CredentialRegistryIdentityValidator is OwnableUpgradeable, ICredentialRequirements, IIdentityValidator {
|
|
12
12
|
uint256 private constant MAX_REQUIREMENTS = 8;
|
|
13
13
|
uint256 private constant MAX_REQUIREMENT_SOURCES = 8;
|
|
14
|
+
uint256 private constant MAX_CREDENTIAL_TYPES_PER_REQUIREMENT = 32;
|
|
14
15
|
|
|
15
|
-
/// @custom:storage-location erc7201:
|
|
16
|
+
/// @custom:storage-location erc7201:chainlink.ace.CredentialRegistryIdentityValidator
|
|
16
17
|
struct CredentialRegistryIdentityValidatorStorage {
|
|
17
18
|
bytes32[] requirements;
|
|
18
19
|
mapping(bytes32 requirementId => CredentialRequirement credentialRequirement) credentialRequirementMap;
|
|
19
20
|
mapping(bytes32 credential => CredentialSource[] sources) credentialSources;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
// keccak256(abi.encode(uint256(keccak256("
|
|
23
|
+
// keccak256(abi.encode(uint256(keccak256("chainlink.ace.CredentialRegistryIdentityValidator")) - 1)) &
|
|
23
24
|
// ~bytes32(uint256(0xff))
|
|
24
25
|
// solhint-disable-next-line const-name-snakecase
|
|
25
26
|
bytes32 private constant credentialRegistryIdentityValidatorStorageLocation =
|
|
26
|
-
|
|
27
|
+
0xd345ab5a3e8073283824dcc06e7ac0c586290818c864e9d2ee66361ecca47600;
|
|
27
28
|
|
|
28
29
|
function _credentialRegistryIdentityValidatorStorage()
|
|
29
30
|
private
|
|
@@ -36,6 +37,11 @@ contract CredentialRegistryIdentityValidator is OwnableUpgradeable, ICredentialR
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
constructor() {
|
|
41
|
+
// disabling initializers on the implementation contract itself
|
|
42
|
+
_disableInitializers();
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
/**
|
|
40
46
|
* @dev Initializes the credential validator and sets the initial credential sources and requirements.
|
|
41
47
|
* @param credentialSourceInputs The credential sources to add.
|
|
@@ -96,6 +102,11 @@ contract CredentialRegistryIdentityValidator is OwnableUpgradeable, ICredentialR
|
|
|
96
102
|
}
|
|
97
103
|
}
|
|
98
104
|
bytes32[] memory credentialTypeIds = input.credentialTypeIds;
|
|
105
|
+
uint256 credentialTypesLength = credentialTypeIds.length;
|
|
106
|
+
|
|
107
|
+
if (credentialTypesLength == 0 || credentialTypesLength > MAX_CREDENTIAL_TYPES_PER_REQUIREMENT) {
|
|
108
|
+
revert InvalidRequirementConfiguration("Invalid credential types length");
|
|
109
|
+
}
|
|
99
110
|
_credentialRegistryIdentityValidatorStorage().requirements.push(requirementId);
|
|
100
111
|
_credentialRegistryIdentityValidatorStorage().credentialRequirementMap[requirementId] =
|
|
101
112
|
CredentialRequirement(credentialTypeIds, minValidations, input.invert);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
3
|
|
|
4
4
|
import {CredentialRegistryIdentityValidator} from "./CredentialRegistryIdentityValidator.sol";
|
|
5
5
|
import {IPolicyEngine} from "@chainlink/policy-management/interfaces/IPolicyEngine.sol";
|
|
@@ -7,6 +7,8 @@ import {Policy} from "@chainlink/policy-management/core/Policy.sol";
|
|
|
7
7
|
import {ICredentialRequirements} from "@chainlink/cross-chain-identity/interfaces/ICredentialRequirements.sol";
|
|
8
8
|
|
|
9
9
|
contract CredentialRegistryIdentityValidatorPolicy is Policy, CredentialRegistryIdentityValidator {
|
|
10
|
+
string public constant override typeAndVersion = "CredentialRegistryIdentityValidatorPolicy 1.0.0";
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
13
|
* @notice Configures the policy by setting up credential sources and credential requirements.
|
|
12
14
|
* @dev The `parameters` input must be the ABI encoding of two dynamic arrays:
|
|
@@ -53,14 +55,15 @@ contract CredentialRegistryIdentityValidatorPolicy is Policy, CredentialRegistry
|
|
|
53
55
|
override
|
|
54
56
|
returns (IPolicyEngine.PolicyResult)
|
|
55
57
|
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
revert IPolicyEngine.InvalidConfiguration("expected 1 parameter");
|
|
58
|
+
if (parameters.length < 1) {
|
|
59
|
+
revert InvalidParameters("expected at least 1 parameter");
|
|
59
60
|
}
|
|
60
|
-
address account = abi.decode(parameters[0], (address));
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
for (uint256 i = 0; i < parameters.length; i++) {
|
|
63
|
+
address account = abi.decode(parameters[i], (address));
|
|
64
|
+
if (!validate(account, context)) {
|
|
65
|
+
revert IPolicyEngine.PolicyRejected("account identity validation failed");
|
|
66
|
+
}
|
|
64
67
|
}
|
|
65
68
|
return IPolicyEngine.PolicyResult.Continue;
|
|
66
69
|
}
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
3
|
|
|
4
4
|
import {IIdentityRegistry} from "./interfaces/IIdentityRegistry.sol";
|
|
5
|
-
import {
|
|
5
|
+
import {PolicyProtectedUpgradeable} from "@chainlink/policy-management/core/PolicyProtectedUpgradeable.sol";
|
|
6
6
|
|
|
7
|
-
contract IdentityRegistry is
|
|
8
|
-
|
|
7
|
+
contract IdentityRegistry is PolicyProtectedUpgradeable, IIdentityRegistry {
|
|
8
|
+
string public constant override typeAndVersion = "IdentityRegistry 1.0.0";
|
|
9
|
+
|
|
10
|
+
/// @custom:storage-location erc7201:chainlink.ace.IdentityRegistry
|
|
9
11
|
struct IdentityRegistryStorage {
|
|
10
12
|
mapping(address account => bytes32 ccid) accountToCcid;
|
|
11
13
|
mapping(bytes32 ccid => address[] accounts) ccidToAccounts;
|
|
14
|
+
// Maps ccid => account => index in ccidToAccounts array (index + 1, 0 means not found)
|
|
15
|
+
mapping(bytes32 ccid => mapping(address account => uint256 index)) accountIndex;
|
|
12
16
|
}
|
|
13
17
|
|
|
14
|
-
// keccak256(abi.encode(uint256(keccak256("
|
|
18
|
+
// keccak256(abi.encode(uint256(keccak256("chainlink.ace.IdentityRegistry")) - 1)) &
|
|
15
19
|
// ~bytes32(uint256(0xff))
|
|
16
20
|
// solhint-disable-next-line const-name-snakecase
|
|
17
21
|
bytes32 private constant identityRegistryStorageLocation =
|
|
18
|
-
|
|
22
|
+
0x31dacf4de1329b533dc7ad419a7ae424eacaaf799ae802e5d062dea412180200;
|
|
19
23
|
|
|
20
24
|
function _identityRegistryStorage() private pure returns (IdentityRegistryStorage storage $) {
|
|
21
25
|
// solhint-disable-next-line no-inline-assembly
|
|
@@ -83,6 +87,8 @@ contract IdentityRegistry is PolicyProtected, IIdentityRegistry {
|
|
|
83
87
|
}
|
|
84
88
|
_identityRegistryStorage().accountToCcid[account] = ccid;
|
|
85
89
|
_identityRegistryStorage().ccidToAccounts[ccid].push(account);
|
|
90
|
+
// Store index + 1 (so 0 means not found)
|
|
91
|
+
_identityRegistryStorage().accountIndex[ccid][account] = _identityRegistryStorage().ccidToAccounts[ccid].length;
|
|
86
92
|
emit IdentityRegistered(ccid, account);
|
|
87
93
|
}
|
|
88
94
|
|
|
@@ -97,18 +103,28 @@ contract IdentityRegistry is PolicyProtected, IIdentityRegistry {
|
|
|
97
103
|
override
|
|
98
104
|
runPolicyWithContext(context)
|
|
99
105
|
{
|
|
100
|
-
uint256
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
uint256 indexPlusOne = _identityRegistryStorage().accountIndex[ccid][account];
|
|
107
|
+
if (indexPlusOne == 0) {
|
|
108
|
+
revert IdentityNotFound(ccid, account);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
uint256 index = indexPlusOne - 1;
|
|
112
|
+
uint256 lastIndex = _identityRegistryStorage().ccidToAccounts[ccid].length - 1;
|
|
113
|
+
|
|
114
|
+
if (index != lastIndex) {
|
|
115
|
+
// Move the last element to the position being removed
|
|
116
|
+
address lastAccount = _identityRegistryStorage().ccidToAccounts[ccid][lastIndex];
|
|
117
|
+
_identityRegistryStorage().ccidToAccounts[ccid][index] = lastAccount;
|
|
118
|
+
// Update the moved account's index
|
|
119
|
+
_identityRegistryStorage().accountIndex[ccid][lastAccount] = indexPlusOne;
|
|
110
120
|
}
|
|
111
|
-
|
|
121
|
+
|
|
122
|
+
// Remove the last element
|
|
123
|
+
_identityRegistryStorage().ccidToAccounts[ccid].pop();
|
|
124
|
+
delete _identityRegistryStorage().accountToCcid[account];
|
|
125
|
+
delete _identityRegistryStorage().accountIndex[ccid][account];
|
|
126
|
+
|
|
127
|
+
emit IdentityRemoved(ccid, account);
|
|
112
128
|
}
|
|
113
129
|
|
|
114
130
|
/// @inheritdoc IIdentityRegistry
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {IdentityRegistry} from "./IdentityRegistry.sol";
|
|
5
|
+
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @title IdentityRegistryFactory
|
|
9
|
+
* @notice Factory contract for creating deterministic minimal proxy clones of identity registry implementations.
|
|
10
|
+
* @dev Uses OpenZeppelin's Clones library to create deterministic minimal proxies (EIP-1167) of identity
|
|
11
|
+
* registry contracts.
|
|
12
|
+
* Each registry is deployed with a unique salt derived from the creator's address and a unique registry ID,
|
|
13
|
+
* ensuring deterministic addresses and preventing duplicate deployments.
|
|
14
|
+
*/
|
|
15
|
+
contract IdentityRegistryFactory {
|
|
16
|
+
/// @notice Emitted when a new identity registry is created
|
|
17
|
+
event IdentityRegistryCreated(address registry);
|
|
18
|
+
|
|
19
|
+
/// @notice Emitted when registry initialization fails
|
|
20
|
+
error RegistryInitializationFailed(bytes reason);
|
|
21
|
+
|
|
22
|
+
/// @notice Emitted when implementation address is zero
|
|
23
|
+
error ImplementationIsZeroAddress();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @notice Creates a new identity registry contract using deterministic minimal proxy cloning.
|
|
27
|
+
* @dev If a registry with the same salt already exists, returns the existing address instead of reverting.
|
|
28
|
+
* Uses CREATE2 for deterministic deployment addresses. The registry is automatically initialized
|
|
29
|
+
* with the provided parameters after deployment.
|
|
30
|
+
* @param implementation The address of the identity registry implementation contract to clone
|
|
31
|
+
* @param uniqueRegistryId A unique identifier for this registry (combined with msg.sender to create salt)
|
|
32
|
+
* @param policyEngine The address of the policy engine that will manage this registry
|
|
33
|
+
* @param initialOwner The address that will own the newly created registry contract
|
|
34
|
+
* @return registryAddress The address of the created (or existing) registry contract
|
|
35
|
+
*/
|
|
36
|
+
function createIdentityRegistry(
|
|
37
|
+
address implementation,
|
|
38
|
+
bytes32 uniqueRegistryId,
|
|
39
|
+
address policyEngine,
|
|
40
|
+
address initialOwner
|
|
41
|
+
)
|
|
42
|
+
public
|
|
43
|
+
returns (address registryAddress)
|
|
44
|
+
{
|
|
45
|
+
if (implementation == address(0)) revert ImplementationIsZeroAddress();
|
|
46
|
+
|
|
47
|
+
bytes32 salt = getSalt(msg.sender, uniqueRegistryId);
|
|
48
|
+
registryAddress = Clones.predictDeterministicAddress(implementation, salt);
|
|
49
|
+
if (registryAddress.code.length > 0) {
|
|
50
|
+
return registryAddress;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
registryAddress = Clones.cloneDeterministic(implementation, salt);
|
|
54
|
+
|
|
55
|
+
try IdentityRegistry(registryAddress).initialize(policyEngine, initialOwner) {
|
|
56
|
+
emit IdentityRegistryCreated(registryAddress);
|
|
57
|
+
} catch (bytes memory reason) {
|
|
58
|
+
revert RegistryInitializationFailed(reason);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @notice Predicts the deterministic address where an identity registry would be deployed.
|
|
64
|
+
* @dev Useful for calculating registry addresses before deployment or checking if a registry already exists.
|
|
65
|
+
* Uses the same salt generation as createIdentityRegistry to ensure address consistency.
|
|
66
|
+
* @param creator The address of the account that would create the registry
|
|
67
|
+
* @param implementation The address of the identity registry implementation contract
|
|
68
|
+
* @param uniqueRegistryId The unique identifier for the registry
|
|
69
|
+
* @return The predicted address where the registry would be deployed
|
|
70
|
+
*/
|
|
71
|
+
function predictRegistryAddress(
|
|
72
|
+
address creator,
|
|
73
|
+
address implementation,
|
|
74
|
+
bytes32 uniqueRegistryId
|
|
75
|
+
)
|
|
76
|
+
public
|
|
77
|
+
view
|
|
78
|
+
returns (address)
|
|
79
|
+
{
|
|
80
|
+
bytes32 salt = getSalt(creator, uniqueRegistryId);
|
|
81
|
+
return Clones.predictDeterministicAddress(implementation, salt);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @notice Generates a deterministic salt for registry deployment.
|
|
86
|
+
* @dev Combines the sender address and unique registry ID to create a unique salt.
|
|
87
|
+
* This ensures that the same creator cannot deploy multiple registries with the same ID,
|
|
88
|
+
* while allowing different creators to use the same registry ID.
|
|
89
|
+
* @param sender The address of the registry creator
|
|
90
|
+
* @param uniqueRegistryId The unique identifier for the registry
|
|
91
|
+
* @return The generated salt for deterministic deployment
|
|
92
|
+
*/
|
|
93
|
+
function getSalt(address sender, bytes32 uniqueRegistryId) public pure returns (bytes32) {
|
|
94
|
+
return keccak256(abi.encodePacked(sender, uniqueRegistryId));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
3
|
|
|
4
4
|
import {ITrustedIssuerRegistry} from "./interfaces/ITrustedIssuerRegistry.sol";
|
|
5
|
-
import {
|
|
5
|
+
import {PolicyProtectedUpgradeable} from "@chainlink/policy-management/core/PolicyProtectedUpgradeable.sol";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @title TrustedIssuerRegistry
|
|
9
9
|
* @dev Implementation of the ITrustedIssuerRegistry interface using ERC-7201 storage pattern.
|
|
10
10
|
*/
|
|
11
|
-
contract TrustedIssuerRegistry is
|
|
12
|
-
|
|
11
|
+
contract TrustedIssuerRegistry is PolicyProtectedUpgradeable, ITrustedIssuerRegistry {
|
|
12
|
+
string public constant override typeAndVersion = "TrustedIssuerRegistry 1.0.0";
|
|
13
|
+
|
|
14
|
+
/// @custom:storage-location erc7201:chainlink.ace.TrustedIssuerRegistry
|
|
13
15
|
struct TrustedIssuerRegistryStorage {
|
|
14
16
|
mapping(bytes32 issuerIdHash => bool isTrusted) trustedIssuers;
|
|
15
17
|
bytes32[] issuerList;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
// keccak256(abi.encode(uint256(keccak256("
|
|
20
|
+
// keccak256(abi.encode(uint256(keccak256("chainlink.ace.TrustedIssuerRegistry")) - 1)) &
|
|
19
21
|
// ~bytes32(uint256(0xff))
|
|
20
22
|
// solhint-disable-next-line const-name-snakecase
|
|
21
23
|
bytes32 private constant trustedIssuerRegistryStorageLocation =
|
|
22
|
-
|
|
24
|
+
0x24368af76f28f75e0d3c893af5175655a9b97a18e82e58c43f9152ef16421900;
|
|
23
25
|
|
|
24
26
|
function _trustedIssuerRegistryStorage() private pure returns (TrustedIssuerRegistryStorage storage $) {
|
|
25
27
|
assembly {
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {TrustedIssuerRegistry} from "./TrustedIssuerRegistry.sol";
|
|
5
|
+
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @title TrustedIssuerRegistryFactory
|
|
9
|
+
* @notice Factory contract for creating deterministic minimal proxy clones of trusted issuer registry implementations.
|
|
10
|
+
* @dev Uses OpenZeppelin's Clones library to create deterministic minimal proxies (EIP-1167) of trusted issuer
|
|
11
|
+
* registry contracts.
|
|
12
|
+
* Each registry is deployed with a unique salt derived from the creator's address and a unique registry ID,
|
|
13
|
+
* ensuring deterministic addresses and preventing duplicate deployments.
|
|
14
|
+
*/
|
|
15
|
+
contract TrustedIssuerRegistryFactory {
|
|
16
|
+
/// @notice Emitted when a new trusted issuer registry is created
|
|
17
|
+
event TrustedIssuerRegistryCreated(address registry);
|
|
18
|
+
|
|
19
|
+
/// @notice Emitted when registry initialization fails
|
|
20
|
+
error RegistryInitializationFailed(bytes reason);
|
|
21
|
+
|
|
22
|
+
/// @notice Emitted when implementation address is zero
|
|
23
|
+
error ImplementationIsZeroAddress();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @notice Creates a new trusted issuer registry contract using deterministic minimal proxy cloning.
|
|
27
|
+
* @dev If a registry with the same salt already exists, returns the existing address instead of reverting.
|
|
28
|
+
* Uses CREATE2 for deterministic deployment addresses. The registry is automatically initialized
|
|
29
|
+
* with the provided parameters after deployment.
|
|
30
|
+
* @param implementation The address of the trusted issuer registry implementation contract to clone
|
|
31
|
+
* @param uniqueRegistryId A unique identifier for this registry (combined with msg.sender to create salt)
|
|
32
|
+
* @param policyEngine The address of the policy engine that will manage this registry
|
|
33
|
+
* @param initialOwner The address that will own the newly created registry contract
|
|
34
|
+
* @return registryAddress The address of the created (or existing) registry contract
|
|
35
|
+
*/
|
|
36
|
+
function createTrustedIssuerRegistry(
|
|
37
|
+
address implementation,
|
|
38
|
+
bytes32 uniqueRegistryId,
|
|
39
|
+
address policyEngine,
|
|
40
|
+
address initialOwner
|
|
41
|
+
)
|
|
42
|
+
public
|
|
43
|
+
returns (address registryAddress)
|
|
44
|
+
{
|
|
45
|
+
if (implementation == address(0)) revert ImplementationIsZeroAddress();
|
|
46
|
+
|
|
47
|
+
bytes32 salt = getSalt(msg.sender, uniqueRegistryId);
|
|
48
|
+
registryAddress = Clones.predictDeterministicAddress(implementation, salt);
|
|
49
|
+
if (registryAddress.code.length > 0) {
|
|
50
|
+
return registryAddress;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
registryAddress = Clones.cloneDeterministic(implementation, salt);
|
|
54
|
+
|
|
55
|
+
try TrustedIssuerRegistry(registryAddress).initialize(policyEngine, initialOwner) {
|
|
56
|
+
emit TrustedIssuerRegistryCreated(registryAddress);
|
|
57
|
+
} catch (bytes memory reason) {
|
|
58
|
+
revert RegistryInitializationFailed(reason);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @notice Predicts the deterministic address where a trusted issuer registry would be deployed.
|
|
64
|
+
* @dev Useful for calculating registry addresses before deployment or checking if a registry already exists.
|
|
65
|
+
* Uses the same salt generation as createTrustedIssuerRegistry to ensure address consistency.
|
|
66
|
+
* @param creator The address of the account that would create the registry
|
|
67
|
+
* @param implementation The address of the trusted issuer registry implementation contract
|
|
68
|
+
* @param uniqueRegistryId The unique identifier for the registry
|
|
69
|
+
* @return The predicted address where the registry would be deployed
|
|
70
|
+
*/
|
|
71
|
+
function predictRegistryAddress(
|
|
72
|
+
address creator,
|
|
73
|
+
address implementation,
|
|
74
|
+
bytes32 uniqueRegistryId
|
|
75
|
+
)
|
|
76
|
+
public
|
|
77
|
+
view
|
|
78
|
+
returns (address)
|
|
79
|
+
{
|
|
80
|
+
bytes32 salt = getSalt(creator, uniqueRegistryId);
|
|
81
|
+
return Clones.predictDeterministicAddress(implementation, salt);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @notice Generates a deterministic salt for registry deployment.
|
|
86
|
+
* @dev Combines the sender address and unique registry ID to create a unique salt.
|
|
87
|
+
* This ensures that the same creator cannot deploy multiple registries with the same ID,
|
|
88
|
+
* while allowing different creators to use the same registry ID.
|
|
89
|
+
* @param sender The address of the registry creator
|
|
90
|
+
* @param uniqueRegistryId The unique identifier for the registry
|
|
91
|
+
* @return The generated salt for deterministic deployment
|
|
92
|
+
*/
|
|
93
|
+
function getSalt(address sender, bytes32 uniqueRegistryId) public pure returns (bytes32) {
|
|
94
|
+
return keccak256(abi.encodePacked(sender, uniqueRegistryId));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -55,6 +55,12 @@ interface ICredentialRegistry is ICredentialValidator {
|
|
|
55
55
|
bytes32 indexed ccid, bytes32 indexed credentialTypeId, uint40 previousExpiresAt, uint40 expiresAt
|
|
56
56
|
);
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* @notice Returns the type and version of the credential registry.
|
|
60
|
+
* @return A string representing the type and version of the credential registry.
|
|
61
|
+
*/
|
|
62
|
+
function typeAndVersion() external pure returns (string memory);
|
|
63
|
+
|
|
58
64
|
/**
|
|
59
65
|
* @notice Registers a credential for an account.
|
|
60
66
|
*
|
|
@@ -49,7 +49,8 @@ interface ICredentialRequirements {
|
|
|
49
49
|
* @param requirementId The identifier of the requirement.
|
|
50
50
|
* @param credentialTypeIds The credential type identifier(s) that satisfy the requirement.
|
|
51
51
|
* @param minValidations The minimum number of validations required for the requirement.
|
|
52
|
-
* @param invert If the requirement is satisfied
|
|
52
|
+
* @param invert If true, the requirement is satisfied when at least minValidations sources confirm the credential is
|
|
53
|
+
* absent. This is useful for requirements that are satisfied by the absence of a credential on multiple sources.
|
|
53
54
|
*/
|
|
54
55
|
struct CredentialRequirementInput {
|
|
55
56
|
bytes32 requirementId;
|