@agenticprimitives/contracts 1.0.0-alpha.6 → 1.0.0-alpha.8

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 (41) hide show
  1. package/AUDIT.md +21 -1
  2. package/README.md +36 -12
  3. package/deployments-base-sepolia.json +35 -1
  4. package/deployments-registries-base-sepolia.json +1 -0
  5. package/dist/abi/AgentAccount.json +23 -0
  6. package/dist/abi/AgentAccountFactory.json +5 -0
  7. package/dist/abi/AgentNameRegistry.json +5 -0
  8. package/dist/abi/AgenticGovernance.json +32 -0
  9. package/dist/abi/AgreementRegistry.json +0 -5
  10. package/dist/abi/CustodyPolicy.json +11 -0
  11. package/dist/abi/IPaymentReceiptRegistry.json +50 -0
  12. package/dist/abi/MockUSDC.json +621 -0
  13. package/dist/abi/PaymentEnforcer.json +407 -0
  14. package/dist/abi/PaymentReceiptRegistry.json +182 -0
  15. package/dist/abi/QuorumEnforcer.json +16 -0
  16. package/dist/abi/SmartAgentPaymaster.json +209 -0
  17. package/dist/abi/index.d.ts +4 -0
  18. package/dist/abi/index.js +4 -0
  19. package/dist/deployments/base-sepolia.d.ts +3 -0
  20. package/dist/deployments/base-sepolia.js +31 -28
  21. package/dist/deployments/index.d.ts +2 -1
  22. package/dist/deployments/index.js +3 -0
  23. package/dist/deployments/registries-base-sepolia.d.ts +9 -0
  24. package/dist/deployments/registries-base-sepolia.js +7 -0
  25. package/package.json +4 -1
  26. package/src/AgentAccount.sol +61 -2
  27. package/src/AgentAccountFactory.sol +32 -7
  28. package/src/SmartAgentPaymaster.sol +149 -12
  29. package/src/agency/DelegationManager.sol +17 -2
  30. package/src/agency/IDelegationManager.sol +3 -0
  31. package/src/agreement/AgreementRegistry.sol +46 -21
  32. package/src/attestation/AttestationRegistry.sol +86 -22
  33. package/src/custody/CustodyPolicy.sol +59 -6
  34. package/src/enforcers/PaymentEnforcer.sol +194 -0
  35. package/src/enforcers/QuorumEnforcer.sol +12 -0
  36. package/src/governance/AgenticGovernance.sol +26 -4
  37. package/src/libraries/SignatureSlotRecovery.sol +8 -3
  38. package/src/libraries/WebAuthnLib.sol +13 -0
  39. package/src/mocks/MockUSDC.sol +152 -0
  40. package/src/naming/AgentNameRegistry.sol +27 -2
  41. package/src/payments/PaymentReceiptRegistry.sol +67 -0
@@ -30,6 +30,19 @@
30
30
  ],
31
31
  "stateMutability": "nonpayable"
32
32
  },
33
+ {
34
+ "type": "function",
35
+ "name": "DEPOSIT_WITHDRAWAL_TIMELOCK",
36
+ "inputs": [],
37
+ "outputs": [
38
+ {
39
+ "name": "",
40
+ "type": "uint64",
41
+ "internalType": "uint64"
42
+ }
43
+ ],
44
+ "stateMutability": "view"
45
+ },
33
46
  {
34
47
  "type": "function",
35
48
  "name": "acceptOwnership",
@@ -50,6 +63,13 @@
50
63
  "outputs": [],
51
64
  "stateMutability": "payable"
52
65
  },
66
+ {
67
+ "type": "function",
68
+ "name": "cancelDepositWithdrawal",
69
+ "inputs": [],
70
+ "outputs": [],
71
+ "stateMutability": "nonpayable"
72
+ },
53
73
  {
54
74
  "type": "function",
55
75
  "name": "deposit",
@@ -83,6 +103,13 @@
83
103
  ],
84
104
  "stateMutability": "view"
85
105
  },
106
+ {
107
+ "type": "function",
108
+ "name": "executeDepositWithdrawal",
109
+ "inputs": [],
110
+ "outputs": [],
111
+ "stateMutability": "nonpayable"
112
+ },
86
113
  {
87
114
  "type": "function",
88
115
  "name": "getDeposit",
@@ -217,6 +244,19 @@
217
244
  ],
218
245
  "stateMutability": "view"
219
246
  },
247
+ {
248
+ "type": "function",
249
+ "name": "paused",
250
+ "inputs": [],
251
+ "outputs": [
252
+ {
253
+ "name": "",
254
+ "type": "bool",
255
+ "internalType": "bool"
256
+ }
257
+ ],
258
+ "stateMutability": "view"
259
+ },
220
260
  {
221
261
  "type": "function",
222
262
  "name": "pendingOwner",
@@ -230,6 +270,29 @@
230
270
  ],
