@percolatorct/sdk 1.0.0-beta.13 → 1.0.0-beta.14
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/dist/index.d.ts +3459 -7
- package/dist/index.js +305 -837
- package/dist/index.js.map +1 -1
- package/package.json +5 -11
- package/LICENSE +0 -201
- package/README.md +0 -754
- package/dist/abi/accounts.d.ts +0 -249
- package/dist/abi/encode.d.ts +0 -46
- package/dist/abi/errors.d.ts +0 -45
- package/dist/abi/index.d.ts +0 -4
- package/dist/abi/instructions.d.ts +0 -1111
- package/dist/config/program-ids.d.ts +0 -50
- package/dist/math/index.d.ts +0 -2
- package/dist/math/trading.d.ts +0 -222
- package/dist/math/warmup.d.ts +0 -105
- package/dist/oracle/price-router.d.ts +0 -38
- package/dist/runtime/index.d.ts +0 -2
- package/dist/runtime/lighthouse.d.ts +0 -170
- package/dist/runtime/tx.d.ts +0 -31
- package/dist/solana/adl.d.ts +0 -305
- package/dist/solana/ata.d.ts +0 -18
- package/dist/solana/dex-oracle.d.ts +0 -49
- package/dist/solana/discovery.d.ts +0 -492
- package/dist/solana/index.d.ts +0 -11
- package/dist/solana/oracle.d.ts +0 -52
- package/dist/solana/pda.d.ts +0 -49
- package/dist/solana/rpc-pool.d.ts +0 -347
- package/dist/solana/slab.d.ts +0 -358
- package/dist/solana/stake.d.ts +0 -216
- package/dist/solana/static-markets.d.ts +0 -86
- package/dist/solana/token-program.d.ts +0 -19
- package/dist/validation.d.ts +0 -70
package/README.md
DELETED
|
@@ -1,754 +0,0 @@
|
|
|
1
|
-
# @percolator/sdk
|
|
2
|
-
|
|
3
|
-
TypeScript SDK for building clients, bots, and UIs on top of the [Percolator](https://github.com/dcccrypto/percolator) perpetual futures protocol on Solana.
|
|
4
|
-
|
|
5
|
-
> **⚠️ BETA** — `1.0.0-beta.1`. Security audit (0x-SquidSol) complete. All 709 tests passing. Pending Helius API key + mainnet market deployment before `1.0.0` stable.
|
|
6
|
-
|
|
7
|
-
[](https://www.npmjs.com/package/@percolator/sdk)
|
|
8
|
-
[](LICENSE)
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Installation
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
pnpm add @percolator/sdk
|
|
16
|
-
# or
|
|
17
|
-
npm install @percolator/sdk
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
**Peer dependency:** `@solana/web3.js ^1.95`
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Quick Start
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
import {
|
|
28
|
-
getProgramId,
|
|
29
|
-
deriveVaultAuthority,
|
|
30
|
-
encodeInitUser,
|
|
31
|
-
encodeDepositCollateral,
|
|
32
|
-
encodeTradeNoCpi,
|
|
33
|
-
parseHeader,
|
|
34
|
-
parseConfig,
|
|
35
|
-
parseAllAccounts,
|
|
36
|
-
detectSlabLayout,
|
|
37
|
-
computeMarkPnl,
|
|
38
|
-
computeLiqPrice,
|
|
39
|
-
simulateOrSend,
|
|
40
|
-
} from "@percolator/sdk";
|
|
41
|
-
|
|
42
|
-
// Get program ID (defaults to devnet)
|
|
43
|
-
const programId = getProgramId("devnet");
|
|
44
|
-
|
|
45
|
-
// Derive vault authority PDA
|
|
46
|
-
const [vaultAuth, bump] = deriveVaultAuthority(programId, slabPubkey);
|
|
47
|
-
|
|
48
|
-
// Read and parse on-chain slab account
|
|
49
|
-
const slabInfo = await connection.getAccountInfo(slabPubkey);
|
|
50
|
-
const slabData = new Uint8Array(slabInfo!.data);
|
|
51
|
-
const header = parseHeader(slabData);
|
|
52
|
-
const layout = detectSlabLayout(slabData.length);
|
|
53
|
-
const config = parseConfig(slabData, layout!);
|
|
54
|
-
const accounts = parseAllAccounts(slabData);
|
|
55
|
-
|
|
56
|
-
// Compute PnL for a position
|
|
57
|
-
const pnl = computeMarkPnl(positionSize, entryPrice, oraclePrice);
|
|
58
|
-
const liqPrice = computeLiqPrice(entryPrice, capital, positionSize, 500n);
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Features
|
|
64
|
-
|
|
65
|
-
### ABI Encoding & Decoding
|
|
66
|
-
|
|
67
|
-
Type-safe instruction builders matching the on-chain Rust layout byte-for-byte:
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
import { buildInitMarketIxData, buildTradeNoCpiIxData, IX_TAG } from "@percolator/sdk";
|
|
71
|
-
|
|
72
|
-
// Build InitMarket instruction data (256 bytes)
|
|
73
|
-
const data = buildInitMarketIxData({
|
|
74
|
-
admin: adminPubkey,
|
|
75
|
-
collateralMint: mintPubkey,
|
|
76
|
-
indexFeedId: pythFeedId,
|
|
77
|
-
maxStaleSecs: 60n,
|
|
78
|
-
confFilterBps: 250,
|
|
79
|
-
invert: false,
|
|
80
|
-
unitScale: 1_000_000_000, // lamports per unit
|
|
81
|
-
riskParams: { /* ... */ },
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// Build trade instruction
|
|
85
|
-
const tradeData = buildTradeNoCpiIxData({
|
|
86
|
-
userIdx: 0,
|
|
87
|
-
lpIdx: 0,
|
|
88
|
-
requestedSize: 1_000_000n, // positive = long, negative = short
|
|
89
|
-
maxSlippage: 50, // bps
|
|
90
|
-
});
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
**Supported instructions:** `InitMarket`, `InitUser`, `InitLP`, `DepositCollateral`, `WithdrawCollateral`, `KeeperCrank`, `TradeNoCpi`, `TradeCpi`, `LiquidateAtOracle`, `CloseAccount`, `TopUpInsurance`, `SetRiskThreshold`, `UpdateAdmin`, `UpdateConfig`, `SetMaintenanceFee`, `PushOraclePrice`, `ResolveMarket`, `SetOiImbalanceHardBlock`, `SetOracleAuthority`, and more.
|
|
94
|
-
|
|
95
|
-
### Admin Instructions
|
|
96
|
-
|
|
97
|
-
#### SetOiImbalanceHardBlock (tag=71)
|
|
98
|
-
|
|
99
|
-
Prevents new trades from pushing the long/short OI skew above a configurable threshold.
|
|
100
|
-
When triggered, the on-chain error `OiImbalanceHardBlock` (code 59) is returned.
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
import {
|
|
104
|
-
encodeSetOiImbalanceHardBlock,
|
|
105
|
-
ACCOUNTS_SET_OI_IMBALANCE_HARD_BLOCK,
|
|
106
|
-
buildAccountMetas,
|
|
107
|
-
buildIx,
|
|
108
|
-
simulateOrSend,
|
|
109
|
-
} from "@percolator/sdk";
|
|
110
|
-
|
|
111
|
-
// threshold_bps = 0 → hard block disabled (default)
|
|
112
|
-
// threshold_bps = 5_000 → block trades that push skew above 50%
|
|
113
|
-
// threshold_bps = 8_000 → block trades that push skew above 80%
|
|
114
|
-
// threshold_bps = 10_000 → lock dominant side once any OI exists
|
|
115
|
-
|
|
116
|
-
const data = encodeSetOiImbalanceHardBlock({ thresholdBps: 8_000 });
|
|
117
|
-
const keys = buildAccountMetas(ACCOUNTS_SET_OI_IMBALANCE_HARD_BLOCK, [
|
|
118
|
-
adminPublicKey, // [signer]
|
|
119
|
-
slabPublicKey, // [writable]
|
|
120
|
-
]);
|
|
121
|
-
const ix = buildIx({ programId, keys, data });
|
|
122
|
-
|
|
123
|
-
const result = await simulateOrSend({ connection, ix, signers: [admin] });
|
|
124
|
-
console.log("signature:", result.signature);
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
#### SetOracleAuthority (tag=16)
|
|
128
|
-
|
|
129
|
-
Delegates the `PushOraclePrice` right to a specific keypair (e.g. a crank bot).
|
|
130
|
-
Pass `PublicKey.default` (all zeros) to revoke — the program then falls back to Pyth/Chainlink.
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
import { PublicKey } from "@solana/web3.js";
|
|
134
|
-
import {
|
|
135
|
-
encodeSetOracleAuthority,
|
|
136
|
-
ACCOUNTS_SET_ORACLE_AUTHORITY,
|
|
137
|
-
buildAccountMetas,
|
|
138
|
-
buildIx,
|
|
139
|
-
simulateOrSend,
|
|
140
|
-
} from "@percolator/sdk";
|
|
141
|
-
|
|
142
|
-
// Delegate to a crank bot
|
|
143
|
-
const data = encodeSetOracleAuthority({ newAuthority: crankBot.publicKey });
|
|
144
|
-
const keys = buildAccountMetas(ACCOUNTS_SET_ORACLE_AUTHORITY, [
|
|
145
|
-
adminPublicKey, // [signer, writable]
|
|
146
|
-
slabPublicKey, // [writable]
|
|
147
|
-
]);
|
|
148
|
-
const ix = buildIx({ programId, keys, data });
|
|
149
|
-
|
|
150
|
-
await simulateOrSend({ connection, ix, signers: [admin] });
|
|
151
|
-
|
|
152
|
-
// Revoke — fall back to Pyth/Chainlink
|
|
153
|
-
const revokeData = encodeSetOracleAuthority({ newAuthority: PublicKey.default });
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
See [`examples/admin-instructions.ts`](examples/admin-instructions.ts) for full end-to-end examples.
|
|
157
|
-
|
|
158
|
-
### Account Deserialization
|
|
159
|
-
|
|
160
|
-
Parse the on-chain slab account into typed TypeScript objects:
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
import {
|
|
164
|
-
parseHeader,
|
|
165
|
-
parseConfig,
|
|
166
|
-
parseAllAccounts,
|
|
167
|
-
detectSlabLayout,
|
|
168
|
-
} from "@percolator/sdk";
|
|
169
|
-
|
|
170
|
-
const slabData = new Uint8Array(accountInfo.data);
|
|
171
|
-
const header = parseHeader(slabData);
|
|
172
|
-
const layout = detectSlabLayout(slabData.length)!;
|
|
173
|
-
const config = parseConfig(slabData, layout);
|
|
174
|
-
const accounts = parseAllAccounts(slabData);
|
|
175
|
-
|
|
176
|
-
// header.magic, header.version, header.admin, header.nonce
|
|
177
|
-
// header.resolved, header.paused
|
|
178
|
-
|
|
179
|
-
// config.collateralMint, config.vaultPubkey, config.indexFeedId
|
|
180
|
-
// config.maxStalenessSlots, config.confFilterBps
|
|
181
|
-
// config.fundingHorizonSlots, config.fundingKBps
|
|
182
|
-
// config.threshFloor, config.threshRiskBps
|
|
183
|
-
|
|
184
|
-
// accounts[i].account.owner, accounts[i].account.capital,
|
|
185
|
-
// accounts[i].account.pnl, accounts[i].account.positionSize
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### PDA Derivation
|
|
189
|
-
|
|
190
|
-
All program-derived addresses with correct seeds:
|
|
191
|
-
|
|
192
|
-
```typescript
|
|
193
|
-
import {
|
|
194
|
-
deriveVaultAuthority,
|
|
195
|
-
deriveLpPda,
|
|
196
|
-
deriveInsuranceLpMint,
|
|
197
|
-
} from "@percolator/sdk";
|
|
198
|
-
|
|
199
|
-
const [vaultAuth, bump] = deriveVaultAuthority(programId, slab);
|
|
200
|
-
const [lpPda, lpBump] = deriveLpPda(programId, slab, lpIndex);
|
|
201
|
-
const [insMint, insBump] = deriveInsuranceLpMint(programId, slab);
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Trading Math
|
|
205
|
-
|
|
206
|
-
Coin-margined perpetual math utilities (all BigInt, no floating-point):
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
import {
|
|
210
|
-
computeMarkPnl,
|
|
211
|
-
computeLiqPrice,
|
|
212
|
-
computeEntryPrice,
|
|
213
|
-
computeEffectiveLeverage,
|
|
214
|
-
} from "@percolator/sdk";
|
|
215
|
-
|
|
216
|
-
// Mark-to-market PnL (in native token units)
|
|
217
|
-
const pnl = computeMarkPnl(positionSize, entryPriceE6, oraclePriceE6);
|
|
218
|
-
|
|
219
|
-
// Liquidation price given capital and maintenance margin
|
|
220
|
-
const liqPrice = computeLiqPrice(entryPriceE6, capital, positionSize, 500n);
|
|
221
|
-
|
|
222
|
-
// All values use e6 format: 1 USD = 1_000_000
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Oracle Price Router
|
|
226
|
-
|
|
227
|
-
Automatic oracle discovery and ranking for any Solana token:
|
|
228
|
-
|
|
229
|
-
```typescript
|
|
230
|
-
import { resolvePrice } from "@percolator/sdk";
|
|
231
|
-
|
|
232
|
-
const result = await resolvePrice(tokenMint);
|
|
233
|
-
// result.bestSource — highest-confidence price source
|
|
234
|
-
// result.allSources — all discovered sources ranked by liquidity
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
Supports **Pyth**, **DexScreener** (Raydium, Orca, Meteora), and **Jupiter** price feeds.
|
|
238
|
-
|
|
239
|
-
### Program ID Configuration
|
|
240
|
-
|
|
241
|
-
Network-aware program ID resolution:
|
|
242
|
-
|
|
243
|
-
```typescript
|
|
244
|
-
import { getProgramId, getMatcherProgramId } from "@percolator/sdk";
|
|
245
|
-
|
|
246
|
-
// Defaults to devnet
|
|
247
|
-
const programId = getProgramId();
|
|
248
|
-
|
|
249
|
-
// Explicit network selection
|
|
250
|
-
const mainnetId = getProgramId("mainnet");
|
|
251
|
-
|
|
252
|
-
// Environment variable override: PROGRAM_ID=<your-id>
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### Auto-Deleveraging (ADL)
|
|
256
|
-
|
|
257
|
-
ADL (Auto-Deleveraging) reduces the most-profitable opposing positions when the insurance fund is depleted. The SDK provides both on-chain and API-based ADL utilities.
|
|
258
|
-
|
|
259
|
-
#### Checking if ADL is triggered
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
import { isAdlTriggered } from "@percolator/sdk";
|
|
263
|
-
|
|
264
|
-
const accountInfo = await connection.getAccountInfo(slabKey);
|
|
265
|
-
if (isAdlTriggered(accountInfo!.data)) {
|
|
266
|
-
console.log("Insurance fund depleted — ADL is active");
|
|
267
|
-
}
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
#### Ranking positions for ADL
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
import { fetchAdlRankedPositions } from "@percolator/sdk";
|
|
274
|
-
|
|
275
|
-
const { ranked, longs, shorts, isTriggered } = await fetchAdlRankedPositions(
|
|
276
|
-
connection,
|
|
277
|
-
slabKey,
|
|
278
|
-
);
|
|
279
|
-
// ranked — all positions sorted by PnL% descending (ADL priority order)
|
|
280
|
-
// longs — top-ranked long position (ADL target if insurance negative on short side)
|
|
281
|
-
// shorts — top-ranked short position
|
|
282
|
-
// isTriggered — whether pnl_pos_tot exceeds max_pnl_cap on-chain
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
#### Building an ADL instruction
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
288
|
-
import { buildAdlInstruction, buildAdlTransaction, getProgramId } from "@percolator/sdk";
|
|
289
|
-
|
|
290
|
-
const programId = getProgramId("devnet");
|
|
291
|
-
|
|
292
|
-
// Build instruction directly (caller already has target index)
|
|
293
|
-
const ix = buildAdlInstruction(
|
|
294
|
-
callerPublicKey, // keeper / crank wallet
|
|
295
|
-
slabPublicKey,
|
|
296
|
-
oracleFeedPublicKey,
|
|
297
|
-
programId,
|
|
298
|
-
targetAccountIndex, // number — index of account to deleverage
|
|
299
|
-
);
|
|
300
|
-
|
|
301
|
-
// OR: fetch + rank + pick top target automatically
|
|
302
|
-
const ix2 = await buildAdlTransaction(
|
|
303
|
-
connection,
|
|
304
|
-
callerPublicKey,
|
|
305
|
-
slabPublicKey,
|
|
306
|
-
oracleFeedPublicKey,
|
|
307
|
-
programId,
|
|
308
|
-
"long", // side to deleverage ("long" | "short")
|
|
309
|
-
);
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
#### Decoding on-chain ADL events
|
|
313
|
-
|
|
314
|
-
```typescript
|
|
315
|
-
import { parseAdlEvent } from "@percolator/sdk";
|
|
316
|
-
|
|
317
|
-
// After sending / confirming an ExecuteAdl transaction:
|
|
318
|
-
const tx = await connection.getTransaction(sig, { commitment: "confirmed" });
|
|
319
|
-
const event = parseAdlEvent(tx?.meta?.logMessages ?? []);
|
|
320
|
-
// event.tag — 0xAD1E_0001 (2904424449)
|
|
321
|
-
// event.targetIdx — account index that was deleveraged
|
|
322
|
-
// event.price — oracle price at execution (e6)
|
|
323
|
-
// event.closedAbs — absolute position size closed (i128)
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
#### Fetching ADL rankings via HTTP API
|
|
327
|
-
|
|
328
|
-
```typescript
|
|
329
|
-
import { fetchAdlRankings } from "@percolator/sdk";
|
|
330
|
-
|
|
331
|
-
const result = await fetchAdlRankings(
|
|
332
|
-
"https://percolatorlaunch.com/api",
|
|
333
|
-
slabAddress,
|
|
334
|
-
);
|
|
335
|
-
// result.slabAddress — slab public key (base58)
|
|
336
|
-
// result.adlNeeded — true if ADL is triggered (capExceeded or utilizationTriggered)
|
|
337
|
-
// result.capExceeded — true if pnlPosTot > maxPnlCap
|
|
338
|
-
// result.insuranceDepleted — true if insurance fund balance == 0
|
|
339
|
-
// result.utilizationTriggered — true if utilization BPS exceeds the configured ADL threshold
|
|
340
|
-
// result.pnlPosTot — aggregate profitable PnL (decimal string)
|
|
341
|
-
// result.maxPnlCap — max PnL cap from market config (decimal string, "0" if unconfigured)
|
|
342
|
-
// result.excess — excess PnL above cap (decimal string)
|
|
343
|
-
// result.insuranceFundBalance — insurance fund balance (decimal string)
|
|
344
|
-
// result.insuranceFundFeeRevenue — insurance fund lifetime fee revenue (decimal string)
|
|
345
|
-
// result.insuranceUtilizationBps — insurance utilization in basis points (0–10000)
|
|
346
|
-
// result.rankings — AdlApiRanking[] sorted by rank (1 = first to deleverage)
|
|
347
|
-
// .rank — rank (1 = highest PnL%, deleveraged first)
|
|
348
|
-
// .idx — slab account index (pass as targetIdx to buildAdlInstruction)
|
|
349
|
-
// .pnlAbs — absolute PnL in lamports (decimal string)
|
|
350
|
-
// .capital — capital at entry in lamports (decimal string)
|
|
351
|
-
// .pnlPctMillionths — pnl * 1_000_000 / capital (decimal string)
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
#### ADL error codes (61–65)
|
|
355
|
-
|
|
356
|
-
| Code | Name | Description |
|
|
357
|
-
|------|------|-------------|
|
|
358
|
-
| 61 | `EngineSideBlocked` | Trade blocked — this side is in DrainOnly or ResetPending mode |
|
|
359
|
-
| 62 | `EngineCorruptState` | Slab state corrupt — critical internal error, please report |
|
|
360
|
-
| 63 | `InsuranceFundNotDepleted` | ADL not triggered yet (insurance fund healthy) |
|
|
361
|
-
| 64 | `NoAdlCandidates` | No eligible positions to deleverage |
|
|
362
|
-
| 65 | `BankruptPositionAlreadyClosed` | Target position already closed |
|
|
363
|
-
|
|
364
|
-
---
|
|
365
|
-
|
|
366
|
-
### Transaction Helpers
|
|
367
|
-
|
|
368
|
-
Build, simulate, and send transactions with error parsing:
|
|
369
|
-
|
|
370
|
-
```typescript
|
|
371
|
-
import { buildIx, simulateOrSend } from "@percolator/sdk";
|
|
372
|
-
|
|
373
|
-
const ix = buildIx({ programId, keys: accountMetas, data: ixData });
|
|
374
|
-
|
|
375
|
-
const result = await simulateOrSend({
|
|
376
|
-
connection,
|
|
377
|
-
ix,
|
|
378
|
-
signers: [payer],
|
|
379
|
-
simulate: false, // true = simulate only
|
|
380
|
-
computeUnitLimit: 400_000,
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
// result.signature, result.slot, result.err, result.logs
|
|
384
|
-
// Errors are automatically parsed from logs into human-readable messages
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
### Client-Side Validation
|
|
388
|
-
|
|
389
|
-
Validate parameters before submitting transactions:
|
|
390
|
-
|
|
391
|
-
```typescript
|
|
392
|
-
import {
|
|
393
|
-
validatePublicKey,
|
|
394
|
-
validateAmount,
|
|
395
|
-
validateBps,
|
|
396
|
-
validateI128,
|
|
397
|
-
validateIndex,
|
|
398
|
-
} from "@percolator/sdk";
|
|
399
|
-
|
|
400
|
-
// Validates a public key string (throws ValidationError on invalid input)
|
|
401
|
-
const slabKey = validatePublicKey(slabAddress, "slab");
|
|
402
|
-
|
|
403
|
-
// Validates a u64 amount (throws ValidationError if negative or > u64 max)
|
|
404
|
-
const amount = validateAmount("1000000000", "depositAmount");
|
|
405
|
-
|
|
406
|
-
// Validates basis points (0-10000)
|
|
407
|
-
const feeBps = validateBps("50", "tradingFee");
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
---
|
|
411
|
-
|
|
412
|
-
## Mainnet vs Devnet
|
|
413
|
-
|
|
414
|
-
By default the SDK targets **devnet** (safety default — PERC-697). The `NETWORK` environment
|
|
415
|
-
variable and the `network` parameter to `getProgramId()` / `discoverMarkets()` control which
|
|
416
|
-
program IDs and RPC endpoints are used. Production deployments always set `NETWORK=mainnet`
|
|
417
|
-
explicitly via Railway env vars.
|
|
418
|
-
|
|
419
|
-
```typescript
|
|
420
|
-
import { getProgramId, discoverMarkets } from "@percolator/sdk";
|
|
421
|
-
|
|
422
|
-
// Devnet (default — safe fallback)
|
|
423
|
-
const programId = getProgramId("devnet");
|
|
424
|
-
|
|
425
|
-
// Mainnet — set env or pass explicitly
|
|
426
|
-
// NETWORK=mainnet
|
|
427
|
-
const mainnetProgramId = getProgramId("mainnet");
|
|
428
|
-
|
|
429
|
-
// Market discovery — uses network from NETWORK env by default
|
|
430
|
-
const markets = await discoverMarkets(connection);
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
### Program Addresses
|
|
434
|
-
|
|
435
|
-
| Program | Network | Address |
|
|
436
|
-
|---------|---------|---------|
|
|
437
|
-
| Percolator | Mainnet | `GM8zjJ8LTBMv9xEsverh6H6wLyevgMHEJXcEzyY3rY24` |
|
|
438
|
-
| Matcher | Mainnet | `DHP6DtwXP1yJsz8YzfoeigRFPB979gzmumkmCxDLSkUX` |
|
|
439
|
-
| Percolator | Devnet | `FxfD37s1AZTeWfFQps9Zpebi2dNQ9QSSDtfMKdbsfKrD` |
|
|
440
|
-
| Matcher | Devnet | `GTRgyTDfrMvBubALAqtHuQwT8tbGyXid7svXZKtWfC9k` |
|
|
441
|
-
|
|
442
|
-
> Use `PROGRAM_ID` / `MATCHER_PROGRAM_ID` env vars to override for local test validators.
|
|
443
|
-
|
|
444
|
-
---
|
|
445
|
-
|
|
446
|
-
## RPC Connection Pool, Retry, and Health Probes
|
|
447
|
-
|
|
448
|
-
The SDK provides a production-grade RPC connection pool with automatic retry,
|
|
449
|
-
failover, and health probing. Use `RpcPool` for mainnet reliability.
|
|
450
|
-
|
|
451
|
-
### RpcPool — Multi-endpoint with failover/round-robin
|
|
452
|
-
|
|
453
|
-
```typescript
|
|
454
|
-
import { RpcPool } from "@percolator/sdk";
|
|
455
|
-
|
|
456
|
-
const pool = new RpcPool({
|
|
457
|
-
endpoints: [
|
|
458
|
-
{ url: "https://mainnet.helius-rpc.com/?api-key=KEY", weight: 10, label: "helius" },
|
|
459
|
-
{ url: "https://api.mainnet-beta.solana.com", weight: 1, label: "public" },
|
|
460
|
-
],
|
|
461
|
-
strategy: "failover", // or "round-robin"
|
|
462
|
-
retry: {
|
|
463
|
-
maxRetries: 3, // default: 3
|
|
464
|
-
baseDelayMs: 500, // default: 500 (exponential backoff)
|
|
465
|
-
maxDelayMs: 10_000, // default: 10s cap
|
|
466
|
-
jitterFactor: 0.25, // default: 0.25 (avoid thundering herd)
|
|
467
|
-
},
|
|
468
|
-
requestTimeoutMs: 30_000, // default: 30s per request
|
|
469
|
-
commitment: "confirmed", // default: confirmed
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
// Execute any async call through the pool — auto-retry + failover
|
|
473
|
-
const slot = await pool.call(conn => conn.getSlot());
|
|
474
|
-
const balance = await pool.call(conn => conn.getBalance(pubkey));
|
|
475
|
-
|
|
476
|
-
// Use with discoverMarkets
|
|
477
|
-
const markets = await pool.call(conn =>
|
|
478
|
-
discoverMarkets(conn, programId, { apiBaseUrl: "https://percolatorlaunch.com/api" })
|
|
479
|
-
);
|
|
480
|
-
|
|
481
|
-
// Pool status & diagnostics
|
|
482
|
-
console.log(pool.status()); // [{ label, url, healthy, failures, lastLatencyMs }]
|
|
483
|
-
console.log(`${pool.healthyCount}/${pool.size} endpoints healthy`);
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
### Standalone retry wrapper
|
|
487
|
-
|
|
488
|
-
```typescript
|
|
489
|
-
import { withRetry } from "@percolator/sdk";
|
|
490
|
-
import { Connection } from "@solana/web3.js";
|
|
491
|
-
|
|
492
|
-
const conn = new Connection("https://api.mainnet-beta.solana.com");
|
|
493
|
-
const slot = await withRetry(
|
|
494
|
-
() => conn.getSlot(),
|
|
495
|
-
{ maxRetries: 3, baseDelayMs: 1000 },
|
|
496
|
-
);
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
### RPC health probes
|
|
500
|
-
|
|
501
|
-
```typescript
|
|
502
|
-
import { checkRpcHealth } from "@percolator/sdk";
|
|
503
|
-
|
|
504
|
-
const health = await checkRpcHealth("https://api.mainnet-beta.solana.com", 5000);
|
|
505
|
-
console.log(`${health.endpoint}: ${health.healthy ? "UP" : "DOWN"} — ${health.latencyMs}ms, slot ${health.slot}`);
|
|
506
|
-
|
|
507
|
-
// Or check all pool endpoints at once
|
|
508
|
-
const results = await pool.healthCheck();
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
---
|
|
512
|
-
|
|
513
|
-
## RPC Concurrency
|
|
514
|
-
|
|
515
|
-
`discoverMarkets()` fires one `getProgramAccounts` request per known slab tier size.
|
|
516
|
-
There are ~15 known tier sizes. To avoid hitting rate limits on public or free-tier endpoints,
|
|
517
|
-
the SDK caps parallel in-flight requests at **6** by default.
|
|
518
|
-
|
|
519
|
-
For production use a [Helius](https://helius.dev) paid-tier key. On the free tier,
|
|
520
|
-
pass `sequential: true` to serialize requests with exponential backoff:
|
|
521
|
-
|
|
522
|
-
```typescript
|
|
523
|
-
import { discoverMarkets } from "@percolator/sdk";
|
|
524
|
-
|
|
525
|
-
// Helius paid tier — parallel (default, fast)
|
|
526
|
-
const markets = await discoverMarkets(connection, { maxParallelTiers: 6 });
|
|
527
|
-
|
|
528
|
-
// Free-tier or public RPC — sequential with 429 backoff
|
|
529
|
-
const marketsSafe = await discoverMarkets(connection, { sequential: true });
|
|
530
|
-
|
|
531
|
-
// Custom concurrency
|
|
532
|
-
const marketsCustom = await discoverMarkets(connection, { maxParallelTiers: 3 });
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
> **Note:** Public mainnet-beta RPC (`api.mainnet-beta.solana.com`) rejects
|
|
536
|
-
> `getProgramAccounts` calls entirely. Use the **API fallback** (below) or a
|
|
537
|
-
> Helius/QuickNode endpoint.
|
|
538
|
-
|
|
539
|
-
## Market Discovery — 3-Tier Fallback Chain
|
|
540
|
-
|
|
541
|
-
Public mainnet RPCs reject `getProgramAccounts`, which blocks `discoverMarkets()`.
|
|
542
|
-
The SDK provides a resilient 3-tier fallback chain that works on any RPC endpoint:
|
|
543
|
-
|
|
544
|
-
| Tier | Method | Requires |
|
|
545
|
-
|------|--------|----------|
|
|
546
|
-
| 1 | `getProgramAccounts` (RPC) | Helius/premium RPC key |
|
|
547
|
-
| 2 | REST API (`GET /markets`) | Percolator API online |
|
|
548
|
-
| 3 | Static bundle (bundled addresses) | Nothing — works offline |
|
|
549
|
-
|
|
550
|
-
All tiers verify data on-chain via `getMultipleAccounts` (works on all RPCs).
|
|
551
|
-
|
|
552
|
-
### Recommended: Full 3-tier fallback
|
|
553
|
-
|
|
554
|
-
Pass both `apiBaseUrl` and `network` to enable all three tiers:
|
|
555
|
-
|
|
556
|
-
```typescript
|
|
557
|
-
import { discoverMarkets, getProgramId } from "@percolator/sdk";
|
|
558
|
-
import { Connection } from "@solana/web3.js";
|
|
559
|
-
|
|
560
|
-
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
561
|
-
const markets = await discoverMarkets(connection, getProgramId("mainnet"), {
|
|
562
|
-
apiBaseUrl: "https://percolatorlaunch.com/api",
|
|
563
|
-
network: "mainnet", // enables tier-3 static fallback
|
|
564
|
-
});
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
### API-only discovery via `discoverMarketsViaApi()`
|
|
568
|
-
|
|
569
|
-
Skip `getProgramAccounts` entirely — query the REST API for slab addresses,
|
|
570
|
-
then fetch full on-chain data:
|
|
571
|
-
|
|
572
|
-
```typescript
|
|
573
|
-
import { discoverMarketsViaApi, getProgramId } from "@percolator/sdk";
|
|
574
|
-
import { Connection } from "@solana/web3.js";
|
|
575
|
-
|
|
576
|
-
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
577
|
-
const programId = getProgramId("mainnet");
|
|
578
|
-
const markets = await discoverMarketsViaApi(
|
|
579
|
-
connection,
|
|
580
|
-
programId,
|
|
581
|
-
"https://percolatorlaunch.com/api",
|
|
582
|
-
);
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
### Static-only discovery via `discoverMarketsViaStaticBundle()`
|
|
586
|
-
|
|
587
|
-
Use the bundled address list directly (no network calls except `getMultipleAccounts`):
|
|
588
|
-
|
|
589
|
-
```typescript
|
|
590
|
-
import {
|
|
591
|
-
discoverMarketsViaStaticBundle,
|
|
592
|
-
getStaticMarkets,
|
|
593
|
-
getProgramId,
|
|
594
|
-
} from "@percolator/sdk";
|
|
595
|
-
import { Connection } from "@solana/web3.js";
|
|
596
|
-
|
|
597
|
-
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
598
|
-
const entries = getStaticMarkets("mainnet");
|
|
599
|
-
const markets = await discoverMarketsViaStaticBundle(
|
|
600
|
-
connection,
|
|
601
|
-
getProgramId("mainnet"),
|
|
602
|
-
entries,
|
|
603
|
-
);
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
### Extending the static registry at runtime
|
|
607
|
-
|
|
608
|
-
The static bundle can be augmented before calling `discoverMarkets()`:
|
|
609
|
-
|
|
610
|
-
```typescript
|
|
611
|
-
import { registerStaticMarkets, discoverMarkets, getProgramId } from "@percolator/sdk";
|
|
612
|
-
import { Connection } from "@solana/web3.js";
|
|
613
|
-
|
|
614
|
-
// Register known slab addresses before discovery
|
|
615
|
-
registerStaticMarkets("mainnet", [
|
|
616
|
-
{ slabAddress: "ABC123...", symbol: "SOL-PERP" },
|
|
617
|
-
{ slabAddress: "DEF456...", symbol: "ETH-PERP" },
|
|
618
|
-
]);
|
|
619
|
-
|
|
620
|
-
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
621
|
-
const markets = await discoverMarkets(connection, getProgramId("mainnet"), {
|
|
622
|
-
apiBaseUrl: "https://percolatorlaunch.com/api",
|
|
623
|
-
network: "mainnet",
|
|
624
|
-
});
|
|
625
|
-
```
|
|
626
|
-
|
|
627
|
-
### Known addresses via `getMarketsByAddress()`
|
|
628
|
-
|
|
629
|
-
If you already know your market slab addresses (e.g. from an indexer or
|
|
630
|
-
hardcoded list), fetch them directly:
|
|
631
|
-
|
|
632
|
-
```typescript
|
|
633
|
-
import { getMarketsByAddress, getProgramId } from "@percolator/sdk";
|
|
634
|
-
import { Connection, PublicKey } from "@solana/web3.js";
|
|
635
|
-
|
|
636
|
-
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
637
|
-
const markets = await getMarketsByAddress(
|
|
638
|
-
connection,
|
|
639
|
-
getProgramId("mainnet"),
|
|
640
|
-
[new PublicKey("..."), new PublicKey("...")],
|
|
641
|
-
);
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
---
|
|
645
|
-
|
|
646
|
-
## Architecture
|
|
647
|
-
|
|
648
|
-
```
|
|
649
|
-
@percolator/sdk
|
|
650
|
-
├── abi/ # Binary encoding/decoding matching on-chain layout
|
|
651
|
-
│ ├── instructions.ts # Instruction data builders (all 72 instructions)
|
|
652
|
-
│ ├── accounts.ts # Account struct deserialization
|
|
653
|
-
│ ├── encode.ts # Low-level binary encoding (u8/u16/u32/u64/i128/pubkey)
|
|
654
|
-
│ ├── errors.ts # On-chain error code → human-readable parsing
|
|
655
|
-
│ └── index.ts
|
|
656
|
-
├── solana/ # Solana-specific helpers
|
|
657
|
-
│ ├── slab.ts # Slab account parser (header + config + accounts)
|
|
658
|
-
│ ├── pda.ts # PDA derivation (vault, LP, insurance mint)
|
|
659
|
-
│ ├── discovery.ts # Market discovery (find all Percolator markets)
|
|
660
|
-
│ ├── rpc-pool.ts # RPC connection pool, retry, failover, health probes
|
|
661
|
-
│ ├── dex-oracle.ts # DEX oracle price integration
|
|
662
|
-
│ ├── token-program.ts # SPL Token helpers
|
|
663
|
-
│ ├── ata.ts # Associated Token Account helpers
|
|
664
|
-
│ └── index.ts
|
|
665
|
-
├── runtime/ # Transaction building and submission
|
|
666
|
-
│ ├── tx.ts # buildIx, simulateOrSend, error handling
|
|
667
|
-
│ └── index.ts
|
|
668
|
-
├── math/ # Trading math (all BigInt)
|
|
669
|
-
│ ├── trading.ts # PnL, liquidation price, leverage, entry price
|
|
670
|
-
│ └── index.ts
|
|
671
|
-
├── oracle/ # Price feed integration
|
|
672
|
-
│ └── price-router.ts # Multi-source oracle resolution (Pyth, DEX, Jupiter)
|
|
673
|
-
├── config/ # Configuration
|
|
674
|
-
│ └── program-ids.ts # Network-aware program IDs
|
|
675
|
-
├── validation.ts # Client-side parameter validation
|
|
676
|
-
└── index.ts # Public API re-exports
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
---
|
|
680
|
-
|
|
681
|
-
## Development
|
|
682
|
-
|
|
683
|
-
### Prerequisites
|
|
684
|
-
|
|
685
|
-
- Node.js 20+ and pnpm 9+
|
|
686
|
-
|
|
687
|
-
### Commands
|
|
688
|
-
|
|
689
|
-
```bash
|
|
690
|
-
pnpm install # Install dependencies
|
|
691
|
-
pnpm build # Build with tsup (outputs to dist/)
|
|
692
|
-
pnpm test # Run test suite (vitest)
|
|
693
|
-
pnpm lint # Type-check (tsc --noEmit)
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
### Testing
|
|
697
|
-
|
|
698
|
-
Tests cover ABI encoding roundtrips, PDA derivation, slab parsing, validation, and trading math:
|
|
699
|
-
|
|
700
|
-
```bash
|
|
701
|
-
pnpm test # Run all tests
|
|
702
|
-
pnpm test -- --watch # Watch mode
|
|
703
|
-
```
|
|
704
|
-
|
|
705
|
-
### Publishing
|
|
706
|
-
|
|
707
|
-
```bash
|
|
708
|
-
pnpm build
|
|
709
|
-
npm publish --access public
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
---
|
|
713
|
-
|
|
714
|
-
## Configuration
|
|
715
|
-
|
|
716
|
-
| Environment Variable | Description | Default |
|
|
717
|
-
|---------------------|-------------|---------|
|
|
718
|
-
| `PROGRAM_ID` | Override Percolator program ID | Network default |
|
|
719
|
-
| `MATCHER_PROGRAM_ID` | Override Matcher program ID | Network default |
|
|
720
|
-
| `NETWORK` | Target network (`devnet` / `mainnet`) | `devnet` |
|
|
721
|
-
|
|
722
|
-
### Devnet Program Addresses
|
|
723
|
-
|
|
724
|
-
| Program | Address |
|
|
725
|
-
|---------|---------|
|
|
726
|
-
| Percolator | `FxfD37s1AZTeWfFQps9Zpebi2dNQ9QSSDtfMKdbsfKrD` |
|
|
727
|
-
| Matcher | `4HcGCsyjAqnFua5ccuXyt8KRRQzKFbGTJkVChpS7Yfzy` |
|
|
728
|
-
|
|
729
|
-
---
|
|
730
|
-
|
|
731
|
-
## Browser Compatibility
|
|
732
|
-
|
|
733
|
-
The SDK uses `DataView` for all binary reads (no Node.js `Buffer` dependency). Works in:
|
|
734
|
-
- Node.js 20+
|
|
735
|
-
- Modern browsers (Chrome, Firefox, Safari, Edge)
|
|
736
|
-
- React Native (via `@solana/web3.js`)
|
|
737
|
-
|
|
738
|
-
---
|
|
739
|
-
|
|
740
|
-
## Related Repositories
|
|
741
|
-
|
|
742
|
-
| Repository | Description |
|
|
743
|
-
|-----------|-------------|
|
|
744
|
-
| [percolator](https://github.com/dcccrypto/percolator) | Core risk engine crate (Rust) |
|
|
745
|
-
| [percolator-prog](https://github.com/dcccrypto/percolator-prog) | Solana on-chain program (wrapper) |
|
|
746
|
-
| [percolator-matcher](https://github.com/dcccrypto/percolator-matcher) | Reference matcher program for LP pricing |
|
|
747
|
-
| [percolator-stake](https://github.com/dcccrypto/percolator-stake) | Insurance LP staking program |
|
|
748
|
-
| [percolator-ops](https://github.com/dcccrypto/percolator-ops) | Operations dashboard |
|
|
749
|
-
| [percolator-mobile](https://github.com/dcccrypto/percolator-mobile) | Solana Seeker mobile trading app |
|
|
750
|
-
| [percolator-launch](https://github.com/dcccrypto/percolator-launch) | Full-stack launch platform (monorepo) |
|
|
751
|
-
|
|
752
|
-
## License
|
|
753
|
-
|
|
754
|
-
Apache 2.0 — see [LICENSE](LICENSE).
|