@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.
Files changed (84) hide show
  1. package/AUDIT.md +67 -0
  2. package/CLAUDE.md +40 -0
  3. package/LICENSE +21 -0
  4. package/README.md +45 -0
  5. package/deployments-anvil.json +1 -0
  6. package/deployments-base-sepolia.json +1 -0
  7. package/dist/abi/AgentNameAttributeResolver.json +798 -0
  8. package/dist/abi/AgentNamePredicates.json +1 -0
  9. package/dist/abi/AgentNameRegistry.json +826 -0
  10. package/dist/abi/AgentNameUniversalResolver.json +222 -0
  11. package/dist/abi/AgentProfilePredicates.json +1 -0
  12. package/dist/abi/AgentProfileResolver.json +1044 -0
  13. package/dist/abi/AgentRelationship.json +583 -0
  14. package/dist/abi/AgentRelationshipPredicates.json +1 -0
  15. package/dist/abi/AgenticGovernance.json +259 -0
  16. package/dist/abi/AllowedMethodsEnforcer.json +108 -0
  17. package/dist/abi/AllowedTargetsEnforcer.json +103 -0
  18. package/dist/abi/ApprovedHashRegistry.json +114 -0
  19. package/dist/abi/AttributeStorage.json +557 -0
  20. package/dist/abi/CaveatEnforcerBase.json +130 -0
  21. package/dist/abi/GovernanceManaged.json +43 -0
  22. package/dist/abi/IAttributeReader.json +98 -0
  23. package/dist/abi/ICaveatEnforcer.json +98 -0
  24. package/dist/abi/IDelegationManager.json +211 -0
  25. package/dist/abi/IERC7579Module.json +34 -0
  26. package/dist/abi/IERC7579ModuleLifecycle.json +60 -0
  27. package/dist/abi/IGovernanceView.json +34 -0
  28. package/dist/abi/MultiSendCallOnly.json +29 -0
  29. package/dist/abi/MultiSendCallOnlyHarness.json +42 -0
  30. package/dist/abi/OntologyTermRegistry.json +397 -0
  31. package/dist/abi/P256Verifier.json +1 -0
  32. package/dist/abi/PermissionlessSubregistry.json +207 -0
  33. package/dist/abi/RelationshipTypeRegistry.json +455 -0
  34. package/dist/abi/ShapeRegistry.json +627 -0
  35. package/dist/abi/SmartAgentModuleTypes.json +1 -0
  36. package/dist/abi/TimestampEnforcer.json +108 -0
  37. package/dist/abi/ValueEnforcer.json +103 -0
  38. package/dist/abi/WebAuthnLib.json +1 -0
  39. package/dist/abi/index.d.ts +35 -0
  40. package/dist/abi/index.js +35 -0
  41. package/package.json +48 -0
  42. package/spec.md +52 -0
  43. package/src/AgentAccount.sol +1374 -0
  44. package/src/AgentAccountFactory.sol +274 -0
  45. package/src/ApprovedHashRegistry.sol +57 -0
  46. package/src/IAgentAccount.sol +138 -0
  47. package/src/SmartAgentPaymaster.sol +281 -0
  48. package/src/UniversalSignatureValidator.sol +136 -0
  49. package/src/agency/DelegationManager.sol +374 -0
  50. package/src/agency/ICaveatEnforcer.sol +62 -0
  51. package/src/agency/IDelegationManager.sol +69 -0
  52. package/src/custody/CustodyPolicy.sol +892 -0
  53. package/src/custody/IERC7579Module.sol +60 -0
  54. package/src/enforcers/AllowedMethodsEnforcer.AUDIT.md +51 -0
  55. package/src/enforcers/AllowedMethodsEnforcer.sol +48 -0
  56. package/src/enforcers/AllowedTargetsEnforcer.AUDIT.md +49 -0
  57. package/src/enforcers/AllowedTargetsEnforcer.sol +44 -0
  58. package/src/enforcers/CaveatEnforcerBase.sol +19 -0
  59. package/src/enforcers/QuorumEnforcer.AUDIT.md +71 -0
  60. package/src/enforcers/QuorumEnforcer.sol +191 -0
  61. package/src/enforcers/TimestampEnforcer.AUDIT.md +50 -0
  62. package/src/enforcers/TimestampEnforcer.sol +43 -0
  63. package/src/enforcers/ValueEnforcer.AUDIT.md +51 -0
  64. package/src/enforcers/ValueEnforcer.sol +41 -0
  65. package/src/governance/AgenticGovernance.sol +140 -0
  66. package/src/governance/GovernanceManaged.sol +75 -0
  67. package/src/governance/IGovernance.sol +15 -0
  68. package/src/identity/AgentProfilePredicates.sol +40 -0
  69. package/src/identity/AgentProfileResolver.sol +194 -0
  70. package/src/libraries/MultiSendCallOnly.sol +95 -0
  71. package/src/libraries/P256Verifier.sol +47 -0
  72. package/src/libraries/SignatureSlotRecovery.sol +196 -0
  73. package/src/libraries/WebAuthnLib.sol +164 -0
  74. package/src/naming/AgentNameAttributeResolver.sol +95 -0
  75. package/src/naming/AgentNamePredicates.sol +74 -0
  76. package/src/naming/AgentNameRegistry.sol +362 -0
  77. package/src/naming/AgentNameUniversalResolver.sol +210 -0
  78. package/src/naming/PermissionlessSubregistry.sol +98 -0
  79. package/src/ontology/AttributeStorage.sol +289 -0
  80. package/src/ontology/OntologyTermRegistry.sol +146 -0
  81. package/src/ontology/ShapeRegistry.sol +240 -0
  82. package/src/relationships/AgentRelationship.sol +289 -0
  83. package/src/relationships/AgentRelationshipPredicates.sol +44 -0
  84. package/src/relationships/RelationshipTypeRegistry.sol +143 -0
