@agenticprimitives/contracts 0.1.0-alpha.2
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/AUDIT.md +67 -0
- package/CLAUDE.md +40 -0
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/deployments-anvil.json +1 -0
- package/deployments-base-sepolia.json +1 -0
- package/dist/abi/AgentNameAttributeResolver.json +798 -0
- package/dist/abi/AgentNamePredicates.json +1 -0
- package/dist/abi/AgentNameRegistry.json +826 -0
- package/dist/abi/AgentNameUniversalResolver.json +222 -0
- package/dist/abi/AgentProfilePredicates.json +1 -0
- package/dist/abi/AgentProfileResolver.json +1044 -0
- package/dist/abi/AgentRelationship.json +583 -0
- package/dist/abi/AgentRelationshipPredicates.json +1 -0
- package/dist/abi/AgenticGovernance.json +259 -0
- package/dist/abi/AllowedMethodsEnforcer.json +108 -0
- package/dist/abi/AllowedTargetsEnforcer.json +103 -0
- package/dist/abi/ApprovedHashRegistry.json +114 -0
- package/dist/abi/AttributeStorage.json +557 -0
- package/dist/abi/CaveatEnforcerBase.json +130 -0
- package/dist/abi/GovernanceManaged.json +43 -0
- package/dist/abi/IAttributeReader.json +98 -0
- package/dist/abi/ICaveatEnforcer.json +98 -0
- package/dist/abi/IDelegationManager.json +211 -0
- package/dist/abi/IERC7579Module.json +34 -0
- package/dist/abi/IERC7579ModuleLifecycle.json +60 -0
- package/dist/abi/IGovernanceView.json +34 -0
- package/dist/abi/MultiSendCallOnly.json +29 -0
- package/dist/abi/MultiSendCallOnlyHarness.json +42 -0
- package/dist/abi/OntologyTermRegistry.json +397 -0
- package/dist/abi/P256Verifier.json +1 -0
- package/dist/abi/PermissionlessSubregistry.json +207 -0
- package/dist/abi/RelationshipTypeRegistry.json +455 -0
- package/dist/abi/ShapeRegistry.json +627 -0
- package/dist/abi/SmartAgentModuleTypes.json +1 -0
- package/dist/abi/TimestampEnforcer.json +108 -0
- package/dist/abi/ValueEnforcer.json +103 -0
- package/dist/abi/WebAuthnLib.json +1 -0
- package/dist/abi/index.d.ts +35 -0
- package/dist/abi/index.js +35 -0
- package/package.json +48 -0
- package/spec.md +52 -0
- package/src/AgentAccount.sol +1374 -0
- package/src/AgentAccountFactory.sol +274 -0
- package/src/ApprovedHashRegistry.sol +57 -0
- package/src/IAgentAccount.sol +138 -0
- package/src/SmartAgentPaymaster.sol +281 -0
- package/src/UniversalSignatureValidator.sol +136 -0
- package/src/agency/DelegationManager.sol +374 -0
- package/src/agency/ICaveatEnforcer.sol +62 -0
- package/src/agency/IDelegationManager.sol +69 -0
- package/src/custody/CustodyPolicy.sol +892 -0
- package/src/custody/IERC7579Module.sol +60 -0
- package/src/enforcers/AllowedMethodsEnforcer.AUDIT.md +51 -0
- package/src/enforcers/AllowedMethodsEnforcer.sol +48 -0
- package/src/enforcers/AllowedTargetsEnforcer.AUDIT.md +49 -0
- package/src/enforcers/AllowedTargetsEnforcer.sol +44 -0
- package/src/enforcers/CaveatEnforcerBase.sol +19 -0
- package/src/enforcers/QuorumEnforcer.AUDIT.md +71 -0
- package/src/enforcers/QuorumEnforcer.sol +191 -0
- package/src/enforcers/TimestampEnforcer.AUDIT.md +50 -0
- package/src/enforcers/TimestampEnforcer.sol +43 -0
- package/src/enforcers/ValueEnforcer.AUDIT.md +51 -0
- package/src/enforcers/ValueEnforcer.sol +41 -0
- package/src/governance/AgenticGovernance.sol +140 -0
- package/src/governance/GovernanceManaged.sol +75 -0
- package/src/governance/IGovernance.sol +15 -0
- package/src/identity/AgentProfilePredicates.sol +40 -0
- package/src/identity/AgentProfileResolver.sol +194 -0
- package/src/libraries/MultiSendCallOnly.sol +95 -0
- package/src/libraries/P256Verifier.sol +47 -0
- package/src/libraries/SignatureSlotRecovery.sol +196 -0
- package/src/libraries/WebAuthnLib.sol +164 -0
- package/src/naming/AgentNameAttributeResolver.sol +95 -0
- package/src/naming/AgentNamePredicates.sol +74 -0
- package/src/naming/AgentNameRegistry.sol +362 -0
- package/src/naming/AgentNameUniversalResolver.sol +210 -0
- package/src/naming/PermissionlessSubregistry.sol +98 -0
- package/src/ontology/AttributeStorage.sol +289 -0
- package/src/ontology/OntologyTermRegistry.sol +146 -0
- package/src/ontology/ShapeRegistry.sol +240 -0
- package/src/relationships/AgentRelationship.sol +289 -0
- package/src/relationships/AgentRelationshipPredicates.sol +44 -0
- package/src/relationships/RelationshipTypeRegistry.sol +143 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.28;
|
|
3
|
+
|
|
4
|
+
import "./OntologyTermRegistry.sol";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @title AttributeStorage
|
|
8
|
+
* @notice Abstract base for typed, ontology-governed attribute storage.
|
|
9
|
+
* Each contract that inherits gets its OWN copy of the mappings
|
|
10
|
+
* (no shared backend). Subclasses choose their auth model; the
|
|
11
|
+
* setters are `internal` so the subclass enforces preconditions.
|
|
12
|
+
*
|
|
13
|
+
* Eight typed value families, identified by `uint8` datatype
|
|
14
|
+
* discriminators:
|
|
15
|
+
* 1 string, 2 address, 3 bool, 4 uint256,
|
|
16
|
+
* 5 bytes32, 6 string[], 7 address[], 8 bytes32[]
|
|
17
|
+
*
|
|
18
|
+
* Predicate enumeration: `predicatesOf(subject)` returns the list of
|
|
19
|
+
* predicates ever set on a subject (insertion order, no duplicates).
|
|
20
|
+
* `subjectVersion(subject)` is bumped on every write — off-chain syncs
|
|
21
|
+
* (RDF mirrors, indexers) use it as a watermark for diff-aware emission.
|
|
22
|
+
*
|
|
23
|
+
* Predicate governance: every internal setter checks the predicate is
|
|
24
|
+
* ACTIVE in the bound `ONTOLOGY` (an OntologyTermRegistry instance
|
|
25
|
+
* passed at construction). Bypass-the-SDK writes with unknown
|
|
26
|
+
* predicates revert at the chain edge.
|
|
27
|
+
*
|
|
28
|
+
* Adapted from smart-agent
|
|
29
|
+
* (`packages/contracts/src/AttributeStorage.sol`, 283 LOC) —
|
|
30
|
+
* structurally identical.
|
|
31
|
+
*/
|
|
32
|
+
abstract contract AttributeStorage {
|
|
33
|
+
OntologyTermRegistry public immutable ONTOLOGY;
|
|
34
|
+
|
|
35
|
+
// ─── Datatype discriminators ────────────────────────────────────
|
|
36
|
+
uint8 internal constant DT_STRING = 1;
|
|
37
|
+
uint8 internal constant DT_ADDRESS = 2;
|
|
38
|
+
uint8 internal constant DT_BOOL = 3;
|
|
39
|
+
uint8 internal constant DT_UINT256 = 4;
|
|
40
|
+
uint8 internal constant DT_BYTES32 = 5;
|
|
41
|
+
uint8 internal constant DT_STRING_ARR = 6;
|
|
42
|
+
uint8 internal constant DT_ADDRESS_ARR = 7;
|
|
43
|
+
uint8 internal constant DT_BYTES32_ARR = 8;
|
|
44
|
+
|
|
45
|
+
// ─── Typed value families ───────────────────────────────────────
|
|
46
|
+
mapping(bytes32 => mapping(bytes32 => string)) private _string;
|
|
47
|
+
mapping(bytes32 => mapping(bytes32 => address)) private _address;
|
|
48
|
+
mapping(bytes32 => mapping(bytes32 => bool)) private _bool;
|
|
49
|
+
mapping(bytes32 => mapping(bytes32 => uint256)) private _uint;
|
|
50
|
+
mapping(bytes32 => mapping(bytes32 => bytes32)) private _bytes32;
|
|
51
|
+
mapping(bytes32 => mapping(bytes32 => string[])) private _stringArr;
|
|
52
|
+
mapping(bytes32 => mapping(bytes32 => address[])) private _addressArr;
|
|
53
|
+
mapping(bytes32 => mapping(bytes32 => bytes32[])) private _bytes32Arr;
|
|
54
|
+
|
|
55
|
+
// ─── Indexing / metadata ────────────────────────────────────────
|
|
56
|
+
mapping(bytes32 => bytes32[]) private _predicates;
|
|
57
|
+
mapping(bytes32 => mapping(bytes32 => bool)) private _isSet;
|
|
58
|
+
mapping(bytes32 => mapping(bytes32 => uint8)) private _datatype;
|
|
59
|
+
mapping(bytes32 => mapping(bytes32 => uint64)) private _updatedAt;
|
|
60
|
+
mapping(bytes32 => uint64) private _subjectVersion;
|
|
61
|
+
bytes32[] private _allSubjects;
|
|
62
|
+
mapping(bytes32 => bool) private _subjectKnown;
|
|
63
|
+
|
|
64
|
+
// ─── Events ─────────────────────────────────────────────────────
|
|
65
|
+
event AttributeSet(bytes32 indexed subject, bytes32 indexed predicate, uint8 datatype, uint64 version);
|
|
66
|
+
event AttributeUnset(bytes32 indexed subject, bytes32 indexed predicate, uint64 version);
|
|
67
|
+
event AttributeAppended(bytes32 indexed subject, bytes32 indexed predicate, uint8 datatype, uint64 version);
|
|
68
|
+
event SubjectFirstSeen(bytes32 indexed subject);
|
|
69
|
+
|
|
70
|
+
// ─── Errors ─────────────────────────────────────────────────────
|
|
71
|
+
error PredicateNotActive();
|
|
72
|
+
error AttributeNotSet();
|
|
73
|
+
|
|
74
|
+
constructor(address ontologyRegistry) {
|
|
75
|
+
ONTOLOGY = OntologyTermRegistry(ontologyRegistry);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ─── Public datatype discriminator readers (for ShapeRegistry) ──
|
|
79
|
+
|
|
80
|
+
function DT_STRING_PUB() external pure returns (uint8) { return DT_STRING; }
|
|
81
|
+
function DT_ADDRESS_PUB() external pure returns (uint8) { return DT_ADDRESS; }
|
|
82
|
+
function DT_BOOL_PUB() external pure returns (uint8) { return DT_BOOL; }
|
|
83
|
+
function DT_UINT256_PUB() external pure returns (uint8) { return DT_UINT256; }
|
|
84
|
+
function DT_BYTES32_PUB() external pure returns (uint8) { return DT_BYTES32; }
|
|
85
|
+
function DT_STRING_ARR_PUB() external pure returns (uint8) { return DT_STRING_ARR; }
|
|
86
|
+
function DT_ADDRESS_ARR_PUB() external pure returns (uint8) { return DT_ADDRESS_ARR; }
|
|
87
|
+
function DT_BYTES32_ARR_PUB() external pure returns (uint8) { return DT_BYTES32_ARR; }
|
|
88
|
+
|
|
89
|
+
// ─── Internal setters (subclass-only) ───────────────────────────
|
|
90
|
+
|
|
91
|
+
function _setString(bytes32 subject, bytes32 predicate, string memory value) internal {
|
|
92
|
+
_requirePredicate(predicate);
|
|
93
|
+
_string[subject][predicate] = value;
|
|
94
|
+
_record(subject, predicate, DT_STRING);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function _setAddress(bytes32 subject, bytes32 predicate, address value) internal {
|
|
98
|
+
_requirePredicate(predicate);
|
|
99
|
+
_address[subject][predicate] = value;
|
|
100
|
+
_record(subject, predicate, DT_ADDRESS);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function _setBool(bytes32 subject, bytes32 predicate, bool value) internal {
|
|
104
|
+
_requirePredicate(predicate);
|
|
105
|
+
_bool[subject][predicate] = value;
|
|
106
|
+
_record(subject, predicate, DT_BOOL);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function _setUint(bytes32 subject, bytes32 predicate, uint256 value) internal {
|
|
110
|
+
_requirePredicate(predicate);
|
|
111
|
+
_uint[subject][predicate] = value;
|
|
112
|
+
_record(subject, predicate, DT_UINT256);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function _setBytes32(bytes32 subject, bytes32 predicate, bytes32 value) internal {
|
|
116
|
+
_requirePredicate(predicate);
|
|
117
|
+
_bytes32[subject][predicate] = value;
|
|
118
|
+
_record(subject, predicate, DT_BYTES32);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function _setStringArr(bytes32 subject, bytes32 predicate, string[] memory values) internal {
|
|
122
|
+
_requirePredicate(predicate);
|
|
123
|
+
delete _stringArr[subject][predicate];
|
|
124
|
+
for (uint256 i = 0; i < values.length; i++) {
|
|
125
|
+
_stringArr[subject][predicate].push(values[i]);
|
|
126
|
+
}
|
|
127
|
+
_record(subject, predicate, DT_STRING_ARR);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function _setAddressArr(bytes32 subject, bytes32 predicate, address[] memory values) internal {
|
|
131
|
+
_requirePredicate(predicate);
|
|
132
|
+
delete _addressArr[subject][predicate];
|
|
133
|
+
for (uint256 i = 0; i < values.length; i++) {
|
|
134
|
+
_addressArr[subject][predicate].push(values[i]);
|
|
135
|
+
}
|
|
136
|
+
_record(subject, predicate, DT_ADDRESS_ARR);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function _setBytes32Arr(bytes32 subject, bytes32 predicate, bytes32[] memory values) internal {
|
|
140
|
+
_requirePredicate(predicate);
|
|
141
|
+
delete _bytes32Arr[subject][predicate];
|
|
142
|
+
for (uint256 i = 0; i < values.length; i++) {
|
|
143
|
+
_bytes32Arr[subject][predicate].push(values[i]);
|
|
144
|
+
}
|
|
145
|
+
_record(subject, predicate, DT_BYTES32_ARR);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function _appendString(bytes32 subject, bytes32 predicate, string memory value) internal {
|
|
149
|
+
_requirePredicate(predicate);
|
|
150
|
+
_stringArr[subject][predicate].push(value);
|
|
151
|
+
_recordAppend(subject, predicate, DT_STRING_ARR);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function _appendAddress(bytes32 subject, bytes32 predicate, address value) internal {
|
|
155
|
+
_requirePredicate(predicate);
|
|
156
|
+
_addressArr[subject][predicate].push(value);
|
|
157
|
+
_recordAppend(subject, predicate, DT_ADDRESS_ARR);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function _appendBytes32(bytes32 subject, bytes32 predicate, bytes32 value) internal {
|
|
161
|
+
_requirePredicate(predicate);
|
|
162
|
+
_bytes32Arr[subject][predicate].push(value);
|
|
163
|
+
_recordAppend(subject, predicate, DT_BYTES32_ARR);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function _unset(bytes32 subject, bytes32 predicate) internal {
|
|
167
|
+
if (!_isSet[subject][predicate]) revert AttributeNotSet();
|
|
168
|
+
uint8 dt = _datatype[subject][predicate];
|
|
169
|
+
if (dt == DT_STRING) delete _string[subject][predicate];
|
|
170
|
+
else if (dt == DT_ADDRESS) delete _address[subject][predicate];
|
|
171
|
+
else if (dt == DT_BOOL) delete _bool[subject][predicate];
|
|
172
|
+
else if (dt == DT_UINT256) delete _uint[subject][predicate];
|
|
173
|
+
else if (dt == DT_BYTES32) delete _bytes32[subject][predicate];
|
|
174
|
+
else if (dt == DT_STRING_ARR) delete _stringArr[subject][predicate];
|
|
175
|
+
else if (dt == DT_ADDRESS_ARR) delete _addressArr[subject][predicate];
|
|
176
|
+
else if (dt == DT_BYTES32_ARR) delete _bytes32Arr[subject][predicate];
|
|
177
|
+
_isSet[subject][predicate] = false;
|
|
178
|
+
delete _datatype[subject][predicate];
|
|
179
|
+
delete _updatedAt[subject][predicate];
|
|
180
|
+
uint64 v = _bumpVersion(subject);
|
|
181
|
+
emit AttributeUnset(subject, predicate, v);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ─── Public getters ─────────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
function getString(bytes32 subject, bytes32 predicate) external view returns (string memory) {
|
|
187
|
+
return _string[subject][predicate];
|
|
188
|
+
}
|
|
189
|
+
function getAddress(bytes32 subject, bytes32 predicate) external view returns (address) {
|
|
190
|
+
return _address[subject][predicate];
|
|
191
|
+
}
|
|
192
|
+
function getBool(bytes32 subject, bytes32 predicate) external view returns (bool) {
|
|
193
|
+
return _bool[subject][predicate];
|
|
194
|
+
}
|
|
195
|
+
function getUint(bytes32 subject, bytes32 predicate) external view returns (uint256) {
|
|
196
|
+
return _uint[subject][predicate];
|
|
197
|
+
}
|
|
198
|
+
function getBytes32(bytes32 subject, bytes32 predicate) external view returns (bytes32) {
|
|
199
|
+
return _bytes32[subject][predicate];
|
|
200
|
+
}
|
|
201
|
+
function getStringArr(bytes32 subject, bytes32 predicate) external view returns (string[] memory) {
|
|
202
|
+
return _stringArr[subject][predicate];
|
|
203
|
+
}
|
|
204
|
+
function getAddressArr(bytes32 subject, bytes32 predicate) external view returns (address[] memory) {
|
|
205
|
+
return _addressArr[subject][predicate];
|
|
206
|
+
}
|
|
207
|
+
function getBytes32Arr(bytes32 subject, bytes32 predicate) external view returns (bytes32[] memory) {
|
|
208
|
+
return _bytes32Arr[subject][predicate];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function predicatesOf(bytes32 subject) external view returns (bytes32[] memory) {
|
|
212
|
+
return _predicates[subject];
|
|
213
|
+
}
|
|
214
|
+
function datatypeOf(bytes32 subject, bytes32 predicate) external view returns (uint8) {
|
|
215
|
+
return _datatype[subject][predicate];
|
|
216
|
+
}
|
|
217
|
+
function updatedAt(bytes32 subject, bytes32 predicate) external view returns (uint64) {
|
|
218
|
+
return _updatedAt[subject][predicate];
|
|
219
|
+
}
|
|
220
|
+
function isSet(bytes32 subject, bytes32 predicate) external view returns (bool) {
|
|
221
|
+
return _isSet[subject][predicate];
|
|
222
|
+
}
|
|
223
|
+
function subjectVersion(bytes32 subject) external view returns (uint64) {
|
|
224
|
+
return _subjectVersion[subject];
|
|
225
|
+
}
|
|
226
|
+
function allSubjects() external view returns (bytes32[] memory) {
|
|
227
|
+
return _allSubjects;
|
|
228
|
+
}
|
|
229
|
+
function subjectCount() external view returns (uint256) {
|
|
230
|
+
return _allSubjects.length;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ─── Internal helpers ───────────────────────────────────────────
|
|
234
|
+
|
|
235
|
+
function _requirePredicate(bytes32 predicate) internal view {
|
|
236
|
+
if (!ONTOLOGY.isActive(predicate)) revert PredicateNotActive();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function _record(bytes32 subject, bytes32 predicate, uint8 dt) internal {
|
|
240
|
+
_trackSubject(subject);
|
|
241
|
+
if (!_isSet[subject][predicate]) {
|
|
242
|
+
_predicates[subject].push(predicate);
|
|
243
|
+
_isSet[subject][predicate] = true;
|
|
244
|
+
}
|
|
245
|
+
_datatype[subject][predicate] = dt;
|
|
246
|
+
uint64 v = _bumpVersion(subject);
|
|
247
|
+
_updatedAt[subject][predicate] = v;
|
|
248
|
+
emit AttributeSet(subject, predicate, dt, v);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function _recordAppend(bytes32 subject, bytes32 predicate, uint8 dt) internal {
|
|
252
|
+
_trackSubject(subject);
|
|
253
|
+
if (!_isSet[subject][predicate]) {
|
|
254
|
+
_predicates[subject].push(predicate);
|
|
255
|
+
_isSet[subject][predicate] = true;
|
|
256
|
+
}
|
|
257
|
+
_datatype[subject][predicate] = dt;
|
|
258
|
+
uint64 v = _bumpVersion(subject);
|
|
259
|
+
_updatedAt[subject][predicate] = v;
|
|
260
|
+
emit AttributeAppended(subject, predicate, dt, v);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function _trackSubject(bytes32 subject) internal {
|
|
264
|
+
if (!_subjectKnown[subject]) {
|
|
265
|
+
_subjectKnown[subject] = true;
|
|
266
|
+
_allSubjects.push(subject);
|
|
267
|
+
emit SubjectFirstSeen(subject);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function _bumpVersion(bytes32 subject) internal returns (uint64) {
|
|
272
|
+
uint64 next = _subjectVersion[subject] + 1;
|
|
273
|
+
_subjectVersion[subject] = next;
|
|
274
|
+
return next;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* @title IAttributeReader
|
|
280
|
+
* @notice Read-only interface every AttributeStorage subclass exposes.
|
|
281
|
+
* ShapeRegistry takes this so a single shape definition can
|
|
282
|
+
* validate any registry's subject.
|
|
283
|
+
*/
|
|
284
|
+
interface IAttributeReader {
|
|
285
|
+
function isSet(bytes32 subject, bytes32 predicate) external view returns (bool);
|
|
286
|
+
function datatypeOf(bytes32 subject, bytes32 predicate) external view returns (uint8);
|
|
287
|
+
function getBytes32(bytes32 subject, bytes32 predicate) external view returns (bytes32);
|
|
288
|
+
function getBytes32Arr(bytes32 subject, bytes32 predicate) external view returns (bytes32[] memory);
|
|
289
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.28;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title OntologyTermRegistry
|
|
6
|
+
* @notice Governed registry of valid ontology predicates for the
|
|
7
|
+
* agenticprimitives trust fabric.
|
|
8
|
+
*
|
|
9
|
+
* Controls which predicate bytes32 ids may appear on any
|
|
10
|
+
* `AttributeStorage` subclass (naming records, future relationships /
|
|
11
|
+
* identity records). Each term maps a `bytes32` id (keccak256 of a
|
|
12
|
+
* CURIE like `"atl:displayName"`) to its full URI, human label, and
|
|
13
|
+
* expected datatype family.
|
|
14
|
+
*
|
|
15
|
+
* Governance: only `governor` may register or deactivate terms.
|
|
16
|
+
* Deployer is the bootstrap governor; rotation to a multi-sig /
|
|
17
|
+
* Smart Agent CustodyPolicy is a single `transferGovernor` call.
|
|
18
|
+
*
|
|
19
|
+
* Adapted from smart-agent
|
|
20
|
+
* (`packages/contracts/src/OntologyTermRegistry.sol`, 136 LOC) —
|
|
21
|
+
* structurally identical; copied here to keep the agenticprimitives
|
|
22
|
+
* trust fabric self-contained.
|
|
23
|
+
*/
|
|
24
|
+
contract OntologyTermRegistry {
|
|
25
|
+
struct Term {
|
|
26
|
+
bytes32 id; // keccak256("atl:displayName")
|
|
27
|
+
string curie; // "atl:displayName"
|
|
28
|
+
string uri; // "https://agentictrust.io/ontology/core#displayName"
|
|
29
|
+
string label; // "Display Name"
|
|
30
|
+
string datatype; // "string" | "address" | "bool" | "uint256" | "bytes32" | "string[]" | "address[]" | "bytes32[]"
|
|
31
|
+
bool active;
|
|
32
|
+
uint256 registeredAt;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
address public governor;
|
|
36
|
+
mapping(bytes32 => Term) private _terms;
|
|
37
|
+
bytes32[] private _termIds;
|
|
38
|
+
|
|
39
|
+
event TermRegistered(bytes32 indexed id, string curie, string uri);
|
|
40
|
+
event TermDeactivated(bytes32 indexed id);
|
|
41
|
+
event TermActivated(bytes32 indexed id);
|
|
42
|
+
event GovernorTransferred(address indexed oldGovernor, address indexed newGovernor);
|
|
43
|
+
|
|
44
|
+
error NotGovernor();
|
|
45
|
+
error TermExists();
|
|
46
|
+
error TermNotFound();
|
|
47
|
+
error ZeroGovernor();
|
|
48
|
+
|
|
49
|
+
modifier onlyGovernor() {
|
|
50
|
+
if (msg.sender != governor) revert NotGovernor();
|
|
51
|
+
_;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
constructor(address governor_) {
|
|
55
|
+
if (governor_ == address(0)) revert ZeroGovernor();
|
|
56
|
+
governor = governor_;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function transferGovernor(address newGovernor) external onlyGovernor {
|
|
60
|
+
if (newGovernor == address(0)) revert ZeroGovernor();
|
|
61
|
+
emit GovernorTransferred(governor, newGovernor);
|
|
62
|
+
governor = newGovernor;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function registerTerm(
|
|
66
|
+
bytes32 id,
|
|
67
|
+
string calldata curie,
|
|
68
|
+
string calldata uri,
|
|
69
|
+
string calldata label,
|
|
70
|
+
string calldata datatype
|
|
71
|
+
) external onlyGovernor {
|
|
72
|
+
if (_terms[id].registeredAt != 0) revert TermExists();
|
|
73
|
+
_terms[id] = Term({
|
|
74
|
+
id: id,
|
|
75
|
+
curie: curie,
|
|
76
|
+
uri: uri,
|
|
77
|
+
label: label,
|
|
78
|
+
datatype: datatype,
|
|
79
|
+
active: true,
|
|
80
|
+
registeredAt: block.timestamp
|
|
81
|
+
});
|
|
82
|
+
_termIds.push(id);
|
|
83
|
+
emit TermRegistered(id, curie, uri);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function registerTermBatch(
|
|
87
|
+
bytes32[] calldata ids,
|
|
88
|
+
string[] calldata curies,
|
|
89
|
+
string[] calldata uris,
|
|
90
|
+
string[] calldata labels,
|
|
91
|
+
string[] calldata datatypes
|
|
92
|
+
) external onlyGovernor {
|
|
93
|
+
for (uint256 i = 0; i < ids.length; i++) {
|
|
94
|
+
if (_terms[ids[i]].registeredAt != 0) continue; // skip existing
|
|
95
|
+
_terms[ids[i]] = Term({
|
|
96
|
+
id: ids[i],
|
|
97
|
+
curie: curies[i],
|
|
98
|
+
uri: uris[i],
|
|
99
|
+
label: labels[i],
|
|
100
|
+
datatype: datatypes[i],
|
|
101
|
+
active: true,
|
|
102
|
+
registeredAt: block.timestamp
|
|
103
|
+
});
|
|
104
|
+
_termIds.push(ids[i]);
|
|
105
|
+
emit TermRegistered(ids[i], curies[i], uris[i]);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function deactivateTerm(bytes32 id) external onlyGovernor {
|
|
110
|
+
if (_terms[id].registeredAt == 0) revert TermNotFound();
|
|
111
|
+
_terms[id].active = false;
|
|
112
|
+
emit TermDeactivated(id);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function activateTerm(bytes32 id) external onlyGovernor {
|
|
116
|
+
if (_terms[id].registeredAt == 0) revert TermNotFound();
|
|
117
|
+
_terms[id].active = true;
|
|
118
|
+
emit TermActivated(id);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ─── Queries ────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
function getTerm(bytes32 id) external view returns (Term memory) {
|
|
124
|
+
return _terms[id];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isRegistered(bytes32 id) external view returns (bool) {
|
|
128
|
+
return _terms[id].registeredAt != 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function isActive(bytes32 id) external view returns (bool) {
|
|
132
|
+
return _terms[id].active;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function termCount() external view returns (uint256) {
|
|
136
|
+
return _termIds.length;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function getTermAt(uint256 index) external view returns (Term memory) {
|
|
140
|
+
return _terms[_termIds[index]];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function getAllTermIds() external view returns (bytes32[] memory) {
|
|
144
|
+
return _termIds;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.28;
|
|
3
|
+
|
|
4
|
+
import "./AttributeStorage.sol";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @title ShapeRegistry
|
|
8
|
+
* @notice SHACL-inspired class shape constraints. Decoupled from any
|
|
9
|
+
* specific store: every validation call takes the reader's
|
|
10
|
+
* address, so a single registry can hold shapes that validate
|
|
11
|
+
* against any AttributeStorage subclass (naming records today,
|
|
12
|
+
* relationship + identity records in Phase 3+).
|
|
13
|
+
*
|
|
14
|
+
* Supported SHACL subset:
|
|
15
|
+
* `sh:path` → predicate id
|
|
16
|
+
* `sh:datatype` → AttributeStorage datatype family discriminator (`DT_*`)
|
|
17
|
+
* `sh:minCount` / `sh:maxCount` → cardinality enum
|
|
18
|
+
* `sh:in` → enumSetId referencing the contract's stored allowed values
|
|
19
|
+
* `sh:class` → expectedClass id (informational; off-chain class IRI hash)
|
|
20
|
+
*
|
|
21
|
+
* Adapted from smart-agent
|
|
22
|
+
* (`packages/contracts/src/ShapeRegistry.sol`, 231 LOC) —
|
|
23
|
+
* structurally identical.
|
|
24
|
+
*/
|
|
25
|
+
contract ShapeRegistry {
|
|
26
|
+
address public governor;
|
|
27
|
+
|
|
28
|
+
enum Cardinality {
|
|
29
|
+
OPTIONAL, // 0..1
|
|
30
|
+
REQUIRED_ONE, // 1..1
|
|
31
|
+
REQUIRED_MANY, // 1..*
|
|
32
|
+
OPTIONAL_MANY // 0..*
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
struct PropertyConstraint {
|
|
36
|
+
bytes32 predicate;
|
|
37
|
+
uint8 expectedDatatype;
|
|
38
|
+
Cardinality cardinality;
|
|
39
|
+
bytes32 enumSetId;
|
|
40
|
+
bytes32 expectedClass;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
struct Shape {
|
|
44
|
+
bytes32 classId;
|
|
45
|
+
string shapeURI;
|
|
46
|
+
bytes32 shapeHash;
|
|
47
|
+
uint16 version;
|
|
48
|
+
bool active;
|
|
49
|
+
bool exists;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
mapping(bytes32 => Shape) private _shapes;
|
|
53
|
+
mapping(bytes32 => PropertyConstraint[]) private _props;
|
|
54
|
+
mapping(bytes32 => bytes32[]) private _enumValues;
|
|
55
|
+
mapping(bytes32 => mapping(bytes32 => bool)) private _enumContains;
|
|
56
|
+
bytes32[] private _classIds;
|
|
57
|
+
|
|
58
|
+
event ShapeDefined(bytes32 indexed classId, uint16 version, string shapeURI, bytes32 shapeHash);
|
|
59
|
+
event ShapeUpdated(bytes32 indexed classId, uint16 version, bytes32 shapeHash);
|
|
60
|
+
event ShapeDeactivated(bytes32 indexed classId);
|
|
61
|
+
event ShapeActivated(bytes32 indexed classId);
|
|
62
|
+
event EnumSetDefined(bytes32 indexed enumSetId, uint256 valueCount);
|
|
63
|
+
event GovernorTransferred(address indexed previousGovernor, address indexed newGovernor);
|
|
64
|
+
|
|
65
|
+
error NotGovernor();
|
|
66
|
+
error ShapeAlreadyDefined();
|
|
67
|
+
error ShapeNotDefined();
|
|
68
|
+
error ShapeNotActive();
|
|
69
|
+
error MissingRequiredProperty(bytes32 predicate);
|
|
70
|
+
error WrongDatatype(bytes32 predicate, uint8 actual, uint8 expected);
|
|
71
|
+
error EnumValueNotAllowed(bytes32 predicate, bytes32 actualValue);
|
|
72
|
+
error EnumSetEmpty();
|
|
73
|
+
error ZeroGovernor();
|
|
74
|
+
|
|
75
|
+
modifier onlyGovernor() {
|
|
76
|
+
if (msg.sender != governor) revert NotGovernor();
|
|
77
|
+
_;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
constructor(address governor_) {
|
|
81
|
+
if (governor_ == address(0)) revert ZeroGovernor();
|
|
82
|
+
governor = governor_;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function transferGovernor(address newGovernor) external onlyGovernor {
|
|
86
|
+
if (newGovernor == address(0)) revert ZeroGovernor();
|
|
87
|
+
emit GovernorTransferred(governor, newGovernor);
|
|
88
|
+
governor = newGovernor;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ─── Shape definition ───────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
function defineShape(
|
|
94
|
+
bytes32 classId,
|
|
95
|
+
PropertyConstraint[] calldata props,
|
|
96
|
+
string calldata shapeURI,
|
|
97
|
+
bytes32 shapeHash
|
|
98
|
+
) external onlyGovernor returns (uint16) {
|
|
99
|
+
if (_shapes[classId].exists) revert ShapeAlreadyDefined();
|
|
100
|
+
_shapes[classId] = Shape({
|
|
101
|
+
classId: classId,
|
|
102
|
+
shapeURI: shapeURI,
|
|
103
|
+
shapeHash: shapeHash,
|
|
104
|
+
version: 1,
|
|
105
|
+
active: true,
|
|
106
|
+
exists: true
|
|
107
|
+
});
|
|
108
|
+
_classIds.push(classId);
|
|
109
|
+
for (uint256 i = 0; i < props.length; i++) {
|
|
110
|
+
_props[classId].push(props[i]);
|
|
111
|
+
}
|
|
112
|
+
emit ShapeDefined(classId, 1, shapeURI, shapeHash);
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function updateShape(
|
|
117
|
+
bytes32 classId,
|
|
118
|
+
PropertyConstraint[] calldata props,
|
|
119
|
+
string calldata shapeURI,
|
|
120
|
+
bytes32 shapeHash
|
|
121
|
+
) external onlyGovernor returns (uint16) {
|
|
122
|
+
Shape storage s = _shapes[classId];
|
|
123
|
+
if (!s.exists) revert ShapeNotDefined();
|
|
124
|
+
s.version += 1;
|
|
125
|
+
s.shapeURI = shapeURI;
|
|
126
|
+
s.shapeHash = shapeHash;
|
|
127
|
+
delete _props[classId];
|
|
128
|
+
for (uint256 i = 0; i < props.length; i++) {
|
|
129
|
+
_props[classId].push(props[i]);
|
|
130
|
+
}
|
|
131
|
+
emit ShapeUpdated(classId, s.version, shapeHash);
|
|
132
|
+
return s.version;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function deactivateShape(bytes32 classId) external onlyGovernor {
|
|
136
|
+
Shape storage s = _shapes[classId];
|
|
137
|
+
if (!s.exists) revert ShapeNotDefined();
|
|
138
|
+
s.active = false;
|
|
139
|
+
emit ShapeDeactivated(classId);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function activateShape(bytes32 classId) external onlyGovernor {
|
|
143
|
+
Shape storage s = _shapes[classId];
|
|
144
|
+
if (!s.exists) revert ShapeNotDefined();
|
|
145
|
+
s.active = true;
|
|
146
|
+
emit ShapeActivated(classId);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function defineEnumSet(bytes32 enumSetId, bytes32[] calldata allowedValues) external onlyGovernor {
|
|
150
|
+
if (allowedValues.length == 0) revert EnumSetEmpty();
|
|
151
|
+
bytes32[] storage existing = _enumValues[enumSetId];
|
|
152
|
+
for (uint256 i = 0; i < existing.length; i++) {
|
|
153
|
+
_enumContains[enumSetId][existing[i]] = false;
|
|
154
|
+
}
|
|
155
|
+
delete _enumValues[enumSetId];
|
|
156
|
+
for (uint256 i = 0; i < allowedValues.length; i++) {
|
|
157
|
+
_enumValues[enumSetId].push(allowedValues[i]);
|
|
158
|
+
_enumContains[enumSetId][allowedValues[i]] = true;
|
|
159
|
+
}
|
|
160
|
+
emit EnumSetDefined(enumSetId, allowedValues.length);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ─── Validation ─────────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
/// @notice Validate `subject` in `store` against the shape `classId`.
|
|
166
|
+
/// Reverts with a specific error if any constraint fails.
|
|
167
|
+
function validateSubject(bytes32 classId, bytes32 subject, address store) external view {
|
|
168
|
+
_validate(classId, subject, IAttributeReader(store));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/// @notice Try-validate variant: returns false instead of reverting.
|
|
172
|
+
function isValid(bytes32 classId, bytes32 subject, address store) external view returns (bool) {
|
|
173
|
+
try this.validateSubject(classId, subject, store) {
|
|
174
|
+
return true;
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function _validate(bytes32 classId, bytes32 subject, IAttributeReader store) internal view {
|
|
181
|
+
Shape storage s = _shapes[classId];
|
|
182
|
+
if (!s.exists) revert ShapeNotDefined();
|
|
183
|
+
if (!s.active) revert ShapeNotActive();
|
|
184
|
+
|
|
185
|
+
PropertyConstraint[] storage props = _props[classId];
|
|
186
|
+
for (uint256 i = 0; i < props.length; i++) {
|
|
187
|
+
PropertyConstraint storage p = props[i];
|
|
188
|
+
bool present = store.isSet(subject, p.predicate);
|
|
189
|
+
bool required = p.cardinality == Cardinality.REQUIRED_ONE
|
|
190
|
+
|| p.cardinality == Cardinality.REQUIRED_MANY;
|
|
191
|
+
|
|
192
|
+
if (!present) {
|
|
193
|
+
if (required) revert MissingRequiredProperty(p.predicate);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
uint8 actualDt = store.datatypeOf(subject, p.predicate);
|
|
198
|
+
if (actualDt != p.expectedDatatype) {
|
|
199
|
+
revert WrongDatatype(p.predicate, actualDt, p.expectedDatatype);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (p.enumSetId != bytes32(0)) {
|
|
203
|
+
if (p.expectedDatatype == 5) { // DT_BYTES32
|
|
204
|
+
bytes32 v = store.getBytes32(subject, p.predicate);
|
|
205
|
+
if (!_enumContains[p.enumSetId][v]) {
|
|
206
|
+
revert EnumValueNotAllowed(p.predicate, v);
|
|
207
|
+
}
|
|
208
|
+
} else if (p.expectedDatatype == 8) { // DT_BYTES32_ARR
|
|
209
|
+
bytes32[] memory arr = store.getBytes32Arr(subject, p.predicate);
|
|
210
|
+
for (uint256 j = 0; j < arr.length; j++) {
|
|
211
|
+
if (!_enumContains[p.enumSetId][arr[j]]) {
|
|
212
|
+
revert EnumValueNotAllowed(p.predicate, arr[j]);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ─── Queries ────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
function getShape(bytes32 classId) external view returns (Shape memory) {
|
|
223
|
+
return _shapes[classId];
|
|
224
|
+
}
|
|
225
|
+
function getProperties(bytes32 classId) external view returns (PropertyConstraint[] memory) {
|
|
226
|
+
return _props[classId];
|
|
227
|
+
}
|
|
228
|
+
function getEnumValues(bytes32 enumSetId) external view returns (bytes32[] memory) {
|
|
229
|
+
return _enumValues[enumSetId];
|
|
230
|
+
}
|
|
231
|
+
function isInEnumSet(bytes32 enumSetId, bytes32 value) external view returns (bool) {
|
|
232
|
+
return _enumContains[enumSetId][value];
|
|
233
|
+
}
|
|
234
|
+
function shapeCount() external view returns (uint256) {
|
|
235
|
+
return _classIds.length;
|
|
236
|
+
}
|
|
237
|
+
function getClassIdAt(uint256 index) external view returns (bytes32) {
|
|
238
|
+
return _classIds[index];
|
|
239
|
+
}
|
|
240
|
+
}
|