@prism-ing/wallet 0.1.0

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 (113) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +21 -0
  3. package/README.md +596 -0
  4. package/SPEC.md +192 -0
  5. package/dist/backends/squads-recovery-backend.d.ts +59 -0
  6. package/dist/backends/squads-recovery-backend.d.ts.map +1 -0
  7. package/dist/backends/squads-recovery-backend.js +81 -0
  8. package/dist/backends/squads-recovery-backend.js.map +1 -0
  9. package/dist/backends/squads-types.d.ts +74 -0
  10. package/dist/backends/squads-types.d.ts.map +1 -0
  11. package/dist/backends/squads-types.js +22 -0
  12. package/dist/backends/squads-types.js.map +1 -0
  13. package/dist/backends/zerodev-policy-mapper.d.ts +41 -0
  14. package/dist/backends/zerodev-policy-mapper.d.ts.map +1 -0
  15. package/dist/backends/zerodev-policy-mapper.js +127 -0
  16. package/dist/backends/zerodev-policy-mapper.js.map +1 -0
  17. package/dist/backends/zerodev-session-backend.d.ts +43 -0
  18. package/dist/backends/zerodev-session-backend.d.ts.map +1 -0
  19. package/dist/backends/zerodev-session-backend.js +63 -0
  20. package/dist/backends/zerodev-session-backend.js.map +1 -0
  21. package/dist/backends/zerodev-types.d.ts +104 -0
  22. package/dist/backends/zerodev-types.d.ts.map +1 -0
  23. package/dist/backends/zerodev-types.js +13 -0
  24. package/dist/backends/zerodev-types.js.map +1 -0
  25. package/dist/create-wallet.d.ts +89 -0
  26. package/dist/create-wallet.d.ts.map +1 -0
  27. package/dist/create-wallet.js +235 -0
  28. package/dist/create-wallet.js.map +1 -0
  29. package/dist/cross-chain.d.ts +64 -0
  30. package/dist/cross-chain.d.ts.map +1 -0
  31. package/dist/cross-chain.js +200 -0
  32. package/dist/cross-chain.js.map +1 -0
  33. package/dist/errors.d.ts +115 -0
  34. package/dist/errors.d.ts.map +1 -0
  35. package/dist/errors.js +97 -0
  36. package/dist/errors.js.map +1 -0
  37. package/dist/index.d.ts +55 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +52 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/internal/base58.d.ts +8 -0
  42. package/dist/internal/base58.d.ts.map +1 -0
  43. package/dist/internal/base58.js +34 -0
  44. package/dist/internal/base58.js.map +1 -0
  45. package/dist/internal/eip712.d.ts +41 -0
  46. package/dist/internal/eip712.d.ts.map +1 -0
  47. package/dist/internal/eip712.js +182 -0
  48. package/dist/internal/eip712.js.map +1 -0
  49. package/dist/internal/file-spend-persistence.d.ts +9 -0
  50. package/dist/internal/file-spend-persistence.d.ts.map +1 -0
  51. package/dist/internal/file-spend-persistence.js +58 -0
  52. package/dist/internal/file-spend-persistence.js.map +1 -0
  53. package/dist/internal/onebalance-client.d.ts +59 -0
  54. package/dist/internal/onebalance-client.d.ts.map +1 -0
  55. package/dist/internal/onebalance-client.js +2 -0
  56. package/dist/internal/onebalance-client.js.map +1 -0
  57. package/dist/internal/onebalance-http-client.d.ts +25 -0
  58. package/dist/internal/onebalance-http-client.d.ts.map +1 -0
  59. package/dist/internal/onebalance-http-client.js +161 -0
  60. package/dist/internal/onebalance-http-client.js.map +1 -0
  61. package/dist/internal/onebalance-types.d.ts +201 -0
  62. package/dist/internal/onebalance-types.d.ts.map +1 -0
  63. package/dist/internal/onebalance-types.js +39 -0
  64. package/dist/internal/onebalance-types.js.map +1 -0
  65. package/dist/internal/platform.d.ts +14 -0
  66. package/dist/internal/platform.d.ts.map +1 -0
  67. package/dist/internal/platform.js +22 -0
  68. package/dist/internal/platform.js.map +1 -0
  69. package/dist/internal/quote-verifier.d.ts +71 -0
  70. package/dist/internal/quote-verifier.d.ts.map +1 -0
  71. package/dist/internal/quote-verifier.js +172 -0
  72. package/dist/internal/quote-verifier.js.map +1 -0
  73. package/dist/internal/recovery-manager.d.ts +29 -0
  74. package/dist/internal/recovery-manager.d.ts.map +1 -0
  75. package/dist/internal/recovery-manager.js +161 -0
  76. package/dist/internal/recovery-manager.js.map +1 -0
  77. package/dist/result.d.ts +132 -0
  78. package/dist/result.d.ts.map +1 -0
  79. package/dist/result.js +114 -0
  80. package/dist/result.js.map +1 -0
  81. package/dist/schemas.d.ts +184 -0
  82. package/dist/schemas.d.ts.map +1 -0
  83. package/dist/schemas.js +76 -0
  84. package/dist/schemas.js.map +1 -0
  85. package/dist/session-keys.d.ts +53 -0
  86. package/dist/session-keys.d.ts.map +1 -0
  87. package/dist/session-keys.js +345 -0
  88. package/dist/session-keys.js.map +1 -0
  89. package/dist/signers/node-signing-backend.d.ts +11 -0
  90. package/dist/signers/node-signing-backend.d.ts.map +1 -0
  91. package/dist/signers/node-signing-backend.js +120 -0
  92. package/dist/signers/node-signing-backend.js.map +1 -0
  93. package/dist/signers/ows-adapter.d.ts +70 -0
  94. package/dist/signers/ows-adapter.d.ts.map +1 -0
  95. package/dist/signers/ows-adapter.js +53 -0
  96. package/dist/signers/ows-adapter.js.map +1 -0
  97. package/dist/signers/ows-signing-backend.d.ts +25 -0
  98. package/dist/signers/ows-signing-backend.d.ts.map +1 -0
  99. package/dist/signers/ows-signing-backend.js +192 -0
  100. package/dist/signers/ows-signing-backend.js.map +1 -0
  101. package/dist/signers/secure-enclave-backend.d.ts +19 -0
  102. package/dist/signers/secure-enclave-backend.d.ts.map +1 -0
  103. package/dist/signers/secure-enclave-backend.js +201 -0
  104. package/dist/signers/secure-enclave-backend.js.map +1 -0
  105. package/dist/signers/secure-enclave-types.d.ts +98 -0
  106. package/dist/signers/secure-enclave-types.d.ts.map +1 -0
  107. package/dist/signers/secure-enclave-types.js +12 -0
  108. package/dist/signers/secure-enclave-types.js.map +1 -0
  109. package/dist/types.d.ts +371 -0
  110. package/dist/types.d.ts.map +1 -0
  111. package/dist/types.js +2 -0
  112. package/dist/types.js.map +1 -0
  113. package/package.json +85 -0
