@lightconexyz/lightcone-sdk 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +150 -3
  2. package/dist/auth/client.d.ts +17 -1
  3. package/dist/auth/client.d.ts.map +1 -1
  4. package/dist/auth/client.js +68 -0
  5. package/dist/auth/client.js.map +1 -1
  6. package/dist/auth/index.d.ts +57 -5
  7. package/dist/auth/index.d.ts.map +1 -1
  8. package/dist/auth/index.js +26 -5
  9. package/dist/auth/index.js.map +1 -1
  10. package/dist/client.d.ts +23 -1
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +38 -2
  13. package/dist/client.js.map +1 -1
  14. package/dist/context.d.ts +23 -2
  15. package/dist/context.d.ts.map +1 -1
  16. package/dist/context.js +238 -17
  17. package/dist/context.js.map +1 -1
  18. package/dist/domain/market/outcome.d.ts +9 -3
  19. package/dist/domain/market/outcome.d.ts.map +1 -1
  20. package/dist/domain/market/outcome.js +10 -7
  21. package/dist/domain/market/outcome.js.map +1 -1
  22. package/dist/domain/market/wire.d.ts +3 -3
  23. package/dist/domain/market/wire.d.ts.map +1 -1
  24. package/dist/domain/market/wire.js.map +1 -1
  25. package/dist/domain/orderbook/wire.d.ts +5 -0
  26. package/dist/domain/orderbook/wire.d.ts.map +1 -1
  27. package/dist/domain/orderbook/wire.js.map +1 -1
  28. package/dist/domain/position/client.d.ts +160 -4
  29. package/dist/domain/position/client.d.ts.map +1 -1
  30. package/dist/domain/position/client.js +592 -4
  31. package/dist/domain/position/client.js.map +1 -1
  32. package/dist/domain/position/index.d.ts +54 -3
  33. package/dist/domain/position/index.d.ts.map +1 -1
  34. package/dist/domain/position/index.js +1 -0
  35. package/dist/domain/position/index.js.map +1 -1
  36. package/dist/domain/position/state.d.ts +118 -0
  37. package/dist/domain/position/state.d.ts.map +1 -0
  38. package/dist/domain/position/state.js +262 -0
  39. package/dist/domain/position/state.js.map +1 -0
  40. package/dist/error.d.ts +8 -2
  41. package/dist/error.d.ts.map +1 -1
  42. package/dist/error.js +14 -1
  43. package/dist/error.js.map +1 -1
  44. package/dist/index.d.ts +4 -3
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +3 -1
  47. package/dist/index.js.map +1 -1
  48. package/dist/prelude.d.ts +6 -5
  49. package/dist/prelude.d.ts.map +1 -1
  50. package/dist/prelude.js +10 -1
  51. package/dist/prelude.js.map +1 -1
  52. package/dist/privy/client.d.ts +4 -0
  53. package/dist/privy/client.d.ts.map +1 -1
  54. package/dist/privy/client.js +4 -0
  55. package/dist/privy/client.js.map +1 -1
  56. package/dist/rpc.d.ts +46 -1
  57. package/dist/rpc.d.ts.map +1 -1
  58. package/dist/rpc.js +125 -0
  59. package/dist/rpc.js.map +1 -1
  60. package/dist/shared/api_response.d.ts +7 -0
  61. package/dist/shared/api_response.d.ts.map +1 -1
  62. package/dist/shared/api_response.js +16 -0
  63. package/dist/shared/api_response.js.map +1 -1
  64. package/dist/shared/index.d.ts +1 -1
  65. package/dist/shared/index.d.ts.map +1 -1
  66. package/dist/shared/index.js +2 -1
  67. package/dist/shared/index.js.map +1 -1
  68. package/dist/shared/signing.d.ts +17 -0
  69. package/dist/shared/signing.d.ts.map +1 -1
  70. package/dist/shared/signing.js +27 -0
  71. package/dist/shared/signing.js.map +1 -1
  72. package/dist/shared/types.d.ts +3 -1
  73. package/dist/shared/types.d.ts.map +1 -1
  74. package/dist/shared/types.js +12 -4
  75. package/dist/shared/types.js.map +1 -1
  76. package/dist/ws/client.browser.d.ts +4 -0
  77. package/dist/ws/client.browser.d.ts.map +1 -1
  78. package/dist/ws/client.browser.js +13 -1
  79. package/dist/ws/client.browser.js.map +1 -1
  80. package/dist/ws/client.node.d.ts +4 -0
  81. package/dist/ws/client.node.d.ts.map +1 -1
  82. package/dist/ws/client.node.js +13 -1
  83. package/dist/ws/client.node.js.map +1 -1
  84. package/dist/ws/index.d.ts +24 -0
  85. package/dist/ws/index.d.ts.map +1 -1
  86. package/dist/ws/index.js +162 -1
  87. package/dist/ws/index.js.map +1 -1
  88. package/dist/ws/subscriptions.d.ts +12 -1
  89. package/dist/ws/subscriptions.d.ts.map +1 -1
  90. package/dist/ws/subscriptions.js +11 -0
  91. package/dist/ws/subscriptions.js.map +1 -1
  92. package/dist/ws/types.d.ts +5 -0
  93. package/dist/ws/types.d.ts.map +1 -1
  94. package/dist/ws/types.js.map +1 -1
  95. package/package.json +2 -1