231
271
  "stateMutability": "view"
232
272
  },
273
+ {
274
+ "type": "function",
275
+ "name": "pendingWithdrawal",
276
+ "inputs": [],
277
+ "outputs": [
278
+ {
279
+ "name": "to",
280
+ "type": "address",
281
+ "internalType": "address"
282
+ },
283
+ {
284
+ "name": "amount",
285
+ "type": "uint256",
286
+ "internalType": "uint256"
287
+ },
288
+ {
289
+ "name": "eta",
290
+ "type": "uint64",
291
+ "internalType": "uint64"
292
+ }
293
+ ],
294
+ "stateMutability": "view"
295
+ },
233
296
  {
234
297
  "type": "function",
235
298
  "name": "postOp",
@@ -265,6 +328,24 @@
265
328
  "outputs": [],
266
329
  "stateMutability": "nonpayable"
267
330
  },
331
+ {
332
+ "type": "function",
333
+ "name": "scheduleDepositWithdrawal",
334
+ "inputs": [
335
+ {
336
+ "name": "to",
337
+ "type": "address",
338
+ "internalType": "address payable"
339
+ },
340
+ {
341
+ "name": "amount",
342
+ "type": "uint256",
343
+ "internalType": "uint256"
344
+ }
345
+ ],
346
+ "outputs": [],
347
+ "stateMutability": "nonpayable"
348
+ },
268
349
  {
269
350
  "type": "function",
270
351
  "name": "setAccepted",
@@ -314,6 +395,19 @@
314
395
  "outputs": [],
315
396
  "stateMutability": "nonpayable"
316
397
  },
398
+ {
399
+ "type": "function",
400
+ "name": "setPauseMirror",
401
+ "inputs": [
402
+ {
403
+ "name": "paused_",
404
+ "type": "bool",
405
+ "internalType": "bool"
406
+ }
407
+ ],
408
+ "outputs": [],
409
+ "stateMutability": "nonpayable"
410
+ },
317
411
  {
318
412
  "type": "function",
319
413
  "name": "setVerifyingSigner",
@@ -327,6 +421,13 @@
327
421
  "outputs": [],
328
422
  "stateMutability": "nonpayable"
329
423
  },
424
+ {
425
+ "type": "function",
426
+ "name": "syncPauseFromGovernance",
427
+ "inputs": [],
428
+ "outputs": [],
429
+ "stateMutability": "nonpayable"
430
+ },
330
431
  {
331
432
  "type": "function",
332
433
  "name": "transferOwnership",
@@ -472,6 +573,69 @@
472
573
  "outputs": [],
473
574
  "stateMutability": "nonpayable"
474
575
  },
576
+ {
577
+ "type": "event",
578
+ "name": "DepositWithdrawalCancelled",
579
+ "inputs": [
580
+ {
581
+ "name": "to",
582
+ "type": "address",
583
+ "indexed": true,
584
+ "internalType": "address"
585
+ },
586
+ {
587
+ "name": "amount",
588
+ "type": "uint256",
589
+ "indexed": false,
590
+ "internalType": "uint256"
591
+ }
592
+ ],
593
+ "anonymous": false
594
+ },
595
+ {
596
+ "type": "event",
597
+ "name": "DepositWithdrawalExecuted",
598
+ "inputs": [
599
+ {
600
+ "name": "to",
601
+ "type": "address",
602
+ "indexed": true,
603
+ "internalType": "address"
604
+ },
605
+ {
606
+ "name": "amount",
607
+ "type": "uint256",
608
+ "indexed": false,
609
+ "internalType": "uint256"
610
+ }
611
+ ],
612
+ "anonymous": false
613
+ },
614
+ {
615
+ "type": "event",
616
+ "name": "DepositWithdrawalScheduled",
617
+ "inputs": [
618
+ {
619
+ "name": "to",
620
+ "type": "address",
621
+ "indexed": true,
622
+ "internalType": "address"
623
+ },
624
+ {
625
+ "name": "amount",
626
+ "type": "uint256",
627
+ "indexed": false,
628
+ "internalType": "uint256"
629
+ },
630
+ {
631
+ "name": "eta",
632
+ "type": "uint64",
633
+ "indexed": false,
634
+ "internalType": "uint64"
635
+ }
636
+ ],
637
+ "anonymous": false
638
+ },
475
639
  {
476
640
  "type": "event",
477
641
  "name": "DevModeSet",
@@ -523,6 +687,25 @@
523
687
  ],
