@cavos/kit 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,8 +6,10 @@ secp256r1 (P-256) keys that live on the device and sign **invisibly** (no passke
6
6
  no Face ID / Touch ID, no popups). OAuth / email is used only to derive the
7
7
  address, never to sign. No exported keys, no MPC, no on-chain JWT/RSA.
8
8
 
9
- **Phase 1: Starknet only.** The API is chain-configurable by design so Stellar
10
- and Solana adapters slot in behind the same `ChainAdapter` interface later.
9
+ **Chains:** **Starknet** and **Solana** are implemented today, behind a single
10
+ unified `Cavos.connect({ chain, network })` entry point. The API is
11
+ chain-configurable by design — Stellar (and others) will slot in behind the same
12
+ `ChainAdapter` interface.
11
13
 
12
14
  > New package. Does **not** replace `@cavos/react` / `react-native` (legacy
13
15
  > OAuth/session-key SDKs), which continue on the old flow.
@@ -22,45 +24,94 @@ npm install @cavos/kit
22
24
 
23
25
  | Piece | Role |
24
26
  |-------|------|
25
- | `deriveAddressSeed` | Stable `address_seed` from `{ userId, appSalt }`. Identitywallet, device-independent. |
26
- | `StarknetAdapter` | Computes the deterministic address, builds deploy/initialize/add/remove calls, serializes signatures. |
27
+ | `Cavos.connect` | Unified entry point: log in derive deterministic address create/load device key → auto-deploy → ready, gas-sponsored wallet. |
28
+ | `deriveAddressSeed` / `deriveAddressSeedSolana` | Stable `address_seed` from `{ userId, appSalt }`. Identity → wallet, device-independent. |
29
+ | `StarknetAdapter` / `SolanaAdapter` | Per-chain: compute the deterministic address, build deploy/initialize/add/remove calls, serialize signatures. |
27
30
  | `WebCryptoSigner` | Browser silent device signer: non-extractable P-256 key in IndexedDB, no UI on sign. |
28
- | `StarknetDeviceSigner` | Drop-in starknet.js `SignerInterface` backed by a device signer. |
29
- | `CavosAccount` | High-level facade tying identity + adapter + signer together. |
30
- | `RecoveryClient` | Interface to the (non-custodial) backend for the email-approval multi-device flow. |
31
+ | `StarknetDeviceSigner` | Drop-in starknet.js `SignerInterface` backed by a device signer (advanced). |
32
+ | `SolanaRelayer` | Cavos gasless sponsor for Solana: co-signs as fee payer so the integrator holds no keypair. |
33
+ | `RecoveryClient` | Interface to the (non-custodial) backend for the email-approval multi-device flow (Starknet). |
31
34
 
32
- ## Quickstart — high-level (Privy-like)
35
+ ## Quickstart — Starknet
33
36
 
34
37
  One call logs the user in and returns a ready, deployed, gas-sponsored smart
35
38
  account controlled by a silent device key. The user only sees the login.
36
39
 
37
40
  ```ts
38
- import { Cavos, StaticIdentity, CavosPaymaster } from "@cavos/kit";
41
+ import { Cavos, StaticIdentity } from "@cavos/kit";
39
42
 
40
- const cavos = await Cavos.connect({
41
- network: "sepolia",
43
+ const wallet = await Cavos.connect({
44
+ chain: "starknet",
45
+ network: "testnet", // "testnet" (sepolia) | "mainnet"
42
46
  appSalt: "my-app",
43
- // Identity from your login (Cavos-hosted auth lands here; or pass your own userId)
47
+ // Identity from your login (use CavosAuth for hosted Google/Apple/email, or
48
+ // wrap your own userId with StaticIdentity)
44
49
  auth: new StaticIdentity({ userId: user.id, email: user.email }),
45
- // Gas sponsor — deploy + execute are gasless
46
- sponsor: new CavosPaymaster({ network: "sepolia", apiKey: process.env.CAVOS_API_KEY! }),
50
+ appId: process.env.NEXT_PUBLIC_CAVOS_APP_ID, // hosted registry + recovery
51
+ paymasterApiKey: process.env.CAVOS_PAYMASTER_API_KEY!, // gas sponsor
47
52
  });
48
53
 
49
- console.log(cavos.address); // deterministic; auto-deployed on first connect
50
- await cavos.execute(calls); // gasless; signed invisibly by the device key
54
+ console.log(wallet.address); // deterministic; auto-deployed on first connect
55
+
56
+ if (wallet.chain === "starknet" && wallet.status === "ready") {
57
+ await wallet.execute(calls); // gasless; signed invisibly by the device key
58
+ }
59
+ ```
60
+
61
+ `wallet` is a discriminated union (`Cavos | CavosSolana`); narrow on
62
+ `wallet.chain` before calling `execute`, since its signature differs per chain.
63
+
64
+ ## Quickstart — Solana
65
+
66
+ Same unified entry point; pass `chain: "solana"`. Gas is sponsored by the Cavos
67
+ relayer (activated by `appId`) — no `paymasterApiKey` and no fee-payer keypair
68
+ needed.
69
+
70
+ ```ts
71
+ import { Cavos, StaticIdentity } from "@cavos/kit";
72
+
73
+ const wallet = await Cavos.connect({
74
+ chain: "solana",
75
+ network: "testnet", // -> solana-devnet ("mainnet" -> solana-mainnet)
76
+ appSalt: "my-app",
77
+ auth: new StaticIdentity({ userId: user.id, email: user.email }),
78
+ appId: process.env.NEXT_PUBLIC_CAVOS_APP_ID, // activates the gasless relayer
79
+ });
80
+
81
+ if (wallet.chain === "solana" && wallet.status === "ready") {
82
+ const signature = await wallet.execute(1_000_000n, recipient); // lamports, base58 dest
83
+ console.log(signature);
84
+ }
51
85
  ```