package/SPEC.md ADDED
@@ -0,0 +1,192 @@
1
+ # @prism-ing/wallet — Specification
2
+
3
+ **Version:** 0.2.0
4
+ **Status:** Release Candidate
5
+ **Last Updated:** April 2026
6
+
7
+ ## Purpose
8
+
9
+ @prism-ing/wallet is a production-grade, framework-agnostic wallet SDK that provides self-custodial wallet functionality across EVM and Solana chains. It integrates:
10
+
11
+ - **iOS Secure Enclave** for hardware-backed key security with biometric authentication
12
+ - **OWS (Open Wallet Standard)** for Node.js software signing with encrypted persistence
13
+ - **OneBalance** for ERC-4337 account abstraction and cross-chain execution
14
+ - **ZeroDev Kernel v3.1** for EVM social recovery and on-chain session key enforcement
15
+ - **Squads V4** for Solana multisig-based key recovery
16
+
17
+ Any application — mobile, web, CLI, agent — can install this package and get best-in-class wallet functionality in under 50 lines of integration code.
18
+
19
+ ## Design Decisions
20
+
21
+ ### No Seed Phrases
22
+
23
+ The wallet never generates, stores, or exports mnemonic phrases to users. Keys are created via the Secure Enclave (iOS) or OWS software signing (Node/browser) and are non-exportable through the SDK's API. Recovery is handled via social recovery (ZeroDev guardians + Squads co-signers), not by re-deriving keys from a seed.
24
+
25
+ ### Signer Injection Pattern
26
+
27
+ Router packages (`@prism-ing/evm-router`, `@prism-ing/sol-router`) accept any object conforming to the `PrismSigner` interface. They never import `@prism-ing/wallet` directly. This means external applications can use routers with their own signing solution.
28
+
29
+ ### Result Types Over Exceptions
30
+
31
+ All fallible operations return `Result<T, WalletError>` instead of throwing. This makes failure explicit in the type system and eliminates the need for try/catch in consumer code. The only place exceptions are caught is in `fromPromise()`, which wraps third-party calls at the boundary.
32
+
33
+ ### Counterfactual Accounts
34
+
35
+ OneBalance accounts are initialized counterfactually — no on-chain transaction is needed to create an account. The address is deterministic from the signer's public key. Funds can be received at the address before any on-chain deployment.
36
+
37
+ ### Backend Injection (No Direct SDK Dependencies)
38
+
39
+ ZeroDev and Squads operations are injected via bridge interfaces (`ZeroDevSessionBackendConfig`, `SquadsNativeBridge`). This keeps `@prism-ing/wallet` free of transitive dependencies on `@zerodev/sdk` and `@sqds/multisig` — consumers provide their own integration, and the wallet SDK handles orchestration and policy mapping.
40
+
41
+ ## Private Key Security Model
42
+
43
+ ### iOS Secure Enclave Backend
44
+
45
+ - Keys (secp256k1 for EVM, ed25519 for Solana) are generated and encrypted by the Secure Enclave's hardware P-256 key
46
+ - Encrypted keys are stored in the iOS Keychain, tagged by wallet identifier
47
+ - Every signing operation requires biometric authentication (Face ID / Touch ID) via `LocalAuthentication`
48
+ - After biometric success, the encrypted key is decrypted in-process for signing, then discarded
49
+ - The `OWSKeyPair` type stores only the Keychain tag and derived addresses, never private key material
50
+ - The Secure Enclave does not natively support secp256k1 — it provides encryption/decryption for key-at-rest protection, not direct signing
51
+
52
+ ### Node.js OWS Backend
53
+
54
+ - BIP-39 mnemonic generates secp256k1 + ed25519 keys
55
+ - Keys are encrypted at rest with scrypt + AES-256-GCM at `~/.ows/`
56
+ - Same wallet name = same keys across sessions (deterministic from mnemonic)
57
+ - Two auth modes: passphrase (owner) or API key (agent, policy-gated)
58
+ - Keys are held in-memory during the process lifetime
59
+
60
+ ### Node.js Software Backend
61
+
62
+ - Random key generation via `@noble/hashes` CSPRNG
63
+ - Keys held in-memory Map only — no persistence, no encryption
64
+ - Intended for testing and development only
65
+ - Production deployments should use OWS or Secure Enclave
66
+
67
+ ### Security Assumptions
68
+
69
+ 1. **The runtime environment is trusted.** Private keys are decrypted in-process for signing. A compromised Node.js process or jailbroken iOS device could extract key material. Session keys and on-chain enforcement mitigate this.
70
+
71
+ 2. **OneBalance is a trusted co-signer.** Quote generation, settlement, and account abstraction depend on OneBalance availability and correctness. Client-side quote verification provides defense-in-depth but cannot independently verify settlement.
72
+
73
+ 3. **Recovery backends are correctly implemented.** The wallet SDK orchestrates recovery but delegates on-chain operations to injected backends. A buggy `EvmRecoveryBackend` or `SquadsNativeBridge` could fail to rotate signers correctly.
74
+
75
+ 4. **Time-based security relies on system clocks.** Recovery challenge time windows and session key expiration use `Date.now()`. A significantly skewed clock could extend or shorten these windows.
76
+
77
+ ## Session Keys
78
+
79
+ Session keys are ephemeral, policy-scoped signers that wrap the root `PrismSigner`. They limit what an agent or automated process can do without holding unrestricted root signing authority.
80
+
81
+ ### Policy Dimensions
82
+
83
+ | Dimension | Field | Effect |
84
+ |-----------|-------|--------|
85
+ | Time | `expiresAt` | Session becomes inactive after this Unix timestamp |
86
+ | Assets | `allowedAssets` | Only these OneBalance asset IDs can be operated on |
87
+ | Per-op amount | `maxAmountPerOp` | Single operation cannot exceed this amount |
88
+ | Cumulative amount | `maxTotalAmount` | Total spend across all operations is capped |
89
+ | Recipients | `allowedRecipients` | Only these addresses can be destinations |
90
+ | Chains | `allowedChains` | Only these chain IDs are permitted |
91
+
92
+ ### Enforcement Architecture
93
+
94
+ **Layer 1: Client-side (always active)**
95
+ The session signer checks policy before every `signMessage`, `signTypedData`, and `signTransaction`. Violations throw immediately, preventing the signature from being produced.
96
+
97
+ **Layer 2: On-chain (optional, via ZeroDev)**
98
+ When a `SessionKeyBackend` is provided, policies are mapped to ZeroDev Kernel v3.1 permission validators using `mapSessionPolicyToZeroDev()`. The smart contract enforces the same constraints on-chain.
99
+
100
+ **Policy mapping to ZeroDev:**
101
+ - `expiresAt` → `toTimestampPolicy({ validUntil })` — on-chain expiration
102
+ - `allowedAssets` → `toCallPolicy({ permissions })` — ERC-20 `transfer()` function selector + contract address restrictions
103
+ - `maxAmountPerOp` → `ParamCondition.LESS_THAN_OR_EQUAL` on transfer amount parameter
104
+ - `allowedRecipients` → `ParamCondition.ONE_OF` on transfer recipient parameter
105
+ - A `toGasPolicy` (default 0.1 ETH) is always included — prevents gas drain
106
+ - A `toRateLimitPolicy` is included when amount caps are set (100 ops/hour)
107
+ - CallPolicyVersion V0.0.4 is used by default (fixes ERC-4337 storage access violation for Alchemy bundler compatibility)
108
+
109
+ **Security consideration:** Client-side spend tracking (`totalSpent`) resets when the process restarts. For agents that may restart, rely on on-chain enforcement via ZeroDev rather than client-side cumulative caps.
110
+
111
+ ## Social Recovery
112
+
113
+ ### Architecture
114
+
115
+ Recovery rotates the smart contract's authorized signer. The original key becomes useless. Funds never move.
116
+
117
+ **EVM (ZeroDev Kernel v3.1):**
118
+ - Guardian registered via `EvmRecoveryBackend.addGuardian()`
119
+ - Rotation: `rotateValidator(oldSigner, newSigner, guardianProof)` calls `removeValidator(old)` + `addValidator(new)` on the Kernel contract
120
+
121
+ **Solana (Squads V4):**
122
+ - Guardian registered via `SolanaRecoveryBackend.addMember()` as a Voter member
123
+ - Rotation: `rotateMember(oldSigner, newSigner, guardianProof)` creates a config transaction with `AddMember(new, fullPermissions)` + `RemoveMember(old)`
124
+ - `configAuthority: null` — all changes go through proposal/approval/execute flow
125
+ - `AddMember` before `RemoveMember` in the same transaction prevents zero-member state
126
+ - Threshold auto-increments when adding guardians (configurable)
127
+
128
+ ### Recovery Challenge (Anti-Replay)
129
+
130
+ ```
131
+ challenge = keccak256(
132
+ "prism-recovery-v1" // Domain tag
133
+ || currentSigner // Being rotated away
134
+ || newSigner // Being rotated to
135
+ || floor(now / 300000) // 5-minute time window
136
+ )
137
+ ```
138
+
139
+ Properties:
140
+ - A proof for A → B cannot replay for A → C (different `newSigner`)
141
+ - A proof expires within 5 minutes (time-bounded)
142
+ - A proof for wallet X cannot be used on wallet Y (different `currentSigner`)
143
+ - The domain tag prevents cross-protocol replay
144
+
145
+ ### Guardian Security
146
+
147
+ - Passkey guardians derive an EVM address from the WebAuthn public key via `keccak256`
148
+ - Device guardians are registered on both EVM (via ZeroDev) and Solana (via Squads)
149
+ - Contact guardians are registered on EVM only (requires `EvmRecoveryBackend`)
150
+ - Guardian proofs are obtained via passkey signing or provided out-of-band
151
+
152
+ ## Quote Verification
153
+
154
+ Before signing any OneBalance quote, `executeCrossChain` performs 6 client-side checks:
155
+
156
+ 1. **Expiration** — `expirationTimestamp` vs current time
157
+ 2. **Tamper-proof signature** — OneBalance's `tamperProofSignature` field is present and non-empty
158
+ 3. **Origin asset** — Quote's origin asset matches the requested `fromAsset`
159
+ 4. **Origin amount** — Within configurable slippage tolerance (default 1%)
160
+ 5. **Destination asset** — Quote's destination matches the requested `toAsset`
161
+ 6. **EVM sender** — All `userOp.sender` fields match the wallet's `accountAddress`
162
+
163
+ This prevents blind-signing attacks where a compromised or man-in-the-middle API could alter the EIP-712 payload to transfer funds to an attacker's address.
164
+
165
+ ## Network Resilience
166
+
167
+ The OneBalance HTTP client includes:
168
+ - Exponential backoff with jitter (3 retries, base delay 1s → 2s → 4s + random jitter)
169
+ - Retries only on: timeouts (AbortError), network errors (TypeError), and 5xx server errors
170
+ - No retry on 4xx client errors (bad request, auth failure, validation)
171
+ - Configurable via `maxRetries` and `baseDelayMs` in `OneBalanceHttpConfig`
172
+
173
+ ## Error Codes
174
+
175
+ | Code | Retryable | User-Facing | Description |
176
+ |------|-----------|-------------|-------------|
177
+ | `ENCLAVE_UNAVAILABLE` | No | No | Secure Enclave not available on this platform |
178
+ | `ONEBALANCE_TIMEOUT` | Yes | No | OneBalance API did not respond in time |
179
+ | `ONEBALANCE_API_ERROR` | Yes | No | OneBalance API returned an error |
180
+ | `RECOVERY_GUARDIAN_INVALID` | No | Yes | Invalid guardian address |
181
+ | `SIGNING_REJECTED` | No | Yes | User rejected signing (biometric fail / cancel) |
182
+ | `ACCOUNT_NOT_INITIALIZED` | No | No | Operation on uninitialized account |
183
+ | `INSUFFICIENT_BALANCE` | No | Yes | Not enough balance for operation |
184
+ | `KEY_NOT_FOUND` | No | No | Requested key pair not found in signing backend |
185
+ | `VALIDATION_ERROR` | No | Yes | Input failed Zod validation |
186
+ | `SESSION_KEY_EXPIRED` | No | No | Session key has expired or been revoked |
187
+ | `SESSION_KEY_POLICY_VIOLATION` | No | Yes | Operation violates session key policy |
188
+ | `QUOTE_VERIFICATION_FAILED` | No | No | OneBalance quote failed integrity verification |
189
+
190
+ ## Upgrade Policy
191
+
192
+ Breaking changes require a major version bump and a changeset entry. The `PrismSigner` interface is considered frozen after v1.0 — any changes to it are treated as a critical breaking change across the entire monorepo.
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Squads V4 Solana recovery backend.
3
+ *
4
+ * @remarks
5
+ * Implements {@link SolanaRecoveryBackend} using Squads V4 multisig
6
+ * operations for guardian-based key recovery on Solana.
7
+ *
8
+ * Architecture:
9
+ * - Wallet's Solana signer is a member of a Squads V4 multisig
10
+ * - Guardians are added as additional members with Voter permissions
11
+ * - Recovery rotates the signer: AddMember(new) + RemoveMember(old)
12
+ * - All changes go through config transactions (proposal → approval → execution)
13
+ * - No `configAuthority` — all changes require guardian approval
14
+ *
15
+ * Key design decisions:
16
+ * - **AddMember before RemoveMember** — actions execute in order, preventing
17
+ * a moment with zero members
18
+ * - **Auto-increment threshold** — adding a guardian increases the approval
19
+ * threshold, requiring more guardian consensus
20
+ * - **Config transactions invalidate active transactions** — expected during
21
+ * recovery (Squads V4 behavior)
22
+ *
23
+ * @packageDocumentation
24
+ */
25
+ import type { SolanaRecoveryBackend } from '../types.js';
26
+ import type { SquadsRecoveryBackendConfig } from './squads-types.js';
27
+ /**
28
+ * Create a Squads V4 Solana recovery backend.
29
+ *
30
+ * @remarks
31
+ * The backend delegates all Squads SDK operations to the injected
32
+ * {@link SquadsNativeBridge}. This keeps `@prism-ing/wallet` free of a
33
+ * direct `@sqds/multisig` dependency.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * import * as multisig from '@sqds/multisig';
38
+ *
39
+ * const bridge: SquadsNativeBridge = {
40
+ * async executeConfigTransaction(actions) {
41
+ * // Create config tx, proposal, approve, execute
42
+ * const txIndex = await getNextTransactionIndex(multisigPda);
43
+ * await multisig.rpc.configTransactionCreate({ multisigPda, transactionIndex: txIndex, creator, actions });
44
+ * await multisig.rpc.proposalCreate({ multisigPda, transactionIndex: txIndex, creator });
45
+ * await multisig.rpc.proposalApprove({ multisigPda, transactionIndex: txIndex, member: guardian });
46
+ * return await multisig.rpc.configTransactionExecute({ multisigPda, transactionIndex: txIndex, member: guardian });
47
+ * },
48
+ * async getThreshold() { ... },
49
+ * async getMemberCount() { ... },
50
+ * };
51
+ *
52
+ * const backend = createSquadsRecoveryBackend({ bridge });
53
+ * ```
54
+ *
55
+ * @param config - Squads backend configuration with bridge functions.
56
+ * @returns A {@link SolanaRecoveryBackend} for guardian-based Solana recovery.
57
+ */
58
+ export declare function createSquadsRecoveryBackend(config: SquadsRecoveryBackendConfig): SolanaRecoveryBackend;
59
+ //# sourceMappingURL=squads-recovery-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"squads-recovery-backend.d.ts","sourceRoot":"","sources":["../../src/backends/squads-recovery-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EACV,2BAA2B,EAG5B,MAAM,mBAAmB,CAAC;AAO3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,2BAA2B,GAClC,qBAAqB,CAsDvB"}
@@ -0,0 +1,81 @@
1
+ import { FULL_PERMISSIONS, VOTER_PERMISSIONS } from './squads-types.js';
2
+ // ---------------------------------------------------------------------------
3
+ // Factory
4
+ // ---------------------------------------------------------------------------
5
+ /**
6
+ * Create a Squads V4 Solana recovery backend.
7
+ *
8
+ * @remarks
9
+ * The backend delegates all Squads SDK operations to the injected
10
+ * {@link SquadsNativeBridge}. This keeps `@prism-ing/wallet` free of a
11
+ * direct `@sqds/multisig` dependency.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import * as multisig from '@sqds/multisig';
16
+ *
17
+ * const bridge: SquadsNativeBridge = {
18
+ * async executeConfigTransaction(actions) {
19
+ * // Create config tx, proposal, approve, execute
20
+ * const txIndex = await getNextTransactionIndex(multisigPda);
21
+ * await multisig.rpc.configTransactionCreate({ multisigPda, transactionIndex: txIndex, creator, actions });
22
+ * await multisig.rpc.proposalCreate({ multisigPda, transactionIndex: txIndex, creator });
23
+ * await multisig.rpc.proposalApprove({ multisigPda, transactionIndex: txIndex, member: guardian });
24
+ * return await multisig.rpc.configTransactionExecute({ multisigPda, transactionIndex: txIndex, member: guardian });
25
+ * },
26
+ * async getThreshold() { ... },
27
+ * async getMemberCount() { ... },
28
+ * };
29
+ *
30
+ * const backend = createSquadsRecoveryBackend({ bridge });
31
+ * ```
32
+ *
33
+ * @param config - Squads backend configuration with bridge functions.
34
+ * @returns A {@link SolanaRecoveryBackend} for guardian-based Solana recovery.
35
+ */
36
+ export function createSquadsRecoveryBackend(config) {
37
+ const bridge = config.bridge;
38
+ const autoIncrement = config.autoIncrementThreshold !== false;
39
+ const backend = {
40
+ async addMember(solanaAddress) {
41
+ const actions = [
42
+ {
43
+ __kind: 'AddMember',
44
+ newMember: {
45
+ key: solanaAddress,
46
+ permissions: VOTER_PERMISSIONS,
47
+ },
48
+ },
49
+ ];
50
+ // Auto-increment threshold when adding a guardian
51
+ if (autoIncrement) {
52
+ const currentThreshold = await bridge.getThreshold();
53
+ actions.push({
54
+ __kind: 'ChangeThreshold',
55
+ newThreshold: currentThreshold + 1,
56
+ });
57
+ }
58
+ return bridge.executeConfigTransaction(actions);
59
+ },
60
+ async rotateMember(oldSigner, newSigner, _guardianProof) {
61
+ // AddMember first, then RemoveMember — ensures the multisig
62
+ // never has zero members during the rotation
63
+ const actions = [
64
+ {
65
+ __kind: 'AddMember',
66
+ newMember: {
67
+ key: newSigner,
68
+ permissions: FULL_PERMISSIONS,
69
+ },
70
+ },
71
+ {
72
+ __kind: 'RemoveMember',
73
+ oldMember: oldSigner,
74
+ },
75
+ ];
76
+ return bridge.executeConfigTransaction(actions);
77
+ },
78
+ };
79
+ return backend;
80
+ }
81
+ //# sourceMappingURL=squads-recovery-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"squads-recovery-backend.js","sourceRoot":"","sources":["../../src/backends/squads-recovery-backend.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAExE,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,2BAA2B,CACzC,MAAmC;IAEnC,MAAM,MAAM,GAAuB,MAAM,CAAC,MAAM,CAAC;IACjD,MAAM,aAAa,GAAG,MAAM,CAAC,sBAAsB,KAAK,KAAK,CAAC;IAE9D,MAAM,OAAO,GAA0B;QACrC,KAAK,CAAC,SAAS,CAAC,aAAqB;YACnC,MAAM,OAAO,GAAyB;gBACpC;oBACE,MAAM,EAAE,WAAW;oBACnB,SAAS,EAAE;wBACT,GAAG,EAAE,aAAa;wBAClB,WAAW,EAAE,iBAAiB;qBAC/B;iBACF;aACF,CAAC;YAEF,kDAAkD;YAClD,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;gBACrD,OAAO,CAAC,IAAI,CAAC;oBACX,MAAM,EAAE,iBAAiB;oBACzB,YAAY,EAAE,gBAAgB,GAAG,CAAC;iBACnC,CAAC,CAAC;YACL,CAAC;YAED,OAAO,MAAM,CAAC,wBAAwB,CAAC,OAAO,CAAC,CAAC;QAClD,CAAC;QAED,KAAK,CAAC,YAAY,CAChB,SAAiB,EACjB,SAAiB,EACjB,cAA0B;YAE1B,4DAA4D;YAC5D,6CAA6C;YAC7C,MAAM,OAAO,GAAyB;gBACpC;oBACE,MAAM,EAAE,WAAW;oBACnB,SAAS,EAAE;wBACT,GAAG,EAAE,SAAS;wBACd,WAAW,EAAE,gBAAgB;qBAC9B;iBACF;gBACD;oBACE,MAAM,EAAE,cAAc;oBACtB,SAAS,EAAE,SAAS;iBACrB;aACF,CAAC;YAEF,OAAO,MAAM,CAAC,wBAAwB,CAAC,OAAO,CAAC,CAAC;QAClD,CAAC;KACF,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Types for the Squads V4 Solana recovery backend.
3
+ *
4
+ * @remarks
5
+ * These types model the Squads V4 multisig operations needed for
6
+ * key recovery: member management and config transaction execution.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ /**
11
+ * Squads V4 member permissions.
12
+ *
13
+ * @remarks
14
+ * Maps to `Permissions` from `@sqds/multisig`. Members can have
15
+ * any combination of Proposer, Voter, and Executor roles.
16
+ */
17
+ export interface SquadsMemberPermissions {
18
+ readonly proposer: boolean;
19
+ readonly voter: boolean;
20
+ readonly executor: boolean;
21
+ }
22
+ /** All permissions enabled. */
23
+ export declare const FULL_PERMISSIONS: SquadsMemberPermissions;
24
+ /** Voter-only permissions (for guardians). */
25
+ export declare const VOTER_PERMISSIONS: SquadsMemberPermissions;
26
+ /**
27
+ * Config actions for Squads V4 config transactions.
28
+ *
29
+ * @remarks
30
+ * Config transactions modify the multisig's configuration (members,
31
+ * threshold, time lock). They go through the full proposal flow:
32
+ * create → approve → execute.
33
+ */
34
+ export type SquadsConfigAction = {
35
+ readonly __kind: 'AddMember';
36
+ readonly newMember: {
37
+ readonly key: string;
38
+ readonly permissions: SquadsMemberPermissions;
39
+ };
40
+ } | {
41
+ readonly __kind: 'RemoveMember';
42
+ readonly oldMember: string;
43
+ } | {
44
+ readonly __kind: 'ChangeThreshold';
45
+ readonly newThreshold: number;
46
+ };
47
+ /**
48
+ * Native bridge to the Squads V4 SDK.
49
+ *
50
+ * @remarks
51
+ * Consumers implement this by wrapping `@sqds/multisig` operations.
52
+ * This keeps `@prism-ing/wallet` free of a direct Squads dependency.
53
+ */
54
+ export interface SquadsNativeBridge {
55
+ /** Execute a full config transaction: create → propose → approve → execute. */
56
+ executeConfigTransaction(actions: readonly SquadsConfigAction[]): Promise<string>;
57
+ /** Get the current multisig threshold. */
58
+ getThreshold(): Promise<number>;
59
+ /** Get the current member count. */
60
+ getMemberCount(): Promise<number>;
61
+ }
62
+ /**
63
+ * Configuration for the Squads V4 recovery backend.
64
+ */
65
+ export interface SquadsRecoveryBackendConfig {
66
+ /** The Squads native bridge for executing multisig operations. */
67
+ readonly bridge: SquadsNativeBridge;
68
+ /**
69
+ * Whether to auto-increment threshold when adding guardians.
70
+ * Defaults to `true` — adding a guardian increases the threshold by 1.
71
+ */
72
+ readonly autoIncrementThreshold?: boolean;
73
+ }
74
+ //# sourceMappingURL=squads-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"squads-types.d.ts","sourceRoot":"","sources":["../../src/backends/squads-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,+BAA+B;AAC/B,eAAO,MAAM,gBAAgB,EAAE,uBAIrB,CAAC;AAEX,8CAA8C;AAC9C,eAAO,MAAM,iBAAiB,EAAE,uBAItB,CAAC;AAEX;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,EAAE,uBAAuB,CAAA;KAAE,CAAA;CAAE,GAC7H;IAAE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,+EAA+E;IAC/E,wBAAwB,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAElF,0CAA0C;IAC1C,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAEhC,oCAAoC;IACpC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IAEpC;;;OAGG;IACH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAC3C"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Types for the Squads V4 Solana recovery backend.
3
+ *
4
+ * @remarks
5
+ * These types model the Squads V4 multisig operations needed for
6
+ * key recovery: member management and config transaction execution.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ /** All permissions enabled. */
11
+ export const FULL_PERMISSIONS = {
12
+ proposer: true,
13
+ voter: true,
14
+ executor: true,
15
+ };
16
+ /** Voter-only permissions (for guardians). */
17
+ export const VOTER_PERMISSIONS = {
18
+ proposer: false,
19
+ voter: true,
20
+ executor: false,
21
+ };
22
+ //# sourceMappingURL=squads-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"squads-types.js","sourceRoot":"","sources":["../../src/backends/squads-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAeH,+BAA+B;AAC/B,MAAM,CAAC,MAAM,gBAAgB,GAA4B;IACvD,QAAQ,EAAE,IAAI;IACd,KAAK,EAAE,IAAI;IACX,QAAQ,EAAE,IAAI;CACN,CAAC;AAEX,8CAA8C;AAC9C,MAAM,CAAC,MAAM,iBAAiB,GAA4B;IACxD,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,IAAI;IACX,QAAQ,EAAE,KAAK;CACP,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Maps Prism session key policies to ZeroDev on-chain policy representations.
3
+ *
4
+ * @remarks
5
+ * This module is the bridge between Prism's {@link SessionKeyPolicy} (agent-friendly,
6
+ * asset-centric) and ZeroDev's on-chain permission system (contract-centric,
7
+ * function-selector based). The mapped policies are then registered on-chain
8
+ * via the ZeroDev SDK.
9
+ *
10
+ * Policy mapping follows ZeroDev best practices:
11
+ * - Always include a timestamp policy (every session must expire)
12
+ * - Always include a gas policy (prevent gas budget drain)
13
+ * - Use CallPolicyVersion V0.0.4 (Alchemy bundler compatible)
14
+ * - Map asset whitelists to ERC-20 transfer call restrictions
15
+ * - Map recipient whitelists to `ONE_OF` parameter conditions
16
+ * - Map amount caps to `LESS_THAN_OR_EQUAL` parameter conditions
17
+ *
18
+ * @internal
19
+ */
20
+ import type { Address } from '../result.js';
21
+ import type { SessionKeyPolicy } from '../types.js';
22
+ import type { ZeroDevPolicy, CallPolicyVersion } from './zerodev-types.js';
23
+ /**
24
+ * Map a Prism {@link SessionKeyPolicy} to ZeroDev on-chain policies.
25
+ *
26
+ * @param policy - The Prism session key policy to map.
27
+ * @param options - Optional overrides for gas budget and call policy version.
28
+ * @returns An array of ZeroDev policies ready for on-chain registration.
29
+ */
30
+ export declare function mapSessionPolicyToZeroDev(policy: SessionKeyPolicy, options?: {
31
+ readonly gasBudgetWei?: bigint;
32
+ readonly callPolicyVersion?: CallPolicyVersion;
33
+ }): readonly ZeroDevPolicy[];
34
+ /**
35
+ * Resolve a OneBalance asset identifier to a contract address.
36
+ *
37
+ * @param asset - OneBalance asset ID (e.g., `'ob:usdc'`).
38
+ * @returns The contract address, or undefined if unknown.
39
+ */
40
+ export declare function resolveAssetToContract(asset: string): Address | undefined;
41
+ //# sourceMappingURL=zerodev-policy-mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zerodev-policy-mapper.d.ts","sourceRoot":"","sources":["../../src/backends/zerodev-policy-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EACV,aAAa,EAGb,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAsC5B;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CAChD,GACA,SAAS,aAAa,EAAE,CAwC1B;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAEzE"}
@@ -0,0 +1,127 @@
1
+ import { RECOMMENDED_CALL_POLICY_VERSION } from './zerodev-types.js';
2
+ // ---------------------------------------------------------------------------
3
+ // Constants
4
+ // ---------------------------------------------------------------------------
5
+ /** Default gas budget: 0.1 ETH in wei. */
6
+ const DEFAULT_GAS_BUDGET_WEI = 100000000000000000n;
7
+ /**
8
+ * ERC-20 transfer function selector: `transfer(address,uint256)`.
9
+ * keccak256("transfer(address,uint256)") = 0xa9059cbb...
10
+ */
11
+ const ERC20_TRANSFER_SELECTOR = '0xa9059cbb';
12
+ /**
13
+ * Well-known OneBalance asset identifiers mapped to contract addresses.
14
+ *
15
+ * @remarks
16
+ * OneBalance uses `ob:` prefixed asset IDs. This map resolves them to
17
+ * the canonical ERC-20 contract addresses on supported chains.
18
+ * The zero address is used as a wildcard — the actual per-chain address
19
+ * is resolved by OneBalance at quote time. This restricts the function
20
+ * selector while allowing OneBalance to handle chain-specific routing.
21
+ */
22
+ const ASSET_CONTRACT_MAP = {
23
+ 'ob:usdc': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
24
+ 'ob:usdt': '0xdAC17F958D2ee523a2206206994597C13D831ec7',
25
+ 'ob:dai': '0x6B175474E89094C44Da98b954EedeAC495271d0F',
26
+ 'ob:weth': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
27
+ 'ob:wbtc': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
28
+ };
29
+ // ---------------------------------------------------------------------------
30
+ // Public API
31
+ // ---------------------------------------------------------------------------
32
+ /**
33
+ * Map a Prism {@link SessionKeyPolicy} to ZeroDev on-chain policies.
34
+ *
35
+ * @param policy - The Prism session key policy to map.
36
+ * @param options - Optional overrides for gas budget and call policy version.
37
+ * @returns An array of ZeroDev policies ready for on-chain registration.
38
+ */
39
+ export function mapSessionPolicyToZeroDev(policy, options) {
40
+ const policies = [];
41
+ // 1. Timestamp policy — every session key MUST expire
42
+ policies.push({
43
+ type: 'timestamp',
44
+ validAfter: 0,
45
+ validUntil: policy.expiresAt,
46
+ });
47
+ // 2. Gas policy — prevent session key from draining gas budget
48
+ policies.push({
49
+ type: 'gas',
50
+ maxGasAllowedWei: options?.gasBudgetWei ?? DEFAULT_GAS_BUDGET_WEI,
51
+ });
52
+ // 3. Rate limit policy — if maxAmountPerOp implies operational limits
53
+ // Use a sensible default: 100 operations per hour
54
+ if (policy.maxAmountPerOp !== undefined) {
55
+ policies.push({
56
+ type: 'rate_limit',
57
+ count: 100,
58
+ interval: 3600,
59
+ });
60
+ }
61
+ // 4. Call policy — map asset/recipient/amount restrictions to contract calls
62
+ const callPermissions = buildCallPermissions(policy, options?.callPolicyVersion ?? RECOMMENDED_CALL_POLICY_VERSION);
63
+ if (callPermissions.length > 0) {
64
+ policies.push({
65
+ type: 'call',
66
+ version: options?.callPolicyVersion ?? RECOMMENDED_CALL_POLICY_VERSION,
67
+ permissions: callPermissions,
68
+ });
69
+ }
70
+ return policies;
71
+ }
72
+ /**
73
+ * Resolve a OneBalance asset identifier to a contract address.
74
+ *
75
+ * @param asset - OneBalance asset ID (e.g., `'ob:usdc'`).
76
+ * @returns The contract address, or undefined if unknown.
77
+ */
78
+ export function resolveAssetToContract(asset) {
79
+ return ASSET_CONTRACT_MAP[asset.toLowerCase()];
80
+ }
81
+ // ---------------------------------------------------------------------------
82
+ // Internal Helpers
83
+ // ---------------------------------------------------------------------------
84
+ function buildCallPermissions(policy, _version) {
85
+ const permissions = [];
86
+ const assets = policy.allowedAssets ?? [];
87
+ // If no asset restrictions, no call policy needed
88
+ // (the timestamp + gas policies still protect)
89
+ if (assets.length === 0 && (policy.allowedRecipients === undefined || policy.allowedRecipients.length === 0)) {
90
+ return permissions;
91
+ }
92
+ // Build a permission entry for each allowed asset
93
+ for (const asset of assets) {
94
+ const contractAddress = resolveAssetToContract(asset);
95
+ if (contractAddress === undefined)
96
+ continue;
97
+ const args = [];
98
+ // Restrict recipients (first arg of transfer: address to)
99
+ if (policy.allowedRecipients !== undefined && policy.allowedRecipients.length > 0) {
100
+ // ONE_OF condition: recipient must be in the allowed list
101
+ // Encode each address as a 32-byte ABI-encoded value
102
+ for (const recipient of policy.allowedRecipients) {
103
+ args.push({
104
+ condition: 'ONE_OF',
105
+ offset: 0, // First parameter (address to)
106
+ value: recipient,
107
+ });
108
+ }
109
+ }
110
+ // Restrict amount (second arg of transfer: uint256 amount)
111
+ if (policy.maxAmountPerOp !== undefined) {
112
+ args.push({
113
+ condition: 'LESS_THAN_OR_EQUAL',
114
+ offset: 32, // Second parameter (uint256 amount), 32 bytes after first
115
+ value: policy.maxAmountPerOp,
116
+ });
117
+ }
118
+ permissions.push({
119
+ target: contractAddress,
120
+ functionSelector: ERC20_TRANSFER_SELECTOR,
121
+ valueLimit: 0n, // No native ETH value for ERC-20 transfers
122
+ args,
123
+ });
124
+ }
125
+ return permissions;
126
+ }
127
+ //# sourceMappingURL=zerodev-policy-mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zerodev-policy-mapper.js","sourceRoot":"","sources":["../../src/backends/zerodev-policy-mapper.ts"],"names":[],"mappings":"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"}