package/README.md CHANGED
@@ -17,6 +17,7 @@ TypeScript SDK for the Lightcone impact market protocol on Solana.
17
17
  - [Environment Configuration](#environment-configuration)
18
18
  - [Examples](#examples)
19
19
  - [Error Handling](#error-handling)
20
+ - [Transaction Fee Funding](#transaction-fee-funding)
20
21
  - [Retry Strategy](#retry-strategy)
21
22
 
22
23
  ## Installation
@@ -25,6 +26,24 @@ TypeScript SDK for the Lightcone impact market protocol on Solana.
25
26
  npm install @lightconexyz/lightcone-sdk
26
27
  ```
27
28
 
29
+ ## Transaction Fee Funding
30
+
31
+ Shared on-chain submission checks the exact prepared message fee and declared
32
+ fee-payer Native SOL Balance before signing when both RPC facts are available.
33
+ A proven shortfall is an `SdkError` with variant
34
+ `InsufficientSolForTransactionFees`, bigint `availableLamports` and
35
+ `requiredLamports` fields, and the canonical deposit-SOL message. Fee or balance
36
+ lookup failure continues through the existing submission path; planner-owned SOL
37
+ actions retain fail-closed live fee, rent, and reserve checks.
38
+
39
+ `LightconeClient.builder().transactionSponsorship(true)` and
40
+ `client.setTransactionSponsorshipEnabled(true)` are trusted application assertions
41
+ for wallet-adapter and Privy signing. The default is false, each transaction
42
+ captures its signer and capability before asynchronous RPC work, `clone()` copies
43
+ the current value, and local-keypair submission rejects an enabled capability. Raw
44
+ `Privy.signAndSendTx` forwarding and off-chain order-message signing are outside
45
+ this contract.
46
+
28
47
  ## Quick Start
29
48
 
30
49
  ```typescript
@@ -184,20 +203,129 @@ reconnect/resubscribe; `resync: true` requires unsubscribe/resubscribe with the
184
203
  same aggregation. Each `(orderbook, aggregation)` pair needs its own state.
185
204
  Truncation flags are preserved and mean that side is not exhaustive. See
186
205
  [`examples/ws_book_and_trades.ts`](examples/ws_book_and_trades.ts).
206
+ Each decoded bid and ask level includes exact decimal-string `quote_notional`.
207
+ For grouped books, `price` is a display bucket boundary, so quote liquidity
208
+ and totals must use `quote_notional` rather than `price * size`. The
209
+ price-to-base-size `OrderbookState` maps do not retain quote notional; read it
210
+ from the decoded `OrderBook` levels.
187
211
  Ticker consumers should use the supplied `mid`; it is engine-authoritative and
188
212
  may use one-sided-book or last-trade fallback.
189
213
  REST depth is a coherent projection that may briefly lag a mutation. Use its
190
214
  `revision` and `captured_at_ms` metadata, and expect revision gaps.
191
215
 
216
+ ```typescript
217
+ ws.subscribe({ type: "book_update", orderbook_ids: [orderbook.orderbookId], nSigFigs: 5, mantissa: 2 });
218
+ ```
219
+
192
220
  Order submission accepts decimal strings and uses exact `bigint` construction.
193
221
  `submit()` fetches and caches `/api/orderbooks/{id}/decimals` before invoking a
194
222
  signer. Direct `sign()`/`finalize()` calls require the returned `OrderbookRules`.
195
223
  Raw amounts, explicit salts, and derived prices are preflighted against the same
196
224
  signed-64-bit admission rules; no tick or size normalization is implicit.
197
225
 
198
- ```typescript
199
- ws.subscribe({ type: "book_update", orderbook_ids: [orderbook.orderbookId], nSigFigs: 5, mantissa: 2 });
200
- ```
226
+ ### Wallet Balances and SOL Action Planning
227
+
228
+ `depositTokenBalances()` returns a required exact nine-decimal
229
+ `native_sol_balance` alongside the separate mint-keyed SPL map. Initialize
230
+ `WalletDepositBalancesState` with `applyRestSnapshot(wallet, snapshot)` and feed
231
+ the outer `wallet_deposit_balances` channel's nested snapshot, absolute SPL,
232
+ absolute native-SOL, and status events to `applyEvent()`. Complete snapshots
233
+ replace state even after a higher component slot; status and wrong-wallet events
234
+ do not mutate it, pre-baseline component updates are ignored, and explicit-zero
235
+ SPL updates remove their mint. Matching SPL updates with invalid or negative
236
+ idle balances return `rejected` without mutation. `contextSlot` records the latest accepted
237
+ component observation rather than enforcing global monotonic ordering.
238
+ `combinedSolBalance()` sums native SOL and canonical WSOL with `bigint` precision
239
+ while retaining both stored values. REST response types are trusted rather than
240
+ runtime-decoded; WebSocket frames are validated strictly, while malformed REST
241
+ exact values fail when a state method scales them. The reducer owns its map
242
+ container but retains balance objects by reference, so treat applied payloads as
243
+ immutable.
244
+
245
+ `planSolSplit`, `planSolMerge`, `planSolRedeem`, and
246
+ `planNativeSolWithdrawal` return unsigned action plans with live fee/rent costs,
247
+ the action-specific reserve and spendable balance, and separate expected native
248
+ and canonical WSOL deltas. Each planner requires complete matching-wallet state,
249
+ checks the canonical account when needed, and fails closed when RPC estimates or
250
+ native reserve are unavailable. Unsponsored actions reserve the greater of live
251
+ costs and the applicable 0.001 SOL or 0.0035 SOL floor. Sponsored planning is
252
+ rejected until a concrete sponsor owns transaction fees and account rent.
253
+ An occupied canonical address is accepted only when it decodes as the wallet's
254
+ initialized, unfrozen Tokenkeg native-mint account.
255
+
256
+ Split plans consume canonical WSOL first and wrap only a shortfall in the same
257
+ transaction. Merge and redeem plans retain proceeds in the persistent canonical
258
+ account. Native withdrawal transfers directly when possible; otherwise it moves
259
+ only the shortfall through a bounded seeded temporary Tokenkeg account and closes
260
+ that temporary account before sending the exact native amount to the recipient.
261
+ The temporary account's create, initialize, WSOL transfer, close, and native
262
+ transfer instructions share one Solana transaction, so an instruction failure
263
+ rolls the entire conversion back atomically. No planner closes the canonical
264
+ account implicitly.
265
+
266
+ Native-keypair self-custody users can explicitly call `planWrapSol` with a
267
+ positive exact `bigint` lamport amount or call no-amount `planUnwrapWsolAll`.
268
+ These standalone planners require the authenticated Trading Wallet's local
269
+ native signing strategy; wallet-adapter and Privy strategies are rejected before
270
+ RPC planning. Wrap creates or reuses only that wallet's canonical Tokenkeg ATA,
271
+ transfers the exact amount, and runs `SyncNative`, the Token Program instruction
272
+ that recalculates the WSOL token amount from account lamports. Wrap retains the
273
+ ordinary 0.0035 SOL account-creation or 0.001 SOL existing-account reserve floor
274
+ above lower live costs. Live decoded canonical amounts must match the authoritative
275
+ wallet state. Wrap also requires an existing account's full lamports to equal
276
+ its decoded token amount plus native rent reserve; unsynchronized direct
277
+ donations reject before `SyncNative` can make the projected canonical delta
278
+ inexact. Unwrap-all still accepts such excess and returns it on close.
279
+
280
+ Unwrap-all accepts no partial amount and closes the entire positive canonical
281
+ account back to the same wallet. Its `SolActionCosts` fields contain the fresh fee, zero
282
+ upfront rent, no account creation, and no sponsorship. Availability reserves
283
+ only that fee rather than the ordinary persistent-account floor;
284
+ `unwrapAllSolBalanceAvailability(components, costs)` validates that complete
285
+ cost tuple before deriving fee-only fields. The
286
+ native delta credits every live account lamport, including refunded rent or
287
+ direct donations, minus the fee and removes the full canonical token amount.
288
+ Closing a pre-existing account returns all of its WSOL and means a future WSOL
289
+ action may need to fund account rent again. Ordinary split, merge, redeem,
290
+ claim, order, and native-withdraw paths never call these conversion planners or
291
+ close canonical WSOL.
292
+
293
+ For either explicit conversion, rebuild immediately before signing,
294
+ submit with `signAndSubmitPreparedTxConfirmedWithSlot` so the wallet cannot
295
+ replace the fee-estimated message, and refresh a complete snapshot covering its
296
+ slot before restoring action authority. Prepared submission is unavailable for
297
+ Privy because its final signed bytes cannot be verified by the SDK. Atomic
298
+ execution does not resolve uncertain submission or confirmation errors; inspect
299
+ authoritative balances before retrying. See the
300
+ [persistent canonical WSOL ADR](../docs/adr/0001-persistent-canonical-wsol.md).
301
+
302
+ WebSocket clients are owned independently from `Auth`; logout does not clean them
303
+ up. For each retained client, `clearAuthedSubscriptions()` purges User/wallet
304
+ reconnect tracking and queued authenticated messages. It does not stop an
305
+ already-open server stream; send the matching unsubscribe or disconnect the socket
306
+ for live teardown.
307
+
308
+ The [`deposit_token_balances`](examples/deposit_token_balances.ts) self-custody
309
+ example is manual-only and runs with `LIGHTCONE_ENV=local` or `staging` only
310
+ when `SDK_API_URL`, `SDK_WS_URL`, `SDK_RPC_URL`, and `SDK_PROGRAM_ID` are all
311
+ unset. It sends 0.001 SOL to the Python SDK wallet configured by
312
+ `LIGHTCONE_WALLET_PATH_PYTHON` from the distinct sender configured by
313
+ `LIGHTCONE_WALLET_PATH_TS`, confirms with a slot, and refreshes a complete
314
+ snapshot at that slot. Running it moves funds. If it fails after submission,
315
+ inspect authoritative balances before retrying because funds may already have moved.
316
+
317
+ The separate [`wsol_conversion`](examples/wsol_conversion.ts) example runs with
318
+ the TypeScript wallet in local aggregate runs and is included when the globally
319
+ gated stateful example workflow is enabled for staging CI; that workflow
320
+ currently disables all stateful CI jobs. Local runs may use a paid RPC while
321
+ retaining built-in API, WebSocket, and program identity; an enabled staging-CI
322
+ run may supply its managed endpoints. Direct staging runs remain override-free.
323
+ It permits a pre-existing canonical balance, wraps exactly 0.001 SOL, prints the
324
+ exact wallet, costs, full-account return, and future-rent warning, then unwraps
325
+ the complete canonical account without pausing. It retains each frozen
326
+ projection until a complete REST snapshot covers the confirmed slot. Any
327
+ planning, signing, submission, or uncertain-confirmation failure exits without
328
+ automatic retry.
201
329
 
202
330
  ### Step 5: Cancel an Order
203
331
 
@@ -240,9 +368,17 @@ const withdrawIx = client.positions().withdraw()
240
368
 
241
369
  Authentication is only required for user-specific endpoints. Authentication is session-based using ED25519 signed messages. The flow is: request a nonce, sign it with your wallet, and exchange it for a session cookie.
242
370
 
371
+ Privy hosts can also authenticate with passwordless Email, Google, X, or Wallet. After every interactive success, call `client.auth().registerPrivy({ attempted_identity })`. The backend validates the exact selector against Privy's verified methods, creates or synchronizes the Account, and changes the Primary Login Identity only for a new Account. `session.user.identity` is that stable primary; `session.user.linked_identities` contains every connected method with primary first.
372
+
243
373
  Use `walletDisplayName(session.user, session.auth_method)` to show a shortened
244
374
  label for the wallet the session trades with, regardless of login identity.
245
375
 
376
+ `session.user.max_slippage_preference` is an exact decimal string strictly below
377
+ `10`, or `null` until one is stored. Persist a value greater than zero and less
378
+ than 10 using `client.auth().updateMaxSlippagePreference(value)`; the method
379
+ returns the canonical exact decimal string. Values at or above 10% remain valid
380
+ order protection but are not remembered through this API.
381
+
246
382
  ### Cookie handling
247
383
 
248
384
  After login succeeds, the SDK stores the session token internally and attaches it as `Cookie: lightcone-token=…` on every authenticated request. Behaviour depends on the runtime:
@@ -348,6 +484,16 @@ All examples are runnable with `npx tsx examples/<name>.ts`. Examples default to
348
484
  | [`onchain_transactions`](examples/onchain_transactions.ts) | Build, sign, and submit mint/merge complete set and increment nonce on-chain |
349
485
  | [`global_deposit_withdrawal`](examples/global_deposit_withdrawal.ts) | Init position tokens, deposit to global pool, move capital into a market, extend an existing ALT, withdraw from global, and merge back to keep the run net-neutral |
350
486
 
487
+ ### Manual Fund-Moving Operations
488
+
489
+ These examples refuse production and endpoint overrides and are intentionally
490
+ excluded from routine example runs.
491
+
492
+ | Example | Description |
493
+ |---------|-------------|
494
+ | [`deposit_token_balances`](examples/deposit_token_balances.ts) | Confirm an exact native withdrawal without closing canonical WSOL, then refresh complete state past the confirmed slot |
495
+ | [`wsol_conversion`](examples/wsol_conversion.ts) | Wrap an exact native amount, warn, close and unwrap the complete canonical account, and hold frozen projections through covering refreshes |
496
+
351
497
  ### WebSocket Streaming
352
498
 
353
499
  | Example | Description |
@@ -385,6 +531,7 @@ When the backend rejects a request (insufficient balance, expired order, etc.),
385
531
  | `errorCode` | `string \| undefined` | API-level error code (for example `"NOT_FOUND"` or `"INVALID_ARGUMENT"`) |
386
532
  | `errorLogId` | `string \| undefined` | Backend support correlation ID (`LCERR_*`) |
387
533
  | `requestId` | `string \| undefined` | SDK-generated `x-request-id` for cross-service tracing |
534
+ | `existingMethod` | `string \| undefined` | Primary method of the conflicting Account when identity ownership has one deterministic owner |
388
535
 
389
536
  `ApiRejectedDetails.toString()` formats all present fields as a multi-line report for logs or support tickets.
390
537
 
@@ -1,5 +1,17 @@
1
1
  import { type LightconeHttp } from "../http";
2
- import type { AuthCredentials, SessionResponse } from "./index";
2
+ import type { AuthCredentials, LinkedIdentityType, RegisterPrivyRequest, SessionResponse } from "./index";
3
+ /** Bounded ownership conflicts safe for client recovery guidance. */
4
+ export type RegisterPrivyConflict = {
5
+ code: "IDENTITY_OWNED_BY_ANOTHER_ACCOUNT";
6
+ existingMethod?: LinkedIdentityType;
7
+ } | {
8
+ code: "IDENTITIES_OWNED_BY_MULTIPLE_ACCOUNTS";
9
+ } | {
10
+ code: "WALLET_OWNED_BY_ANOTHER_ACCOUNT";
11
+ existingMethod?: LinkedIdentityType;
12
+ };
13
+ /** Classify only stable register-or-sync ownership rejection codes. */
14
+ export declare function classifyRegisterPrivyConflict(error: unknown): RegisterPrivyConflict | undefined;
3
15
  interface AuthState {
4
16
  getCredentials(): AuthCredentials | undefined;
5
17
  setCredentials(credentials: AuthCredentials | undefined): void;
@@ -27,6 +39,10 @@ export declare class Auth {
27
39
  */
28
40
  logout(): Promise<void>;
29
41
  disconnectX(): Promise<void>;
42
+ /** Create or synchronize a Privy Account and install the resulting session. */
43
+ registerPrivy(request: RegisterPrivyRequest): Promise<SessionResponse>;
44
+ /** Persist an account-wide max-slippage preference strictly below 10%. */
45
+ updateMaxSlippagePreference(maxSlippagePreference: string): Promise<string>;
30
46
  connectXUrl(): string;
31
47
  credentials(): AuthCredentials | undefined;
32
48
  isAuthenticated(): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/auth/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAG1D,OAAO,KAAK,EAAE,eAAe,EAA+B,eAAe,EAAE,MAAM,SAAS,CAAC;AAE7F,UAAU,SAAS;IACjB,cAAc,IAAI,eAAe,GAAG,SAAS,CAAC;IAC9C,cAAc,CAAC,WAAW,EAAE,eAAe,GAAG,SAAS,GAAG,IAAI,CAAC;IAC/D,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,qBAAa,IAAI;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAE5C,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAM3B,gBAAgB,CACpB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,UAAU,EACvB,iBAAiB,CAAC,EAAE,OAAO,GAC1B,OAAO,CAAC,eAAe,CAAC;IAuBrB,YAAY,IAAI,OAAO,CAAC,eAAe,CAAC;IAgB9C;;;;;;;;;OASG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBvB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAKlC,WAAW,IAAI,MAAM;IAIrB,WAAW,IAAI,eAAe,GAAG,SAAS;IAI1C,eAAe,IAAI,OAAO;CAO3B"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/auth/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAG1D,OAAO,KAAK,EACV,eAAe,EAIf,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EAChB,MAAM,SAAS,CAAC;AAEjB,qEAAqE;AACrE,MAAM,MAAM,qBAAqB,GAC7B;IACE,IAAI,EAAE,mCAAmC,CAAC;IAC1C,cAAc,CAAC,EAAE,kBAAkB,CAAC;CACrC,GACD;IAAE,IAAI,EAAE,uCAAuC,CAAA;CAAE,GACjD;IACE,IAAI,EAAE,iCAAiC,CAAC;IACxC,cAAc,CAAC,EAAE,kBAAkB,CAAC;CACrC,CAAC;AAEN,uEAAuE;AACvE,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,OAAO,GACb,qBAAqB,GAAG,SAAS,CAoBnC;AAED,UAAU,SAAS;IACjB,cAAc,IAAI,eAAe,GAAG,SAAS,CAAC;IAC9C,cAAc,CAAC,WAAW,EAAE,eAAe,GAAG,SAAS,GAAG,IAAI,CAAC;IAC/D,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,qBAAa,IAAI;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAE5C,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAS3B,gBAAgB,CACpB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,UAAU,EACvB,iBAAiB,CAAC,EAAE,OAAO,GAC1B,OAAO,CAAC,eAAe,CAAC;IAwBrB,YAAY,IAAI,OAAO,CAAC,eAAe,CAAC;IAoB9C;;;;;;;;;OASG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBvB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IASlC,+EAA+E;IACzE,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAe5E,0EAA0E;IACpE,2BAA2B,CAC/B,qBAAqB,EAAE,MAAM,GAC5B,OAAO,CAAC,MAAM,CAAC;IAuBlB,WAAW,IAAI,MAAM;IAIrB,WAAW,IAAI,eAAe,GAAG,SAAS;IAI1C,eAAe,IAAI,OAAO;CAO3B"}
@@ -1,10 +1,33 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Auth = void 0;
4
+ exports.classifyRegisterPrivyConflict = classifyRegisterPrivyConflict;
4
5
  const error_1 = require("../error");
5
6
  const http_1 = require("../http");
6
7
  const shared_1 = require("../shared");
7
8
  const index_1 = require("./index");
9
+ /** Classify only stable register-or-sync ownership rejection codes. */
10
+ function classifyRegisterPrivyConflict(error) {
11
+ if (!(error instanceof error_1.SdkError) || error.variant !== "ApiRejected")
12
+ return undefined;
13
+ const details = error.apiRejectedDetails;
14
+ switch (details?.errorCode) {
15
+ case "IDENTITY_OWNED_BY_ANOTHER_ACCOUNT":
16
+ return {
17
+ code: details.errorCode,
18
+ existingMethod: details.existingMethod,
19
+ };
20
+ case "IDENTITIES_OWNED_BY_MULTIPLE_ACCOUNTS":
21
+ return { code: details.errorCode };
22
+ case "WALLET_OWNED_BY_ANOTHER_ACCOUNT":
23
+ return {
24
+ code: details.errorCode,
25
+ existingMethod: details.existingMethod,
26
+ };
27
+ default:
28
+ return undefined;
29
+ }
30
+ }
8
31
  class Auth {
9
32
  client;
10
33
  constructor(client) {
@@ -28,6 +51,7 @@ class Auth {
28
51
  // verifying the signature, so a replayed login deterministically fails —
29
52
  // and restoring credentials in order to log in is circular.
30
53
  const session = await this.client.http.postWithoutCredentialRestore(url, body, http_1.RetryPolicy.None);
54
+ normalizeSessionMaxSlippagePreference(session);
31
55
  this.client.authState.setCredentials(credentialsFromSession(session));
32
56
  return session;
33
57
  }
@@ -36,6 +60,7 @@ class Auth {
36
60
  let session;
37
61
  try {
38
62
  session = await this.client.http.get(url, http_1.RetryPolicy.Idempotent);
63
+ normalizeSessionMaxSlippagePreference(session);
39
64
  }
40
65
  catch (error) {
41
66
  this.client.authState.setCredentials(undefined);
@@ -78,6 +103,24 @@ class Auth {
78
103
  const url = `${this.client.http.baseUrl()}/api/auth/disconnect_x`;
79
104
  await this.client.http.post(url, {}, http_1.RetryPolicy.None);
80
105
  }
106
+ /** Create or synchronize a Privy Account and install the resulting session. */
107
+ async registerPrivy(request) {
108
+ const url = `${this.client.http.baseUrl()}/api/auth/register-privy`;
109
+ const session = await this.client.http.post(url, request, http_1.RetryPolicy.Idempotent);
110
+ normalizeSessionMaxSlippagePreference(session);
111
+ this.client.authState.setCredentials(credentialsFromSession(session));
112
+ return session;
113
+ }
114
+ /** Persist an account-wide max-slippage preference strictly below 10%. */
115
+ async updateMaxSlippagePreference(maxSlippagePreference) {
116
+ const url = `${this.client.http.baseUrl()}/api/auth/max_slippage_preference`;
117
+ const response = await this.client.http.post(url, { max_slippage_preference: maxSlippagePreference }, http_1.RetryPolicy.Idempotent);
118
+ const preference = decodeMaxSlippagePreference(response, false, "update response");
119
+ if (preference === null) {
120
+ throw error_1.SdkError.serde("Max-slippage update response must contain a decimal string");
121
+ }
122
+ return preference;
123
+ }
81
124
  connectXUrl() {
82
125
  return `${this.client.http.baseUrl()}/api/auth/oauth/link/x`;
83
126
  }
@@ -93,6 +136,31 @@ class Auth {
93
136
  }
94
137
  }
95
138
  exports.Auth = Auth;
139
+ function normalizeSessionMaxSlippagePreference(session) {
140
+ const preference = decodeMaxSlippagePreference(session?.user, true, "session user", true);
141
+ session.user.max_slippage_preference = preference;
142
+ }
143
+ /** Enforces the exact nullable/string JSON contract erased by TypeScript types. */
144
+ function decodeMaxSlippagePreference(payload, allowNull, context, allowMissing = false) {
145
+ if (typeof payload !== "object" || payload === null) {
146
+ throw error_1.SdkError.serde(`Max-slippage ${context} is malformed`);
147
+ }
148
+ if (!Object.prototype.hasOwnProperty.call(payload, "max_slippage_preference")) {
149
+ if (allowMissing) {
150
+ return null;
151
+ }
152
+ throw error_1.SdkError.serde(`Max-slippage ${context} is missing max_slippage_preference`);
153
+ }
154
+ const preference = payload
155
+ .max_slippage_preference;
156
+ if (typeof preference === "string") {
157
+ return preference;
158
+ }
159
+ if (allowNull && preference === null) {
160
+ return null;
161
+ }
162
+ throw error_1.SdkError.serde(`Max-slippage ${context} must contain ${allowNull ? "a string or null" : "a string"}`);
163
+ }
96
164
  /**
97
165
  * Derive session credentials from the envelope. The trading wallet comes from
98
166
  * the identity + auth method.
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/auth/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { SdkError, isUnauthorized } from \"../error\";\nimport { RetryPolicy, type LightconeHttp } from \"../http\";\nimport { asPubkeyStr } from \"../shared\";\nimport { tradingWallet } from \"./index\";\nimport type { AuthCredentials, LoginRequest, NonceResponse, SessionResponse } from \"./index\";\n\ninterface AuthState {\n getCredentials(): AuthCredentials | undefined;\n setCredentials(credentials: AuthCredentials | undefined): void;\n clearCaches(): Promise<void>;\n}\n\ninterface ClientContext {\n http: LightconeHttp;\n authState: AuthState;\n}\n\nexport class Auth {\n constructor(private readonly client: ClientContext) {}\n\n async getNonce(): Promise<string> {\n const url = `${this.client.http.baseUrl()}/api/auth/nonce`;\n const response = await this.client.http.get<NonceResponse>(url, RetryPolicy.None);\n return response.nonce;\n }\n\n async loginWithMessage(\n message: string,\n signatureBs58: string,\n pubkeyBytes: Uint8Array,\n useEmbeddedWallet?: boolean\n ): Promise<SessionResponse> {\n const url = `${this.client.http.baseUrl()}/api/auth/login_or_register_with_message`;\n const body: LoginRequest = {\n message,\n signature_bs58: signatureBs58,\n pubkey_bytes: Array.from(pubkeyBytes),\n use_embedded_wallet: useEmbeddedWallet,\n };\n\n // Credential-management endpoint: opts out of the transport's 401\n // restore-and-replay. The backend consumes the login nonce before\n // verifying the signature, so a replayed login deterministically fails —\n // and restoring credentials in order to log in is circular.\n const session = await this.client.http.postWithoutCredentialRestore<\n SessionResponse,\n LoginRequest\n >(url, body, RetryPolicy.None);\n\n this.client.authState.setCredentials(credentialsFromSession(session));\n\n return session;\n }\n\n async checkSession(): Promise<SessionResponse> {\n const url = `${this.client.http.baseUrl()}/api/auth/me`;\n\n let session: SessionResponse;\n try {\n session = await this.client.http.get<SessionResponse>(url, RetryPolicy.Idempotent);\n } catch (error) {\n this.client.authState.setCredentials(undefined);\n throw SdkError.from(error);\n }\n\n this.client.authState.setCredentials(credentialsFromSession(session));\n\n return session;\n }\n\n /**\n * Logout — clears the server-side cookie, internal token, and credentials.\n *\n * Local state is cleared even when the server call fails — the caller asked\n * to be signed out locally regardless — but the failure is then rethrown:\n * callers gating security decisions on teardown (e.g. whether an app may\n * restart an authenticated transport) must be able to see that the\n * server-side cookie may still be valid. A 401 counts as success: it means\n * \"already logged out\".\n */\n async logout(): Promise<void> {\n const url = `${this.client.http.baseUrl()}/api/auth/logout`;\n let logoutError: unknown = null;\n try {\n // Credential-management endpoint: opts out of the transport's 401\n // restore-and-replay — a 401 here means \"already logged out\".\n await this.client.http.postWithoutCredentialRestore<\n { success: boolean },\n Record<string, never>\n >(url, {}, RetryPolicy.None);\n } catch (error) {\n if (!isUnauthorized(error)) {\n logoutError = error;\n }\n }\n\n await this.client.http.clearAuthToken();\n this.client.authState.setCredentials(undefined);\n await this.client.authState.clearCaches();\n\n if (logoutError !== null) {\n throw logoutError;\n }\n }\n\n async disconnectX(): Promise<void> {\n const url = `${this.client.http.baseUrl()}/api/auth/disconnect_x`;\n await this.client.http.post<{ success: boolean }, Record<string, never>>(url, {}, RetryPolicy.None);\n }\n\n connectXUrl(): string {\n return `${this.client.http.baseUrl()}/api/auth/oauth/link/x`;\n }\n\n credentials(): AuthCredentials | undefined {\n return this.client.authState.getCredentials();\n }\n\n isAuthenticated(): boolean {\n const credentials = this.credentials();\n if (!credentials) {\n return false;\n }\n return Date.now() < credentials.expires_at.getTime();\n }\n}\n\n/**\n * Derive session credentials from the envelope. The trading wallet comes from\n * the identity + auth method.\n */\nfunction credentialsFromSession(session: SessionResponse): AuthCredentials {\n return {\n user_id: session.user.user_id,\n wallet_address: asPubkeyStr(tradingWallet(session.user, session.auth_method)),\n expires_at: parseExpiry(session.expires_at),\n };\n}\n\nfunction parseExpiry(timestamp: number): Date {\n if (timestamp > 1_000_000_000_000) {\n return new Date(timestamp);\n }\n return new Date(timestamp * 1000);\n}\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/auth/client.ts"],"names":[],"mappings":";;;AA2BA,sEAsBC;AAjDD,oCAAoD;AACpD,kCAA0D;AAC1D,sCAAwC;AACxC,mCAAwC;AAuBxC,uEAAuE;AACvE,SAAgB,6BAA6B,CAC3C,KAAc;IAEd,IAAI,CAAC,CAAC,KAAK,YAAY,gBAAQ,CAAC,IAAI,KAAK,CAAC,OAAO,KAAK,aAAa;QACjE,OAAO,SAAS,CAAC;IACnB,MAAM,OAAO,GAAG,KAAK,CAAC,kBAAkB,CAAC;IACzC,QAAQ,OAAO,EAAE,SAAS,EAAE,CAAC;QAC3B,KAAK,mCAAmC;YACtC,OAAO;gBACL,IAAI,EAAE,OAAO,CAAC,SAAS;gBACvB,cAAc,EAAE,OAAO,CAAC,cAAc;aACvC,CAAC;QACJ,KAAK,uCAAuC;YAC1C,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;QACrC,KAAK,iCAAiC;YACpC,OAAO;gBACL,IAAI,EAAE,OAAO,CAAC,SAAS;gBACvB,cAAc,EAAE,OAAO,CAAC,cAAc;aACvC,CAAC;QACJ;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAaD,MAAa,IAAI;IACc;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,QAAQ;QACZ,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC;QAC3D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CACzC,GAAG,EACH,kBAAW,CAAC,IAAI,CACjB,CAAC;QACF,OAAO,QAAQ,CAAC,KAAK,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,OAAe,EACf,aAAqB,EACrB,WAAuB,EACvB,iBAA2B;QAE3B,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,0CAA0C,CAAC;QACpF,MAAM,IAAI,GAAiB;YACzB,OAAO;YACP,cAAc,EAAE,aAAa;YAC7B,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC;YACrC,mBAAmB,EAAE,iBAAiB;SACvC,CAAC;QAEF,kEAAkE;QAClE,kEAAkE;QAClE,yEAAyE;QACzE,4DAA4D;QAC5D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAGjE,GAAG,EAAE,IAAI,EAAE,kBAAW,CAAC,IAAI,CAAC,CAAC;QAC/B,qCAAqC,CAAC,OAAO,CAAC,CAAC;QAE/C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC;QAEtE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC;QAExD,IAAI,OAAwB,CAAC;QAC7B,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAClC,GAAG,EACH,kBAAW,CAAC,UAAU,CACvB,CAAC;YACF,qCAAqC,CAAC,OAAO,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;YAChD,MAAM,gBAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC;QAEtE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,MAAM;QACV,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC;QAC5D,IAAI,WAAW,GAAY,IAAI,CAAC;QAChC,IAAI,CAAC;YACH,kEAAkE;YAClE,8DAA8D;YAC9D,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAGjD,GAAG,EAAE,EAAE,EAAE,kBAAW,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,IAAA,sBAAc,EAAC,KAAK,CAAC,EAAE,CAAC;gBAC3B,WAAW,GAAG,KAAK,CAAC;YACtB,CAAC;QACH,CAAC;QAED,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QAChD,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QAE1C,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,MAAM,WAAW,CAAC;QACpB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,wBAAwB,CAAC;QAClE,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CACzB,GAAG,EACH,EAAE,EACF,kBAAW,CAAC,IAAI,CACjB,CAAC;IACJ,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAC,aAAa,CAAC,OAA6B;QAC/C,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,0BAA0B,CAAC;QACpE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAIzC,GAAG,EACH,OAAO,EACP,kBAAW,CAAC,UAAU,CACvB,CAAC;QACF,qCAAqC,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC;QACtE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,0EAA0E;IAC1E,KAAK,CAAC,2BAA2B,CAC/B,qBAA6B;QAE7B,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,mCAAmC,CAAC;QAC7E,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAI1C,GAAG,EACH,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,EAClD,kBAAW,CAAC,UAAU,CACvB,CAAC;QACF,MAAM,UAAU,GAAG,2BAA2B,CAC5C,QAAQ,EACR,KAAK,EACL,iBAAiB,CAClB,CAAC;QACF,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,MAAM,gBAAQ,CAAC,KAAK,CAClB,4DAA4D,CAC7D,CAAC;QACJ,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,WAAW;QACT,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,wBAAwB,CAAC;IAC/D,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,EAAE,CAAC;IAChD,CAAC;IAED,eAAe;QACb,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACvC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;IACvD,CAAC;CACF;AAlKD,oBAkKC;AAED,SAAS,qCAAqC,CAAC,OAAwB;IACrE,MAAM,UAAU,GAAG,2BAA2B,CAC5C,OAAO,EAAE,IAAI,EACb,IAAI,EACJ,cAAc,EACd,IAAI,CACL,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,uBAAuB,GAAG,UAAU,CAAC;AACpD,CAAC;AAED,mFAAmF;AACnF,SAAS,2BAA2B,CAClC,OAAgB,EAChB,SAAkB,EAClB,OAAe,EACf,YAAY,GAAG,KAAK;IAEpB,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACpD,MAAM,gBAAQ,CAAC,KAAK,CAAC,gBAAgB,OAAO,eAAe,CAAC,CAAC;IAC/D,CAAC;IACD,IACE,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,yBAAyB,CAAC,EACzE,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,gBAAQ,CAAC,KAAK,CAClB,gBAAgB,OAAO,qCAAqC,CAC7D,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAI,OAAmC;SACpD,uBAAuB,CAAC;IAC3B,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QACnC,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,IAAI,SAAS,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,gBAAQ,CAAC,KAAK,CAClB,gBAAgB,OAAO,iBAAiB,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,UAAU,EAAE,CACtF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,sBAAsB,CAAC,OAAwB;IACtD,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO;QAC7B,cAAc,EAAE,IAAA,oBAAW,EACzB,IAAA,qBAAa,EAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,CACjD;QACD,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC;KAC5C,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,SAAiB;IACpC,IAAI,SAAS,GAAG,iBAAiB,EAAE,CAAC;QAClC,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;AACpC,CAAC","sourcesContent":["import { SdkError, isUnauthorized } from \"../error\";\nimport { RetryPolicy, type LightconeHttp } from \"../http\";\nimport { asPubkeyStr } from \"../shared\";\nimport { tradingWallet } from \"./index\";\nimport type {\n AuthCredentials,\n LoginRequest,\n MaxSlippagePreferenceBody,\n NonceResponse,\n LinkedIdentityType,\n RegisterPrivyRequest,\n SessionResponse,\n} from \"./index\";\n\n/** Bounded ownership conflicts safe for client recovery guidance. */\nexport type RegisterPrivyConflict =\n | {\n code: \"IDENTITY_OWNED_BY_ANOTHER_ACCOUNT\";\n existingMethod?: LinkedIdentityType;\n }\n | { code: \"IDENTITIES_OWNED_BY_MULTIPLE_ACCOUNTS\" }\n | {\n code: \"WALLET_OWNED_BY_ANOTHER_ACCOUNT\";\n existingMethod?: LinkedIdentityType;\n };\n\n/** Classify only stable register-or-sync ownership rejection codes. */\nexport function classifyRegisterPrivyConflict(\n error: unknown,\n): RegisterPrivyConflict | undefined {\n if (!(error instanceof SdkError) || error.variant !== \"ApiRejected\")\n return undefined;\n const details = error.apiRejectedDetails;\n switch (details?.errorCode) {\n case \"IDENTITY_OWNED_BY_ANOTHER_ACCOUNT\":\n return {\n code: details.errorCode,\n existingMethod: details.existingMethod,\n };\n case \"IDENTITIES_OWNED_BY_MULTIPLE_ACCOUNTS\":\n return { code: details.errorCode };\n case \"WALLET_OWNED_BY_ANOTHER_ACCOUNT\":\n return {\n code: details.errorCode,\n existingMethod: details.existingMethod,\n };\n default:\n return undefined;\n }\n}\n\ninterface AuthState {\n getCredentials(): AuthCredentials | undefined;\n setCredentials(credentials: AuthCredentials | undefined): void;\n clearCaches(): Promise<void>;\n}\n\ninterface ClientContext {\n http: LightconeHttp;\n authState: AuthState;\n}\n\nexport class Auth {\n constructor(private readonly client: ClientContext) {}\n\n async getNonce(): Promise<string> {\n const url = `${this.client.http.baseUrl()}/api/auth/nonce`;\n const response = await this.client.http.get<NonceResponse>(\n url,\n RetryPolicy.None,\n );\n return response.nonce;\n }\n\n async loginWithMessage(\n message: string,\n signatureBs58: string,\n pubkeyBytes: Uint8Array,\n useEmbeddedWallet?: boolean,\n ): Promise<SessionResponse> {\n const url = `${this.client.http.baseUrl()}/api/auth/login_or_register_with_message`;\n const body: LoginRequest = {\n message,\n signature_bs58: signatureBs58,\n pubkey_bytes: Array.from(pubkeyBytes),\n use_embedded_wallet: useEmbeddedWallet,\n };\n\n // Credential-management endpoint: opts out of the transport's 401\n // restore-and-replay. The backend consumes the login nonce before\n // verifying the signature, so a replayed login deterministically fails —\n // and restoring credentials in order to log in is circular.\n const session = await this.client.http.postWithoutCredentialRestore<\n SessionResponse,\n LoginRequest\n >(url, body, RetryPolicy.None);\n normalizeSessionMaxSlippagePreference(session);\n\n this.client.authState.setCredentials(credentialsFromSession(session));\n\n return session;\n }\n\n async checkSession(): Promise<SessionResponse> {\n const url = `${this.client.http.baseUrl()}/api/auth/me`;\n\n let session: SessionResponse;\n try {\n session = await this.client.http.get<SessionResponse>(\n url,\n RetryPolicy.Idempotent,\n );\n normalizeSessionMaxSlippagePreference(session);\n } catch (error) {\n this.client.authState.setCredentials(undefined);\n throw SdkError.from(error);\n }\n\n this.client.authState.setCredentials(credentialsFromSession(session));\n\n return session;\n }\n\n /**\n * Logout — clears the server-side cookie, internal token, and credentials.\n *\n * Local state is cleared even when the server call fails — the caller asked\n * to be signed out locally regardless — but the failure is then rethrown:\n * callers gating security decisions on teardown (e.g. whether an app may\n * restart an authenticated transport) must be able to see that the\n * server-side cookie may still be valid. A 401 counts as success: it means\n * \"already logged out\".\n */\n async logout(): Promise<void> {\n const url = `${this.client.http.baseUrl()}/api/auth/logout`;\n let logoutError: unknown = null;\n try {\n // Credential-management endpoint: opts out of the transport's 401\n // restore-and-replay — a 401 here means \"already logged out\".\n await this.client.http.postWithoutCredentialRestore<\n { success: boolean },\n Record<string, never>\n >(url, {}, RetryPolicy.None);\n } catch (error) {\n if (!isUnauthorized(error)) {\n logoutError = error;\n }\n }\n\n await this.client.http.clearAuthToken();\n this.client.authState.setCredentials(undefined);\n await this.client.authState.clearCaches();\n\n if (logoutError !== null) {\n throw logoutError;\n }\n }\n\n async disconnectX(): Promise<void> {\n const url = `${this.client.http.baseUrl()}/api/auth/disconnect_x`;\n await this.client.http.post<{ success: boolean }, Record<string, never>>(\n url,\n {},\n RetryPolicy.None,\n );\n }\n\n /** Create or synchronize a Privy Account and install the resulting session. */\n async registerPrivy(request: RegisterPrivyRequest): Promise<SessionResponse> {\n const url = `${this.client.http.baseUrl()}/api/auth/register-privy`;\n const session = await this.client.http.post<\n SessionResponse,\n RegisterPrivyRequest\n >(\n url,\n request,\n RetryPolicy.Idempotent,\n );\n normalizeSessionMaxSlippagePreference(session);\n this.client.authState.setCredentials(credentialsFromSession(session));\n return session;\n }\n\n /** Persist an account-wide max-slippage preference strictly below 10%. */\n async updateMaxSlippagePreference(\n maxSlippagePreference: string,\n ): Promise<string> {\n const url = `${this.client.http.baseUrl()}/api/auth/max_slippage_preference`;\n const response = await this.client.http.post<\n MaxSlippagePreferenceBody,\n MaxSlippagePreferenceBody\n >(\n url,\n { max_slippage_preference: maxSlippagePreference },\n RetryPolicy.Idempotent,\n );\n const preference = decodeMaxSlippagePreference(\n response,\n false,\n \"update response\",\n );\n if (preference === null) {\n throw SdkError.serde(\n \"Max-slippage update response must contain a decimal string\",\n );\n }\n return preference;\n }\n\n connectXUrl(): string {\n return `${this.client.http.baseUrl()}/api/auth/oauth/link/x`;\n }\n\n credentials(): AuthCredentials | undefined {\n return this.client.authState.getCredentials();\n }\n\n isAuthenticated(): boolean {\n const credentials = this.credentials();\n if (!credentials) {\n return false;\n }\n return Date.now() < credentials.expires_at.getTime();\n }\n}\n\nfunction normalizeSessionMaxSlippagePreference(session: SessionResponse): void {\n const preference = decodeMaxSlippagePreference(\n session?.user,\n true,\n \"session user\",\n true,\n );\n session.user.max_slippage_preference = preference;\n}\n\n/** Enforces the exact nullable/string JSON contract erased by TypeScript types. */\nfunction decodeMaxSlippagePreference(\n payload: unknown,\n allowNull: boolean,\n context: string,\n allowMissing = false,\n): string | null {\n if (typeof payload !== \"object\" || payload === null) {\n throw SdkError.serde(`Max-slippage ${context} is malformed`);\n }\n if (\n !Object.prototype.hasOwnProperty.call(payload, \"max_slippage_preference\")\n ) {\n if (allowMissing) {\n return null;\n }\n throw SdkError.serde(\n `Max-slippage ${context} is missing max_slippage_preference`,\n );\n }\n\n const preference = (payload as Record<string, unknown>)\n .max_slippage_preference;\n if (typeof preference === \"string\") {\n return preference;\n }\n if (allowNull && preference === null) {\n return null;\n }\n throw SdkError.serde(\n `Max-slippage ${context} must contain ${allowNull ? \"a string or null\" : \"a string\"}`,\n );\n}\n\n/**\n * Derive session credentials from the envelope. The trading wallet comes from\n * the identity + auth method.\n */\nfunction credentialsFromSession(session: SessionResponse): AuthCredentials {\n return {\n user_id: session.user.user_id,\n wallet_address: asPubkeyStr(\n tradingWallet(session.user, session.auth_method),\n ),\n expires_at: parseExpiry(session.expires_at),\n };\n}\n\nfunction parseExpiry(timestamp: number): Date {\n if (timestamp > 1_000_000_000_000) {\n return new Date(timestamp);\n }\n return new Date(timestamp * 1000);\n}\n"]}
@@ -48,6 +48,46 @@ export interface GoogleAccountData {
48
48
  family_name?: string;
49
49
  avatar_url?: string;
50
50
  }
51
+ /** Canonical address for a passwordless Email login identity. */
52
+ export interface EmailAccountData {
53
+ email: string;
54
+ }
55
+ /** Public login-method names exposed in profiles and bounded conflicts. */
56
+ export type LinkedIdentityType = "email" | "google" | "x" | "wallet";
57
+ /** Connected Login Identity without repeated Account-level Privy wallet data. */
58
+ export type LinkedIdentity = {
59
+ type: "email";
60
+ account: EmailAccountData;
61
+ } | {
62
+ type: "google";
63
+ account: GoogleAccountData;
64
+ } | {
65
+ type: "x";
66
+ account: XAccountData;
67
+ } | {
68
+ type: "wallet";
69
+ address: string;
70
+ chain: ChainType;
71
+ };
72
+ /** Verified method and canonical identifier that initiated Privy authentication. */
73
+ export type LinkedIdentitySelector = {
74
+ type: "email";
75
+ email: string;
76
+ } | {
77
+ type: "google";
78
+ email: string;
79
+ } | {
80
+ type: "x";
81
+ username: string;
82
+ } | {
83
+ type: "wallet";
84
+ address: string;
85
+ chain: ChainType;
86
+ };
87
+ /** Register-or-sync request naming the verified attempted identity. */
88
+ export interface RegisterPrivyRequest {
89
+ attempted_identity: LinkedIdentitySelector;
90
+ }
51
91
  /**
52
92
  * The login identity — how the user authenticates. `type` narrows the variant.
53
93
  *
@@ -57,6 +97,10 @@ export interface GoogleAccountData {
57
97
  * self-custody.
58
98
  */
59
99
  export type UserIdentity = {
100
+ type: "email";
101
+ account: EmailAccountData;
102
+ privy: UserPrivyData;
103
+ } | {
60
104
  type: "google";
61
105
  account: GoogleAccountData;
62
106
  privy: UserPrivyData;
@@ -74,6 +118,10 @@ export type UserIdentity = {
74
118
  export interface User {
75
119
  user_id: string;
76
120
  identity: UserIdentity;
121
+ /** Every verified login identity connected to the Account, primary first. */
122
+ linked_identities?: LinkedIdentity[];
123
+ /** Remembered account-wide percentage below 10%; null until one is stored. */
124
+ max_slippage_preference: string | null;
77
125
  /** X account connected by a non-X-identity user; absent when identity is X. */
78
126
  connected_x?: XAccountData;
79
127
  }
@@ -88,8 +136,12 @@ export interface SessionResponse {
88
136
  auth_method: AuthMethod;
89
137
  is_beta: boolean;
90
138
  }
91
- /** Human-readable login-method label ("Google" / "X" / "Solana"). */
92
- export declare function identityText(identity: UserIdentity): "Google" | "X" | "Solana";
139
+ /** Exact decimal-string body used to update and return max slippage. */
140
+ export interface MaxSlippagePreferenceBody {
141
+ max_slippage_preference: string;
142
+ }
143
+ /** Human-readable login-method label ("Email" / "Google" / "X" / "Solana"). */
144
+ export declare function identityText(identity: UserIdentity): "Email" | "Google" | "X" | "Solana";
93
145
  /** Privy account data, regardless of identity type. */
94
146
  export declare function userPrivy(user: User): UserPrivyData | undefined;
95
147
  /** The X account, whether it is the login identity or a connected account. */
@@ -107,9 +159,9 @@ export declare function tradingWallet(user: User, authMethod: AuthMethod): strin
107
159
  /** Short display label for the wallet a session operates as. */
108
160
  export declare function walletDisplayName(user: User, authMethod: AuthMethod): string;
109
161
  /**
110
- * Best display name for the user. Google: `name`, falling back to the email;
111
- * X: `display_name`, falling back to the username; wallet identities show the
112
- * shortened address (`FRGk...WcPR`).
162
+ * Best display name for the user. Email addresses are limited to 20 characters;
163
+ * Google uses `name` with an email fallback; X uses `display_name` with a
164
+ * username fallback; wallet identities show the shortened address (`FRGk...WcPR`).
113
165
  */
114
166
  export declare function displayName(user: User): string;
115
167
  /** Avatar URL from the login identity's OAuth provider, if any. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"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"}
@@ -41,9 +41,11 @@ var ChainType;
41
41
  ChainType["Solana"] = "solana";
42
42
  ChainType["Ethereum"] = "ethereum";
43
43
  })(ChainType || (exports.ChainType = ChainType = {}));
44
- /** Human-readable login-method label ("Google" / "X" / "Solana"). */
44
+ /** Human-readable login-method label ("Email" / "Google" / "X" / "Solana"). */
45
45
  function identityText(identity) {
46
46
  switch (identity.type) {
47
+ case "email":
48
+ return "Email";
47
49
  case "google":
48
50
  return "Google";
49
51
  case "x":
@@ -59,6 +61,7 @@ function identityText(identity) {
59
61
  /** Privy account data, regardless of identity type. */
60
62
  function userPrivy(user) {
61
63
  switch (user.identity.type) {
64
+ case "email":
62
65
  case "google":
63
66
  case "x":
64
67
  return user.identity.privy;
@@ -81,6 +84,7 @@ function userXAccount(user) {
81
84
  */
82
85
  function tradingWallet(user, authMethod) {
83
86
  switch (user.identity.type) {
87
+ case "email":
84
88
  case "google":
85
89
  case "x":
86
90
  return user.identity.privy.wallet.address;
@@ -94,17 +98,32 @@ function tradingWallet(user, authMethod) {
94
98
  function walletDisplayName(user, authMethod) {
95
99
  return (0, str_1.shorten)(tradingWallet(user, authMethod), 8);
96
100
  }
101
+ /** Keeps Email labels compact while preserving the recognizable address ends. */
102
+ function emailDisplayName(email) {
103
+ const maxChars = 20;
104
+ const chars = [...email];
105
+ if (chars.length <= maxChars) {
106
+ return email;
107
+ }
108
+ const visibleChars = maxChars - 3;
109
+ const prefixChars = Math.floor(visibleChars / 2);
110
+ return `${chars.slice(0, prefixChars).join("")}...${chars
111
+ .slice(-(visibleChars - prefixChars))
112
+ .join("")}`;
113
+ }
97
114
  /**
98
- * Best display name for the user. Google: `name`, falling back to the email;
99
- * X: `display_name`, falling back to the username; wallet identities show the
100
- * shortened address (`FRGk...WcPR`).
115
+ * Best display name for the user. Email addresses are limited to 20 characters;
116
+ * Google uses `name` with an email fallback; X uses `display_name` with a
117
+ * username fallback; wallet identities show the shortened address (`FRGk...WcPR`).
101
118
  */
102
119
  function displayName(user) {
103
120
  switch (user.identity.type) {
121
+ case "email":
122
+ return emailDisplayName(user.identity.account.email);
104
123
  case "google":
105
124
  return user.identity.account.name ?? user.identity.account.email;
106
125
  case "x":
107
- return user.identity.account.display_name ?? user.identity.account.username;
126
+ return (user.identity.account.display_name ?? user.identity.account.username);
108
127
  case "wallet":
109
128
  return (0, str_1.shorten)(user.identity.address, 8);
110
129
  }
@@ -112,6 +131,8 @@ function displayName(user) {
112
131
  /** Avatar URL from the login identity's OAuth provider, if any. */
113
132
  function avatarUrl(user) {
114
133
  switch (user.identity.type) {
134
+ case "email":
135
+ return undefined;
115
136
  case "google":
116
137
  case "x":
117
138
  return user.identity.account.avatar_url;