52
86
 
53
- > **Status:** `Cavos.connect` orchestration (auth device key address →
54
- > auto-deploy execute) is built. Fully-gasless execution needs the contract to
55
- > add SNIP-6 `is_valid_signature` + SNIP-9 `execute_from_outside_v2` (tracked
56
- > follow-up) and the Cavos paymaster to support the new class. The self-funded
57
- > path below is proven on-chain today.
87
+ On Solana every guarded action (initialize, add/remove signer, execute) is a
88
+ two-instruction bundle pairing Solana's **native secp256r1 precompile** with the
89
+ Cavos `cavos-device-account` program instruction. The address is a deterministic
90
+ PDA derived from `deriveAddressSeedSolana` (`{ userId, appSalt }`).
58
91
 
59
- ## Quickstart — low-level (Starknet)
92
+ ```ts
93
+ // Arbitrary program calls (SPL transfers, swaps, staking):
94
+ import type { InstructionData } from "@cavos/kit";
95
+
96
+ if (wallet.chain === "solana" && wallet.status === "ready") {
97
+ const instructions: InstructionData[] = [/* … SPL/swap instructions … */];
98
+ await wallet.executeInstructions(instructions); // CPIs run with the PDA signing
99
+ }
100
+ ```
101
+
102
+ > **Note:** `execute(amount, destination)` moves **lamports** (SOL); use
103
+ > `executeInstructions(instructions)` for arbitrary program calls. Sponsored
104
+ > `executeInstructions` is gated by the app's Solana program allowlist (dashboard
105
+ > → Solana Programs); targets outside the allowlist + safe set are rejected.
106
+
107
+ ## Quickstart — low-level (Starknet, advanced)
108
+
109
+ If you want to drive the pieces yourself (own paymaster, custom deploy), use the
110
+ adapter + signer directly instead of `Cavos.connect`:
60
111
 
61
112
  ```ts
62
113
  import {
63
- CavosAccount, StarknetAdapter, WebCryptoSigner,
114
+ StarknetAdapter, WebCryptoSigner,
64
115
  deriveAddressSeed, DEVICE_ACCOUNT_CLASS_HASH,
65
116
  } from "@cavos/kit";
66
117
 
@@ -75,47 +126,46 @@ const address = new StarknetAdapter({ classHash }).computeAddress({
75
126
  // 2. Create/load the SILENT device key (keyed by the address). No prompt, ever.
76
127
  const signer = await WebCryptoSigner.loadOrCreate({ keyId: address });
77
128
 
78
- // 3. Build the account.
79
- const adapter = new StarknetAdapter({ classHash, signer });
80
- const account = new CavosAccount({ identity, adapter, signer });
81
- console.log(account.address); // deterministic, pre-deploy
82
-
83
- // 4. Onboarding: deploy + register first signer (route through your paymaster).
84
- const calls = await account.buildOnboarding(); // [UDC deploy, initialize] — submit atomically
85
-
86
- // 5. Add another device later (must be self-submitted by an existing signer).
87
- const addCall = account.buildAddSigner(otherDevicePublicKey);
88
-
89
- // 6. Submit transactions through a standard starknet.js Account.
129
+ // 3. Build deploy/initialize/add/remove calls, then submit through your own
130
+ // paymaster. Route signing through a standard starknet.js Account:
90
131
  import { Account, RpcProvider } from "starknet";
91
132
  import { StarknetDeviceSigner } from "@cavos/kit";
92
133
 
93
134
  const provider = new RpcProvider({ nodeUrl: "https://api.cartridge.gg/x/starknet/sepolia" });
94
- const snAccount = new Account(provider, account.address, new StarknetDeviceSigner(signer), "1");
135
+ const snAccount = new Account(provider, address, new StarknetDeviceSigner(signer), "1");
95
136
  await snAccount.execute(someCalls); // signed silently; DeviceAccount validates on-chain
96
137
  ```
97
138
 
98
139
  `StarknetDeviceSigner` is a drop-in starknet.js `SignerInterface`, so it also
99
140
  plugs into paymaster SDKs (AVNU) for gasless flows. The kit does **not** own gas
