@aastar/sdk 0.20.4 → 0.20.6

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 (92) hide show
  1. package/README.md +32 -24
  2. package/dist/BaseClient-BjbYP0cf.d.ts +88 -0
  3. package/dist/UserClient-AIIHB54I.js +6 -0
  4. package/dist/{UserClient-JMNNZT4P.js.map → UserClient-AIIHB54I.js.map} +1 -1
  5. package/dist/account.d.ts +48 -1
  6. package/dist/account.js +3 -3
  7. package/dist/admin.d.ts +62 -1
  8. package/dist/admin.js +3 -3
  9. package/dist/airaccount.d.ts +4 -2
  10. package/dist/airaccount.js +2 -4498
  11. package/dist/airaccount.js.map +1 -1
  12. package/dist/channel-CkRRbzT8.d.ts +77 -0
  13. package/dist/channel.d.ts +64 -1
  14. package/dist/channel.js +3 -3
  15. package/dist/{chunk-YSDWB6DO.js → chunk-4EZD7LPE.js} +30 -24
  16. package/dist/chunk-4EZD7LPE.js.map +1 -0
  17. package/dist/{chunk-TBDMSJVL.js → chunk-6QYXGMCR.js} +513 -322
  18. package/dist/chunk-6QYXGMCR.js.map +1 -0
  19. package/dist/{chunk-QNYP7RPI.js → chunk-7ARJ3OSU.js} +29 -21
  20. package/dist/chunk-7ARJ3OSU.js.map +1 -0
  21. package/dist/{chunk-KL6SCEIU.js → chunk-BN5WY5GM.js} +16 -14
  22. package/dist/chunk-BN5WY5GM.js.map +1 -0
  23. package/dist/{chunk-5TDDS3T5.js → chunk-FJ7XECC5.js} +4 -4
  24. package/dist/chunk-FJ7XECC5.js.map +1 -0
  25. package/dist/{chunk-4KRQXOTI.js → chunk-FUU7RIIA.js} +113 -102
  26. package/dist/chunk-FUU7RIIA.js.map +1 -0
  27. package/dist/{chunk-ZJLIKMCA.js → chunk-G3UJC4EL.js} +17 -8
  28. package/dist/chunk-G3UJC4EL.js.map +1 -0
  29. package/dist/{chunk-M5ORP2PM.js → chunk-KDH3UPKD.js} +10 -13
  30. package/dist/chunk-KDH3UPKD.js.map +1 -0
  31. package/dist/chunk-KISL64KW.js +4245 -0
  32. package/dist/chunk-KISL64KW.js.map +1 -0
  33. package/dist/{chunk-Y3KP7R2Y.js → chunk-LXWIPTPX.js} +5 -5
  34. package/dist/chunk-LXWIPTPX.js.map +1 -0
  35. package/dist/{chunk-NAPMVAVJ.js → chunk-MVEWJIPY.js} +124 -110
  36. package/dist/chunk-MVEWJIPY.js.map +1 -0
  37. package/dist/{chunk-LNICTATJ.js → chunk-PAABYXS6.js} +33 -40
  38. package/dist/chunk-PAABYXS6.js.map +1 -0
  39. package/dist/{chunk-O454V5ML.js → chunk-PKCHRXFR.js} +8 -12
  40. package/dist/chunk-PKCHRXFR.js.map +1 -0
  41. package/dist/{chunk-R2RATIK2.js → chunk-TENYCMJ3.js} +31 -31
  42. package/dist/chunk-TENYCMJ3.js.map +1 -0
  43. package/dist/{contract-addresses-VLAHHXOO.js → contract-addresses-N3TOL2WL.js} +3 -3
  44. package/dist/{contract-addresses-VLAHHXOO.js.map → contract-addresses-N3TOL2WL.js.map} +1 -1
  45. package/dist/core.d.ts +6930 -13
  46. package/dist/core.js +2 -2
  47. package/dist/dapp.d.ts +127 -1
  48. package/dist/dapp.js +12 -13
  49. package/dist/dapp.js.map +1 -1
  50. package/dist/doc-types-471vSmPO.d.ts +16 -0
  51. package/dist/enduser.d.ts +261 -1
  52. package/dist/enduser.js +4 -4
  53. package/dist/identity.d.ts +81 -1
  54. package/dist/identity.js +3 -3
  55. package/dist/index-B6SfEQxo.d.ts +47 -0
  56. package/dist/index.d.ts +55 -14
  57. package/dist/index.js +22 -22
  58. package/dist/index.js.map +1 -1
  59. package/dist/kms.d.ts +2986 -0
  60. package/dist/kms.js +5 -0
  61. package/dist/kms.js.map +1 -0
  62. package/dist/operator.d.ts +164 -1
  63. package/dist/operator.js +3 -3
  64. package/dist/paymaster.d.ts +312 -1
  65. package/dist/paymaster.js +3 -3
  66. package/dist/{index.node-GNYPFVZQ.js → src-L5SI5WNB.js} +4 -4
  67. package/dist/src-L5SI5WNB.js.map +1 -0
  68. package/dist/{dist-WMYQOYRO.js → src-X5MIV3EB.js} +5 -5
  69. package/dist/src-X5MIV3EB.js.map +1 -0
  70. package/dist/tier-router-DLiMxs0h.d.ts +321 -0
  71. package/dist/tokens.d.ts +64 -1
  72. package/dist/tokens.js +3 -3
  73. package/dist/x402.d.ts +373 -1
  74. package/dist/x402.js +3 -3
  75. package/package.json +19 -15
  76. package/LICENSE +0 -180
  77. package/dist/UserClient-JMNNZT4P.js +0 -6
  78. package/dist/chunk-4KRQXOTI.js.map +0 -1
  79. package/dist/chunk-5TDDS3T5.js.map +0 -1
  80. package/dist/chunk-KL6SCEIU.js.map +0 -1
  81. package/dist/chunk-LNICTATJ.js.map +0 -1
  82. package/dist/chunk-M5ORP2PM.js.map +0 -1
  83. package/dist/chunk-NAPMVAVJ.js.map +0 -1
  84. package/dist/chunk-O454V5ML.js.map +0 -1
  85. package/dist/chunk-QNYP7RPI.js.map +0 -1
  86. package/dist/chunk-R2RATIK2.js.map +0 -1
  87. package/dist/chunk-TBDMSJVL.js.map +0 -1
  88. package/dist/chunk-Y3KP7R2Y.js.map +0 -1
  89. package/dist/chunk-YSDWB6DO.js.map +0 -1
  90. package/dist/chunk-ZJLIKMCA.js.map +0 -1
  91. package/dist/dist-WMYQOYRO.js.map +0 -1
  92. package/dist/index.node-GNYPFVZQ.js.map +0 -1
@@ -1,4501 +1,5 @@
1
- import { ecdsa, bls12_381, weierstrass, sha256 } from './chunk-4KRQXOTI.js';
2
- export { BLSManager, CryptoUtil, ERC4337Utils, PasskeyManager, UserOpBuilder, YAAAClient } from './chunk-4KRQXOTI.js';
1
+ export { ACCOUNT_ABI, AGENT_SESSION_KEY_VALIDATOR_ABI, AIRACCOUNT_ABI, AIRACCOUNT_ADDRESSES, AIRACCOUNT_FACTORY_ABI, AIR_ACCOUNT_COMPOSITE_VALIDATOR_ABI, AIR_ACCOUNT_DELEGATE_ABI, AIR_ACCOUNT_DELEGATE_ADDRESS, ALG_ID, AccountManager, AgentRegistryService, BLSSignatureService, CALLDATA_PARSER_REGISTRY_ABI, ConsoleLogger, DEFAULT_CREDENTIAL_ID, DEFAULT_KMS_ENDPOINT, DEFAULT_ORIGIN, DEFAULT_RP_ID, DvtPendingConfirmationError, EIP7702DelegateService, ENTRYPOINT_ABI_V6, ENTRYPOINT_ABI_V7_V8, ENTRYPOINT_ADDRESSES, ERC20_ABI, ERC8004Service, ERC8004_ADDRESSES, EXECUTE_BATCH_SELECTOR, EXECUTE_SELECTOR, EXECUTE_USER_OP_SELECTOR, EntryPointVersion, EthereumProvider, FACTORY_ABI_V6, FACTORY_ABI_V7_V8, FORCE_EXIT_MODULE_ABI, ForceExitService, GLOBAL_GUARD_ABI, GuardChecker, GuardStateReader, KmsAgentService, KmsHttpClient, KmsManager, KmsMonitorService, KmsPaymentSigner, KmsSessionService, KmsSigner, L2_TYPE, LocalWalletSigner, MAX_GUARDIANS, MODULE_TYPE, MemoryStorage, ModuleManager, P256PasskeySigner, PaymasterManager, PaymasterPriceStalenessError, RECOVERY_THRESHOLD, RECOVERY_TIMELOCK_SECONDS, RecoveryService, SESSION_KEY_VALIDATOR_ABI, SessionKeyService, SilentLogger, TIER_GUARD_HOOK_ABI, TokenService, TransferManager, VALIDATOR_ABI, WEIGHT_CHANGE_EXPIRY_SECONDS, WEIGHT_CHANGE_THRESHOLD, WEIGHT_CHANGE_TIMELOCK_SECONDS, WalletManager, WeightedSignatureService, YAAAServerClient, base64UrlDecode, base64UrlEncode, beginAuthenticationChallenge, beginGrantSessionChallenge, buildAuthenticationCredential, buildAuthenticatorData, buildClientDataJSON, buildInstallModuleHash, buildUninstallModuleHash, computeOapdSalt, erc8004AddressesForChain, getOapdAddress, getOapdAddressWithChainId, isExecuteUserOpWrapped, isOapdDeployed, isPendingConfirmation, packP256SessionSignature, packSecp256k1SessionSignature, runAuthenticationCeremony, runGrantSessionCeremony, runWebAuthnCeremony, sepoliaV07Config, validateConfig, wrapExecuteUserOp } from './chunk-KISL64KW.js';
2
+ export { ALG_BLS, ALG_CUMULATIVE_T2, ALG_CUMULATIVE_T3, ALG_ECDSA, ALG_P256, BLSManager, CryptoUtil, ERC4337Utils, PasskeyManager, UserOpBuilder, YAAAClient, algIdForTier, resolveTier } from './chunk-FUU7RIIA.js';
3
3
  import './chunk-PZ5AY32C.js';