@@ -0,0 +1,374 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.28;
3
+
4
+ import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
5
+ import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
6
+ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
7
+ import "@openzeppelin/contracts/interfaces/IERC1271.sol";
8
+ import "./IDelegationManager.sol";
9
+ import "./ICaveatEnforcer.sol";
10
+ import "../governance/IGovernance.sol";
11
+
12
+ /**
13
+ * @title DelegationManager
14
+ * @notice On-chain delegation management with caveat enforcement.
15
+ *
16
+ * Aligned with ERC-7710 patterns and MetaMask delegation-framework design:
17
+ * 1. Delegator signs a Delegation struct via EIP-712
18
+ * 2. Delegate calls redeemDelegation() with the signed delegation chain
19
+ * 3. DelegationManager validates signatures and enforces all caveats (beforeHook/afterHook)
20
+ * 4. Execution goes through the delegator's smart account via execute()
21
+ *
22
+ * Spec 007 Phase A.5:
23
+ * - `redeemDelegation` is `nonReentrant` (SC5 § 6.2). Blocks nested
24
+ * redemption via caveat-enforcer or target-call callbacks.
25
+ * - `revokeDelegation(bytes32, bytes calldata)` is authenticated:
26
+ * either the delegator OR the delegate may revoke, with the
27
+ * verifier reading the delegation struct out of the explicit
28
+ * signature payload so Variant A (off-chain) delegations can be
29
+ * revoked too. C2 § 5 revocation-gap closure.
30
+ *
31
+ * Key ERC-7710 / DeleGator alignments:
32
+ * - Caveat args: redeemer-provided runtime arguments (excluded from delegation hash)
33
+ * - beforeHook/afterHook: enforcers revert on failure (no bool return)
34
+ * - Execute through delegator account, not direct target.call
35
+ * - Open delegations: delegate = address(0xa11) allows any redeemer
36
+ */
37
+ contract DelegationManager is IDelegationManager, ReentrancyGuard {
38
+ using ECDSA for bytes32;
39
+ using MessageHashUtils for bytes32;
40
+
41
+ // ─── H7-C.10 / EXT3-010 — system-wide pause hook ───────────────────
42
+ //
43
+ // DelegationManager is a singleton — every redeem of every delegation
44
+ // on every chain flows through this contract. An incident-mode pause
45
+ // here is the kill-switch that stops new delegation actions while an
46
+ // exploit is being investigated. The pause flag is sourced from
47
+ // `AgenticGovernance.isPaused()` (a guardian can pause without delay;
48
+ // unpause needs the timelock).
49
+ //
50
+ // `governance` is `address(0)` for legacy deploys; in that case the
51
+ // pause check is skipped (no governance, no pause source). Production
52
+ // deploys pass a real AgenticGovernance address.
53
+ address public immutable governance;
54
+
55
+ error SystemPaused();
56
+
57
+ /// @dev Root authority constant — delegations with this authority are root-level
58
+ bytes32 public constant ROOT_AUTHORITY = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
59
+
60
+ /// @dev Open delegation sentinel — any address can redeem
61
+ address public constant OPEN_DELEGATION = address(0xa11);
62
+
63
+ /// @dev EIP-712 domain separator
64
+ bytes32 public immutable DOMAIN_SEPARATOR;
65
+
66
+ /// @dev Delegation type hash for EIP-712.
67
+ ///
68
+ /// **R1 / CROSS-STACK-001 closure (2026-05-31):** converged to
69
+ /// STANDARD EIP-712. Previously used the non-standard inline form
70
+ /// `Delegation(...,bytes32 caveatsHash,...)` which produced a
71
+ /// different typehash from what off-chain `viem.hashTypedData`
72
+ /// derives from a `Caveat[]` field. The standard form encodes
73
+ /// `Caveat[] caveats` in the type string + appends the `Caveat(...)`
74
+ /// type definition.
75
+ ///
76
+ /// The `_hashCaveats` function below already computes
77
+ /// `keccak256(concat(hashStruct(c) for c in caveats))` which IS the
78
+ /// EIP-712 standard `encodeData(Caveat[])`. structHash computation
79
+ /// stays identical; only the typehash STRING changed.
80
+ bytes32 public constant DELEGATION_TYPEHASH = keccak256(
81
+ "Delegation(address delegator,address delegate,bytes32 authority,Caveat[] caveats,uint256 salt)Caveat(address enforcer,bytes terms)"
82
+ );
83
+
84
+ /// @dev Caveat type hash for EIP-712 (only enforcer + terms; args excluded).
85
+ bytes32 public constant CAVEAT_TYPEHASH = keccak256(
86
+ "Caveat(address enforcer,bytes terms)"
87
+ );
88
+
89
+ /// @dev Revoked delegation hashes
90
+ mapping(bytes32 => bool) private _revoked;
91
+
92
+ /// @notice Emitted when a revocation succeeds with the address that
93
+ /// submitted it (delegator or delegate).
94
+ event DelegationRevokedBy(bytes32 indexed delegationHash, address indexed by);
95
+
96
+ error DelegationRevoked_();
97
+ error DelegationManager_InvalidSignature();
98
+ error InvalidAuthority();
99
+ error OnlyDelegator();
100
+ error ExecutionFailed();
101
+ error InvalidDelegate();
102
+ error EmptyChain();
103
+
104
+ // Phase A.5 errors.
105
+ error NotDelegatorOrDelegate();
106
+ error HashMismatch();
107
+ /// @dev Thrown by the deprecated permissionless `revokeDelegation(bytes32)`
108
+ /// path. Migrate callers to `revokeDelegationByOwner(Delegation)`.
109
+ error LegacyRevocationDisabled();
110
+
111
+ /// @dev Storage gap reserves 50 slots for future state. The contract
112
+ /// is currently NOT upgradeable (SC4 § 4.3.4 — DelegationManager
113
+ /// is singleton + re-deploy on bug), but the gap standardises
114
+ /// our storage discipline and keeps the option open.
115
+ uint256[50] private __gap;
116
+
117
+ /// @notice Deploys the DelegationManager. Pass `address(0)` for
118
+ /// `governance_` to opt out of the H7-C.10 pause gate
119
+ /// (legacy / test deploys). Production deploys pass an
120
+ /// `AgenticGovernance` address.
121
+ constructor(address governance_) {
122
+ governance = governance_;
123
+ DOMAIN_SEPARATOR = keccak256(
124
+ abi.encode(
125
+ keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
126
+ keccak256("AgentDelegationManager"),
127
+ keccak256("1"),
128
+ block.chainid,
129
+ address(this)
130
+ )
131
+ );
132
+ }
133
+
134
+ /// @inheritdoc IDelegationManager
135
+ /// @dev Phase A.5 (SC5 § 6.2) — `nonReentrant` blocks nested
136
+ /// redemption (a caveat enforcer or target call cannot
137
+ /// reenter this function).
138
+ function redeemDelegation(
139
+ Delegation[] calldata delegations,
140
+ address target,
141
+ uint256 value,
142
+ bytes calldata data
143
+ ) external nonReentrant {
144
+ // H7-C.10 / EXT3-010: system-wide pause gate. Skipped when
145
+ // `governance` is `address(0)`, an EOA, or a non-conforming
146
+ // contract (legacy / test deploys). Production deploys pass an
147
+ // `AgenticGovernance` address; a guardian can pause without delay;
148
+ // unpause requires the 24h timelock.
149
+ if (governance != address(0) && governance.code.length > 0) {
150
+ (bool ok, bytes memory data) = governance.staticcall(
151
+ abi.encodeWithSelector(IGovernanceView.isPaused.selector)
152
+ );
153
+ if (ok && data.length >= 32 && abi.decode(data, (bool))) revert SystemPaused();
154
+ }
155
+ if (delegations.length == 0) revert EmptyChain();
156
+
157
+ // Phase 1: Validate chain + run beforeHooks (leaf to root)
158
+ for (uint256 i = 0; i < delegations.length; i++) {
159
+ _validateDelegation(delegations, i);
160
+ _runBeforeHooks(delegations[i], target, value, data);
161
+ }
162
+
163
+ // Phase 2: Execute through the root delegator's smart account
164
+ address rootDelegator = delegations[delegations.length - 1].delegator;
165
+ _executeFromDelegator(rootDelegator, target, value, data);
166
+
167
+ // Phase 3: After-hooks (root to leaf, per DeleGator convention)
168
+ for (uint256 i = delegations.length; i > 0; i--) {
169
+ _runAfterHooks(delegations[i - 1], target, value, data);
170
+ }
171
+ }
172
+
173
+ /// @inheritdoc IDelegationManager
174
+ /// @notice DEPRECATED — permissionless revocation by hash.
175
+ /// @dev Production-readiness pass: the permissionless legacy path is a
176
+ /// DoS surface — a mempool observer or any party who reconstructs
177
+ /// a Variant A delegation hash can mark it revoked. The original
178
+ /// rationale (Variant B delegations register on-chain so the
179
+ /// hash is already public) doesn't hold for Variant A, which is
180
+ /// now the default.
181
+ ///
182
+ /// We retain the function selector for ABI compatibility with
183
+ /// existing tooling, but always revert with `LegacyRevocationDisabled()`.
184
+ /// Callers MUST migrate to `revokeDelegationByOwner(Delegation)`
185
+ /// which authenticates `msg.sender` against the delegation struct.
186
+ function revokeDelegation(bytes32 /* delegationHash */) external pure {
187
+ revert LegacyRevocationDisabled();
188
+ }
189
+
190
+ /// @notice Phase A.5 — authenticated revocation path that works for
191
+ /// Variant A (off-chain caveated delegation, never
192
+ /// registered) by deriving the hash from the delegation
193
+ /// struct + signature provided by the caller.
194
+ ///
195
+ /// @dev Authorization gate: `msg.sender` MUST be either
196
+ /// `delegation.delegator` OR `delegation.delegate` (the latter
197
+ /// blocks a random EOA from revoking a delegation it doesn't
198
+ /// hold, which would otherwise be a permissionless DoS vector
199
+ /// against the legacy `revokeDelegation(bytes32)` path).
200
+ ///
201
+ /// @param delegation The delegation to revoke. The signature inside
202
+ /// it is verified to confirm the struct is
203
+ /// legitimate before we mark the hash revoked —
204
+ /// otherwise a malicious delegate could revoke a
205
+ /// *forged* delegation hash by submitting a struct
206
+ /// they invented.
207
+ function revokeDelegationByOwner(Delegation calldata delegation) external {
208
+ if (msg.sender != delegation.delegator && msg.sender != delegation.delegate) {
209
+ revert NotDelegatorOrDelegate();
210
+ }
211
+
212
+ bytes32 dHash = hashDelegation(delegation);
213
+
214
+ // Verify the delegation struct is signed by its declared
215
+ // delegator. We don't want a delegate to be able to revoke
216
+ // a hash that was never a real delegation.
217
+ _validateSignature(delegation.delegator, dHash, delegation.signature);
218
+
219
+ _revoked[dHash] = true;
220
+ emit DelegationRevoked(dHash);
221
+ emit DelegationRevokedBy(dHash, msg.sender);
222
+ }
223
+
224
+ /// @inheritdoc IDelegationManager
225
+ function isRevoked(bytes32 delegationHash) external view returns (bool) {
226
+ return _revoked[delegationHash];
227
+ }
228
+
229
+ /// @notice Compute the EIP-712 hash of a delegation.
230
+ function hashDelegation(Delegation calldata d) public view returns (bytes32) {
231
+ bytes32 caveatsHash = _hashCaveats(d.caveats);
232
+ bytes32 structHash = keccak256(
233
+ abi.encode(
234
+ DELEGATION_TYPEHASH,
235
+ d.delegator,
236
+ d.delegate,
237
+ d.authority,
238
+ caveatsHash,
239
+ d.salt
240
+ )
241
+ );
242
+ return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, structHash));
243
+ }
244
+
245
+ // ─── Internal: Validation ──────────────────────────────────────────
246
+
247
+ function _validateDelegation(
248
+ Delegation[] calldata delegations,
249
+ uint256 i
250
+ ) internal {
251
+ Delegation calldata d = delegations[i];
252
+ bytes32 dHash = hashDelegation(d);
253
+
254
+ // Check not revoked
255
+ if (_revoked[dHash]) revert DelegationRevoked_();
256
+
257
+ // Validate delegate
258
+ if (i == 0) {
259
+ if (d.delegate != OPEN_DELEGATION && d.delegate != msg.sender) revert InvalidDelegate();
260
+ } else {
261
+ if (d.delegate != OPEN_DELEGATION && d.delegate != delegations[i - 1].delegator) revert InvalidDelegate();
262
+ }
263
+
264
+ // Validate authority chain
265
+ if (d.authority != ROOT_AUTHORITY) {
266
+ if (i + 1 >= delegations.length) revert InvalidAuthority();
267
+ bytes32 parentHash = hashDelegation(delegations[i + 1]);
268
+ if (d.authority != parentHash) revert InvalidAuthority();
269
+ }
270
+
271
+ // Validate signature
272
+ _validateSignature(d.delegator, dHash, d.signature);
273
+
274
+ emit DelegationRedeemed(dHash, d.delegator, d.delegate);
275
+ }
276
+
277
+ // ─── Internal: Caveat Hooks ────────────────────────────────────────
278
+
279
+ function _runBeforeHooks(
280
+ Delegation calldata d,
281
+ address target,
282
+ uint256 value,
283
+ bytes calldata data
284
+ ) internal {
285
+ bytes32 dHash = hashDelegation(d);
286
+ for (uint256 j = 0; j < d.caveats.length; j++) {
287
+ ICaveatEnforcer(d.caveats[j].enforcer).beforeHook(
288
+ d.caveats[j].terms,
289
+ d.caveats[j].args,
290
+ dHash,
291
+ d.delegator,
292
+ msg.sender,
293
+ target,
294
+ value,
295
+ data
296
+ );
297
+ }
298
+ }
299
+
300
+ function _runAfterHooks(
301
+ Delegation calldata d,
302
+ address target,
303
+ uint256 value,
304
+ bytes calldata data
305
+ ) internal {
306
+ bytes32 dHash = hashDelegation(d);
307
+ for (uint256 j = 0; j < d.caveats.length; j++) {
308
+ ICaveatEnforcer(d.caveats[j].enforcer).afterHook(
309
+ d.caveats[j].terms,
310
+ d.caveats[j].args,
311
+ dHash,
312
+ d.delegator,
313
+ msg.sender,
314
+ target,
315
+ value,
316
+ data
317
+ );
318
+ }
319
+ }
320
+
321
+ // ─── Internal: Execution ───────────────────────────────────────────
322
+
323
+ function _executeFromDelegator(
324
+ address delegator,
325
+ address target,
326
+ uint256 value,
327
+ bytes calldata data
328
+ ) internal {
329
+ // Call the delegator account's execute(address,uint256,bytes) function
330
+ // This ensures msg.sender in the target contract is the delegator
331
+ (bool success, bytes memory returnData) = delegator.call(
332
+ abi.encodeWithSignature("execute(address,uint256,bytes)", target, value, data)
333
+ );
334
+ if (!success) {
335
+ if (returnData.length > 0) {
336
+ assembly {
337
+ revert(add(returnData, 32), mload(returnData))
338
+ }
339
+ }
340
+ revert ExecutionFailed();
341
+ }
342
+ }
343
+
344
+ // ─── Internal: Signature ───────────────────────────────────────────
345
+
346
+ function _validateSignature(
347
+ address signer,
348
+ bytes32 digest,
349
+ bytes calldata signature
350
+ ) internal view {
351
+ // ERC-1271 for smart accounts
352
+ if (signer.code.length > 0) {
353
+ bytes4 result = IERC1271(signer).isValidSignature(digest, signature);
354
+ if (result != IERC1271.isValidSignature.selector) revert DelegationManager_InvalidSignature();
355
+ return;
356
+ }
357
+ // EOA — recover from eth-signed message hash
358
+ bytes32 ethHash = digest.toEthSignedMessageHash();
359
+ address recovered = ethHash.recover(signature);
360
+ if (recovered != signer) revert DelegationManager_InvalidSignature();
361
+ }
362
+
363
+ // ─── Internal: Hashing ─────────────────────────────────────────────
364
+
365
+ function _hashCaveats(Caveat[] calldata caveats) internal pure returns (bytes32) {
366
+ bytes32[] memory hashes = new bytes32[](caveats.length);
367
+ for (uint256 i = 0; i < caveats.length; i++) {
368
+ hashes[i] = keccak256(
369
+ abi.encode(CAVEAT_TYPEHASH, caveats[i].enforcer, keccak256(caveats[i].terms))
370
+ );
371
+ }
372
+ return keccak256(abi.encodePacked(hashes));
373
+ }
374
+ }
@@ -0,0 +1,62 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.28;
3
+
4
+ /**
5
+ * @title ICaveatEnforcer
6
+ * @notice Interface for caveat enforcers following MetaMask DeleGator patterns.
7
+ *
8
+ * Enforcers validate delegation constraints using a beforeHook/afterHook pattern.
9
+ * They REVERT on failure rather than returning a bool — this aligns with
10
+ * the MetaMask delegation-framework and ERC-7710 conventions.
11
+ *
12
+ * @dev terms: Immutable parameters set when the delegation is created (e.g., time window, allowed methods).
13
+ * args: Mutable parameters provided by the redeemer at redemption time (e.g., proof data).
14
+ */
15
+ interface ICaveatEnforcer {
16
+ /**
17
+ * @notice Called before the delegated action is executed.
18
+ * MUST revert if the caveat is not satisfied.
19
+ * @param terms Encoded parameters set at delegation creation time.
20
+ * @param args Encoded parameters provided at redemption time.
21
+ * @param delegationHash Hash of the delegation being redeemed.
22
+ * @param delegator The account that created the delegation.
23
+ * @param redeemer The address calling redeemDelegation.
24
+ * @param target The target contract being called.
25
+ * @param value The ETH value being sent.
26
+ * @param callData The calldata for the target call.
27
+ */
28
+ function beforeHook(
29
+ bytes calldata terms,
30
+ bytes calldata args,
31
+ bytes32 delegationHash,
32
+ address delegator,
33
+ address redeemer,
34
+ address target,
35
+ uint256 value,
36
+ bytes calldata callData
37
+ ) external;
38
+
39
+ /**
40
+ * @notice Called after the delegated action is executed.
41
+ * MUST revert if post-execution state is invalid.
42
+ * Not all enforcers need post-hooks — default is a no-op.
43
+ * @param terms Encoded parameters set at delegation creation time.
44
+ * @param args Encoded parameters provided at redemption time.
45
+ * @param delegationHash Hash of the delegation being redeemed.
46
+ * @param delegator The account that created the delegation.
47
+ * @param redeemer The address calling redeemDelegation.
48
+ * @param target The target contract being called.
49
+ * @param value The ETH value being sent.
50
+ * @param callData The calldata for the target call.
51
+ */
52
+ function afterHook(
53
+ bytes calldata terms,
54
+ bytes calldata args,
55
+ bytes32 delegationHash,
56
+ address delegator,
57
+ address redeemer,
58
+ address target,
59
+ uint256 value,
60
+ bytes calldata callData
61
+ ) external;
62
+ }
@@ -0,0 +1,69 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.28;
3
+
4
+ /**
5
+ * @title IDelegationManager
6
+ * @notice Manages delegations between agent accounts with caveat enforcement.
7
+ *
8
+ * Aligned with ERC-7710 (Smart Contract Delegation) patterns and inspired by
9
+ * MetaMask delegation-framework:
10
+ * - A delegation grants a delegate the right to act on behalf of a delegator
11
+ * - Caveats constrain what the delegate can do (time, value, methods, targets)
12
+ * - Delegations can be chained: A delegates to B, B delegates to C
13
+ * - Revocation is immediate and on-chain
14
+ * - Execution goes through the delegator's smart account (executeFromExecutor)
15
+ *
16
+ * ERC-7710 defines: redeemDelegations(bytes[], bytes32[], bytes[])
17
+ * We provide both the typed `redeemDelegation` and the ERC-7710 opaque interface.
18
+ */
19
+ interface IDelegationManager {
20
+ struct Caveat {
21
+ address enforcer; // contract that validates this caveat
22
+ bytes terms; // encoded parameters set at delegation creation time
23
+ bytes args; // encoded parameters provided by redeemer at redemption time
24
+ }
25
+
26
+ struct Delegation {
27
+ address delegator; // account granting authority
28
+ address delegate; // account receiving authority (address(0xa11) = open delegation)
29
+ bytes32 authority; // parent delegation hash (ROOT_AUTHORITY for root)
30
+ Caveat[] caveats; // restrictions on the delegation
31
+ uint256 salt; // replay protection
32
+ bytes signature; // EIP-712 signature from delegator
33
+ }
34
+
35
+ /// @notice Emitted when a delegation is redeemed.
36
+ event DelegationRedeemed(
37
+ bytes32 indexed delegationHash,
38
+ address indexed delegator,
39
+ address indexed delegate
40
+ );
41
+
42
+ /// @notice Emitted when a delegation is revoked.
43
+ event DelegationRevoked(bytes32 indexed delegationHash);
44
+
45
+ /// @notice Redeem a delegation chain to execute an action on behalf of the delegator.
46
+ /// The delegator's smart account executes the call via executeFromExecutor.
47
+ function redeemDelegation(
48
+ Delegation[] calldata delegations,
49
+ address target,
50
+ uint256 value,
51
+ bytes calldata data
52
+ ) external;
53
+
54
+ /// @notice Revoke a delegation by its hash.
55
+ ///
56
+ /// @dev Legacy permissionless path. Phase A.5 introduces
57
+ /// `revokeDelegationByOwner` for authenticated revocation that
58
+ /// works for Variant A delegations.
59
+ function revokeDelegation(bytes32 delegationHash) external;
60
+
61
+ /// @notice Phase A.5 — authenticated revocation. Caller must be
62
+ /// either `delegation.delegator` or `delegation.delegate`.
63
+ /// The delegation struct is signature-checked first to
64
+ /// prevent a malicious delegate from revoking a forged hash.
65
+ function revokeDelegationByOwner(Delegation calldata delegation) external;
66
+
67
+ /// @notice Check if a delegation has been revoked.
68
+ function isRevoked(bytes32 delegationHash) external view returns (bool);
69
+ }