100
- sponsorship — route execution through your paymaster of choice.
141
+ sponsorship in the low-level path — route execution through your paymaster of
142
+ choice.
101
143
 
102
144
  ## How signing works
103
145
 
104
- The device key signs `sha256(tx_hash)` with no user interaction (WebCrypto's
105
- ECDSA hashes the message internally). The signature is serialized as
106
- `[r_low, r_high, s_low, s_high, y_parity]` — exactly what
146
+ On **Starknet**, the device key signs `sha256(tx_hash)` with no user interaction
147
+ (WebCrypto's ECDSA hashes the message internally). The signature is serialized
148
+ as `[r_low, r_high, s_low, s_high, y_parity]` — exactly what
107
149
  `DeviceAccount.__validate__` decodes. The contract recomputes `sha256(tx_hash)`,
108
150
  normalizes high-s, and recovers the secp256r1 signer. This 5-felt encoding is
109
151
  covered by a cross-checked contract test (`test_sdk_signature_payload_authorized`
110
152
  in `account-contracts/starknet`).
111
153
 
154
+ On **Solana**, each guarded action pairs the native `Secp256r1SigVerify`
155
+ precompile (which records the device's P-256 signature of a domain-separated
156
+ message) with the Cavos program instruction that consumes it. The fee payer is
157
+ not bound by the device signature, so the relayer co-signs without re-authorizing
158
+ the action.
159
+
112
160
  **Security model:** the private key is non-extractable (never visible to JS) and
113
161
  device-bound — non-custodial, no MPC, verified on-chain. Because signing is
114
162
  silent there is no per-signature user-verification gate (unlike a biometric
115
163
  passkey); this is the standard embedded-wallet trade-off. Multi-device + the
116
164
  non-custodial recovery relay cover device loss.
117
165
 
118
- ## Status (Phase 1)
166
+ ## Status
167
+
168
+ ### Starknet
119
169
 
120
170
  - ✅ Silent secp256r1 device signer (`WebCryptoSigner`) + 5-felt signature
121
171
  serialization, cross-checked against the live contract.
@@ -124,7 +174,6 @@ non-custodial recovery relay cover device loss.
124
174
  - ✅ **Proven on-chain (Sepolia):** silent device key signs a real STRK `approve`,
125
175
  the deployed DeviceAccount validates it ([tx](https://sepolia.starkscan.co/tx/0x51e0e961ee535bf3c45ea020b9c258aee544ed18aea57dbbc80767f8e86ab9e)).
126
176
  - ✅ `Cavos.connect` orchestration: auth → device key → address → auto-deploy → execute.
127
- - ✅ `CavosPaymaster` client + `Sponsor` interface (Cavos-hosted gasless).
128
177
  - ✅ **Gasless proven on-chain (Sepolia):** relayer-paid `execute_from_outside_v2`,
129
178
  authorized solely by the silent device signature, executed a real STRK approve
130
179
  ([tx](https://sepolia.starkscan.co/tx/0x05ade4008f4ccbcfe4a7f016c61eb0eb591c8f696db3f5dad6f0db3ea3b5d2e6)).
@@ -132,7 +181,29 @@ non-custodial recovery relay cover device loss.
132
181
  - ✅ `CavosAuth` (hosted Google/Apple/email/OTP login, mirroring `@cavos/react`).
133
182
  - ✅ Recovery client interface (non-custodial multi-device email-approval flow).
134
183
  - 🚧 Cavos paymaster backend must register the new class hash (backend, out of repo).
184
+
185
+ ### Solana
186
+
187
+ - ✅ `SolanaAdapter` — PDA derivation, the `[secp256r1 precompile, program]`
188
+ instruction builders, low-S normalization, anchor discriminators.
189
+ - ✅ `CavosSolana` high-level client — `connect`, `execute(amount, destination)`,
190
+ `executeInstructions(instructions)`, `addSigner`, `setupRecovery`, static
191
+ `recover`; gasless by default via the relayer when `appId` is set.
192
+ - ✅ `executeInstructions` arbitrary CPI — the device key signs over a hash of the
193
+ instruction set; the on-chain `execute` instruction invokes the CPIs with the
194
+ PDA signing. Sponsored calls are gated by the app's program allowlist.
195
+ - ✅ `SolanaRelayer` — co-signs as fee payer for seedless/gasless execution
196
+ (integrator holds no fee-payer keypair); self-funded `feePayer` fallback.
197
+ - ✅ Unit tests + end-to-end scripts (`scripts/solana_e2e.ts`,
198
+ `scripts/solana_relayer_e2e.ts`).
199
+ - ✅ Recovery (`setupRecovery` / `recover`) — same self-custodial model as Starknet.
200
+
201
+ ### Cross-chain / next
202
+
203
+ - ✅ Unified `Cavos.connect({ chain, network })` dispatcher with a `CavosWallet`
204
+ discriminated union.
135
205
  - 🚧 Recovery backend service + session keys (Phase 2).
206
+ - 🚧 Stellar adapter (planned).
136
207
 
137
208
  ## Demo
138
209