northstar-eva-sdk 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,6 +16,41 @@ const sdk = NorthstarEva.create({ provider, erRpc: ER_RPC_URL, evaProgramId });
16
16
  ```
17
17
  > Ships compiled JS + TypeScript types — works in any Node ≥18 project (JS or TS). The three Solana libs are **peer dependencies** (install them alongside). Prefer a single drop-in file instead? See `share/` (no npm). Publishing it yourself? See [PUBLISHING.md](PUBLISHING.md).
18
18
 
19
+ ## What's new in 0.4.0
20
+
21
+ **Complete eva coverage — the SDK now mirrors all 11 eva instructions.** Added the 5 that were missing: `updateConfig`, `withdraw`, `closePool`, `claimTokens`, `distributePrize` (each with a `build…Ix` twin):
22
+
23
+ ```ts
24
+ // admin / lifecycle
25
+ await sdk.updateConfig({ biddingDuration: 120n, tradingDuration: 100_000n, feeRecipient });
26
+ await sdk.withdraw({ trenchId, amount: 20_000_000n }); // withdraw a bid deposit from a trench
27
+ await sdk.closePool({ trenchId });
28
+ await sdk.claimTokens({ trenchId, user }); // release a user's tokens from the vault
29
+ await sdk.distributePrize({ trenchId, winners: [w1, w2], amounts: [a1, a2] });
30
+ ```
31
+ > Note: eva `withdraw` (withdraw a trench bid deposit) is **different** from the Portal `withdrawFeeFromEr` (bridge SOL ER→L1). They coexist.
32
+
33
+ **Instruction builders — get the unsigned instruction instead of sending it.** Every send-method now has a `build…Ix` twin with the **same inputs** that returns an unsigned `TransactionInstruction`, so your backend can compose it with its own instructions, sign, and submit however it needs.
34
+
35
+ ```ts
36
+ import { NorthstarEva } from "northstar-eva-sdk";
37
+
38
+ // 1) build instructions (no signing, no network send — only the reads needed to resolve accounts)
39
+ const ataIx = sdk.buildUserAtaIx({ trenchId });
40
+ const buyIx = await sdk.buildBuyIx({ trenchId, solPayWithFee: 10_000_000n });
41
+
42
+ // 2) compose them (add your own instructions too), get an unsigned Transaction
43
+ const tx = await sdk.buildTransaction([ataIx, buyIx], { lane: "er" }); // feePayer + blockhash set
44
+
45
+ // 3) your backend signs + submits — the SDK never sends it
46
+ tx.sign(myKeypair); // or wallet.signTransaction(tx)
47
+ await connection.sendRawTransaction(tx.serialize());
48
+ ```
49
+
50
+ Builders (each mirrors the send-method's inputs): `buildOpenSessionIx()`, `buildCloseSessionIx()`, `buildDepositFeeIx(lamports, recipient?)`, `buildWithdrawFeeFromErIx({ lamports, recipient?, toL1? })` (Portal SOL bridge), `buildInitializeIx(p)`, `buildUpdateConfigIx(p)`, `buildCreateTrenchIx({ trenchId?, initialVirtualTokenReserves? })`, `buildDepositIx(p)`, `buildWithdrawIx({ trenchId, amount, thinkId? })` (eva trench withdraw), `buildFinalizeIx(p)`, `buildUserAtaIx(p)`, `buildBuyIx(p)`, `buildSellIx(p)`, `buildClosePoolIx(p)`, `buildClaimTokensIx(p)`, `buildDistributePrizeIx(p)`, plus `buildTransaction(ixs, { lane?, feePayer? })`, `nextTrenchId()`, and the `agentPda(id, user?)` PDA helper.
51
+
52
+ > The existing send-methods (`buy`, `sell`, `fundFeeToErPayer`, …) are unchanged — they now just call these builders internally. Builders perform the same state **reads** to resolve accounts (e.g. `buildBuyIx` reads global for `feeRecipient`) but never **send**.
53
+
19
54
  ## What's new in 0.3.0
20
55
 
21
56
  **Construct from your existing `program` + `provider`, and an exported `EVA_IDL` — minimal code change.**
@@ -317,6 +317,97 @@ export declare class NorthstarEva {
317
317
  private requireGlobal;
318
318
  private get methods();
319
319
  private bn;
320
+ /** Portal OpenSession instruction (L1). */
321
+ buildOpenSessionIx(): TransactionInstruction;
322
+ /** Portal CloseSession instruction (L1). */
323
+ buildCloseSessionIx(): TransactionInstruction;
324
+ /** Portal DepositFee instruction (L1) — "fund fee to ER payer". (Caller must ensure a session exists.) */
325
+ buildDepositFeeIx(lamports: bigint, recipient?: PublicKey): TransactionInstruction;
326
+ /** "Withdraw fee from ER" instruction (ER) — system transfer from `recipient` (default: signer) → its WithdrawalSink. (Portal SOL bridge; distinct from the eva `withdraw` instruction.) */
327
+ buildWithdrawFeeFromErIx(p: {
328
+ lamports: bigint;
329
+ recipient?: PublicKey;
330
+ toL1?: PublicKey;
331
+ }): TransactionInstruction;
332
+ /** Next sequential trench id (= total_trenches + 1) read from the ER global state. */
333
+ nextTrenchId(): Promise<bigint>;
334
+ /** eva initialize instruction (ER). */
335
+ buildInitializeIx(p: {
336
+ biddingDuration: bigint;
337
+ tradingDuration: bigint;
338
+ }): Promise<TransactionInstruction>;
339
+ /** eva createTrench instruction (ER). `trenchId` defaults to {@link nextTrenchId}. */
340
+ buildCreateTrenchIx(p?: {
341
+ trenchId?: bigint;
342
+ initialVirtualTokenReserves?: bigint;
343
+ }): Promise<TransactionInstruction>;
344
+ /** eva deposit instruction (ER). */
345
+ buildDepositIx(p: {
346
+ trenchId: bigint;
347
+ lamports: bigint;
348
+ thinkId?: bigint;
349
+ user?: PublicKey;
350
+ }): Promise<TransactionInstruction>;
351
+ /** eva finalize instruction (ER). Reads global for feeRecipient; does NOT wait for the bidding window. */
352
+ buildFinalizeIx(p: {
353
+ trenchId: bigint;
354
+ }): Promise<TransactionInstruction>;
355
+ /** ATA-create instruction (ER) for a user's trench token account (idempotent). */
356
+ buildUserAtaIx(p: {
357
+ trenchId: bigint;
358
+ user?: PublicKey;
359
+ }): TransactionInstruction;
360
+ /** eva buy instruction (ER). Reads global for feeRecipient. */
361
+ buildBuyIx(p: {
362
+ trenchId: bigint;
363
+ solPayWithFee: bigint;
364
+ thinkId?: bigint;
365
+ user?: PublicKey;
366
+ }): Promise<TransactionInstruction>;
367
+ /** eva sell instruction (ER). Reads global for feeRecipient. */
368
+ buildSellIx(p: {
369
+ trenchId: bigint;
370
+ tokenAmount: bigint;
371
+ minSolOutput?: bigint;
372
+ thinkId?: bigint;
373
+ user?: PublicKey;
374
+ }): Promise<TransactionInstruction>;
375
+ /** eva updateConfig instruction (ER, admin) — change bidding/trading durations + fee recipient. */
376
+ buildUpdateConfigIx(p: {
377
+ biddingDuration: bigint;
378
+ tradingDuration: bigint;
379
+ feeRecipient: PublicKey;
380
+ }): Promise<TransactionInstruction>;
381
+ /** eva withdraw instruction (ER) — withdraw a bid deposit from a trench (inverse of {@link buildDepositIx}). NOT the Portal SOL bridge ({@link buildWithdrawFeeFromErIx}). */
382
+ buildWithdrawIx(p: {
383
+ trenchId: bigint;
384
+ amount: bigint;
385
+ thinkId?: bigint;
386
+ user?: PublicKey;
387
+ }): Promise<TransactionInstruction>;
388
+ /** eva closePool instruction (ER, admin) — close a finished trench's pool. */
389
+ buildClosePoolIx(p: {
390
+ trenchId: bigint;
391
+ }): Promise<TransactionInstruction>;
392
+ /** eva claimTokens instruction (ER, admin) — release `user`'s tokens from the trench vault to their ATA. */
393
+ buildClaimTokensIx(p: {
394
+ trenchId: bigint;
395
+ user?: PublicKey;
396
+ }): Promise<TransactionInstruction>;
397
+ /** eva distributePrize instruction (ER, admin) — pay out the prize pool to `winners` by `amounts` (lamports). */
398
+ buildDistributePrizeIx(p: {
399
+ trenchId: bigint;
400
+ winners: PublicKey[];
401
+ amounts: bigint[];
402
+ }): Promise<TransactionInstruction>;
403
+ /**
404
+ * Bundle instruction(s) into an UNSIGNED `Transaction` (feePayer + recent blockhash set) for the
405
+ * given lane (`"er"` default, or `"l1"`). The backend signs + submits it (or adds more instructions first).
406
+ */
407
+ buildTransaction(instructions: TransactionInstruction | TransactionInstruction[], opts?: {
408
+ lane?: Lane;
409
+ feePayer?: PublicKey;
410
+ }): Promise<Transaction>;
320
411
  openSession(): Promise<{
321
412
  signature: string | null;
322
413
  sessionPda: PublicKey;
@@ -358,6 +449,7 @@ export declare class NorthstarEva {
358
449
  tokenMintPda(id: bigint): anchor.web3.PublicKey;
359
450
  trenchVault(id: bigint): anchor.web3.PublicKey;
360
451
  userAta(id: bigint, user?: PublicKey): anchor.web3.PublicKey;
452
+ agentPda(id: bigint, user?: PublicKey): anchor.web3.PublicKey;
361
453
  getGlobal(source?: Lane): Promise<GlobalState | null>;
362
454
  getTrench(id: bigint, source?: Lane, retries?: number): Promise<Trench | null>;
363
455
  getUserTokenBalance(id: bigint, user?: PublicKey, source?: Lane): Promise<bigint>;
@@ -449,4 +541,31 @@ export declare class NorthstarEva {
449
541
  trench: Trench;
450
542
  userTokens: bigint;
451
543
  }>;
544
+ /** eva updateConfig (ER, admin) — set bidding/trading durations + fee recipient on the global config. */
545
+ updateConfig(p: {
546
+ biddingDuration: bigint;
547
+ tradingDuration: bigint;
548
+ feeRecipient: PublicKey;
549
+ }): Promise<string>;
550
+ /** eva withdraw (ER) — withdraw a bid deposit from a trench (inverse of {@link deposit}; NOT the Portal SOL bridge {@link withdrawFeeFromEr}). */
551
+ withdraw(p: {
552
+ trenchId: bigint;
553
+ amount: bigint;
554
+ thinkId?: bigint;
555
+ }): Promise<string>;
556
+ /** eva closePool (ER, admin) — close a finished trench's pool. */
557
+ closePool(p: {
558
+ trenchId: bigint;
559
+ }): Promise<string>;
560
+ /** eva claimTokens (ER, admin) — release `user`'s tokens from the trench vault to their ATA. */
561
+ claimTokens(p: {
562
+ trenchId: bigint;
563
+ user?: PublicKey;
564
+ }): Promise<string>;
565
+ /** eva distributePrize (ER, admin) — pay the prize pool to `winners` by `amounts` (lamports). */
566
+ distributePrize(p: {
567
+ trenchId: bigint;
568
+ winners: PublicKey[];
569
+ amounts: bigint[];
570
+ }): Promise<string>;
452
571
  }
@@ -2352,21 +2352,117 @@ export class NorthstarEva {
2352
2352
  }
2353
2353
  get methods() { return this.program.methods; }
2354
2354
  bn(v) { return new A.BN(v.toString()); }
2355
+ // ───────────────────────── instruction builders (build, DON'T send) ─────────────────────────
2356
+ // Same inputs as the matching send-methods, but each RETURNS the unsigned instruction instead
2357
+ // of signing + submitting — so a backend can compose it with its own instructions, sign, and
2358
+ // send however it wants. State reads needed to resolve accounts still happen; only the network
2359
+ // SEND is omitted. Pair with `buildTransaction()` to get a ready-to-sign Transaction.
2360
+ /** Portal OpenSession instruction (L1). */
2361
+ buildOpenSessionIx() {
2362
+ return portal.openSessionIx({
2363
+ programId: this.cfg.portalProgramId, payer: this.payer,
2364
+ gridId: this.cfg.gridId, ttlSlots: this.cfg.ttlSlots, feeCap: this.cfg.feeCap,
2365
+ validator: this.validatorIdentity, settlementIntervalSlots: this.cfg.settlementIntervalSlots,
2366
+ });
2367
+ }
2368
+ /** Portal CloseSession instruction (L1). */
2369
+ buildCloseSessionIx() {
2370
+ return portal.closeSessionIx({ programId: this.cfg.portalProgramId, closer: this.payer });
2371
+ }
2372
+ /** Portal DepositFee instruction (L1) — "fund fee to ER payer". (Caller must ensure a session exists.) */
2373
+ buildDepositFeeIx(lamports, recipient = this.payer) {
2374
+ return portal.depositFeeIx({ programId: this.cfg.portalProgramId, depositor: this.payer, recipient, lamports });
2375
+ }
2376
+ /** "Withdraw fee from ER" instruction (ER) — system transfer from `recipient` (default: signer) → its WithdrawalSink. (Portal SOL bridge; distinct from the eva `withdraw` instruction.) */
2377
+ buildWithdrawFeeFromErIx(p) {
2378
+ const recipient = p.recipient ?? this.payer;
2379
+ if (p.toL1 && !p.toL1.equals(recipient))
2380
+ throw new Error("Arbitrary L1 withdrawal destination is not supported yet (Portal pays the deposit recipient). Omit `toL1` or set it equal to recipient.");
2381
+ return SystemProgram.transfer({ fromPubkey: recipient, toPubkey: this.getWithdrawalSink(recipient), lamports: Number(p.lamports) });
2382
+ }
2383
+ /** Next sequential trench id (= total_trenches + 1) read from the ER global state. */
2384
+ async nextTrenchId() { return (await this.requireGlobal()).totalTrenches + 1n; }
2385
+ /** eva initialize instruction (ER). */
2386
+ buildInitializeIx(p) {
2387
+ return this.methods.initialize(this.bn(p.biddingDuration), this.bn(p.tradingDuration)).accounts({ admin: this.payer }).instruction();
2388
+ }
2389
+ /** eva createTrench instruction (ER). `trenchId` defaults to {@link nextTrenchId}. */
2390
+ async buildCreateTrenchIx(p = {}) {
2391
+ const trenchId = p.trenchId ?? await this.nextTrenchId();
2392
+ const ivtr = p.initialVirtualTokenReserves ?? INITIAL_TOKEN_SUPPLY / 2n;
2393
+ return this.methods.createTrench(this.bn(trenchId), this.bn(ivtr)).accounts({ globalState: this.globalStatePda(), creator: this.payer }).instruction();
2394
+ }
2395
+ /** eva deposit instruction (ER). */
2396
+ buildDepositIx(p) {
2397
+ return this.methods.deposit(this.bn(p.lamports), this.bn(p.thinkId ?? 0n)).accounts({ trench: this.trenchPda(p.trenchId), user: p.user ?? this.payer }).instruction();
2398
+ }
2399
+ /** eva finalize instruction (ER). Reads global for feeRecipient; does NOT wait for the bidding window. */
2400
+ async buildFinalizeIx(p) {
2401
+ const g = await this.requireGlobal();
2402
+ return this.methods.finalize().accounts({ trench: this.trenchPda(p.trenchId), globalState: this.globalStatePda(), feeRecipient: new PublicKey(g.feeRecipient), admin: this.payer }).instruction();
2403
+ }
2404
+ /** ATA-create instruction (ER) for a user's trench token account (idempotent). */
2405
+ buildUserAtaIx(p) {
2406
+ const user = p.user ?? this.payer;
2407
+ return createAssociatedTokenAccountIdempotentInstruction(this.payer, this.userAta(p.trenchId, user), user, this.tokenMintPda(p.trenchId), TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID);
2408
+ }
2409
+ /** eva buy instruction (ER). Reads global for feeRecipient. */
2410
+ async buildBuyIx(p) {
2411
+ const g = await this.requireGlobal();
2412
+ const user = p.user ?? this.payer;
2413
+ return this.methods.buy(this.bn(p.solPayWithFee), this.bn(p.thinkId ?? 0n)).accounts({ trench: this.trenchPda(p.trenchId), globalState: this.globalStatePda(), feeRecipient: new PublicKey(g.feeRecipient), user, trenchTokenVault: this.trenchVault(p.trenchId), userTokenAccount: this.userAta(p.trenchId, user), systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID }).instruction();
2414
+ }
2415
+ /** eva sell instruction (ER). Reads global for feeRecipient. */
2416
+ async buildSellIx(p) {
2417
+ const g = await this.requireGlobal();
2418
+ const user = p.user ?? this.payer;
2419
+ return this.methods.sell(this.bn(p.tokenAmount), this.bn(p.minSolOutput ?? 0n), this.bn(p.thinkId ?? 0n)).accounts({ trench: this.trenchPda(p.trenchId), globalState: this.globalStatePda(), feeRecipient: new PublicKey(g.feeRecipient), user, trenchTokenVault: this.trenchVault(p.trenchId), userTokenAccount: this.userAta(p.trenchId, user), tokenProgram: TOKEN_PROGRAM_ID }).instruction();
2420
+ }
2421
+ /** eva updateConfig instruction (ER, admin) — change bidding/trading durations + fee recipient. */
2422
+ buildUpdateConfigIx(p) {
2423
+ return this.methods.updateConfig(this.bn(p.biddingDuration), this.bn(p.tradingDuration), p.feeRecipient).accounts({ globalState: this.globalStatePda(), admin: this.payer }).instruction();
2424
+ }
2425
+ /** eva withdraw instruction (ER) — withdraw a bid deposit from a trench (inverse of {@link buildDepositIx}). NOT the Portal SOL bridge ({@link buildWithdrawFeeFromErIx}). */
2426
+ buildWithdrawIx(p) {
2427
+ const user = p.user ?? this.payer;
2428
+ return this.methods.withdraw(this.bn(p.amount), this.bn(p.thinkId ?? 0n)).accounts({ trench: this.trenchPda(p.trenchId), agent: this.agentPda(p.trenchId, user), user }).instruction();
2429
+ }
2430
+ /** eva closePool instruction (ER, admin) — close a finished trench's pool. */
2431
+ async buildClosePoolIx(p) {
2432
+ const g = await this.requireGlobal();
2433
+ return this.methods.closePool().accounts({ trench: this.trenchPda(p.trenchId), globalState: this.globalStatePda(), feeRecipient: new PublicKey(g.feeRecipient), admin: this.payer }).instruction();
2434
+ }
2435
+ /** eva claimTokens instruction (ER, admin) — release `user`'s tokens from the trench vault to their ATA. */
2436
+ buildClaimTokensIx(p) {
2437
+ const user = p.user ?? this.payer;
2438
+ return this.methods.claimTokens().accounts({ globalState: this.globalStatePda(), trench: this.trenchPda(p.trenchId), agent: this.agentPda(p.trenchId, user), user, admin: this.payer, trenchTokenVault: this.trenchVault(p.trenchId), userTokenAccount: this.userAta(p.trenchId, user), tokenProgram: TOKEN_PROGRAM_ID }).instruction();
2439
+ }
2440
+ /** eva distributePrize instruction (ER, admin) — pay out the prize pool to `winners` by `amounts` (lamports). */
2441
+ async buildDistributePrizeIx(p) {
2442
+ const g = await this.requireGlobal();
2443
+ return this.methods.distributePrize(p.winners, p.amounts.map((a) => this.bn(a))).accounts({ trench: this.trenchPda(p.trenchId), globalState: this.globalStatePda(), feeRecipient: new PublicKey(g.feeRecipient), admin: this.payer }).instruction();
2444
+ }
2445
+ /**
2446
+ * Bundle instruction(s) into an UNSIGNED `Transaction` (feePayer + recent blockhash set) for the
2447
+ * given lane (`"er"` default, or `"l1"`). The backend signs + submits it (or adds more instructions first).
2448
+ */
2449
+ async buildTransaction(instructions, opts = {}) {
2450
+ const conn = opts.lane === "l1" ? this.l1 : this.er;
2451
+ const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash(opts.lane === "l1" ? "confirmed" : "processed");
2452
+ const tx = new Transaction({ feePayer: opts.feePayer ?? this.payer, blockhash, lastValidBlockHeight });
2453
+ tx.add(...(Array.isArray(instructions) ? instructions : [instructions]));
2454
+ return tx;
2455
+ }
2355
2456
  // ───────────────────────── Portal control (L1) ─────────────────────────
2356
2457
  async openSession() {
2357
2458
  const pda = this.sessionPda();
2358
2459
  if (await this.l1GetAccount(pda))
2359
2460
  return { signature: null, sessionPda: pda, alreadyOpen: true };
2360
- const ix = portal.openSessionIx({
2361
- programId: this.cfg.portalProgramId, payer: this.wallet.publicKey,
2362
- gridId: this.cfg.gridId, ttlSlots: this.cfg.ttlSlots, feeCap: this.cfg.feeCap,
2363
- validator: this.validatorIdentity, settlementIntervalSlots: this.cfg.settlementIntervalSlots,
2364
- });
2365
- const signature = await this.sendOnL1([ix], [this.wallet]);
2461
+ const signature = await this.sendOnL1([this.buildOpenSessionIx()], [this.wallet]);
2366
2462
  return { signature, sessionPda: pda, alreadyOpen: false };
2367
2463
  }
2368
2464
  async closeSession() {
2369
- return this.sendOnL1([portal.closeSessionIx({ programId: this.cfg.portalProgramId, closer: this.wallet.publicKey })], [this.wallet]);
2465
+ return this.sendOnL1([this.buildCloseSessionIx()], [this.wallet]);
2370
2466
  }
2371
2467
  /**
2372
2468
  * Ensure the Portal session is open (idempotent). Opens it via {@link openSession} only if
@@ -2393,7 +2489,7 @@ export class NorthstarEva {
2393
2489
  async fundErFeePayer(lamports, recipient = this.wallet.publicKey, opts = {}) {
2394
2490
  if (opts.ensureSession !== false)
2395
2491
  await this.ensureSession();
2396
- return this.sendOnL1([portal.depositFeeIx({ programId: this.cfg.portalProgramId, depositor: this.wallet.publicKey, recipient, lamports })], [this.wallet]);
2492
+ return this.sendOnL1([this.buildDepositFeeIx(lamports, recipient)], [this.wallet]);
2397
2493
  }
2398
2494
  /** Ensure the ER fee payer has >= `lamports`; tops up via DepositFee + waits for the credit. */
2399
2495
  async ensureErFunds(lamports, recipient = this.wallet.publicKey) {
@@ -2431,6 +2527,7 @@ export class NorthstarEva {
2431
2527
  tokenMintPda(id) { return evaPdas.tokenMintPda(this.cfg.evaProgramId, this.trenchPda(id)); }
2432
2528
  trenchVault(id) { return evaPdas.trenchTokenVault(this.tokenMintPda(id), this.trenchPda(id)); }
2433
2529
  userAta(id, user = this.wallet.publicKey) { return evaPdas.userTokenAccount(this.tokenMintPda(id), user); }
2530
+ agentPda(id, user = this.wallet.publicKey) { return evaPdas.agentPda(this.cfg.evaProgramId, this.trenchPda(id), user); }
2434
2531
  // ───────────────────────── eva reads (processed by default) ─────────────────────────
2435
2532
  async getGlobal(source = "er") {
2436
2533
  const info = source === "er" ? await this.erGetAccount(this.globalStatePda()) : await this.l1GetAccount(this.globalStatePda());
@@ -2491,11 +2588,7 @@ export class NorthstarEva {
2491
2588
  */
2492
2589
  async requestWithdraw(p) {
2493
2590
  const recipient = p.recipient ?? this.wallet;
2494
- if (p.toL1 && !p.toL1.equals(recipient.publicKey)) {
2495
- throw new Error("Arbitrary L1 withdrawal destination is not supported yet (Portal pays the deposit recipient). Omit `toL1` or set it equal to recipient.");
2496
- }
2497
- const sink = this.getWithdrawalSink(recipient.publicKey);
2498
- const ix = SystemProgram.transfer({ fromPubkey: recipient.publicKey, toPubkey: sink, lamports: Number(p.lamports) });
2591
+ const ix = this.buildWithdrawFeeFromErIx({ lamports: p.lamports, recipient: recipient.publicKey, toL1: p.toL1 });
2499
2592
  return this.sendOnEr([ix], [recipient]);
2500
2593
  }
2501
2594
  /** "Withdraw fee from ER" (the green-box withdraw API) — alias of {@link requestWithdraw}. */
@@ -2519,30 +2612,20 @@ export class NorthstarEva {
2519
2612
  async initialize(p) {
2520
2613
  if (await this.getGlobal("er"))
2521
2614
  return null; // already initialized
2522
- const ix = await this.methods.initialize(this.bn(p.biddingDuration), this.bn(p.tradingDuration))
2523
- .accounts({ admin: this.wallet.publicKey }).instruction();
2524
- return this.sendEva(ix, "initialize");
2615
+ return this.sendEva(await this.buildInitializeIx(p), "initialize");
2525
2616
  }
2526
2617
  /** createTrench with the next sequential id (id == total_trenches + 1). */
2527
2618
  async createTrench(p = {}) {
2528
- const g = await this.getGlobal("er");
2529
- if (!g)
2530
- throw new Error("global state not initialized — call initialize() first");
2531
- const trenchId = g.totalTrenches + 1n;
2532
- const ivtr = p.initialVirtualTokenReserves ?? INITIAL_TOKEN_SUPPLY / 2n;
2533
- const ix = await this.methods.createTrench(this.bn(trenchId), this.bn(ivtr))
2534
- .accounts({ globalState: this.globalStatePda(), creator: this.wallet.publicKey }).instruction();
2619
+ const trenchId = await this.nextTrenchId();
2620
+ const ix = await this.buildCreateTrenchIx({ trenchId, initialVirtualTokenReserves: p.initialVirtualTokenReserves });
2535
2621
  const signature = await this.sendEva(ix, "createTrench");
2536
2622
  return { trenchId, signature };
2537
2623
  }
2538
2624
  async deposit(p) {
2539
- const ix = await this.methods.deposit(this.bn(p.lamports), this.bn(p.thinkId ?? 0n))
2540
- .accounts({ trench: this.trenchPda(p.trenchId), user: this.wallet.publicKey }).instruction();
2541
- return this.sendEva(ix, "deposit");
2625
+ return this.sendEva(await this.buildDepositIx(p), "deposit");
2542
2626
  }
2543
2627
  /** finalize — waits for the ER slot to pass biddingEndBlock, then seeds the AMM. */
2544
2628
  async finalize(p) {
2545
- const g = await this.requireGlobal();
2546
2629
  const t = await this.getTrench(p.trenchId, "er", 10);
2547
2630
  if (t)
2548
2631
  for (let i = 0; i < 80; i++) {
@@ -2551,28 +2634,38 @@ export class NorthstarEva {
2551
2634
  break;
2552
2635
  await sleep(this.pollMs);
2553
2636
  }
2554
- const ix = await this.methods.finalize()
2555
- .accounts({ trench: this.trenchPda(p.trenchId), globalState: this.globalStatePda(), feeRecipient: new PublicKey(g.feeRecipient), admin: this.wallet.publicKey }).instruction();
2556
- return this.sendEva(ix, "finalize");
2637
+ return this.sendEva(await this.buildFinalizeIx(p), "finalize");
2557
2638
  }
2558
2639
  /** Create the user's token ATA on the ER (buy/sell require it to pre-exist). */
2559
2640
  async ensureUserAta(p) {
2560
- const user = p.user ?? this.wallet.publicKey;
2561
- const ix = createAssociatedTokenAccountIdempotentInstruction(this.wallet.publicKey, this.userAta(p.trenchId, user), user, this.tokenMintPda(p.trenchId), TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID);
2562
- return this.sendEva(ix, "ensureUserAta");
2641
+ return this.sendEva(this.buildUserAtaIx(p), "ensureUserAta");
2563
2642
  }
2564
2643
  async buy(p) {
2565
- const g = await this.requireGlobal();
2566
- const ix = await this.methods.buy(this.bn(p.solPayWithFee), this.bn(p.thinkId ?? 0n))
2567
- .accounts({ trench: this.trenchPda(p.trenchId), globalState: this.globalStatePda(), feeRecipient: new PublicKey(g.feeRecipient), user: this.wallet.publicKey, trenchTokenVault: this.trenchVault(p.trenchId), userTokenAccount: this.userAta(p.trenchId), systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID }).instruction();
2568
- const signature = await this.sendEva(ix, "buy");
2644
+ const signature = await this.sendEva(await this.buildBuyIx(p), "buy");
2569
2645
  return { signature, trench: (await this.getTrench(p.trenchId, "er", 5)), userTokens: await this.getUserTokenBalance(p.trenchId) };
2570
2646
  }
2571
2647
  async sell(p) {
2572
- const g = await this.requireGlobal();
2573
- const ix = await this.methods.sell(this.bn(p.tokenAmount), this.bn(p.minSolOutput ?? 0n), this.bn(p.thinkId ?? 0n))
2574
- .accounts({ trench: this.trenchPda(p.trenchId), globalState: this.globalStatePda(), feeRecipient: new PublicKey(g.feeRecipient), user: this.wallet.publicKey, trenchTokenVault: this.trenchVault(p.trenchId), userTokenAccount: this.userAta(p.trenchId), tokenProgram: TOKEN_PROGRAM_ID }).instruction();
2575
- const signature = await this.sendEva(ix, "sell");
2648
+ const signature = await this.sendEva(await this.buildSellIx(p), "sell");
2576
2649
  return { signature, trench: (await this.getTrench(p.trenchId, "er", 5)), userTokens: await this.getUserTokenBalance(p.trenchId) };
2577
2650
  }
2651
+ /** eva updateConfig (ER, admin) — set bidding/trading durations + fee recipient on the global config. */
2652
+ async updateConfig(p) {
2653
+ return this.sendEva(await this.buildUpdateConfigIx(p), "updateConfig");
2654
+ }
2655
+ /** eva withdraw (ER) — withdraw a bid deposit from a trench (inverse of {@link deposit}; NOT the Portal SOL bridge {@link withdrawFeeFromEr}). */
2656
+ async withdraw(p) {
2657
+ return this.sendEva(await this.buildWithdrawIx(p), "withdraw");
2658
+ }
2659
+ /** eva closePool (ER, admin) — close a finished trench's pool. */
2660
+ async closePool(p) {
2661
+ return this.sendEva(await this.buildClosePoolIx(p), "closePool");
2662
+ }
2663
+ /** eva claimTokens (ER, admin) — release `user`'s tokens from the trench vault to their ATA. */
2664
+ async claimTokens(p) {
2665
+ return this.sendEva(await this.buildClaimTokensIx(p), "claimTokens");
2666
+ }
2667
+ /** eva distributePrize (ER, admin) — pay the prize pool to `winners` by `amounts` (lamports). */
2668
+ async distributePrize(p) {
2669
+ return this.sendEva(await this.buildDistributePrizeIx(p), "distributePrize");
2670
+ }
2578
2671
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "northstar-eva-sdk",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Run the eva program on a NorthStar ephemeral rollup (zero-fee hot path). A high-level class SDK over the NorthStar Portal/ER protocol + the eva Anchor program. Works localnet & devnet.",
5
5
  "type": "module",
6
6
  "license": "MIT",