@lightconexyz/lightcone-sdk 0.8.3 → 0.8.4-rc.54
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 +72 -54
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -123,23 +123,17 @@ submission is unavailable until signed v1 bytes can be verified.
|
|
|
123
123
|
|
|
124
124
|
## Quick Start
|
|
125
125
|
|
|
126
|
+
This example authenticates and subscribes to public market data. For funded order submission, use the [trading example](examples/submit_order.ts).
|
|
127
|
+
|
|
126
128
|
```typescript
|
|
127
|
-
import { Keypair
|
|
129
|
+
import { Keypair } from "@solana/web3.js";
|
|
128
130
|
import {
|
|
129
131
|
LightconeClient,
|
|
130
|
-
DepositSource,
|
|
131
132
|
auth,
|
|
132
133
|
} from "@lightconexyz/lightcone-sdk";
|
|
133
134
|
|
|
134
135
|
async function main() {
|
|
135
|
-
const client = LightconeClient.builder()
|
|
136
|
-
.depositSource(DepositSource.Market)
|
|
137
|
-
.transactionResources({
|
|
138
|
-
computeUnitLimit: 200_000,
|
|
139
|
-
loadedAccountsDataSizeLimit: 65_536,
|
|
140
|
-
priorityFeeLamports: 0n,
|
|
141
|
-
})
|
|
142
|
-
.build();
|
|
136
|
+
const client = LightconeClient.builder().build();
|
|
143
137
|
const keypair = Keypair.generate();
|
|
144
138
|
|
|
145
139
|
// 1. Authenticate
|
|
@@ -162,31 +156,7 @@ async function main() {
|
|
|
162
156
|
throw new Error("Selected market has no orderbooks");
|
|
163
157
|
}
|
|
164
158
|
|
|
165
|
-
// 3.
|
|
166
|
-
const depositMint = new PublicKey(market.depositAssets[0].pubkey);
|
|
167
|
-
const depositIx = client.positions().deposit()
|
|
168
|
-
.user(keypair.publicKey)
|
|
169
|
-
.mint(depositMint)
|
|
170
|
-
.amount(1_000_000n)
|
|
171
|
-
.buildIx();
|
|
172
|
-
|
|
173
|
-
// 4. Fetch/cache immutable trading rules, validate exactly, sign, and submit
|
|
174
|
-
const response = await client.orders().limitOrder()
|
|
175
|
-
.maker(keypair.publicKey)
|
|
176
|
-
.bid()
|
|
177
|
-
.price("0.55")
|
|
178
|
-
.size("100")
|
|
179
|
-
.submit(client, orderbook);
|
|
180
|
-
console.log("Order submitted:", response);
|
|
181
|
-
|
|
182
|
-
// 5. Withdraw from the global pool
|
|
183
|
-
const withdrawIx = client.positions().withdraw()
|
|
184
|
-
.user(keypair.publicKey)
|
|
185
|
-
.mint(depositMint)
|
|
186
|
-
.amount(1_000_000n)
|
|
187
|
-
.buildIx();
|
|
188
|
-
|
|
189
|
-
// 6. Stream real-time updates
|
|
159
|
+
// 3. Subscribe to real-time updates.
|
|
190
160
|
const ws = client.ws();
|
|
191
161
|
await ws.connect();
|
|
192
162
|
ws.subscribe({ type: "book_update", orderbook_ids: [orderbook.orderbookId] });
|
|
@@ -197,6 +167,8 @@ main().catch(console.error);
|
|
|
197
167
|
|
|
198
168
|
## Start Trading
|
|
199
169
|
|
|
170
|
+
These excerpts use top-level `await` in an ES module. Use a funded keypair for the selected environment. The [submit example](examples/submit_order.ts) confirms the deposit and waits for the API balance before ordering.
|
|
171
|
+
|
|
200
172
|
```typescript
|
|
201
173
|
import * as fs from "fs";
|
|
202
174
|
import * as os from "os";
|
|
@@ -205,6 +177,7 @@ import { Keypair, PublicKey } from "@solana/web3.js";
|
|
|
205
177
|
import {
|
|
206
178
|
LightconeClient,
|
|
207
179
|
DepositSource,
|
|
180
|
+
auth,
|
|
208
181
|
} from "@lightconexyz/lightcone-sdk";
|
|
209
182
|
|
|
210
183
|
function readKeypairFile(filePath: string): Keypair {
|
|
@@ -220,8 +193,19 @@ const keypair = readKeypairFile("~/.config/solana/id.json");
|
|
|
220
193
|
// Defaults to Prod. Use .env(LightconeEnv.Staging) for staging.
|
|
221
194
|
const client = LightconeClient.builder()
|
|
222
195
|
.nativeSigner(keypair)
|
|
223
|
-
.depositSource(DepositSource.
|
|
196
|
+
.depositSource(DepositSource.Global)
|
|
197
|
+
.transactionResources({
|
|
198
|
+
computeUnitLimit: 200_000,
|
|
199
|
+
loadedAccountsDataSizeLimit: 1_048_576,
|
|
200
|
+
priorityFeeLamports: 0n,
|
|
201
|
+
})
|
|
224
202
|
.build();
|
|
203
|
+
const nonce = await client.auth().getNonce();
|
|
204
|
+
const signed = auth.signLoginMessage(keypair, nonce);
|
|
205
|
+
await client.auth().loginWithMessage(
|
|
206
|
+
signed.message, signed.signature_bs58, signed.pubkey_bytes
|
|
207
|
+
);
|
|
208
|
+
client.setOrderNonce(await client.orders().currentNonce(keypair.publicKey));
|
|
225
209
|
```
|
|
226
210
|
|
|
227
211
|
### Step 1: Find a Market
|
|
@@ -241,13 +225,42 @@ if (!orderbook) {
|
|
|
241
225
|
|
|
242
226
|
### Step 2: Deposit Collateral
|
|
243
227
|
|
|
228
|
+
The amount is in the deposit mint's smallest units. Instruction construction alone does not transfer collateral. Submit and confirm the transaction, then wait for the API balance to cover the order. Select the deposit mint backing the orderbook's quote token, as shown in the [submit example](examples/submit_order.ts).
|
|
229
|
+
|
|
230
|
+
Calculate the deposit from the same price and size used by the order. Poll at most 15 times, with two seconds between unsuccessful reads. A timeout or read failure stops before order submission. Check the confirmed deposit before repeating this step.
|
|
231
|
+
|
|
244
232
|
```typescript
|
|
245
|
-
|
|
246
|
-
|
|
233
|
+
import { shared } from "@lightconexyz/lightcone-sdk";
|
|
234
|
+
import { program } from "@lightconexyz/lightcone-sdk";
|
|
235
|
+
|
|
236
|
+
const rules = await client.orderbooks().decimals(orderbook.orderbookId);
|
|
237
|
+
const orderPrice = "0.55"; // Quote tokens per base token.
|
|
238
|
+
const orderSize = "1"; // Base tokens.
|
|
239
|
+
const quoteAtoms = shared.scalePriceSize(
|
|
240
|
+
orderPrice, orderSize, program.OrderSide.BID, rules
|
|
241
|
+
).quoteAtoms;
|
|
242
|
+
const depositMint = new PublicKey(orderbook.quote.depositAsset);
|
|
243
|
+
const depositContext = await client.transactionContext();
|
|
244
|
+
const depositTx = client.positions().deposit()
|
|
247
245
|
.user(keypair.publicKey)
|
|
248
246
|
.mint(depositMint)
|
|
249
|
-
.amount(
|
|
250
|
-
.
|
|
247
|
+
.amount(quoteAtoms)
|
|
248
|
+
.buildTx(depositContext);
|
|
249
|
+
await client.signAndSubmitTxConfirmedWithSlot(depositTx);
|
|
250
|
+
|
|
251
|
+
// Confirmation can precede indexing. Check the available global collateral.
|
|
252
|
+
for (let attempt = 0; attempt < 15; attempt++) {
|
|
253
|
+
const snapshot = await client.positions().depositTokenBalances();
|
|
254
|
+
const entry = Object.values(snapshot.balances).find(
|
|
255
|
+
(balance) => balance.mint === depositMint.toBase58()
|
|
256
|
+
);
|
|
257
|
+
const idleAtoms = shared.exactScaledInteger(entry?.idle ?? "0", rules.quoteDecimals);
|
|
258
|
+
if (idleAtoms >= quoteAtoms) break;
|
|
259
|
+
if (attempt === 14) {
|
|
260
|
+
throw new Error("Global collateral is not indexed yet; order was not submitted");
|
|
261
|
+
}
|
|
262
|
+
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
263
|
+
}
|
|
251
264
|
```
|
|
252
265
|
|
|
253
266
|
### Step 3: Place an Order
|
|
@@ -256,8 +269,8 @@ const depositIx = client.positions().deposit()
|
|
|
256
269
|
const order = await client.orders().limitOrder()
|
|
257
270
|
.maker(keypair.publicKey)
|
|
258
271
|
.bid()
|
|
259
|
-
.price(
|
|
260
|
-
.size(
|
|
272
|
+
.price(orderPrice)
|
|
273
|
+
.size(orderSize)
|
|
261
274
|
.submit(client, orderbook);
|
|
262
275
|
```
|
|
263
276
|
|
|
@@ -268,7 +281,7 @@ import { asPubkeyStr } from "@lightconexyz/lightcone-sdk";
|
|
|
268
281
|
|
|
269
282
|
const open = await client
|
|
270
283
|
.orders()
|
|
271
|
-
.getUserOrders(
|
|
284
|
+
.getUserOrders(50);
|
|
272
285
|
const ws = client.ws();
|
|
273
286
|
await ws.connect();
|
|
274
287
|
ws.subscribe({ type: "book_update", orderbook_ids: [orderbook.orderbookId] });
|
|
@@ -305,6 +318,8 @@ signer. Direct `sign()`/`finalize()` calls require the returned `OrderbookRules`
|
|
|
305
318
|
Raw amounts, explicit salts, and derived prices are preflighted against the same
|
|
306
319
|
signed-64-bit admission rules; no tick or size normalization is implicit.
|
|
307
320
|
|
|
321
|
+
The envelope generates a salt when omitted. The price is quote tokens per base token, and the size is base tokens. Example values must satisfy the selected orderbook's trading rules and available collateral.
|
|
322
|
+
|
|
308
323
|
### Wallet Balances and SOL Action Planning
|
|
309
324
|
|
|
310
325
|
`depositTokenBalances()` returns a required exact nine-decimal
|
|
@@ -415,7 +430,7 @@ automatic retry.
|
|
|
415
430
|
### Step 5: Cancel an Order
|
|
416
431
|
|
|
417
432
|
```typescript
|
|
418
|
-
|
|
433
|
+
// program and asPubkeyStr were imported in the preceding steps.
|
|
419
434
|
|
|
420
435
|
const signature = program.signCancelOrder(order.order_hash, keypair);
|
|
421
436
|
await client.orders().cancel({
|
|
@@ -427,6 +442,8 @@ await client.orders().cancel({
|
|
|
427
442
|
|
|
428
443
|
### Step 6: Exit a Position
|
|
429
444
|
|
|
445
|
+
Merge only when the wallet holds a complete set of outcome tokens for this deposit mint. Cancelling an unfilled order does not create that set. To return unused global collateral, proceed to withdrawal.
|
|
446
|
+
|
|
430
447
|
```typescript
|
|
431
448
|
// Uses the explicit transaction resources configured on the client.
|
|
432
449
|
// The signature means RPC acceptance; confirm before dependent account operations.
|
|
@@ -452,7 +469,9 @@ const withdrawIx = client.positions().withdraw()
|
|
|
452
469
|
|
|
453
470
|
## Authentication
|
|
454
471
|
|
|
455
|
-
Authentication is
|
|
472
|
+
Authentication is required for user-specific endpoints. Fetch `/api/auth/nonce`, then sign the exact message `Sign in to Lightcone\nNonce: {nonce}` with ED25519. Use `auth.signLoginMessage` to construct this challenge, then exchange the signed message for a session. Do not sign a timestamp or the nonce alone.
|
|
473
|
+
|
|
474
|
+
Authenticate before connecting a private WebSocket. Node clients send the session cookie during the upgrade, and browsers supply the cookie automatically. Private user subscriptions require `wallet_address`. Derive the Trading Wallet with `tradingWallet(session.user, session.auth_method)`. Refer to the [authenticated streaming example](examples/ws_user_and_market.ts).
|
|
456
475
|
|
|
457
476
|
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.
|
|
458
477
|
|
|
@@ -472,30 +491,29 @@ After login succeeds, the SDK stores the session token internally and attaches i
|
|
|
472
491
|
- **Node / non-browser**: token is stored on the `LightconeHttp` instance and added as a `Cookie` header per request.
|
|
473
492
|
- **Browser**: requests use `credentials: "include"` and the runtime supplies the cookie automatically — the SDK's internal store is unused.
|
|
474
493
|
|
|
475
|
-
### Server-side cookie forwarding (`*
|
|
494
|
+
### Server-side cookie forwarding (`*WithCookies` variants)
|
|
476
495
|
|
|
477
|
-
|
|
496
|
+
Ordinary authenticated methods use the client's session or the browser cookie. Server-side rendering and route handlers must pass the incoming raw `Cookie` header to the corresponding `*WithCookies` method.
|
|
478
497
|
|
|
479
|
-
|
|
498
|
+
The cookie name is `lightcone-token`. Do not install a user's token on a client shared between requests from different users.
|
|
480
499
|
|
|
481
500
|
> **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.
|
|
482
501
|
|
|
483
|
-
|
|
502
|
+
These methods forward the supplied header for one call:
|
|
484
503
|
|
|
485
504
|
```typescript
|
|
486
|
-
//
|
|
487
|
-
// from the incoming request:
|
|
505
|
+
// cookieHeader is the incoming raw Cookie header, including lightcone-token=...
|
|
488
506
|
const snapshot = await client
|
|
489
507
|
.positions()
|
|
490
|
-
.depositTokenBalancesWithCookies(undefined,
|
|
508
|
+
.depositTokenBalancesWithCookies(undefined, cookieHeader);
|
|
491
509
|
console.log(`snapshot slot ${snapshot.context_slot}: ${Object.keys(snapshot.balances).length} balances`);
|
|
492
510
|
|
|
493
511
|
const positions = await client
|
|
494
512
|
.positions()
|
|
495
|
-
.
|
|
513
|
+
.positionsWithCookies(cookieHeader);
|
|
496
514
|
```
|
|
497
515
|
|
|
498
|
-
In
|
|
516
|
+
In browsers, use the ordinary methods. The browser supplies the cookie through `credentials: "include"`.
|
|
499
517
|
|
|
500
518
|
## Environment Configuration
|
|
501
519
|
|