@towns-protocol/contracts 0.0.413 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@towns-protocol/contracts",
3
- "version": "0.0.413",
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.413",
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": "6ed94c2e82a94946e75076a62b8e1ddc74e4b643"
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 {IAppFactory, IAppFactoryBase} from "src/apps/facets/factory/IAppFactory.sol";
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 {DeployAppRegistryFacet} from "../deployments/facets/DeployAppRegistryFacet.s.sol";
18
- import {DeployAppInstallerFacet} from "../deployments/facets/DeployAppInstallerFacet.s.sol";
19
- import {DeployAppFactoryFacet} from "../deployments/facets/DeployAppFactoryFacet.s.sol";
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 AppRegistry diamond address
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
- address appRegistryFacet = DeployAppRegistryFacet.deploy();
41
- console.log("AppRegistryFacet deployed at:", appRegistryFacet);
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
- address appInstallerFacet = DeployAppInstallerFacet.deploy();
44
- console.log("AppInstallerFacet deployed at:", appInstallerFacet);
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
- address appFactoryFacet = DeployAppFactoryFacet.deploy();
47
- console.log("AppFactoryFacet deployed at:", appFactoryFacet);
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
- // Add the cuts for the new facet implementations
50
- addCut(DeployAppRegistryFacet.makeCut(appRegistryFacet, FacetCutAction.Replace));
51
- addCut(DeployAppInstallerFacet.makeCut(appInstallerFacet, FacetCutAction.Replace));
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
- // Prepare initialization data for AppFactoryFacet with the beacon configuration
55
- IAppFactoryBase.Beacon[] memory beacons = new IAppFactoryBase.Beacon[](1);
56
- beacons[0] = IAppFactoryBase.Beacon({
57
- beaconId: beaconHelper.SIMPLE_APP_BEACON_ID(),
58
- beacon: simpleAppBeacon
59
- });
60
- bytes memory initData = DeployAppFactoryFacet.makeInitData(beacons, entryPoint);
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
- appFactoryFacet,
70
- initData
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
+ }