@towns-protocol/contracts 0.0.414 → 0.0.415
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/package.json +3 -3
- package/scripts/deployments/diamonds/DeployAppRegistry.s.sol +27 -1
- package/scripts/deployments/facets/DeployIdentityRegistry.s.sol +64 -0
- package/scripts/deployments/facets/DeployReputationRegistry.s.sol +64 -0
- package/scripts/interactions/InteractAppRegistry.s.sol +60 -26
- package/src/apps/facets/identity/IIdentityRegistry.sol +130 -0
- package/src/apps/facets/identity/IdentityRegistryBase.sol +32 -0
- package/src/apps/facets/identity/IdentityRegistryFacet.sol +135 -0
- package/src/apps/facets/identity/IdentityRegistryStorage.sol +27 -0
- package/src/apps/facets/registry/AppRegistryBase.sol +0 -1
- package/src/apps/facets/reputation/IReputationRegistry.sol +186 -0
- package/src/apps/facets/reputation/ReputationRegistryBase.sol +450 -0
- package/src/apps/facets/reputation/ReputationRegistryFacet.sol +155 -0
- package/src/apps/facets/reputation/ReputationRegistryStorage.sol +38 -0
- package/src/apps/simple/app/ISimpleApp.sol +22 -0
- package/src/apps/simple/app/SimpleAppFacet.sol +20 -1
- package/src/apps/simple/app/SimpleAppStorage.sol +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@towns-protocol/contracts",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.415",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"clean": "forge clean",
|
|
6
6
|
"compile": "forge build",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@layerzerolabs/oapp-evm": "^0.3.2",
|
|
34
34
|
"@openzeppelin/merkle-tree": "^1.0.8",
|
|
35
35
|
"@prb/test": "^0.6.4",
|
|
36
|
-
"@towns-protocol/prettier-config": "^0.0.
|
|
36
|
+
"@towns-protocol/prettier-config": "^0.0.415",
|
|
37
37
|
"@wagmi/cli": "^2.2.0",
|
|
38
38
|
"forge-std": "github:foundry-rs/forge-std#v1.10.0",
|
|
39
39
|
"prettier": "^3.5.3",
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"publishConfig": {
|
|
54
54
|
"access": "public"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "27f0f8758cd1a18fad7a2774490389e48637942b"
|
|
57
57
|
}
|
|
@@ -3,7 +3,7 @@ pragma solidity ^0.8.23;
|
|
|
3
3
|
|
|
4
4
|
// interfaces
|
|
5
5
|
import {IDiamondInitHelper} from "./IDiamondInitHelper.sol";
|
|
6
|
-
import {
|
|
6
|
+
import {IAppFactoryBase} from "src/apps/facets/factory/IAppFactory.sol";
|
|
7
7
|
|
|
8
8
|
// libraries
|
|
9
9
|
import {DeployDiamondCut} from "@towns-protocol/diamond/scripts/deployments/facets/DeployDiamondCut.sol";
|
|
@@ -17,6 +17,8 @@ import {DeployAppInstallerFacet} from "../facets/DeployAppInstallerFacet.s.sol";
|
|
|
17
17
|
import {DeployAppFactoryFacet} from "../facets/DeployAppFactoryFacet.s.sol";
|
|
18
18
|
import {DeploySpaceFactory} from "../diamonds/DeploySpaceFactory.s.sol";
|
|
19
19
|
import {DeploySimpleAppBeacon} from "../diamonds/DeploySimpleAppBeacon.s.sol";
|
|
20
|
+
import {DeployIdentityRegistry} from "../facets/DeployIdentityRegistry.s.sol";
|
|
21
|
+
import {DeployReputationRegistry} from "../facets/DeployReputationRegistry.s.sol";
|
|
20
22
|
|
|
21
23
|
// contracts
|
|
22
24
|
import {Diamond} from "@towns-protocol/diamond/src/Diamond.sol";
|
|
@@ -36,6 +38,11 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
|
|
|
36
38
|
DeploySimpleAppBeacon private deploySimpleAppBeacon = new DeploySimpleAppBeacon();
|
|
37
39
|
|
|
38
40
|
string internal constant APP_REGISTRY_SCHEMA = "address app, address client";
|
|
41
|
+
string internal constant FEEDBACK_SCHEMA =
|
|
42
|
+
"uint256 agentId, uint8 score, bytes32 tag1, bytes32 tag2, string feedbackUri, bytes32 feedbackHash";
|
|
43
|
+
string internal constant RESPONSE_SCHEMA =
|
|
44
|
+
"uint256 agentId, address reviewerAddress, uint64 feedbackIndex, string responseUri, bytes32 responseHash";
|
|
45
|
+
|
|
39
46
|
address internal spaceFactory;
|
|
40
47
|
address internal simpleAppBeacon;
|
|
41
48
|
|
|
@@ -101,6 +108,8 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
|
|
|
101
108
|
facetHelper.add("AppRegistryFacet");
|
|
102
109
|
facetHelper.add("AppInstallerFacet");
|
|
103
110
|
facetHelper.add("AppFactoryFacet");
|
|
111
|
+
facetHelper.add("IdentityRegistryFacet");
|
|
112
|
+
facetHelper.add("ReputationRegistryFacet");
|
|
104
113
|
|
|
105
114
|
facetHelper.deployBatch(deployer);
|
|
106
115
|
|
|
@@ -131,6 +140,20 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
|
|
|
131
140
|
DeployAppFactoryFacet.makeInitData(beacons, _getEntryPoint())
|
|
132
141
|
);
|
|
133
142
|
|
|
143
|
+
facet = facetHelper.getDeployedAddress("IdentityRegistryFacet");
|
|
144
|
+
addFacet(
|
|
145
|
+
makeCut(facet, FacetCutAction.Add, DeployIdentityRegistry.selectors()),
|
|
146
|
+
facet,
|
|
147
|
+
DeployIdentityRegistry.makeInitData()
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
facet = facetHelper.getDeployedAddress("ReputationRegistryFacet");
|
|
151
|
+
addFacet(
|
|
152
|
+
makeCut(facet, FacetCutAction.Add, DeployReputationRegistry.selectors()),
|
|
153
|
+
facet,
|
|
154
|
+
DeployReputationRegistry.makeInitData(FEEDBACK_SCHEMA, RESPONSE_SCHEMA)
|
|
155
|
+
);
|
|
156
|
+
|
|
134
157
|
address multiInit = facetHelper.getDeployedAddress("MultiInit");
|
|
135
158
|
|
|
136
159
|
return
|
|
@@ -166,6 +189,9 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
|
|
|
166
189
|
if (facetName.eq("AppFactoryFacet")) {
|
|
167
190
|
addCut(makeCut(facet, FacetCutAction.Add, DeployAppFactoryFacet.selectors()));
|
|
168
191
|
}
|
|
192
|
+
if (facetName.eq("IdentityRegistryFacet")) {
|
|
193
|
+
addCut(makeCut(facet, FacetCutAction.Add, DeployIdentityRegistry.selectors()));
|
|
194
|
+
}
|
|
169
195
|
}
|
|
170
196
|
|
|
171
197
|
return baseFacets();
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IDiamond} from "@towns-protocol/diamond/src/IDiamond.sol";
|
|
6
|
+
|
|
7
|
+
// libraries
|
|
8
|
+
import {LibDeploy} from "@towns-protocol/diamond/src/utils/LibDeploy.sol";
|
|
9
|
+
import {DeployERC721A} from "../facets/DeployERC721A.s.sol";
|
|
10
|
+
|
|
11
|
+
// contracts
|
|
12
|
+
import {IdentityRegistryFacet} from "src/apps/facets/identity/IdentityRegistryFacet.sol";
|
|
13
|
+
import {DynamicArrayLib} from "solady/utils/DynamicArrayLib.sol";
|
|
14
|
+
|
|
15
|
+
library DeployIdentityRegistry {
|
|
16
|
+
using DynamicArrayLib for DynamicArrayLib.DynamicArray;
|
|
17
|
+
|
|
18
|
+
function selectors() internal pure returns (bytes4[] memory res) {
|
|
19
|
+
uint256 selectorsCount = 20;
|
|
20
|
+
DynamicArrayLib.DynamicArray memory arr = DynamicArrayLib.p().reserve(selectorsCount);
|
|
21
|
+
arr.p(bytes4(keccak256("register()")));
|
|
22
|
+
arr.p(bytes4(keccak256("register(string)")));
|
|
23
|
+
arr.p(bytes4(keccak256("register(string,(string,bytes)[])")));
|
|
24
|
+
arr.p(IdentityRegistryFacet.getMetadata.selector);
|
|
25
|
+
arr.p(IdentityRegistryFacet.setMetadata.selector);
|
|
26
|
+
arr.p(IdentityRegistryFacet.setAgentUri.selector);
|
|
27
|
+
|
|
28
|
+
{
|
|
29
|
+
bytes4[] memory selectors_2 = DeployERC721A.selectors();
|
|
30
|
+
if (selectors_2.length > selectorsCount) {
|
|
31
|
+
revert("Selectors count is greater than the reserved space");
|
|
32
|
+
}
|
|
33
|
+
for (uint256 i; i < selectors_2.length; ++i) {
|
|
34
|
+
arr.p(selectors_2[i]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
bytes32[] memory selectors_ = arr.asBytes32Array();
|
|
39
|
+
|
|
40
|
+
assembly ("memory-safe") {
|
|
41
|
+
res := selectors_
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function makeCut(
|
|
46
|
+
address facetAddress,
|
|
47
|
+
IDiamond.FacetCutAction action
|
|
48
|
+
) internal pure returns (IDiamond.FacetCut memory) {
|
|
49
|
+
return
|
|
50
|
+
IDiamond.FacetCut({
|
|
51
|
+
action: action,
|
|
52
|
+
facetAddress: facetAddress,
|
|
53
|
+
functionSelectors: selectors()
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function makeInitData() internal pure returns (bytes memory) {
|
|
58
|
+
return abi.encodeCall(IdentityRegistryFacet.__IdentityRegistryFacet_init, ());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function deploy() internal returns (address) {
|
|
62
|
+
return LibDeploy.deployCode("IdentityRegistryFacet.sol", "");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IDiamond} from "@towns-protocol/diamond/src/IDiamond.sol";
|
|
6
|
+
|
|
7
|
+
// libraries
|
|
8
|
+
import {LibDeploy} from "@towns-protocol/diamond/src/utils/LibDeploy.sol";
|
|
9
|
+
import {DynamicArrayLib} from "solady/utils/DynamicArrayLib.sol";
|
|
10
|
+
|
|
11
|
+
// contracts
|
|
12
|
+
import {ReputationRegistryFacet} from "src/apps/facets/reputation/ReputationRegistryFacet.sol";
|
|
13
|
+
|
|
14
|
+
library DeployReputationRegistry {
|
|
15
|
+
using DynamicArrayLib for DynamicArrayLib.DynamicArray;
|
|
16
|
+
|
|
17
|
+
function selectors() internal pure returns (bytes4[] memory res) {
|
|
18
|
+
uint256 selectorsCount = 10;
|
|
19
|
+
DynamicArrayLib.DynamicArray memory arr = DynamicArrayLib.p().reserve(selectorsCount);
|
|
20
|
+
arr.p(ReputationRegistryFacet.giveFeedback.selector);
|
|
21
|
+
arr.p(ReputationRegistryFacet.revokeFeedback.selector);
|
|
22
|
+
arr.p(ReputationRegistryFacet.appendResponse.selector);
|
|
23
|
+
arr.p(ReputationRegistryFacet.getIdentityRegistry.selector);
|
|
24
|
+
arr.p(ReputationRegistryFacet.getSummary.selector);
|
|
25
|
+
arr.p(ReputationRegistryFacet.readFeedback.selector);
|
|
26
|
+
arr.p(ReputationRegistryFacet.readAllFeedback.selector);
|
|
27
|
+
arr.p(ReputationRegistryFacet.getResponseCount.selector);
|
|
28
|
+
arr.p(ReputationRegistryFacet.getClients.selector);
|
|
29
|
+
arr.p(ReputationRegistryFacet.getLastIndex.selector);
|
|
30
|
+
|
|
31
|
+
bytes32[] memory selectors_ = arr.asBytes32Array();
|
|
32
|
+
|
|
33
|
+
assembly ("memory-safe") {
|
|
34
|
+
res := selectors_
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function makeCut(
|
|
39
|
+
address facetAddress,
|
|
40
|
+
IDiamond.FacetCutAction action
|
|
41
|
+
) internal pure returns (IDiamond.FacetCut memory) {
|
|
42
|
+
return
|
|
43
|
+
IDiamond.FacetCut({
|
|
44
|
+
action: action,
|
|
45
|
+
facetAddress: facetAddress,
|
|
46
|
+
functionSelectors: selectors()
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function makeInitData(
|
|
51
|
+
string memory feedbackSchema,
|
|
52
|
+
string memory responseSchema
|
|
53
|
+
) internal pure returns (bytes memory) {
|
|
54
|
+
return
|
|
55
|
+
abi.encodeCall(
|
|
56
|
+
ReputationRegistryFacet.__ReputationRegistry_init,
|
|
57
|
+
(feedbackSchema, responseSchema)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function deploy() internal returns (address) {
|
|
62
|
+
return LibDeploy.deployCode("ReputationRegistryFacet.sol", "");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -3,6 +3,7 @@ pragma solidity ^0.8.23;
|
|
|
3
3
|
|
|
4
4
|
// interfaces
|
|
5
5
|
import {IAppFactoryBase} from "src/apps/facets/factory/IAppFactory.sol";
|
|
6
|
+
import {UpgradeableBeaconFacet} from "src/diamond/facets/beacon/UpgradeableBeaconFacet.sol";
|
|
6
7
|
|
|
7
8
|
// libraries
|
|
8
9
|
import {console} from "forge-std/console.sol";
|
|
@@ -14,50 +15,83 @@ import {DeployAppRegistry} from "../deployments/diamonds/DeployAppRegistry.s.sol
|
|
|
14
15
|
import {DeploySimpleAppBeacon} from "../deployments/diamonds/DeploySimpleAppBeacon.s.sol";
|
|
15
16
|
|
|
16
17
|
// facet deployers
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import {MultiInit} from "@towns-protocol/diamond/src/initializers/MultiInit.sol";
|
|
19
|
+
import {DeployIdentityRegistry} from "../deployments/facets/DeployIdentityRegistry.s.sol";
|
|
20
|
+
import {DeployReputationRegistry} from "../deployments/facets/DeployReputationRegistry.s.sol";
|
|
21
|
+
import {DeployFacet} from "../common/DeployFacet.s.sol";
|
|
20
22
|
|
|
21
23
|
contract InteractAppRegistry is Interaction, AlphaHelper {
|
|
22
24
|
DeployAppRegistry private deployHelper = new DeployAppRegistry();
|
|
23
25
|
DeploySimpleAppBeacon private beaconHelper = new DeploySimpleAppBeacon();
|
|
26
|
+
DeployFacet private facetHelper = new DeployFacet();
|
|
27
|
+
|
|
28
|
+
string internal constant FEEDBACK_SCHEMA =
|
|
29
|
+
"uint256 agentId, uint8 score, bytes32 tag1, bytes32 tag2, string feedbackUri, bytes32 feedbackHash";
|
|
30
|
+
string internal constant RESPONSE_SCHEMA =
|
|
31
|
+
"uint256 agentId, address reviewerAddress, uint64 feedbackIndex, string responseUri, bytes32 responseHash";
|
|
24
32
|
|
|
25
33
|
function __interact(address deployer) internal override {
|
|
26
|
-
// Get the deployed
|
|
34
|
+
// Get the deployed contract addresses
|
|
27
35
|
address appRegistry = getDeployment("appRegistry");
|
|
28
|
-
address spaceFactory = getDeployment("spaceFactory");
|
|
29
36
|
address simpleAppBeacon = getDeployment("simpleAppBeacon");
|
|
30
|
-
address entryPoint = getDeployment("entryPoint");
|
|
31
37
|
|
|
32
38
|
console.log("AppRegistry Diamond:", appRegistry);
|
|
33
|
-
console.log("Space Factory:", spaceFactory);
|
|
34
39
|
console.log("Simple App Beacon:", simpleAppBeacon);
|
|
35
40
|
|
|
36
41
|
// Deploy new facet implementations
|
|
37
42
|
console.log("\n=== Deploying New Facets ===");
|
|
38
43
|
vm.setEnv("OVERRIDE_DEPLOYMENTS", "1");
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
// Deploy Identity Registry Facet
|
|
46
|
+
address identityRegistryFacet = DeployIdentityRegistry.deploy();
|
|
47
|
+
console.log("IdentityRegistryFacet deployed at:", identityRegistryFacet);
|
|
48
|
+
|
|
49
|
+
// Deploy Reputation Registry Facet
|
|
50
|
+
address reputationRegistryFacet = DeployReputationRegistry.deploy();
|
|
51
|
+
console.log("ReputationRegistryFacet deployed at:", reputationRegistryFacet);
|
|
52
|
+
|
|
53
|
+
// Deploy new version of Simple App Facet
|
|
54
|
+
facetHelper.add("SimpleAppFacet");
|
|
55
|
+
facetHelper.deployBatch(deployer);
|
|
56
|
+
address newSimpleAppFacet = facetHelper.getDeployedAddress("SimpleAppFacet");
|
|
57
|
+
console.log("SimpleAppFacet deployed at:", newSimpleAppFacet);
|
|
42
58
|
|
|
43
|
-
|
|
44
|
-
console.log("
|
|
59
|
+
// Upgrade Simple App Beacon to use new implementation
|
|
60
|
+
console.log("\n=== Upgrading Simple App Beacon ===");
|
|
61
|
+
vm.broadcast(deployer);
|
|
62
|
+
UpgradeableBeaconFacet(simpleAppBeacon).upgradeTo(newSimpleAppFacet);
|
|
63
|
+
console.log("Simple App Beacon upgraded to:", newSimpleAppFacet);
|
|
45
64
|
|
|
46
|
-
|
|
47
|
-
|
|
65
|
+
// Add the cuts for the new registry facets (Add action)
|
|
66
|
+
addCut(DeployIdentityRegistry.makeCut(identityRegistryFacet, FacetCutAction.Add));
|
|
67
|
+
addCut(DeployReputationRegistry.makeCut(reputationRegistryFacet, FacetCutAction.Add));
|
|
68
|
+
|
|
69
|
+
// Prepare initialization data for the new facets
|
|
70
|
+
bytes memory identityInitData = DeployIdentityRegistry.makeInitData();
|
|
71
|
+
bytes memory reputationInitData = DeployReputationRegistry.makeInitData(
|
|
72
|
+
FEEDBACK_SCHEMA,
|
|
73
|
+
RESPONSE_SCHEMA
|
|
74
|
+
);
|
|
48
75
|
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
addCut(DeployAppFactoryFacet.makeCut(appFactoryFacet, FacetCutAction.Replace));
|
|
76
|
+
// Prepare combined initialization using MultiInit pattern
|
|
77
|
+
address[] memory initAddresses = new address[](2);
|
|
78
|
+
bytes[] memory initDatas = new bytes[](2);
|
|
53
79
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
80
|
+
initAddresses[0] = identityRegistryFacet;
|
|
81
|
+
initDatas[0] = identityInitData;
|
|
82
|
+
|
|
83
|
+
initAddresses[1] = reputationRegistryFacet;
|
|
84
|
+
initDatas[1] = reputationInitData;
|
|
85
|
+
|
|
86
|
+
// Get MultiInit address from facet helper
|
|
87
|
+
facetHelper.add("MultiInit");
|
|
88
|
+
facetHelper.deployBatch(deployer);
|
|
89
|
+
address multiInit = facetHelper.getDeployedAddress("MultiInit");
|
|
90
|
+
|
|
91
|
+
bytes memory combinedInitData = abi.encodeCall(
|
|
92
|
+
MultiInit.multiInit,
|
|
93
|
+
(initAddresses, initDatas)
|
|
94
|
+
);
|
|
61
95
|
|
|
62
96
|
// Generate and execute smart cuts with initialization
|
|
63
97
|
console.log("\n=== Executing Diamond Cut with Initialization ===");
|
|
@@ -66,8 +100,8 @@ contract InteractAppRegistry is Interaction, AlphaHelper {
|
|
|
66
100
|
appRegistry,
|
|
67
101
|
"AppRegistry",
|
|
68
102
|
deployHelper,
|
|
69
|
-
|
|
70
|
-
|
|
103
|
+
multiInit,
|
|
104
|
+
combinedInitData
|
|
71
105
|
);
|
|
72
106
|
|
|
73
107
|
console.log("\n=== Diamond Cut Complete ===");
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
|
|
6
|
+
// libraries
|
|
7
|
+
|
|
8
|
+
// contracts
|
|
9
|
+
|
|
10
|
+
/// @title IIdentityRegistryBase
|
|
11
|
+
/// @notice Base interface for ERC-8004 compliant agent identity registry
|
|
12
|
+
/// @dev Defines core data structures, events, and errors for agent registration and metadata management
|
|
13
|
+
interface IIdentityRegistryBase {
|
|
14
|
+
/// @notice Metadata entry for storing additional on-chain agent information
|
|
15
|
+
/// @dev Used in batch metadata operations during agent registration
|
|
16
|
+
struct MetadataEntry {
|
|
17
|
+
/// @notice The metadata key identifier
|
|
18
|
+
string metadataKey;
|
|
19
|
+
/// @notice The metadata value stored as bytes
|
|
20
|
+
bytes metadataValue;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// @notice Emitted when a new agent is registered in the identity registry
|
|
24
|
+
/// @param agentId The unique identifier (agentId) assigned to the agent
|
|
25
|
+
/// @param agentUri The URI pointing to the agent's registration file
|
|
26
|
+
/// @param owner The address that owns the agent identity
|
|
27
|
+
event Registered(uint256 indexed agentId, string agentUri, address indexed owner);
|
|
28
|
+
|
|
29
|
+
/// @notice Emitted when metadata is set or updated for an agent
|
|
30
|
+
/// @param agentId The unique identifier of the agent
|
|
31
|
+
/// @param indexedKey The metadata key as indexed parameter for efficient filtering
|
|
32
|
+
/// @param metadataKey The metadata key as non-indexed parameter for reading
|
|
33
|
+
/// @param metadataValue The metadata value stored as bytes
|
|
34
|
+
event MetadataSet(
|
|
35
|
+
uint256 indexed agentId,
|
|
36
|
+
string indexed indexedKey,
|
|
37
|
+
string metadataKey,
|
|
38
|
+
bytes metadataValue
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
/// @notice Emitted when an agent's URI is updated
|
|
42
|
+
/// @param agentId The unique identifier of the agent
|
|
43
|
+
/// @param agentUri The new URI pointing to the agent's registration file
|
|
44
|
+
/// @param updatedBy The address that performed the update
|
|
45
|
+
event UriUpdated(uint256 indexed agentId, string agentUri, address indexed updatedBy);
|
|
46
|
+
|
|
47
|
+
/// @notice Thrown when caller is not authorized to perform the operation
|
|
48
|
+
/// @dev Authorization requires being the owner, an approved operator, or approved for the specific token
|
|
49
|
+
error IdentityRegistry__NotAuthorized();
|
|
50
|
+
|
|
51
|
+
/// @notice Thrown when attempting to access an agent that does not exist
|
|
52
|
+
error IdentityRegistry__AgentDoesNotExist();
|
|
53
|
+
|
|
54
|
+
/// @notice Thrown when attempting an operation on an agent that is not registered
|
|
55
|
+
error IdentityRegistry__AgentNotRegistered();
|
|
56
|
+
|
|
57
|
+
/// @notice Thrown when attempting to register an agent that is banned
|
|
58
|
+
error IdentityRegistry__AgentBanned();
|
|
59
|
+
|
|
60
|
+
/// @notice Thrown when attempting to promote or register an agent that already has a promoted identity
|
|
61
|
+
error IdentityRegistry__AgentAlreadyPromoted();
|
|
62
|
+
|
|
63
|
+
/// @notice Thrown when attempting to register an agent with too many metadata entries
|
|
64
|
+
error IdentityRegistry__TooManyMetadataEntries();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// @title IIdentityRegistry
|
|
68
|
+
/// @notice ERC-8004 compliant agent identity registry interface
|
|
69
|
+
/// @dev Extends ERC-721 with agent registration, metadata management, and URI storage capabilities.
|
|
70
|
+
/// Each agent is uniquely identified globally by: namespace (eip155), chainId, identityRegistry address, and agentId.
|
|
71
|
+
/// The tokenURI resolves to an agent registration file containing endpoints, capabilities, and trust model information.
|
|
72
|
+
interface IIdentityRegistry is IIdentityRegistryBase {
|
|
73
|
+
/// @notice Register a new agent identity without initial URI or metadata
|
|
74
|
+
/// @dev Mints a new ERC-721 token to msg.sender. URI can be set later via setAgentUri.
|
|
75
|
+
/// @return agentId The unique identifier assigned to the newly registered agent
|
|
76
|
+
function register() external returns (uint256 agentId);
|
|
77
|
+
|
|
78
|
+
/// @notice Register a new agent identity with a tokenURI
|
|
79
|
+
/// @dev Mints a new ERC-721 token to msg.sender and sets the tokenURI
|
|
80
|
+
/// @param agentUri The URI pointing to the agent's registration file (e.g., ipfs://cid or https://domain.com/agent.json)
|
|
81
|
+
/// @return agentId The unique identifier assigned to the newly registered agent
|
|
82
|
+
function register(string calldata agentUri) external returns (uint256 agentId);
|
|
83
|
+
|
|
84
|
+
/// @notice Register a new agent identity with tokenURI and metadata
|
|
85
|
+
/// @dev Mints a new ERC-721 token to msg.sender, sets the tokenURI, and stores metadata entries.
|
|
86
|
+
/// This is the most complete registration method for agents with immediate metadata.
|
|
87
|
+
/// @param agentUri The URI pointing to the agent's registration file
|
|
88
|
+
/// @param metadata Array of metadata entries to store as on-chain metadata
|
|
89
|
+
/// @return agentId The unique identifier assigned to the newly registered agent
|
|
90
|
+
function register(
|
|
91
|
+
string calldata agentUri,
|
|
92
|
+
MetadataEntry[] calldata metadata
|
|
93
|
+
) external returns (uint256 agentId);
|
|
94
|
+
|
|
95
|
+
/// @notice Retrieve metadata value for a specific agent and metadataKey
|
|
96
|
+
/// @dev Returns empty bytes if the metadataKey does not exist
|
|
97
|
+
/// @param agentId The unique identifier of the agent
|
|
98
|
+
/// @param metadataKey The metadata key to retrieve
|
|
99
|
+
/// @return The metadata value stored as bytes
|
|
100
|
+
function getMetadata(
|
|
101
|
+
uint256 agentId,
|
|
102
|
+
string memory metadataKey
|
|
103
|
+
) external view returns (bytes memory);
|
|
104
|
+
|
|
105
|
+
/// @notice Set or update metadata for a specific agent
|
|
106
|
+
/// @dev Only callable by the agent owner, approved operator, or token-specific approved address.
|
|
107
|
+
/// Emits MetadataSet event.
|
|
108
|
+
/// @param agentId The unique identifier of the agent
|
|
109
|
+
/// @param metadataKey The metadata key to set
|
|
110
|
+
/// @param metadataValue The metadata value to store as bytes
|
|
111
|
+
function setMetadata(
|
|
112
|
+
uint256 agentId,
|
|
113
|
+
string memory metadataKey,
|
|
114
|
+
bytes memory metadataValue
|
|
115
|
+
) external;
|
|
116
|
+
|
|
117
|
+
/// @notice Update the tokenURI for an agent
|
|
118
|
+
/// @dev Only callable by the agent owner, approved operator, or token-specific approved address.
|
|
119
|
+
/// Emits UriUpdated event. Useful when agent registration data changes.
|
|
120
|
+
/// @param agentId The unique identifier of the agent
|
|
121
|
+
/// @param agentUri The new URI pointing to the agent's updated registration file
|
|
122
|
+
function setAgentUri(uint256 agentId, string calldata agentUri) external;
|
|
123
|
+
|
|
124
|
+
/// @notice Get the tokenURI for an agent
|
|
125
|
+
/// @dev Returns the URI pointing to the agent's registration file.
|
|
126
|
+
/// Reverts if the token does not exist.
|
|
127
|
+
/// @param agentId The unique identifier of the agent (ERC-721 tokenId)
|
|
128
|
+
/// @return The URI string pointing to the agent's registration file
|
|
129
|
+
function tokenURI(uint256 agentId) external view returns (string memory);
|
|
130
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IIdentityRegistryBase} from "./IIdentityRegistry.sol";
|
|
6
|
+
|
|
7
|
+
// libraries
|
|
8
|
+
import {IdentityRegistryStorage} from "./IdentityRegistryStorage.sol";
|
|
9
|
+
|
|
10
|
+
// contracts
|
|
11
|
+
|
|
12
|
+
abstract contract IdentityRegistryBase is IIdentityRegistryBase {
|
|
13
|
+
function _setAgentUri(uint256 agentId, string memory agentUri) internal {
|
|
14
|
+
IdentityRegistryStorage.getLayout().agentUri[agentId] = agentUri;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function _getAgentUri(uint256 agentId) internal view returns (string memory) {
|
|
18
|
+
return IdentityRegistryStorage.getLayout().agentUri[agentId];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function _setMetadata(uint256 agentId, string memory metadataKey, bytes memory value) internal {
|
|
22
|
+
IdentityRegistryStorage.getLayout().metadata[agentId][metadataKey] = value;
|
|
23
|
+
emit MetadataSet(agentId, metadataKey, metadataKey, value);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function _getMetadata(
|
|
27
|
+
uint256 agentId,
|
|
28
|
+
string memory metadataKey
|
|
29
|
+
) internal view returns (bytes memory) {
|
|
30
|
+
return IdentityRegistryStorage.getLayout().metadata[agentId][metadataKey];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IIdentityRegistry} from "./IIdentityRegistry.sol";
|
|
6
|
+
|
|
7
|
+
// libraries
|
|
8
|
+
import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
|
|
9
|
+
import {EMPTY_UID} from "@ethereum-attestation-service/eas-contracts/Common.sol";
|
|
10
|
+
|
|
11
|
+
// contracts
|
|
12
|
+
import {AppRegistryBase} from "../registry/AppRegistryBase.sol";
|
|
13
|
+
import {IdentityRegistryBase} from "./IdentityRegistryBase.sol";
|
|
14
|
+
import {ERC721A} from "../../../diamond/facets/token/ERC721A/ERC721A.sol";
|
|
15
|
+
|
|
16
|
+
contract IdentityRegistryFacet is
|
|
17
|
+
IIdentityRegistry,
|
|
18
|
+
AppRegistryBase,
|
|
19
|
+
IdentityRegistryBase,
|
|
20
|
+
ERC721A
|
|
21
|
+
{
|
|
22
|
+
using CustomRevert for bytes4;
|
|
23
|
+
|
|
24
|
+
uint256 internal constant MAX_METADATA_ENTRIES = 10;
|
|
25
|
+
|
|
26
|
+
function __IdentityRegistryFacet_init() external onlyInitializing {
|
|
27
|
+
__IdentityRegistryFacet_init_unchained();
|
|
28
|
+
__ERC721A_init_unchained("Towns Bot Agent", "TBA");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function __IdentityRegistryFacet_init_unchained() internal {
|
|
32
|
+
_addInterface(type(IIdentityRegistry).interfaceId);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// @inheritdoc IIdentityRegistry
|
|
36
|
+
function register() external returns (uint256 agentId) {
|
|
37
|
+
_verifyAgent();
|
|
38
|
+
agentId = _nextTokenId();
|
|
39
|
+
_mint(msg.sender, 1);
|
|
40
|
+
emit Registered(agentId, "", msg.sender);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// @inheritdoc IIdentityRegistry
|
|
44
|
+
function register(string calldata agentUri) external returns (uint256 agentId) {
|
|
45
|
+
_verifyAgent();
|
|
46
|
+
agentId = _nextTokenId();
|
|
47
|
+
_mint(msg.sender, 1);
|
|
48
|
+
_setAgentUri(agentId, agentUri);
|
|
49
|
+
emit Registered(agentId, agentUri, msg.sender);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// @inheritdoc IIdentityRegistry
|
|
53
|
+
function register(
|
|
54
|
+
string calldata agentUri,
|
|
55
|
+
MetadataEntry[] calldata metadata
|
|
56
|
+
) external returns (uint256 agentId) {
|
|
57
|
+
_verifyMetadataLength(metadata);
|
|
58
|
+
_verifyAgent();
|
|
59
|
+
agentId = _nextTokenId();
|
|
60
|
+
_mint(msg.sender, 1);
|
|
61
|
+
_setAgentUri(agentId, agentUri);
|
|
62
|
+
|
|
63
|
+
for (uint256 i; i < metadata.length; ++i) {
|
|
64
|
+
_setMetadata(agentId, metadata[i].metadataKey, metadata[i].metadataValue);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
emit Registered(agentId, agentUri, msg.sender);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// @inheritdoc IIdentityRegistry
|
|
71
|
+
function setMetadata(
|
|
72
|
+
uint256 agentId,
|
|
73
|
+
string memory metadataKey,
|
|
74
|
+
bytes memory metadataValue
|
|
75
|
+
) external {
|
|
76
|
+
_verifyAuthorization(agentId);
|
|
77
|
+
_setMetadata(agentId, metadataKey, metadataValue);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// @inheritdoc IIdentityRegistry
|
|
81
|
+
function setAgentUri(uint256 agentId, string calldata agentUri) external {
|
|
82
|
+
_verifyAuthorization(agentId);
|
|
83
|
+
_setAgentUri(agentId, agentUri);
|
|
84
|
+
emit UriUpdated(agentId, agentUri, msg.sender);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// @inheritdoc IIdentityRegistry
|
|
88
|
+
function getMetadata(
|
|
89
|
+
uint256 agentId,
|
|
90
|
+
string memory metadataKey
|
|
91
|
+
) external view returns (bytes memory) {
|
|
92
|
+
return _getMetadata(agentId, metadataKey);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// @inheritdoc IIdentityRegistry
|
|
96
|
+
function tokenURI(
|
|
97
|
+
uint256 agentId
|
|
98
|
+
) public view override(ERC721A, IIdentityRegistry) returns (string memory) {
|
|
99
|
+
if (!_exists(agentId)) revert IdentityRegistry__AgentDoesNotExist();
|
|
100
|
+
return _getAgentUri(agentId);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
|
|
104
|
+
/* INTERNAL FUNCTIONS */
|
|
105
|
+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
|
|
106
|
+
|
|
107
|
+
function _verifyMetadataLength(MetadataEntry[] calldata metadata) internal pure {
|
|
108
|
+
uint256 metadataLength = metadata.length;
|
|
109
|
+
if (metadataLength > MAX_METADATA_ENTRIES)
|
|
110
|
+
IdentityRegistry__TooManyMetadataEntries.selector.revertWith();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function _verifyAgent() internal view {
|
|
114
|
+
if (_balanceOf(msg.sender) > 0)
|
|
115
|
+
IdentityRegistry__AgentAlreadyPromoted.selector.revertWith();
|
|
116
|
+
if (_getLatestAppId(msg.sender) == EMPTY_UID)
|
|
117
|
+
IdentityRegistry__AgentNotRegistered.selector.revertWith();
|
|
118
|
+
if (_isBanned(msg.sender)) IdentityRegistry__AgentBanned.selector.revertWith();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function _verifyAuthorization(uint256 agentId) internal view {
|
|
122
|
+
address owner = _ownerOf(agentId);
|
|
123
|
+
if (
|
|
124
|
+
msg.sender != owner &&
|
|
125
|
+
!_isApprovedForAll(owner, msg.sender) &&
|
|
126
|
+
msg.sender != _getApproved(agentId)
|
|
127
|
+
) {
|
|
128
|
+
revert IdentityRegistry__NotAuthorized();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function _startTokenId() internal pure override returns (uint256) {
|
|
133
|
+
return 1;
|
|
134
|
+
}
|
|
135
|
+
}
|