northstar-eva-sdk 0.1.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 +294 -0
- package/dist/northstar-eva-sdk.d.ts +361 -0
- package/dist/northstar-eva-sdk.js +2473 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# northstar-eva-sdk
|
|
2
|
+
|
|
3
|
+
A high-level TypeScript SDK to run the **eva** program on a **NorthStar Ephemeral Rollup (ER)** and cut fees. It speaks the NorthStar Portal/ER protocol directly with **corrected encoders** and is self-contained on the three standard Solana libraries — no `@sonicsvm/northstar-sdk` dependency.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install northstar-eva-sdk @solana/web3.js @coral-xyz/anchor @solana/spl-token
|
|
9
|
+
```
|
|
10
|
+
```ts
|
|
11
|
+
import { NorthstarEva } from "northstar-eva-sdk";
|
|
12
|
+
const sdk = NorthstarEva.create({ network: "localnet", wallet });
|
|
13
|
+
```
|
|
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
|
+
|
|
16
|
+
## What it is + the fee point
|
|
17
|
+
|
|
18
|
+
NorthStar is **one validator process with two lanes**:
|
|
19
|
+
|
|
20
|
+
- **L1 lane** (`:8899`) — the base Solana chain. Trust, custody, money, finality. Normal per-signature + priority fees.
|
|
21
|
+
- **ER lane** (`:8910` RPC / `:8911` WS / `:8912` TPU) — an Ephemeral Rollup that lives *inside the same process* and reanchors to the L1 bank every block. **Execution is zero-fee**, but state is in-memory and only a narrow slice ever settles back to L1.
|
|
22
|
+
|
|
23
|
+
The fee-reduction play: do the cheap, one-time **setup/control/custody** on L1 (open session, fund the ER fee payer, create the game), then run the **hot path — repeated `buy`/`sell` — on the ER for ~0 fee.** This SDK handles the **lane routing** for you (it is otherwise manual: NorthStar has no auto-router):
|
|
24
|
+
|
|
25
|
+
| Operation | Lane |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| `openSession`, `fundErFeePayer`/`ensureErFunds`, `delegateKeypairAccount`, `createPortalOwnedAccount`, `closeSession` | **L1** |
|
|
28
|
+
| eva `initialize` / `createTrench` / `deposit` / `finalize` / `ensureUserAta` / `buy` / `sell` | **ER (zero fee)** |
|
|
29
|
+
| Reads (`erGetAccount`, `getGlobal`, `getTrench`, …) | **Either** — ER reads pinned to commitment `processed` so read-after-write is fresh |
|
|
30
|
+
|
|
31
|
+
Verified live on a local validator: Portal `5TeWSsjg2gbxCyWVniXeCmwM7UtHTCK7svzJr5xYJzHf`, eva `CQru6EauVg2UwepFCvY5qWuqCPEWYC4ta43wBbkFYtmo`.
|
|
32
|
+
|
|
33
|
+
> **Why the corrected encoders?** The published `@sonicsvm/northstar-sdk` has stale `OpenSession` and `Delegate` encoders. The deployed runtime expects `OpenSession` to carry `validator` + `settlement_interval_slots` (65-byte payload) and `Delegate` to place the session account at **index 2** (the deployed IDL annotation wrongly lists it at index 6 — this SDK deliberately follows the *runtime*, not that stale IDL). Anyone regenerating a client from the Portal IDL would build a broken `Delegate`. This SDK gets it right.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
The SDK is self-contained on three standard Solana peer dependencies:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install @solana/web3.js@^1.95 @coral-xyz/anchor@^0.31 @solana/spl-token@^0.4
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
(The package's `peerDependencies` accept `@coral-xyz/anchor` `^0.31 || ^0.32`, `@solana/spl-token` `^0.4`, `@solana/web3.js` `^1.95`.)
|
|
44
|
+
|
|
45
|
+
**Running TypeScript:**
|
|
46
|
+
- **Node >= 22.6** to run `.ts` directly via `--experimental-strip-types` (this is what the test scripts use), **or**
|
|
47
|
+
- compile with `tsc` (`npm run typecheck` runs `tsc --noEmit`).
|
|
48
|
+
|
|
49
|
+
The package ships TypeScript sources — `package.json` `exports` maps `.` → `./src/index.ts`, and `files` includes `src`, `idl`, and `README.md`. The eva IDL (`idl/eva_arena.json`) is bundled and loaded automatically unless you override it.
|
|
50
|
+
|
|
51
|
+
## 60-second quickstart (localnet buy/sell)
|
|
52
|
+
|
|
53
|
+
Copy-paste runnable against a running NorthStar validator (L1 `:8899` + ER `:8910`). Save as `quickstart.ts` and run with `node --experimental-strip-types quickstart.ts`.
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { readFileSync } from "node:fs";
|
|
57
|
+
import { homedir } from "node:os";
|
|
58
|
+
import { resolve } from "node:path";
|
|
59
|
+
import { Keypair } from "@solana/web3.js";
|
|
60
|
+
import { NorthstarEva } from "northstar-eva-sdk"; // or: from "../src/index.ts"
|
|
61
|
+
|
|
62
|
+
// Load a funded local keypair (e.g. ~/.config/solana/id.json)
|
|
63
|
+
const path = resolve(homedir(), ".config/solana/id.json");
|
|
64
|
+
const wallet = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(readFileSync(path, "utf8"))));
|
|
65
|
+
|
|
66
|
+
const sdk = NorthstarEva.create({ network: "localnet", wallet });
|
|
67
|
+
|
|
68
|
+
// 1. Health + sync (both lanes should be up)
|
|
69
|
+
const h = await sdk.health();
|
|
70
|
+
if (!h.l1 || !h.er) throw new Error(`validator not healthy: ${JSON.stringify(h)}`);
|
|
71
|
+
|
|
72
|
+
// 2. Open the Portal session on L1 (idempotent — no-op if already open)
|
|
73
|
+
await sdk.openSession();
|
|
74
|
+
|
|
75
|
+
// 3. Fund the ER fee payer via Portal DepositFee on L1 (ER balance = these credits)
|
|
76
|
+
await sdk.ensureErFunds(250_000_000n); // tops up + waits for the credit to land
|
|
77
|
+
|
|
78
|
+
// 4. Initialize the eva global state on the ER (idempotent)
|
|
79
|
+
// NOTE: keep biddingDuration SMALL so finalize doesn't wait for many slots
|
|
80
|
+
await sdk.initialize({ biddingDuration: 120n, tradingDuration: 100_000n });
|
|
81
|
+
|
|
82
|
+
// 5. Create a trench (id auto-derived as totalTrenches + 1)
|
|
83
|
+
const { trenchId } = await sdk.createTrench();
|
|
84
|
+
|
|
85
|
+
// 6. Deposit into the bidding phase (ER)
|
|
86
|
+
await sdk.deposit({ trenchId, lamports: 50_000_000n }); // 0.05 SOL
|
|
87
|
+
|
|
88
|
+
// 7. Finalize — waits for ER slot >= biddingEndBlock, then seeds the AMM -> Trading
|
|
89
|
+
await sdk.finalize({ trenchId });
|
|
90
|
+
|
|
91
|
+
// 8. Pre-create the user's token ATA on the ER (buy/sell require it to exist)
|
|
92
|
+
await sdk.ensureUserAta({ trenchId });
|
|
93
|
+
|
|
94
|
+
// 9. BUY 0.01 SOL on the ER (ZERO fee). Offline quote matches on-chain mint exactly.
|
|
95
|
+
const trench = await sdk.getTrench(trenchId, "er", 5);
|
|
96
|
+
const quote = sdk.previewBuy(trench!.reserves, 10_000_000n);
|
|
97
|
+
const buy = await sdk.buy({ trenchId, solPayWithFee: 10_000_000n });
|
|
98
|
+
console.log("bought tokens:", buy.userTokens, "(offline quote:", quote?.tokensOut, ")");
|
|
99
|
+
|
|
100
|
+
// 10. SELL half of them back (ER, zero fee)
|
|
101
|
+
const sell = await sdk.sell({ trenchId, tokenAmount: buy.userTokens / 2n });
|
|
102
|
+
console.log("remaining tokens:", sell.userTokens);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
All lamport/token amounts are `bigint`. The wallet must be a funded L1 keypair (it pays L1 setup fees and is recorded as the validator identity in the session unless you pass `validatorIdentity`).
|
|
106
|
+
|
|
107
|
+
## Two usage modes
|
|
108
|
+
|
|
109
|
+
**(a) Package import (recommended for a project):**
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { NorthstarEva } from "northstar-eva-sdk";
|
|
113
|
+
// also available: NETWORKS, resolveConfig, previewBuy/previewSell,
|
|
114
|
+
// decodeTrench/decodeGlobalState, portal.*, evaPdas.*, and the AMM helpers.
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**(b) Single-file drop-in:** `dist-single/northstar-eva-sdk.single.ts` is a self-contained file whose only dependencies are the three standard Solana libs. Copy it into any project and import from it directly — no package install of the SDK needed. Regenerate it with `npm run bundle`.
|
|
118
|
+
|
|
119
|
+
> The committed `dist-single/northstar-eva-sdk.single.ts` is a build artifact and may be out of date — **re-run `npm run bundle` before shipping the drop-in file.**
|
|
120
|
+
|
|
121
|
+
## Configuration
|
|
122
|
+
|
|
123
|
+
`NorthstarEva.create(opts)` takes:
|
|
124
|
+
|
|
125
|
+
| Option | Type | Default | Notes |
|
|
126
|
+
| --- | --- | --- | --- |
|
|
127
|
+
| `wallet` | `Keypair` | **required** | Signer / fee payer (L1). |
|
|
128
|
+
| `network` | `"localnet" \| "devnet"` | `"localnet"` | Selects the preset endpoints/program-ids. |
|
|
129
|
+
| `l1Rpc` / `erRpc` / `erWs` | `string` | from preset | Override individual endpoints. |
|
|
130
|
+
| `portalProgramId` / `evaProgramId` | `string \| PublicKey` | Portal `5TeW…`, eva `CQru…` | Override for a different deployment. |
|
|
131
|
+
| `evaIdl` / `evaIdlPath` | `Idl` / path | bundled `idl/eva_arena.json` | Supply a custom IDL or path. |
|
|
132
|
+
| `validatorIdentity` | `string \| PublicKey` | `wallet.publicKey` | Settlement signer recorded in the session. |
|
|
133
|
+
| `gridId` | `number \| bigint` | `1n` | OpenSession param. |
|
|
134
|
+
| `ttlSlots` | `number \| bigint` | `1_000_000n` | Session TTL (slots). |
|
|
135
|
+
| `feeCap` | `number \| bigint` | `1_000_000_000n` | Session fee cap. |
|
|
136
|
+
| `settlementIntervalSlots` | `number \| bigint` | `50n` | Settlement cadence (slots). |
|
|
137
|
+
| `pollIntervalMs` | `number` | `500` | ms between status/read polls. |
|
|
138
|
+
|
|
139
|
+
### Localnet
|
|
140
|
+
|
|
141
|
+
Zero config — the `localnet` preset points at the in-process validator:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
const sdk = NorthstarEva.create({ network: "localnet", wallet });
|
|
145
|
+
// l1Rpc http://127.0.0.1:8899 · erRpc http://127.0.0.1:8910 · erWs ws://127.0.0.1:8911
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Devnet
|
|
149
|
+
|
|
150
|
+
**Public Solana devnet has NO NorthStar ER/Portal.** "devnet" here means a NorthStar validator running in a devnet-style deployment. The `devnet` preset has **empty `l1Rpc`/`erRpc` sentinels** — `resolveConfig` throws fast unless you pass your node's endpoints:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const sdk = NorthstarEva.create({
|
|
154
|
+
network: "devnet",
|
|
155
|
+
l1Rpc: "https://your-northstar-node:8899", // REQUIRED — your NorthStar node
|
|
156
|
+
erRpc: "https://your-northstar-node:8910", // REQUIRED — its ER lane
|
|
157
|
+
erWs: "wss://your-northstar-node:8911", // optional
|
|
158
|
+
// portalProgramId / evaProgramId default to the same ids; override if redeployed
|
|
159
|
+
wallet,
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The "works devnet later" claim is true only against a **self-hosted NorthStar devnet deployment** — there is no hosted default.
|
|
164
|
+
|
|
165
|
+
## API reference
|
|
166
|
+
|
|
167
|
+
`NorthstarEva` — `src/client.ts`, re-exported from `src/index.ts`. Lamports/tokens are `bigint`.
|
|
168
|
+
|
|
169
|
+
### Construction
|
|
170
|
+
|
|
171
|
+
| Method | Lane | Description | Returns |
|
|
172
|
+
| --- | --- | --- | --- |
|
|
173
|
+
| `static create(opts: NorthstarEvaOptions)` | — | Build an instance (resolves config, binds an L1 connection at `confirmed` and an ER connection at `processed`, loads the eva IDL). | `NorthstarEva` |
|
|
174
|
+
|
|
175
|
+
### Health / session reads
|
|
176
|
+
|
|
177
|
+
| Method | Lane | Description | Returns |
|
|
178
|
+
| --- | --- | --- | --- |
|
|
179
|
+
| `health()` | L1 + ER | Pings both lanes (`getSlot` on L1, `getHealth` on ER). | `Promise<{ l1: boolean; er: boolean }>` |
|
|
180
|
+
| `syncStatus()` | ER | ER sync status (`northstarSysGetSyncStatus`). | `Promise<{ isSyncing; latestSyncedSlot; latestL1Slot }>` |
|
|
181
|
+
| `getSessionPdaFromEr()` | ER | Session PDA as reported by the ER (`getSessionPda`). | `Promise<any>` |
|
|
182
|
+
| `getDelegatedAccounts()` | ER | Accounts currently delegated in the session. | `Promise<string[]>` |
|
|
183
|
+
| `sessionPda()` | — (pure) | Derives the Portal session PDA locally. | `PublicKey` |
|
|
184
|
+
|
|
185
|
+
### Raw account reads
|
|
186
|
+
|
|
187
|
+
| Method | Lane | Description | Returns |
|
|
188
|
+
| --- | --- | --- | --- |
|
|
189
|
+
| `erRpc(method, params?)` | ER | Raw JSON-RPC to the ER (3 retries on transient network errors). | `Promise<T>` |
|
|
190
|
+
| `erGetAccount(pk)` | ER | Read an account from the ER at `processed`. | `Promise<AccountData \| null>` |
|
|
191
|
+
| `l1GetAccount(pk)` | L1 | Read an account from L1 at `confirmed`. | `Promise<AccountData \| null>` |
|
|
192
|
+
| `erBalance(pk)` | ER | Lamport balance on the ER (`getBalance`, `processed`). | `Promise<bigint>` |
|
|
193
|
+
|
|
194
|
+
`AccountData = { data: Uint8Array; lamports: bigint; owner: string }`.
|
|
195
|
+
|
|
196
|
+
### Portal control / custody (L1)
|
|
197
|
+
|
|
198
|
+
| Method | Lane | Description | Returns |
|
|
199
|
+
| --- | --- | --- | --- |
|
|
200
|
+
| `openSession()` | L1 | Open the global Portal session (idempotent — checks if the session PDA exists first). | `Promise<{ signature: string \| null; sessionPda: PublicKey; alreadyOpen: boolean }>` |
|
|
201
|
+
| `closeSession()` | L1 | Close the session. | `Promise<string>` (signature) |
|
|
202
|
+
| `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) |
|
|
205
|
+
| `delegateKeypairAccount(target: Keypair)` | L1 | Delegate a plain System-owned keypair account into the ER (the "proper" delegation path). | `Promise<string>` |
|
|
206
|
+
| `createPortalOwnedAccount(space?)` | L1 | Create a fresh Portal-owned account (delegation-target helper). | `Promise<Keypair>` |
|
|
207
|
+
|
|
208
|
+
> `delegateKeypairAccount` / `createPortalOwnedAccount` are **not exercised by the integration test** (dev mode uses zero delegated accounts). Their encoders/account order are unit-tested and verified against the runtime, but the end-to-end delegation+settlement flow is unproven on the live validator.
|
|
209
|
+
|
|
210
|
+
### eva PDA derivations (pure, no network)
|
|
211
|
+
|
|
212
|
+
| Method | Lane | Description | Returns |
|
|
213
|
+
| --- | --- | --- | --- |
|
|
214
|
+
| `globalStatePda()` | — | eva global state PDA (`["global"]`). | `PublicKey` |
|
|
215
|
+
| `trenchPda(id)` | — | Trench PDA (`["trench", id_le]`). | `PublicKey` |
|
|
216
|
+
| `tokenMintPda(id)` | — | Trench token mint PDA (`["mint", trench]`). | `PublicKey` |
|
|
217
|
+
| `trenchVault(id)` | — | Trench vault ATA (mint, trench). | `PublicKey` |
|
|
218
|
+
| `userAta(id, user?)` | — | User token ATA (mint, user; defaults to wallet). | `PublicKey` |
|
|
219
|
+
|
|
220
|
+
### eva state reads
|
|
221
|
+
|
|
222
|
+
| Method | Lane | Description | Returns |
|
|
223
|
+
| --- | --- | --- | --- |
|
|
224
|
+
| `getGlobal(source?="er")` | ER or L1 | Decode the global state. | `Promise<GlobalState \| null>` |
|
|
225
|
+
| `getTrench(id, source?="er", retries?=1)` | ER or L1 | Decode a trench (re-reads up to `retries` attempts, sleeping `pollIntervalMs` between). Param is really "attempts": `retries=1` = a single read, no sleep. | `Promise<Trench \| null>` |
|
|
226
|
+
| `getUserTokenBalance(id, user?, source?="er")` | ER or L1 | User's token balance (SPL amount @ offset 64). | `Promise<bigint>` |
|
|
227
|
+
|
|
228
|
+
`GlobalState = { admin; feeRecipient; totalTrenches; biddingDuration; tradingDuration }`. `Trench = { id; status: "Bidding"|"Trading"|"Ended"|"Unknown"; totalDepositedSol; reserves: AmmReserves; biddingStartBlock; biddingEndBlock; tradingEndBlock; tokenMint; startTime; prizePoolReserves; bump }`.
|
|
229
|
+
|
|
230
|
+
### Off-chain quotes / decode (pure)
|
|
231
|
+
|
|
232
|
+
| Method | Lane | Description | Returns |
|
|
233
|
+
| --- | --- | --- | --- |
|
|
234
|
+
| `previewBuy(reserves, solPayWithFee)` | — | Offline buy quote (1% fee, then constant-product curve). Matches the on-chain mint exactly. | `BuyQuote \| null` |
|
|
235
|
+
| `previewSell(reserves, tokensIn)` | — | Offline sell quote (curve output, then 1% fee). | `SellQuote \| null` |
|
|
236
|
+
| `decodeReserves(data)` | — | Decode AMM reserves from raw trench bytes. | `AmmReserves` |
|
|
237
|
+
|
|
238
|
+
`BuyQuote = { solPayWithFee; fee; solIntoCurve; tokensOut; reservesAfter }`. `SellQuote = { tokensIn; solOutGross; fee; netSolToUser; reservesAfter }`.
|
|
239
|
+
|
|
240
|
+
### eva game (executed on the ER, zero fee)
|
|
241
|
+
|
|
242
|
+
| Method | Lane | Description | Returns |
|
|
243
|
+
| --- | --- | --- | --- |
|
|
244
|
+
| `initialize({ biddingDuration, tradingDuration })` | ER | Create the global state. Idempotent — returns `null` if already initialized. | `Promise<string \| null>` |
|
|
245
|
+
| `createTrench({ initialVirtualTokenReserves? })` | ER | Create the next trench; `trenchId = totalTrenches + 1` (auto). Default `initialVirtualTokenReserves = INITIAL_TOKEN_SUPPLY / 2`. | `Promise<{ trenchId: bigint; signature: string }>` |
|
|
246
|
+
| `deposit({ trenchId, lamports, thinkId? })` | ER | Deposit SOL during the bidding phase. | `Promise<string>` |
|
|
247
|
+
| `finalize({ trenchId })` | ER | Waits for ER slot `>= biddingEndBlock` (polls up to 80×`pollIntervalMs`), then seeds the AMM → Trading. | `Promise<string>` |
|
|
248
|
+
| `ensureUserAta({ trenchId, user? })` | ER | Idempotently create the user's token ATA on the ER (buy/sell require it). | `Promise<string>` |
|
|
249
|
+
| `buy({ trenchId, solPayWithFee, thinkId? })` | ER | Buy tokens off the curve (1% fee). | `Promise<{ signature; trench: Trench; userTokens: bigint }>` |
|
|
250
|
+
| `sell({ trenchId, tokenAmount, minSolOutput?, thinkId? })` | ER | Sell tokens back to the curve (1% fee). | `Promise<{ signature; trench: Trench; userTokens: bigint }>` |
|
|
251
|
+
|
|
252
|
+
> `finalize` sets `biddingEndBlock = current_slot + biddingDuration`. With a large `biddingDuration` (the test uses `120`), the slot wait can hit its ~40s budget (80×500ms) and then attempt finalize anyway, failing on-chain with `NotBiddingPhase`. **Use a small `biddingDuration` for fast demos.**
|
|
253
|
+
|
|
254
|
+
### Also exported from `src/index.ts`
|
|
255
|
+
|
|
256
|
+
- `NETWORKS`, `resolveConfig`, `DEFAULT_PORTAL_PROGRAM_ID`, `DEFAULT_EVA_PROGRAM_ID`, `INITIAL_TOKEN_SUPPLY`, `TOKEN_DECIMALS`
|
|
257
|
+
- AMM helpers (`src/amm.ts`): `getBuyPrice`, `applyBuy`, `getSellPrice`, `applySell`, type `AmmReserves`, `TradeResult`
|
|
258
|
+
- Quote helpers: `previewBuy`, `previewSell`, `FEE_RATE_BPS` (100 = 1%), `BASIS_POINTS` (10000), types `BuyQuote`/`SellQuote`
|
|
259
|
+
- Decoders: `decodeTrench`, `decodeTrenchReserves`, `decodeGlobalState`, `decodeTokenAmount`, `TRENCH_OFFSETS`, `TRENCH_LEN` (242), types `Trench`/`TrenchStatus`/`GlobalState`
|
|
260
|
+
- `portal.*` (corrected Portal encoders + PDA derivations + instruction builders)
|
|
261
|
+
- `evaPdas.*` (eva PDA/ATA derivations)
|
|
262
|
+
- Types: `NorthstarEvaOptions`, `Lane`, `AccountData`, `ErTxResult`, `NetworkName`, `NetworkPreset`, `ResolvedConfig`, `NorthstarEvaInput`
|
|
263
|
+
|
|
264
|
+
## Running the tests
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
# Unit tests (pure: AMM, codec, decode) — no validator needed (~200ms)
|
|
268
|
+
npm test # node --experimental-strip-types --test test/{amm,codec,decode}.test.ts
|
|
269
|
+
|
|
270
|
+
# Live integration test — requires a running validator
|
|
271
|
+
# (L1 :8899 + ER :8910 up, eva deployed, ~/.config/solana/id.json funded)
|
|
272
|
+
npm run test:integration # override the keypair with ADMIN_KEYPAIR=/path/to/id.json
|
|
273
|
+
|
|
274
|
+
# Typecheck
|
|
275
|
+
npm run typecheck # tsc --noEmit
|
|
276
|
+
|
|
277
|
+
# Regenerate the single-file drop-in
|
|
278
|
+
npm run bundle # writes dist-single/northstar-eva-sdk.single.ts
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The integration test (`test/integration.test.ts`) drives the full lifecycle: health → sync → session → fund ER → initialize → createTrench → deposit → finalize → ATA → BUY → SELL, asserting that `realSolReserves == totalDepositedSol * 20 / 100` after finalize and that the offline `previewBuy` quote **exactly equals** the on-chain tokens minted.
|
|
282
|
+
|
|
283
|
+
**Validation status:** unit 16/16 pass; integration all 11 steps pass against a live validator; `tsc --noEmit` clean (both the package and the single-file project). No correctness bugs found in a source review against the deployed runtime + IDL.
|
|
284
|
+
|
|
285
|
+
## Limitations (read before using for anything real)
|
|
286
|
+
|
|
287
|
+
- **DEV-MODE ONLY / not trustless.** eva runs on the ER with a session that has **zero delegated accounts** (the write-filter is bypassed) because eva's PDAs cannot sign `Portal::Delegate` (the "PDA-signer wall"). This is the only way it runs today. Single global session, single validator, ER state in-memory (non-durable, lost on restart). **Not for real user funds — demo/devnet only.**
|
|
288
|
+
- **ER fee-payer balance = Portal DepositFee credits**, not the L1 balance. `ensureErFunds` polls ~20s for the credit; if it lags beyond that, the method returns the (insufficient) balance **without throwing** — assert the returned balance yourself.
|
|
289
|
+
- **Settlement is deliberately narrow:** only same-size DATA diffs of pre-existing delegated accounts + Portal SOL rails settle back. eva's new ER accounts, lamport moves, and owner/size changes **do not** settle — its ER results are demo-grade, not committed custody.
|
|
290
|
+
- **The deployed Portal IDL annotation for `Delegate` is stale** (session listed at index 6); this SDK follows the runtime (session at index 2). Do not regenerate a Portal client from that IDL.
|
|
291
|
+
- **devnet has no hosted endpoints** — you must self-host a NorthStar node and pass `l1Rpc`/`erRpc`.
|
|
292
|
+
- **The single-file bundle is a build artifact** — re-run `npm run bundle` before shipping it.
|
|
293
|
+
|
|
294
|
+
For the full system picture (the two lanes, dev mode, settlement), see `local_docs/architecture/eva-on-northstar-how-it-works.md`.
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import * as anchor from "@coral-xyz/anchor";
|
|
2
|
+
import type { Idl } from "@coral-xyz/anchor";
|
|
3
|
+
import { Connection, Keypair, PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js";
|
|
4
|
+
/**
|
|
5
|
+
* Faithful TypeScript port of eva-contract's constant-product bonding-curve AMM.
|
|
6
|
+
* Source: eva-contract/programs/program/src/amm.rs (u128 math → JS bigint).
|
|
7
|
+
* Pure / dependency-free / unit-tested. READ-ONLY simulation for quotes/previews.
|
|
8
|
+
*/
|
|
9
|
+
export interface AmmReserves {
|
|
10
|
+
virtualSolReserves: bigint;
|
|
11
|
+
virtualTokenReserves: bigint;
|
|
12
|
+
realSolReserves: bigint;
|
|
13
|
+
realTokenReserves: bigint;
|
|
14
|
+
initialVirtualTokenReserves: bigint;
|
|
15
|
+
}
|
|
16
|
+
export interface TradeResult {
|
|
17
|
+
tokenAmount: bigint;
|
|
18
|
+
solAmount: bigint;
|
|
19
|
+
}
|
|
20
|
+
/** tokens out for `solAmount` lamports in (ceil division — favors the pool). */
|
|
21
|
+
export declare function getBuyPrice(amm: AmmReserves, solAmount: bigint): bigint | null;
|
|
22
|
+
export declare function applyBuy(amm: AmmReserves, solAmount: bigint): {
|
|
23
|
+
result: TradeResult;
|
|
24
|
+
reserves: AmmReserves;
|
|
25
|
+
} | null;
|
|
26
|
+
/** SOL out for `tokens` in (floor division, capped by real SOL). */
|
|
27
|
+
export declare function getSellPrice(amm: AmmReserves, tokens: bigint): bigint | null;
|
|
28
|
+
export declare function applySell(amm: AmmReserves, tokenAmount: bigint): {
|
|
29
|
+
result: TradeResult;
|
|
30
|
+
reserves: AmmReserves;
|
|
31
|
+
} | null;
|
|
32
|
+
/**
|
|
33
|
+
* Quote / preview engine (pure). Mirrors on-chain fee handling:
|
|
34
|
+
* buy (buy.rs): fee = solPayWithFee * 1%; AMM applied to (solPayWithFee - fee).
|
|
35
|
+
* sell (sell.rs): solOut = curve output; fee = solOut * 1%; user gets (solOut - fee).
|
|
36
|
+
*/
|
|
37
|
+
export declare const FEE_RATE_BPS = 100n;
|
|
38
|
+
export declare const BASIS_POINTS = 10000n;
|
|
39
|
+
export interface BuyQuote {
|
|
40
|
+
solPayWithFee: bigint;
|
|
41
|
+
fee: bigint;
|
|
42
|
+
solIntoCurve: bigint;
|
|
43
|
+
tokensOut: bigint;
|
|
44
|
+
reservesAfter: AmmReserves;
|
|
45
|
+
}
|
|
46
|
+
export interface SellQuote {
|
|
47
|
+
tokensIn: bigint;
|
|
48
|
+
solOutGross: bigint;
|
|
49
|
+
fee: bigint;
|
|
50
|
+
netSolToUser: bigint;
|
|
51
|
+
reservesAfter: AmmReserves;
|
|
52
|
+
}
|
|
53
|
+
export declare function previewBuy(reserves: AmmReserves, solPayWithFee: bigint): BuyQuote | null;
|
|
54
|
+
export declare function previewSell(reserves: AmmReserves, tokensIn: bigint): SellQuote | null;
|
|
55
|
+
/**
|
|
56
|
+
* Dependency-free decoders for eva account bytes (works for L1 OR ER reads).
|
|
57
|
+
* Layouts from eva-contract/programs/program/src/state.rs (Anchor = 8-byte
|
|
58
|
+
* discriminator + Borsh fields). SPL token amount @ offset 64.
|
|
59
|
+
*/
|
|
60
|
+
export declare const TRENCH_OFFSETS: {
|
|
61
|
+
readonly id: 8;
|
|
62
|
+
readonly creator: 16;
|
|
63
|
+
readonly authority: 48;
|
|
64
|
+
readonly status: 80;
|
|
65
|
+
readonly totalDepositedSol: 81;
|
|
66
|
+
readonly virtualSolReserves: 89;
|
|
67
|
+
readonly virtualTokenReserves: 105;
|
|
68
|
+
readonly realSolReserves: 121;
|
|
69
|
+
readonly realTokenReserves: 137;
|
|
70
|
+
readonly initialVirtualTokenReserves: 153;
|
|
71
|
+
readonly biddingStartBlock: 169;
|
|
72
|
+
readonly biddingEndBlock: 177;
|
|
73
|
+
readonly tradingEndBlock: 185;
|
|
74
|
+
readonly tokenMint: 193;
|
|
75
|
+
readonly startTime: 225;
|
|
76
|
+
readonly prizePoolReserves: 233;
|
|
77
|
+
readonly bump: 241;
|
|
78
|
+
};
|
|
79
|
+
export declare const TRENCH_LEN = 242;
|
|
80
|
+
export type TrenchStatus = "Bidding" | "Trading" | "Ended" | "Unknown";
|
|
81
|
+
export interface Trench {
|
|
82
|
+
id: bigint;
|
|
83
|
+
status: TrenchStatus;
|
|
84
|
+
totalDepositedSol: bigint;
|
|
85
|
+
reserves: AmmReserves;
|
|
86
|
+
biddingStartBlock: bigint;
|
|
87
|
+
biddingEndBlock: bigint;
|
|
88
|
+
tradingEndBlock: bigint;
|
|
89
|
+
tokenMint: Uint8Array;
|
|
90
|
+
startTime: bigint;
|
|
91
|
+
prizePoolReserves: bigint;
|
|
92
|
+
bump: number;
|
|
93
|
+
}
|
|
94
|
+
export declare function decodeTrenchReserves(data: Uint8Array): AmmReserves;
|
|
95
|
+
export declare function decodeTrench(data: Uint8Array): Trench;
|
|
96
|
+
export interface GlobalState {
|
|
97
|
+
admin: Uint8Array;
|
|
98
|
+
feeRecipient: Uint8Array;
|
|
99
|
+
totalTrenches: bigint;
|
|
100
|
+
biddingDuration: bigint;
|
|
101
|
+
tradingDuration: bigint;
|
|
102
|
+
}
|
|
103
|
+
export declare function decodeGlobalState(data: Uint8Array): GlobalState;
|
|
104
|
+
export declare function decodeTokenAmount(data: Uint8Array): bigint;
|
|
105
|
+
/**
|
|
106
|
+
* Network presets + resolved config. Works for localnet now and devnet later —
|
|
107
|
+
* the only difference is the endpoints + (possibly) program ids you pass in.
|
|
108
|
+
*
|
|
109
|
+
* IMPORTANT (devnet): public Solana devnet has NO NorthStar ER/Portal. "devnet"
|
|
110
|
+
* here means a NorthStar validator running in a devnet-style deployment — you MUST
|
|
111
|
+
* point l1Rpc/erRpc at THAT node's endpoints. There is no hosted default yet.
|
|
112
|
+
*/
|
|
113
|
+
export declare const DEFAULT_PORTAL_PROGRAM_ID = "5TeWSsjg2gbxCyWVniXeCmwM7UtHTCK7svzJr5xYJzHf";
|
|
114
|
+
export declare const DEFAULT_EVA_PROGRAM_ID = "CQru6EauVg2UwepFCvY5qWuqCPEWYC4ta43wBbkFYtmo";
|
|
115
|
+
export declare const INITIAL_TOKEN_SUPPLY: bigint;
|
|
116
|
+
export declare const TOKEN_DECIMALS = 6;
|
|
117
|
+
export type NetworkName = "localnet" | "devnet";
|
|
118
|
+
export interface NetworkPreset {
|
|
119
|
+
l1Rpc: string;
|
|
120
|
+
erRpc: string;
|
|
121
|
+
erWs: string;
|
|
122
|
+
portalProgramId: string;
|
|
123
|
+
evaProgramId: string;
|
|
124
|
+
}
|
|
125
|
+
export declare const NETWORKS: Record<NetworkName, NetworkPreset>;
|
|
126
|
+
export interface ResolvedConfig {
|
|
127
|
+
l1Rpc: string;
|
|
128
|
+
erRpc: string;
|
|
129
|
+
erWs: string;
|
|
130
|
+
portalProgramId: PublicKey;
|
|
131
|
+
evaProgramId: PublicKey;
|
|
132
|
+
/** Session params for OpenSession. */
|
|
133
|
+
gridId: bigint;
|
|
134
|
+
ttlSlots: bigint;
|
|
135
|
+
feeCap: bigint;
|
|
136
|
+
settlementIntervalSlots: bigint;
|
|
137
|
+
}
|
|
138
|
+
export interface NorthstarEvaInput {
|
|
139
|
+
network?: NetworkName;
|
|
140
|
+
l1Rpc?: string;
|
|
141
|
+
erRpc?: string;
|
|
142
|
+
erWs?: string;
|
|
143
|
+
portalProgramId?: string | PublicKey;
|
|
144
|
+
evaProgramId?: string | PublicKey;
|
|
145
|
+
gridId?: number | bigint;
|
|
146
|
+
ttlSlots?: number | bigint;
|
|
147
|
+
feeCap?: number | bigint;
|
|
148
|
+
settlementIntervalSlots?: number | bigint;
|
|
149
|
+
}
|
|
150
|
+
export declare function resolveConfig(input: NorthstarEvaInput): ResolvedConfig;
|
|
151
|
+
export interface OpenSessionArgs {
|
|
152
|
+
gridId: bigint;
|
|
153
|
+
ttlSlots: bigint;
|
|
154
|
+
feeCap: bigint;
|
|
155
|
+
validator: Uint8Array;
|
|
156
|
+
settlementIntervalSlots: bigint;
|
|
157
|
+
}
|
|
158
|
+
export declare const encodeOpenSession: (a: OpenSessionArgs) => Uint8Array<ArrayBuffer>;
|
|
159
|
+
export declare const encodeCloseSession: () => Uint8Array<ArrayBuffer>;
|
|
160
|
+
export declare const encodeDepositFee: (lamports: bigint) => Uint8Array<ArrayBuffer>;
|
|
161
|
+
export declare const encodeDelegate: (gridId: bigint) => Uint8Array<ArrayBuffer>;
|
|
162
|
+
export declare const encodeUndelegate: () => Uint8Array<ArrayBuffer>;
|
|
163
|
+
export declare const sessionPda: (programId: PublicKey) => anchor.web3.PublicKey;
|
|
164
|
+
export declare const feeVaultPda: (programId: PublicKey) => anchor.web3.PublicKey;
|
|
165
|
+
export declare const delegationRecordPda: (programId: PublicKey, delegated: PublicKey) => anchor.web3.PublicKey;
|
|
166
|
+
export declare const depositReceiptPda: (programId: PublicKey, session: PublicKey, recipient: PublicKey) => anchor.web3.PublicKey;
|
|
167
|
+
export declare const withdrawalSinkPda: (programId: PublicKey, session: PublicKey, recipient: PublicKey) => anchor.web3.PublicKey;
|
|
168
|
+
export declare function openSessionIx(p: {
|
|
169
|
+
programId: PublicKey;
|
|
170
|
+
payer: PublicKey;
|
|
171
|
+
gridId: bigint;
|
|
172
|
+
ttlSlots: bigint;
|
|
173
|
+
feeCap: bigint;
|
|
174
|
+
validator: PublicKey;
|
|
175
|
+
settlementIntervalSlots: bigint;
|
|
176
|
+
}): TransactionInstruction;
|
|
177
|
+
export declare function closeSessionIx(p: {
|
|
178
|
+
programId: PublicKey;
|
|
179
|
+
closer: PublicKey;
|
|
180
|
+
}): TransactionInstruction;
|
|
181
|
+
export declare function depositFeeIx(p: {
|
|
182
|
+
programId: PublicKey;
|
|
183
|
+
depositor: PublicKey;
|
|
184
|
+
recipient: PublicKey;
|
|
185
|
+
lamports: bigint;
|
|
186
|
+
}): TransactionInstruction;
|
|
187
|
+
/** Multi-delegation layout: prefix [payer, system_program, session] + one group. */
|
|
188
|
+
export declare function delegateIx(p: {
|
|
189
|
+
programId: PublicKey;
|
|
190
|
+
payer: PublicKey;
|
|
191
|
+
delegatedAccount: PublicKey;
|
|
192
|
+
ownerProgram: PublicKey;
|
|
193
|
+
buffer: PublicKey;
|
|
194
|
+
gridId: bigint;
|
|
195
|
+
}): TransactionInstruction;
|
|
196
|
+
/** Legacy order (undelegate was not refactored). */
|
|
197
|
+
export declare function undelegateIx(p: {
|
|
198
|
+
programId: PublicKey;
|
|
199
|
+
authority: PublicKey;
|
|
200
|
+
delegatedAccount: PublicKey;
|
|
201
|
+
ownerProgram: PublicKey;
|
|
202
|
+
}): TransactionInstruction;
|
|
203
|
+
export declare const globalStatePda: (programId: PublicKey) => anchor.web3.PublicKey;
|
|
204
|
+
export declare const trenchPda: (programId: PublicKey, id: bigint) => anchor.web3.PublicKey;
|
|
205
|
+
export declare const agentPda: (programId: PublicKey, trench: PublicKey, user: PublicKey) => anchor.web3.PublicKey;
|
|
206
|
+
export declare const tokenMintPda: (programId: PublicKey, trench: PublicKey) => anchor.web3.PublicKey;
|
|
207
|
+
export declare const trenchTokenVault: (mint: PublicKey, trench: PublicKey) => anchor.web3.PublicKey;
|
|
208
|
+
export declare const userTokenAccount: (mint: PublicKey, user: PublicKey) => anchor.web3.PublicKey;
|
|
209
|
+
export type Lane = "l1" | "er";
|
|
210
|
+
export interface AccountData {
|
|
211
|
+
data: Uint8Array;
|
|
212
|
+
lamports: bigint;
|
|
213
|
+
owner: string;
|
|
214
|
+
}
|
|
215
|
+
export interface ErTxResult {
|
|
216
|
+
signature: string;
|
|
217
|
+
err: unknown | null;
|
|
218
|
+
confirmed: boolean;
|
|
219
|
+
}
|
|
220
|
+
export interface NorthstarEvaOptions extends NorthstarEvaInput {
|
|
221
|
+
/** Signer / fee payer. (v1: a Keypair.) */
|
|
222
|
+
wallet: Keypair;
|
|
223
|
+
/** eva IDL object. If omitted, loaded from `evaIdlPath` or the bundled IDL. */
|
|
224
|
+
evaIdl?: Idl;
|
|
225
|
+
evaIdlPath?: string;
|
|
226
|
+
/** Validator identity recorded in the session (settlement signer). Defaults to wallet. */
|
|
227
|
+
validatorIdentity?: string | PublicKey;
|
|
228
|
+
/** ms between status/read polls. */
|
|
229
|
+
pollIntervalMs?: number;
|
|
230
|
+
}
|
|
231
|
+
export declare class NorthstarEva {
|
|
232
|
+
readonly cfg: ResolvedConfig;
|
|
233
|
+
readonly l1: Connection;
|
|
234
|
+
readonly er: Connection;
|
|
235
|
+
readonly wallet: Keypair;
|
|
236
|
+
readonly validatorIdentity: PublicKey;
|
|
237
|
+
readonly program: anchor.Program<Idl>;
|
|
238
|
+
private readonly pollMs;
|
|
239
|
+
constructor(opts: NorthstarEvaOptions);
|
|
240
|
+
static create(opts: NorthstarEvaOptions): NorthstarEva;
|
|
241
|
+
erRpc<T = any>(method: string, params?: unknown[]): Promise<T>;
|
|
242
|
+
/** Read an account from the ER at `processed` (fresh) — null on not-found. */
|
|
243
|
+
erGetAccount(address: PublicKey): Promise<AccountData | null>;
|
|
244
|
+
l1GetAccount(address: PublicKey): Promise<AccountData | null>;
|
|
245
|
+
erBalance(address: PublicKey): Promise<bigint>;
|
|
246
|
+
health(): Promise<{
|
|
247
|
+
l1: boolean;
|
|
248
|
+
er: boolean;
|
|
249
|
+
}>;
|
|
250
|
+
syncStatus(): Promise<{
|
|
251
|
+
isSyncing: boolean;
|
|
252
|
+
latestSyncedSlot: number;
|
|
253
|
+
latestL1Slot: number;
|
|
254
|
+
}>;
|
|
255
|
+
getSessionPdaFromEr(): Promise<any>;
|
|
256
|
+
getDelegatedAccounts(): Promise<string[]>;
|
|
257
|
+
sessionPda(): anchor.web3.PublicKey;
|
|
258
|
+
private sendOnL1;
|
|
259
|
+
/** Send a tx to the ER and confirm by polling (the ER returns Ok on acceptance). */
|
|
260
|
+
sendOnEr(instructions: Transaction["instructions"], signers: Keypair[]): Promise<ErTxResult>;
|
|
261
|
+
private sendEva;
|
|
262
|
+
/** Fetch eva GlobalState from the ER or throw a clear "not initialized" error. */
|
|
263
|
+
private requireGlobal;
|
|
264
|
+
private get methods();
|
|
265
|
+
private bn;
|
|
266
|
+
openSession(): Promise<{
|
|
267
|
+
signature: string | null;
|
|
268
|
+
sessionPda: PublicKey;
|
|
269
|
+
alreadyOpen: boolean;
|
|
270
|
+
}>;
|
|
271
|
+
closeSession(): Promise<string>;
|
|
272
|
+
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>;
|
|
275
|
+
/** Ensure the ER fee payer has >= `lamports`; tops up via DepositFee + waits for the credit. */
|
|
276
|
+
ensureErFunds(lamports: bigint, recipient?: PublicKey): Promise<bigint>;
|
|
277
|
+
/** Delegate a plain keypair account (System-owned) into the ER (the "proper" path). */
|
|
278
|
+
delegateKeypairAccount(target: Keypair): Promise<string>;
|
|
279
|
+
/** Create a fresh Portal-owned account on L1 (delegation target helper). */
|
|
280
|
+
createPortalOwnedAccount(space?: number): Promise<Keypair>;
|
|
281
|
+
globalStatePda(): anchor.web3.PublicKey;
|
|
282
|
+
trenchPda(id: bigint): anchor.web3.PublicKey;
|
|
283
|
+
tokenMintPda(id: bigint): anchor.web3.PublicKey;
|
|
284
|
+
trenchVault(id: bigint): anchor.web3.PublicKey;
|
|
285
|
+
userAta(id: bigint, user?: PublicKey): anchor.web3.PublicKey;
|
|
286
|
+
getGlobal(source?: Lane): Promise<GlobalState | null>;
|
|
287
|
+
getTrench(id: bigint, source?: Lane, retries?: number): Promise<Trench | null>;
|
|
288
|
+
getUserTokenBalance(id: bigint, user?: PublicKey, source?: Lane): Promise<bigint>;
|
|
289
|
+
previewBuy(reserves: AmmReserves, solPayWithFee: bigint): BuyQuote | null;
|
|
290
|
+
previewSell(reserves: AmmReserves, tokensIn: bigint): SellQuote | null;
|
|
291
|
+
decodeReserves(data: Uint8Array): AmmReserves;
|
|
292
|
+
/** Query an account's SOL balance on BOTH lanes (lamports). No program call, no fee. */
|
|
293
|
+
querySolBalance(account?: PublicKey): Promise<{
|
|
294
|
+
er: bigint;
|
|
295
|
+
l1: bigint;
|
|
296
|
+
}>;
|
|
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>;
|
|
299
|
+
/** The ER WithdrawalSink PDA for a recipient (where ER withdrawal requests are sent). */
|
|
300
|
+
getWithdrawalSink(recipient?: PublicKey): PublicKey;
|
|
301
|
+
/**
|
|
302
|
+
* "Withdraw fee from ER" (path 2): an ER `system_transfer` from `recipient` → its
|
|
303
|
+
* WithdrawalSink. The validator pays the L1 SOL ASYNCHRONOUSLY at the next settlement
|
|
304
|
+
* (use {@link waitForL1Settlement} to await it). `recipient` is a Keypair (it signs the
|
|
305
|
+
* ER transfer; the L1 receiver never signs).
|
|
306
|
+
*
|
|
307
|
+
* NOTE: the L1 payout currently goes to the SAME account that deposited — an arbitrary
|
|
308
|
+
* `toL1` destination is not supported by the Portal program yet (pending protocol change).
|
|
309
|
+
*/
|
|
310
|
+
requestWithdraw(p: {
|
|
311
|
+
lamports: bigint;
|
|
312
|
+
recipient: Keypair;
|
|
313
|
+
toL1?: PublicKey;
|
|
314
|
+
}): Promise<ErTxResult>;
|
|
315
|
+
/** Poll an account's L1 balance until it rises by >= `expectedDelta` (settlement is async). Returns the final L1 balance. */
|
|
316
|
+
waitForL1Settlement(account: PublicKey, expectedDelta: bigint, timeoutMs?: number): Promise<bigint>;
|
|
317
|
+
initialize(p: {
|
|
318
|
+
biddingDuration: bigint;
|
|
319
|
+
tradingDuration: bigint;
|
|
320
|
+
}): Promise<string | null>;
|
|
321
|
+
/** createTrench with the next sequential id (id == total_trenches + 1). */
|
|
322
|
+
createTrench(p?: {
|
|
323
|
+
initialVirtualTokenReserves?: bigint;
|
|
324
|
+
}): Promise<{
|
|
325
|
+
trenchId: bigint;
|
|
326
|
+
signature: string;
|
|
327
|
+
}>;
|
|
328
|
+
deposit(p: {
|
|
329
|
+
trenchId: bigint;
|
|
330
|
+
lamports: bigint;
|
|
331
|
+
thinkId?: bigint;
|
|
332
|
+
}): Promise<string>;
|
|
333
|
+
/** finalize — waits for the ER slot to pass biddingEndBlock, then seeds the AMM. */
|
|
334
|
+
finalize(p: {
|
|
335
|
+
trenchId: bigint;
|
|
336
|
+
}): Promise<string>;
|
|
337
|
+
/** Create the user's token ATA on the ER (buy/sell require it to pre-exist). */
|
|
338
|
+
ensureUserAta(p: {
|
|
339
|
+
trenchId: bigint;
|
|
340
|
+
user?: PublicKey;
|
|
341
|
+
}): Promise<string>;
|
|
342
|
+
buy(p: {
|
|
343
|
+
trenchId: bigint;
|
|
344
|
+
solPayWithFee: bigint;
|
|
345
|
+
thinkId?: bigint;
|
|
346
|
+
}): Promise<{
|
|
347
|
+
signature: string;
|
|
348
|
+
trench: Trench;
|
|
349
|
+
userTokens: bigint;
|
|
350
|
+
}>;
|
|
351
|
+
sell(p: {
|
|
352
|
+
trenchId: bigint;
|
|
353
|
+
tokenAmount: bigint;
|
|
354
|
+
minSolOutput?: bigint;
|
|
355
|
+
thinkId?: bigint;
|
|
356
|
+
}): Promise<{
|
|
357
|
+
signature: string;
|
|
358
|
+
trench: Trench;
|
|
359
|
+
userTokens: bigint;
|
|
360
|
+
}>;
|
|
361
|
+
}
|