northstar-eva-sdk 0.5.0 → 0.7.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 +3 -2
- package/dist/northstar-eva-sdk.d.ts +64 -8
- package/dist/northstar-eva-sdk.js +74 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@ const sdk = NorthstarEva.create({ provider, erRpc: "https://ephemeral.devnet.son
|
|
|
36
36
|
- **`l1Endpoint`** means "the provider's RPC is the ER; this is the L1." It's the cleanest fit when your app's provider already points at the ER. (`L1Endpoint` is accepted too.)
|
|
37
37
|
- **Same SDK, both services:** with no `l1Endpoint`/`erRpc`, you get a normal eva client — one codebase covers the NorthStar and non-NorthStar deployments.
|
|
38
38
|
- **Withdraw returns to the sender:** `withdrawFeeFromEr({ lamports })` reclaims the fee to whoever funded the ER (the SDK signer) — no recipient needed.
|
|
39
|
+
- **Session helpers:** `await sdk.isSessionOpen()` (read-only boolean), `await sdk.getSession()` (decoded session struct — validator, `ttlSlots`, `createdAt`, `expiresAtSlot`, … or `null`), and `await sdk.buildEnsureSessionIx()` (returns the OpenSession instruction to compose/sign yourself, or `null` if the session already exists).
|
|
39
40
|
|
|
40
41
|
## What's new in 0.4.0
|
|
41
42
|
|
|
@@ -68,7 +69,7 @@ tx.sign(myKeypair); // or wallet.signTransaction(tx)
|
|
|
68
69
|
await connection.sendRawTransaction(tx.serialize());
|
|
69
70
|
```
|
|
70
71
|
|
|
71
|
-
Builders (each mirrors the send-method's inputs): `buildOpenSessionIx()`, `buildCloseSessionIx()`, `buildDepositFeeIx(lamports, recipient?)`, `buildWithdrawFeeFromErIx({ lamports,
|
|
72
|
+
Builders (each mirrors the send-method's inputs): `buildOpenSessionIx()`, `buildCloseSessionIx()`, `buildDepositFeeIx(lamports, recipient?)`, `buildWithdrawFeeFromErIx({ lamports, sender?, 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.
|
|
72
73
|
|
|
73
74
|
> 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**.
|
|
74
75
|
|
|
@@ -133,7 +134,7 @@ The deposit ("fund fee to ER payer") API now **contains the open-session step**,
|
|
|
133
134
|
|
|
134
135
|
> **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.
|
|
135
136
|
|
|
136
|
-
The three user-fund APIs: **query balance** → `querySolBalance(account?)` · **deposit** → `fundFeeToErPayer(lamports, recipient?)` · **withdraw** → `withdrawFeeFromEr({ lamports,
|
|
137
|
+
The three user-fund APIs: **query balance** → `querySolBalance(account?)` · **deposit** → `fundFeeToErPayer(lamports, recipient?)` · **withdraw** → `withdrawFeeFromEr({ lamports, sender?, toL1? })`.
|
|
137
138
|
|
|
138
139
|
## What it is + the fee point
|
|
139
140
|
|
|
@@ -101,6 +101,46 @@ export interface GlobalState {
|
|
|
101
101
|
tradingDuration: bigint;
|
|
102
102
|
}
|
|
103
103
|
export declare function decodeGlobalState(data: Uint8Array): GlobalState;
|
|
104
|
+
export declare const SESSION_LEN = 219;
|
|
105
|
+
export declare const SESSION_DISCRIMINATOR = 1;
|
|
106
|
+
export declare const SESSION_OFFSETS: {
|
|
107
|
+
readonly discriminator: 0;
|
|
108
|
+
readonly gridId: 1;
|
|
109
|
+
readonly ttlSlots: 9;
|
|
110
|
+
readonly feeCap: 17;
|
|
111
|
+
readonly createdAt: 25;
|
|
112
|
+
readonly nonce: 33;
|
|
113
|
+
readonly authority: 49;
|
|
114
|
+
readonly validator: 81;
|
|
115
|
+
readonly settlementIntervalSlots: 113;
|
|
116
|
+
readonly lastSettledL1Slot: 121;
|
|
117
|
+
readonly lastSettledErSlot: 129;
|
|
118
|
+
readonly settlementStatus: 137;
|
|
119
|
+
readonly settlementErSlot: 138;
|
|
120
|
+
readonly settlementStartedL1Slot: 210;
|
|
121
|
+
readonly bump: 218;
|
|
122
|
+
};
|
|
123
|
+
export type SettlementStatus = "Idle" | "InProgress" | "Unknown";
|
|
124
|
+
export interface SessionAccount {
|
|
125
|
+
discriminator: number;
|
|
126
|
+
gridId: bigint;
|
|
127
|
+
ttlSlots: bigint;
|
|
128
|
+
feeCap: bigint;
|
|
129
|
+
createdAt: bigint;
|
|
130
|
+
/** created_at + ttl_slots — the session is expired once the L1 slot passes this (matches the program's `is_expired`). */
|
|
131
|
+
expiresAtSlot: bigint;
|
|
132
|
+
nonce: bigint;
|
|
133
|
+
authority: Uint8Array;
|
|
134
|
+
validator: Uint8Array;
|
|
135
|
+
settlementIntervalSlots: bigint;
|
|
136
|
+
lastSettledL1Slot: bigint;
|
|
137
|
+
lastSettledErSlot: bigint;
|
|
138
|
+
settlementStatus: SettlementStatus;
|
|
139
|
+
settlementErSlot: bigint;
|
|
140
|
+
settlementStartedL1Slot: bigint;
|
|
141
|
+
bump: number;
|
|
142
|
+
}
|
|
143
|
+
export declare function decodeSession(data: Uint8Array): SessionAccount;
|
|
104
144
|
export declare function decodeTokenAmount(data: Uint8Array): bigint;
|
|
105
145
|
/**
|
|
106
146
|
* Network presets + resolved config. Works for localnet now and devnet later —
|
|
@@ -319,6 +359,15 @@ export declare class NorthstarEva {
|
|
|
319
359
|
getSessionPdaFromEr(): Promise<any>;
|
|
320
360
|
getDelegatedAccounts(): Promise<string[]>;
|
|
321
361
|
sessionPda(): anchor.web3.PublicKey;
|
|
362
|
+
/**
|
|
363
|
+
* Read + decode the Portal session account from L1 (the single global session for this Portal).
|
|
364
|
+
* Returns `null` if no valid session exists. Read-only — no transaction, no fee. Use the returned
|
|
365
|
+
* `ttlSlots`/`createdAt`/`expiresAtSlot`/`validator` for details (compare `expiresAtSlot` to the
|
|
366
|
+
* current L1 slot — `await this.l1.getSlot()` — to tell if it's expired).
|
|
367
|
+
*/
|
|
368
|
+
getSession(): Promise<SessionAccount | null>;
|
|
369
|
+
/** True if a valid Portal session is open on L1 (read-only; no tx). */
|
|
370
|
+
isSessionOpen(): Promise<boolean>;
|
|
322
371
|
/** Sign `tx` with a mixed set of signers: raw Keypairs are partial-signed, wallet adapters sign via signTransaction. */
|
|
323
372
|
private signTx;
|
|
324
373
|
private sendOnL1;
|
|
@@ -333,12 +382,19 @@ export declare class NorthstarEva {
|
|
|
333
382
|
buildOpenSessionIx(): TransactionInstruction;
|
|
334
383
|
/** Portal CloseSession instruction (L1). */
|
|
335
384
|
buildCloseSessionIx(): TransactionInstruction;
|
|
385
|
+
/**
|
|
386
|
+
* Build version of {@link ensureSession}: returns the OpenSession instruction **only if the
|
|
387
|
+
* session isn't created yet**, or **`null`** if it already exists. Async (it reads the session
|
|
388
|
+
* on L1). The caller composes the returned ix into a tx, signs, and submits — e.g.:
|
|
389
|
+
* `const ix = await sdk.buildEnsureSessionIx(); if (ix) tx.add(ix);`
|
|
390
|
+
*/
|
|
391
|
+
buildEnsureSessionIx(): Promise<TransactionInstruction | null>;
|
|
336
392
|
/** Portal DepositFee instruction (L1) — "fund fee to ER payer". (Caller must ensure a session exists.) */
|
|
337
393
|
buildDepositFeeIx(lamports: bigint, recipient?: PublicKey): TransactionInstruction;
|
|
338
|
-
/** "Withdraw fee from ER" instruction (ER) — system transfer from `
|
|
394
|
+
/** "Withdraw fee from ER" instruction (ER) — system transfer from `sender` (default: the signer) → its WithdrawalSink. (Portal SOL bridge; distinct from the eva `withdraw` instruction.) */
|
|
339
395
|
buildWithdrawFeeFromErIx(p: {
|
|
340
396
|
lamports: bigint;
|
|
341
|
-
|
|
397
|
+
sender?: PublicKey;
|
|
342
398
|
toL1?: PublicKey;
|
|
343
399
|
}): TransactionInstruction;
|
|
344
400
|
/** Next sequential trench id (= total_trenches + 1) read from the ER global state. */
|
|
@@ -491,23 +547,23 @@ export declare class NorthstarEva {
|
|
|
491
547
|
* WithdrawalSink, returning the fee to whoever sent it. The validator pays the L1 SOL
|
|
492
548
|
* ASYNCHRONOUSLY at the next settlement (use {@link waitForL1Settlement} to await it).
|
|
493
549
|
*
|
|
494
|
-
*
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
550
|
+
* `sender` is a **PublicKey** (default: the SDK signer's pubkey). This send-method signs with
|
|
551
|
+
* the SDK's wallet adapter — so `sender` must be the SDK's own account (the user's wallet, which
|
|
552
|
+
* signs remotely; no secret key needed). To withdraw for a different account whose signature you
|
|
553
|
+
* collect elsewhere, use {@link buildWithdrawFeeFromErIx} and have that account sign the tx.
|
|
498
554
|
*
|
|
499
555
|
* NOTE: the L1 payout goes to the SAME account that deposited (the sender) — an arbitrary
|
|
500
556
|
* `toL1` destination is not supported by the Portal program yet (pending protocol change).
|
|
501
557
|
*/
|
|
502
558
|
requestWithdraw(p: {
|
|
503
559
|
lamports: bigint;
|
|
504
|
-
|
|
560
|
+
sender?: PublicKey;
|
|
505
561
|
toL1?: PublicKey;
|
|
506
562
|
}): Promise<ErTxResult>;
|
|
507
563
|
/** "Withdraw fee from ER" (the green-box withdraw API) — alias of {@link requestWithdraw}. */
|
|
508
564
|
withdrawFeeFromEr(p: {
|
|
509
565
|
lamports: bigint;
|
|
510
|
-
|
|
566
|
+
sender?: PublicKey;
|
|
511
567
|
toL1?: PublicKey;
|
|
512
568
|
}): Promise<ErTxResult>;
|
|
513
569
|
/** Poll an account's L1 balance until it rises by >= `expectedDelta` (settlement is async). Returns the final L1 balance. */
|
|
@@ -1938,6 +1938,41 @@ export function decodeGlobalState(data) {
|
|
|
1938
1938
|
tradingDuration: data.length >= 96 ? uLE(data, 88, 8) : 0n,
|
|
1939
1939
|
};
|
|
1940
1940
|
}
|
|
1941
|
+
// ---- Portal Session (portal/src/state.rs struct Session; Borsh, LEN 219, discriminator 1) ----
|
|
1942
|
+
export const SESSION_LEN = 219;
|
|
1943
|
+
export const SESSION_DISCRIMINATOR = 1;
|
|
1944
|
+
export const SESSION_OFFSETS = {
|
|
1945
|
+
discriminator: 0, gridId: 1, ttlSlots: 9, feeCap: 17, createdAt: 25, nonce: 33,
|
|
1946
|
+
authority: 49, validator: 81, settlementIntervalSlots: 113, lastSettledL1Slot: 121,
|
|
1947
|
+
lastSettledErSlot: 129, settlementStatus: 137, settlementErSlot: 138,
|
|
1948
|
+
settlementStartedL1Slot: 210, bump: 218,
|
|
1949
|
+
};
|
|
1950
|
+
const SETTLEMENT_STATUS = ["Idle", "InProgress"];
|
|
1951
|
+
export function decodeSession(data) {
|
|
1952
|
+
if (data.length < SESSION_LEN)
|
|
1953
|
+
throw new Error(`Session data too short: ${data.length} < ${SESSION_LEN}`);
|
|
1954
|
+
const o = SESSION_OFFSETS;
|
|
1955
|
+
const createdAt = uLE(data, o.createdAt, 8);
|
|
1956
|
+
const ttlSlots = uLE(data, o.ttlSlots, 8);
|
|
1957
|
+
return {
|
|
1958
|
+
discriminator: data[o.discriminator],
|
|
1959
|
+
gridId: uLE(data, o.gridId, 8),
|
|
1960
|
+
ttlSlots,
|
|
1961
|
+
feeCap: uLE(data, o.feeCap, 8),
|
|
1962
|
+
createdAt,
|
|
1963
|
+
expiresAtSlot: createdAt + ttlSlots,
|
|
1964
|
+
nonce: uLE(data, o.nonce, 16),
|
|
1965
|
+
authority: data.slice(o.authority, o.authority + 32),
|
|
1966
|
+
validator: data.slice(o.validator, o.validator + 32),
|
|
1967
|
+
settlementIntervalSlots: uLE(data, o.settlementIntervalSlots, 8),
|
|
1968
|
+
lastSettledL1Slot: uLE(data, o.lastSettledL1Slot, 8),
|
|
1969
|
+
lastSettledErSlot: uLE(data, o.lastSettledErSlot, 8),
|
|
1970
|
+
settlementStatus: SETTLEMENT_STATUS[data[o.settlementStatus]] ?? "Unknown",
|
|
1971
|
+
settlementErSlot: uLE(data, o.settlementErSlot, 8),
|
|
1972
|
+
settlementStartedL1Slot: uLE(data, o.settlementStartedL1Slot, 8),
|
|
1973
|
+
bump: data[o.bump],
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1941
1976
|
// ---- SPL token account amount (u64 @ 64) ----
|
|
1942
1977
|
export function decodeTokenAmount(data) {
|
|
1943
1978
|
if (data.length < 72)
|
|
@@ -2321,6 +2356,22 @@ export class NorthstarEva {
|
|
|
2321
2356
|
getSessionPdaFromEr() { return this.erRpc("getSessionPda"); }
|
|
2322
2357
|
getDelegatedAccounts() { return this.erRpc("getDelegatedAccounts"); }
|
|
2323
2358
|
sessionPda() { return portal.sessionPda(this.cfg.portalProgramId); }
|
|
2359
|
+
/**
|
|
2360
|
+
* Read + decode the Portal session account from L1 (the single global session for this Portal).
|
|
2361
|
+
* Returns `null` if no valid session exists. Read-only — no transaction, no fee. Use the returned
|
|
2362
|
+
* `ttlSlots`/`createdAt`/`expiresAtSlot`/`validator` for details (compare `expiresAtSlot` to the
|
|
2363
|
+
* current L1 slot — `await this.l1.getSlot()` — to tell if it's expired).
|
|
2364
|
+
*/
|
|
2365
|
+
async getSession() {
|
|
2366
|
+
const info = await this.l1GetAccount(this.sessionPda());
|
|
2367
|
+
if (!info || info.data.length < SESSION_LEN || info.data[0] !== SESSION_DISCRIMINATOR)
|
|
2368
|
+
return null;
|
|
2369
|
+
return decodeSession(info.data);
|
|
2370
|
+
}
|
|
2371
|
+
/** True if a valid Portal session is open on L1 (read-only; no tx). */
|
|
2372
|
+
async isSessionOpen() {
|
|
2373
|
+
return (await this.getSession()) !== null;
|
|
2374
|
+
}
|
|
2324
2375
|
// ───────────────────────── signing (adapter-based, no Keypair required) ─────────────────────────
|
|
2325
2376
|
/** Sign `tx` with a mixed set of signers: raw Keypairs are partial-signed, wallet adapters sign via signTransaction. */
|
|
2326
2377
|
async signTx(tx, signers) {
|
|
@@ -2415,16 +2466,28 @@ export class NorthstarEva {
|
|
|
2415
2466
|
buildCloseSessionIx() {
|
|
2416
2467
|
return portal.closeSessionIx({ programId: this.cfg.portalProgramId, closer: this.payer });
|
|
2417
2468
|
}
|
|
2469
|
+
/**
|
|
2470
|
+
* Build version of {@link ensureSession}: returns the OpenSession instruction **only if the
|
|
2471
|
+
* session isn't created yet**, or **`null`** if it already exists. Async (it reads the session
|
|
2472
|
+
* on L1). The caller composes the returned ix into a tx, signs, and submits — e.g.:
|
|
2473
|
+
* `const ix = await sdk.buildEnsureSessionIx(); if (ix) tx.add(ix);`
|
|
2474
|
+
*/
|
|
2475
|
+
async buildEnsureSessionIx() {
|
|
2476
|
+
this.requireNorthStar("buildEnsureSessionIx");
|
|
2477
|
+
if (await this.l1GetAccount(this.sessionPda()))
|
|
2478
|
+
return null; // already created
|
|
2479
|
+
return this.buildOpenSessionIx();
|
|
2480
|
+
}
|
|
2418
2481
|
/** Portal DepositFee instruction (L1) — "fund fee to ER payer". (Caller must ensure a session exists.) */
|
|
2419
2482
|
buildDepositFeeIx(lamports, recipient = this.payer) {
|
|
2420
2483
|
return portal.depositFeeIx({ programId: this.cfg.portalProgramId, depositor: this.payer, recipient, lamports });
|
|
2421
2484
|
}
|
|
2422
|
-
/** "Withdraw fee from ER" instruction (ER) — system transfer from `
|
|
2485
|
+
/** "Withdraw fee from ER" instruction (ER) — system transfer from `sender` (default: the signer) → its WithdrawalSink. (Portal SOL bridge; distinct from the eva `withdraw` instruction.) */
|
|
2423
2486
|
buildWithdrawFeeFromErIx(p) {
|
|
2424
|
-
const
|
|
2425
|
-
if (p.toL1 && !p.toL1.equals(
|
|
2426
|
-
throw new Error("Arbitrary L1 withdrawal destination is not supported yet (Portal pays the
|
|
2427
|
-
return SystemProgram.transfer({ fromPubkey:
|
|
2487
|
+
const sender = p.sender ?? this.payer;
|
|
2488
|
+
if (p.toL1 && !p.toL1.equals(sender))
|
|
2489
|
+
throw new Error("Arbitrary L1 withdrawal destination is not supported yet (Portal pays the sender). Omit `toL1` or set it equal to sender.");
|
|
2490
|
+
return SystemProgram.transfer({ fromPubkey: sender, toPubkey: this.getWithdrawalSink(sender), lamports: Number(p.lamports) });
|
|
2428
2491
|
}
|
|
2429
2492
|
/** Next sequential trench id (= total_trenches + 1) read from the ER global state. */
|
|
2430
2493
|
async nextTrenchId() { return (await this.requireGlobal()).totalTrenches + 1n; }
|
|
@@ -2630,19 +2693,18 @@ export class NorthstarEva {
|
|
|
2630
2693
|
* WithdrawalSink, returning the fee to whoever sent it. The validator pays the L1 SOL
|
|
2631
2694
|
* ASYNCHRONOUSLY at the next settlement (use {@link waitForL1Settlement} to await it).
|
|
2632
2695
|
*
|
|
2633
|
-
*
|
|
2634
|
-
*
|
|
2635
|
-
*
|
|
2636
|
-
*
|
|
2696
|
+
* `sender` is a **PublicKey** (default: the SDK signer's pubkey). This send-method signs with
|
|
2697
|
+
* the SDK's wallet adapter — so `sender` must be the SDK's own account (the user's wallet, which
|
|
2698
|
+
* signs remotely; no secret key needed). To withdraw for a different account whose signature you
|
|
2699
|
+
* collect elsewhere, use {@link buildWithdrawFeeFromErIx} and have that account sign the tx.
|
|
2637
2700
|
*
|
|
2638
2701
|
* NOTE: the L1 payout goes to the SAME account that deposited (the sender) — an arbitrary
|
|
2639
2702
|
* `toL1` destination is not supported by the Portal program yet (pending protocol change).
|
|
2640
2703
|
*/
|
|
2641
2704
|
async requestWithdraw(p) {
|
|
2642
2705
|
this.requireNorthStar("withdrawFeeFromEr (withdraw ER→L1)");
|
|
2643
|
-
const
|
|
2644
|
-
|
|
2645
|
-
return this.sendOnEr([ix], [sender]);
|
|
2706
|
+
const ix = this.buildWithdrawFeeFromErIx({ lamports: p.lamports, sender: p.sender ?? this.payer, toL1: p.toL1 });
|
|
2707
|
+
return this.sendOnEr([ix], [this.wallet]); // signed by the SDK's wallet adapter (the user)
|
|
2646
2708
|
}
|
|
2647
2709
|
/** "Withdraw fee from ER" (the green-box withdraw API) — alias of {@link requestWithdraw}. */
|
|
2648
2710
|
async withdrawFeeFromEr(p) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "northstar-eva-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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",
|