@lightconexyz/lightcone-sdk 0.8.1-rc.8 → 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.
- package/README.md +198 -10
- package/dist/auth/client.d.ts +27 -1
- package/dist/auth/client.d.ts.map +1 -1
- package/dist/auth/client.js +94 -4
- package/dist/auth/client.js.map +1 -1
- package/dist/auth/index.d.ts +59 -5
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +31 -5
- package/dist/auth/index.js.map +1 -1
- package/dist/client.d.ts +49 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +70 -2
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +59 -3
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +303 -6
- package/dist/context.js.map +1 -1
- package/dist/domain/market/client.d.ts +16 -0
- package/dist/domain/market/client.d.ts.map +1 -1
- package/dist/domain/market/client.js +33 -0
- package/dist/domain/market/client.js.map +1 -1
- package/dist/domain/market/convert.d.ts.map +1 -1
- package/dist/domain/market/convert.js +24 -10
- package/dist/domain/market/convert.js.map +1 -1
- package/dist/domain/market/index.d.ts +18 -4
- package/dist/domain/market/index.d.ts.map +1 -1
- package/dist/domain/market/index.js.map +1 -1
- package/dist/domain/market/outcome.d.ts +9 -3
- package/dist/domain/market/outcome.d.ts.map +1 -1
- package/dist/domain/market/outcome.js +10 -7
- package/dist/domain/market/outcome.js.map +1 -1
- package/dist/domain/market/tokens.js +8 -2
- package/dist/domain/market/tokens.js.map +1 -1
- package/dist/domain/market/wire.d.ts +20 -3
- package/dist/domain/market/wire.d.ts.map +1 -1
- package/dist/domain/market/wire.js.map +1 -1
- package/dist/domain/order/client.d.ts +8 -7
- package/dist/domain/order/client.d.ts.map +1 -1
- package/dist/domain/order/client.js +23 -8
- package/dist/domain/order/client.js.map +1 -1
- package/dist/domain/orderbook/client.d.ts +7 -1
- package/dist/domain/orderbook/client.d.ts.map +1 -1
- package/dist/domain/orderbook/client.js +54 -1
- package/dist/domain/orderbook/client.js.map +1 -1
- package/dist/domain/orderbook/index.d.ts +7 -9
- package/dist/domain/orderbook/index.d.ts.map +1 -1
- package/dist/domain/orderbook/index.js +28 -24
- package/dist/domain/orderbook/index.js.map +1 -1
- package/dist/domain/orderbook/state.d.ts +21 -16
- package/dist/domain/orderbook/state.d.ts.map +1 -1
- package/dist/domain/orderbook/state.js +36 -15
- package/dist/domain/orderbook/state.js.map +1 -1
- package/dist/domain/orderbook/ticker.d.ts +1 -0
- package/dist/domain/orderbook/ticker.d.ts.map +1 -1
- package/dist/domain/orderbook/ticker.js.map +1 -1
- package/dist/domain/orderbook/wire.d.ts +34 -10
- package/dist/domain/orderbook/wire.d.ts.map +1 -1
- package/dist/domain/orderbook/wire.js +47 -0
- package/dist/domain/orderbook/wire.js.map +1 -1
- package/dist/domain/position/builders.d.ts +6 -2
- package/dist/domain/position/builders.d.ts.map +1 -1
- package/dist/domain/position/builders.js +24 -8
- package/dist/domain/position/builders.js.map +1 -1
- package/dist/domain/position/client.d.ts +166 -12
- package/dist/domain/position/client.d.ts.map +1 -1
- package/dist/domain/position/client.js +614 -15
- package/dist/domain/position/client.js.map +1 -1
- package/dist/domain/position/index.d.ts +58 -3
- package/dist/domain/position/index.d.ts.map +1 -1
- package/dist/domain/position/index.js +1 -0
- package/dist/domain/position/index.js.map +1 -1
- package/dist/domain/position/state.d.ts +118 -0
- package/dist/domain/position/state.d.ts.map +1 -0
- package/dist/domain/position/state.js +262 -0
- package/dist/domain/position/state.js.map +1 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +2 -2
- package/dist/env.js.map +1 -1
- package/dist/error.d.ts +32 -2
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +59 -1
- package/dist/error.js.map +1 -1
- package/dist/http/client.d.ts +76 -1
- package/dist/http/client.d.ts.map +1 -1
- package/dist/http/client.js +259 -39
- package/dist/http/client.js.map +1 -1
- package/dist/http/credentialRestorer.d.ts +49 -0
- package/dist/http/credentialRestorer.d.ts.map +1 -0
- package/dist/http/credentialRestorer.js +3 -0
- package/dist/http/credentialRestorer.js.map +1 -0
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/prelude.d.ts +10 -7
- package/dist/prelude.d.ts.map +1 -1
- package/dist/prelude.js +12 -2
- package/dist/prelude.js.map +1 -1
- package/dist/privy/client.d.ts +4 -0
- package/dist/privy/client.d.ts.map +1 -1
- package/dist/privy/client.js +4 -0
- package/dist/privy/client.js.map +1 -1
- package/dist/privy/index.d.ts +4 -4
- package/dist/privy/index.d.ts.map +1 -1
- package/dist/privy/index.js +11 -16
- package/dist/privy/index.js.map +1 -1
- package/dist/program/builder.d.ts +13 -11
- package/dist/program/builder.d.ts.map +1 -1
- package/dist/program/builder.js +31 -16
- package/dist/program/builder.js.map +1 -1
- package/dist/program/constants.d.ts +1 -0
- package/dist/program/constants.d.ts.map +1 -1
- package/dist/program/constants.js +1 -0
- package/dist/program/constants.js.map +1 -1
- package/dist/program/envelope.d.ts +14 -16
- package/dist/program/envelope.d.ts.map +1 -1
- package/dist/program/envelope.js +48 -39
- package/dist/program/envelope.js.map +1 -1
- package/dist/program/index.d.ts +2 -2
- package/dist/program/index.d.ts.map +1 -1
- package/dist/program/index.js +6 -4
- package/dist/program/index.js.map +1 -1
- package/dist/program/instructions.d.ts +17 -9
- package/dist/program/instructions.d.ts.map +1 -1
- package/dist/program/instructions.js +40 -19
- package/dist/program/instructions.js.map +1 -1
- package/dist/program/orders.d.ts +7 -8
- package/dist/program/orders.d.ts.map +1 -1
- package/dist/program/orders.js +20 -22
- package/dist/program/orders.js.map +1 -1
- package/dist/program/types.d.ts +4 -3
- package/dist/program/types.d.ts.map +1 -1
- package/dist/program/types.js.map +1 -1
- package/dist/rpc.d.ts +89 -1
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +285 -4
- package/dist/rpc.js.map +1 -1
- package/dist/shared/api_response.d.ts +16 -1
- package/dist/shared/api_response.d.ts.map +1 -1
- package/dist/shared/api_response.js +26 -1
- package/dist/shared/api_response.js.map +1 -1
- package/dist/shared/index.d.ts +3 -3
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +13 -6
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/json.d.ts +5 -0
- package/dist/shared/json.d.ts.map +1 -0
- package/dist/shared/json.js +38 -0
- package/dist/shared/json.js.map +1 -0
- package/dist/shared/rejection.d.ts.map +1 -1
- package/dist/shared/rejection.js +8 -0
- package/dist/shared/rejection.js.map +1 -1
- package/dist/shared/scaling.d.ts +30 -18
- package/dist/shared/scaling.d.ts.map +1 -1
- package/dist/shared/scaling.js +153 -95
- package/dist/shared/scaling.js.map +1 -1
- package/dist/shared/signing.d.ts +17 -0
- package/dist/shared/signing.d.ts.map +1 -1
- package/dist/shared/signing.js +27 -0
- package/dist/shared/signing.js.map +1 -1
- package/dist/shared/types.d.ts +9 -7
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js +12 -4
- package/dist/shared/types.js.map +1 -1
- package/dist/ws/client.browser.d.ts +4 -0
- package/dist/ws/client.browser.d.ts.map +1 -1
- package/dist/ws/client.browser.js +13 -1
- package/dist/ws/client.browser.js.map +1 -1
- package/dist/ws/client.node.d.ts +4 -0
- package/dist/ws/client.node.d.ts.map +1 -1
- package/dist/ws/client.node.js +13 -1
- package/dist/ws/client.node.js.map +1 -1
- package/dist/ws/index.d.ts +24 -0
- package/dist/ws/index.d.ts.map +1 -1
- package/dist/ws/index.js +176 -2
- package/dist/ws/index.js.map +1 -1
- package/dist/ws/subscriptions.d.ts +12 -1
- package/dist/ws/subscriptions.d.ts.map +1 -1
- package/dist/ws/subscriptions.js +11 -0
- package/dist/ws/subscriptions.js.map +1 -1
- package/dist/ws/types.d.ts +5 -0
- package/dist/ws/types.d.ts.map +1 -1
- package/dist/ws/types.js.map +1 -1
- package/package.json +6 -2
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
|
|
@@ -69,7 +88,7 @@ async function main() {
|
|
|
69
88
|
.amount(1_000_000n)
|
|
70
89
|
.buildIx();
|
|
71
90
|
|
|
72
|
-
// 4.
|
|
91
|
+
// 4. Fetch/cache immutable trading rules, validate exactly, sign, and submit
|
|
73
92
|
const response = await client.orders().limitOrder()
|
|
74
93
|
.maker(keypair.publicKey)
|
|
75
94
|
.bid()
|
|
@@ -177,12 +196,137 @@ ws.subscribe({
|
|
|
177
196
|
});
|
|
178
197
|
```
|
|
179
198
|
|
|
180
|
-
Book streams are snapshot-only: every `book_update`
|
|
199
|
+
Book streams are snapshot-only: every accepted `book_update` replaces the full
|
|
200
|
+
top-20 view. `OrderbookState` discards equal/older `seq` values within a
|
|
201
|
+
subscription generation and accepts forward gaps. Call `beginGeneration()` on
|
|
202
|
+
reconnect/resubscribe; `resync: true` requires unsubscribe/resubscribe with the
|
|
203
|
+
same aggregation. Each `(orderbook, aggregation)` pair needs its own state.
|
|
204
|
+
Truncation flags are preserved and mean that side is not exhaustive. See
|
|
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.
|
|
211
|
+
Ticker consumers should use the supplied `mid`; it is engine-authoritative and
|
|
212
|
+
may use one-sided-book or last-trade fallback.
|
|
213
|
+
REST depth is a coherent projection that may briefly lag a mutation. Use its
|
|
214
|
+
`revision` and `captured_at_ms` metadata, and expect revision gaps.
|
|
181
215
|
|
|
182
216
|
```typescript
|
|
183
217
|
ws.subscribe({ type: "book_update", orderbook_ids: [orderbook.orderbookId], nSigFigs: 5, mantissa: 2 });
|
|
184
218
|
```
|
|
185
219
|
|
|
220
|
+
Order submission accepts decimal strings and uses exact `bigint` construction.
|
|
221
|
+
`submit()` fetches and caches `/api/orderbooks/{id}/decimals` before invoking a
|
|
222
|
+
signer. Direct `sign()`/`finalize()` calls require the returned `OrderbookRules`.
|
|
223
|
+
Raw amounts, explicit salts, and derived prices are preflighted against the same
|
|
224
|
+
signed-64-bit admission rules; no tick or size normalization is implicit.
|
|
225
|
+
|
|
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.
|
|
329
|
+
|
|
186
330
|
### Step 5: Cancel an Order
|
|
187
331
|
|
|
188
332
|
```typescript
|
|
@@ -208,6 +352,8 @@ const txHash = await client.positions().merge()
|
|
|
208
352
|
.signAndSubmit();
|
|
209
353
|
```
|
|
210
354
|
|
|
355
|
+
`market.numOutcomes` is the validated protocol outcome count. Market deposit, merge, and unified withdrawal use it instead of the length of display outcome metadata. The pubkey-only `withdrawFromPosition()` builder requires `.numOutcomes(market.numOutcomes)` before building.
|
|
356
|
+
|
|
211
357
|
### Step 7: Withdraw
|
|
212
358
|
|
|
213
359
|
```typescript
|
|
@@ -222,6 +368,17 @@ const withdrawIx = client.positions().withdraw()
|
|
|
222
368
|
|
|
223
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.
|
|
224
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
|
+
|
|
373
|
+
Use `walletDisplayName(session.user, session.auth_method)` to show a shortened
|
|
374
|
+
label for the wallet the session trades with, regardless of login identity.
|
|
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
|
+
|
|
225
382
|
### Cookie handling
|
|
226
383
|
|
|
227
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:
|
|
@@ -235,14 +392,17 @@ After login succeeds, the SDK stores the session token internally and attaches i
|
|
|
235
392
|
|
|
236
393
|
When the SDK runs on a server (SSR, an Express / Next.js route handler, etc.) and the *user's* `auth_token` cookie arrives on an incoming HTTP request, the SDK's process-wide token store is the wrong place to route it through — the store is shared across all users of that server process.
|
|
237
394
|
|
|
395
|
+
> **Behavior change.** `getWithCookies` responses no longer capture `Set-Cookie` into the shared token slot (they previously did): a forwarded per-user request rotating its token must not leak that token to every later request from a shared server client. These requests also never consult the credential restorer.
|
|
396
|
+
|
|
238
397
|
For these cases, authed methods that need per-call forwarding ship a `*WithAuth(authToken)` sibling that injects the cookie just for that one call:
|
|
239
398
|
|
|
240
399
|
```typescript
|
|
241
400
|
// Inside a server route, after extracting the auth_token cookie
|
|
242
401
|
// from the incoming request:
|
|
243
|
-
const
|
|
402
|
+
const snapshot = await client
|
|
244
403
|
.positions()
|
|
245
|
-
.
|
|
404
|
+
.depositTokenBalancesWithCookies(undefined, authToken);
|
|
405
|
+
console.log(`snapshot slot ${snapshot.context_slot}: ${Object.keys(snapshot.balances).length} balances`);
|
|
246
406
|
|
|
247
407
|
const positions = await client
|
|
248
408
|
.positions()
|
|
@@ -279,6 +439,8 @@ Each environment configures the API URL, WebSocket URL, Solana RPC URL, and on-c
|
|
|
279
439
|
|
|
280
440
|
The Solana RPC URL can also be overridden via the `SDK_RPC_URL` environment variable, which takes precedence over the environment default. This is useful for pointing all examples at a private RPC to avoid public devnet rate limits.
|
|
281
441
|
|
|
442
|
+
Favorite-market add and remove methods are idempotent set operations, so the SDK may safely replay them after supported credential restoration or transient transport failures. Per-call cookie variants used by SSR and route handlers retry transient failures with the supplied cookie but never invoke the process-wide credential restorer.
|
|
443
|
+
|
|
282
444
|
## Examples
|
|
283
445
|
|
|
284
446
|
All examples are runnable with `npx tsx examples/<name>.ts`. Examples default to the production environment and read the wallet keypair from `~/.config/solana/id.json`.
|
|
@@ -288,13 +450,13 @@ All examples are runnable with `npx tsx examples/<name>.ts`. Examples default to
|
|
|
288
450
|
| Example | Description |
|
|
289
451
|
|---------|-------------|
|
|
290
452
|
| [`login`](examples/login.ts) | Full auth lifecycle: sign message, login, check session, logout |
|
|
291
|
-
| [`
|
|
453
|
+
| [`with_cookies`](examples/with_cookies.ts) | Per-call cookie forwarding for SSR / route-handler consumers, including paginated favorite-market list/add/remove while restoring the original state |
|
|
292
454
|
|
|
293
455
|
### Market Discovery & Data
|
|
294
456
|
|
|
295
457
|
| Example | Description |
|
|
296
458
|
|---------|-------------|
|
|
297
|
-
| [`markets`](examples/markets.ts) | Featured markets, paginated listing, fetch by pubkey, search, platform deposit assets via `globalDepositAssets()` |
|
|
459
|
+
| [`markets`](examples/markets.ts) | Featured markets, paginated listing, fetch by pubkey, search, and platform deposit assets via `globalDepositAssets()`; authenticated favorite-market APIs are demonstrated by `with_cookies` |
|
|
298
460
|
| [`orderbook`](examples/orderbook.ts) | Fetch orderbook depth (bids/asks) and decimal precision metadata |
|
|
299
461
|
| [`trades`](examples/trades.ts) | Recent trade history with cursor-based pagination (per-orderbook and market-wide) |
|
|
300
462
|
| [`price_history`](examples/price_history.ts) | Historical candlestick data (OHLCV) at various resolutions |
|
|
@@ -305,7 +467,7 @@ All examples are runnable with `npx tsx examples/<name>.ts`. Examples default to
|
|
|
305
467
|
|
|
306
468
|
| Example | Description |
|
|
307
469
|
|---------|-------------|
|
|
308
|
-
| [`submit_order`](examples/submit_order.ts) | Deposit
|
|
470
|
+
| [`submit_order`](examples/submit_order.ts) | Deposit collateral, then place an exactly validated limit order using cached trading rules. Companion `cancel_order` cancels it and withdraws to stay net-neutral |
|
|
309
471
|
|
|
310
472
|
### Cancelling Orders
|
|
311
473
|
|
|
@@ -322,6 +484,16 @@ All examples are runnable with `npx tsx examples/<name>.ts`. Examples default to
|
|
|
322
484
|
| [`onchain_transactions`](examples/onchain_transactions.ts) | Build, sign, and submit mint/merge complete set and increment nonce on-chain |
|
|
323
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 |
|
|
324
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
|
+
|
|
325
497
|
### WebSocket Streaming
|
|
326
498
|
|
|
327
499
|
| Example | Description |
|
|
@@ -359,6 +531,7 @@ When the backend rejects a request (insufficient balance, expired order, etc.),
|
|
|
359
531
|
| `errorCode` | `string \| undefined` | API-level error code (for example `"NOT_FOUND"` or `"INVALID_ARGUMENT"`) |
|
|
360
532
|
| `errorLogId` | `string \| undefined` | Backend support correlation ID (`LCERR_*`) |
|
|
361
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 |
|
|
362
535
|
|
|
363
536
|
`ApiRejectedDetails.toString()` formats all present fields as a multi-line report for logs or support tickets.
|
|
364
537
|
|
|
@@ -430,14 +603,29 @@ The SDK generates a UUID v4 `x-request-id` header on every HTTP request. On reje
|
|
|
430
603
|
| `NotFound` | 404 - resource not found |
|
|
431
604
|
| `BadRequest` | 400 - invalid request |
|
|
432
605
|
| `Timeout` | Request timed out |
|
|
433
|
-
| `MaxRetriesExceeded` |
|
|
606
|
+
| `MaxRetriesExceeded` | Never produced by the SDK itself: the HTTP retry loop propagates the final underlying error on exhaustion (structured details intact — see the retry-exhaustion tests). Kept public for consumer-built retry loops |
|
|
434
607
|
|
|
435
608
|
## Retry Strategy
|
|
436
609
|
|
|
437
|
-
- **
|
|
438
|
-
- **
|
|
610
|
+
- **Replay-safe requests**: GETs and idempotent set operations such as favorite-market updates use `RetryPolicy.Idempotent`, which retries transport failures and 502/503/504 and backs off on 429 with exponential backoff + jitter.
|
|
611
|
+
- **Non-idempotent requests** (order submit, cancel, auth): `RetryPolicy.None` - no automatic retry, which prevents duplicate side effects.
|
|
439
612
|
- Customizable per-call with `RetryPolicy.custom(config)`.
|
|
440
613
|
|
|
614
|
+
### Credential restoration (401 recovery)
|
|
615
|
+
|
|
616
|
+
Sessions built on short-lived tokens expire mid-run: the backend starts answering 401 even though the app could mint a fresh token (e.g. a browser refreshing its Privy session). Rather than every caller hand-rolling "detect 401 → refresh → retry", the transport accepts a host-supplied hook:
|
|
617
|
+
|
|
618
|
+
```typescript
|
|
619
|
+
client.setCredentialRestorer(async () => {
|
|
620
|
+
// e.g. ask the auth provider's SDK to refresh the session
|
|
621
|
+
return await refreshSession();
|
|
622
|
+
});
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
When a request to the API origin fails with HTTP 401 and a restorer is registered, the transport consults it **at most once per logical request**, with concurrent 401s sharing one restoration (bounded by a 30-second timeout). A successful restoration replays the request once **only if it declared itself retry-safe** (an idempotent/custom retry policy); `RetryPolicy.None` requests — mutations like orders and cancels — are never auto-replayed: the restoration still heals the session for the caller's next attempt, but the original 401 propagates. Restoration is skipped for credential-management endpoints (login, logout) and for cookie-override/custom-session requests, redirects are never followed on the API transport, and without a registered restorer 401s propagate unchanged. A timed-out restoration has its `AbortSignal` fired — promises cannot be cancelled, so restorers whose work is non-idempotent (refresh-token rotation) must honor the signal or serialize internally.
|
|
626
|
+
|
|
627
|
+
The SDK stays credential-agnostic: what "restore" means belongs to the host. For classifying auth failures in your own code, use `isUnauthorized(error)` from the error module — it covers both bare 401s and 401s carrying a structured rejection envelope (`ApiRejectedDetails.httpStatus`).
|
|
628
|
+
|
|
441
629
|
## Trigger Orders
|
|
442
630
|
|
|
443
631
|
Trigger orders (stop-limit, take-profit-limit) are under development and not yet available. Internal types exist in the source for internal use only.
|
package/dist/auth/client.d.ts
CHANGED
|
@@ -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;
|
|
@@ -15,8 +27,22 @@ export declare class Auth {
|
|
|
15
27
|
getNonce(): Promise<string>;
|
|
16
28
|
loginWithMessage(message: string, signatureBs58: string, pubkeyBytes: Uint8Array, useEmbeddedWallet?: boolean): Promise<SessionResponse>;
|
|
17
29
|
checkSession(): Promise<SessionResponse>;
|
|
30
|
+
/**
|
|
31
|
+
* Logout — clears the server-side cookie, internal token, and credentials.
|
|
32
|
+
*
|
|
33
|
+
* Local state is cleared even when the server call fails — the caller asked
|
|
34
|
+
* to be signed out locally regardless — but the failure is then rethrown:
|
|
35
|
+
* callers gating security decisions on teardown (e.g. whether an app may
|
|
36
|
+
* restart an authenticated transport) must be able to see that the
|
|
37
|
+
* server-side cookie may still be valid. A 401 counts as success: it means
|
|
38
|
+
* "already logged out".
|
|
39
|
+
*/
|
|
18
40
|
logout(): Promise<void>;
|
|
19
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>;
|
|
20
46
|
connectXUrl(): string;
|
|
21
47
|
credentials(): AuthCredentials | undefined;
|
|
22
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,
|
|
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"}
|
package/dist/auth/client.js
CHANGED
|
@@ -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) {
|
|
@@ -23,7 +46,12 @@ class Auth {
|
|
|
23
46
|
pubkey_bytes: Array.from(pubkeyBytes),
|
|
24
47
|
use_embedded_wallet: useEmbeddedWallet,
|
|
25
48
|
};
|
|
26
|
-
|
|
49
|
+
// Credential-management endpoint: opts out of the transport's 401
|
|
50
|
+
// restore-and-replay. The backend consumes the login nonce before
|
|
51
|
+
// verifying the signature, so a replayed login deterministically fails —
|
|
52
|
+
// and restoring credentials in order to log in is circular.
|
|
53
|
+
const session = await this.client.http.postWithoutCredentialRestore(url, body, http_1.RetryPolicy.None);
|
|
54
|
+
normalizeSessionMaxSlippagePreference(session);
|
|
27
55
|
this.client.authState.setCredentials(credentialsFromSession(session));
|
|
28
56
|
return session;
|
|
29
57
|
}
|
|
@@ -32,6 +60,7 @@ class Auth {
|
|
|
32
60
|
let session;
|
|
33
61
|
try {
|
|
34
62
|
session = await this.client.http.get(url, http_1.RetryPolicy.Idempotent);
|
|
63
|
+
normalizeSessionMaxSlippagePreference(session);
|
|
35
64
|
}
|
|
36
65
|
catch (error) {
|
|
37
66
|
this.client.authState.setCredentials(undefined);
|
|
@@ -40,22 +69,58 @@ class Auth {
|
|
|
40
69
|
this.client.authState.setCredentials(credentialsFromSession(session));
|
|
41
70
|
return session;
|
|
42
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Logout — clears the server-side cookie, internal token, and credentials.
|
|
74
|
+
*
|
|
75
|
+
* Local state is cleared even when the server call fails — the caller asked
|
|
76
|
+
* to be signed out locally regardless — but the failure is then rethrown:
|
|
77
|
+
* callers gating security decisions on teardown (e.g. whether an app may
|
|
78
|
+
* restart an authenticated transport) must be able to see that the
|
|
79
|
+
* server-side cookie may still be valid. A 401 counts as success: it means
|
|
80
|
+
* "already logged out".
|
|
81
|
+
*/
|
|
43
82
|
async logout() {
|
|
44
83
|
const url = `${this.client.http.baseUrl()}/api/auth/logout`;
|
|
84
|
+
let logoutError = null;
|
|
45
85
|
try {
|
|
46
|
-
|
|
86
|
+
// Credential-management endpoint: opts out of the transport's 401
|
|
87
|
+
// restore-and-replay — a 401 here means "already logged out".
|
|
88
|
+
await this.client.http.postWithoutCredentialRestore(url, {}, http_1.RetryPolicy.None);
|
|
47
89
|
}
|
|
48
|
-
catch {
|
|
49
|
-
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (!(0, error_1.isUnauthorized)(error)) {
|
|
92
|
+
logoutError = error;
|
|
93
|
+
}
|
|
50
94
|
}
|
|
51
95
|
await this.client.http.clearAuthToken();
|
|
52
96
|
this.client.authState.setCredentials(undefined);
|
|
53
97
|
await this.client.authState.clearCaches();
|
|
98
|
+
if (logoutError !== null) {
|
|
99
|
+
throw logoutError;
|
|
100
|
+
}
|
|
54
101
|
}
|
|
55
102
|
async disconnectX() {
|
|
56
103
|
const url = `${this.client.http.baseUrl()}/api/auth/disconnect_x`;
|
|
57
104
|
await this.client.http.post(url, {}, http_1.RetryPolicy.None);
|
|
58
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
|
+
}
|
|
59
124
|
connectXUrl() {
|
|
60
125
|
return `${this.client.http.baseUrl()}/api/auth/oauth/link/x`;
|
|
61
126
|
}
|
|
@@ -71,6 +136,31 @@ class Auth {
|
|
|
71
136
|
}
|
|
72
137
|
}
|
|
73
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
|
+
}
|
|
74
164
|
/**
|
|
75
165
|
* Derive session credentials from the envelope. The trading wallet comes from
|
|
76
166
|
* the identity + auth method.
|
package/dist/auth/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/auth/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { SdkError } 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 const session = await this.client.http.post<SessionResponse, LoginRequest>(\n url,\n body,\n RetryPolicy.None\n );\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 async logout(): Promise<void> {\n const url = `${this.client.http.baseUrl()}/api/auth/logout`;\n try {\n await this.client.http.post<{ success: boolean }, Record<string, never>>(url, {}, RetryPolicy.None);\n } catch {\n // Backend cookie clear can fail in local/dev setups; still clear local state.\n }\n\n await this.client.http.clearAuthToken();\n this.client.authState.setCredentials(undefined);\n await this.client.authState.clearCaches();\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":"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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 {\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"]}
|