524
688
  "anonymous": false
525
689
  },
690
+ {
691
+ "type": "event",
692
+ "name": "PauseMirrorSynced",
693
+ "inputs": [
694
+ {
695
+ "name": "paused",
696
+ "type": "bool",
697
+ "indexed": false,
698
+ "internalType": "bool"
699
+ },
700
+ {
701
+ "name": "by",
702
+ "type": "address",
703
+ "indexed": true,
704
+ "internalType": "address"
705
+ }
706
+ ],
707
+ "anonymous": false
708
+ },
526
709
  {
527
710
  "type": "event",
528
711
  "name": "SenderAcceptedSet",
@@ -582,6 +765,11 @@
582
765
  "name": "MustOverride",
583
766
  "inputs": []
584
767
  },
768
+ {
769
+ "type": "error",
770
+ "name": "NoPendingWithdrawal",
771
+ "inputs": []
772
+ },
585
773
  {
586
774
  "type": "error",
587
775
  "name": "NotFromEntryPoint",
@@ -656,6 +844,27 @@
656
844
  "name": "SystemPaused",
657
845
  "inputs": []
658
846
  },
847
+ {
848
+ "type": "error",
849
+ "name": "WithdrawalAmountZero",
850
+ "inputs": []
851
+ },
852
+ {
853
+ "type": "error",
854
+ "name": "WithdrawalNotReady",
855
+ "inputs": [
856
+ {
857
+ "name": "eta",
858
+ "type": "uint64",
859
+ "internalType": "uint64"
860
+ }
861
+ ]
862
+ },
863
+ {
864
+ "type": "error",
865
+ "name": "WithdrawalToZero",
866
+ "inputs": []
867
+ },
659
868
  {
660
869
  "type": "error",
661
870
  "name": "ZeroGovernance",
@@ -40,10 +40,14 @@ export declare const IERC7579Module: Abi;
40
40
  export declare const IERC7579ModuleLifecycle: Abi;
41
41
  export declare const IERC7579ModuleLike: Abi;
42
42
  export declare const IGovernanceView: Abi;
43
+ export declare const IPaymentReceiptRegistry: Abi;
44
+ export declare const MockUSDC: Abi;
43
45
  export declare const MultiSendCallOnly: Abi;
44
46
  export declare const MultiSendCallOnlyHarness: Abi;
45
47
  export declare const OntologyTermRegistry: Abi;
46
48
  export declare const P256Verifier: Abi;
49
+ export declare const PaymentEnforcer: Abi;
50
+ export declare const PaymentReceiptRegistry: Abi;
47
51
  export declare const PermissionlessSubregistry: Abi;
48
52
  export declare const QuorumEnforcer: Abi;
49
53
  export declare const RelationshipTypeRegistry: Abi;
package/dist/abi/index.js CHANGED
@@ -40,10 +40,14 @@ export { default as IERC7579Module } from './IERC7579Module.json' with { type: '
40
40
  export { default as IERC7579ModuleLifecycle } from './IERC7579ModuleLifecycle.json' with { type: 'json' };
41
41
  export { default as IERC7579ModuleLike } from './IERC7579ModuleLike.json' with { type: 'json' };
42
42
  export { default as IGovernanceView } from './IGovernanceView.json' with { type: 'json' };
43
+ export { default as IPaymentReceiptRegistry } from './IPaymentReceiptRegistry.json' with { type: 'json' };
44
+ export { default as MockUSDC } from './MockUSDC.json' with { type: 'json' };
43
45
  export { default as MultiSendCallOnly } from './MultiSendCallOnly.json' with { type: 'json' };
44
46
  export { default as MultiSendCallOnlyHarness } from './MultiSendCallOnlyHarness.json' with { type: 'json' };
45
47
  export { default as OntologyTermRegistry } from './OntologyTermRegistry.json' with { type: 'json' };
46
48
  export { default as P256Verifier } from './P256Verifier.json' with { type: 'json' };
49
+ export { default as PaymentEnforcer } from './PaymentEnforcer.json' with { type: 'json' };
50
+ export { default as PaymentReceiptRegistry } from './PaymentReceiptRegistry.json' with { type: 'json' };
47
51
  export { default as PermissionlessSubregistry } from './PermissionlessSubregistry.json' with { type: 'json' };
48
52
  export { default as QuorumEnforcer } from './QuorumEnforcer.json' with { type: 'json' };
49
53
  export { default as RelationshipTypeRegistry } from './RelationshipTypeRegistry.json' with { type: 'json' };
@@ -31,6 +31,9 @@ export interface Deployments {
31
31
  readonly "timestampEnforcer": Address;
32
32
  readonly "universalSignatureValidator": Address;
33
33
  readonly "valueEnforcer": Address;
34
+ readonly "paymentEnforcer": Address;
35
+ readonly "paymentReceiptRegistry": Address;
36
+ readonly "mockUsdc": Address;
34
37
  readonly "chainId": number;
35
38
  }
36
39
  export declare const CONTRACTS: Deployments;
@@ -1,35 +1,38 @@
1
1
  // Generated by scripts/build-deployments.mjs — do not edit by hand.
2
2
  // Source: deployments-base-sepolia.json
3
3
  export const CONTRACTS = {
4
- "agentAccountFactory": "0x6f7Fc9B36977F55666548e0a73D9063F0D88A760",
5
- "agentAccountImplementation": "0x11f1523D9883BdF382993d70C601D307ED0F1f13",
6
- "agentNameRegistry": "0x6e660e41CFd0B410827fb96D21c26600AF6abF2f",
7
- "agentNameResolver": "0x324F5A60c5DE45244c70a43cA3A6B1462d80fF5f",
8
- "agentNameUniversalResolver": "0x40587e3591F7051ba7534177aB1F0B366034040e",
9
- "agentProfileResolver": "0x748D005E8B3b366132F209F066821B2713A6e19e",
10
- "agentRelationship": "0x5c3bf13822a37c84Dc617C920dB61a8217f6311d",
11
- "agreementRegistry": "0x3B1DB6AC972b649E9E0960F0816F4D1EC3264c22",
12
- "allowedMethodsEnforcer": "0x1500601201aA339249e2B78c42952Cf26CD12Ea7",
13
- "allowedTargetsEnforcer": "0xfA1c40F9dC4ACC4d339D2eDE0003872154292CEa",
14
- "approvedHashRegistry": "0xE01c1356F4B10FEfa8Ab210020B802cf3e900759",
15
- "attestationRegistry": "0x16EB207C65A63E3ae256F49a4B11e785fE001139",
16
- "callDataHashEnforcer": "0x7267eF129fCf341717D7f3469DDf7A0135d00b61",
4
+ "agentAccountFactory": "0x3E68B72B45e7C9d35B210E4Ab06e5Cece85cEbE4",
5
+ "agentAccountImplementation": "0x338318ABf9C8b28E7e77ed96a91857cCE1Db1328",
6
+ "agentNameRegistry": "0x15F7ed064A230C011b0244A14fD9653f011d609B",
7
+ "agentNameResolver": "0xcC9855A23C7a4D4592E68Fe47E195C2f9D832473",
8
+ "agentNameUniversalResolver": "0x7d777d2d0bbc1806B9Cc779121C27fbaAaFDb60b",
9
+ "agentProfileResolver": "0xc448EB159B2a35F07f6E1814CC8d125244d7384B",
10
+ "agentRelationship": "0xE6B8F6C7F1c9857013dA356DF08D0384334A9607",
11
+ "agreementRegistry": "0x7f4d6A74eda1D6ce251D371566f1c6639F9B0DDa",
12
+ "allowedMethodsEnforcer": "0xf77cf06aCd8b9e2f3204fe4bE16449B3594c9633",
13
+ "allowedTargetsEnforcer": "0xe3909FdEfD8b74B7D006E045DCd678e97D469Bfd",
14
+ "approvedHashRegistry": "0x90735B5281434D1746926E2AA722Cc6DbF3EFEe6",
15
+ "attestationRegistry": "0xaE3861fd299c74D96F6FCA25f19c7ACf8b9020ed",
16
+ "callDataHashEnforcer": "0x9F12932F1b85A1F96c1Cb1c5A0645e3F0Ab478Dd",
17
17
  "chainId": 84532,
18
- "custodyPolicy": "0x5e3e3Bda33c799f2403a30aE35bf81deff236283",
19
- "delegationManager": "0xBC5AE6213E91E076232a6c8e3942746f06A50154",
18
+ "custodyPolicy": "0xDeD6542A55476745846370562d1c75D15DDf2572",
19
+ "delegationManager": "0x3a8E2cE74564f699b135db6f266ccDb563979C05",
20
20
  "deployer": "0x31ed17fb99e82E02085Ab4B3cbdaB05489098b44",
21
- "entryPoint": "0x7707C2b483CcADc3E72C2d39d216aC1C337FCd9d",
22
- "geoFeatureRegistry": "0x0A9f10cb72D6Fb5C89Aa5865f397957729cF71F0",
23
- "ontologyTermRegistry": "0xa9635EbfDb993395df6CD03b0DEE21E5b2A66Ec1",
24
- "permissionlessSubregistry": "0xF38fdcd8eC83360C2F73CEEa52ae419D37aCaB5F",
25
- "permissionlessSubregistryDemoAgent": "0x1c698cBFE2733301614a245a505E20eB5597a972",
26
- "quorumEnforcer": "0xeB88df4492FB8Eb254f6017eD0463d42CF6FC8e7",
27
- "relationshipTypeRegistry": "0x7c5FfD0aB55C52f6aa00c04D2a30457579A3083B",
28
- "shapeRegistry": "0xAe431c6F000235d30ae28f507493215815eF88E2",
29
- "skillDefinitionRegistry": "0xc0A26f8bac0482E0F8e3a61762F5862Bc21a2D11",
30
- "smartAgentPaymaster": "0x0e54A8Cc986C86530f0593817E8CafE01FC52983",
31
- "timestampEnforcer": "0xf9AeF60b8cb4939bF5B1AF48E777dad99Ef1084f",
32
- "universalSignatureValidator": "0x7A282fFf06E6DC73613A31F55345535e24CB6832",
33
- "valueEnforcer": "0x4be1B537b9583E69D9baBf8af1dc5371A4a750C5"
21
+ "entryPoint": "0x9B33De9d9597de82Eb56b5B26C478Dc0a6955388",
22
+ "geoFeatureRegistry": "0x78FdCae0aC9B0CD9943996282b8F39D77bec5b1c",
23
+ "ontologyTermRegistry": "0xf486Ba6e9FD2d687e68a1a7A77D0FA27bb1E66c3",
24
+ "permissionlessSubregistry": "0x1B8ED8693738e1A9DD653FEE5430d49e00202Bb7",
25
+ "permissionlessSubregistryDemoAgent": "0xc76b1cdB4340398f59fD9C5d6Cb65fCdC606B33b",
26
+ "quorumEnforcer": "0x76fb494aE991d1fe1603a91384BA4c1F890442a8",
27
+ "relationshipTypeRegistry": "0x526fBF5900b18B1D06201493bcE470b416FB71C3",
28
+ "shapeRegistry": "0x03AbD67A910abB2c5026f1973A05502e0473e316",
29
+ "skillDefinitionRegistry": "0x5ae94aba46D7daA1Ca9eaE0051043Ad05bcc392B",
30
+ "smartAgentPaymaster": "0x8eF92B9D62826052D8F7e6dcaB630dC3890bF540",
31
+ "timestampEnforcer": "0x7c452434314A5bf2048f9263aeCd0cC20Fa07965",
32
+ "universalSignatureValidator": "0xe8F3e531429F44D77dC3A331C18032bfa1550D84",
33
+ "valueEnforcer": "0x7c4533e0A9fB0B1cb5aa70327b222a80d72d52db",
34
+ "paymentEnforcer": "0xAF48273e11435cC2D56f4AFBfD091Abb162458E9",
35
+ "paymentReceiptRegistry": "0x366616E265cd3cDE0F042A592C17838fe210D1d4",
36
+ "mockUsdc": "0x8fb56ff3C13347DFC4E1287aE83E88deE5a7211C"
34
37
  };
35
38
  export default CONTRACTS;
@@ -3,6 +3,7 @@ import type { Address } from 'viem';
3
3
  import type { Deployments } from './base-sepolia';
4
4
  export type { Deployments };
5
5
  export declare const BASE_SEPOLIA: Deployments;
6
+ export declare const REGISTRIES_BASE_SEPOLIA: Deployments;
6
7
 
7
- export type Network = 'base-sepolia';
8
+ export type Network = 'base-sepolia' | 'registries-base-sepolia';
8
9
  export declare function getDeployments(network: Network): Deployments;
@@ -1,9 +1,12 @@
1
1
  // Generated by scripts/build-deployments.mjs — do not edit by hand.
2
2
  import { CONTRACTS as BASE_SEPOLIA } from './base-sepolia.js';
3
3
  export { BASE_SEPOLIA };
4
+ import { CONTRACTS as REGISTRIES_BASE_SEPOLIA } from './registries-base-sepolia.js';
5
+ export { REGISTRIES_BASE_SEPOLIA };
4
6
 
5
7
  const TABLE = {
6
8
  "base-sepolia": BASE_SEPOLIA,
9
+ "registries-base-sepolia": REGISTRIES_BASE_SEPOLIA,
7
10
  };
8
11
 
9
12
  export function getDeployments(network) {
@@ -0,0 +1,9 @@
1
+ // Generated by scripts/build-deployments.mjs — do not edit by hand.
2
+ // Source: deployments-registries-base-sepolia.json
3
+ import type { Address } from 'viem';
4
+ export interface Deployments {
5
+ readonly "agreementRegistry": Address;
6
+ readonly "attestationRegistry": Address;
7
+ }
8
+ export declare const CONTRACTS: Deployments;
9
+ export default CONTRACTS;
@@ -0,0 +1,7 @@
1
+ // Generated by scripts/build-deployments.mjs — do not edit by hand.
2
+ // Source: deployments-registries-base-sepolia.json
3
+ export const CONTRACTS = {
4
+ "agreementRegistry": "0x9f2A085f0bd2d8Afb69490C0C98D88592093906A",
5
+ "attestationRegistry": "0x4a156CAaE69E894E3C5e6F3064fE028F138b6847"
6
+ };
7
+ export default CONTRACTS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticprimitives/contracts",
3
- "version": "1.0.0-alpha.6",
3
+ "version": "1.0.0-alpha.8",
4
4
  "description": "Solidity contracts + ABIs + flattened sources + per-network deployment addresses for the agenticprimitives stack.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -59,6 +59,9 @@
59
59
  "deploy:base-sepolia": "DEPLOY_NETWORK=base-sepolia forge script script/Deploy.s.sol --rpc-url $BASE_SEPOLIA_RPC --broadcast --private-key $PRIVATE_KEY",
60
60
  "deploy:paymaster:base-sepolia": "DEPLOY_NETWORK=base-sepolia forge script script/DeployPaymaster.s.sol --rpc-url $BASE_SEPOLIA_RPC --broadcast --private-key $PRIVATE_KEY --sig 'run()'",
61
61
  "deploy:paymaster:anvil": "DEPLOY_NETWORK=anvil forge script script/DeployPaymaster.s.sol --rpc-url http://127.0.0.1:8545 --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --sig 'run()'",
62
+ "deploy:registries:base-sepolia": "DEPLOY_NETWORK=base-sepolia forge script script/DeployRegistries.s.sol --rpc-url $BASE_SEPOLIA_RPC --broadcast --private-key $PRIVATE_KEY --sig 'run()'",
63
+ "deploy:payments:base-sepolia": "DEPLOY_NETWORK=base-sepolia forge script script/DeployPayments.s.sol --rpc-url $BASE_SEPOLIA_RPC --broadcast --private-key $PRIVATE_KEY --sig 'run()'",
64
+ "deploy:registries:merge": "node scripts/merge-registry-deploy.mjs",
62
65
  "verify:base-sepolia": "bash scripts/verify-base-sepolia.sh",
63
66
  "clean": "rm -rf out cache broadcast dist"
64
67
  }
@@ -143,9 +143,21 @@ contract AgentAccount is
143
143
  }
144
144
 
145
145
  /// @dev Spec 007 Phase A.5 — current pending upgrade (set by
146
- /// `upgradeToWithAuthorization` when `_upgradeTimelock > 0`).
146
+ /// `scheduleUpgrade` when `_upgradeTimelock > 0`).
147
147
  PendingUpgrade private _pendingUpgrade;
148
148
 
149
+ /// @dev CA-1 — transient "the current call stack is an authorized upgrade
150
+ /// context" flag. Set while (a) a module action executes
151
+ /// (custody-governed, e.g. `CustodyPolicy.ApplySystemUpdate`, which
152
+ /// carries its own T5 quorum + timelock) or (b) a matured owner-queued
153
+ /// upgrade fires (`executePendingUpgrade`). `_authorizeUpgrade` reads it
154
+ /// so that, once an account configures `_upgradeTimelock`, a DIRECT owner
155
+ /// `upgradeToAndCall` is refused (must `scheduleUpgrade` then
156
+ /// `executePendingUpgrade`), while the custody path keeps working with no
157
+ /// double delay. Transient ⇒ tx-global (visible in the nested
158
+ /// `_authorizeUpgrade` frame) AND absent from the storage layout.
159
+ bool private transient _upgradeAuthorizedCtx;
160
+
149
161
  /// @dev Spec 007 Phase A.5 — maximum permitted upgrade timelock to
150
162
  /// protect against an owner accidentally setting an absurdly
151
163
  /// large delay that bricks future migrations.
@@ -198,6 +210,13 @@ contract AgentAccount is
198
210
  error UpgradeNotReady(uint64 readyAt, uint256 nowTs);
199
211
  error PendingUpgradeMismatch(address pending, address attempted);
200
212
  error UpgradePending();
213
+ /// @notice CA-1 — a direct `upgradeToAndCall` was attempted while a per-account
214
+ /// upgrade timelock is set; route through `scheduleUpgrade` +
215
+ /// `executePendingUpgrade` (or the custody module path) instead.
216
+ error DirectUpgradeBlocked();
217
+ /// @notice CA-1 — `scheduleUpgrade` called with no upgrade timelock configured
218
+ /// (nothing to queue against — upgrade directly).
219
+ error UpgradeTimelockNotSet();
201
220
 
202
221
  // Wave 2A — on-chain authority closure (contract audit C-1..C-3).
203
222
  error ValidatorRequired();
@@ -412,7 +431,20 @@ contract AgentAccount is
412
431
  * will revert here. Master / bundler / session-issuer
413
432
  * therefore cannot upgrade even by submitting the tx.
414
433
  */
415
- function _authorizeUpgrade(address) internal view override onlySelf {}
434
+ function _authorizeUpgrade(address) internal view override onlySelf {
435
+ // CA-1: the per-account upgrade timelock is now ENFORCED here (it was
436
+ // previously dead code — set via `setUpgradeTimelock` but never consulted,
437
+ // so a direct `upgradeToAndCall` fired immediately). When a timelock is
438
+ // configured, a direct owner upgrade is refused; the owner must
439
+ // `scheduleUpgrade` then `executePendingUpgrade` after the delay. Authorized
440
+ // contexts — a custody-module action (its own T5 quorum + timelock) or the
441
+ // matured owner queue — set `_upgradeAuthorizedCtx` and pass through, so the
442
+ // custody path is not double-delayed. A zero timelock keeps the
443
+ // immediate-upgrade default (backward-compatible).
444
+ if (_upgradeTimelock != 0 && !_upgradeAuthorizedCtx) {
445
+ revert DirectUpgradeBlocked();
446
+ }
447
+ }
416
448
 
417
449
  /**
418
450
  * @notice DEPRECATED — single-signature upgrade path (contract audit C-3).
@@ -439,6 +471,21 @@ contract AgentAccount is
439
471
  revert LegacyUpgradePathDisabled();
440
472
  }
441
473
 
474
+ /// @notice CA-1 — queue an implementation upgrade subject to the per-account
475
+ /// upgrade timelock. The owner signs a userOp that calls this
476
+ /// (`onlySelf`); the bound implementation can then be applied via
477
+ /// `executePendingUpgrade` once `_upgradeTimelock` has elapsed. Only
478
+ /// meaningful when a timelock is configured — with none set, upgrade
479
+ /// directly. Overwrites any existing pending upgrade.
480
+ function scheduleUpgrade(address newImplementation) external onlySelf {
481
+ if (newImplementation == address(0)) revert ZeroAddress();
482
+ uint256 tl = _upgradeTimelock;
483
+ if (tl == 0) revert UpgradeTimelockNotSet();
484
+ uint64 readyAt = uint64(block.timestamp + tl);
485
+ _pendingUpgrade = PendingUpgrade({newImplementation: newImplementation, readyAt: readyAt});
486
+ emit UpgradeProposed(newImplementation, readyAt);
487
+ }
488
+
442
489
  /// @notice Execute a previously-queued upgrade. Permissionless once
443
490
  /// the timelock has expired — anyone can pay the gas, but
444
491
  /// the implementation address was bound at queue time.
@@ -450,7 +497,12 @@ contract AgentAccount is
450
497
  // impl can't replay this state.
451
498
  delete _pendingUpgrade;
452
499
  emit UpgradeAuthorized(p.newImplementation);
500
+ // CA-1: authorize the nested `_authorizeUpgrade` for this matured queue
501
+ // (transient, tx-global). Cleared after so a later same-tx direct upgrade
502
+ // isn't inadvertently authorized.
503
+ _upgradeAuthorizedCtx = true;
453
504
  this.upgradeToAndCall(p.newImplementation, "");
505
+ _upgradeAuthorizedCtx = false;
454
506
  }
455
507
 
456
508
  /// @notice Cancel a queued upgrade during the timelock window. Owner
@@ -926,7 +978,14 @@ contract AgentAccount is
926
978
  revert NotInstalledExecutor(msg.sender);
927
979
  }
928
980
  emit ModuleExecuted(msg.sender, target, value);
981
+ // CA-1: a module action is a custody-governed context (only an installed
982
+ // EXECUTOR — the CustodyPolicy — reaches here, gated upstream by its own
983
+ // T5 quorum + timelock). Authorize any upgrade it dispatches so the custody
984
+ // path is exempt from the per-account `_upgradeTimelock` (no double delay).
985
+ // Transient + nonReentrant, cleared after the call.
986
+ _upgradeAuthorizedCtx = true;
929
987
  (bool ok, bytes memory ret) = target.call{value: value}(data);
988
+ _upgradeAuthorizedCtx = false;
930
989
  if (!ok) {
931
990
  assembly { revert(add(ret, 32), mload(ret)) }
932
991
  }
@@ -156,10 +156,13 @@ contract AgentAccountFactory is GovernanceManaged {
156
156
  // timelock (see AgenticGovernance).
157
157
  _validateInitParams(params);
158
158
 
159
- address addr = _getAddressForAgentAccount(params, salt);
159
+ // CA-F1 (audit 2026-06-10): derive the deploy salt from the FULL custody config so the
160
+ // counterfactual address commits to mode/trustees/timelocks too — not just custodians/passkey.
161
+ bytes32 deploySalt = _effectiveSalt(params, timelockOverrides, salt);
162
+ address addr = _getAddressFromSalt(params, deploySalt);
160
163
  if (addr.code.length > 0) return AgentAccount(payable(addr));
161
164
 
162
- ERC1967Proxy proxy = new ERC1967Proxy{salt: bytes32(salt)}(
165
+ ERC1967Proxy proxy = new ERC1967Proxy{salt: deploySalt}(
163
166
  address(accountImplementation),
164
167
  _initData(params)
165
168
  );
@@ -181,12 +184,16 @@ contract AgentAccountFactory is GovernanceManaged {
181
184
  }
182
185
 
183
186
  /// @notice Counterfactual address for `createAgentAccount`.
187
+ /// @dev CA-F1: takes `timelockOverrides` because the address now commits to it. Callers MUST
188
+ /// predict with the SAME (params, timelockOverrides, salt) they will deploy with, else the
189
+ /// predicted address won't match the deployed one.
184
190
  function getAddressForAgentAccount(
185
191
  AgentAccountInitParams calldata params,
192
+ uint32[7] calldata timelockOverrides,
186
193
  uint256 salt
187
194
  ) external view returns (address) {
188
195
  _validateInitParams(params);
189
- return _getAddressForAgentAccount(params, salt);
196
+ return _getAddressFromSalt(params, _effectiveSalt(params, timelockOverrides, salt));
190
197
  }
191
198
 
192
199
  // ─── Internals ──────────────────────────────────────────────────────
@@ -209,22 +216,40 @@ contract AgentAccountFactory is GovernanceManaged {
209
216
  );
210
217
  }
211
218
 
212
- function _getAddressForAgentAccount(
219
+ /// @dev CA-F1 (audit 2026-06-10) — the deploy salt commits to the FULL custody configuration:
220
+ /// mode, trustees, and per-tier timelock overrides. `_initData` already folds custodians +
221
+ /// the initial passkey into the proxy bytecode hash (and thus the address); those, plus the
222
+ /// mode/trustees/timelocks bound here, mean the canonical address IS its complete custody
223
+ /// config. Two `createAgentAccount` calls that differ in ANY of these resolve to DIFFERENT
224
+ /// addresses, so an attacker can no longer front-run a victim's counterfactual address with a
225
+ /// weaker mode, attacker-controlled recovery trustees, or shortened timelocks. The
226
+ /// occupied-address branch is consequently reachable only by an identical-config request —
227
+ /// which also closes CA-F2 (silent adoption of a mismatched pre-existing account) by
228
+ /// construction, with no separate config-equality assertion needed.
229
+ function _effectiveSalt(
213
230
  AgentAccountInitParams calldata params,
231
+ uint32[7] calldata timelockOverrides,
214
232
  uint256 salt
233
+ ) internal pure returns (bytes32) {
234
+ return keccak256(abi.encode(salt, params.mode, params.trustees, timelockOverrides));
235
+ }
236
+
237
+ function _getAddressFromSalt(
238
+ AgentAccountInitParams calldata params,
239
+ bytes32 deploySalt
215
240
  ) internal view returns (address) {
216
241
  bytes memory initData = _initData(params);
217
- return _create2Address(initData, salt);
242
+ return _create2Address(initData, deploySalt);
218
243
  }
219
244
 
220
- function _create2Address(bytes memory initData, uint256 salt) internal view returns (address) {
245
+ function _create2Address(bytes memory initData, bytes32 salt) internal view returns (address) {
221
246
  bytes memory proxyBytecode = abi.encodePacked(
222
247
  type(ERC1967Proxy).creationCode,
223
248
  abi.encode(address(accountImplementation), initData)
224
249
  );
225
250
  bytes32 bytecodeHash = keccak256(proxyBytecode);
226
251
  return address(uint160(uint256(keccak256(
227
- abi.encodePacked(bytes1(0xff), address(this), bytes32(salt), bytecodeHash)
252
+ abi.encodePacked(bytes1(0xff), address(this), salt, bytecodeHash)
228
253
  ))));
229
254
  }
230
255