4
- import { ethers } from 'ethers';
5
- import axios from 'axios';
6
- import { createHash } from 'crypto';
7
-
8
- // ../../node_modules/.pnpm/@noble+curves@2.0.1/node_modules/@noble/curves/nist.js
9
- var p256_CURVE = /* @__PURE__ */ (() => ({
10
- p: BigInt("0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"),
11
- n: BigInt("0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"),
12
- h: BigInt(1),
13
- a: BigInt("0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc"),
14
- b: BigInt("0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b"),
15
- Gx: BigInt("0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296"),
16
- Gy: BigInt("0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5")
17
- }))();
18
- var p256_Point = /* @__PURE__ */ weierstrass(p256_CURVE);
19
- var p256 = /* @__PURE__ */ ecdsa(p256_Point, sha256);
20
-
21
- // ../airaccount/dist/server/index.js
22
- var EntryPointVersion = /* @__PURE__ */ ((EntryPointVersion2) => {
23
- EntryPointVersion2["V0_6"] = "0.6";
24
- EntryPointVersion2["V0_7"] = "0.7";
25
- EntryPointVersion2["V0_8"] = "0.8";
26
- return EntryPointVersion2;
27
- })(EntryPointVersion || {});
28
- var ENTRYPOINT_ADDRESSES = {
29
- [
30
- "0.6"
31
- /* V0_6 */
32
- ]: {
33
- sepolia: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
34
- mainnet: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
35
- optimism: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
36
- },
37
- [
38
- "0.7"
39
- /* V0_7 */
40
- ]: {
41
- sepolia: "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
42
- mainnet: "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
43
- optimism: "0x0000000071727De22E5E9d8BAf0edAc6f37da032"
44
- },
45
- [
46
- "0.8"
47
- /* V0_8 */
48
- ]: {
49
- sepolia: "0x0576a174D229E3cFA37253523E645A78A0C91B57",
50
- mainnet: "0x0576a174D229E3cFA37253523E645A78A0C91B57",
51
- optimism: "0x0576a174D229E3cFA37253523E645A78A0C91B57"
52
- }
53
- };
54
- var ENTRYPOINT_ABI_V6 = [
55
- "function simulateValidation((address,uint256,bytes,bytes,uint256,uint256,uint256,uint256,uint256,bytes,bytes) userOp) external",
56
- "function getNonce(address sender, uint192 key) external view returns (uint256 nonce)",
57
- "function getUserOpHash((address,uint256,bytes,bytes,uint256,uint256,uint256,uint256,uint256,bytes,bytes) userOp) external view returns (bytes32)",
58
- "function handleOps((address,uint256,bytes,bytes,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[] ops, address payable beneficiary) external"
59
- ];
60
- var ENTRYPOINT_ABI_V7_V8 = [
61
- "function simulateValidation((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes) packedUserOp) external",
62
- "function getNonce(address sender, uint192 key) external view returns (uint256 nonce)",
63
- "function getUserOpHash((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes) packedUserOp) external view returns (bytes32)",
64
- "function handleOps((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes)[] ops, address payable beneficiary) external"
65
- ];
66
- var FACTORY_ABI_V6 = [
67
- "function getAddress(address creator, address signer, address validator, bool useAAStarValidator, uint256 salt) view returns (address)",
68
- "function createAccountWithAAStarValidator(address creator, address signer, address aaStarValidator, bool useAAStarValidator, uint256 salt) returns (address)"
69
- ];
70
- var FACTORY_ABI_V7_V8 = [
71
- "function getAddress(address creator, address signer, address validator, bool useAAStarValidator, uint256 salt) view returns (address)",
72
- "function createAccount(address creator, address signer, address aaStarValidator, bool useAAStarValidator, uint256 salt) returns (address)"
73
- ];
74
- var ACCOUNT_ABI = [
75
- "function execute(address dest, uint256 value, bytes calldata func) external"
76
- ];
77
- var VALIDATOR_ABI = [
78
- "function getGasEstimate(uint256 nodeCount) external pure returns (uint256 gasEstimate)"
79
- ];
80
- var AIRACCOUNT_ADDRESSES = {
81
- sepolia: {
82
- // M4 factory (legacy — 3-field InitConfig)
83
- factoryM4: "0x914db0a849f55e68a726c72fd02b7114b1176d88",
84
- // M5 factory r5 — 6-field InitConfig, guardian acceptance sigs required
85
- factoryM5: "0xd72a236d84be6c388a8bc7deb64afd54704ae385",
86
- /** @deprecated defaultCommunityGuardian was address(0); superseded by r6 and r4. Do not use for new accounts. */
87
- factoryM7r5Prev: "0xa0007c5dB27548D8c1582773856dB1D123107383",
88
- // ── Deprecated: r6 addresses (2026-03-29 deployment, superseded by r4 audit-final) ──────────
89
- // Retain for legacy account lookups and historical event indexing ONLY.
90
- // DO NOT use for new account creation — CREATE2 address will differ from r4.
91
- /** @deprecated Use {@link factory} (r4 audit-final) for new accounts. */
92
- factoryM7r6: "0x42f82d77f9cf940686b6a64a369245cb563e0e85",
93
- /** @deprecated Use {@link accountImpl} (r4 audit-final). */
94
- accountImplM7r6: "0x2F1B4EB63143D338bE78d0AF878B806f075080c1",
95
- /** @deprecated Use {@link compositeValidator} (r4 audit-final). */
96
- compositeValidatorM7r6: "0x4135c539fec5e200fe9762b721f6829b2315cbe1",
97
- /** @deprecated Use {@link tierGuardHook} (r4 audit-final). */
98
- tierGuardHookM7r6: "0x73572e9e6138fd53465ee243e2fb4842cf86a787",
99
- /** @deprecated Use {@link agentSessionKeyValidator} (r4 audit-final). */
100
- agentSessionKeyValidatorM7r6: "0xa3e52db4b6e0a9d7cd5dd1414a90eedcf950e029",
101
- // ── Deprecated: r4 audit-final (v0.16.0 era — pre-beta). Retained for existing account recovery. ─
102
- /** @deprecated Use factory (beta.4) for new accounts. */
103
- factoryM7r4: "0x61bBAf9E1b8Fd78fF874776cFa50497dB9d43C3F",
104
- /** @deprecated */
105
- accountImplM7r4: "0xA674D308ce22230B70412b20Ee5a66fC6B24F49c",
106
- /** @deprecated Use validatorRouter. */
107
- validatorRouterM7r4: "0x730a162Ce3202b94cC5B74181B75b11eBB3045B1",
108
- /** @deprecated */
109
- compositeValidatorM7r4: "0xB65569950C48AA56dbe876915ca3605fD6FF2980",
110
- /** @deprecated */
111
- tierGuardHookM7r4: "0x67f878295cFF7451CBD2A775C4490607AF1b07d7",
112
- /** @deprecated */
113
- agentSessionKeyValidatorM7r4: "0x1F06961e133217801F92e1CF552187F594a32873",
114
- // ── Current: v0.17.2-beta.4 (bundler-compat — algId whitelist on account + executeUserOp) ─────
115
- // beta.4 upgrades the AirAccount account contracts; it REUSES the beta.3 router /
116
- // sessionKey / forceExit / BLS (per the beta.4 migration notes).
117
- factory: "0x3a9127a5f0b4ca734d54629d0c3ad9f52739c071",
118
- // beta.4
119
- factoryM7: "0x3a9127a5f0b4ca734d54629d0c3ad9f52739c071",
120
- // beta.4
121
- accountImpl: "0x0321Fa7261Ad5945e4B3f0c73aFD7D9392E39796",
122
- // beta.4
123
- validatorRouter: "0x3c2b06f50300912794f29de031b33dd37bb8d6c6",
124
- // beta.3 (reused; M3 timelock)
125
- blsAlgorithm: "0xB82127182A855B82eED05e47536FcE568b626457",
126
- blsAggregator: "0xBAc3f24946d0eb15189E1c01e38182e5B078Bbc1",
127
- superPaymaster: "0xFb090E82bD041C6e9787eDEbE1D3BE55b3c7266a",
128
- // beta.3 ERC-7579 modules (reused by beta.4)
129
- sessionKeyValidator: "0x655ca2e9a2d1178f7fbcea1856560d1e0c657ebf",
130
- forceExitModule: "0xdb396ca2dc279f9bcb95fa3d8275f77c9f0c8702",
131
- airAccountDelegate: "0x4bda4849b80cc444fb2da65beec0724005c6675c",
132
- // beta.4
133
- airAccountExtension: "0x20FB2A65a52Fc6507FdD51260f055017a2BA2860",
134
- // beta.4
135
- agentRegistry: "0xe1320c35485b4d7817866a8d0d8f77dd58202253",
136
- // beta.4
137
- calldataParserRegistry: "0x076EE45d2a97F70FCb2e45809DC5f9b72BB4883F",
138
- uniswapV3Parser: "0x5671810ac8aa1857397870e60232579cfc519515"
139
- }
140
- };
141
- var AIRACCOUNT_ABI = [
142
- // ── Core execution ──
143
- "function execute(address dest, uint256 value, bytes calldata func) external",
144
- "function executeBatch(address[] calldata dest, uint256[] calldata value, bytes[] calldata func) external",
145
- // ── ERC-7579 Module Management (M7.2) ──
146
- "function installModule(uint256 moduleTypeId, address module, bytes calldata initData) external",
147
- "function uninstallModule(uint256 moduleTypeId, address module, bytes calldata deInitData) external",
148
- "function executeFromExecutor(bytes32 mode, bytes calldata executionCalldata) external returns (bytes[] memory returnData)",
149
- // ── ERC-7579 Introspection ──
150
- "function accountId() external pure returns (string memory)",
151
- "function supportsModule(uint256 moduleTypeId) external pure returns (bool)",
152
- "function isModuleInstalled(uint256 moduleTypeId, address module, bytes calldata additionalContext) external view returns (bool)",
153
- // ── ERC-1271 / ERC-165 ──
154
- "function isValidSignature(bytes32 hash, bytes calldata sig) external view returns (bytes4)",
155
- "function validateCompositeSignature(bytes32 hash, bytes calldata sig) external returns (uint256)",
156
- "function supportsInterface(bytes4 interfaceId) external pure returns (bool)",
157
- // ── State readers ──
158
- "function owner() external view returns (address)",
159
- "function entryPoint() external view returns (address)",
160
- "function validator() external view returns (address)",
161
- "function guard() external view returns (address)",
162
- "function guardianCount() external view returns (uint8)",
163
- "function guardians(uint256 index) external view returns (address)",
164
- "function p256KeyX() external view returns (bytes32)",
165
- "function p256KeyY() external view returns (bytes32)",
166
- "function getConfigDescription() external view returns (tuple(address accountOwner, address guardAddress, uint256 dailyLimit, uint256 dailyRemaining, uint256 tier1Limit, uint256 tier2Limit, address[3] guardianAddresses, uint8 guardianCount, bool hasP256Key, bool hasValidator, bool hasAggregator, bool hasActiveRecovery))",
167
- // ── Owner / key management ──
168
- "function setValidator(address _validator) external",
169
- "function setP256Key(bytes32 _x, bytes32 _y) external",
170
- "function setTierLimits(uint256 _tier1, uint256 _tier2) external",
171
- "function modifyTierLimitsWithGuardians(uint256 _tier1, uint256 _tier2, uint256 deadline, bytes[] calldata guardianSigs) external",
172
- // ── Algorithm whitelist (v0.17.2-beta.4: single source of truth on the ACCOUNT, not the guard) ──
173
- "function approvedAlgorithms(uint8 algId) external view returns (bool)",
174
- "function guardApproveAlgorithm(uint8 algId) external",
175
- // ── Social / guardian recovery (F28) ──
176
- // Guardian set: owner adds guardians (max 3); removal needs RECOVERY_THRESHOLD guardian sigs.
177
- // Recovery lifecycle: a guardian proposes a new owner (starts the timelock), guardians
178
- // approve to reach 2-of-3 threshold, then anyone executes after the timelock; guardians
179
- // (not the owner) can vote to cancel. See RecoveryService for the full flow.
180
- "function addGuardian(address _guardian) external",
181
- "function removeGuardian(uint8 index, bytes[] calldata guardianSigs) external",
182
- "function proposeRecovery(address _newOwner) external",
183
- "function approveRecovery() external",
184
- "function cancelRecovery() external",
185
- "function executeRecovery() external",
186
- "function activeRecovery() external view returns (address newOwner, uint256 proposedAt, uint256 approvalBitmap, uint256 cancellationBitmap)",
187
- // ── Weighted-signature governance (algId 0x07) ──
188
- // WeightConfig tuple = (passkeyWeight, ecdsaWeight, blsWeight, guardian0Weight,
189
- // guardian1Weight, guardian2Weight, _padding, tier1Threshold, tier2Threshold, tier3Threshold)
190
- "function setWeightConfig((uint8 passkeyWeight, uint8 ecdsaWeight, uint8 blsWeight, uint8 guardian0Weight, uint8 guardian1Weight, uint8 guardian2Weight, uint8 _padding, uint8 tier1Threshold, uint8 tier2Threshold, uint8 tier3Threshold) config) external",
191
- "function proposeWeightChange((uint8 passkeyWeight, uint8 ecdsaWeight, uint8 blsWeight, uint8 guardian0Weight, uint8 guardian1Weight, uint8 guardian2Weight, uint8 _padding, uint8 tier1Threshold, uint8 tier2Threshold, uint8 tier3Threshold) proposed) external",
192
- "function approveWeightChange() external",
193
- "function cancelWeightChange() external",
194
- "function executeWeightChange() external",
195
- "function weightConfig() external view returns (uint8 passkeyWeight, uint8 ecdsaWeight, uint8 blsWeight, uint8 guardian0Weight, uint8 guardian1Weight, uint8 guardian2Weight, uint8 _padding, uint8 tier1Threshold, uint8 tier2Threshold, uint8 tier3Threshold)",
196
- "function pendingWeightChange() external view returns ((uint8 passkeyWeight, uint8 ecdsaWeight, uint8 blsWeight, uint8 guardian0Weight, uint8 guardian1Weight, uint8 guardian2Weight, uint8 _padding, uint8 tier1Threshold, uint8 tier2Threshold, uint8 tier3Threshold) proposed, uint256 proposedAt, uint256 approvalBitmap)",
197
- // ── ERC-4337 v0.7 bundler entrypoint (v0.17.2-beta.4) ──
198
- // Routes a UserOp whose callData starts with the executeUserOp selector to the account,
199
- // re-deriving the signature algId in-frame (fixes guard-account bundler gas estimation).
200
- // Only an inner execute()/executeBatch() may be wrapped (else reverts UnsupportedInnerSelector).
201
- "function executeUserOp((address sender, uint256 nonce, bytes initCode, bytes callData, bytes32 accountGasLimits, uint256 preVerificationGas, bytes32 gasFees, bytes paymasterAndData, bytes signature) userOp, bytes32 userOpHash) external",
202
- // ── Events ──
203
- "event ModuleInstalled(uint256 indexed moduleTypeId, address indexed module)",
204
- "event ModuleUninstalled(uint256 indexed moduleTypeId, address indexed module)",
205
- "event OwnerChanged(address indexed oldOwner, address indexed newOwner)",
206
- "event RecoveryProposed(address indexed newOwner, address indexed proposedBy)",
207
- "event RecoveryExecuted(address indexed oldOwner, address indexed newOwner)"
208
- ];
209
- var AIRACCOUNT_FACTORY_ABI = [
210
- // Full config creation
211
- "function createAccount(address owner, uint256 salt, (address[3] guardians, uint256 dailyLimit, uint8[] approvedAlgIds, uint256 minDailyLimit, address[] initialTokens, (uint256 tier1Limit, uint256 tier2Limit, uint256 dailyLimit)[] initialTokenConfigs) config) external returns (address)",
212
- "function getAddress(address owner, uint256 salt, (address[3] guardians, uint256 dailyLimit, uint8[] approvedAlgIds, uint256 minDailyLimit, address[] initialTokens, (uint256 tier1Limit, uint256 tier2Limit, uint256 dailyLimit)[] initialTokenConfigs) config) external view returns (address)",
213
- // Default guardian setup (requires guardian acceptance sigs — M5.3+)
214
- "function createAccountWithDefaults(address owner, uint256 salt, address guardian1, bytes guardian1Sig, address guardian2, bytes guardian2Sig, uint256 dailyLimit) external returns (address)",
215
- "function getAddressWithDefaults(address owner, uint256 salt, address guardian1, address guardian2, uint256 dailyLimit) external view returns (address)",
216
- // Agent-account creation (V7: agentKey + human-guardian2 co-owned account, registered in AgentRegistry)
217
- "function createAgentAccount(address agentKey, bytes32 agentId, address guardian2, bytes guardian2Sig, bytes agentKeySig, uint48 deadline, uint256 dailyLimit) external returns (address account)",
218
- "function getAgentAddress(address humanOwner, address agentKey, bytes32 agentId) external view returns (address)",
219
- "function setAgentRegistry(address _agentRegistry) external",
220
- "function agentRegistry() external view returns (address)",
221
- // M7 immutable state
222
- "function implementation() external view returns (address)",
223
- "function entryPoint() external view returns (address)",
224
- "function defaultCommunityGuardian() external view returns (address)",
225
- "function defaultValidatorModule() external view returns (address)",
226
- "function defaultHookModule() external view returns (address)",
227
- // M7.4 ERC-7828 chain-qualified address helpers
228
- "function getChainQualifiedAddress(address account) external view returns (bytes32)",
229
- "function getAddressWithChainId(address owner, uint256 salt, (address[3] guardians, uint256 dailyLimit, uint8[] approvedAlgIds, uint256 minDailyLimit, address[] initialTokens, (uint256 tier1Limit, uint256 tier2Limit, uint256 dailyLimit)[] initialTokenConfigs) config) external view returns (address account, bytes32 chainQualified)",
230
- // Events
231
- "event AccountCreated(address indexed account, address indexed owner, uint256 salt)"
232
- ];
233
- var GLOBAL_GUARD_ABI = [
234
- "function remainingDailyAllowance() external view returns (uint256)",
235
- "function dailyLimit() external view returns (uint256)",
236
- "function account() external view returns (address)",
237
- // Spend accounting (record* — algId dropped from the ETH path; kept for per-token tier math)
238
- "function recordSpend(uint256 value) external returns (bool)",
239
- "function recordTokenSpend(address token, uint256 amount, uint8 algId) external returns (bool)"
240
- ];
241
- var ERC20_ABI = [
242
- "function name() view returns (string)",
243
- "function symbol() view returns (string)",
244
- "function decimals() view returns (uint8)",
245
- "function totalSupply() view returns (uint256)",
246
- "function balanceOf(address owner) view returns (uint256)",
247
- "function transfer(address to, uint256 amount) returns (bool)",
248
- "function allowance(address owner, address spender) view returns (uint256)",
249
- "function approve(address spender, uint256 amount) returns (bool)"
250
- ];
251
- var AGENT_SESSION_KEY_VALIDATOR_ABI = [
252
- // ERC-7579 lifecycle
253
- "function onInstall(bytes calldata data) external",
254
- "function onUninstall(bytes calldata data) external",
255
- "function isInitialized(address smartAccount) external view returns (bool)",
256
- // Session management
257
- "function grantAgentSession(address sessionKey, (uint48 expiry, uint16 velocityLimit, uint32 velocityWindow, bool revoked, address[] callTargets, bytes4[] selectorAllowlist) cfg) external",
258
- "function delegateSession(address account, address subKey, (uint48 expiry, uint16 velocityLimit, uint32 velocityWindow, bool revoked, address[] callTargets, bytes4[] selectorAllowlist) subCfg) external",
259
- "function revokeAgentSession(address sessionKey) external",
260
- // Validation
261
- "function validateUserOp((address sender, uint256 nonce, bytes initCode, bytes callData, bytes32 accountGasLimits, uint256 preVerificationGas, bytes32 gasFees, bytes paymasterAndData, bytes signature) userOp, bytes32 userOpHash) external returns (uint256 validationData)",
262
- "function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata data) external pure returns (bytes4)",
263
- // Enforcement
264
- "function enforceSessionScope(address account, address sessionKey, address callTarget, bytes4 selector) external view",
265
- // State readers
266
- "function agentSessions(address account, address sessionKey) external view returns (uint48 expiry, uint16 velocityLimit, uint32 velocityWindow, bool revoked, address[] memory callTargets, bytes4[] memory selectorAllowlist)",
267
- "function sessionStates(address account, address sessionKey) external view returns (uint256 callCount, uint256 windowStart)",
268
- "function sessionKeyOwner(address sessionKey) external view returns (address)",
269
- "function delegatedBy(address account, address subKey) external view returns (address parentKey)",
270
- // Events
271
- "event AgentSessionGranted(address indexed account, address indexed sessionKey, uint48 expiry)",
272
- "event AgentSessionRevoked(address indexed account, address indexed sessionKey)",
273
- "event AgentSessionDelegated(address indexed parentAccount, address indexed parentKey, address indexed subKey, uint48 expiry)"
274
- ];
275
- var TIER_GUARD_HOOK_ABI = [
276
- // ERC-7579 lifecycle
277
- "function onInstall(bytes calldata data) external",
278
- "function onUninstall(bytes calldata data) external",
279
- "function isInitialized(address smartAccount) external view returns (bool)",
280
- // ERC-7579 Hook interface
281
- "function preCheck(address msgSender, uint256 msgValue, bytes calldata msgData) external returns (bytes memory hookData)",
282
- "function postCheck(bytes calldata hookData) external",
283
- // State readers
284
- "function accountGuard(address account) external view returns (address)",
285
- "function accountTier1(address account) external view returns (uint256)",
286
- "function accountTier2(address account) external view returns (uint256)"
287
- ];
288
- var AIR_ACCOUNT_COMPOSITE_VALIDATOR_ABI = [
289
- // ERC-7579 lifecycle
290
- "function onInstall(bytes calldata data) external",
291
- "function onUninstall(bytes calldata data) external",
292
- "function isInitialized(address smartAccount) external view returns (bool)",
293
- // ERC-7579 Validator interface
294
- "function validateUserOp((address sender, uint256 nonce, bytes initCode, bytes callData, bytes32 accountGasLimits, uint256 preVerificationGas, bytes32 gasFees, bytes paymasterAndData, bytes signature) userOp, bytes32 userOpHash) external returns (uint256 validationData)",
295
- "function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata data) external view returns (bytes4 magicValue)"
296
- ];
297
- var FORCE_EXIT_MODULE_ABI = [
298
- // ERC-7579 lifecycle
299
- "function onInstall(bytes calldata data) external",
300
- "function onUninstall(bytes calldata data) external",
301
- "function isInitialized(address smartAccount) external view returns (bool)",
302
- // Force exit flow
303
- "function proposeForceExit(address target, uint256 value, bytes calldata data) external",
304
- "function approveForceExit(address account, bytes calldata guardianSig) external",
305
- "function executeForceExit(address account) external",
306
- "function cancelForceExit(address account) external",
307
- // State readers
308
- "function accountL2Type(address account) external view returns (uint8)",
309
- "function getPendingExit(address account) external view returns (address target, uint256 value, bytes memory data, uint256 proposedAt, uint256 approvalBitmap, address[3] memory guardians)",
310
- // Events
311
- "event ExitProposed(address indexed account, address indexed target, uint256 value)",
312
- "event ExitApproved(address indexed account, address indexed guardian, uint256 bitmap)",
313
- "event ExitExecuted(address indexed account, address indexed target, uint256 value)",
314
- "event ExitCancelled(address indexed account)"
315
- ];
316
- var MODULE_TYPE = {
317
- VALIDATOR: 1,
318
- EXECUTOR: 2,
319
- FALLBACK: 3,
320
- HOOK: 4
321
- };
322
- var ALG_ID = {
323
- BLS: 1,
324
- ECDSA: 2,
325
- P256: 3,
326
- CUMULATIVE_T2: 4,
327
- // P256 + BLS
328
- CUMULATIVE_T3: 5,
329
- // P256 + BLS + Guardian ECDSA
330
- COMBINED_T1: 6,
331
- // P256 AND ECDSA simultaneously
332
- WEIGHTED: 7,
333
- // Weighted multi-signature
334
- SESSION_KEY: 8,
335
- // Time-limited session key (SessionKeyValidator)
336
- AGENT_SESSION_KEY: 9
337
- // AI agent session key (AgentSessionKeyValidator)
338
- };
339
- var SESSION_KEY_VALIDATOR_ABI = [
340
- // Session struct (8 fields) — authoritative tuple shape from
341
- // airaccount-contract/src/validators/SessionKeyValidator.sol and
342
- // packages/core/src/abis/SessionKeyValidator.json. The grant/build/read
343
- // functions take/return this tuple as a single arg — NOT flat params.
344
- // Canonical tuple type: (uint48,address,bytes4,bool,uint16,uint32,address[],bytes4[])
345
- // ECDSA session key
346
- "function grantSession(address account, address sessionKey, (uint48 expiry, address contractScope, bytes4 selectorScope, bool revoked, uint16 velocityLimit, uint32 velocityWindow, address[] callTargets, bytes4[] selectorAllowlist) cfg, bytes calldata ownerSig) external",
347
- "function grantSessionDirect(address account, address sessionKey, (uint48 expiry, address contractScope, bytes4 selectorScope, bool revoked, uint16 velocityLimit, uint32 velocityWindow, address[] callTargets, bytes4[] selectorAllowlist) cfg) external",
348
- "function revokeSession(address account, address sessionKey) external",
349
- "function isSessionActive(address account, address sessionKey) external view returns (bool)",
350
- "function getSession(address account, address sessionKey) external view returns ((uint48 expiry, address contractScope, bytes4 selectorScope, bool revoked, uint16 velocityLimit, uint32 velocityWindow, address[] callTargets, bytes4[] selectorAllowlist))",
351
- "function buildGrantHash(address account, address sessionKey, (uint48 expiry, address contractScope, bytes4 selectorScope, bool revoked, uint16 velocityLimit, uint32 velocityWindow, address[] callTargets, bytes4[] selectorAllowlist) cfg) external view returns (bytes32)",
352
- // P256 session key
353
- "function grantP256Session(address account, bytes32 p256KeyX, bytes32 p256KeyY, (uint48 expiry, address contractScope, bytes4 selectorScope, bool revoked, uint16 velocityLimit, uint32 velocityWindow, address[] callTargets, bytes4[] selectorAllowlist) cfg, bytes calldata ownerSig) external",
354
- "function grantP256SessionDirect(address account, bytes32 p256KeyX, bytes32 p256KeyY, (uint48 expiry, address contractScope, bytes4 selectorScope, bool revoked, uint16 velocityLimit, uint32 velocityWindow, address[] callTargets, bytes4[] selectorAllowlist) cfg) external",
355
- "function revokeP256Session(address account, bytes32 p256KeyX, bytes32 p256KeyY) external",
356
- "function isP256SessionActive(address account, bytes32 p256KeyX, bytes32 p256KeyY) external view returns (bool)",
357
- "function getP256Session(address account, bytes32 p256KeyHash) external view returns ((uint48 expiry, address contractScope, bytes4 selectorScope, bool revoked, uint16 velocityLimit, uint32 velocityWindow, address[] callTargets, bytes4[] selectorAllowlist))",
358
- "function buildP256GrantHash(address account, bytes32 p256KeyX, bytes32 p256KeyY, (uint48 expiry, address contractScope, bytes4 selectorScope, bool revoked, uint16 velocityLimit, uint32 velocityWindow, address[] callTargets, bytes4[] selectorAllowlist) cfg) external view returns (bytes32)",
359
- // Events
360
- "event SessionGranted(address indexed account, address indexed sessionKey, uint48 expiry, address contractScope, bytes4 selectorScope)",
361
- "event SessionRevoked(address indexed account, address indexed sessionKey)",
362
- "event P256SessionGranted(address indexed account, bytes32 indexed p256KeyHash, uint48 expiry)",
363
- "event P256SessionRevoked(address indexed account, bytes32 indexed p256KeyHash)"
364
- ];
365
- var CALLDATA_PARSER_REGISTRY_ABI = [
366
- "function registerParser(address dest, address parser) external",
367
- "function getParser(address dest) external view returns (address)",
368
- "function transferOwnership(address newOwner) external",
369
- "function parserFor(address dest) external view returns (address)",
370
- "event ParserRegistered(address indexed dest, address indexed parser)",
371
- "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)"
372
- ];
373
- var AIR_ACCOUNT_DELEGATE_ABI = [
374
- // EIP-7702 初始化(仅限 EOA 自身调用)
375
- "function initialize(address guardian1, bytes calldata g1Sig, address guardian2, bytes calldata g2Sig, uint256 dailyLimit) external",
376
- // ERC-4337 执行
377
- "function validateUserOp((address sender, uint256 nonce, bytes initCode, bytes callData, bytes32 accountGasLimits, uint256 preVerificationGas, bytes32 gasFees, bytes paymasterAndData, bytes signature) userOp, bytes32 userOpHash, uint256 missingAccountFunds) external returns (uint256)",
378
- "function execute(address dest, uint256 value, bytes calldata data) external",
379
- "function executeBatch(address[] calldata dest, uint256[] calldata value, bytes[] calldata data) external",
380
- // 社会恢复(Rescue,EIP-7702 术语避免与 AirAccount Recovery 混淆)
381
- "function initiateRescue(address rescueTo) external",
382
- "function approveRescue() external",
383
- "function executeRescue() external",
384
- // Events
385
- "event DelegateInitialized(address indexed eoa, address guard, address g1, address g2)",
386
- "event RescueInitiated(address indexed eoa, address rescueTo, address indexed initiator)",
387
- "event RescueApproved(address indexed eoa, address indexed guardian, uint8 approvals)",
388
- "event RescueExecuted(address indexed eoa, address rescueTo, uint256 ethAmount)",
389
- "event RescueCancelled(address indexed eoa)"
390
- ];
391
- function sepoliaV07Config(version = "M7") {
392
- const factoryAddress = version === "M5" ? AIRACCOUNT_ADDRESSES.sepolia.factoryM5 : version === "M7r6" ? AIRACCOUNT_ADDRESSES.sepolia.factoryM7r6 : AIRACCOUNT_ADDRESSES.sepolia.factory;
393
- return {
394
- entryPointAddress: ENTRYPOINT_ADDRESSES[
395
- "0.7"
396
- /* V0_7 */
397
- ].sepolia,
398
- factoryAddress,
399
- validatorAddress: AIRACCOUNT_ADDRESSES.sepolia.validatorRouter
400
- };
401
- }
402
- function validateConfig(config) {
403
- if (!config.rpcUrl) {
404
- throw new Error("ServerConfig: rpcUrl is required");
405
- }
406
- if (!config.bundlerRpcUrl) {
407
- throw new Error("ServerConfig: bundlerRpcUrl is required");
408
- }
409
- if (!config.chainId) {
410
- throw new Error("ServerConfig: chainId is required");
411
- }
412
- const { entryPoints } = config;
413
- if (!entryPoints || !entryPoints.v06 && !entryPoints.v07 && !entryPoints.v08) {
414
- throw new Error("ServerConfig: at least one entryPoint version must be configured");
415
- }
416
- for (const [key, ep] of Object.entries(entryPoints)) {
417
- if (ep) {
418
- if (!ep.entryPointAddress) {
419
- throw new Error(`ServerConfig: entryPoints.${key}.entryPointAddress is required`);
420
- }
421
- if (!ep.factoryAddress) {
422
- throw new Error(`ServerConfig: entryPoints.${key}.factoryAddress is required`);
423
- }
424
- if (!ep.validatorAddress) {
425
- throw new Error(`ServerConfig: entryPoints.${key}.validatorAddress is required`);
426
- }
427
- }
428
- }
429
- if (!config.storage) {
430
- throw new Error("ServerConfig: storage adapter is required");
431
- }
432
- if (!config.signer) {
433
- throw new Error("ServerConfig: signer adapter is required");
434
- }
435
- }
436
- var ConsoleLogger = class {
437
- constructor(prefix = "[YAAA]") {
438
- this.prefix = prefix;
439
- }
440
- debug(message, ...args) {
441
- console.debug(`${this.prefix} ${message}`, ...args);
442
- }
443
- log(message, ...args) {
444
- console.log(`${this.prefix} ${message}`, ...args);
445
- }
446
- warn(message, ...args) {
447
- console.warn(`${this.prefix} ${message}`, ...args);
448
- }
449
- error(message, ...args) {
450
- console.error(`${this.prefix} ${message}`, ...args);
451
- }
452
- };
453
- var SilentLogger = class {
454
- debug() {
455
- }
456
- log() {
457
- }
458
- warn() {
459
- }
460
- error() {
461
- }
462
- };
463
- var EthereumProvider = class {
464
- provider;
465
- bundlerProvider;
466
- config;
467
- logger;
468
- constructor(config) {
469
- this.config = config;
470
- this.logger = config.logger ?? new ConsoleLogger("[EthereumProvider]");
471
- this.provider = new ethers.JsonRpcProvider(config.rpcUrl);
472
- this.bundlerProvider = new ethers.JsonRpcProvider(config.bundlerRpcUrl);
473
- }
474
- getProvider() {
475
- return this.provider;
476
- }
477
- getBundlerProvider() {
478
- return this.bundlerProvider;
479
- }
480
- // ── Config helpers ──────────────────────────────────────────────
481
- getVersionConfig(version) {
482
- const map = {
483
- [
484
- "0.6"
485
- /* V0_6 */
486
- ]: this.config.entryPoints.v06,
487
- [
488
- "0.7"
489
- /* V0_7 */
490
- ]: this.config.entryPoints.v07,
491
- [
492
- "0.8"
493
- /* V0_8 */
494
- ]: this.config.entryPoints.v08
495
- };
496
- const versionConfig = map[version];
497
- if (!versionConfig) {
498
- throw new Error(`EntryPoint version ${version} is not configured`);
499
- }
500
- return versionConfig;
501
- }
502
- getEntryPointAddress(version) {
503
- return this.getVersionConfig(version).entryPointAddress;
504
- }
505
- getFactoryAddress(version) {
506
- return this.getVersionConfig(version).factoryAddress;
507
- }
508
- getValidatorAddress(version) {
509
- return this.getVersionConfig(version).validatorAddress;
510
- }
511
- getDefaultVersion() {
512
- const v = this.config.defaultVersion;
513
- if (v === "0.7") return "0.7";
514
- if (v === "0.8") return "0.8";
515
- return "0.6";
516
- }
517
- // ── Contract factories ──────────────────────────────────────────
518
- getFactoryContract(version = "0.6") {
519
- const address = this.getFactoryAddress(version);
520
- const abi = version === "0.6" ? FACTORY_ABI_V6 : AIRACCOUNT_FACTORY_ABI;
521
- return new ethers.Contract(address, abi, this.provider);
522
- }
523
- getEntryPointContract(version = "0.6") {
524
- const address = this.getEntryPointAddress(version);
525
- const abi = version === "0.6" ? ENTRYPOINT_ABI_V6 : ENTRYPOINT_ABI_V7_V8;
526
- return new ethers.Contract(address, abi, this.provider);
527
- }
528
- getValidatorContract(version = "0.6") {
529
- const address = this.getValidatorAddress(version);
530
- return new ethers.Contract(address, VALIDATOR_ABI, this.provider);
531
- }
532
- getAccountContract(address) {
533
- return new ethers.Contract(address, AIRACCOUNT_ABI, this.provider);
534
- }
535
- // ── M7 Module contracts ─────────────────────────────────────────
536
- // M7 r4 module helpers — addresses renamed to *M7r4 suffix in beta.3 to avoid ambiguity.
537
- // These methods are retained for backwards compatibility; callers should pass an explicit address.
538
- getAgentSessionKeyValidatorContract(address = AIRACCOUNT_ADDRESSES.sepolia.agentSessionKeyValidatorM7r4) {
539
- return new ethers.Contract(address, AGENT_SESSION_KEY_VALIDATOR_ABI, this.provider);
540
- }
541
- getTierGuardHookContract(address = AIRACCOUNT_ADDRESSES.sepolia.tierGuardHookM7r4) {
542
- return new ethers.Contract(address, TIER_GUARD_HOOK_ABI, this.provider);
543
- }
544
- getCompositeValidatorContract(address = AIRACCOUNT_ADDRESSES.sepolia.compositeValidatorM7r4) {
545
- return new ethers.Contract(address, AIR_ACCOUNT_COMPOSITE_VALIDATOR_ABI, this.provider);
546
- }
547
- getForceExitModuleContract(address) {
548
- return new ethers.Contract(address, FORCE_EXIT_MODULE_ABI, this.provider);
549
- }
550
- // ── On-chain queries ────────────────────────────────────────────
551
- async getBalance(address) {
552
- const balance = await this.provider.getBalance(address);
553
- return ethers.formatEther(balance);
554
- }
555
- async getNonce(accountAddress, key = 0, version = "0.6") {
556
- const entryPoint = this.getEntryPointContract(version);
557
- return await entryPoint.getNonce(accountAddress, key);
558
- }
559
- async getUserOpHash(userOp, version = "0.6") {
560
- const entryPoint = this.getEntryPointContract(version);
561
- if (version === "0.6") {
562
- const op = userOp;
563
- const userOpArray = [
564
- op.sender,
565
- op.nonce,
566
- op.initCode || "0x",
567
- op.callData,
568
- op.callGasLimit,
569
- op.verificationGasLimit,
570
- op.preVerificationGas,
571
- op.maxFeePerGas,
572
- op.maxPriorityFeePerGas,
573
- op.paymasterAndData || "0x",
574
- "0x"
575
- // Always use empty signature for hash calculation
576
- ];
577
- return await entryPoint.getUserOpHash(userOpArray);
578
- } else {
579
- const packedOp = userOp;
580
- const packedOpArray = [
581
- packedOp.sender,
582
- packedOp.nonce,
583
- packedOp.initCode || "0x",
584
- packedOp.callData,
585
- packedOp.accountGasLimits,
586
- packedOp.preVerificationGas,
587
- packedOp.gasFees,
588
- packedOp.paymasterAndData || "0x",
589
- "0x"
590
- ];
591
- return await entryPoint.getUserOpHash(packedOpArray);
592
- }
593
- }
594
- // ── Bundler RPC ─────────────────────────────────────────────────
595
- async estimateUserOperationGas(userOp, version = "0.6") {
596
- try {
597
- return await this.bundlerProvider.send("eth_estimateUserOperationGas", [
598
- userOp,
599
- this.getEntryPointAddress(version)
600
- ]);
601
- } catch {
602
- return {
603
- callGasLimit: "0x249f0",
604
- verificationGasLimit: "0x3d0900",
605
- // 4M — enough for M4 factory deployment + BLS verification
606
- preVerificationGas: "0x11170"
607
- };
608
- }
609
- }
610
- async sendUserOperation(userOp, version = "0.6") {
611
- return await this.bundlerProvider.send("eth_sendUserOperation", [
612
- userOp,
613
- this.getEntryPointAddress(version)
614
- ]);
615
- }
616
- async getUserOperationReceipt(userOpHash) {
617
- return await this.bundlerProvider.send("eth_getUserOperationReceipt", [userOpHash]);
618
- }
619
- async waitForUserOp(userOpHash, maxAttempts = 60) {
620
- const pollInterval = 2e3;
621
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
622
- try {
623
- const receipt = await this.getUserOperationReceipt(userOpHash);
624
- if (receipt) {
625
- const txHash = receipt.transactionHash || receipt.receipt?.transactionHash;
626
- if (txHash) return txHash;
627
- }
628
- } catch {
629
- }
630
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
631
- }
632
- throw new Error(`UserOp timeout: ${userOpHash}`);
633
- }
634
- async getUserOperationGasPrice() {
635
- try {
636
- const gasPrice = await this.bundlerProvider.send("pimlico_getUserOperationGasPrice", []);
637
- return {
638
- maxFeePerGas: gasPrice.fast.maxFeePerGas,
639
- maxPriorityFeePerGas: gasPrice.fast.maxPriorityFeePerGas
640
- };
641
- } catch {
642
- try {
643
- const feeData = await this.provider.getFeeData();
644
- const baseFee = feeData.maxFeePerGas || ethers.parseUnits("20", "gwei");
645
- const priorityFee = feeData.maxPriorityFeePerGas || ethers.parseUnits("2", "gwei");
646
- const maxFeePerGas = baseFee * 3n / 2n;
647
- const maxPriorityFeePerGas = priorityFee * 3n / 2n;
648
- return {
649
- maxFeePerGas: "0x" + maxFeePerGas.toString(16),
650
- maxPriorityFeePerGas: "0x" + maxPriorityFeePerGas.toString(16)
651
- };
652
- } catch {
653
- return {
654
- maxFeePerGas: "0x" + ethers.parseUnits("3", "gwei").toString(16),
655
- maxPriorityFeePerGas: "0x" + ethers.parseUnits("1", "gwei").toString(16)
656
- };
657
- }
658
- }
659
- }
660
- };
661
- var AccountManager = class {
662
- constructor(ethereum, storage, signer, logger) {
663
- this.ethereum = ethereum;
664
- this.storage = storage;
665
- this.signer = signer;
666
- this.logger = logger ?? new ConsoleLogger("[AccountManager]");
667
- }
668
- logger;
669
- async createAccount(userId, options) {
670
- const version = options?.entryPointVersion ?? this.ethereum.getDefaultVersion();
671
- const versionStr = version;
672
- const existingAccounts = await this.storage.getAccounts();
673
- const existing = existingAccounts.find(
674
- (a) => a.userId === userId && a.entryPointVersion === versionStr
675
- );
676
- if (existing) return existing;
677
- const factory = this.ethereum.getFactoryContract(version);
678
- const validatorAddress = this.ethereum.getValidatorContract(version).target || this.ethereum.getValidatorAddress(version);
679
- const { address: signerAddress } = await this.signer.ensureSigner(userId);
680
- const salt = options?.salt ?? Math.floor(Math.random() * 1e6);
681
- const dailyLimitValue = options?.dailyLimit ?? 0n;
682
- const minimalConfig = [
683
- [ethers.ZeroAddress, ethers.ZeroAddress, ethers.ZeroAddress],
684
- // guardians (address[3])
685
- dailyLimitValue,
686
- // dailyLimit (0 = no guard)
687
- [],
688
- // approvedAlgIds
689
- 0n,
690
- // minDailyLimit
691
- [],
692
- // initialTokens
693
- []
694
- // initialTokenConfigs
695
- ];
696
- const accountAddress = await factory.getFunction("getAddress")(signerAddress, salt, minimalConfig);
697
- let deployed = false;
698
- try {
699
- const code = await this.ethereum.getProvider().getCode(accountAddress);
700
- deployed = code !== "0x";
701
- } catch {
702
- }
703
- const account = {
704
- userId,
705
- address: accountAddress,
706
- signerAddress,
707
- salt,
708
- deployed,
709
- deploymentTxHash: null,
710
- validatorAddress,
711
- entryPointVersion: versionStr,
712
- factoryAddress: factory.target || this.ethereum.getFactoryAddress(version),
713
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
714
- // Persist dailyLimit so buildUserOperation can reconstruct identical initCode at deploy time.
715
- ...dailyLimitValue > 0n ? { dailyLimit: dailyLimitValue.toString() } : {}
716
- };
717
- await this.storage.saveAccount(account);
718
- return account;
719
- }
720
- async getAccount(userId) {
721
- const account = await this.storage.findAccountByUserId(userId);
722
- if (!account) return null;
723
- let balance = "0";
724
- try {
725
- balance = await this.ethereum.getBalance(account.address);
726
- } catch {
727
- }
728
- const version = account.entryPointVersion || "0.6";
729
- const nonce = await this.ethereum.getNonce(account.address, 0, version);
730
- return { ...account, balance, nonce: nonce.toString() };
731
- }
732
- async getAccountAddress(userId) {
733
- const account = await this.storage.findAccountByUserId(userId);
734
- if (!account) throw new Error("Account not found");
735
- return account.address;
736
- }
737
- async getAccountBalance(userId) {
738
- const account = await this.storage.findAccountByUserId(userId);
739
- if (!account) throw new Error("Account not found");
740
- const balance = await this.ethereum.getBalance(account.address);
741
- return {
742
- address: account.address,
743
- balance,
744
- balanceInWei: ethers.parseEther(balance).toString()
745
- };
746
- }
747
- async getAccountNonce(userId) {
748
- const account = await this.storage.findAccountByUserId(userId);
749
- if (!account) throw new Error("Account not found");
750
- const nonce = await this.ethereum.getNonce(account.address);
751
- return { address: account.address, nonce: nonce.toString() };
752
- }
753
- async getAccountByUserId(userId) {
754
- return this.storage.findAccountByUserId(userId);
755
- }
756
- /**
757
- * Build the acceptance hash that guardian devices must sign before account creation.
758
- *
759
- * Encoding: keccak256(solidityPacked(
760
- * ["string","uint256","address","address","uint256","uint256"],
761
- * ["ACCEPT_GUARDIAN", chainId, factoryAddress, owner, salt, dailyLimit]
762
- * ))
763
- *
764
- * dailyLimit is bound in the hash (PR #47 / C-3) to prevent a front-runner from
765
- * replaying guardian sigs with a weaker limit on the same counterfactual address.
766
- *
767
- * Returns the RAW keccak256 hash (no EIP-191 prefix).
768
- * Guardians MUST sign via personal_sign / ethers.signMessage(ethers.getBytes(hash)).
769
- * Do NOT use eth_sign — the EIP-191 "\x19Ethereum Signed Message:\n32" prefix
770
- * is applied inside the contract (toEthSignedMessageHash) before ecrecover, not here.
771
- *
772
- * @returns raw hex keccak256 hash — encode this into the QR code shown to guardian devices
773
- */
774
- buildGuardianAcceptanceHash(owner, salt, factoryAddress, chainId, dailyLimit) {
775
- if (typeof salt === "number" && !Number.isSafeInteger(salt)) {
776
- throw new Error(
777
- `salt value ${salt} exceeds Number.MAX_SAFE_INTEGER; pass as bigint to avoid precision loss`
778
- );
779
- }
780
- return ethers.keccak256(
781
- ethers.solidityPacked(
782
- ["string", "uint256", "address", "address", "uint256", "uint256"],
783
- ["ACCEPT_GUARDIAN", chainId, factoryAddress, owner, salt, dailyLimit]
784
- )
785
- );
786
- }
787
- /**
788
- * Encode calldata for modifyTierLimitsWithGuardians() — guardian-gated tier-limit change (PR #43).
789
- *
790
- * Both tier1 and tier2 can be raised or lowered, subject to guardian approval.
791
- * Caller is responsible for building and submitting the resulting UserOp.
792
- *
793
- * @param tier1 New Tier-1 ceiling in wei (ECDSA-only spending; 0 = no limit)
794
- * @param tier2 New Tier-2 ceiling in wei (dual-factor; 0 = no limit)
795
- * @param deadline Unix timestamp — guardian sigs rejected after this
796
- * @param guardianSigs 65-byte EIP-191 hex signatures from required guardians
797
- */
798
- encodeModifyTierLimits(tier1, tier2, deadline, guardianSigs) {
799
- const iface = new ethers.Interface(AIRACCOUNT_ABI);
800
- return iface.encodeFunctionData("modifyTierLimitsWithGuardians", [
801
- tier1,
802
- tier2,
803
- deadline,
804
- guardianSigs
805
- ]);
806
- }
807
- /**
808
- * Create an AirAccount with 3 on-chain guardians:
809
- * - guardian1 and guardian2: user's own devices (passkeys on phone 1 and phone 2)
810
- * - guardian3: team Safe multisig (defaultCommunityGuardian, set in factory at deploy time)
811
- *
812
- * Both guardian1 and guardian2 must sign the acceptance hash produced by
813
- * buildGuardianAcceptanceHash() before this method is called.
814
- *
815
- * Recovery: any 2-of-3 guardians can initiate social recovery after a 48h timelock.
816
- */
817
- async createAccountWithGuardians(userId, params) {
818
- if (params.guardian1.toLowerCase() === params.guardian2.toLowerCase()) {
819
- throw new Error("guardian1 and guardian2 must be different addresses");
820
- }
821
- if (params.dailyLimit <= 0n) {
822
- throw new Error("Guardian accounts require dailyLimit > 0 (on-chain enforcement)");
823
- }
824
- const version = params.entryPointVersion ?? this.ethereum.getDefaultVersion();
825
- if (version === "0.6") {
826
- throw new Error(
827
- "createAccountWithGuardians requires EntryPoint v0.7 or v0.8; v0.6 factory does not support getAddressWithDefaults"
828
- );
829
- }
830
- const versionStr = version;
831
- const existingAccounts = await this.storage.getAccounts();
832
- const existing = existingAccounts.find(
833
- (a) => a.userId === userId && a.entryPointVersion === versionStr && a.guardian1
834
- );
835
- if (existing) return existing;
836
- const { address: signerAddress } = await this.signer.ensureSigner(userId);
837
- const salt = params.salt ?? Math.floor(Math.random() * 1e6);
838
- const factory = this.ethereum.getFactoryContract(version);
839
- const factoryAddress = factory.target ?? this.ethereum.getFactoryAddress(version);
840
- const accountAddress = await factory.getFunction("getAddressWithDefaults")(
841
- signerAddress,
842
- salt,
843
- params.guardian1,
844
- params.guardian2,
845
- params.dailyLimit
846
- );
847
- let deployed = false;
848
- try {
849
- const code = await this.ethereum.getProvider().getCode(accountAddress);
850
- deployed = code !== "0x";
851
- } catch {
852
- }
853
- const validatorAddress = this.ethereum.getValidatorAddress(version);
854
- const account = {
855
- userId,
856
- address: accountAddress,
857
- signerAddress,
858
- salt,
859
- deployed,
860
- deploymentTxHash: null,
861
- validatorAddress,
862
- entryPointVersion: versionStr,
863
- factoryAddress,
864
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
865
- // Persist dailyLimit so transfer-manager can reconstruct identical initCode at deploy time.
866
- ...params.dailyLimit > 0n ? { dailyLimit: params.dailyLimit.toString() } : {},
867
- // Persist guardian addresses and sigs so transfer-manager can use createAccountWithDefaults
868
- // to reconstruct the correct initCode on first UserOp deployment.
869
- guardian1: params.guardian1,
870
- guardian1Sig: params.guardian1Sig,
871
- guardian2: params.guardian2,
872
- guardian2Sig: params.guardian2Sig
873
- };
874
- await this.storage.saveAccount(account);
875
- this.logger.log(`[AccountManager] account created with guardians: ${accountAddress}`);
876
- return account;
877
- }
878
- };
879
- var EXECUTE_USER_OP_SELECTOR = ethers.id("executeUserOp((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes),bytes32)").slice(0, 10);
880
- var EXECUTE_SELECTOR = ethers.id("execute(address,uint256,bytes)").slice(0, 10);
881
- var EXECUTE_BATCH_SELECTOR = ethers.id("executeBatch(address[],uint256[],bytes[])").slice(0, 10);
882
- function wrapExecuteUserOp(innerCallData) {
883
- if (!/^0x[0-9a-fA-F]*$/.test(innerCallData) || innerCallData.length < 10) {
884
- throw new Error("wrapExecuteUserOp: innerCallData must be 0x-prefixed calldata with a 4-byte selector");
885
- }
886
- const sel = innerCallData.slice(0, 10).toLowerCase();
887
- if (sel !== EXECUTE_SELECTOR && sel !== EXECUTE_BATCH_SELECTOR) {
888
- throw new Error(
889
- `wrapExecuteUserOp: only execute()/executeBatch() may be wrapped (got selector ${sel}); the account reverts UnsupportedInnerSelector otherwise`
890
- );
891
- }
892
- return ethers.concat([EXECUTE_USER_OP_SELECTOR, innerCallData]);
893
- }
894
- function isExecuteUserOpWrapped(callData) {
895
- return callData.slice(0, 10).toLowerCase() === EXECUTE_USER_OP_SELECTOR;
896
- }
897
- var PaymasterPriceStalenessError = class extends Error {
898
- constructor(paymasterAddress, ageSeconds, thresholdSeconds) {
899
- super(
900
- `Paymaster ${paymasterAddress} price is stale (age: ${Math.floor(ageSeconds / 60)}min, threshold: ${Math.floor(thresholdSeconds / 60)}min). Call updatePrice() on the paymaster contract before retrying.`
901
- );
902
- this.paymasterAddress = paymasterAddress;
903
- this.ageSeconds = ageSeconds;
904
- this.thresholdSeconds = thresholdSeconds;
905
- this.name = "PaymasterPriceStalenessError";
906
- }
907
- };
908
- var PAYMASTER_PRICE_ABI = [
909
- "function token() view returns (address)",
910
- "function cachedPriceTimestamp() view returns (uint256)",
911
- "function priceStalenessThreshold() view returns (uint256)",
912
- "function updatePrice() external"
913
- ];
914
- var PaymasterManager = class {
915
- constructor(ethereum, storage, logger) {
916
- this.ethereum = ethereum;
917
- this.storage = storage;
918
- this.logger = logger ?? new ConsoleLogger("[PaymasterManager]");
919
- }
920
- logger;
921
- async getAvailablePaymasters(userId) {
922
- const paymasters = await this.storage.getPaymasters(userId);
923
- return paymasters.map((config) => ({
924
- name: config.name,
925
- address: config.address,
926
- configured: !!config.address && config.address !== "0x"
927
- }));
928
- }
929
- async addCustomPaymaster(userId, name, address, type = "custom", apiKey, endpoint) {
930
- const paymaster = {
931
- id: `${userId}-${name}-${Date.now()}`,
932
- name,
933
- address,
934
- type,
935
- apiKey,
936
- endpoint,
937
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
938
- };
939
- await this.storage.savePaymaster(userId, paymaster);
940
- }
941
- async removeCustomPaymaster(userId, name) {
942
- return this.storage.removePaymaster(userId, name);
943
- }
944
- /**
945
- * Check whether a paymaster's on-chain price cache is still fresh.
946
- * Returns `{ fresh, ageSeconds, thresholdSeconds }`.
947
- * Throws if the contract does not implement `cachedPriceTimestamp()` / `priceStalenessThreshold()`.
948
- */
949
- async checkPriceFreshness(paymasterAddress) {
950
- const provider = this.ethereum.getProvider();
951
- const contract = new ethers.Contract(paymasterAddress, PAYMASTER_PRICE_ABI, provider);
952
- const [timestamp, threshold] = await Promise.all([
953
- contract.cachedPriceTimestamp(),
954
- contract.priceStalenessThreshold()
955
- ]);
956
- const nowSeconds = Math.floor(Date.now() / 1e3);
957
- const ageSeconds = nowSeconds - Number(timestamp);
958
- const thresholdSeconds = Number(threshold);
959
- return {
960
- fresh: ageSeconds <= thresholdSeconds,
961
- ageSeconds,
962
- thresholdSeconds
963
- };
964
- }
965
- /**
966
- * Call `updatePrice()` on a paymaster contract (permissionless).
967
- * Useful when `checkPriceFreshness()` reports stale price.
968
- *
969
- * @param signer - An ethers Signer that will send the transaction (must have gas).
970
- */
971
- async updatePrice(paymasterAddress, signer) {
972
- const contract = new ethers.Contract(paymasterAddress, PAYMASTER_PRICE_ABI, signer);
973
- const tx = await contract.updatePrice({ gasLimit: 3e5 });
974
- await tx.wait();
975
- this.logger.log(`Paymaster ${paymasterAddress} price updated, tx: ${tx.hash}`);
976
- return tx.hash;
977
- }
978
- async getPaymasterData(userId, paymasterName, userOp, entryPoint, customAddress, options) {
979
- if (paymasterName === "custom-user-provided" && customAddress) {
980
- const formattedAddress = customAddress.toLowerCase().startsWith("0x") ? customAddress : `0x${customAddress}`;
981
- if (!/^0x[a-fA-F0-9]{40}$/.test(formattedAddress)) {
982
- throw new Error(`Invalid paymaster address format: ${customAddress}`);
983
- }
984
- const isV07OrV08 = entryPoint.toLowerCase() === "0x0000000071727De22E5E9d8BAf0edAc6f37da032".toLowerCase() || entryPoint.toLowerCase() === "0x0576a174D229E3cFA37253523E645A78A0C91B57".toLowerCase();
985
- if (isV07OrV08) {
986
- const provider = this.ethereum.getProvider();
987
- let isSuperPaymaster = false;
988
- let operatorAddress = "0x";
989
- try {
990
- const spContract = new ethers.Contract(
991
- formattedAddress,
992
- [
993
- "function owner() view returns (address)",
994
- "function operators(address) view returns (bool,uint256,address,uint256)"
995
- ],
996
- provider
997
- );
998
- const owner = await spContract.owner();
999
- const opInfo = await spContract.operators(owner);
1000
- if (opInfo && opInfo[0] === true) {
1001
- isSuperPaymaster = true;
1002
- operatorAddress = owner;
1003
- this.logger.log(`SuperPaymaster detected, operator: ${operatorAddress}`);
1004
- }
1005
- } catch {
1006
- }
1007
- if (isSuperPaymaster) {
1008
- const verGas = BigInt(8e4);
1009
- const postGas = BigInt(3e5);
1010
- const maxRate = (BigInt(1) << BigInt(256)) - BigInt(1);
1011
- return ethers.concat([
1012
- formattedAddress,
1013
- ethers.zeroPadValue(ethers.toBeHex(verGas), 16),
1014
- ethers.zeroPadValue(ethers.toBeHex(postGas), 16),
1015
- operatorAddress,
1016
- ethers.zeroPadValue(ethers.toBeHex(maxRate), 32)
1017
- ]);
1018
- }
1019
- const paymasterVerificationGasLimit = BigInt(196608);
1020
- const paymasterPostOpGasLimit = BigInt(196608);
1021
- let tokenAddress = options?.tokenAddress ?? null;
1022
- if (tokenAddress) {
1023
- this.logger.log(`PaymasterV4 token from options: ${tokenAddress}`);
1024
- } else {
1025
- try {
1026
- const pmContract = new ethers.Contract(
1027
- formattedAddress,
1028
- PAYMASTER_PRICE_ABI,
1029
- provider
1030
- );
1031
- tokenAddress = await pmContract.token();
1032
- if (tokenAddress === ethers.ZeroAddress) tokenAddress = null;
1033
- if (tokenAddress) {
1034
- this.logger.log(`PaymasterV4 token auto-detected: ${tokenAddress}`);
1035
- }
1036
- } catch {
1037
- this.logger.log(`PaymasterV4 token() not available, paymasterData will have no token`);
1038
- }
1039
- }
1040
- const parts = [
1041
- formattedAddress,
1042
- ethers.zeroPadValue(ethers.toBeHex(paymasterVerificationGasLimit), 16),
1043
- ethers.zeroPadValue(ethers.toBeHex(paymasterPostOpGasLimit), 16)
1044
- ];
1045
- if (tokenAddress) {
1046
- parts.push(tokenAddress);
1047
- }
1048
- return ethers.concat(parts);
1049
- }
1050
- return formattedAddress;
1051
- }
1052
- const paymasters = await this.storage.getPaymasters(userId);
1053
- const config = paymasters.find((p) => p.name === paymasterName);
1054
- if (!config) {
1055
- throw new Error(`Paymaster ${paymasterName} not found`);
1056
- }
1057
- switch (config.type) {
1058
- case "pimlico":
1059
- if (!config.apiKey) return "0x";
1060
- return this.getPimlicoPaymasterData(config, userOp, entryPoint);
1061
- case "stackup":
1062
- if (!config.apiKey) return "0x";
1063
- return this.getStackUpPaymasterData(config, userOp, entryPoint);
1064
- case "alchemy":
1065
- if (!config.apiKey) return "0x";
1066
- return this.getAlchemyPaymasterData(config, userOp, entryPoint);
1067
- case "custom":
1068
- if (config.address.toLowerCase() === "0x0000000000325602a77416A16136FDafd04b299f".toLowerCase() && config.apiKey) {
1069
- return this.getPimlicoPaymasterData(
1070
- { ...config, type: "pimlico", endpoint: "https://api.pimlico.io/v2/11155111/rpc" },
1071
- userOp,
1072
- entryPoint
1073
- );
1074
- }
1075
- return config.address;
1076
- default:
1077
- return "0x";
1078
- }
1079
- }
1080
- async getPimlicoPaymasterData(config, userOp, entryPoint) {
1081
- const url = `${config.endpoint}?apikey=${config.apiKey}`;
1082
- const response = await globalThis.fetch(url, {
1083
- method: "POST",
1084
- headers: { "Content-Type": "application/json" },
1085
- body: JSON.stringify({
1086
- jsonrpc: "2.0",
1087
- method: "pm_sponsorUserOperation",
1088
- params: [userOp, entryPoint, {}],
1089
- id: 1
1090
- })
1091
- });
1092
- const result = await response.json();
1093
- if (result.error) {
1094
- throw new Error(
1095
- `Pimlico sponsorship failed: ${result.error.message || JSON.stringify(result.error)}`
1096
- );
1097
- }
1098
- if (result.result) {
1099
- if (result.result.paymasterAndData) {
1100
- return result.result.paymasterAndData;
1101
- }
1102
- if (result.result.paymaster) {
1103
- return ethers.concat([
1104
- result.result.paymaster,
1105
- ethers.zeroPadValue(
1106
- ethers.toBeHex(BigInt(result.result.paymasterVerificationGasLimit || "0x30000")),
1107
- 16
1108
- ),
1109
- ethers.zeroPadValue(
1110
- ethers.toBeHex(BigInt(result.result.paymasterPostOpGasLimit || "0x30000")),
1111
- 16
1112
- ),
1113
- result.result.paymasterData || "0x"
1114
- ]);
1115
- }
1116
- }
1117
- throw new Error("Pimlico API did not return valid paymaster data");
1118
- }
1119
- async getStackUpPaymasterData(config, userOp, entryPoint) {
1120
- try {
1121
- const response = await globalThis.fetch(`${config.endpoint}/${config.apiKey}`, {
1122
- method: "POST",
1123
- headers: { "Content-Type": "application/json" },
1124
- body: JSON.stringify({
1125
- jsonrpc: "2.0",
1126
- method: "pm_sponsorUserOperation",
1127
- params: { userOperation: userOp, entryPoint, context: { type: "payg" } },
1128
- id: 1
1129
- })
1130
- });
1131
- const result = await response.json();
1132
- if (result.error) return "0x";
1133
- return result.result || "0x";
1134
- } catch {
1135
- return "0x";
1136
- }
1137
- }
1138
- async getAlchemyPaymasterData(config, userOp, entryPoint) {
1139
- try {
1140
- const response = await globalThis.fetch(`${config.endpoint}/${config.apiKey}`, {
1141
- method: "POST",
1142
- headers: { "Content-Type": "application/json" },
1143
- body: JSON.stringify({
1144
- jsonrpc: "2.0",
1145
- method: "alchemy_requestGasAndPaymasterAndData",
1146
- params: [{ policyId: "default", entryPoint, userOperation: userOp }],
1147
- id: 1
1148
- })
1149
- });
1150
- const result = await response.json();
1151
- if (result.error) return "0x";
1152
- return result.result?.paymasterAndData || "0x";
1153
- } catch {
1154
- return "0x";
1155
- }
1156
- }
1157
- };
1158
- var ERC4337Utils2 = class _ERC4337Utils {
1159
- static packAccountGasLimits(verificationGasLimit, callGasLimit) {
1160
- const vgl = BigInt(verificationGasLimit);
1161
- const cgl = BigInt(callGasLimit);
1162
- const packed = vgl << 128n | cgl;
1163
- return "0x" + packed.toString(16).padStart(64, "0");
1164
- }
1165
- static unpackAccountGasLimits(accountGasLimits) {
1166
- const packed = BigInt(accountGasLimits);
1167
- return {
1168
- verificationGasLimit: packed >> 128n,
1169
- callGasLimit: packed & (1n << 128n) - 1n
1170
- };
1171
- }
1172
- static packGasFees(maxPriorityFeePerGas, maxFeePerGas) {
1173
- const priority = BigInt(maxPriorityFeePerGas);
1174
- const max = BigInt(maxFeePerGas);
1175
- const packed = priority << 128n | max;
1176
- return "0x" + packed.toString(16).padStart(64, "0");
1177
- }
1178
- static unpackGasFees(gasFees) {
1179
- const packed = BigInt(gasFees);
1180
- return {
1181
- maxPriorityFeePerGas: packed >> 128n,
1182
- maxFeePerGas: packed & (1n << 128n) - 1n
1183
- };
1184
- }
1185
- static packUserOperation(userOp) {
1186
- return {
1187
- sender: userOp.sender,
1188
- nonce: userOp.nonce,
1189
- initCode: userOp.initCode || "0x",
1190
- callData: userOp.callData,
1191
- accountGasLimits: _ERC4337Utils.packAccountGasLimits(
1192
- userOp.verificationGasLimit,
1193
- userOp.callGasLimit
1194
- ),
1195
- preVerificationGas: userOp.preVerificationGas,
1196
- gasFees: _ERC4337Utils.packGasFees(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas),
1197
- paymasterAndData: userOp.paymasterAndData || "0x",
1198
- signature: userOp.signature || "0x"
1199
- };
1200
- }
1201
- static unpackUserOperation(packedOp) {
1202
- const gasLimits = _ERC4337Utils.unpackAccountGasLimits(packedOp.accountGasLimits);
1203
- const gasFees = _ERC4337Utils.unpackGasFees(packedOp.gasFees);
1204
- return {
1205
- sender: packedOp.sender,
1206
- nonce: packedOp.nonce,
1207
- initCode: packedOp.initCode,
1208
- callData: packedOp.callData,
1209
- callGasLimit: "0x" + gasLimits.callGasLimit.toString(16),
1210
- verificationGasLimit: "0x" + gasLimits.verificationGasLimit.toString(16),
1211
- preVerificationGas: packedOp.preVerificationGas,
1212
- maxFeePerGas: "0x" + gasFees.maxFeePerGas.toString(16),
1213
- maxPriorityFeePerGas: "0x" + gasFees.maxPriorityFeePerGas.toString(16),
1214
- paymasterAndData: packedOp.paymasterAndData,
1215
- signature: packedOp.signature
1216
- };
1217
- }
1218
- };
1219
- async function detectSignatureStrategy(provider, accountAddress) {
1220
- try {
1221
- const accountCode = await provider.getCode(accountAddress);
1222
- if (accountCode === "0x") {
1223
- return { useECDSA: true, isCompositeValidator: true };
1224
- }
1225
- const acc = new ethers.Contract(
1226
- accountAddress,
1227
- ["function validator() view returns (address)"],
1228
- provider
1229
- );
1230
- const v = await acc.validator();
1231
- return { useECDSA: v === ethers.ZeroAddress, isCompositeValidator: true };
1232
- } catch {
1233
- return { useECDSA: true, isCompositeValidator: false };
1234
- }
1235
- }
1236
- function generateId() {
1237
- const hex = () => Math.random().toString(16).slice(2, 10);
1238
- return `${hex()}${hex()}-${hex()}-${hex()}-${hex()}-${hex()}${hex()}${hex()}`;
1239
- }
1240
- var TransferManager = class {
1241
- constructor(ethereum, accountManager, blsService, paymasterManager, tokenService, storage, signer, logger, guardChecker) {
1242
- this.ethereum = ethereum;
1243
- this.accountManager = accountManager;
1244
- this.blsService = blsService;
1245
- this.paymasterManager = paymasterManager;
1246
- this.tokenService = tokenService;
1247
- this.storage = storage;
1248
- this.signer = signer;
1249
- this.logger = logger ?? new ConsoleLogger("[TransferManager]");
1250
- this.guardChecker = guardChecker ?? null;
1251
- }
1252
- logger;
1253
- guardChecker;
1254
- async executeTransfer(userId, params) {
1255
- const account = await this.accountManager.getAccountByUserId(userId);
1256
- if (!account) throw new Error("User account not found");
1257
- const code = await this.ethereum.getProvider().getCode(account.address);
1258
- const needsDeployment = code === "0x";
1259
- if (needsDeployment) {
1260
- this.logger.log("Account needs deployment, will deploy with first transaction");
1261
- }
1262
- const smartAccountBalance = parseFloat(await this.ethereum.getBalance(account.address));
1263
- const isTokenTransfer = !!params.tokenAddress;
1264
- const transferAmount = isTokenTransfer ? 0 : parseFloat(params.amount);
1265
- if (!params.usePaymaster) {
1266
- const minRequiredBalance = 2e-4;
1267
- const totalNeeded = transferAmount + minRequiredBalance;
1268
- if (smartAccountBalance < totalNeeded) {
1269
- throw new Error(
1270
- `Insufficient balance: Account has ${smartAccountBalance} ETH but needs ${totalNeeded} ETH`
1271
- );
1272
- }
1273
- } else if (!isTokenTransfer && transferAmount > smartAccountBalance) {
1274
- throw new Error(
1275
- `Insufficient balance: Account has ${smartAccountBalance} ETH but trying to send ${transferAmount} ETH`
1276
- );
1277
- }
1278
- const version = account.entryPointVersion || "0.6";
1279
- const userOp = await this.buildUserOperation(
1280
- userId,
1281
- account.address,
1282
- params.to,
1283
- params.amount,
1284
- params.data || "0x",
1285
- params.usePaymaster,
1286
- params.paymasterAddress,
1287
- params.paymasterData,
1288
- params.tokenAddress,
1289
- version,
1290
- params.paymasterTokenAddress,
1291
- params.wrapExecuteUserOp ?? false
1292
- );
1293
- const userOpHash = await this.ethereum.getUserOpHash(userOp, version);
1294
- await this.signer.ensureSigner(userId);
1295
- const assertionCtx = params.passkeyAssertion ? { assertion: params.passkeyAssertion } : void 0;
1296
- let useECDSA = false;
1297
- let isCompositeValidator = false;
1298
- if (version === "0.7" || version === "0.8") {
1299
- const provider = this.ethereum.getProvider();
1300
- ({ useECDSA, isCompositeValidator } = await detectSignatureStrategy(
1301
- provider,
1302
- account.address
1303
- ));
1304
- }
1305
- if (useECDSA) {
1306
- const signer = await this.signer.getSigner(userId, assertionCtx);
1307
- const ecdsaSig = await signer.signMessage(ethers.getBytes(userOpHash));
1308
- if (isCompositeValidator) {
1309
- this.logger.log("ECDSA path for compositeValidator: prepending algId prefix");
1310
- userOp.signature = ethers.concat([ethers.toBeHex(ALG_ID.ECDSA, 1), ecdsaSig]);
1311
- } else {
1312
- this.logger.log("ECDSA path for non-compositeValidator: raw signature");
1313
- userOp.signature = ecdsaSig;
1314
- }
1315
- } else if (params.useAirAccountTiering && this.guardChecker) {
1316
- const transferValue = params.tokenAddress ? 0n : ethers.parseEther(params.amount);
1317
- const preCheck = await this.guardChecker.preCheck(account.address, transferValue);
1318
- if (!preCheck.ok) {
1319
- throw new Error(`Guard pre-check failed: ${preCheck.errors.join("; ")}`);
1320
- }
1321
- this.logger.log(
1322
- `Tier ${preCheck.tier} selected (algId=0x${preCheck.algId.toString(16).padStart(2, "0")})`
1323
- );
1324
- userOp.signature = await this.blsService.generateTieredSignature({
1325
- tier: preCheck.tier,
1326
- userId,
1327
- userOpHash,
1328
- p256Signature: params.p256Signature,
1329
- guardianSigner: params.guardianSigner,
1330
- ctx: assertionCtx
1331
- });
1332
- } else {
1333
- const blsData = await this.blsService.generateBLSSignature(userId, userOpHash, assertionCtx);
1334
- const packedBls = await this.blsService.packSignature(blsData);
1335
- userOp.signature = ethers.concat([ethers.toBeHex(ALG_ID.BLS, 1), packedBls]);
1336
- }
1337
- const transferId = generateId();
1338
- let tokenSymbol = "ETH";
1339
- if (params.tokenAddress) {
1340
- try {
1341
- const tokenInfo = await this.tokenService.getTokenInfo(params.tokenAddress);
1342
- tokenSymbol = tokenInfo.symbol;
1343
- } catch {
1344
- tokenSymbol = `${params.tokenAddress.slice(0, 6)}...${params.tokenAddress.slice(-4)}`;
1345
- }
1346
- }
1347
- await this.storage.saveTransfer({
1348
- id: transferId,
1349
- userId,
1350
- from: account.address,
1351
- to: params.to,
1352
- amount: params.amount,
1353
- data: params.data,
1354
- userOpHash,
1355
- status: "pending",
1356
- nodeIndices: [],
1357
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1358
- tokenAddress: params.tokenAddress,
1359
- tokenSymbol
1360
- });
1361
- this.processTransferAsync(transferId, userOp, account.address, version);
1362
- return {
1363
- success: true,
1364
- transferId,
1365
- userOpHash,
1366
- status: "pending",
1367
- message: "Transfer submitted successfully. Use transferId to check status.",
1368
- from: account.address,
1369
- to: params.to,
1370
- amount: params.amount
1371
- };
1372
- }
1373
- async processTransferAsync(transferId, userOp, from, version) {
1374
- try {
1375
- const formatted = this.formatUserOpForBundler(userOp, version);
1376
- const bundlerUserOpHash = await this.ethereum.sendUserOperation(formatted, version);
1377
- await this.storage.updateTransfer(transferId, {
1378
- bundlerUserOpHash,
1379
- status: "submitted",
1380
- submittedAt: (/* @__PURE__ */ new Date()).toISOString()
1381
- });
1382
- const txHash = await this.ethereum.waitForUserOp(bundlerUserOpHash);
1383
- await this.storage.updateTransfer(transferId, {
1384
- transactionHash: txHash,
1385
- status: "completed",
1386
- completedAt: (/* @__PURE__ */ new Date()).toISOString()
1387
- });
1388
- const code = await this.ethereum.getProvider().getCode(from);
1389
- if (code !== "0x") {
1390
- const account = (await this.storage.getAccounts()).find((a) => a.address === from);
1391
- if (account && !account.deployed) {
1392
- await this.storage.updateAccount(account.userId, {
1393
- deployed: true,
1394
- deploymentTxHash: txHash
1395
- });
1396
- }
1397
- }
1398
- } catch (error) {
1399
- let message = error instanceof Error ? error.message : String(error);
1400
- if (message.includes("expires too soon") || message.includes("AA32") || message.includes("paymaster deposit not locked")) {
1401
- const validUntilMatch = message.match(/validUntil=(\d+)/);
1402
- const hint = validUntilMatch ? ` (validUntil=${validUntilMatch[1]}, expired ${Math.floor(Date.now() / 1e3) - Number(validUntilMatch[1])}s ago)` : "";
1403
- message = `Paymaster price is stale${hint}. Call paymasterManager.checkPriceFreshness(paymasterAddress) to diagnose, then paymasterManager.updatePrice(paymasterAddress, signer) to refresh. Original error: ${message}`;
1404
- error = new PaymasterPriceStalenessError(
1405
- "unknown",
1406
- 0,
1407
- 0
1408
- );
1409
- error.message = message;
1410
- }
1411
- await this.storage.updateTransfer(transferId, {
1412
- status: "failed",
1413
- error: message,
1414
- failedAt: (/* @__PURE__ */ new Date()).toISOString()
1415
- });
1416
- this.logger.error(`Transfer ${transferId} failed: ${message}`);
1417
- }
1418
- }
1419
- async estimateGas(userId, params) {
1420
- const account = await this.accountManager.getAccountByUserId(userId);
1421
- if (!account) throw new Error("User account not found");
1422
- const version = account.entryPointVersion || "0.6";
1423
- const userOp = await this.buildUserOperation(
1424
- userId,
1425
- account.address,
1426
- params.to,
1427
- params.amount,
1428
- params.data || "0x",
1429
- false,
1430
- void 0,
1431
- void 0,
1432
- params.tokenAddress,
1433
- version,
1434
- void 0,
1435
- params.wrapExecuteUserOp ?? false
1436
- );
1437
- const formatted = this.formatUserOpForBundler(userOp, version);
1438
- const gasEstimates = await this.ethereum.estimateUserOperationGas(formatted, version);
1439
- const gasPrices = await this.ethereum.getUserOperationGasPrice();
1440
- const validatorContract = this.ethereum.getValidatorContract(version);
1441
- const validatorGasEstimate = await validatorContract.getGasEstimate(3);
1442
- return {
1443
- callGasLimit: gasEstimates.callGasLimit,
1444
- verificationGasLimit: gasEstimates.verificationGasLimit,
1445
- preVerificationGas: gasEstimates.preVerificationGas,
1446
- validatorGasEstimate: validatorGasEstimate.toString(),
1447
- totalGasEstimate: (BigInt(gasEstimates.callGasLimit) + BigInt(gasEstimates.verificationGasLimit) + BigInt(gasEstimates.preVerificationGas)).toString(),
1448
- maxFeePerGas: gasPrices.maxFeePerGas,
1449
- maxPriorityFeePerGas: gasPrices.maxPriorityFeePerGas
1450
- };
1451
- }
1452
- async getTransferStatus(userId, transferId) {
1453
- const transfer = await this.storage.findTransferById(transferId);
1454
- if (!transfer || transfer.userId !== userId) {
1455
- throw new Error("Transfer not found");
1456
- }
1457
- const response = { ...transfer };
1458
- if (transfer.status === "pending" || transfer.status === "submitted") {
1459
- const elapsed = Math.floor((Date.now() - new Date(transfer.createdAt).getTime()) / 1e3);
1460
- response.elapsedSeconds = elapsed;
1461
- }
1462
- if (transfer.transactionHash) {
1463
- response.explorerUrl = `https://sepolia.etherscan.io/tx/${transfer.transactionHash}`;
1464
- }
1465
- const statusDescriptions = {
1466
- pending: "Preparing transaction and generating signatures",
1467
- submitted: "Transaction submitted to bundler, waiting for confirmation",
1468
- completed: "Transaction confirmed on chain",
1469
- failed: "Transaction failed"
1470
- };
1471
- response.statusDescription = statusDescriptions[transfer.status] || transfer.status;
1472
- return response;
1473
- }
1474
- async getTransferHistory(userId, page = 1, limit = 10) {
1475
- const transfers = await this.storage.findTransfersByUserId(userId);
1476
- if (!transfers || transfers.length === 0) {
1477
- return { transfers: [], total: 0, page, limit, totalPages: 0 };
1478
- }
1479
- transfers.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
1480
- const start = (page - 1) * limit;
1481
- const paginated = transfers.slice(start, start + limit);
1482
- return {
1483
- transfers: paginated,
1484
- total: transfers.length,
1485
- page,
1486
- limit,
1487
- totalPages: Math.ceil(transfers.length / limit)
1488
- };
1489
- }
1490
- // ── Private helpers ─────────────────────────────────────────────
1491
- async buildUserOperation(userId, sender, to, amount, data, usePaymaster, paymasterAddress, _paymasterData, tokenAddress, version = "0.6", paymasterTokenAddress, wrapExecuteUserOpFlag = false) {
1492
- const accountContract = this.ethereum.getAccountContract(sender);
1493
- const nonce = await this.ethereum.getNonce(sender, 0, version);
1494
- const provider = this.ethereum.getProvider();
1495
- const code = await provider.getCode(sender);
1496
- const needsDeployment = code === "0x";
1497
- let initCode = "0x";
1498
- if (needsDeployment) {
1499
- const accounts = await this.storage.getAccounts();
1500
- const account = accounts.find((a) => a.address === sender);
1501
- if (account) {
1502
- const factory = this.ethereum.getFactoryContract(version);
1503
- const factoryAddress = await factory.getAddress();
1504
- let deployCalldata;
1505
- if (version === "0.7" || version === "0.8") {
1506
- const storedDailyLimit = account.dailyLimit ? BigInt(account.dailyLimit) : 0n;
1507
- if (account.guardian1 && account.guardian2 && account.guardian1Sig && account.guardian2Sig) {
1508
- const sig1 = account.guardian1Sig.startsWith("0x") ? account.guardian1Sig : `0x${account.guardian1Sig}`;
1509
- const sig2 = account.guardian2Sig.startsWith("0x") ? account.guardian2Sig : `0x${account.guardian2Sig}`;
1510
- deployCalldata = factory.interface.encodeFunctionData("createAccountWithDefaults", [
1511
- account.signerAddress,
1512
- account.salt,
1513
- account.guardian1,
1514
- sig1,
1515
- account.guardian2,
1516
- sig2,
1517
- storedDailyLimit
1518
- ]);
1519
- } else {
1520
- const minimalConfig = [
1521
- [ethers.ZeroAddress, ethers.ZeroAddress, ethers.ZeroAddress],
1522
- // guardians (address[3])
1523
- storedDailyLimit,
1524
- [],
1525
- // approvedAlgIds
1526
- 0n,
1527
- // minDailyLimit
1528
- [],
1529
- // initialTokens
1530
- []
1531
- // initialTokenConfigs
1532
- ];
1533
- deployCalldata = factory.interface.encodeFunctionData("createAccount", [
1534
- account.signerAddress,
1535
- account.salt,
1536
- minimalConfig
1537
- ]);
1538
- }
1539
- } else {
1540
- deployCalldata = factory.interface.encodeFunctionData(
1541
- "createAccountWithAAStarValidator",
1542
- [
1543
- account.signerAddress,
1544
- account.signerAddress,
1545
- account.validatorAddress,
1546
- true,
1547
- account.salt
1548
- ]
1549
- );
1550
- }
1551
- initCode = ethers.concat([factoryAddress, deployCalldata]);
1552
- }
1553
- }
1554
- let callData;
1555
- if (tokenAddress) {
1556
- const tokenInfo = await this.tokenService.getTokenInfo(tokenAddress);
1557
- const transferCalldata = this.tokenService.generateTransferCalldata(
1558
- to,
1559
- amount,
1560
- tokenInfo.decimals
1561
- );
1562
- callData = accountContract.interface.encodeFunctionData("execute", [
1563
- tokenAddress,
1564
- 0,
1565
- transferCalldata
1566
- ]);
1567
- } else {
1568
- callData = accountContract.interface.encodeFunctionData("execute", [
1569
- to,
1570
- ethers.parseEther(amount),
1571
- data
1572
- ]);
1573
- }
1574
- if (wrapExecuteUserOpFlag) {
1575
- callData = wrapExecuteUserOp(callData);
1576
- }
1577
- const gasPrices = await this.ethereum.getUserOperationGasPrice();
1578
- const isV07 = version === "0.7" || version === "0.8";
1579
- let baseUserOp;
1580
- if (isV07) {
1581
- let factory;
1582
- let factoryData;
1583
- if (initCode && initCode !== "0x" && initCode.length > 2) {
1584
- factory = initCode.slice(0, 42);
1585
- factoryData = initCode.length > 42 ? "0x" + initCode.slice(42) : "0x";
1586
- }
1587
- baseUserOp = {
1588
- sender,
1589
- nonce: "0x" + nonce.toString(16),
1590
- ...factory ? { factory, factoryData } : {},
1591
- callData,
1592
- callGasLimit: "0x0",
1593
- verificationGasLimit: "0x0",
1594
- preVerificationGas: "0x0",
1595
- maxFeePerGas: gasPrices.maxFeePerGas,
1596
- maxPriorityFeePerGas: gasPrices.maxPriorityFeePerGas,
1597
- signature: "0x"
1598
- };
1599
- } else {
1600
- baseUserOp = {
1601
- sender,
1602
- nonce: "0x" + nonce.toString(16),
1603
- initCode,
1604
- callData,
1605
- callGasLimit: "0x0",
1606
- verificationGasLimit: "0x0",
1607
- preVerificationGas: "0x0",
1608
- maxFeePerGas: gasPrices.maxFeePerGas,
1609
- maxPriorityFeePerGas: gasPrices.maxPriorityFeePerGas,
1610
- paymasterAndData: "0x",
1611
- signature: "0x"
1612
- };
1613
- }
1614
- let paymasterAndData = "0x";
1615
- if (usePaymaster) {
1616
- if (paymasterAddress) {
1617
- const entryPoint = this.ethereum.getEntryPointAddress(version);
1618
- paymasterAndData = await this.paymasterManager.getPaymasterData(
1619
- userId,
1620
- "custom-user-provided",
1621
- baseUserOp,
1622
- entryPoint,
1623
- paymasterAddress,
1624
- paymasterTokenAddress ? { tokenAddress: paymasterTokenAddress } : void 0
1625
- );
1626
- } else {
1627
- const available = await this.paymasterManager.getAvailablePaymasters(userId);
1628
- const configured = available.find((pm) => pm.configured);
1629
- if (configured) {
1630
- const entryPoint = this.ethereum.getEntryPointAddress(version);
1631
- paymasterAndData = await this.paymasterManager.getPaymasterData(
1632
- userId,
1633
- configured.name,
1634
- baseUserOp,
1635
- entryPoint
1636
- );
1637
- } else {
1638
- throw new Error("No paymaster configured and no paymaster address provided");
1639
- }
1640
- }
1641
- if (!paymasterAndData || paymasterAndData === "0x") {
1642
- throw new Error(
1643
- `Paymaster failed to provide sponsorship data. The paymaster at ${paymasterAddress} may not be configured correctly.`
1644
- );
1645
- }
1646
- if (isV07) {
1647
- baseUserOp.paymaster = paymasterAndData.slice(0, 42);
1648
- if (paymasterAndData.length >= 74) {
1649
- baseUserOp.paymasterVerificationGasLimit = "0x" + BigInt("0x" + paymasterAndData.slice(42, 74)).toString(16);
1650
- }
1651
- if (paymasterAndData.length >= 106) {
1652
- baseUserOp.paymasterPostOpGasLimit = "0x" + BigInt("0x" + paymasterAndData.slice(74, 106)).toString(16);
1653
- }
1654
- if (paymasterAndData.length > 106) {
1655
- baseUserOp.paymasterData = "0x" + paymasterAndData.slice(106);
1656
- }
1657
- } else {
1658
- baseUserOp.paymasterAndData = paymasterAndData;
1659
- }
1660
- }
1661
- const gasEstimates = await this.ethereum.estimateUserOperationGas(baseUserOp, version);
1662
- const standardUserOp = {
1663
- sender,
1664
- nonce,
1665
- initCode,
1666
- callData,
1667
- callGasLimit: BigInt(gasEstimates.callGasLimit),
1668
- verificationGasLimit: BigInt(gasEstimates.verificationGasLimit),
1669
- preVerificationGas: BigInt(gasEstimates.preVerificationGas),
1670
- maxFeePerGas: BigInt(gasPrices.maxFeePerGas),
1671
- maxPriorityFeePerGas: BigInt(gasPrices.maxPriorityFeePerGas),
1672
- paymasterAndData,
1673
- signature: "0x"
1674
- };
1675
- if (version === "0.7" || version === "0.8") {
1676
- return ERC4337Utils2.packUserOperation(standardUserOp);
1677
- }
1678
- return standardUserOp;
1679
- }
1680
- formatUserOpForBundler(userOp, version = "0.6") {
1681
- if (version === "0.7" || version === "0.8") {
1682
- const packedOp = userOp;
1683
- const gasLimits = ERC4337Utils2.unpackAccountGasLimits(packedOp.accountGasLimits);
1684
- const gasFees = ERC4337Utils2.unpackGasFees(packedOp.gasFees);
1685
- let factory;
1686
- let factoryData;
1687
- if (packedOp.initCode && packedOp.initCode !== "0x" && packedOp.initCode.length > 2) {
1688
- factory = packedOp.initCode.slice(0, 42);
1689
- if (packedOp.initCode.length > 42) {
1690
- factoryData = "0x" + packedOp.initCode.slice(42);
1691
- }
1692
- }
1693
- let paymaster;
1694
- let paymasterVerificationGasLimit;
1695
- let paymasterPostOpGasLimit;
1696
- let paymasterData;
1697
- if (packedOp.paymasterAndData && packedOp.paymasterAndData !== "0x" && packedOp.paymasterAndData.length > 2) {
1698
- paymaster = packedOp.paymasterAndData.slice(0, 42);
1699
- if (packedOp.paymasterAndData.length >= 74) {
1700
- paymasterVerificationGasLimit = "0x" + BigInt("0x" + packedOp.paymasterAndData.slice(42, 74)).toString(16);
1701
- }
1702
- if (packedOp.paymasterAndData.length >= 106) {
1703
- paymasterPostOpGasLimit = "0x" + BigInt("0x" + packedOp.paymasterAndData.slice(74, 106)).toString(16);
1704
- }
1705
- if (packedOp.paymasterAndData.length > 106) {
1706
- paymasterData = "0x" + packedOp.paymasterAndData.slice(106);
1707
- }
1708
- }
1709
- const result = {
1710
- sender: packedOp.sender,
1711
- nonce: typeof packedOp.nonce === "bigint" ? "0x" + packedOp.nonce.toString(16) : packedOp.nonce.toString().startsWith("0x") ? packedOp.nonce.toString() : "0x" + BigInt(packedOp.nonce).toString(16),
1712
- callData: packedOp.callData,
1713
- callGasLimit: "0x" + gasLimits.callGasLimit.toString(16),
1714
- verificationGasLimit: "0x" + gasLimits.verificationGasLimit.toString(16),
1715
- preVerificationGas: typeof packedOp.preVerificationGas === "bigint" ? "0x" + packedOp.preVerificationGas.toString(16) : packedOp.preVerificationGas.toString().startsWith("0x") ? packedOp.preVerificationGas.toString() : "0x" + BigInt(packedOp.preVerificationGas).toString(16),
1716
- maxFeePerGas: "0x" + gasFees.maxFeePerGas.toString(16),
1717
- maxPriorityFeePerGas: "0x" + gasFees.maxPriorityFeePerGas.toString(16),
1718
- signature: packedOp.signature || "0x"
1719
- };
1720
- if (factory) result.factory = factory;
1721
- if (factoryData) result.factoryData = factoryData;
1722
- if (paymaster) {
1723
- result.paymaster = paymaster;
1724
- result.paymasterVerificationGasLimit = paymasterVerificationGasLimit || "0x30000";
1725
- result.paymasterPostOpGasLimit = paymasterPostOpGasLimit || "0x30000";
1726
- if (paymasterData && paymasterData !== "0x") {
1727
- result.paymasterData = paymasterData;
1728
- }
1729
- }
1730
- return result;
1731
- }
1732
- const op = userOp;
1733
- return {
1734
- sender: op.sender,
1735
- nonce: "0x" + op.nonce.toString(16),
1736
- initCode: op.initCode,
1737
- callData: op.callData,
1738
- callGasLimit: "0x" + op.callGasLimit.toString(16),
1739
- verificationGasLimit: "0x" + op.verificationGasLimit.toString(16),
1740
- preVerificationGas: "0x" + op.preVerificationGas.toString(16),
1741
- maxFeePerGas: "0x" + op.maxFeePerGas.toString(16),
1742
- maxPriorityFeePerGas: "0x" + op.maxPriorityFeePerGas.toString(16),
1743
- paymasterAndData: op.paymasterAndData,
1744
- signature: op.signature
1745
- };
1746
- }
1747
- };
1748
- var BLSManager2 = class {
1749
- config;
1750
- constructor(config) {
1751
- this.config = config;
1752
- }
1753
- /**
1754
- * Discover available BLS nodes from seed nodes (Gossip network)
1755
- */
1756
- async getAvailableNodes() {
1757
- const { seedNodes, discoveryTimeout = 5e3 } = this.config;
1758
- for (const seedEndpoint of seedNodes) {
1759
- try {
1760
- const response = await axios.get(`${seedEndpoint}/gossip/peers`, {
1761
- timeout: discoveryTimeout
1762
- });
1763
- const peers = response.data.peers || [];
1764
- const activeNodes = peers.filter((p) => p.status === "active" && p.apiEndpoint && p.publicKey).map((p, index) => ({
1765
- index: index + 1,
1766
- // 1-based index likely expected by contract if using bitmap
1767
- nodeId: p.nodeId,
1768
- nodeName: p.nodeName,
1769
- apiEndpoint: p.apiEndpoint,
1770
- status: "active",
1771
- publicKey: p.publicKey
1772
- }));
1773
- if (activeNodes.length > 0) {
1774
- return activeNodes;
1775
- }
1776
- } catch {
1777
- continue;
1778
- }
1779
- }
1780
- return [];
1781
- }
1782
- /**
1783
- * Helper to pack the full signature for ERC-4337 UserOp
1784
- * Format: [nodeIdsLength][nodeIds...][blsSignature][messagePoint][aaSignature][messagePointSignature]
1785
- */
1786
- packSignature(data) {
1787
- if (!data.nodeIds || !data.aaSignature || !data.messagePointSignature) {
1788
- throw new Error("Missing required signature components");
1789
- }
1790
- const nodeIdsLength = ethers.solidityPacked(["uint256"], [data.nodeIds.length]);
1791
- const nodeIdsBytes = ethers.solidityPacked(
1792
- Array(data.nodeIds.length).fill("bytes32"),
1793
- data.nodeIds
1794
- );
1795
- return ethers.solidityPacked(
1796
- ["bytes", "bytes", "bytes", "bytes", "bytes", "bytes"],
1797
- [
1798
- nodeIdsLength,
1799
- nodeIdsBytes,
1800
- data.signature,
1801
- data.messagePoint,
1802
- data.aaSignature,
1803
- data.messagePointSignature
1804
- ]
1805
- );
1806
- }
1807
- /**
1808
- * Calculate the MessagePoint G2 point for a given message (UserOpHash)
1809
- */
1810
- async generateMessagePoint(message) {
1811
- const messageBytes = typeof message === "string" ? ethers.getBytes(message) : message;
1812
- const DST = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
1813
- const messagePointBLS = await bls12_381.G2.hashToCurve(messageBytes, { DST });
1814
- const messageG2EIP = this.encodeG2Point(messagePointBLS);
1815
- return "0x" + Buffer.from(messageG2EIP).toString("hex");
1816
- }
1817
- /**
1818
- * Encode G2 Point to bytes for EIP-2537 format
1819
- */
1820
- encodeG2Point(point) {
1821
- const result = new Uint8Array(256);
1822
- const affine = point.toAffine();
1823
- const x0Bytes = this.hexToBytes(affine.x.c0.toString(16).padStart(96, "0"));
1824
- const x1Bytes = this.hexToBytes(affine.x.c1.toString(16).padStart(96, "0"));
1825
- const y0Bytes = this.hexToBytes(affine.y.c0.toString(16).padStart(96, "0"));
1826
- const y1Bytes = this.hexToBytes(affine.y.c1.toString(16).padStart(96, "0"));
1827
- result.set(x0Bytes, 16);
1828
- result.set(x1Bytes, 80);
1829
- result.set(y0Bytes, 144);
1830
- result.set(y1Bytes, 208);
1831
- return result;
1832
- }
1833
- hexToBytes(hex) {
1834
- if (hex.startsWith("0x")) hex = hex.slice(2);
1835
- const bytes = new Uint8Array(hex.length / 2);
1836
- for (let i = 0; i < hex.length; i += 2) {
1837
- bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
1838
- }
1839
- return bytes;
1840
- }
1841
- /**
1842
- * Pack cumulative Tier 2 signature (algId 0x04): P256 + BLS.
1843
- *
1844
- * Format:
1845
- * [algId=0x04 (1)] [P256 r (32)] [P256 s (32)]
1846
- * [nodeIdsLength (32)] [nodeIds (N×32)]
1847
- * [blsAggregateSig (256)] [messagePoint (256)]
1848
- * [messagePointECDSA (65)]
1849
- */
1850
- packCumulativeT2Signature(data) {
1851
- const nodeIdsLength = ethers.solidityPacked(["uint256"], [data.nodeIds.length]);
1852
- const nodeIdsBytes = ethers.solidityPacked(
1853
- Array(data.nodeIds.length).fill("bytes32"),
1854
- data.nodeIds
1855
- );
1856
- return ethers.solidityPacked(
1857
- ["bytes1", "bytes", "bytes", "bytes", "bytes", "bytes", "bytes"],
1858
- [
1859
- "0x04",
1860
- data.p256Signature,
1861
- nodeIdsLength,
1862
- nodeIdsBytes,
1863
- data.blsSignature,
1864
- data.messagePoint,
1865
- data.messagePointSignature
1866
- ]
1867
- );
1868
- }
1869
- /**
1870
- * Pack cumulative Tier 3 signature (algId 0x05): P256 + BLS + Guardian.
1871
- *
1872
- * Format:
1873
- * [algId=0x05 (1)] [P256 r (32)] [P256 s (32)]
1874
- * [nodeIdsLength (32)] [nodeIds (N×32)]
1875
- * [blsAggregateSig (256)] [messagePoint (256)]
1876
- * [messagePointECDSA (65)] [guardianECDSA (65)]
1877
- */
1878
- packCumulativeT3Signature(data) {
1879
- const nodeIdsLength = ethers.solidityPacked(["uint256"], [data.nodeIds.length]);
1880
- const nodeIdsBytes = ethers.solidityPacked(
1881
- Array(data.nodeIds.length).fill("bytes32"),
1882
- data.nodeIds
1883
- );
1884
- return ethers.solidityPacked(
1885
- ["bytes1", "bytes", "bytes", "bytes", "bytes", "bytes", "bytes", "bytes"],
1886
- [
1887
- "0x05",
1888
- data.p256Signature,
1889
- nodeIdsLength,
1890
- nodeIdsBytes,
1891
- data.blsSignature,
1892
- data.messagePoint,
1893
- data.messagePointSignature,
1894
- data.guardianSignature
1895
- ]
1896
- );
1897
- }
1898
- /**
1899
- * Request signature from a single node
1900
- */
1901
- async requestNodeSignature(node, message) {
1902
- const response = await axios.post(`${node.apiEndpoint}/signature/sign`, {
1903
- message
1904
- });
1905
- const signatureEIP = response.data.signature;
1906
- const signature = response.data.signatureCompact || signatureEIP;
1907
- return {
1908
- signature: signature.startsWith("0x") ? signature : `0x${signature}`,
1909
- publicKey: response.data.publicKey
1910
- };
1911
- }
1912
- /**
1913
- * Request aggregation from a node
1914
- */
1915
- async aggregateSignatures(node, signatures) {
1916
- const response = await axios.post(`${node.apiEndpoint}/signature/aggregate`, {
1917
- signatures
1918
- });
1919
- const sig = response.data.signature;
1920
- return sig.startsWith("0x") ? sig : `0x${sig}`;
1921
- }
1922
- };
1923
- var DvtPendingConfirmationError = class extends Error {
1924
- constructor(userOpHash, nodeEndpoint) {
1925
- super(
1926
- `DVT node ${nodeEndpoint} withheld its co-signature pending out-of-band confirmation for userOpHash ${userOpHash}; release it via POST /signature/confirm.`
1927
- );
1928
- this.userOpHash = userOpHash;
1929
- this.nodeEndpoint = nodeEndpoint;
1930
- this.name = "DvtPendingConfirmationError";
1931
- }
1932
- };
1933
- function isPendingConfirmation(data) {
1934
- return typeof data === "object" && data !== null && data.status === "pending_confirmation";
1935
- }
1936
- var BLSSignatureService = class {
1937
- constructor(config, ethereum, storage, signer, logger) {
1938
- this.config = config;
1939
- this.ethereum = ethereum;
1940
- this.storage = storage;
1941
- this.signer = signer;
1942
- this.logger = logger ?? new ConsoleLogger("[BLSSignatureService]");
1943
- }
1944
- blsManager = null;
1945
- logger;
1946
- /** Lazy-initialize BLSManager on first use. */
1947
- async ensureInitialized() {
1948
- if (this.blsManager) return this.blsManager;
1949
- const blsConfig = await this.storage.getBlsConfig();
1950
- const seedNodes = this.config.blsSeedNodes ?? blsConfig?.discovery?.seedNodes?.map((n) => n.endpoint) ?? [];
1951
- this.blsManager = new BLSManager2({
1952
- seedNodes,
1953
- discoveryTimeout: this.config.blsDiscoveryTimeout ?? 1e4
1954
- });
1955
- return this.blsManager;
1956
- }
1957
- async getActiveSignerNodes() {
1958
- const manager = await this.ensureInitialized();
1959
- const nodes = await manager.getAvailableNodes();
1960
- if (nodes.length > 0) {
1961
- try {
1962
- await this.storage.updateSignerNodesCache(nodes);
1963
- } catch {
1964
- }
1965
- }
1966
- return nodes;
1967
- }
1968
- async generateBLSSignature(userId, userOpHash, ctx) {
1969
- const manager = await this.ensureInitialized();
1970
- const activeNodes = await this.getActiveSignerNodes();
1971
- if (activeNodes.length < 1) {
1972
- throw new Error("No active BLS signer nodes available");
1973
- }
1974
- const selectedNodes = activeNodes.slice(0, Math.min(3, activeNodes.length));
1975
- const signerNodeSignatures = [];
1976
- const signerNodeIds = [];
1977
- for (const node of selectedNodes) {
1978
- try {
1979
- const response = await axios.post(`${node.apiEndpoint}/signature/sign`, {
1980
- message: userOpHash
1981
- });
1982
- if (isPendingConfirmation(response.data)) {
1983
- throw new DvtPendingConfirmationError(response.data.userOpHash ?? userOpHash, node.apiEndpoint);
1984
- }
1985
- const signatureForAggregation = response.data.signatureCompact || response.data.signature;
1986
- const formatted = signatureForAggregation.startsWith("0x") ? signatureForAggregation : `0x${signatureForAggregation}`;
1987
- signerNodeSignatures.push(formatted);
1988
- signerNodeIds.push(response.data.nodeId);
1989
- } catch (err) {
1990
- if (err instanceof DvtPendingConfirmationError) throw err;
1991
- }
1992
- }
1993
- if (signerNodeSignatures.length === 0) {
1994
- throw new Error("Failed to get signatures from any BLS signer nodes");
1995
- }
1996
- let aggregatedSignature;
1997
- if (signerNodeSignatures.length > 1) {
1998
- const aggregateResponse = await axios.post(
1999
- `${selectedNodes[0].apiEndpoint}/signature/aggregate`,
2000
- { signatures: signerNodeSignatures }
2001
- );
2002
- aggregatedSignature = aggregateResponse.data.signature.startsWith("0x") ? aggregateResponse.data.signature : `0x${aggregateResponse.data.signature}`;
2003
- } else {
2004
- const singleSignResponse = await axios.post(
2005
- `${selectedNodes[0].apiEndpoint}/signature/sign`,
2006
- { message: userOpHash }
2007
- );
2008
- if (isPendingConfirmation(singleSignResponse.data)) {
2009
- throw new DvtPendingConfirmationError(
2010
- singleSignResponse.data.userOpHash ?? userOpHash,
2011
- selectedNodes[0].apiEndpoint
2012
- );
2013
- }
2014
- aggregatedSignature = singleSignResponse.data.signature.startsWith("0x") ? singleSignResponse.data.signature : `0x${singleSignResponse.data.signature}`;
2015
- }
2016
- const messagePoint = await manager.generateMessagePoint(userOpHash);
2017
- const account = await this.storage.findAccountByUserId(userId);
2018
- if (!account) {
2019
- throw new Error(`User account not found for userId: ${userId}`);
2020
- }
2021
- const wallet = await this.signer.getSigner(userId, ctx);
2022
- const walletAddress = await wallet.getAddress();
2023
- if (walletAddress.toLowerCase() !== account.signerAddress.toLowerCase()) {
2024
- throw new Error(
2025
- `Wallet address mismatch! Wallet: ${walletAddress}, Expected: ${account.signerAddress}`
2026
- );
2027
- }
2028
- const aaSignature = await wallet.signMessage(ethers.getBytes(userOpHash));
2029
- const messagePointHash = ethers.keccak256(messagePoint);
2030
- const messagePointSignature = await wallet.signMessage(ethers.getBytes(messagePointHash));
2031
- return {
2032
- nodeIds: signerNodeIds,
2033
- signature: aggregatedSignature,
2034
- messagePoint,
2035
- aaAddress: account.signerAddress,
2036
- aaSignature,
2037
- messagePointSignature
2038
- };
2039
- }
2040
- async packSignature(blsData) {
2041
- const manager = await this.ensureInitialized();
2042
- return manager.packSignature(blsData);
2043
- }
2044
- // ── Tiered Signature Support (M4) ─────────────────────────────
2045
- /**
2046
- * Generate a tiered signature based on the required tier level.
2047
- *
2048
- * - Tier 1: raw 65-byte ECDSA (no algId prefix, backwards-compat)
2049
- * - Tier 2: algId 0x04 — P256 + BLS aggregate + messagePoint ECDSA
2050
- * - Tier 3: algId 0x05 — P256 + BLS + messagePoint ECDSA + Guardian ECDSA
2051
- *
2052
- * @param tier - Required tier level (1, 2, or 3)
2053
- * @param userId - User ID for account lookup
2054
- * @param userOpHash - The UserOp hash to sign
2055
- * @param p256Signature - P256 passkey signature (64 bytes, required for tier 2/3)
2056
- * @param guardianSigner - Guardian ethers.Signer (required for tier 3)
2057
- * @param ctx - Optional passkey assertion context for KMS signing
2058
- */
2059
- async generateTieredSignature(params) {
2060
- const { tier, userId, userOpHash, p256Signature, guardianSigner, ctx } = params;
2061
- const manager = await this.ensureInitialized();
2062
- if (tier === 1) {
2063
- const account = await this.storage.findAccountByUserId(userId);
2064
- if (!account) throw new Error(`User account not found for userId: ${userId}`);
2065
- const wallet = await this.signer.getSigner(userId, ctx);
2066
- return wallet.signMessage(ethers.getBytes(userOpHash));
2067
- }
2068
- if (!p256Signature) {
2069
- throw new Error(`P256 signature required for Tier ${tier}`);
2070
- }
2071
- const blsData = await this.generateBLSSignature(userId, userOpHash, ctx);
2072
- if (tier === 2) {
2073
- const t2Data = {
2074
- p256Signature,
2075
- nodeIds: blsData.nodeIds,
2076
- blsSignature: blsData.signature,
2077
- messagePoint: blsData.messagePoint,
2078
- messagePointSignature: blsData.messagePointSignature
2079
- };
2080
- return manager.packCumulativeT2Signature(t2Data);
2081
- }
2082
- if (!guardianSigner) {
2083
- throw new Error("Guardian signer required for Tier 3");
2084
- }
2085
- const guardianSignature = await guardianSigner.signMessage(ethers.getBytes(userOpHash));
2086
- const t3Data = {
2087
- p256Signature,
2088
- nodeIds: blsData.nodeIds,
2089
- blsSignature: blsData.signature,
2090
- messagePoint: blsData.messagePoint,
2091
- messagePointSignature: blsData.messagePointSignature,
2092
- guardianSignature
2093
- };
2094
- return manager.packCumulativeT3Signature(t3Data);
2095
- }
2096
- };
2097
- var TokenService = class {
2098
- constructor(ethereum) {
2099
- this.ethereum = ethereum;
2100
- }
2101
- async getTokenInfo(tokenAddress) {
2102
- const provider = this.ethereum.getProvider();
2103
- const contract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
2104
- const [name, symbol, decimals] = await Promise.all([
2105
- contract.name(),
2106
- contract.symbol(),
2107
- contract.decimals()
2108
- ]);
2109
- return {
2110
- address: tokenAddress.toLowerCase(),
2111
- name,
2112
- symbol,
2113
- decimals: Number(decimals)
2114
- };
2115
- }
2116
- async getTokenBalance(tokenAddress, walletAddress) {
2117
- const provider = this.ethereum.getProvider();
2118
- const contract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
2119
- try {
2120
- const balance = await contract.balanceOf(walletAddress);
2121
- return balance.toString();
2122
- } catch {
2123
- return "0";
2124
- }
2125
- }
2126
- async getFormattedTokenBalance(tokenAddress, walletAddress) {
2127
- const tokenInfo = await this.getTokenInfo(tokenAddress);
2128
- const rawBalance = await this.getTokenBalance(tokenAddress, walletAddress);
2129
- const formattedBalance = ethers.formatUnits(rawBalance, tokenInfo.decimals);
2130
- return { token: tokenInfo, balance: rawBalance, formattedBalance };
2131
- }
2132
- generateTransferCalldata(to, amount, decimals) {
2133
- const contract = new ethers.Contract(ethers.ZeroAddress, ERC20_ABI);
2134
- const parsedAmount = ethers.parseUnits(amount, decimals);
2135
- return contract.interface.encodeFunctionData("transfer", [to, parsedAmount]);
2136
- }
2137
- async validateToken(tokenAddress) {
2138
- try {
2139
- const provider = this.ethereum.getProvider();
2140
- const contract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
2141
- const [name, symbol, decimals] = await Promise.race([
2142
- Promise.all([contract.name(), contract.symbol(), contract.decimals()]),
2143
- new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), 1e4))
2144
- ]);
2145
- return {
2146
- isValid: true,
2147
- token: {
2148
- address: tokenAddress.toLowerCase(),
2149
- name,
2150
- symbol,
2151
- decimals: Number(decimals)
2152
- }
2153
- };
2154
- } catch (error) {
2155
- return {
2156
- isValid: false,
2157
- error: error instanceof Error ? error.message : "Invalid ERC20 token"
2158
- };
2159
- }
2160
- }
2161
- };
2162
- var WalletManager = class {
2163
- constructor(signer) {
2164
- this.signer = signer;
2165
- }
2166
- async getAddress(userId) {
2167
- return this.signer.getAddress(userId);
2168
- }
2169
- async getSigner(userId) {
2170
- return this.signer.getSigner(userId);
2171
- }
2172
- async ensureSigner(userId) {
2173
- return this.signer.ensureSigner(userId);
2174
- }
2175
- };
2176
- var YAAAServerClient = class {
2177
- ethereum;
2178
- accounts;
2179
- transfers;
2180
- bls;
2181
- paymaster;
2182
- tokens;
2183
- wallets;
2184
- constructor(config) {
2185
- validateConfig(config);
2186
- const logger = config.logger ?? new ConsoleLogger("[YAAA]");
2187
- this.ethereum = new EthereumProvider(config);
2188
- this.wallets = new WalletManager(config.signer);
2189
- this.tokens = new TokenService(this.ethereum);
2190
- this.paymaster = new PaymasterManager(this.ethereum, config.storage, logger);
2191
- this.accounts = new AccountManager(this.ethereum, config.storage, config.signer, logger);
2192
- this.bls = new BLSSignatureService(
2193
- config,
2194
- this.ethereum,
2195
- config.storage,
2196
- config.signer,
2197
- logger
2198
- );
2199
- this.transfers = new TransferManager(
2200
- this.ethereum,
2201
- this.accounts,
2202
- this.bls,
2203
- this.paymaster,
2204
- this.tokens,
2205
- config.storage,
2206
- config.signer,
2207
- logger
2208
- );
2209
- }
2210
- };
2211
- function buildInstallModuleHash(chainId, account, moduleTypeId, module, moduleInitData = "0x") {
2212
- const moduleInitDataHash = ethers.keccak256(moduleInitData);
2213
- const raw = ethers.keccak256(
2214
- ethers.solidityPacked(
2215
- ["string", "uint256", "address", "uint256", "address", "bytes32"],
2216
- ["INSTALL_MODULE", chainId, account, moduleTypeId, module, moduleInitDataHash]
2217
- )
2218
- );
2219
- return ethers.hashMessage(ethers.getBytes(raw));
2220
- }
2221
- function buildUninstallModuleHash(chainId, account, moduleTypeId, module) {
2222
- const raw = ethers.keccak256(
2223
- ethers.solidityPacked(
2224
- ["string", "uint256", "address", "uint256", "address"],
2225
- ["UNINSTALL_MODULE", chainId, account, moduleTypeId, module]
2226
- )
2227
- );
2228
- return ethers.hashMessage(ethers.getBytes(raw));
2229
- }
2230
- var ModuleManager = class {
2231
- provider;
2232
- chainId;
2233
- constructor(provider, chainId) {
2234
- this.provider = provider;
2235
- this.chainId = chainId;
2236
- }
2237
- /**
2238
- * Encode calldata for installModule().
2239
- * Caller is responsible for submitting via UserOp (EntryPoint) or direct tx.
2240
- */
2241
- encodeInstall(params) {
2242
- const sigs = params.guardianSigs ?? [];
2243
- const initData = params.moduleInitData ?? "0x";
2244
- const packed = sigs.length > 0 ? ethers.concat([...sigs.map((s) => ethers.getBytes(s)), ethers.getBytes(initData)]) : ethers.getBytes(initData);
2245
- const iface = new ethers.Interface(AIRACCOUNT_ABI);
2246
- return iface.encodeFunctionData("installModule", [
2247
- params.moduleTypeId,
2248
- params.module,
2249
- packed
2250
- ]);
2251
- }
2252
- /**
2253
- * Encode calldata for uninstallModule().
2254
- * Always requires 2 guardian signatures.
2255
- */
2256
- encodeUninstall(params) {
2257
- const deInitData = params.moduleDeInitData ?? "0x";
2258
- const packed = ethers.concat([
2259
- ethers.getBytes(params.guardianSig1),
2260
- ethers.getBytes(params.guardianSig2),
2261
- ethers.getBytes(deInitData)
2262
- ]);
2263
- const iface = new ethers.Interface(AIRACCOUNT_ABI);
2264
- return iface.encodeFunctionData("uninstallModule", [
2265
- params.moduleTypeId,
2266
- params.module,
2267
- packed
2268
- ]);
2269
- }
2270
- /** Check if a module is currently installed on the account. */
2271
- async isInstalled(account, moduleTypeId, module) {
2272
- const contract = new ethers.Contract(account, AIRACCOUNT_ABI, this.provider);
2273
- return contract.isModuleInstalled(moduleTypeId, module, "0x");
2274
- }
2275
- /** Return the install hash for a guardian to sign (r5 format, includes moduleInitData hash). */
2276
- installHash(account, moduleTypeId, module, moduleInitData = "0x") {
2277
- return buildInstallModuleHash(this.chainId, account, moduleTypeId, module, moduleInitData);
2278
- }
2279
- /** Return the uninstall hash for guardians to sign. */
2280
- uninstallHash(account, moduleTypeId, module) {
2281
- return buildUninstallModuleHash(this.chainId, account, moduleTypeId, module);
2282
- }
2283
- /**
2284
- * Convenience: build install calldata for the standard M7 module set.
2285
- * Uses pre-deployed Sepolia addresses (r4 audit-final). No guardian sigs required when
2286
- * account threshold <= 40 (default for newly created accounts).
2287
- *
2288
- * Note: beta.3 unifies these into SessionKeyValidator. This helper retains the r4
2289
- * addresses for accounts already deployed on r4; new accounts use SessionKeyValidator.
2290
- */
2291
- encodeInstallDefaultModules(account) {
2292
- const addresses = AIRACCOUNT_ADDRESSES.sepolia;
2293
- return {
2294
- compositeValidator: this.encodeInstall({
2295
- account,
2296
- moduleTypeId: MODULE_TYPE.VALIDATOR,
2297
- module: addresses.compositeValidatorM7r4
2298
- }),
2299
- tierGuardHook: this.encodeInstall({
2300
- account,
2301
- moduleTypeId: MODULE_TYPE.HOOK,
2302
- module: addresses.tierGuardHookM7r4
2303
- })
2304
- };
2305
- }
2306
- };
2307
- function buildSessionStruct(params) {
2308
- return {
2309
- expiry: params.expiry,
2310
- contractScope: params.contractScope ?? ethers.ZeroAddress,
2311
- selectorScope: params.selectorScope ?? "0x00000000",
2312
- revoked: false,
2313
- velocityLimit: params.velocityLimit ?? 0,
2314
- velocityWindow: params.velocityWindow ?? 0,
2315
- callTargets: params.callTargets ?? [],
2316
- selectorAllowlist: params.selectorAllowlist ?? []
2317
- };
2318
- }
2319
- function decodeSessionInfo(session) {
2320
- const s = session;
2321
- const expiry = Number(s[0]);
2322
- const now = Math.floor(Date.now() / 1e3);
2323
- return {
2324
- expiry,
2325
- contractScope: s[1],
2326
- selectorScope: s[2],
2327
- revoked: s[3],
2328
- velocityLimit: Number(s[4]),
2329
- velocityWindow: Number(s[5]),
2330
- callTargets: [...s[6] ?? []],
2331
- selectorAllowlist: [...s[7] ?? []],
2332
- active: expiry > now && !s[3]
2333
- };
2334
- }
2335
- var SessionKeyService = class {
2336
- provider;
2337
- skValidator;
2338
- askValidator;
2339
- constructor(provider, sessionKeyValidatorAddress, agentSessionKeyValidatorAddress) {
2340
- this.provider = provider;
2341
- this.skValidator = new ethers.Contract(
2342
- sessionKeyValidatorAddress,
2343
- SESSION_KEY_VALIDATOR_ABI,
2344
- provider
2345
- );
2346
- this.askValidator = new ethers.Contract(
2347
- agentSessionKeyValidatorAddress,
2348
- AGENT_SESSION_KEY_VALIDATOR_ABI,
2349
- provider
2350
- );
2351
- }
2352
- // ── M6: Basic Session Keys ────────────────────────────────────
2353
- /**
2354
- * Build the hash that the account owner must sign to grant a session key.
2355
- * Use grantSession() with this sig, or grantSessionDirect() from the account itself.
2356
- */
2357
- async buildGrantHash(params) {
2358
- return this.skValidator.buildGrantHash(
2359
- params.account,
2360
- params.sessionKey,
2361
- buildSessionStruct(params)
2362
- );
2363
- }
2364
- /** Query an ECDSA session key state (decodes the 8-field Session tuple). */
2365
- async getSession(account, sessionKey) {
2366
- const session = await this.skValidator.getSession(account, sessionKey);
2367
- return decodeSessionInfo(session);
2368
- }
2369
- /** Check if an ECDSA session is currently active. */
2370
- async isSessionActive(account, sessionKey) {
2371
- return this.skValidator.isSessionActive(account, sessionKey);
2372
- }
2373
- /**
2374
- * Encode calldata for session grant.
2375
- *
2376
- * - **With ownerSig** → `grantSession()` — for gasless/UserOp flows.
2377
- * Owner signs the GRANT_SESSION_V2 typed hash via KMS `sign-grant-session`,
2378
- * then the relayer calls `grantSession(account, key, cfg, ownerSig)` on-chain.
2379
- * This is the ONLY path for ERC-4337 sponsored / gasless grant flows.
2380
- *
2381
- * - **Without ownerSig** → `grantSessionDirect()` — **owner EOA direct-send only**.
2382
- * Since v0.17.2 round 3, `grantSessionDirect` requires `msg.sender == ownerOf(account)`.
2383
- * It does NOT accept `msg.sender == account` (removed in round 3 — confused-deputy fix).
2384
- * Do NOT encode this for a UserOp callData; the EntryPoint is not the owner EOA.
2385
- */
2386
- encodeGrantSession(params) {
2387
- const iface = new ethers.Interface(SESSION_KEY_VALIDATOR_ABI);
2388
- const cfg = buildSessionStruct(params);
2389
- if (params.ownerSig) {
2390
- return iface.encodeFunctionData("grantSession", [
2391
- params.account,
2392
- params.sessionKey,
2393
- cfg,
2394
- params.ownerSig
2395
- ]);
2396
- }
2397
- return iface.encodeFunctionData("grantSessionDirect", [
2398
- params.account,
2399
- params.sessionKey,
2400
- cfg
2401
- ]);
2402
- }
2403
- /** Encode calldata for revokeSession(). */
2404
- encodeRevokeSession(account, sessionKey) {
2405
- const iface = new ethers.Interface(SESSION_KEY_VALIDATOR_ABI);
2406
- return iface.encodeFunctionData("revokeSession", [account, sessionKey]);
2407
- }
2408
- // ── M6: P256 / Passkey Session Keys ───────────────────────────
2409
- /**
2410
- * Build the hash that the account owner must sign to grant a P256/passkey session key.
2411
- * Use grantP256Session() with this sig, or grantP256SessionDirect() from the owner EOA itself.
2412
- * The owner/KMS signs this hash to authorize a gasless grantP256Session().
2413
- */
2414
- async buildP256GrantHash(params) {
2415
- return this.skValidator.buildP256GrantHash(
2416
- params.account,
2417
- params.keyX,
2418
- params.keyY,
2419
- buildSessionStruct(params)
2420
- );
2421
- }
2422
- /**
2423
- * Query a P256 session key state (decodes the 8-field Session tuple).
2424
- * @param keyHash The keccak256 hash of (keyX, keyY) used as the on-chain session id.
2425
- */
2426
- async getP256Session(account, keyHash) {
2427
- const session = await this.skValidator.getP256Session(account, keyHash);
2428
- return decodeSessionInfo(session);
2429
- }
2430
- /** Check if a P256 session is currently active. */
2431
- async isP256SessionActive(account, keyX, keyY) {
2432
- return this.skValidator.isP256SessionActive(account, keyX, keyY);
2433
- }
2434
- /**
2435
- * Encode calldata for a P256/passkey session grant.
2436
- *
2437
- * - **With ownerSig** → `grantP256Session()` — for gasless/UserOp flows.
2438
- * Owner signs the buildP256GrantHash() digest via KMS `sign-p256-grant-session`,
2439
- * then the relayer calls `grantP256Session(account, keyX, keyY, cfg, ownerSig)` on-chain.
2440
- * This is the ONLY path for ERC-4337 sponsored / gasless P256 grant flows.
2441
- *
2442
- * - **Without ownerSig** → `grantP256SessionDirect()` — **owner EOA direct-send only**.
2443
- * Since v0.17.2 round 3, `grantP256SessionDirect` requires `msg.sender == ownerOf(account)`.
2444
- * It does NOT accept `msg.sender == account` (removed in round 3 — confused-deputy fix).
2445
- * Do NOT encode this for a UserOp callData; the EntryPoint is not the owner EOA.
2446
- */
2447
- encodeGrantP256Session(params) {
2448
- const iface = new ethers.Interface(SESSION_KEY_VALIDATOR_ABI);
2449
- const cfg = buildSessionStruct(params);
2450
- if (params.ownerSig) {
2451
- return iface.encodeFunctionData("grantP256Session", [
2452
- params.account,
2453
- params.keyX,
2454
- params.keyY,
2455
- cfg,
2456
- params.ownerSig
2457
- ]);
2458
- }
2459
- return iface.encodeFunctionData("grantP256SessionDirect", [
2460
- params.account,
2461
- params.keyX,
2462
- params.keyY,
2463
- cfg
2464
- ]);
2465
- }
2466
- /** Encode calldata for revokeP256Session(). */
2467
- encodeRevokeP256Session(account, keyX, keyY) {
2468
- const iface = new ethers.Interface(SESSION_KEY_VALIDATOR_ABI);
2469
- return iface.encodeFunctionData("revokeP256Session", [account, keyX, keyY]);
2470
- }
2471
- // ── M7: Agent Session Keys ────────────────────────────────────
2472
- /**
2473
- * Encode calldata for grantAgentSession().
2474
- * Must be called from the account (via UserOp or direct execute).
2475
- * The contract uses msg.sender as the account — no account param needed.
2476
- */
2477
- encodeGrantAgentSession(sessionKey, cfg) {
2478
- const iface = new ethers.Interface(AGENT_SESSION_KEY_VALIDATOR_ABI);
2479
- return iface.encodeFunctionData("grantAgentSession", [
2480
- sessionKey,
2481
- {
2482
- expiry: cfg.expiry,
2483
- velocityLimit: cfg.velocityLimit,
2484
- velocityWindow: cfg.velocityWindow,
2485
- revoked: false,
2486
- callTargets: cfg.callTargets,
2487
- selectorAllowlist: cfg.selectorAllowlist
2488
- }
2489
- ]);
2490
- }
2491
- /**
2492
- * Encode calldata for delegateSession() — sub-agent delegation.
2493
- * The sub-agent config must be a strict subset of the parent session's scope.
2494
- * Called by the parent session key (not the account owner).
2495
- * @param account The smart account under which the parent session was granted.
2496
- */
2497
- encodeDelegateSession(account, subKey, subCfg) {
2498
- const iface = new ethers.Interface(AGENT_SESSION_KEY_VALIDATOR_ABI);
2499
- return iface.encodeFunctionData("delegateSession", [
2500
- account,
2501
- subKey,
2502
- {
2503
- expiry: subCfg.expiry,
2504
- velocityLimit: subCfg.velocityLimit,
2505
- velocityWindow: subCfg.velocityWindow,
2506
- revoked: false,
2507
- callTargets: subCfg.callTargets,
2508
- selectorAllowlist: subCfg.selectorAllowlist
2509
- }
2510
- ]);
2511
- }
2512
- /** Encode calldata for revokeAgentSession(). */
2513
- encodeRevokeAgentSession(sessionKey) {
2514
- const iface = new ethers.Interface(AGENT_SESSION_KEY_VALIDATOR_ABI);
2515
- return iface.encodeFunctionData("revokeAgentSession", [sessionKey]);
2516
- }
2517
- /** Query agent session config + runtime state. */
2518
- async getAgentSession(account, sessionKey) {
2519
- const [expiry, velocityLimit, velocityWindow, revoked, callTargets, selectorAllowlist] = await this.askValidator.agentSessions(account, sessionKey);
2520
- const [callCount, windowStart] = await this.askValidator.sessionStates(account, sessionKey);
2521
- return {
2522
- expiry: Number(expiry),
2523
- velocityLimit: Number(velocityLimit),
2524
- velocityWindow: Number(velocityWindow),
2525
- callTargets,
2526
- selectorAllowlist,
2527
- revoked,
2528
- callCount: BigInt(callCount),
2529
- windowStart: BigInt(windowStart)
2530
- };
2531
- }
2532
- /** Check if an agent session is active (not expired, not revoked). */
2533
- async isAgentSessionActive(account, sessionKey) {
2534
- const session = await this.getAgentSession(account, sessionKey);
2535
- return session.expiry > Math.floor(Date.now() / 1e3) && !session.revoked;
2536
- }
2537
- /** Return the parent account of a delegated session key. */
2538
- async getSessionKeyOwner(sessionKey) {
2539
- return this.askValidator.sessionKeyOwner(sessionKey);
2540
- }
2541
- /** Return the parent key that delegated to subKey, or ZeroAddress if not delegated. */
2542
- async getDelegatedBy(account, subKey) {
2543
- return this.askValidator.delegatedBy(account, subKey);
2544
- }
2545
- };
2546
- function packSecp256k1SessionSignature(account, sessionKey, signature) {
2547
- const acc = account.startsWith("0x") ? account.slice(2) : account;
2548
- const key = sessionKey.startsWith("0x") ? sessionKey.slice(2) : sessionKey;
2549
- const sig = signature.startsWith("0x") ? signature.slice(2) : signature;
2550
- if (acc.length !== 40) throw new Error("account must be 20 bytes (40 hex chars)");
2551
- if (key.length !== 40) throw new Error("sessionKey must be 20 bytes (40 hex chars)");
2552
- if (sig.length !== 130) throw new Error("signature must be 65 bytes (130 hex chars)");
2553
- return `0x08${acc}${key}${sig}`;
2554
- }
2555
- function packP256SessionSignature(account, keyX, keyY, signature) {
2556
- const acc = account.startsWith("0x") ? account.slice(2) : account;
2557
- const x = keyX.startsWith("0x") ? keyX.slice(2) : keyX;
2558
- const y = keyY.startsWith("0x") ? keyY.slice(2) : keyY;
2559
- const sig = signature.startsWith("0x") ? signature.slice(2) : signature;
2560
- if (acc.length !== 40) throw new Error("account must be 20 bytes (40 hex chars)");
2561
- if (x.length !== 64) throw new Error("keyX must be 32 bytes (64 hex chars)");
2562
- if (y.length !== 64) throw new Error("keyY must be 32 bytes (64 hex chars)");
2563
- if (sig.length !== 128) throw new Error("P256 signature must be 64 bytes (128 hex chars, R||S)");
2564
- return `0x08${acc}${x}${y}${sig}`;
2565
- }
2566
- var EXTENDED_GUARD_ABI = [
2567
- ...GLOBAL_GUARD_ABI,
2568
- "function todaySpent() external view returns (uint256)",
2569
- "function tokenTodaySpent(address token) external view returns (uint256)",
2570
- // approvedAlgorithms removed from the guard in v0.17.2-beta.4 — now read from the account.
2571
- "function tier1Limit() external view returns (uint256)",
2572
- "function tier2Limit() external view returns (uint256)",
2573
- "function minDailyLimit() external view returns (uint256)"
2574
- ];
2575
- var GuardStateReader = class {
2576
- provider;
2577
- constructor(provider) {
2578
- this.provider = provider;
2579
- }
2580
- /**
2581
- * Read the full ETH guard state for an account.
2582
- * Returns null if the account has no guard (dailyLimit=0).
2583
- */
2584
- async getGuardState(accountAddress) {
2585
- const account = new ethers.Contract(accountAddress, AIRACCOUNT_ABI, this.provider);
2586
- const guardAddress = await account.guard();
2587
- if (guardAddress === ethers.ZeroAddress) return null;
2588
- const guard = new ethers.Contract(guardAddress, EXTENDED_GUARD_ABI, this.provider);
2589
- const [dailyLimit, remaining, todaySpent, tier1Limit, tier2Limit, minDailyLimit] = await Promise.all([
2590
- guard.dailyLimit(),
2591
- guard.remainingDailyAllowance(),
2592
- guard.todaySpent(),
2593
- guard.tier1Limit().catch(() => 0n),
2594
- guard.tier2Limit().catch(() => 0n),
2595
- guard.minDailyLimit().catch(() => 0n)
2596
- ]);
2597
- return {
2598
- dailyLimit: BigInt(dailyLimit),
2599
- todaySpent: BigInt(todaySpent),
2600
- remaining: BigInt(remaining),
2601
- currentTier: resolveTierFromSpend(BigInt(todaySpent), BigInt(tier1Limit), BigInt(tier2Limit)),
2602
- tier1Limit: BigInt(tier1Limit),
2603
- tier2Limit: BigInt(tier2Limit),
2604
- minDailyLimit: BigInt(minDailyLimit),
2605
- guardAddress
2606
- };
2607
- }
2608
- /**
2609
- * Read per-token guard state.
2610
- * Returns null if the token is not configured on the guard.
2611
- */
2612
- async getTokenGuardState(accountAddress, token) {
2613
- const account = new ethers.Contract(accountAddress, AIRACCOUNT_ABI, this.provider);
2614
- const guardAddress = await account.guard();
2615
- if (guardAddress === ethers.ZeroAddress) return null;
2616
- const guard = new ethers.Contract(guardAddress, EXTENDED_GUARD_ABI, this.provider);
2617
- try {
2618
- const todaySpent = await guard.tokenTodaySpent(token);
2619
- return {
2620
- token,
2621
- todaySpent: BigInt(todaySpent),
2622
- dailyLimit: 0n,
2623
- // token daily limit not directly exposed
2624
- remaining: 0n,
2625
- currentTier: 1,
2626
- tier1Limit: 0n,
2627
- tier2Limit: 0n
2628
- };
2629
- } catch {
2630
- return null;
2631
- }
2632
- }
2633
- /**
2634
- * Determine the minimum tier required to send a given ETH amount.
2635
- * Useful for showing "this transfer needs 2 signatures" before submission.
2636
- */
2637
- async requiredTierForAmount(accountAddress, amountWei) {
2638
- const state = await this.getGuardState(accountAddress);
2639
- if (!state) return 1;
2640
- const projectedSpend = state.todaySpent + amountWei;
2641
- return resolveTierFromSpend(projectedSpend, state.tier1Limit, state.tier2Limit);
2642
- }
2643
- /**
2644
- * Check if a given algorithm ID is approved on the guard.
2645
- */
2646
- async isAlgorithmApproved(accountAddress, algId) {
2647
- const account = new ethers.Contract(accountAddress, AIRACCOUNT_ABI, this.provider);
2648
- return account.approvedAlgorithms(algId);
2649
- }
2650
- };
2651
- function resolveTierFromSpend(spent, tier1Limit, tier2Limit) {
2652
- if (tier1Limit === 0n) return 1;
2653
- if (spent < tier1Limit) return 1;
2654
- if (tier2Limit === 0n || spent < tier2Limit) return 2;
2655
- return 3;
2656
- }
2657
- function computeOapdSalt(owner, dappId) {
2658
- const packed = ethers.solidityPacked(["address", "string"], [owner, dappId]);
2659
- return BigInt(ethers.keccak256(packed));
2660
- }
2661
- async function getOapdAddress(provider, config) {
2662
- const factoryAddress = config.factoryAddress ?? AIRACCOUNT_ADDRESSES.sepolia.factory;
2663
- const factory = new ethers.Contract(factoryAddress, AIRACCOUNT_FACTORY_ABI, provider);
2664
- const salt = computeOapdSalt(config.owner, config.dappId);
2665
- return factory.getFunction("getAddress")(config.owner, salt, config.initConfig);
2666
- }
2667
- async function getOapdAddressWithChainId(provider, config) {
2668
- const factoryAddress = config.factoryAddress ?? AIRACCOUNT_ADDRESSES.sepolia.factory;
2669
- const factory = new ethers.Contract(factoryAddress, AIRACCOUNT_FACTORY_ABI, provider);
2670
- const salt = computeOapdSalt(config.owner, config.dappId);
2671
- const result = await factory.getFunction("getAddressWithChainId")(
2672
- config.owner,
2673
- salt,
2674
- config.initConfig
2675
- );
2676
- return { address: result[0], chainQualified: result[1] };
2677
- }
2678
- async function isOapdDeployed(provider, config) {
2679
- const address = await getOapdAddress(provider, config);
2680
- const code = await provider.getCode(address);
2681
- return code !== "0x";
2682
- }
2683
- var ALG_BLS = 1;
2684
- var ALG_ECDSA = 2;
2685
- var ALG_P256 = 3;
2686
- var ALG_CUMULATIVE_T2 = 4;
2687
- var ALG_CUMULATIVE_T3 = 5;
2688
- function resolveTier(value, config) {
2689
- if (config.tier1Limit === 0n && config.tier2Limit === 0n) return 1;
2690
- if (config.tier1Limit > 0n && value <= config.tier1Limit) return 1;
2691
- if (config.tier2Limit > 0n && value <= config.tier2Limit) return 2;
2692
- return 3;
2693
- }
2694
- function algIdForTier(tier) {
2695
- switch (tier) {
2696
- case 1:
2697
- return ALG_ECDSA;
2698
- case 2:
2699
- return ALG_CUMULATIVE_T2;
2700
- case 3:
2701
- return ALG_CUMULATIVE_T3;
2702
- }
2703
- }
2704
- var ALG_NAMES = {
2705
- [ALG_BLS]: "BLS (0x01)",
2706
- [ALG_ECDSA]: "ECDSA (0x02)",
2707
- [ALG_P256]: "P256 (0x03)",
2708
- [ALG_CUMULATIVE_T2]: "Cumulative T2 (0x04)",
2709
- [ALG_CUMULATIVE_T3]: "Cumulative T3 (0x05)"
2710
- };
2711
- var GuardChecker = class {
2712
- constructor(ethereum, logger) {
2713
- this.ethereum = ethereum;
2714
- this.logger = logger ?? new ConsoleLogger("[GuardChecker]");
2715
- }
2716
- logger;
2717
- /**
2718
- * Fetch tier limits from an AirAccount contract.
2719
- */
2720
- async fetchTierConfig(accountAddress) {
2721
- const provider = this.ethereum.getProvider();
2722
- const account = new ethers.Contract(accountAddress, AIRACCOUNT_ABI, provider);
2723
- const [tier1Limit, tier2Limit] = await Promise.all([
2724
- account.tier1Limit(),
2725
- account.tier2Limit()
2726
- ]);
2727
- return {
2728
- tier1Limit: BigInt(tier1Limit),
2729
- tier2Limit: BigInt(tier2Limit)
2730
- };
2731
- }
2732
- /**
2733
- * Fetch guard status from the account's GlobalGuard.
2734
- */
2735
- async fetchGuardStatus(accountAddress) {
2736
- const provider = this.ethereum.getProvider();
2737
- const account = new ethers.Contract(accountAddress, AIRACCOUNT_ABI, provider);
2738
- const config = await account.getConfigDescription();
2739
- const guardAddress = config.guardAddress;
2740
- if (guardAddress === ethers.ZeroAddress) {
2741
- return {
2742
- hasGuard: false,
2743
- guardAddress: ethers.ZeroAddress,
2744
- dailyLimit: 0n,
2745
- dailyRemaining: 0n
2746
- };
2747
- }
2748
- const guard = new ethers.Contract(guardAddress, GLOBAL_GUARD_ABI, provider);
2749
- const [dailyLimit, dailyRemaining] = await Promise.all([
2750
- guard.dailyLimit(),
2751
- guard.remainingDailyAllowance()
2752
- ]);
2753
- return {
2754
- hasGuard: true,
2755
- guardAddress,
2756
- dailyLimit: BigInt(dailyLimit),
2757
- dailyRemaining: BigInt(dailyRemaining)
2758
- };
2759
- }
2760
- /**
2761
- * Pre-check a transaction: determine tier, check guard limits and algorithm approval.
2762
- * Returns errors array (empty = OK to proceed).
2763
- */
2764
- async preCheck(accountAddress, value) {
2765
- const errors = [];
2766
- const tierConfig = await this.fetchTierConfig(accountAddress);
2767
- const tier = resolveTier(value, tierConfig);
2768
- const algId = algIdForTier(tier);
2769
- const guard = await this.fetchGuardStatus(accountAddress);
2770
- if (!guard.hasGuard) {
2771
- return { ok: true, errors: [], tier, algId };
2772
- }
2773
- if (guard.dailyLimit > 0n && value > guard.dailyRemaining) {
2774
- errors.push(
2775
- `Daily limit exceeded: requesting ${value} wei but only ${guard.dailyRemaining} remaining (limit: ${guard.dailyLimit})`
2776
- );
2777
- }
2778
- const provider = this.ethereum.getProvider();
2779
- const accountContract = new ethers.Contract(accountAddress, AIRACCOUNT_ABI, provider);
2780
- const isApproved = await accountContract.approvedAlgorithms(algId);
2781
- if (!isApproved) {
2782
- errors.push(
2783
- `Algorithm ${ALG_NAMES[algId] ?? `0x${algId.toString(16)}`} is not approved by the account`
2784
- );
2785
- }
2786
- if (errors.length > 0) {
2787
- this.logger.warn(`Pre-check failed for ${accountAddress}: ${errors.join("; ")}`);
2788
- }
2789
- return { ok: errors.length === 0, errors, tier, algId };
2790
- }
2791
- };
2792
- var FORCE_EXIT_ABI = [
2793
- // ERC-7579 module lifecycle
2794
- "function onInstall(bytes calldata data) external",
2795
- "function onUninstall(bytes calldata data) external",
2796
- "function isInitialized(address smartAccount) external view returns (bool)",
2797
- // Proposal lifecycle
2798
- "function proposeForceExit(address target, uint256 value, bytes calldata data) external",
2799
- "function approveForceExit(address account, bytes calldata guardianSig) external",
2800
- "function executeForceExit(address account) external",
2801
- "function cancelForceExit(address account) external",
2802
- // State readers
2803
- "function getPendingExit(address account) external view returns (address target, uint256 value, bytes memory data, uint256 proposedAt, uint256 approvalBitmap, address[3] memory guardians)",
2804
- "function accountL2Type(address account) external view returns (uint8)",
2805
- // Constants
2806
- "function APPROVAL_THRESHOLD() external view returns (uint8)",
2807
- "function MODULE_VERSION() external view returns (string)",
2808
- // Errors (for decoding reverts)
2809
- "error AlreadyApproved()",
2810
- "error AlreadyProposed()",
2811
- "error ForceExitCallFailed()",
2812
- "error IncompatibleAccount()",
2813
- "error InvalidGuardianSig()",
2814
- "error NoProposal()",
2815
- "error NotEnoughApprovals()",
2816
- "error NotInstalled()",
2817
- "error NotOwner()",
2818
- "error SignerNoLongerGuardian()",
2819
- "error UnsupportedL2Type()"
2820
- ];
2821
- var L2_TYPE = {
2822
- OPTIMISM: 1,
2823
- ARBITRUM: 2
2824
- };
2825
- var ForceExitService = class {
2826
- constructor(moduleAddress, providerOrSigner) {
2827
- this.moduleAddress = moduleAddress;
2828
- this.contract = new ethers.Contract(moduleAddress, FORCE_EXIT_ABI, providerOrSigner);
2829
- this.iface = new ethers.Interface(FORCE_EXIT_ABI);
2830
- }
2831
- contract;
2832
- iface;
2833
- // ── On-chain reads ──────────────────────────────────────────────
2834
- async isInitialized(smartAccount) {
2835
- return this.contract.isInitialized(smartAccount);
2836
- }
2837
- async getPendingExit(account) {
2838
- const [target, value, data, proposedAt, approvalBitmap, guardians] = await this.contract.getPendingExit(account);
2839
- return {
2840
- target,
2841
- value: BigInt(value),
2842
- data,
2843
- proposedAt: BigInt(proposedAt),
2844
- approvalBitmap: BigInt(approvalBitmap),
2845
- guardians
2846
- };
2847
- }
2848
- async getAccountL2Type(account) {
2849
- return Number(await this.contract.accountL2Type(account));
2850
- }
2851
- async getApprovalThreshold() {
2852
- return Number(await this.contract.APPROVAL_THRESHOLD());
2853
- }
2854
- async getModuleVersion() {
2855
- return this.contract.MODULE_VERSION();
2856
- }
2857
- // ── Calldata encoders (for UserOp or direct tx submission) ─────
2858
- /**
2859
- * Encode onInstall calldata for installModule() call on the smart account.
2860
- * Must be submitted by the account owner, with moduleTypeId=2 (EXECUTOR).
2861
- *
2862
- * @param l2Type - L2_TYPE.OPTIMISM (1) or L2_TYPE.ARBITRUM (2)
2863
- * @example
2864
- * const calldata = forceExit.encodeOnInstall(L2_TYPE.OPTIMISM);
2865
- * // account.installModule(2, forceExitModuleAddress, calldata)
2866
- */
2867
- encodeOnInstall(l2Type) {
2868
- return this.iface.encodeFunctionData("onInstall", [
2869
- ethers.AbiCoder.defaultAbiCoder().encode(["uint8"], [l2Type])
2870
- ]);
2871
- }
2872
- encodeOnUninstall() {
2873
- return this.iface.encodeFunctionData("onUninstall", ["0x"]);
2874
- }
2875
- /**
2876
- * Encode calldata for proposeForceExit — the exit payload to bridge out of L2.
2877
- * `target` is the L2→L1 bridge contract; `data` is the bridge call payload.
2878
- */
2879
- encodeProposeForceExit(target, value, data) {
2880
- return this.iface.encodeFunctionData("proposeForceExit", [target, value, data]);
2881
- }
2882
- /**
2883
- * Encode calldata for approveForceExit — guardian signs off on the pending proposal.
2884
- * `guardianSig` must be an EIP-191 personal_sign over the proposal hash.
2885
- */
2886
- encodeApproveForceExit(account, guardianSig) {
2887
- return this.iface.encodeFunctionData("approveForceExit", [account, guardianSig]);
2888
- }
2889
- encodeExecuteForceExit(account) {
2890
- return this.iface.encodeFunctionData("executeForceExit", [account]);
2891
- }
2892
- encodeCancelForceExit(account) {
2893
- return this.iface.encodeFunctionData("cancelForceExit", [account]);
2894
- }
2895
- // ── Convenience: send transactions (requires Signer) ──────────
2896
- async proposeForceExit(target, value, data) {
2897
- return this.contract.proposeForceExit(target, value, data);
2898
- }
2899
- async approveForceExit(account, guardianSig) {
2900
- return this.contract.approveForceExit(account, guardianSig);
2901
- }
2902
- async executeForceExit(account) {
2903
- return this.contract.executeForceExit(account);
2904
- }
2905
- async cancelForceExit(account) {
2906
- return this.contract.cancelForceExit(account);
2907
- }
2908
- };
2909
- var RECOVERY_THRESHOLD = 2;
2910
- var MAX_GUARDIANS = 3;
2911
- var RECOVERY_TIMELOCK_SECONDS = 2n * 24n * 60n * 60n;
2912
- function popcount(value) {
2913
- let v = value;
2914
- let count = 0;
2915
- while (v > 0n) {
2916
- count += Number(v & 1n);
2917
- v >>= 1n;
2918
- }
2919
- return count;
2920
- }
2921
- var RecoveryService = class {
2922
- constructor(providerOrSigner) {
2923
- this.providerOrSigner = providerOrSigner;
2924
- this.iface = new ethers.Interface(AIRACCOUNT_ABI);
2925
- }
2926
- iface;
2927
- // ── Calldata encoders (submit TO the account address) ─────────────
2928
- /**
2929
- * Encode `addGuardian(guardian)` calldata. **Owner only.**
2930
- * Registers a recovery guardian; reverts once 3 guardians are set, or if the
2931
- * guardian is `address(0)`, the owner, or already registered.
2932
- */
2933
- encodeAddGuardian(guardian) {
2934
- return this.iface.encodeFunctionData("addGuardian", [guardian]);
2935
- }
2936
- /**
2937
- * Encode `removeGuardian(index, guardianSigs)` calldata. **Owner only**, and
2938
- * requires >= {@link RECOVERY_THRESHOLD} guardian signatures over the removal
2939
- * hash. Cannot remove while a recovery is active, nor drop below 2 guardians.
2940
- *
2941
- * @param index Guardian slot to remove (0-indexed).
2942
- * @param guardianSigs EIP-191 guardian signatures over the removal hash.
2943
- */
2944
- encodeRemoveGuardian(index, guardianSigs) {
2945
- return this.iface.encodeFunctionData("removeGuardian", [index, guardianSigs]);
2946
- }
2947
- /**
2948
- * Encode `proposeRecovery(newOwner)` calldata. **Any guardian** may call.
2949
- * Starts the {@link RECOVERY_TIMELOCK_SECONDS} timelock and records the
2950
- * proposer's approval (1 of {@link RECOVERY_THRESHOLD}).
2951
- */
2952
- encodeProposeRecovery(newOwner) {
2953
- return this.iface.encodeFunctionData("proposeRecovery", [newOwner]);
2954
- }
2955
- /**
2956
- * Encode `approveRecovery()` calldata. **Another guardian** approves the
2957
- * active proposal, setting its bit in `approvalBitmap`.
2958
- */
2959
- encodeApproveRecovery() {
2960
- return this.iface.encodeFunctionData("approveRecovery", []);
2961
- }
2962
- /**
2963
- * Encode `cancelRecovery()` calldata. **Guardians only** — each call is one
2964
- * vote; recovery is dropped once {@link RECOVERY_THRESHOLD} cancel votes are
2965
- * reached. The owner cannot cancel.
2966
- */
2967
- encodeCancelRecovery() {
2968
- return this.iface.encodeFunctionData("cancelRecovery", []);
2969
- }
2970
- /**
2971
- * Encode `executeRecovery()` calldata. **Anyone** may call, but it only
2972
- * succeeds once the timelock has elapsed and the approval threshold is met.
2973
- * Rotates the account owner to the proposed `newOwner`.
2974
- */
2975
- encodeExecuteRecovery() {
2976
- return this.iface.encodeFunctionData("executeRecovery", []);
2977
- }
2978
- // ── On-chain reads (against the account address) ──────────────────
2979
- /**
2980
- * Read and decode the account's `activeRecovery()` struct.
2981
- * Returns derived `approvalCount`, `cancellationCount`, `executeAfter`, and
2982
- * `isActive` alongside the raw fields.
2983
- *
2984
- * @param account The AirAccount address to query.
2985
- */
2986
- async getActiveRecovery(account) {
2987
- const contract = new ethers.Contract(account, AIRACCOUNT_ABI, this.providerOrSigner);
2988
- const [newOwner, proposedAt, approvalBitmap, cancellationBitmap] = await contract.activeRecovery();
2989
- const proposedAtBn = BigInt(proposedAt);
2990
- const approvalBitmapBn = BigInt(approvalBitmap);
2991
- const cancellationBitmapBn = BigInt(cancellationBitmap);
2992
- return {
2993
- newOwner,
2994
- proposedAt: proposedAtBn,
2995
- approvalBitmap: approvalBitmapBn,
2996
- cancellationBitmap: cancellationBitmapBn,
2997
- approvalCount: popcount(approvalBitmapBn),
2998
- cancellationCount: popcount(cancellationBitmapBn),
2999
- executeAfter: proposedAtBn + RECOVERY_TIMELOCK_SECONDS,
3000
- isActive: newOwner !== ethers.ZeroAddress
3001
- };
3002
- }
3003
- /**
3004
- * Read the number of registered guardians via `guardianCount()`.
3005
- *
3006
- * @param account The AirAccount address to query.
3007
- */
3008
- async getGuardianCount(account) {
3009
- const contract = new ethers.Contract(account, AIRACCOUNT_ABI, this.providerOrSigner);
3010
- return Number(await contract.guardianCount());
3011
- }
3012
- /**
3013
- * Read the full guardian address list.
3014
- *
3015
- * The V7 account exposes positional `guardians(uint256 i)` (3 packed slots) plus
3016
- * `guardianCount()` — there is no single `getGuardians()` getter on the account
3017
- * (that exists only on `AirAccountDelegate`, the EIP-7702 path). This reads slots
3018
- * `0..guardianCount-1` and returns the non-zero guardian addresses.
3019
- *
3020
- * @param account The AirAccount address to query.
3021
- */
3022
- async getGuardians(account) {
3023
- const contract = new ethers.Contract(account, AIRACCOUNT_ABI, this.providerOrSigner);
3024
- const count = Number(await contract.guardianCount());
3025
- const guardians = [];
3026
- for (let i = 0; i < count; i++) {
3027
- const g = await contract.guardians(i);
3028
- if (g !== ethers.ZeroAddress) guardians.push(g);
3029
- }
3030
- return guardians;
3031
- }
3032
- };
3033
- var DELEGATE_ABI = [
3034
- "function initialize(address guardian1, bytes calldata g1Sig, address guardian2, bytes calldata g2Sig, uint256 dailyLimit) external",
3035
- "function owner() external view returns (address)",
3036
- "function isInitialized() external view returns (bool)",
3037
- "function entryPoint() external view returns (address)",
3038
- "function getGuardians() external view returns (address[3] memory)",
3039
- "function getDeposit() external view returns (uint256)",
3040
- "function execute(address dest, uint256 value, bytes calldata data) external",
3041
- "function executeBatch(address[] calldata dest, uint256[] calldata value, bytes[] calldata data) external",
3042
- "function validateUserOp((address sender, uint256 nonce, bytes initCode, bytes callData, bytes32 accountGasLimits, uint256 preVerificationGas, bytes32 gasFees, bytes paymasterAndData, bytes signature) calldata userOp, bytes32 userOpHash, uint256 missingFunds) external returns (uint256)",
3043
- "function initiateRescue(address rescueTo) external",
3044
- "function approveRescue() external",
3045
- "function cancelRescue() external",
3046
- "function executeRescue() external",
3047
- "function addDeposit() external payable",
3048
- "function withdrawDepositTo(address to, uint256 amount) external",
3049
- "error AlreadyInitialized()",
3050
- "error NotInitialized()",
3051
- "error InvalidAddress()",
3052
- "error InvalidGuardianSignature()"
3053
- ];
3054
- var AIR_ACCOUNT_DELEGATE_ADDRESS = "0x8603AAF6C3f07fdae810B323c95a198D796EC52E";
3055
- var EIP7702DelegateService = class {
3056
- constructor(delegateAddress = AIR_ACCOUNT_DELEGATE_ADDRESS, providerOrSigner) {
3057
- this.delegateAddress = delegateAddress;
3058
- this.iface = new ethers.Interface(DELEGATE_ABI);
3059
- if (providerOrSigner) {
3060
- this.contract = new ethers.Contract(delegateAddress, DELEGATE_ABI, providerOrSigner);
3061
- } else {
3062
- this.contract = new ethers.Contract(delegateAddress, DELEGATE_ABI);
3063
- }
3064
- }
3065
- iface;
3066
- contract;
3067
- // ── Calldata encoders ─────────────────────────────────────────
3068
- /**
3069
- * Encode initialize() calldata for the first post-delegation UserOp.
3070
- * Must be the callData of a UserOp sent immediately after the SET_CODE delegation activates.
3071
- * Guardian acceptance sigs follow the same EIP-712 scheme as AirAccountV7 createAccount.
3072
- */
3073
- encodeInitialize(params) {
3074
- return this.iface.encodeFunctionData("initialize", [
3075
- params.guardian1,
3076
- params.guardian1Sig,
3077
- params.guardian2,
3078
- params.guardian2Sig,
3079
- params.dailyLimit
3080
- ]);
3081
- }
3082
- encodeExecute(dest, value, data) {
3083
- return this.iface.encodeFunctionData("execute", [dest, value, data]);
3084
- }
3085
- encodeExecuteBatch(dests, values, datas) {
3086
- return this.iface.encodeFunctionData("executeBatch", [dests, values, datas]);
3087
- }
3088
- // ── EIP-7702 authorization hash construction ──────────────────
3089
- /**
3090
- * Compute the EIP-7702 SET_CODE authorization hash that the EOA must sign.
3091
- *
3092
- * Hash = keccak256(0x05 || RLP([chainId, address, nonce]))
3093
- *
3094
- * This is the hash the private key signs to delegate code execution to
3095
- * AirAccountDelegate. Use this hash with your KMS sign-hash endpoint or
3096
- * local ethers Signer.
3097
- *
3098
- * @param chainId - Target chain ID (11155111 for Sepolia)
3099
- * @param nonce - EOA's current transaction nonce
3100
- * @returns 32-byte hash (0x-prefixed) ready for signing
3101
- */
3102
- buildAuthorizationHash(chainId, nonce) {
3103
- const encoded = ethers.encodeRlp([
3104
- chainId === 0 ? "0x" : ethers.toBeHex(chainId),
3105
- this.delegateAddress,
3106
- nonce === 0n ? "0x" : ethers.toBeHex(nonce)
3107
- ]);
3108
- return ethers.keccak256(ethers.concat(["0x05", encoded]));
3109
- }
3110
- /**
3111
- * Build the full EIP-7702 authorization object for relay submission.
3112
- * The caller must sign `buildAuthorizationHash()` externally and pass the result here.
3113
- *
3114
- * @param chainId - Target chain ID
3115
- * @param nonce - EOA's current nonce
3116
- * @param signature - 65-byte ECDSA signature (R||S||V) over the authorization hash
3117
- */
3118
- buildAuthorization(chainId, nonce, signature) {
3119
- return {
3120
- chainId,
3121
- address: this.delegateAddress,
3122
- nonce,
3123
- signature
3124
- };
3125
- }
3126
- /**
3127
- * Verify that a signature is a valid EIP-7702 authorization for the given EOA address.
3128
- * Recovers the signer from the authorization hash and checks it matches `eoa`.
3129
- */
3130
- verifyAuthorization(eoa, chainId, nonce, signature) {
3131
- const hash = this.buildAuthorizationHash(chainId, nonce);
3132
- const recovered = ethers.recoverAddress(hash, signature);
3133
- return recovered.toLowerCase() === eoa.toLowerCase();
3134
- }
3135
- // ── On-chain reads (requires provider) ───────────────────────
3136
- async isInitialized(eoa) {
3137
- const provider = this.contract.runner;
3138
- if (!provider) throw new Error("EIP7702DelegateService: provider required for on-chain reads");
3139
- const c = new ethers.Contract(eoa, DELEGATE_ABI, provider);
3140
- return c.isInitialized();
3141
- }
3142
- async getOwner(eoa) {
3143
- const provider = this.contract.runner;
3144
- if (!provider) throw new Error("EIP7702DelegateService: provider required for on-chain reads");
3145
- const c = new ethers.Contract(eoa, DELEGATE_ABI, provider);
3146
- return c.owner();
3147
- }
3148
- async getGuardians(eoa) {
3149
- const provider = this.contract.runner;
3150
- if (!provider) throw new Error("EIP7702DelegateService: provider required for on-chain reads");
3151
- const c = new ethers.Contract(eoa, DELEGATE_ABI, provider);
3152
- return c.getGuardians();
3153
- }
3154
- };
3155
- var WEIGHTED_SIGNATURE_ABI = [
3156
- // Direct owner set (first-time / strengthening only)
3157
- "function setWeightConfig((uint8 passkeyWeight, uint8 ecdsaWeight, uint8 blsWeight, uint8 guardian0Weight, uint8 guardian1Weight, uint8 guardian2Weight, uint8 _padding, uint8 tier1Threshold, uint8 tier2Threshold, uint8 tier3Threshold) config) external",
3158
- // Guardian-governed change flow (required for any weakening)
3159
- "function proposeWeightChange((uint8 passkeyWeight, uint8 ecdsaWeight, uint8 blsWeight, uint8 guardian0Weight, uint8 guardian1Weight, uint8 guardian2Weight, uint8 _padding, uint8 tier1Threshold, uint8 tier2Threshold, uint8 tier3Threshold) proposed) external",
3160
- "function approveWeightChange() external",
3161
- "function cancelWeightChange() external",
3162
- "function executeWeightChange() external",
3163
- // State readers
3164
- "function weightConfig() external view returns (uint8 passkeyWeight, uint8 ecdsaWeight, uint8 blsWeight, uint8 guardian0Weight, uint8 guardian1Weight, uint8 guardian2Weight, uint8 _padding, uint8 tier1Threshold, uint8 tier2Threshold, uint8 tier3Threshold)",
3165
- "function pendingWeightChange() external view returns ((uint8 passkeyWeight, uint8 ecdsaWeight, uint8 blsWeight, uint8 guardian0Weight, uint8 guardian1Weight, uint8 guardian2Weight, uint8 _padding, uint8 tier1Threshold, uint8 tier2Threshold, uint8 tier3Threshold) proposed, uint256 proposedAt, uint256 approvalBitmap)",
3166
- // Errors (for decoding reverts)
3167
- "error InsecureWeightConfig()",
3168
- "error WeakeningRequiresProposal()",
3169
- "error WeightChangePending()",
3170
- "error NoWeightChangeProposal()",
3171
- "error WeightChangeAlreadyApproved()",
3172
- "error WeightChangeNotApproved()",
3173
- "error WeightChangeTimelockNotExpired()"
3174
- ];
3175
- var WEIGHT_CHANGE_TIMELOCK_SECONDS = 2 * 24 * 60 * 60;
3176
- var WEIGHT_CHANGE_THRESHOLD = 2;
3177
- var WEIGHT_CHANGE_EXPIRY_SECONDS = 30 * 24 * 60 * 60;
3178
- var WEIGHT_CONFIG_FIELDS = [
3179
- "passkeyWeight",
3180
- "ecdsaWeight",
3181
- "blsWeight",
3182
- "guardian0Weight",
3183
- "guardian1Weight",
3184
- "guardian2Weight",
3185
- "_padding",
3186
- "tier1Threshold",
3187
- "tier2Threshold",
3188
- "tier3Threshold"
3189
- ];
3190
- function toConfigTuple(config) {
3191
- return WEIGHT_CONFIG_FIELDS.map((f) => config[f]);
3192
- }
3193
- function fromConfigResult(result) {
3194
- const r = result;
3195
- const pick = (name, idx) => Number(r[name] ?? r[idx]);
3196
- return {
3197
- passkeyWeight: pick("passkeyWeight", 0),
3198
- ecdsaWeight: pick("ecdsaWeight", 1),
3199
- blsWeight: pick("blsWeight", 2),
3200
- guardian0Weight: pick("guardian0Weight", 3),
3201
- guardian1Weight: pick("guardian1Weight", 4),
3202
- guardian2Weight: pick("guardian2Weight", 5),
3203
- _padding: pick("_padding", 6),
3204
- tier1Threshold: pick("tier1Threshold", 7),
3205
- tier2Threshold: pick("tier2Threshold", 8),
3206
- tier3Threshold: pick("tier3Threshold", 9)
3207
- };
3208
- }
3209
- var WeightedSignatureService = class {
3210
- constructor(accountAddress, providerOrSigner) {
3211
- this.accountAddress = accountAddress;
3212
- this.contract = new ethers.Contract(accountAddress, WEIGHTED_SIGNATURE_ABI, providerOrSigner);
3213
- this.iface = new ethers.Interface(WEIGHTED_SIGNATURE_ABI);
3214
- }
3215
- contract;
3216
- iface;
3217
- // ── On-chain reads ──────────────────────────────────────────────
3218
- /** Read the account's current active WeightConfig. */
3219
- async getWeightConfig() {
3220
- const result = await this.contract.weightConfig();
3221
- return fromConfigResult(result);
3222
- }
3223
- /**
3224
- * Read the pending weight-change proposal. When `proposedAt === 0n` there is no
3225
- * active proposal (the returned `proposed` config will be all zeros).
3226
- */
3227
- async getPendingWeightChange() {
3228
- const [proposed, proposedAt, approvalBitmap] = await this.contract.pendingWeightChange();
3229
- return {
3230
- proposed: fromConfigResult(proposed),
3231
- proposedAt: BigInt(proposedAt),
3232
- approvalBitmap: BigInt(approvalBitmap)
3233
- };
3234
- }
3235
- // ── Calldata encoders (for UserOp or direct tx submission) ─────
3236
- /**
3237
- * Encode setWeightConfig calldata. OWNER only; for first-time setup or strengthening.
3238
- * Weakening an existing config must go through encodeProposeWeightChange instead.
3239
- */
3240
- encodeSetWeightConfig(config) {
3241
- return this.iface.encodeFunctionData("setWeightConfig", [toConfigTuple(config)]);
3242
- }
3243
- /**
3244
- * Encode proposeWeightChange calldata. OWNER only; opens a guardian-governed proposal
3245
- * (required for any weakening). Subject to 2-of-3 approval + 2-day timelock before execute.
3246
- */
3247
- encodeProposeWeightChange(config) {
3248
- return this.iface.encodeFunctionData("proposeWeightChange", [toConfigTuple(config)]);
3249
- }
3250
- /** Encode approveWeightChange calldata. GUARDIAN only; each guardian may approve once. */
3251
- encodeApproveWeightChange() {
3252
- return this.iface.encodeFunctionData("approveWeightChange", []);
3253
- }
3254
- /** Encode cancelWeightChange calldata. OWNER or any GUARDIAN may cancel a pending proposal. */
3255
- encodeCancelWeightChange() {
3256
- return this.iface.encodeFunctionData("cancelWeightChange", []);
3257
- }
3258
- /**
3259
- * Encode executeWeightChange calldata. Callable by anyone, but only succeeds once the
3260
- * threshold (2-of-3) and timelock (2 days) are both satisfied and the proposal has not expired.
3261
- */
3262
- encodeExecuteWeightChange() {
3263
- return this.iface.encodeFunctionData("executeWeightChange", []);
3264
- }
3265
- };
3266
- var AGENT_REGISTRY_ABI = [
3267
- // ── Writes ──
3268
- "function registerAgent(address agentWallet, bytes agentWalletSig) external",
3269
- "function revokeAgent(address agentWallet) external",
3270
- "function deregisterAgent(address agentWallet) external",
3271
- // ── Reads ──
3272
- "function isRegisteredAgent(address agentWallet) external view returns (bool)",
3273
- "function isValidAccount(address account) external view returns (bool)",
3274
- "function getHumanOwner(address agentWallet) external view returns (address)",
3275
- "function getAgentCount(address owner) external view returns (uint256)",
3276
- "function getAgentByIndex(address owner, uint256 index) external view returns (address)",
3277
- "function getAgents(address humanOwner) external view returns (address[])",
3278
- "function getAgentsPage(address owner, uint256 start, uint256 count) external view returns (address[] page)",
3279
- "function agentWalletOwner(address agentWallet) external view returns (address)",
3280
- "function ownerAgents(address owner, uint256 index) external view returns (address)",
3281
- "function balanceOf(address humanOwner) external view returns (uint256)",
3282
- // ── Errors (for decoding reverts) ──
3283
- "error AgentAlreadyRegistered()",
3284
- "error CallerNotAirAccount()",
3285
- "error InvalidAddress()",
3286
- "error InvalidAgentSignature()",
3287
- "error NotAgentOwner()",
3288
- "error SelfRegistrationForbidden()"
3289
- ];
3290
- var AgentRegistryService = class {
3291
- /**
3292
- * @param providerOrSigner ethers Provider (reads only) or Signer (reads + sends).
3293
- * @param registryAddress deployed AgentRegistry contract address.
3294
- */
3295
- constructor(providerOrSigner, registryAddress) {
3296
- this.registryAddress = registryAddress;
3297
- this.providerOrSigner = providerOrSigner;
3298
- this.contract = new ethers.Contract(registryAddress, AGENT_REGISTRY_ABI, providerOrSigner);
3299
- this.iface = new ethers.Interface(AGENT_REGISTRY_ABI);
3300
- this.factoryIface = new ethers.Interface(AIRACCOUNT_FACTORY_ABI);
3301
- this.accountIface = new ethers.Interface(AIRACCOUNT_ABI);
3302
- }
3303
- contract;
3304
- iface;
3305
- factoryIface;
3306
- accountIface;
3307
- providerOrSigner;
3308
- // ── Composed register/revoke-via-account scenario encoders ──────────────────
3309
- // registerAgent/revokeAgent require msg.sender == the agent account, so they are delivered
3310
- // through the account's execute(registry, 0, calldata). These return the FULL execute
3311
- // calldata to submit TO the agent account (owner-signed) — the scenario-level entry point.
3312
- /** Encode `account.execute(registry, 0, registerAgent(agentWallet, agentWalletSig))`. */
3313
- encodeRegisterAgentViaAccount(agentWallet, agentWalletSig) {
3314
- return this.accountIface.encodeFunctionData("execute", [
3315
- this.registryAddress,
3316
- 0n,
3317
- this.encodeRegisterAgent(agentWallet, agentWalletSig)
3318
- ]);
3319
- }
3320
- /** Encode `account.execute(registry, 0, revokeAgent(agentWallet))`. */
3321
- encodeRevokeAgentViaAccount(agentWallet) {
3322
- return this.accountIface.encodeFunctionData("execute", [
3323
- this.registryAddress,
3324
- 0n,
3325
- this.encodeRevokeAgent(agentWallet)
3326
- ]);
3327
- }
3328
- // ── AgentRegistry calldata encoders ─────────────────────────────────────────
3329
- /**
3330
- * Encode calldata for `registerAgent(agentWallet, agentWalletSig)`.
3331
- *
3332
- * Binds `agentWallet` to the caller (the human owner). `agentWalletSig` must be the agent
3333
- * wallet's EIP-191 signature proving control of the key. Reverts with
3334
- * `SelfRegistrationForbidden` if the caller registers itself, or `AgentAlreadyRegistered`
3335
- * if the wallet is already bound.
3336
- */
3337
- encodeRegisterAgent(agentWallet, agentWalletSig) {
3338
- return this.iface.encodeFunctionData("registerAgent", [agentWallet, agentWalletSig]);
3339
- }
3340
- /**
3341
- * Encode calldata for `revokeAgent(agentWallet)`.
3342
- *
3343
- * Owner-initiated revocation of a previously registered agent wallet. Caller must be the
3344
- * agent's human owner (else `NotAgentOwner`).
3345
- */
3346
- encodeRevokeAgent(agentWallet) {
3347
- return this.iface.encodeFunctionData("revokeAgent", [agentWallet]);
3348
- }
3349
- /**
3350
- * Encode calldata for `deregisterAgent(agentWallet)`.
3351
- *
3352
- * Removes the agent wallet from the registry (full deregistration, distinct from the
3353
- * lighter-weight `revokeAgent`). Caller must be the agent's human owner.
3354
- */
3355
- encodeDeregisterAgent(agentWallet) {
3356
- return this.iface.encodeFunctionData("deregisterAgent", [agentWallet]);
3357
- }
3358
- // ── AgentRegistry on-chain reads ────────────────────────────────────────────
3359
- /** Whether `agentWallet` is currently registered in the registry. */
3360
- async isRegisteredAgent(agentWallet) {
3361
- return this.contract.isRegisteredAgent(agentWallet);
3362
- }
3363
- /** Whether `account` has been marked valid (e.g. an AirAccount minted by the bound factory). */
3364
- async isValidAccount(account) {
3365
- return this.contract.isValidAccount(account);
3366
- }
3367
- /** The human owner bound to `agentWallet` (ZeroAddress if unregistered). */
3368
- async getHumanOwner(agentWallet) {
3369
- return this.contract.getHumanOwner(agentWallet);
3370
- }
3371
- /** Number of agents registered under `owner`. */
3372
- async getAgentCount(owner) {
3373
- return BigInt(await this.contract.getAgentCount(owner));
3374
- }
3375
- /** The agent wallet at `index` in `owner`'s agent list. */
3376
- async getAgentByIndex(owner, index) {
3377
- return this.contract.getAgentByIndex(owner, index);
3378
- }
3379
- /** Full list of agent wallets registered under `humanOwner`. */
3380
- async getAgents(humanOwner) {
3381
- const result = await this.contract.getAgents(humanOwner);
3382
- return Array.from(result);
3383
- }
3384
- /**
3385
- * Paginated slice of `owner`'s agent wallets: `count` entries starting at `start`.
3386
- * The contract clamps `count` to the remaining length, so the returned array may be shorter.
3387
- */
3388
- async getAgentsPage(owner, start, count) {
3389
- const result = await this.contract.getAgentsPage(owner, start, count);
3390
- return Array.from(result);
3391
- }
3392
- /** Raw `agentWalletOwner` mapping read (agentWallet → owner). */
3393
- async agentWalletOwner(agentWallet) {
3394
- return this.contract.agentWalletOwner(agentWallet);
3395
- }
3396
- /** Raw `ownerAgents` array read (owner, index → agentWallet). */
3397
- async ownerAgents(owner, index) {
3398
- return this.contract.ownerAgents(owner, index);
3399
- }
3400
- // ── Factory agent-account helpers (AAStarAirAccountFactoryV7) ───────────────
3401
- /**
3402
- * Encode calldata for the factory's `createAgentAccount(...)`.
3403
- *
3404
- * Targets the AAStarAirAccountFactoryV7, NOT the AgentRegistry. The factory deploys the agent
3405
- * AirAccount and registers it in the bound AgentRegistry in one transaction. Submit this
3406
- * calldata to the factory address (direct tx or via a relayer).
3407
- */
3408
- encodeCreateAgentAccount(params) {
3409
- return this.factoryIface.encodeFunctionData("createAgentAccount", [
3410
- params.agentKey,
3411
- params.agentId,
3412
- params.guardian2,
3413
- params.guardian2Sig,
3414
- params.agentKeySig,
3415
- params.deadline,
3416
- params.dailyLimit
3417
- ]);
3418
- }
3419
- /**
3420
- * Encode calldata for the factory's `setAgentRegistry(_agentRegistry)` (factory-admin only).
3421
- */
3422
- encodeSetAgentRegistry(agentRegistry) {
3423
- return this.factoryIface.encodeFunctionData("setAgentRegistry", [agentRegistry]);
3424
- }
3425
- /**
3426
- * Predict the CREATE2 address of an agent account via the factory's `getAgentAddress(...)`.
3427
- *
3428
- * @param factoryAddress AAStarAirAccountFactoryV7 address.
3429
- * @param humanOwner the human guardian/owner co-owning the agent account.
3430
- * @param agentKey the agent's signing key.
3431
- * @param agentId the bytes32 agent identifier.
3432
- */
3433
- async getAgentAccountAddress(factoryAddress, humanOwner, agentKey, agentId) {
3434
- const factory = new ethers.Contract(
3435
- factoryAddress,
3436
- AIRACCOUNT_FACTORY_ABI,
3437
- this.providerOrSigner
3438
- );
3439
- return factory.getAgentAddress(humanOwner, agentKey, agentId);
3440
- }
3441
- /** Read the AgentRegistry address currently bound to the factory. */
3442
- async getFactoryAgentRegistry(factoryAddress) {
3443
- const factory = new ethers.Contract(
3444
- factoryAddress,
3445
- AIRACCOUNT_FACTORY_ABI,
3446
- this.providerOrSigner
3447
- );
3448
- return factory.agentRegistry();
3449
- }
3450
- };
3451
- var ERC8004_ABI = [
3452
- "function setAgentWallet(uint256 agentId, address agentWallet, address agentRegistry, bytes agentWalletSig) external",
3453
- "function mintAgentIdentity(address identityRegistry, string agentURI) external returns (uint256 agentId)",
3454
- "function bindERC8004AgentWallet(address identityRegistry, uint256 agentId, address agentWallet, uint256 deadline, bytes signature) external",
3455
- "function submitAgentReputation(address reputationRegistry, uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpoint, string feedbackURI, bytes32 feedbackHash) external",
3456
- "function queryAgentReputation(address reputationRegistry, uint256 agentId, address[] clientAddresses, string tag1, string tag2) external view returns (uint64 count, int128 summaryValue, uint8 summaryDecimals)",
3457
- "function agentExtension() external view returns (address)"
3458
- ];
3459
- var ERC8004_ADDRESSES = {
3460
- mainnet: {
3461
- identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
3462
- reputationRegistry: "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63",
3463
- validationRegistry: "0x8004Cc8439f36fd5F9F049D9fF86523Df6dAAB58"
3464
- },
3465
- testnet: {
3466
- identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
3467
- reputationRegistry: "0x8004B663056A597Dffe9eCcC1965A193B7388713",
3468
- validationRegistry: "0x8004Cb1BF31DAf7788923b405b754f57acEB4272"
3469
- }
3470
- };
3471
- var MAINNET_CHAIN_IDS = /* @__PURE__ */ new Set([1, 10, 137, 8453, 42161, 43114, 56, 534352, 100, 42220, 59144, 5e3, 167e3, 360]);
3472
- var TESTNET_CHAIN_IDS = /* @__PURE__ */ new Set([11155111, 11155420, 84532, 421614, 80002]);
3473
- function erc8004AddressesForChain(chainId) {
3474
- if (MAINNET_CHAIN_IDS.has(chainId)) return ERC8004_ADDRESSES.mainnet;
3475
- if (TESTNET_CHAIN_IDS.has(chainId)) return ERC8004_ADDRESSES.testnet;
3476
- throw new Error(`ERC-8004: unsupported chain ${chainId}`);
3477
- }
3478
- var ERC8004Service = class {
3479
- iface;
3480
- provider;
3481
- constructor(provider) {
3482
- this.iface = new ethers.Interface(ERC8004_ABI);
3483
- this.provider = provider;
3484
- }
3485
- // ── AAStar AgentRegistry path ─────────────────────────────────────────────
3486
- /**
3487
- * Encode calldata for `setAgentWallet`.
3488
- *
3489
- * Registers `agentWallet` in the AAStar AgentRegistry (SuperPaymaster-facing). This is the
3490
- * correct path when the goal is SuperPaymaster gasless sponsorship for the agent.
3491
- *
3492
- * **Not** a call to the official ERC-8004 IdentityRegistry. Use `encodeMintAgentIdentity`
3493
- * + `encodeBindERC8004AgentWallet` for the ERC-8004 standard path.
3494
- *
3495
- * Callable: owner EOA direct tx OR via UserOp (gasless).
3496
- */
3497
- encodeSetAgentWallet(params) {
3498
- return this.iface.encodeFunctionData("setAgentWallet", [
3499
- params.agentId,
3500
- params.agentWallet,
3501
- params.agentRegistry,
3502
- params.agentWalletSig
3503
- ]);
3504
- }
3505
- // ── Official ERC-8004 identity path ──────────────────────────────────────
3506
- /**
3507
- * Encode calldata for `mintAgentIdentity`.
3508
- *
3509
- * Mints an ERC-721 agent identity NFT in the official ERC-8004 IdentityRegistry and returns
3510
- * the new `agentId` (decoded from the tx receipt). The `identityRegistry` must be
3511
- * `erc8004AddressesForChain(chainId).identityRegistry` — the contract reverts otherwise.
3512
- *
3513
- * Callable: owner EOA direct tx OR via UserOp (gasless).
3514
- */
3515
- encodeMintAgentIdentity(params) {
3516
- return this.iface.encodeFunctionData("mintAgentIdentity", [
3517
- params.identityRegistry,
3518
- params.agentURI
3519
- ]);
3520
- }
3521
- /**
3522
- * Encode calldata for `bindERC8004AgentWallet`.
3523
- *
3524
- * Binds an execution wallet to an existing ERC-8004 agent identity NFT. Requires a
3525
- * deadline-bounded signature from the expected signer (see the IdentityRegistry contract).
3526
- * The `identityRegistry` must be the official chain-specific address.
3527
- *
3528
- * Callable: owner EOA direct tx OR via UserOp (gasless).
3529
- */
3530
- encodeBindERC8004AgentWallet(params) {
3531
- return this.iface.encodeFunctionData("bindERC8004AgentWallet", [
3532
- params.identityRegistry,
3533
- params.agentId,
3534
- params.agentWallet,
3535
- params.deadline,
3536
- params.signature
3537
- ]);
3538
- }
3539
- // ── Reputation ────────────────────────────────────────────────────────────
3540
- /**
3541
- * Encode calldata for `submitAgentReputation`.
3542
- *
3543
- * Submits a reputation feedback entry to the official ERC-8004 ReputationRegistry.
3544
- * `reputationRegistry` must be `erc8004AddressesForChain(chainId).reputationRegistry`.
3545
- *
3546
- * Callable: owner EOA direct tx OR via UserOp (gasless).
3547
- */
3548
- encodeSubmitAgentReputation(params) {
3549
- return this.iface.encodeFunctionData("submitAgentReputation", [
3550
- params.reputationRegistry,
3551
- params.agentId,
3552
- params.value,
3553
- params.valueDecimals,
3554
- params.tag1,
3555
- params.tag2,
3556
- params.endpoint,
3557
- params.feedbackURI,
3558
- params.feedbackHash
3559
- ]);
3560
- }
3561
- /**
3562
- * Query aggregated reputation for an agent from the official ERC-8004 ReputationRegistry.
3563
- * Returns `null` when no provider was supplied at construction.
3564
- */
3565
- async queryAgentReputation(accountAddress, params) {
3566
- if (!this.provider) throw new Error("ERC8004Service: provider required for on-chain reads");
3567
- const contract = new ethers.Contract(accountAddress, ERC8004_ABI, this.provider);
3568
- const [count, summaryValue, summaryDecimals] = await contract.queryAgentReputation(
3569
- params.reputationRegistry,
3570
- params.agentId,
3571
- params.clientAddresses,
3572
- params.tag1,
3573
- params.tag2
3574
- );
3575
- return { count: BigInt(count), summaryValue: BigInt(summaryValue), summaryDecimals: Number(summaryDecimals) };
3576
- }
3577
- /**
3578
- * Encode calldata for `queryAgentReputation` (for static-call or eth_call without a signer).
3579
- */
3580
- encodeQueryAgentReputation(params) {
3581
- return this.iface.encodeFunctionData("queryAgentReputation", [
3582
- params.reputationRegistry,
3583
- params.agentId,
3584
- params.clientAddresses,
3585
- params.tag1,
3586
- params.tag2
3587
- ]);
3588
- }
3589
- /**
3590
- * Read the agentExtension implementation address from a deployed AirAccount.
3591
- */
3592
- async getAgentExtensionAddress(accountAddress) {
3593
- if (!this.provider) throw new Error("ERC8004Service: provider required for on-chain reads");
3594
- const contract = new ethers.Contract(accountAddress, ERC8004_ABI, this.provider);
3595
- return contract.agentExtension();
3596
- }
3597
- };
3598
- var DEFAULT_KMS_ENDPOINT = "https://kms.aastar.io";
3599
- var KmsHttpClient = class {
3600
- endpoint;
3601
- enabled;
3602
- logger;
3603
- apiKey;
3604
- http;
3605
- constructor(options) {
3606
- this.endpoint = options.kmsEndpoint ?? DEFAULT_KMS_ENDPOINT;
3607
- this.enabled = options.kmsEnabled === true;
3608
- this.apiKey = options.kmsApiKey;
3609
- this.logger = options.logger ?? new ConsoleLogger("[KmsHttpClient]");
3610
- const headers = { "Content-Type": "application/json" };
3611
- if (this.apiKey) {
3612
- headers["x-api-key"] = this.apiKey;
3613
- }
3614
- this.http = axios.create({ baseURL: this.endpoint, headers });
3615
- }
3616
- /** Throw if KMS is not enabled — every operation must call this first. */
3617
- ensureEnabled() {
3618
- if (!this.enabled) {
3619
- throw new Error("KMS service is not enabled");
3620
- }
3621
- }
3622
- /**
3623
- * Plain JSON POST. The axios `config` arg is only forwarded when defined, so a
3624
- * config-less call results in `http.post(path, body)` (2 args) — preserving the
3625
- * exact call shape the existing unit tests assert against.
3626
- */
3627
- async post(path, body, config) {
3628
- const response = config === void 0 ? await this.http.post(path, body) : await this.http.post(path, body, config);
3629
- return response.data;
3630
- }
3631
- /** Plain JSON GET. */
3632
- async get(path, config) {
3633
- const response = config === void 0 ? await this.http.get(path) : await this.http.get(path, config);
3634
- return response.data;
3635
- }
3636
- /** POST with AWS-KMS framing (x-amz-target header) — required for wallet/signing ops. */
3637
- async amzPost(path, target, body) {
3638
- return this.post(path, body, {
3639
- headers: {
3640
- "Content-Type": "application/x-amz-json-1.1",
3641
- "x-amz-target": target
3642
- }
3643
- });
3644
- }
3645
- /** POST authenticated with a TEE-issued agent/session JWT (Authorization: Bearer). */
3646
- async postWithBearer(path, body, jwt) {
3647
- return this.post(path, body, {
3648
- headers: { authorization: `Bearer ${jwt}` }
3649
- });
3650
- }
3651
- };
3652
- var DEFAULT_RP_ID = "aastar.io";
3653
- var DEFAULT_ORIGIN = "https://aastar.io";
3654
- var DEFAULT_CREDENTIAL_ID = "dGVzdC1jcmVkZW50aWFs";
3655
- function base64UrlEncode(bytes) {
3656
- return Buffer.from(bytes).toString("base64url");
3657
- }
3658
- function base64UrlDecode(value) {
3659
- return new Uint8Array(Buffer.from(value, "base64url"));
3660
- }
3661
- function hexToBytes(hex) {
3662
- const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
3663
- if (clean.length % 2 !== 0) {
3664
- throw new Error("hexToBytes: odd-length hex string");
3665
- }
3666
- const out = new Uint8Array(clean.length / 2);
3667
- for (let i = 0; i < out.length; i++) {
3668
- out[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
3669
- }
3670
- return out;
3671
- }
3672
- var P256PasskeySigner = class {
3673
- credentialId;
3674
- privateKey;
3675
- /**
3676
- * @param privateKey raw 32-byte P-256 scalar (Uint8Array or hex, 0x optional).
3677
- * @param credentialId base64url credential id (defaults to the reference fixture).
3678
- */
3679
- constructor(privateKey, credentialId = DEFAULT_CREDENTIAL_ID) {
3680
- this.privateKey = typeof privateKey === "string" ? hexToBytes(privateKey) : privateKey;
3681
- this.credentialId = credentialId;
3682
- }
3683
- /**
3684
- * Uncompressed (0x04…, 65-byte) P-256 public key hex. Register this with the
3685
- * KMS via CreateKey `PasskeyPublicKey` (or ChangePasskey) so the TA can verify
3686
- * assertions produced by this signer.
3687
- */
3688
- get publicKeyHex() {
3689
- return "0x" + Buffer.from(p256.getPublicKey(this.privateKey, false)).toString("hex");
3690
- }
3691
- sign(message) {
3692
- return p256.sign(message, this.privateKey, { prehash: true, format: "der" });
3693
- }
3694
- };
3695
- function buildClientDataJSON(challenge, origin = DEFAULT_ORIGIN) {
3696
- const json = JSON.stringify({ type: "webauthn.get", challenge, origin });
3697
- return new TextEncoder().encode(json);
3698
- }
3699
- function buildAuthenticatorData(rpId = DEFAULT_RP_ID, signCount = 1) {
3700
- const rpIdHash = createHash("sha256").update(rpId).digest();
3701
- const out = new Uint8Array(37);
3702
- out.set(rpIdHash, 0);
3703
- out[32] = 5;
3704
- new DataView(out.buffer).setUint32(33, signCount >>> 0, false);
3705
- return out;
3706
- }
3707
- async function buildAuthenticationCredential(opts) {
3708
- const origin = opts.origin ?? DEFAULT_ORIGIN;
3709
- const rpId = opts.rpId ?? DEFAULT_RP_ID;
3710
- const signCount = opts.signCount ?? 1;
3711
- const clientDataJSON = buildClientDataJSON(opts.challenge, origin);
3712
- const authenticatorData = buildAuthenticatorData(rpId, signCount);
3713
- const clientDataHash = createHash("sha256").update(clientDataJSON).digest();
3714
- const message = new Uint8Array(authenticatorData.length + clientDataHash.length);
3715
- message.set(authenticatorData, 0);
3716
- message.set(clientDataHash, authenticatorData.length);
3717
- const signature = await opts.signer.sign(message);
3718
- return {
3719
- id: opts.signer.credentialId,
3720
- rawId: opts.signer.credentialId,
3721
- type: "public-key",
3722
- response: {
3723
- clientDataJSON: base64UrlEncode(clientDataJSON),
3724
- authenticatorData: base64UrlEncode(authenticatorData),
3725
- signature: base64UrlEncode(signature)
3726
- }
3727
- };
3728
- }
3729
- async function runWebAuthnCeremony(begin, options) {
3730
- const begun = await begin();
3731
- const challenge = begun?.Options?.challenge;
3732
- if (!begun?.ChallengeId || !challenge) {
3733
- throw new Error(
3734
- "WebAuthn ceremony: begin endpoint did not return a ChallengeId + Options.challenge"
3735
- );
3736
- }
3737
- const credential = await buildAuthenticationCredential({
3738
- challenge,
3739
- signer: options.signer,
3740
- rpId: options.rpId,
3741
- origin: options.origin,
3742
- signCount: options.signCount
3743
- });
3744
- return { ChallengeId: begun.ChallengeId, Credential: credential };
3745
- }
3746
- function beginAuthenticationChallenge(http, keyId) {
3747
- return http.post("/BeginAuthentication", { KeyId: keyId });
3748
- }
3749
- function beginGrantSessionChallenge(http, keyId) {
3750
- return http.get("/kms/begin-grant-session-auth", {
3751
- params: { keyId }
3752
- });
3753
- }
3754
- function runAuthenticationCeremony(http, keyId, signer, options) {
3755
- return runWebAuthnCeremony(() => beginAuthenticationChallenge(http, keyId), {
3756
- signer,
3757
- ...options
3758
- });
3759
- }
3760
- function runGrantSessionCeremony(http, keyId, signer, options) {
3761
- return runWebAuthnCeremony(() => beginGrantSessionChallenge(http, keyId), {
3762
- signer,
3763
- ...options
3764
- });
3765
- }
3766
- var KmsManager = class {
3767
- client;
3768
- logger;
3769
- constructor(options) {
3770
- this.client = new KmsHttpClient(options);
3771
- this.logger = this.client.logger;
3772
- }
3773
- isKmsEnabled() {
3774
- return this.client.enabled;
3775
- }
3776
- /** Shared HTTP transport — pass to KmsAgentService / KmsSessionService / etc. */
3777
- get httpClient() {
3778
- return this.client;
3779
- }
3780
- ensureEnabled() {
3781
- this.client.ensureEnabled();
3782
- }
3783
- /** POST with x-amz-target header (required for wallet/signing operations). */
3784
- async amzPost(path, target, body) {
3785
- return this.client.amzPost(path, target, body);
3786
- }
3787
- // ── Key Management ──────────────────────────────────────────────
3788
- async createKey(description, passkeyPublicKey) {
3789
- this.ensureEnabled();
3790
- return this.amzPost("/CreateKey", "TrentService.CreateKey", {
3791
- Description: description,
3792
- KeyUsage: "SIGN_VERIFY",
3793
- KeySpec: "ECC_SECG_P256K1",
3794
- Origin: "EXTERNAL_KMS",
3795
- PasskeyPublicKey: passkeyPublicKey
3796
- });
3797
- }
3798
- async getKeyStatus(keyId) {
3799
- this.ensureEnabled();
3800
- return this.client.get("/KeyStatus", {
3801
- params: { KeyId: keyId }
3802
- });
3803
- }
3804
- async describeKey(keyId) {
3805
- this.ensureEnabled();
3806
- return this.amzPost("/DescribeKey", "TrentService.DescribeKey", { KeyId: keyId });
3807
- }
3808
- /** Get a key's public key (uncompressed). Not WebAuthn-gated. */
3809
- async getPublicKey(target) {
3810
- this.ensureEnabled();
3811
- return this.amzPost("/GetPublicKey", "TrentService.GetPublicKey", target);
3812
- }
3813
- /**
3814
- * Derive an Ethereum address at a BIP-44 path (WebAuthn-gated).
3815
- * Provide a WebAuthn ceremony assertion (preferred) or a Legacy passkey assertion.
3816
- */
3817
- async deriveAddress(params) {
3818
- this.ensureEnabled();
3819
- return this.amzPost("/DeriveAddress", "TrentService.DeriveAddress", params);
3820
- }
3821
- /** List keys (paginated). Not WebAuthn-gated. */
3822
- async listKeys(params = {}) {
3823
- this.ensureEnabled();
3824
- return this.amzPost("/ListKeys", "TrentService.ListKeys", params);
3825
- }
3826
- /**
3827
- * Schedule key deletion (AWS-KMS action ScheduleKeyDeletion; WebAuthn-gated).
3828
- * RPMB-bound on the TEE — requires a passkey/WebAuthn assertion on the normal path.
3829
- */
3830
- async deleteKey(params) {
3831
- this.ensureEnabled();
3832
- return this.amzPost("/DeleteKey", "TrentService.ScheduleKeyDeletion", params);
3833
- }
3834
- /**
3835
- * Unfreeze a dormant (frozen) key (issue #42; WebAuthn-gated).
3836
- * A key auto-frozen by the dormant-key sweep rejects signing until unfrozen.
3837
- * The TEE verifies the owner via the same strict WebAuthn ceremony as
3838
- * {@link deleteKey}; ownership is checked even when the key is already active,
3839
- * so this cannot be used as an unauthenticated key-state probe. Unlike DeleteKey
3840
- * this endpoint takes no `x-amz-target` header — it authenticates via the default
3841
- * API key plus the WebAuthn assertion in the body.
3842
- */
3843
- async unfreezeKey(params) {
3844
- this.ensureEnabled();
3845
- return this.client.post("/UnfreezeKey", params);
3846
- }
3847
- /**
3848
- * Rotate the WebAuthn passkey bound to a key (WebAuthn-gated, RPMB-bound).
3849
- * `PasskeyPublicKey` is the NEW P-256 public key (0x04… 65-byte uncompressed).
3850
- */
3851
- async changePasskey(params) {
3852
- this.ensureEnabled();
3853
- return this.amzPost("/ChangePasskey", "TrentService.ChangePasskey", params);
3854
- }
3855
- /**
3856
- * Sign a message or an EIP-155 transaction (WebAuthn-gated).
3857
- * Provide exactly one of `Message` (hex) or `Transaction`. For a raw 32-byte
3858
- * digest use {@link signHash} / {@link signHashWithWebAuthn} instead.
3859
- */
3860
- async sign(params) {
3861
- this.ensureEnabled();
3862
- return this.amzPost("/Sign", "TrentService.Sign", params);
3863
- }
3864
- /**
3865
- * Poll KeyStatus until the key is ready (address derived) or timeout.
3866
- * STM32 key derivation takes 60-75 seconds on first creation.
3867
- */
3868
- async pollUntilReady(keyId, timeoutMs = 12e4, intervalMs = 3e3) {
3869
- this.ensureEnabled();
3870
- const deadline = Date.now() + timeoutMs;
3871
- while (Date.now() < deadline) {
3872
- const status = await this.getKeyStatus(keyId);
3873
- this.logger.debug(`Key ${keyId} status: ${status.Status}`);
3874
- if (status.Status === "ready") {
3875
- return status;
3876
- }
3877
- if (status.Status === "error") {
3878
- throw new Error(`KMS key derivation failed: ${status.Error ?? "unknown error"}`);
3879
- }
3880
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
3881
- }
3882
- throw new Error(`KMS key derivation timed out after ${timeoutMs}ms`);
3883
- }
3884
- // ── Signing ─────────────────────────────────────────────────────
3885
- /**
3886
- * Sign a hash using Legacy Passkey assertion (reusable for BLS dual-signing).
3887
- */
3888
- async signHash(hash, assertion, target) {
3889
- this.ensureEnabled();
3890
- const formattedHash = hash.startsWith("0x") ? hash : `0x${hash}`;
3891
- const body = {
3892
- Hash: formattedHash,
3893
- Passkey: assertion
3894
- };
3895
- if (target.Address) {
3896
- body.Address = target.Address;
3897
- }
3898
- if (target.KeyId) {
3899
- body.KeyId = target.KeyId;
3900
- }
3901
- return this.amzPost("/SignHash", "TrentService.SignHash", body);
3902
- }
3903
- /**
3904
- * Sign a hash using a WebAuthn ceremony assertion (one-time use).
3905
- */
3906
- async signHashWithWebAuthn(hash, challengeId, credential, target) {
3907
- this.ensureEnabled();
3908
- const formattedHash = hash.startsWith("0x") ? hash : `0x${hash}`;
3909
- const body = {
3910
- Hash: formattedHash,
3911
- WebAuthn: { ChallengeId: challengeId, Credential: credential }
3912
- };
3913
- if (target.Address) {
3914
- body.Address = target.Address;
3915
- }
3916
- if (target.KeyId) {
3917
- body.KeyId = target.KeyId;
3918
- }
3919
- return this.amzPost("/SignHash", "TrentService.SignHash", body);
3920
- }
3921
- // ── Sign Typed Data (v0.19.0+) ─────────────────────────────────
3922
- /**
3923
- * Sign arbitrary EIP-712 typed data via `POST /kms/SignTypedData` (v0.20.0).
3924
- *
3925
- * The KMS hashes the typed data host-side, so the FULL EIP-712 structure
3926
- * (domain / primaryType / types / message) is sent — not a pre-hashed
3927
- * domainSeparator/structHash. The `webAuthnAssertion` challenge comes from a
3928
- * generic {@link beginAuthentication} ceremony (purpose="authentication").
3929
- *
3930
- * Alternatively, agents authenticate with a Bearer JWT — see KmsAgentService.
3931
- */
3932
- async signTypedDataWithWebAuthn(params) {
3933
- this.ensureEnabled();
3934
- return this.client.post("/kms/SignTypedData", params);
3935
- }
3936
- // ── Grant Session Off-chain Signing (v0.19.0+) ─────────────────
3937
- /**
3938
- * Begin a grant-session WebAuthn challenge.
3939
- * The returned challengeId can ONLY be used with sign-grant-session, not sign-typed-data.
3940
- */
3941
- async beginGrantSessionAuth(params) {
3942
- this.ensureEnabled();
3943
- return this.client.get("/kms/begin-grant-session-auth", {
3944
- params: { keyId: params.keyId }
3945
- });
3946
- }
3947
- /**
3948
- * Sign a GRANT_SESSION_V2 hash off-chain inside the TEE (secp256k1 session key).
3949
- * Returns a 65-byte signature (R||S||V, V=27/28) for use in grantSessionWithSig().
3950
- */
3951
- async signGrantSession(params) {
3952
- this.ensureEnabled();
3953
- return this.client.post("/kms/sign-grant-session", params);
3954
- }
3955
- /**
3956
- * Sign a GRANT_P256_SESSION_V2 hash off-chain inside the TEE (P256 session key).
3957
- * Returns a 65-byte signature for use in grantP256SessionWithSig().
3958
- */
3959
- async signP256GrantSession(params) {
3960
- this.ensureEnabled();
3961
- return this.client.post("/kms/sign-p256-grant-session", params);
3962
- }
3963
- // ── Challenge-binding ceremonies (#49 / Beta3) ──────────────────
3964
- //
3965
- // These run the full WebAuthn challenge-binding ceremony in one call:
3966
- // fetch the TA one-time nonce, embed it in clientDataJSON, build + sign the
3967
- // assertion, then invoke the signing endpoint with the resulting
3968
- // `WebAuthn` / `webAuthnAssertion`. They share the
3969
- // {@link runAuthenticationCeremony} / {@link runGrantSessionCeremony} helper,
3970
- // so every path produces an identical, replay-protected assertion structure.
3971
- /**
3972
- * Run a generic authentication ceremony (purpose="authentication") bound to a
3973
- * fresh TA challenge. The returned assertion is valid for DeriveAddress / Sign
3974
- * / SignHash / SignTypedData / agent-key / p256-session signing.
3975
- */
3976
- async runAuthenticationCeremony(keyId, signer, options) {
3977
- this.ensureEnabled();
3978
- return runAuthenticationCeremony(this.client, keyId, signer, options);
3979
- }
3980
- /**
3981
- * Run a grant-session ceremony (purpose="grant-session") bound to a fresh TA
3982
- * challenge — required by {@link signGrantSession} / {@link signP256GrantSession}
3983
- * (the generic 'authentication' challenge is rejected there for replay safety).
3984
- */
3985
- async runGrantSessionCeremony(keyId, signer, options) {
3986
- this.ensureEnabled();
3987
- return runGrantSessionCeremony(this.client, keyId, signer, options);
3988
- }
3989
- /** Derive an address, running the challenge-binding ceremony internally. */
3990
- async deriveAddressWithCeremony(params, signer, options) {
3991
- this.ensureEnabled();
3992
- const WebAuthn = await this.runAuthenticationCeremony(params.KeyId, signer, options);
3993
- return this.deriveAddress({ ...params, WebAuthn });
3994
- }
3995
- /**
3996
- * Sign a message or EIP-155 transaction, running the challenge-binding ceremony
3997
- * internally. `params.KeyId` is required (it identifies the wallet to challenge).
3998
- */
3999
- async signWithCeremony(params, signer, options) {
4000
- this.ensureEnabled();
4001
- const WebAuthn = await this.runAuthenticationCeremony(params.KeyId, signer, options);
4002
- return this.sign({ ...params, WebAuthn });
4003
- }
4004
- /** Sign a 32-byte digest, running the challenge-binding ceremony internally. */
4005
- async signHashWithCeremony(hash, target, signer, options) {
4006
- this.ensureEnabled();
4007
- const assertion = await this.runAuthenticationCeremony(target.KeyId, signer, options);
4008
- return this.signHashWithWebAuthn(hash, assertion.ChallengeId, assertion.Credential, target);
4009
- }
4010
- /** Sign EIP-712 typed data, running the challenge-binding ceremony internally. */
4011
- async signTypedDataWithCeremony(params, signer, options) {
4012
- this.ensureEnabled();
4013
- const webAuthnAssertion = await this.runAuthenticationCeremony(params.keyId, signer, options);
4014
- return this.signTypedDataWithWebAuthn({ ...params, webAuthnAssertion });
4015
- }
4016
- /**
4017
- * Sign a GRANT_SESSION_V2 hash, running the grant-session ceremony internally
4018
- * (uses the purpose-bound `begin-grant-session-auth` challenge).
4019
- */
4020
- async signGrantSessionWithCeremony(params, signer, options) {
4021
- this.ensureEnabled();
4022
- const webAuthnAssertion = await this.runGrantSessionCeremony(params.keyId, signer, options);
4023
- return this.signGrantSession({ ...params, webAuthnAssertion });
4024
- }
4025
- /**
4026
- * Sign a GRANT_P256_SESSION_V2 hash, running the grant-session ceremony
4027
- * internally (uses the purpose-bound `begin-grant-session-auth` challenge).
4028
- */
4029
- async signP256GrantSessionWithCeremony(params, signer, options) {
4030
- this.ensureEnabled();
4031
- const webAuthnAssertion = await this.runGrantSessionCeremony(params.keyId, signer, options);
4032
- return this.signP256GrantSession({ ...params, webAuthnAssertion });
4033
- }
4034
- // ── WebAuthn Ceremonies ─────────────────────────────────────────
4035
- async beginRegistration(params) {
4036
- this.ensureEnabled();
4037
- return this.client.post("/BeginRegistration", params);
4038
- }
4039
- async completeRegistration(params) {
4040
- this.ensureEnabled();
4041
- return this.client.post("/CompleteRegistration", params);
4042
- }
4043
- async beginAuthentication(params) {
4044
- this.ensureEnabled();
4045
- return this.client.post("/BeginAuthentication", params);
4046
- }
4047
- /**
4048
- * Begin a generic WebAuthn authentication ceremony for a key, returning a
4049
- * challenge usable for SignHash / SignTypedData (purpose="authentication").
4050
- *
4051
- * NOTE: there is no dedicated `begin-webauthn-auth` endpoint — this delegates
4052
- * to `POST /BeginAuthentication`. (Grant-session signing needs a purpose-bound
4053
- * challenge from {@link beginGrantSessionAuth} instead.)
4054
- */
4055
- async beginWebAuthnAuth(keyId) {
4056
- this.ensureEnabled();
4057
- return this.client.post("/BeginAuthentication", { KeyId: keyId });
4058
- }
4059
- // ── Factory ─────────────────────────────────────────────────────
4060
- createKmsSigner(keyId, address, assertionProvider, provider) {
4061
- this.ensureEnabled();
4062
- return new KmsSigner(keyId, address, this, assertionProvider, provider);
4063
- }
4064
- };
4065
- var KmsSigner = class _KmsSigner extends ethers.AbstractSigner {
4066
- constructor(keyId, _address, kmsManager, assertionProvider, provider) {
4067
- super(provider);
4068
- this.keyId = keyId;
4069
- this._address = _address;
4070
- this.kmsManager = kmsManager;
4071
- this.assertionProvider = assertionProvider;
4072
- }
4073
- async getAddress() {
4074
- return this._address;
4075
- }
4076
- async signMessage(message) {
4077
- const messageBytes = typeof message === "string" ? ethers.toUtf8Bytes(message) : message;
4078
- const messageHash = ethers.hashMessage(messageBytes);
4079
- const assertion = await this.assertionProvider();
4080
- const signResponse = await this.kmsManager.signHash(messageHash, assertion, {
4081
- Address: this._address
4082
- });
4083
- return "0x" + signResponse.Signature;
4084
- }
4085
- async signTransaction(tx) {
4086
- if (!this.provider) {
4087
- throw new Error("Provider is required for signing transactions");
4088
- }
4089
- const populated = await this.populateTransaction(tx);
4090
- const unsignedTx = ethers.Transaction.from(populated);
4091
- const txHash = unsignedTx.hash;
4092
- if (!txHash) {
4093
- throw new Error("Failed to compute transaction hash");
4094
- }
4095
- const assertion = await this.assertionProvider();
4096
- const signResponse = await this.kmsManager.signHash(txHash, assertion, {
4097
- Address: this._address
4098
- });
4099
- const sig = ethers.Signature.from("0x" + signResponse.Signature);
4100
- unsignedTx.signature = sig;
4101
- return unsignedTx.serialized;
4102
- }
4103
- async signTypedData(domain, types, value) {
4104
- const hash = ethers.TypedDataEncoder.hash(domain, types, value);
4105
- const assertion = await this.assertionProvider();
4106
- const signResponse = await this.kmsManager.signHash(hash, assertion, {
4107
- Address: this._address
4108
- });
4109
- return "0x" + signResponse.Signature;
4110
- }
4111
- connect(provider) {
4112
- return new _KmsSigner(
4113
- this.keyId,
4114
- this._address,
4115
- this.kmsManager,
4116
- this.assertionProvider,
4117
- provider
4118
- );
4119
- }
4120
- };
4121
- var KmsAgentService = class {
4122
- constructor(http) {
4123
- this.http = http;
4124
- }
4125
- /**
4126
- * Mint a new agent key under an existing human key (WebAuthn-gated).
4127
- *
4128
- * The WebAuthn challenge is obtained from a generic
4129
- * {@link KmsManager.beginAuthentication} ceremony (purpose="authentication");
4130
- * the caller supplies the resulting assertion in the request.
4131
- */
4132
- async createAgentKey(params) {
4133
- this.http.ensureEnabled();
4134
- return this.http.post("/kms/create-agent-key", params);
4135
- }
4136
- /**
4137
- * Sign a userOpHash with an agent key, authenticated by the agent's TEE-JWT
4138
- * credential (`jwt`, the `agentCredential` from {@link createAgentKey}).
4139
- * Returns the 106-byte packed signature for ERC-4337 sponsorship.
4140
- */
4141
- async signAgent(params, jwt) {
4142
- this.http.ensureEnabled();
4143
- return this.http.postWithBearer("/kms/sign-agent", params, jwt);
4144
- }
4145
- /**
4146
- * Refresh (re-mint) an agent credential before it expires. Authenticated with
4147
- * the existing credential (`jwt`, Bearer) plus a human WebAuthn / passkey
4148
- * assertion in the request.
4149
- */
4150
- async refreshAgentCredential(params, jwt) {
4151
- this.http.ensureEnabled();
4152
- return this.http.postWithBearer(
4153
- "/kms/refresh-agent-credential",
4154
- params,
4155
- jwt
4156
- );
4157
- }
4158
- /**
4159
- * Revoke an agent's credential (WebAuthn-gated).
4160
- *
4161
- * The WebAuthn challenge is obtained from a generic
4162
- * {@link KmsManager.beginAuthentication} ceremony (purpose="authentication");
4163
- * the caller supplies the resulting assertion in the request.
4164
- */
4165
- async revokeAgentCredential(params) {
4166
- this.http.ensureEnabled();
4167
- return this.http.post(
4168
- "/kms/revoke-agent-credential",
4169
- params
4170
- );
4171
- }
4172
- // ── Challenge-binding ceremony variants (#49 / Beta3) ────────────
4173
- //
4174
- // All agent-key WebAuthn gates use the generic purpose="authentication"
4175
- // challenge bound to the HUMAN key. These helpers run the full ceremony
4176
- // (begin → clientDataJSON → assertion) via the shared
4177
- // {@link runAuthenticationCeremony} helper, then invoke the endpoint.
4178
- /** Mint an agent key, running the challenge-binding ceremony internally. */
4179
- async createAgentKeyWithCeremony(params, signer, options) {
4180
- this.http.ensureEnabled();
4181
- const webAuthnAssertion = await runAuthenticationCeremony(
4182
- this.http,
4183
- params.humanKeyId,
4184
- signer,
4185
- options
4186
- );
4187
- return this.createAgentKey({ ...params, webAuthnAssertion });
4188
- }
4189
- /**
4190
- * Refresh an agent credential, running the challenge-binding ceremony
4191
- * internally. `humanKeyId` is the owning human key challenged by the ceremony
4192
- * (distinct from the agent `keyId` in `params`); `jwt` is the existing credential.
4193
- */
4194
- async refreshAgentCredentialWithCeremony(params, humanKeyId, jwt, signer, options) {
4195
- this.http.ensureEnabled();
4196
- const webAuthnAssertion = await runAuthenticationCeremony(this.http, humanKeyId, signer, options);
4197
- return this.refreshAgentCredential({ ...params, webAuthnAssertion }, jwt);
4198
- }
4199
- /**
4200
- * Revoke an agent credential, running the challenge-binding ceremony internally.
4201
- * `humanKeyId` is the owning human key challenged by the ceremony (distinct from
4202
- * the agent `keyId` in `params`).
4203
- */
4204
- async revokeAgentCredentialWithCeremony(params, humanKeyId, signer, options) {
4205
- this.http.ensureEnabled();
4206
- const webAuthnAssertion = await runAuthenticationCeremony(this.http, humanKeyId, signer, options);
4207
- return this.revokeAgentCredential({ ...params, webAuthnAssertion });
4208
- }
4209
- };
4210
- var KmsSessionService = class {
4211
- constructor(http) {
4212
- this.http = http;
4213
- }
4214
- /**
4215
- * Create a P-256 session key under a human key (WebAuthn-gated).
4216
- *
4217
- * `POST /kms/create-p256-session-key`. The `webAuthnAssertion` challenge comes
4218
- * from a generic {@link KmsManager.beginAuthentication} ceremony supplied by
4219
- * the caller. Returns the session key's public key plus an `agentCredential`
4220
- * JWT used to authenticate subsequent {@link signP256UserOp} calls.
4221
- */
4222
- async createP256SessionKey(params) {
4223
- this.http.ensureEnabled();
4224
- return this.http.post(
4225
- "/kms/create-p256-session-key",
4226
- params
4227
- );
4228
- }
4229
- /**
4230
- * Sign an ERC-4337 UserOp hash with a P-256 session key (Bearer JWT auth).
4231
- *
4232
- * `POST /kms/sign-p256-user-op`, authenticated with the `agentCredential` JWT
4233
- * returned by {@link createP256SessionKey}. Returns the 149-byte P256
4234
- * session-key wire-format signature.
4235
- */
4236
- async signP256UserOp(params, jwt) {
4237
- this.http.ensureEnabled();
4238
- return this.http.postWithBearer(
4239
- "/kms/sign-p256-user-op",
4240
- params,
4241
- jwt
4242
- );
4243
- }
4244
- /**
4245
- * Revoke a P-256 session key (WebAuthn-gated, idempotent).
4246
- *
4247
- * `POST /kms/revoke-p256-session-key`. The `webAuthnAssertion` challenge comes
4248
- * from a generic {@link KmsManager.beginAuthentication} ceremony supplied by
4249
- * the caller. Idempotent: revoking an already-revoked key still resolves.
4250
- */
4251
- async revokeP256SessionKey(params) {
4252
- this.http.ensureEnabled();
4253
- return this.http.post(
4254
- "/kms/revoke-p256-session-key",
4255
- params
4256
- );
4257
- }
4258
- // ── Challenge-binding ceremony variants (#49 / Beta3) ────────────
4259
- //
4260
- // Create + revoke gate on the generic purpose="authentication" challenge bound
4261
- // to the HUMAN key. These helpers run the full ceremony (begin → clientDataJSON
4262
- // → assertion) via the shared {@link runAuthenticationCeremony} helper.
4263
- /** Create a P-256 session key, running the challenge-binding ceremony internally. */
4264
- async createP256SessionKeyWithCeremony(params, signer, options) {
4265
- this.http.ensureEnabled();
4266
- const webAuthnAssertion = await runAuthenticationCeremony(
4267
- this.http,
4268
- params.humanKeyId,
4269
- signer,
4270
- options
4271
- );
4272
- return this.createP256SessionKey({ ...params, webAuthnAssertion });
4273
- }
4274
- /**
4275
- * Revoke a P-256 session key, running the challenge-binding ceremony internally.
4276
- * `humanKeyId` is the owning human key challenged by the ceremony (distinct from
4277
- * the session `keyId` in `params`).
4278
- */
4279
- async revokeP256SessionKeyWithCeremony(params, humanKeyId, signer, options) {
4280
- this.http.ensureEnabled();
4281
- const webAuthnAssertion = await runAuthenticationCeremony(this.http, humanKeyId, signer, options);
4282
- return this.revokeP256SessionKey({ ...params, webAuthnAssertion });
4283
- }
4284
- };
4285
- var KmsPaymentSigner = class {
4286
- constructor(http) {
4287
- this.http = http;
4288
- }
4289
- /**
4290
- * Dispatch a payment-signing request with the chosen auth mode.
4291
- * JWT auth uses `postWithBearer`; WebAuthn auth merges the assertion into the body.
4292
- */
4293
- async signWithAuth(path, body, auth) {
4294
- if ("jwt" in auth) {
4295
- return this.http.postWithBearer(path, body, auth.jwt);
4296
- }
4297
- return this.http.post(path, {
4298
- ...body,
4299
- webAuthnAssertion: auth.webAuthnAssertion
4300
- });
4301
- }
4302
- /**
4303
- * Sign a MicroPaymentChannel voucher (cumulative-amount EIP-712 message)
4304
- * via `POST /kms/SignMicropaymentVoucher`.
4305
- */
4306
- async signMicropaymentVoucher(params, auth) {
4307
- this.http.ensureEnabled();
4308
- return this.signWithAuth("/kms/SignMicropaymentVoucher", { ...params }, auth);
4309
- }
4310
- /**
4311
- * Sign an EIP-3009 TransferWithAuthorization for a GToken transfer
4312
- * via `POST /kms/SignGTokenAuthorization`. `from` MUST equal the derived address.
4313
- */
4314
- async signGTokenAuthorization(params, auth) {
4315
- this.http.ensureEnabled();
4316
- return this.signWithAuth("/kms/SignGTokenAuthorization", { ...params }, auth);
4317
- }
4318
- /**
4319
- * Sign an x402 payment authorization via `POST /kms/SignX402Payment`.
4320
- */
4321
- async signX402Payment(params, auth) {
4322
- this.http.ensureEnabled();
4323
- return this.signWithAuth("/kms/SignX402Payment", { ...params }, auth);
4324
- }
4325
- };
4326
- var KmsMonitorService = class {
4327
- constructor(http) {
4328
- this.http = http;
4329
- }
4330
- /**
4331
- * Liveness probe (`GET /health`, no auth). Does NOT require the KMS feature
4332
- * flag to be enabled.
4333
- */
4334
- async health() {
4335
- return this.http.get("/health");
4336
- }
4337
- /**
4338
- * Version / capability descriptor (`GET /version`, no auth). Does NOT require
4339
- * the KMS feature flag to be enabled.
4340
- */
4341
- async version() {
4342
- return this.http.get("/version");
4343
- }
4344
- /**
4345
- * Request-queue health and circuit-breaker state (`GET /QueueStatus`).
4346
- */
4347
- async queueStatus() {
4348
- this.http.ensureEnabled();
4349
- return this.http.get("/QueueStatus");
4350
- }
4351
- /**
4352
- * RPMB anti-rollback monotonic counter (`GET /RollbackCounter`, diagnostic,
4353
- * v0.20.0).
4354
- */
4355
- async rollbackCounter() {
4356
- this.http.ensureEnabled();
4357
- return this.http.get("/RollbackCounter");
4358
- }
4359
- /**
4360
- * Machine-readable runtime statistics (`GET /stats`, v0.20.0) — wallets, tx,
4361
- * queue, warnings.
4362
- */
4363
- async stats() {
4364
- this.http.ensureEnabled();
4365
- return this.http.get("/stats");
4366
- }
4367
- /**
4368
- * TEE remote-attestation evidence bound to a caller nonce (`GET /attestation`,
4369
- * #37). Public (no auth) — pass a fresh random `nonce` (hex, ≤64 bytes) to bind
4370
- * the evidence + defeat replay, then verify the returned signed measurement.
4371
- */
4372
- async getAttestation(nonce) {
4373
- return this.http.get("/attestation", { params: { nonce } });
4374
- }
4375
- /**
4376
- * Ed25519-signed measurement manifest, version → ta_measurement
4377
- * (`GET /.well-known/attestation-measurements.json`, #12). Public.
4378
- */
4379
- async getAttestationMeasurements() {
4380
- return this.http.get("/.well-known/attestation-measurements.json");
4381
- }
4382
- /**
4383
- * Sigsum transparency proof sidecar for the measurement manifest
4384
- * (`GET /.well-known/attestation-measurements-proof.json`, #87). Public.
4385
- */
4386
- async getAttestationMeasurementsProof() {
4387
- return this.http.get("/.well-known/attestation-measurements-proof.json");
4388
- }
4389
- /**
4390
- * WARNING — DESTRUCTIVE, IRREVERSIBLE. Force-purges a key from both the TEE
4391
- * and the SQLite store with NO passkey/WebAuthn check (`POST /admin/purge-key`,
4392
- * v0.20.0). Operator-only: authorised solely by the `KMS_ADMIN_TOKEN` operator
4393
- * secret sent as `Authorization: Bearer <adminToken>`. There is no recovery
4394
- * once a key is purged.
4395
- *
4396
- * @internal Operator/break-glass tooling only — not part of the general SDK surface.
4397
- * The endpoint is gated server-side and intentionally omitted from the public KMS
4398
- * docs; do not expose it in application-facing flows.
4399
- */
4400
- async adminPurgeKey(params, adminToken) {
4401
- this.http.ensureEnabled();
4402
- return this.http.postWithBearer("/admin/purge-key", params, adminToken);
4403
- }
4404
- };
4405
- var MemoryStorage = class {
4406
- accounts = [];
4407
- transfers = [];
4408
- paymasters = /* @__PURE__ */ new Map();
4409
- blsConfig = null;
4410
- // ── Accounts ─────────────────────────────────────────────────
4411
- async getAccounts() {
4412
- return [...this.accounts];
4413
- }
4414
- async saveAccount(account) {
4415
- this.accounts.push({ ...account });
4416
- }
4417
- async findAccountByUserId(userId) {
4418
- return this.accounts.find((a) => a.userId === userId) ?? null;
4419
- }
4420
- async updateAccount(userId, updates) {
4421
- const index = this.accounts.findIndex((a) => a.userId === userId);
4422
- if (index >= 0) {
4423
- this.accounts[index] = { ...this.accounts[index], ...updates };
4424
- }
4425
- }
4426
- // ── Transfers ────────────────────────────────────────────────
4427
- async saveTransfer(transfer) {
4428
- this.transfers.push({ ...transfer });
4429
- }
4430
- async findTransfersByUserId(userId) {
4431
- return this.transfers.filter((t) => t.userId === userId);
4432
- }
4433
- async findTransferById(id) {
4434
- return this.transfers.find((t) => t.id === id) ?? null;
4435
- }
4436
- async updateTransfer(id, updates) {
4437
- const index = this.transfers.findIndex((t) => t.id === id);
4438
- if (index >= 0) {
4439
- this.transfers[index] = { ...this.transfers[index], ...updates };
4440
- }
4441
- }
4442
- // ── Paymasters ───────────────────────────────────────────────
4443
- async getPaymasters(userId) {
4444
- return this.paymasters.get(userId) ?? [];
4445
- }
4446
- async savePaymaster(userId, paymaster) {
4447
- const list = this.paymasters.get(userId) ?? [];
4448
- const existingIndex = list.findIndex((p) => p.name === paymaster.name);
4449
- if (existingIndex >= 0) {
4450
- list[existingIndex] = { ...paymaster };
4451
- } else {
4452
- list.push({ ...paymaster });
4453
- }
4454
- this.paymasters.set(userId, list);
4455
- }
4456
- async removePaymaster(userId, name) {
4457
- const list = this.paymasters.get(userId) ?? [];
4458
- const filtered = list.filter((p) => p.name !== name);
4459
- if (filtered.length < list.length) {
4460
- this.paymasters.set(userId, filtered);
4461
- return true;
4462
- }
4463
- return false;
4464
- }
4465
- // ── BLS Config ───────────────────────────────────────────────
4466
- async getBlsConfig() {
4467
- return this.blsConfig;
4468
- }
4469
- async updateSignerNodesCache(nodes) {
4470
- this.blsConfig = {
4471
- ...this.blsConfig,
4472
- signerNodes: {
4473
- nodes
4474
- }
4475
- };
4476
- }
4477
- };
4478
- var LocalWalletSigner = class {
4479
- wallet;
4480
- constructor(privateKey, provider) {
4481
- this.wallet = new ethers.Wallet(privateKey, provider);
4482
- }
4483
- async getAddress(_userId) {
4484
- return this.wallet.address;
4485
- }
4486
- async getSigner(_userId, _ctx) {
4487
- return this.wallet;
4488
- }
4489
- async ensureSigner(_userId) {
4490
- return { signer: this.wallet, address: this.wallet.address };
4491
- }
4492
- };
4493
- /*! Bundled license information:
4494
-
4495
- @noble/curves/nist.js:
4496
- (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
4497
- */
4498
-
4499
- export { ACCOUNT_ABI, AGENT_SESSION_KEY_VALIDATOR_ABI, AIRACCOUNT_ABI, AIRACCOUNT_ADDRESSES, AIRACCOUNT_FACTORY_ABI, AIR_ACCOUNT_COMPOSITE_VALIDATOR_ABI, AIR_ACCOUNT_DELEGATE_ABI, AIR_ACCOUNT_DELEGATE_ADDRESS, ALG_ID, AccountManager, AgentRegistryService, BLSSignatureService, CALLDATA_PARSER_REGISTRY_ABI, ConsoleLogger, DEFAULT_CREDENTIAL_ID, DEFAULT_KMS_ENDPOINT, DEFAULT_ORIGIN, DEFAULT_RP_ID, DvtPendingConfirmationError, EIP7702DelegateService, ENTRYPOINT_ABI_V6, ENTRYPOINT_ABI_V7_V8, ENTRYPOINT_ADDRESSES, ERC20_ABI, ERC8004Service, ERC8004_ADDRESSES, EXECUTE_BATCH_SELECTOR, EXECUTE_SELECTOR, EXECUTE_USER_OP_SELECTOR, EntryPointVersion, EthereumProvider, FACTORY_ABI_V6, FACTORY_ABI_V7_V8, FORCE_EXIT_MODULE_ABI, ForceExitService, GLOBAL_GUARD_ABI, GuardChecker, GuardStateReader, KmsAgentService, KmsHttpClient, KmsManager, KmsMonitorService, KmsPaymentSigner, KmsSessionService, KmsSigner, L2_TYPE, LocalWalletSigner, MAX_GUARDIANS, MODULE_TYPE, MemoryStorage, ModuleManager, P256PasskeySigner, PaymasterManager, PaymasterPriceStalenessError, RECOVERY_THRESHOLD, RECOVERY_TIMELOCK_SECONDS, RecoveryService, SESSION_KEY_VALIDATOR_ABI, SessionKeyService, SilentLogger, TIER_GUARD_HOOK_ABI, TokenService, TransferManager, VALIDATOR_ABI, WEIGHT_CHANGE_EXPIRY_SECONDS, WEIGHT_CHANGE_THRESHOLD, WEIGHT_CHANGE_TIMELOCK_SECONDS, WalletManager, WeightedSignatureService, YAAAServerClient, base64UrlDecode, base64UrlEncode, beginAuthenticationChallenge, beginGrantSessionChallenge, buildAuthenticationCredential, buildAuthenticatorData, buildClientDataJSON, buildInstallModuleHash, buildUninstallModuleHash, computeOapdSalt, erc8004AddressesForChain, getOapdAddress, getOapdAddressWithChainId, isExecuteUserOpWrapped, isOapdDeployed, isPendingConfirmation, packP256SessionSignature, packSecp256k1SessionSignature, runAuthenticationCeremony, runGrantSessionCeremony, runWebAuthnCeremony, sepoliaV07Config, validateConfig, wrapExecuteUserOp };
4500
4
  //# sourceMappingURL=airaccount.js.map
4501
5
  //# sourceMappingURL=airaccount.js.map