@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 +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
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IReputationRegistry} from "./IReputationRegistry.sol";
|
|
6
|
+
import {ISchemaResolver} from "@ethereum-attestation-service/eas-contracts/resolver/ISchemaResolver.sol";
|
|
7
|
+
|
|
8
|
+
// libraries
|
|
9
|
+
import {ReputationRegistryStorage} from "./ReputationRegistryStorage.sol";
|
|
10
|
+
|
|
11
|
+
// contracts
|
|
12
|
+
import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
|
|
13
|
+
import {ReputationRegistryBase} from "./ReputationRegistryBase.sol";
|
|
14
|
+
import {SchemaBase} from "../schema/SchemaBase.sol";
|
|
15
|
+
|
|
16
|
+
contract ReputationRegistryFacet is IReputationRegistry, ReputationRegistryBase, SchemaBase, Facet {
|
|
17
|
+
function __ReputationRegistry_init(
|
|
18
|
+
string calldata feedbackSchema,
|
|
19
|
+
string calldata responseSchema
|
|
20
|
+
) external onlyInitializing {
|
|
21
|
+
__ReputationRegistry_init_unchained(feedbackSchema, responseSchema);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function __ReputationRegistry_init_unchained(
|
|
25
|
+
string calldata feedbackSchema,
|
|
26
|
+
string calldata responseSchema
|
|
27
|
+
) internal {
|
|
28
|
+
bytes32 feedbackSchemaId = _registerSchema(
|
|
29
|
+
feedbackSchema,
|
|
30
|
+
ISchemaResolver(address(0)),
|
|
31
|
+
true
|
|
32
|
+
);
|
|
33
|
+
bytes32 responseSchemaId = _registerSchema(
|
|
34
|
+
responseSchema,
|
|
35
|
+
ISchemaResolver(address(0)),
|
|
36
|
+
true
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
ReputationRegistryStorage.Layout storage $ = ReputationRegistryStorage.getLayout();
|
|
40
|
+
$.feedbackSchemaId = feedbackSchemaId;
|
|
41
|
+
$.responseSchemaId = responseSchemaId;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// @inheritdoc IReputationRegistry
|
|
45
|
+
function giveFeedback(
|
|
46
|
+
uint256 agentId,
|
|
47
|
+
uint8 rating,
|
|
48
|
+
bytes32 tag1,
|
|
49
|
+
bytes32 tag2,
|
|
50
|
+
string calldata comment,
|
|
51
|
+
bytes32 commentHash
|
|
52
|
+
) external {
|
|
53
|
+
Feedback memory feedback;
|
|
54
|
+
feedback.rating = rating;
|
|
55
|
+
feedback.tag1 = tag1;
|
|
56
|
+
feedback.tag2 = tag2;
|
|
57
|
+
_giveFeedback(agentId, feedback);
|
|
58
|
+
emit NewFeedback(agentId, msg.sender, rating, tag1, tag2, comment, commentHash);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// @inheritdoc IReputationRegistry
|
|
62
|
+
function revokeFeedback(uint256 agentId, uint64 feedbackIndex) external {
|
|
63
|
+
_revokeFeedback(agentId, feedbackIndex);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// @inheritdoc IReputationRegistry
|
|
67
|
+
function appendResponse(
|
|
68
|
+
uint256 agentId,
|
|
69
|
+
address reviewerAddress,
|
|
70
|
+
uint64 feedbackIndex,
|
|
71
|
+
string calldata comment,
|
|
72
|
+
bytes32 commentHash
|
|
73
|
+
) external {
|
|
74
|
+
_appendResponse(agentId, reviewerAddress, feedbackIndex, comment, commentHash);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// @inheritdoc IReputationRegistry
|
|
78
|
+
function getIdentityRegistry() external view returns (address) {
|
|
79
|
+
return address(this);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// @inheritdoc IReputationRegistry
|
|
83
|
+
function getSummary(
|
|
84
|
+
uint256 agentId,
|
|
85
|
+
address[] calldata clientAddresses,
|
|
86
|
+
bytes32 tag1,
|
|
87
|
+
bytes32 tag2
|
|
88
|
+
) external view returns (uint64 count, uint8 averageScore) {
|
|
89
|
+
return _getSummary(agentId, clientAddresses, tag1, tag2);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// @inheritdoc IReputationRegistry
|
|
93
|
+
function readFeedback(
|
|
94
|
+
uint256 agentId,
|
|
95
|
+
address clientAddress,
|
|
96
|
+
uint64 index
|
|
97
|
+
) external view returns (uint8 score, bytes32 tag1, bytes32 tag2, bool isRevoked) {
|
|
98
|
+
(Feedback memory feedback, bool revoked) = _readFeedback(agentId, clientAddress, index);
|
|
99
|
+
return (feedback.rating, feedback.tag1, feedback.tag2, revoked);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// @inheritdoc IReputationRegistry
|
|
103
|
+
function readAllFeedback(
|
|
104
|
+
uint256 agentId,
|
|
105
|
+
address[] calldata clientAddresses,
|
|
106
|
+
bytes32 tag1,
|
|
107
|
+
bytes32 tag2,
|
|
108
|
+
bool includeRevoked
|
|
109
|
+
)
|
|
110
|
+
external
|
|
111
|
+
view
|
|
112
|
+
returns (
|
|
113
|
+
address[] memory,
|
|
114
|
+
uint8[] memory,
|
|
115
|
+
bytes32[] memory,
|
|
116
|
+
bytes32[] memory,
|
|
117
|
+
bool[] memory
|
|
118
|
+
)
|
|
119
|
+
{
|
|
120
|
+
AllFeedback memory allFeedback = _readAllFeedback(
|
|
121
|
+
agentId,
|
|
122
|
+
clientAddresses,
|
|
123
|
+
tag1,
|
|
124
|
+
tag2,
|
|
125
|
+
includeRevoked
|
|
126
|
+
);
|
|
127
|
+
return (
|
|
128
|
+
allFeedback.clients,
|
|
129
|
+
allFeedback.scores,
|
|
130
|
+
allFeedback.tag1s,
|
|
131
|
+
allFeedback.tag2s,
|
|
132
|
+
allFeedback.revokedStatuses
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// @inheritdoc IReputationRegistry
|
|
137
|
+
function getResponseCount(
|
|
138
|
+
uint256 agentId,
|
|
139
|
+
address clientAddress,
|
|
140
|
+
uint64 feedbackIndex,
|
|
141
|
+
address[] calldata responders
|
|
142
|
+
) external view returns (uint256) {
|
|
143
|
+
return _getResponseCount(agentId, clientAddress, feedbackIndex, responders);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// @inheritdoc IReputationRegistry
|
|
147
|
+
function getClients(uint256 agentId) external view returns (address[] memory) {
|
|
148
|
+
return _getClients(agentId);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/// @inheritdoc IReputationRegistry
|
|
152
|
+
function getLastIndex(uint256 agentId, address clientAddress) external view returns (uint64) {
|
|
153
|
+
return _getLastIndex(agentId, clientAddress);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
|
|
6
|
+
// libraries
|
|
7
|
+
import {EnumerableSetLib} from "solady/utils/EnumerableSetLib.sol";
|
|
8
|
+
|
|
9
|
+
// contracts
|
|
10
|
+
|
|
11
|
+
library ReputationRegistryStorage {
|
|
12
|
+
// keccak256(abi.encode(uint256(keccak256("apps.facets.reputation.storage")) - 1)) & ~bytes32(uint256(0xff))
|
|
13
|
+
bytes32 internal constant STORAGE_SLOT =
|
|
14
|
+
0x501066df74618204890aa19abae8215a74ecf89f414b33f7d3fe6e08c61d9100;
|
|
15
|
+
|
|
16
|
+
struct Layout {
|
|
17
|
+
// feedback schema ID
|
|
18
|
+
bytes32 feedbackSchemaId;
|
|
19
|
+
// response schema ID
|
|
20
|
+
bytes32 responseSchemaId;
|
|
21
|
+
// agentId => clientAddress => last feedback index
|
|
22
|
+
mapping(uint256 => mapping(address => uint64)) lastIndex;
|
|
23
|
+
// agentId => clientAddress => feedback index => attestation ID
|
|
24
|
+
mapping(uint256 => mapping(address => mapping(uint64 => bytes32))) feedback;
|
|
25
|
+
// agentId => clients
|
|
26
|
+
mapping(uint256 => EnumerableSetLib.AddressSet) clients;
|
|
27
|
+
// agentId => client => feedback index => responders
|
|
28
|
+
mapping(uint256 => mapping(address => mapping(uint64 => EnumerableSetLib.AddressSet))) responders;
|
|
29
|
+
// agentId => client => feedback index => sender => count of responses
|
|
30
|
+
mapping(uint256 => mapping(address => mapping(uint64 => mapping(address => uint256)))) responseCount;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getLayout() internal pure returns (Layout storage $) {
|
|
34
|
+
assembly {
|
|
35
|
+
$.slot := STORAGE_SLOT
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
pragma solidity ^0.8.29;
|
|
3
3
|
|
|
4
4
|
// interfaces
|
|
5
|
+
import {IIdentityRegistryBase} from "../../facets/identity/IIdentityRegistry.sol";
|
|
5
6
|
|
|
6
7
|
// libraries
|
|
7
8
|
|
|
@@ -26,6 +27,9 @@ interface ISimpleAppBase {
|
|
|
26
27
|
/// @notice Thrown when the currency is invalid
|
|
27
28
|
error SimpleApp__InvalidCurrency();
|
|
28
29
|
|
|
30
|
+
/// @notice Thrown when the agent is already promoted
|
|
31
|
+
error SimpleApp__AgentAlreadyPromoted();
|
|
32
|
+
|
|
29
33
|
/// @notice Emitted when the app is initialized
|
|
30
34
|
/// @param owner The owner of the app
|
|
31
35
|
/// @param client The client of the app
|
|
@@ -55,9 +59,23 @@ interface ISimpleAppBase {
|
|
|
55
59
|
/// @param oldClient The old client
|
|
56
60
|
/// @param newClient The new client
|
|
57
61
|
event ClientUpdated(address indexed oldClient, address indexed newClient);
|
|
62
|
+
|
|
63
|
+
/// @notice Emitted when the agent is promoted
|
|
64
|
+
/// @param owner The owner of the app
|
|
65
|
+
/// @param agentId The ID of the agent
|
|
66
|
+
event AgentPromoted(address indexed owner, uint256 indexed agentId);
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
interface ISimpleApp is ISimpleAppBase {
|
|
70
|
+
/// @notice Promotes the agent of the app
|
|
71
|
+
/// @param agentUri The URI pointing to the agent's registration file (e.g., ipfs://cid or https://domain.com/agent.json)
|
|
72
|
+
/// @param metadata The metadata of the agent
|
|
73
|
+
/// @return agentId The ID of the agent
|
|
74
|
+
function promoteAgent(
|
|
75
|
+
string calldata agentUri,
|
|
76
|
+
IIdentityRegistryBase.MetadataEntry[] calldata metadata
|
|
77
|
+
) external returns (uint256 agentId);
|
|
78
|
+
|
|
61
79
|
/// @notice Withdraws the ETH balance of the app to the recipient
|
|
62
80
|
/// @param recipient The address to withdraw the ETH to
|
|
63
81
|
function withdrawETH(address recipient) external;
|
|
@@ -74,4 +92,8 @@ interface ISimpleApp is ISimpleAppBase {
|
|
|
74
92
|
/// @notice Updates the permissions of the app
|
|
75
93
|
/// @param permissions The new permissions of the app
|
|
76
94
|
function updatePermissions(bytes32[] calldata permissions) external;
|
|
95
|
+
|
|
96
|
+
/// @notice Returns the ID of the agent
|
|
97
|
+
/// @return agentId The ID of the agent
|
|
98
|
+
function getAgentId() external view returns (uint256 agentId);
|
|
77
99
|
}
|
|
@@ -10,6 +10,7 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
|
10
10
|
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
11
11
|
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
|
|
12
12
|
import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol";
|
|
13
|
+
import {IIdentityRegistry, IIdentityRegistryBase} from "../../facets/identity/IIdentityRegistry.sol";
|
|
13
14
|
|
|
14
15
|
// contracts
|
|
15
16
|
import {BaseApp} from "../../../apps/BaseApp.sol";
|
|
@@ -22,13 +23,13 @@ import {ReentrancyGuardTransient} from "solady/utils/ReentrancyGuardTransient.so
|
|
|
22
23
|
|
|
23
24
|
// libraries
|
|
24
25
|
import {SimpleAppStorage} from "../../simple/app/SimpleAppStorage.sol";
|
|
25
|
-
import {SimpleAccountStorage} from "../account/SimpleAccountStorage.sol";
|
|
26
26
|
import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
|
|
27
27
|
import {CurrencyTransfer} from "../../../utils/libraries/CurrencyTransfer.sol";
|
|
28
28
|
import {SignatureCheckerLib} from "solady/utils/SignatureCheckerLib.sol";
|
|
29
29
|
|
|
30
30
|
contract SimpleAppFacet is
|
|
31
31
|
ISimpleApp,
|
|
32
|
+
IIdentityRegistryBase,
|
|
32
33
|
BaseApp,
|
|
33
34
|
SimpleAccountFacet,
|
|
34
35
|
IntrospectionFacet,
|
|
@@ -75,6 +76,19 @@ contract SimpleAppFacet is
|
|
|
75
76
|
/* Simple App Functions */
|
|
76
77
|
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
|
|
77
78
|
|
|
79
|
+
/// @inheritdoc ISimpleApp
|
|
80
|
+
function promoteAgent(
|
|
81
|
+
string calldata agentUri,
|
|
82
|
+
MetadataEntry[] calldata metadata
|
|
83
|
+
) external onlyOwner nonReentrant returns (uint256 agentId) {
|
|
84
|
+
SimpleAppStorage.Layout storage $ = SimpleAppStorage.getLayout();
|
|
85
|
+
if ($.agentId != 0) SimpleApp__AgentAlreadyPromoted.selector.revertWith();
|
|
86
|
+
address coordinator = _getCoordinator();
|
|
87
|
+
agentId = IIdentityRegistry(coordinator).register(agentUri, metadata);
|
|
88
|
+
$.agentId = agentId;
|
|
89
|
+
emit AgentPromoted(msg.sender, agentId);
|
|
90
|
+
}
|
|
91
|
+
|
|
78
92
|
/// @inheritdoc ISimpleApp
|
|
79
93
|
function withdrawETH(address recipient) external onlyOwner nonReentrant {
|
|
80
94
|
if (recipient == address(0)) SimpleApp__ZeroAddress.selector.revertWith();
|
|
@@ -121,6 +135,11 @@ contract SimpleAppFacet is
|
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
// Public functions
|
|
138
|
+
/// @inheritdoc ISimpleApp
|
|
139
|
+
function getAgentId() external view returns (uint256) {
|
|
140
|
+
return SimpleAppStorage.getLayout().agentId;
|
|
141
|
+
}
|
|
142
|
+
|
|
124
143
|
/// @inheritdoc IModule
|
|
125
144
|
function moduleId() public view returns (string memory) {
|
|
126
145
|
SimpleAppStorage.Layout storage $ = SimpleAppStorage.getLayout();
|