northstar-eva-sdk 0.1.1 → 0.3.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 +47 -0
- package/dist/northstar-eva-sdk.d.ts +65 -10
- package/dist/northstar-eva-sdk.js +96 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,10 +9,57 @@ npm install northstar-eva-sdk @solana/web3.js @coral-xyz/anchor @solana/spl-toke
|
|
|
9
9
|
```
|
|
10
10
|
```ts
|
|
11
11
|
import { NorthstarEva } from "northstar-eva-sdk";
|
|
12
|
+
// localnet with a Keypair:
|
|
12
13
|
const sdk = NorthstarEva.create({ network: "localnet", wallet });
|
|
14
|
+
// OR keep your existing AnchorProvider + read-only wallet + custom RPC (see 0.2.0):
|
|
15
|
+
const sdk = NorthstarEva.create({ provider, erRpc: ER_RPC_URL, evaProgramId });
|
|
13
16
|
```
|
|
14
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).
|
|
15
18
|
|
|
19
|
+
## What's new in 0.3.0
|
|
20
|
+
|
|
21
|
+
**Construct from your existing `program` + `provider`, and an exported `EVA_IDL` — minimal code change.**
|
|
22
|
+
|
|
23
|
+
- **Pass a pre-built Anchor `program`.** `NorthstarEva.create({ program, provider, erRpc })` mirrors the old `new EvaArenaSDK(program, provider)` shape. The SDK **adopts `program.programId` as the single source of truth** (all PDA derivations follow it) and reuses the program's `provider.wallet` + `provider.connection` — so the program id can never disagree between L1 and the ER, or across environments. (If you *also* pass a conflicting `evaProgramId`, the SDK throws instead of silently mismatching.)
|
|
24
|
+
- **`EVA_IDL` is now exported** — build your program exactly like before:
|
|
25
|
+
```ts
|
|
26
|
+
import { NorthstarEva, EVA_IDL } from "northstar-eva-sdk";
|
|
27
|
+
|
|
28
|
+
const wallet = new ReadOnlyWallet(userPubkey); // no Keypair (Turnkey)
|
|
29
|
+
const provider = new anchor.AnchorProvider(connection, wallet, { commitment: "confirmed" });
|
|
30
|
+
const programId = getProgramId(); // per-environment id
|
|
31
|
+
const program = new anchor.Program({ ...EVA_IDL, address: programId.toBase58() }, provider);
|
|
32
|
+
|
|
33
|
+
const sdk = NorthstarEva.create({ program, provider, erRpc: ER_RPC_URL });
|
|
34
|
+
// sdk.evaProgramId === programId (adopted from your program)
|
|
35
|
+
```
|
|
36
|
+
- The eva program is **one address, identical on L1 and ER** (the ER reanchors and executes the L1 program — there is no separate ER deployment), and both `evaProgramId`/`portalProgramId` remain configurable + readable via `sdk.evaProgramId` / `sdk.portalProgramId`.
|
|
37
|
+
|
|
38
|
+
> Backwards compatible: `{ network, wallet: keypair }` and `{ provider, erRpc, evaProgramId }` both still work.
|
|
39
|
+
|
|
40
|
+
## What's new in 0.2.0
|
|
41
|
+
|
|
42
|
+
Integration-friendly construction — **no Keypair required, keep your provider, any RPC URL.**
|
|
43
|
+
|
|
44
|
+
- **Read-only / remote wallets are supported.** `wallet` now accepts any **wallet adapter** (`publicKey` + `signTransaction`) — e.g. a Turnkey `ReadOnlyWallet` — not just a `Keypair`. The SDK signs through `signTransaction` and never needs the secret key. (Fixes the `Type 'Wallet' is missing … 'secretKey'` error.)
|
|
45
|
+
- **Pass your `AnchorProvider` directly.** `NorthstarEva.create({ provider })` reuses the provider's `.wallet` (signer) and `.connection` (the **L1** endpoint) — keep the exact provider/program setup you already have.
|
|
46
|
+
- **Any RPC URL (not just localhost).** Pass `l1Rpc` + `erRpc` (or `provider`/`connection` + `erRpc`). `network` is now just an optional label (any string) and no longer rejects a URL. **The ER is a separate endpoint — always pass `erRpc`** (the SDK throws a clear error if a custom L1 is given without it).
|
|
47
|
+
- **Program ids are configurable + exposed.** `evaProgramId` / `portalProgramId` are options *and* readable as `sdk.evaProgramId` / `sdk.portalProgramId`.
|
|
48
|
+
- **Dynamic recipients.** Deposit credits any `recipient` (a `PublicKey`); withdraw's `recipient` accepts a `Keypair` **or** a wallet adapter (and defaults to the SDK signer).
|
|
49
|
+
|
|
50
|
+
**Migration — your old `new EvaArenaSDK(program, provider)` shape, unchanged inputs:**
|
|
51
|
+
```ts
|
|
52
|
+
const userPubkey = new PublicKey(turnkeyAddress);
|
|
53
|
+
const wallet = new ReadOnlyWallet(userPubkey); // no Keypair
|
|
54
|
+
const provider = new anchor.AnchorProvider(connection, wallet, { commitment: "confirmed" });
|
|
55
|
+
const sdk = NorthstarEva.create({
|
|
56
|
+
provider, // keeps provider + read-only wallet
|
|
57
|
+
erRpc: ER_RPC_URL, // the NorthStar ER endpoint (any URL)
|
|
58
|
+
evaProgramId: getProgramId(),// per-environment program id
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
> Backwards compatible: `NorthstarEva.create({ network, wallet: keypair })` still works (the Keypair is wrapped automatically). No method signatures changed.
|
|
62
|
+
|
|
16
63
|
## What's new in 0.1.1
|
|
17
64
|
|
|
18
65
|
The deposit ("fund fee to ER payer") API now **contains the open-session step**, plus two helper additions:
|
|
@@ -136,7 +136,12 @@ export interface ResolvedConfig {
|
|
|
136
136
|
settlementIntervalSlots: bigint;
|
|
137
137
|
}
|
|
138
138
|
export interface NorthstarEvaInput {
|
|
139
|
-
|
|
139
|
+
/**
|
|
140
|
+
* A preset name (`"localnet"` | `"devnet"`) OR any custom label — presets only set
|
|
141
|
+
* default endpoints. To use ANY RPC (not localhost), pass `l1Rpc` + `erRpc` (or a
|
|
142
|
+
* `provider`/`connection`); `network` is then just a label and may be any string.
|
|
143
|
+
*/
|
|
144
|
+
network?: NetworkName | (string & {});
|
|
140
145
|
l1Rpc?: string;
|
|
141
146
|
erRpc?: string;
|
|
142
147
|
erWs?: string;
|
|
@@ -217,25 +222,72 @@ export interface ErTxResult {
|
|
|
217
222
|
err: unknown | null;
|
|
218
223
|
confirmed: boolean;
|
|
219
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* A wallet adapter the SDK can sign through — exactly what Anchor's `Wallet`,
|
|
227
|
+
* a wallet-adapter, or a Turnkey/remote `ReadOnlyWallet` already implement.
|
|
228
|
+
* The SDK never needs the secret key, so a `Keypair` is NOT required.
|
|
229
|
+
*/
|
|
230
|
+
export interface WalletLike {
|
|
231
|
+
publicKey: PublicKey;
|
|
232
|
+
signTransaction<T extends Transaction>(tx: T): Promise<T>;
|
|
233
|
+
signAllTransactions?<T extends Transaction>(txs: T[]): Promise<T[]>;
|
|
234
|
+
}
|
|
235
|
+
/** Anything the SDK can sign a tx with: a raw Keypair (it partial-signs) or a wallet adapter. */
|
|
236
|
+
export type TxSigner = Keypair | WalletLike;
|
|
220
237
|
export interface NorthstarEvaOptions extends NorthstarEvaInput {
|
|
221
|
-
/**
|
|
222
|
-
|
|
238
|
+
/**
|
|
239
|
+
* Signer / fee payer. A `Keypair` (wrapped automatically) **or** any wallet adapter
|
|
240
|
+
* (`publicKey` + `signTransaction`) — e.g. a Turnkey `ReadOnlyWallet`. Optional when
|
|
241
|
+
* `provider` is given (its `.wallet` is used).
|
|
242
|
+
*/
|
|
243
|
+
wallet?: TxSigner;
|
|
244
|
+
/**
|
|
245
|
+
* An Anchor `AnchorProvider` (as you already build for eva). Its `.wallet` becomes the
|
|
246
|
+
* signer and its `.connection` becomes the **L1** connection — so you keep your provider.
|
|
247
|
+
* NOTE: the ER is a SEPARATE endpoint, so still pass `erRpc`.
|
|
248
|
+
*/
|
|
249
|
+
provider?: anchor.AnchorProvider;
|
|
250
|
+
/** Explicit L1 connection (alternative to `provider`/`l1Rpc`). */
|
|
251
|
+
connection?: Connection;
|
|
252
|
+
/**
|
|
253
|
+
* A pre-built Anchor `Program` for eva (as you already build with your IDL + program id).
|
|
254
|
+
* If given, it becomes the SINGLE SOURCE OF TRUTH for the program id — the SDK adopts
|
|
255
|
+
* `program.programId` (so all PDA derivations follow it), and reuses the program's
|
|
256
|
+
* `provider.wallet`/`provider.connection` for the signer + L1 endpoint. Use the exported
|
|
257
|
+
* `EVA_IDL` to build it: `new anchor.Program({ ...EVA_IDL, address }, provider)`.
|
|
258
|
+
*/
|
|
259
|
+
program?: anchor.Program<Idl>;
|
|
260
|
+
/** Alias of `program`. */
|
|
261
|
+
evaProgram?: anchor.Program<Idl>;
|
|
223
262
|
/** eva IDL object. If omitted, loaded from `evaIdlPath` or the bundled IDL. */
|
|
224
263
|
evaIdl?: Idl;
|
|
225
264
|
evaIdlPath?: string;
|
|
226
|
-
/** Validator identity recorded in the session (settlement signer). Defaults to
|
|
265
|
+
/** Validator identity recorded in the session (settlement signer). Defaults to the signer. */
|
|
227
266
|
validatorIdentity?: string | PublicKey;
|
|
228
267
|
/** ms between status/read polls. */
|
|
229
268
|
pollIntervalMs?: number;
|
|
230
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* The eva Anchor IDL, exported as a constant so you can build your own `Program`:
|
|
272
|
+
* `new anchor.Program({ ...EVA_IDL, address: programId.toBase58() }, provider)` and pass it
|
|
273
|
+
* to `NorthstarEva.create({ program, provider, erRpc })`.
|
|
274
|
+
*/
|
|
275
|
+
export declare const EVA_IDL: Idl;
|
|
231
276
|
export declare class NorthstarEva {
|
|
232
277
|
readonly cfg: ResolvedConfig;
|
|
233
278
|
readonly l1: Connection;
|
|
234
279
|
readonly er: Connection;
|
|
235
|
-
|
|
280
|
+
/** The signer as a wallet adapter (a passed Keypair is wrapped). Use `.payer` for its pubkey. */
|
|
281
|
+
readonly wallet: WalletLike;
|
|
282
|
+
/** The fee-payer / signer public key. */
|
|
283
|
+
readonly payer: PublicKey;
|
|
236
284
|
readonly validatorIdentity: PublicKey;
|
|
237
285
|
readonly program: anchor.Program<Idl>;
|
|
238
286
|
private readonly pollMs;
|
|
287
|
+
/** The eva program id in use (configurable per environment). */
|
|
288
|
+
get evaProgramId(): PublicKey;
|
|
289
|
+
/** The Portal program id in use. */
|
|
290
|
+
get portalProgramId(): PublicKey;
|
|
239
291
|
constructor(opts: NorthstarEvaOptions);
|
|
240
292
|
static create(opts: NorthstarEvaOptions): NorthstarEva;
|
|
241
293
|
erRpc<T = any>(method: string, params?: unknown[]): Promise<T>;
|
|
@@ -255,9 +307,11 @@ export declare class NorthstarEva {
|
|
|
255
307
|
getSessionPdaFromEr(): Promise<any>;
|
|
256
308
|
getDelegatedAccounts(): Promise<string[]>;
|
|
257
309
|
sessionPda(): anchor.web3.PublicKey;
|
|
310
|
+
/** Sign `tx` with a mixed set of signers: raw Keypairs are partial-signed, wallet adapters sign via signTransaction. */
|
|
311
|
+
private signTx;
|
|
258
312
|
private sendOnL1;
|
|
259
313
|
/** Send a tx to the ER and confirm by polling (the ER returns Ok on acceptance). */
|
|
260
|
-
sendOnEr(instructions: Transaction["instructions"], signers:
|
|
314
|
+
sendOnEr(instructions: Transaction["instructions"], signers: TxSigner[]): Promise<ErTxResult>;
|
|
261
315
|
private sendEva;
|
|
262
316
|
/** Fetch eva GlobalState from the ER or throw a clear "not initialized" error. */
|
|
263
317
|
private requireGlobal;
|
|
@@ -331,21 +385,22 @@ export declare class NorthstarEva {
|
|
|
331
385
|
/**
|
|
332
386
|
* "Withdraw fee from ER" (path 2): an ER `system_transfer` from `recipient` → its
|
|
333
387
|
* WithdrawalSink. The validator pays the L1 SOL ASYNCHRONOUSLY at the next settlement
|
|
334
|
-
* (use {@link waitForL1Settlement} to await it). `recipient`
|
|
335
|
-
*
|
|
388
|
+
* (use {@link waitForL1Settlement} to await it). `recipient` signs the ER transfer and may
|
|
389
|
+
* be a **Keypair OR a wallet adapter** (e.g. a Turnkey `ReadOnlyWallet`); the L1 receiver never signs.
|
|
390
|
+
* Defaults to the SDK's own signer if omitted.
|
|
336
391
|
*
|
|
337
392
|
* NOTE: the L1 payout currently goes to the SAME account that deposited — an arbitrary
|
|
338
393
|
* `toL1` destination is not supported by the Portal program yet (pending protocol change).
|
|
339
394
|
*/
|
|
340
395
|
requestWithdraw(p: {
|
|
341
396
|
lamports: bigint;
|
|
342
|
-
recipient
|
|
397
|
+
recipient?: TxSigner;
|
|
343
398
|
toL1?: PublicKey;
|
|
344
399
|
}): Promise<ErTxResult>;
|
|
345
400
|
/** "Withdraw fee from ER" (the green-box withdraw API) — alias of {@link requestWithdraw}. */
|
|
346
401
|
withdrawFeeFromEr(p: {
|
|
347
402
|
lamports: bigint;
|
|
348
|
-
recipient
|
|
403
|
+
recipient?: TxSigner;
|
|
349
404
|
toL1?: PublicKey;
|
|
350
405
|
}): Promise<ErTxResult>;
|
|
351
406
|
/** Poll an account's L1 balance until it rises by >= `expectedDelta` (settlement is async). Returns the final L1 balance. */
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// ============================================================================
|
|
8
8
|
import * as anchor from "@coral-xyz/anchor";
|
|
9
9
|
import { readFileSync } from "node:fs";
|
|
10
|
-
import { Connection, Keypair, PublicKey, SystemProgram, Transaction, TransactionInstruction
|
|
10
|
+
import { Connection, Keypair, PublicKey, SystemProgram, Transaction, TransactionInstruction } from "@solana/web3.js";
|
|
11
11
|
import { TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, createAssociatedTokenAccountIdempotentInstruction } from "@solana/spl-token";
|
|
12
12
|
// --- embedded eva IDL (CQru6…) ---
|
|
13
13
|
const __EVA_IDL__ = {
|
|
@@ -1978,12 +1978,15 @@ export const NETWORKS = {
|
|
|
1978
1978
|
const pk = (v) => (typeof v === "string" ? new PublicKey(v) : v);
|
|
1979
1979
|
const bn = (v, d) => (v === undefined ? d : BigInt(v));
|
|
1980
1980
|
export function resolveConfig(input) {
|
|
1981
|
-
|
|
1981
|
+
// Only "localnet"/"devnet" are presets; any other `network` value is just a label and
|
|
1982
|
+
// falls back to the localnet preset for defaults — pass l1Rpc/erRpc to override them.
|
|
1983
|
+
const presetKey = input.network === "devnet" ? "devnet" : "localnet";
|
|
1984
|
+
const preset = NETWORKS[presetKey];
|
|
1982
1985
|
const l1Rpc = input.l1Rpc ?? preset.l1Rpc;
|
|
1983
1986
|
const erRpc = input.erRpc ?? preset.erRpc;
|
|
1984
1987
|
if (!l1Rpc || !erRpc) {
|
|
1985
|
-
throw new Error(`Missing RPC endpoints for network "${input.network}".
|
|
1986
|
-
`pointing at your NorthStar node
|
|
1988
|
+
throw new Error(`Missing RPC endpoints for network "${input.network}". Pass l1Rpc + erRpc (or a provider + erRpc) ` +
|
|
1989
|
+
`pointing at your NorthStar node — public Solana devnet has no ER/Portal, and the ER is a SEPARATE URL from L1.`);
|
|
1987
1990
|
}
|
|
1988
1991
|
return {
|
|
1989
1992
|
l1Rpc,
|
|
@@ -2136,32 +2139,84 @@ const evaPdas = { globalStatePda, trenchPda, agentPda, tokenMintPda, trenchToken
|
|
|
2136
2139
|
*/
|
|
2137
2140
|
// @coral-xyz/anchor is CommonJS — runtime values live on the default export.
|
|
2138
2141
|
const A = anchor.default ?? anchor;
|
|
2142
|
+
const isWalletLike = (s) => !!s && typeof s.signTransaction === "function";
|
|
2139
2143
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
2140
2144
|
function loadBundledIdl() {
|
|
2141
2145
|
return __EVA_IDL__;
|
|
2142
2146
|
}
|
|
2147
|
+
/**
|
|
2148
|
+
* The eva Anchor IDL, exported as a constant so you can build your own `Program`:
|
|
2149
|
+
* `new anchor.Program({ ...EVA_IDL, address: programId.toBase58() }, provider)` and pass it
|
|
2150
|
+
* to `NorthstarEva.create({ program, provider, erRpc })`.
|
|
2151
|
+
*/
|
|
2152
|
+
export const EVA_IDL = loadBundledIdl();
|
|
2143
2153
|
export class NorthstarEva {
|
|
2144
2154
|
cfg;
|
|
2145
2155
|
l1;
|
|
2146
2156
|
er;
|
|
2157
|
+
/** The signer as a wallet adapter (a passed Keypair is wrapped). Use `.payer` for its pubkey. */
|
|
2147
2158
|
wallet;
|
|
2159
|
+
/** The fee-payer / signer public key. */
|
|
2160
|
+
payer;
|
|
2148
2161
|
validatorIdentity;
|
|
2149
2162
|
program; // eva, bound to the ER connection
|
|
2150
2163
|
pollMs;
|
|
2164
|
+
/** The eva program id in use (configurable per environment). */
|
|
2165
|
+
get evaProgramId() { return this.cfg.evaProgramId; }
|
|
2166
|
+
/** The Portal program id in use. */
|
|
2167
|
+
get portalProgramId() { return this.cfg.portalProgramId; }
|
|
2151
2168
|
constructor(opts) {
|
|
2152
|
-
|
|
2153
|
-
|
|
2169
|
+
// A pre-built Anchor program (Bai's `(program, provider)` shape) is the source of truth
|
|
2170
|
+
// for the program id, and can also supply the provider's wallet + L1 connection.
|
|
2171
|
+
const passedProgram = opts.program ?? opts.evaProgram;
|
|
2172
|
+
const programProvider = passedProgram?.provider;
|
|
2173
|
+
const programId = passedProgram?.programId?.toBase58();
|
|
2174
|
+
if (passedProgram && opts.evaProgramId && new PublicKey(opts.evaProgramId).toBase58() !== programId) {
|
|
2175
|
+
throw new Error(`evaProgramId (${new PublicKey(opts.evaProgramId).toBase58()}) does not match the passed program.programId (${programId}). Pass one source of truth.`);
|
|
2176
|
+
}
|
|
2177
|
+
// ── endpoints: a provider/connection/program can supply the L1 endpoint; ER is always separate ──
|
|
2178
|
+
const providerConn = opts.provider?.connection ?? opts.connection ?? programProvider?.connection;
|
|
2179
|
+
this.cfg = resolveConfig({ ...opts, l1Rpc: opts.l1Rpc ?? providerConn?.rpcEndpoint, evaProgramId: opts.evaProgramId ?? programId });
|
|
2180
|
+
// ── signer: accept a Keypair (wrap it), a wallet adapter, provider.wallet, or program.provider.wallet ──
|
|
2181
|
+
const w = opts.wallet;
|
|
2182
|
+
let signer;
|
|
2183
|
+
if (isWalletLike(w))
|
|
2184
|
+
signer = w; // already an adapter / ReadOnlyWallet
|
|
2185
|
+
else if (w && w.secretKey)
|
|
2186
|
+
signer = new A.Wallet(w); // a Keypair → wrap (NodeWallet)
|
|
2187
|
+
else if (opts.provider?.wallet)
|
|
2188
|
+
signer = opts.provider.wallet;
|
|
2189
|
+
else if (programProvider?.wallet)
|
|
2190
|
+
signer = programProvider.wallet;
|
|
2191
|
+
if (!signer)
|
|
2192
|
+
throw new Error("Pass `wallet` (a Keypair or a wallet adapter with signTransaction), a `provider` (AnchorProvider), or a `program` whose provider has a wallet.");
|
|
2193
|
+
this.wallet = signer;
|
|
2194
|
+
this.payer = signer.publicKey;
|
|
2154
2195
|
this.validatorIdentity = opts.validatorIdentity
|
|
2155
2196
|
? (typeof opts.validatorIdentity === "string" ? new PublicKey(opts.validatorIdentity) : opts.validatorIdentity)
|
|
2156
|
-
:
|
|
2197
|
+
: this.payer;
|
|
2157
2198
|
this.pollMs = opts.pollIntervalMs ?? 500;
|
|
2158
|
-
this.l1 = new Connection(this.cfg.l1Rpc, "confirmed");
|
|
2199
|
+
this.l1 = providerConn ?? new Connection(this.cfg.l1Rpc, "confirmed");
|
|
2159
2200
|
// ER provider reads at "processed" so account resolution / reads are fresh.
|
|
2160
2201
|
this.er = new Connection(this.cfg.erRpc, "processed");
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
const
|
|
2164
|
-
|
|
2202
|
+
// Guard: a custom (non-localhost) L1 with the default localhost ER is almost certainly a
|
|
2203
|
+
// forgotten erRpc — NorthStar's ER is a different URL than L1, fail loudly instead of hitting localhost.
|
|
2204
|
+
const customL1 = !!(opts.l1Rpc || providerConn);
|
|
2205
|
+
const erDefaulted = !opts.erRpc && opts.network !== "devnet";
|
|
2206
|
+
if (customL1 && erDefaulted && !/127\.0\.0\.1|localhost/.test(this.cfg.l1Rpc)) {
|
|
2207
|
+
throw new Error(`Custom L1 endpoint (${this.cfg.l1Rpc}) but no ER endpoint — NorthStar's ER is a SEPARATE URL. Pass erRpc (e.g. the :8910 endpoint).`);
|
|
2208
|
+
}
|
|
2209
|
+
if (passedProgram) {
|
|
2210
|
+
// Use the caller's program as-is (only used to BUILD instructions; the SDK sends manually).
|
|
2211
|
+
this.program = passedProgram;
|
|
2212
|
+
}
|
|
2213
|
+
else {
|
|
2214
|
+
const idl = opts.evaIdl ?? (opts.evaIdlPath ? JSON.parse(readFileSync(opts.evaIdlPath, "utf8")) : loadBundledIdl());
|
|
2215
|
+
idl.address = this.cfg.evaProgramId.toBase58(); // honor config / devnet override
|
|
2216
|
+
// eva program is bound to the ER connection (it executes there) + signs through the same wallet.
|
|
2217
|
+
const provider = new A.AnchorProvider(this.er, this.wallet, { commitment: "processed" });
|
|
2218
|
+
this.program = new A.Program(idl, provider);
|
|
2219
|
+
}
|
|
2165
2220
|
}
|
|
2166
2221
|
static create(opts) { return new NorthstarEva(opts); }
|
|
2167
2222
|
// ───────────────────────── ER transport (raw RPC, retries, processed) ─────────────────────────
|
|
@@ -2220,10 +2275,27 @@ export class NorthstarEva {
|
|
|
2220
2275
|
getSessionPdaFromEr() { return this.erRpc("getSessionPda"); }
|
|
2221
2276
|
getDelegatedAccounts() { return this.erRpc("getDelegatedAccounts"); }
|
|
2222
2277
|
sessionPda() { return portal.sessionPda(this.cfg.portalProgramId); }
|
|
2278
|
+
// ───────────────────────── signing (adapter-based, no Keypair required) ─────────────────────────
|
|
2279
|
+
/** Sign `tx` with a mixed set of signers: raw Keypairs are partial-signed, wallet adapters sign via signTransaction. */
|
|
2280
|
+
async signTx(tx, signers) {
|
|
2281
|
+
const keypairs = signers.filter((s) => !isWalletLike(s));
|
|
2282
|
+
const wallets = signers.filter(isWalletLike);
|
|
2283
|
+
if (keypairs.length)
|
|
2284
|
+
tx.partialSign(...keypairs);
|
|
2285
|
+
let signed = tx;
|
|
2286
|
+
for (const wlt of wallets)
|
|
2287
|
+
signed = await wlt.signTransaction(signed); // adds payer sig, preserves partials
|
|
2288
|
+
return signed;
|
|
2289
|
+
}
|
|
2223
2290
|
// ───────────────────────── L1 send + ER send ─────────────────────────
|
|
2224
2291
|
async sendOnL1(instructions, signers) {
|
|
2225
|
-
const
|
|
2226
|
-
|
|
2292
|
+
const { blockhash, lastValidBlockHeight } = await this.l1.getLatestBlockhash("confirmed");
|
|
2293
|
+
const tx = new Transaction({ feePayer: signers[0].publicKey, blockhash, lastValidBlockHeight });
|
|
2294
|
+
tx.add(...instructions);
|
|
2295
|
+
const signed = await this.signTx(tx, signers);
|
|
2296
|
+
const sig = await this.l1.sendRawTransaction(signed.serialize(), { skipPreflight: false, preflightCommitment: "confirmed" });
|
|
2297
|
+
await this.l1.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, "confirmed");
|
|
2298
|
+
return sig;
|
|
2227
2299
|
}
|
|
2228
2300
|
/** Send a tx to the ER and confirm by polling (the ER returns Ok on acceptance). */
|
|
2229
2301
|
async sendOnEr(instructions, signers) {
|
|
@@ -2233,8 +2305,8 @@ export class NorthstarEva {
|
|
|
2233
2305
|
const { blockhash } = await this.er.getLatestBlockhash("processed");
|
|
2234
2306
|
const tx = new Transaction({ feePayer: signers[0].publicKey, blockhash, lastValidBlockHeight: 0 });
|
|
2235
2307
|
tx.add(...instructions);
|
|
2236
|
-
|
|
2237
|
-
signature = await this.er.sendRawTransaction(
|
|
2308
|
+
const signed = await this.signTx(tx, signers);
|
|
2309
|
+
signature = await this.er.sendRawTransaction(signed.serialize(), { skipPreflight: false, preflightCommitment: "processed" });
|
|
2238
2310
|
break;
|
|
2239
2311
|
}
|
|
2240
2312
|
catch (e) {
|
|
@@ -2410,19 +2482,21 @@ export class NorthstarEva {
|
|
|
2410
2482
|
/**
|
|
2411
2483
|
* "Withdraw fee from ER" (path 2): an ER `system_transfer` from `recipient` → its
|
|
2412
2484
|
* WithdrawalSink. The validator pays the L1 SOL ASYNCHRONOUSLY at the next settlement
|
|
2413
|
-
* (use {@link waitForL1Settlement} to await it). `recipient`
|
|
2414
|
-
*
|
|
2485
|
+
* (use {@link waitForL1Settlement} to await it). `recipient` signs the ER transfer and may
|
|
2486
|
+
* be a **Keypair OR a wallet adapter** (e.g. a Turnkey `ReadOnlyWallet`); the L1 receiver never signs.
|
|
2487
|
+
* Defaults to the SDK's own signer if omitted.
|
|
2415
2488
|
*
|
|
2416
2489
|
* NOTE: the L1 payout currently goes to the SAME account that deposited — an arbitrary
|
|
2417
2490
|
* `toL1` destination is not supported by the Portal program yet (pending protocol change).
|
|
2418
2491
|
*/
|
|
2419
2492
|
async requestWithdraw(p) {
|
|
2420
|
-
|
|
2493
|
+
const recipient = p.recipient ?? this.wallet;
|
|
2494
|
+
if (p.toL1 && !p.toL1.equals(recipient.publicKey)) {
|
|
2421
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.");
|
|
2422
2496
|
}
|
|
2423
|
-
const sink = this.getWithdrawalSink(
|
|
2424
|
-
const ix = SystemProgram.transfer({ fromPubkey:
|
|
2425
|
-
return this.sendOnEr([ix], [
|
|
2497
|
+
const sink = this.getWithdrawalSink(recipient.publicKey);
|
|
2498
|
+
const ix = SystemProgram.transfer({ fromPubkey: recipient.publicKey, toPubkey: sink, lamports: Number(p.lamports) });
|
|
2499
|
+
return this.sendOnEr([ix], [recipient]);
|
|
2426
2500
|
}
|
|
2427
2501
|
/** "Withdraw fee from ER" (the green-box withdraw API) — alias of {@link requestWithdraw}. */
|
|
2428
2502
|
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.3.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",
|