northstar-eva-sdk 0.1.0 → 0.1.1

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
@@ -13,6 +13,25 @@ const sdk = NorthstarEva.create({ network: "localnet", wallet });
13
13
  ```
14
14
  > 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).
15
15
 
16
+ ## What's new in 0.1.1
17
+
18
+ The deposit ("fund fee to ER payer") API now **contains the open-session step**, plus two helper additions:
19
+
20
+ - **`fundFeeToErPayer` / `fundErFeePayer` now open the session for you.** Portal `DepositFee` requires an open session (the DepositReceipt PDA is derived from it). Before, you had to call `openSession()` yourself first; now the deposit call ensures it. Opt out with `{ ensureSession: false }` if you already opened it.
21
+ ```ts
22
+ // 0.1.0 — two calls, deposit fails if you forgot openSession
23
+ await sdk.openSession();
24
+ await sdk.fundFeeToErPayer(200_000_000n);
25
+ // 0.1.1 — one call; the session is opened automatically (idempotent)
26
+ await sdk.fundFeeToErPayer(200_000_000n);
27
+ ```
28
+ - **New `ensureSession()`** — the named, idempotent "open the session if it isn't already" method (opens only when the session PDA is absent). This is the step `fundFeeToErPayer` performs internally; call it directly if you want the session open up front.
29
+ - **New `withdrawFeeFromEr(...)`** — alias of `requestWithdraw(...)` that matches the "withdraw fee from ER" API name.
30
+
31
+ > **Backwards compatible:** existing code that calls `openSession()` before depositing still works (the extra `ensureSession` is a no-op when the session already exists). No signatures or return types changed.
32
+
33
+ The three user-fund APIs: **query balance** → `querySolBalance(account?)` · **deposit** → `fundFeeToErPayer(lamports, recipient?)` · **withdraw** → `withdrawFeeFromEr({ lamports, recipient, toL1? })`.
34
+
16
35
  ## What it is + the fee point
17
36
 
18
37
  NorthStar is **one validator process with two lanes**:
@@ -24,7 +43,7 @@ The fee-reduction play: do the cheap, one-time **setup/control/custody** on L1 (
24
43
 
25
44
  | Operation | Lane |
26
45
  | --- | --- |
27
- | `openSession`, `fundErFeePayer`/`ensureErFunds`, `delegateKeypairAccount`, `createPortalOwnedAccount`, `closeSession` | **L1** |
46
+ | `openSession`/`ensureSession`, `fundFeeToErPayer`/`fundErFeePayer`/`ensureErFunds`, `delegateKeypairAccount`, `createPortalOwnedAccount`, `closeSession` | **L1** |
28
47
  | eva `initialize` / `createTrench` / `deposit` / `finalize` / `ensureUserAta` / `buy` / `sell` | **ER (zero fee)** |
29
48
  | Reads (`erGetAccount`, `getGlobal`, `getTrench`, …) | **Either** — ER reads pinned to commitment `processed` so read-after-write is fresh |
30
49
 
@@ -69,10 +88,11 @@ const sdk = NorthstarEva.create({ network: "localnet", wallet });
69
88
  const h = await sdk.health();
70
89
  if (!h.l1 || !h.er) throw new Error(`validator not healthy: ${JSON.stringify(h)}`);
71
90
 
72
- // 2. Open the Portal session on L1 (idempotent no-op if already open)
73
- await sdk.openSession();
91
+ // 2. (Optional since 0.1.1) Open the Portal session on L1 — the deposit below ensures it for you.
92
+ await sdk.ensureSession(); // idempotent; no-op if already open
74
93
 
75
- // 3. Fund the ER fee payer via Portal DepositFee on L1 (ER balance = these credits)
94
+ // 3. Fund the ER fee payer via Portal DepositFee on L1 (ER balance = these credits).
95
+ // ensureErFunds/fundFeeToErPayer open the session automatically if step 2 was skipped.
76
96
  await sdk.ensureErFunds(250_000_000n); // tops up + waits for the credit to land
77
97
 
78
98
  // 4. Initialize the eva global state on the ER (idempotent)
@@ -198,10 +218,12 @@ The "works devnet later" claim is true only against a **self-hosted NorthStar de
198
218
  | Method | Lane | Description | Returns |
199
219
  | --- | --- | --- | --- |
200
220
  | `openSession()` | L1 | Open the global Portal session (idempotent — checks if the session PDA exists first). | `Promise<{ signature: string \| null; sessionPda: PublicKey; alreadyOpen: boolean }>` |
221
+ | `ensureSession()` | L1 | **The "open session" step** that DepositFee depends on. Idempotent: opens the session only if its PDA is absent. Called for you by `fundFeeToErPayer`/`fundErFeePayer`. | `Promise<{ sessionPda: PublicKey; opened: boolean; signature: string \| null }>` |
201
222
  | `closeSession()` | L1 | Close the session. | `Promise<string>` (signature) |
202
223
  | `depositReceiptPda(recipient?)` | — (pure) | Derive the DepositReceipt PDA for a recipient (defaults to wallet). | `PublicKey` |
203
- | `fundErFeePayer(lamports, recipient?)` | L1 | Portal `DepositFee` credits an ER fee payer (its **ER balance = these credits**, not the L1 balance). | `Promise<string>` |
204
- | `ensureErFunds(lamports, recipient?)` | L1 + ER | Ensure the ER fee payer has `>= lamports`; tops up via `DepositFee` and polls up to 25×800ms (~20s) for the credit. | `Promise<bigint>` (resulting balance) |
224
+ | `fundFeeToErPayer(lamports, recipient?, { ensureSession? })` | L1 | **"Fund fee to ER payer"** deposit. **Contains the open-session step** calls `ensureSession()` first (DepositFee requires a session), then Portal `DepositFee`. Pass `{ ensureSession: false }` to skip. | `Promise<string>` (DepositFee sig) |
225
+ | `fundErFeePayer(lamports, recipient?, { ensureSession? })` | L1 | Same as above (the underlying impl). Portal `DepositFee` — credits an ER fee payer (its **ER balance = these credits**, not the L1 balance); ensures the session first. | `Promise<string>` |
226
+ | `ensureErFunds(lamports, recipient?)` | L1 + ER | Ensure the ER fee payer has `>= lamports`; tops up via `DepositFee` (which ensures the session) and polls up to 25×800ms (~20s) for the credit. | `Promise<bigint>` (resulting balance) |
205
227
  | `delegateKeypairAccount(target: Keypair)` | L1 | Delegate a plain System-owned keypair account into the ER (the "proper" delegation path). | `Promise<string>` |
206
228
  | `createPortalOwnedAccount(space?)` | L1 | Create a fresh Portal-owned account (delegation-target helper). | `Promise<Keypair>` |
207
229
 
@@ -269,9 +269,30 @@ export declare class NorthstarEva {
269
269
  alreadyOpen: boolean;
270
270
  }>;
271
271
  closeSession(): Promise<string>;
272
+ /**
273
+ * Ensure the Portal session is open (idempotent). Opens it via {@link openSession} only if
274
+ * the session PDA doesn't exist yet; otherwise does nothing (no tx, no fee).
275
+ *
276
+ * THIS is the "open session" step that DepositFee depends on: the DepositReceipt PDA is
277
+ * derived from the session, so `fundFeeToErPayer` / `fundErFeePayer` call this for you
278
+ * before depositing. Call it directly if you want the session open up front.
279
+ */
280
+ ensureSession(): Promise<{
281
+ sessionPda: PublicKey;
282
+ opened: boolean;
283
+ signature: string | null;
284
+ }>;
272
285
  depositReceiptPda(recipient?: PublicKey): anchor.web3.PublicKey;
273
- /** DepositFee on L1 — funds an ER fee payer (its ER balance = these credits). */
274
- fundErFeePayer(lamports: bigint, recipient?: PublicKey): Promise<string>;
286
+ /**
287
+ * DepositFee on L1 — funds an ER fee payer (its ER balance = these credits).
288
+ * DepositFee REQUIRES an open Portal session (the DepositReceipt PDA is derived from it), so
289
+ * this ENSURES the session is open first via {@link ensureSession} (idempotent). Pass
290
+ * `{ ensureSession: false }` to skip that if you already opened the session yourself.
291
+ * Returns the DepositFee transaction signature.
292
+ */
293
+ fundErFeePayer(lamports: bigint, recipient?: PublicKey, opts?: {
294
+ ensureSession?: boolean;
295
+ }): Promise<string>;
275
296
  /** Ensure the ER fee payer has >= `lamports`; tops up via DepositFee + waits for the credit. */
276
297
  ensureErFunds(lamports: bigint, recipient?: PublicKey): Promise<bigint>;
277
298
  /** Delegate a plain keypair account (System-owned) into the ER (the "proper" path). */
@@ -294,8 +315,17 @@ export declare class NorthstarEva {
294
315
  er: bigint;
295
316
  l1: bigint;
296
317
  }>;
297
- /** "Fund fee to ER payer" — deposit SOL from L1 so `recipient` is credited & spendable on the ER. Alias of fundErFeePayer. */
298
- fundFeeToErPayer(lamports: bigint, recipient?: PublicKey): Promise<string>;
318
+ /**
319
+ * "Fund fee to ER payer" (the green-box deposit API) — deposit SOL from L1 so `recipient`
320
+ * is credited & spendable on the ER.
321
+ *
322
+ * This NOW CONTAINS the open-session step: it calls {@link ensureSession} first (idempotent)
323
+ * because Portal DepositFee requires an open session. Pass `{ ensureSession: false }` to skip
324
+ * it if you already opened the session. Returns the DepositFee transaction signature.
325
+ */
326
+ fundFeeToErPayer(lamports: bigint, recipient?: PublicKey, opts?: {
327
+ ensureSession?: boolean;
328
+ }): Promise<string>;
299
329
  /** The ER WithdrawalSink PDA for a recipient (where ER withdrawal requests are sent). */
300
330
  getWithdrawalSink(recipient?: PublicKey): PublicKey;
301
331
  /**
@@ -312,6 +342,12 @@ export declare class NorthstarEva {
312
342
  recipient: Keypair;
313
343
  toL1?: PublicKey;
314
344
  }): Promise<ErTxResult>;
345
+ /** "Withdraw fee from ER" (the green-box withdraw API) — alias of {@link requestWithdraw}. */
346
+ withdrawFeeFromEr(p: {
347
+ lamports: bigint;
348
+ recipient: Keypair;
349
+ toL1?: PublicKey;
350
+ }): Promise<ErTxResult>;
315
351
  /** Poll an account's L1 balance until it rises by >= `expectedDelta` (settlement is async). Returns the final L1 balance. */
316
352
  waitForL1Settlement(account: PublicKey, expectedDelta: bigint, timeoutMs?: number): Promise<bigint>;
317
353
  initialize(p: {
@@ -2296,11 +2296,31 @@ export class NorthstarEva {
2296
2296
  async closeSession() {
2297
2297
  return this.sendOnL1([portal.closeSessionIx({ programId: this.cfg.portalProgramId, closer: this.wallet.publicKey })], [this.wallet]);
2298
2298
  }
2299
+ /**
2300
+ * Ensure the Portal session is open (idempotent). Opens it via {@link openSession} only if
2301
+ * the session PDA doesn't exist yet; otherwise does nothing (no tx, no fee).
2302
+ *
2303
+ * THIS is the "open session" step that DepositFee depends on: the DepositReceipt PDA is
2304
+ * derived from the session, so `fundFeeToErPayer` / `fundErFeePayer` call this for you
2305
+ * before depositing. Call it directly if you want the session open up front.
2306
+ */
2307
+ async ensureSession() {
2308
+ const r = await this.openSession();
2309
+ return { sessionPda: r.sessionPda, opened: !r.alreadyOpen, signature: r.signature };
2310
+ }
2299
2311
  depositReceiptPda(recipient = this.wallet.publicKey) {
2300
2312
  return portal.depositReceiptPda(this.cfg.portalProgramId, this.sessionPda(), recipient);
2301
2313
  }
2302
- /** DepositFee on L1 — funds an ER fee payer (its ER balance = these credits). */
2303
- async fundErFeePayer(lamports, recipient = this.wallet.publicKey) {
2314
+ /**
2315
+ * DepositFee on L1 — funds an ER fee payer (its ER balance = these credits).
2316
+ * DepositFee REQUIRES an open Portal session (the DepositReceipt PDA is derived from it), so
2317
+ * this ENSURES the session is open first via {@link ensureSession} (idempotent). Pass
2318
+ * `{ ensureSession: false }` to skip that if you already opened the session yourself.
2319
+ * Returns the DepositFee transaction signature.
2320
+ */
2321
+ async fundErFeePayer(lamports, recipient = this.wallet.publicKey, opts = {}) {
2322
+ if (opts.ensureSession !== false)
2323
+ await this.ensureSession();
2304
2324
  return this.sendOnL1([portal.depositFeeIx({ programId: this.cfg.portalProgramId, depositor: this.wallet.publicKey, recipient, lamports })], [this.wallet]);
2305
2325
  }
2306
2326
  /** Ensure the ER fee payer has >= `lamports`; tops up via DepositFee + waits for the credit. */
@@ -2372,9 +2392,16 @@ export class NorthstarEva {
2372
2392
  const l1 = (await this.l1GetAccount(account))?.lamports ?? 0n;
2373
2393
  return { er, l1 };
2374
2394
  }
2375
- /** "Fund fee to ER payer" — deposit SOL from L1 so `recipient` is credited & spendable on the ER. Alias of fundErFeePayer. */
2376
- async fundFeeToErPayer(lamports, recipient = this.wallet.publicKey) {
2377
- return this.fundErFeePayer(lamports, recipient);
2395
+ /**
2396
+ * "Fund fee to ER payer" (the green-box deposit API) — deposit SOL from L1 so `recipient`
2397
+ * is credited & spendable on the ER.
2398
+ *
2399
+ * This NOW CONTAINS the open-session step: it calls {@link ensureSession} first (idempotent)
2400
+ * because Portal DepositFee requires an open session. Pass `{ ensureSession: false }` to skip
2401
+ * it if you already opened the session. Returns the DepositFee transaction signature.
2402
+ */
2403
+ async fundFeeToErPayer(lamports, recipient = this.wallet.publicKey, opts = {}) {
2404
+ return this.fundErFeePayer(lamports, recipient, opts);
2378
2405
  }
2379
2406
  /** The ER WithdrawalSink PDA for a recipient (where ER withdrawal requests are sent). */
2380
2407
  getWithdrawalSink(recipient = this.wallet.publicKey) {
@@ -2397,6 +2424,10 @@ export class NorthstarEva {
2397
2424
  const ix = SystemProgram.transfer({ fromPubkey: p.recipient.publicKey, toPubkey: sink, lamports: Number(p.lamports) });
2398
2425
  return this.sendOnEr([ix], [p.recipient]);
2399
2426
  }
2427
+ /** "Withdraw fee from ER" (the green-box withdraw API) — alias of {@link requestWithdraw}. */
2428
+ async withdrawFeeFromEr(p) {
2429
+ return this.requestWithdraw(p);
2430
+ }
2400
2431
  /** Poll an account's L1 balance until it rises by >= `expectedDelta` (settlement is async). Returns the final L1 balance. */
2401
2432
  async waitForL1Settlement(account, expectedDelta, timeoutMs = 60_000) {
2402
2433
  const start = (await this.l1GetAccount(account))?.lamports ?? 0n;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "northstar-eva-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
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",