@solard/sdk 0.2.29 → 0.2.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,11 +3,26 @@
3
3
  Public Solard SDK. It intentionally contains no duplicate business logic; it re-exports the supported `@solard/core` API.
4
4
 
5
5
  ```ts
6
- import { Solard, sol } from "@solard/sdk";
6
+ import { createSolard } from "@solard/sdk";
7
7
 
8
- const slrd = new Solard();
9
- const wallet = slrd.createWallet("trader-1");
10
- const wallets = slrd.listWallets();
11
-
12
- await slrd.tx(wallet.address).transferSol("RECIPIENT", sol("0.01")).send();
8
+ const slrd = createSolard();
9
+ await slrd.addToken(mint, undefined, { venueHint: "pumpswap", pool: poolAddress });
10
+ const buy = await slrd.buy(
11
+ { wallet: "trader-1", token: mint, amount: { sol: 0.001 }, slippageBps: 500 },
12
+ { intentKey: "example:buy:1", priorityFee: { cuLimit: 600_000, microLamports: 200_000 } },
13
+ );
14
+ if (buy.status === "confirmed") {
15
+ console.log(buy.solPrincipalDeltaLamports, buy.networkFeeLamports, buy.targetTokenDeltaRaw);
16
+ }
17
+ // Sell through the same atomic quote-token -> SOL route.
18
+ // await slrd.sell({ wallet: "trader-1", token: mint, amount: "all", slippageBps: 500 });
13
19
  ```
20
+
21
+ The trader preset supports Pump/PumpSwap, Meteora DBC and DAMM v2. Custom quote
22
+ routes require a supported SOL market for the quote token and suitable lookup
23
+ tables when needed. Composer routes fall back to Jupiter instructions. Unavailable
24
+ converted prices are null. `unresolved` means reconcile with `resumeTrade(intentKey)`
25
+ before another trade. Buy accepts `minOutputRaw`; sell accepts `minOutputLamports`.
26
+ Both use confirmed/failed/unresolved results with phase, code, attempts and accounting.
27
+ `quote`, `curveLiquidity`, `maxSendableSol`, `transferToken` and `exitWallet` share the
28
+ core implementation. Raydium AMM/CPMM execution does not supply live pool prices.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solard/sdk",
3
- "version": "0.2.29",
3
+ "version": "0.2.31",
4
4
  "description": "Public Solard SDK for wallets, trading, history, live market subscriptions and token metadata.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -27,7 +27,7 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@solana/web3.js": "^1.98.4",
30
- "@solard/core": "0.2.29"
30
+ "@solard/core": "0.2.31"
31
31
  },
32
32
  "scripts": {
33
33
  "typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.browser.json",
@@ -1,60 +1,60 @@
1
- import { expect, spyOn, test } from "bun:test";
2
- import { mkdtempSync, rmSync } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import { Keypair } from "@solana/web3.js";
6
- import { Solard } from "../../core/src/core/solard.ts";
7
- import { TransactionComposer } from "../../core/src/tx/composer.ts";
8
- import type { PlannedTransaction, TransactionDraft } from "../../core/src/tx/types.ts";
9
- import { createSolard } from "./client.ts";
10
-
11
- test("SDK buy rebuild and sell preserve custom fees in immediate submitted results", async () => {
12
- const directory = mkdtempSync(join(tmpdir(), "solard-sdk-fees-"));
13
- const drafts: Array<TransactionDraft> = [];
14
- // A sentinel plan is never compiled, signed, or broadcast in this test.
15
- const payer = Keypair.generate();
16
- const plan = { payer: payer.publicKey } as PlannedTransaction;
17
- const build = spyOn(TransactionComposer.prototype, "build").mockImplementation(async function (this: TransactionComposer) {
18
- drafts.push(this.snapshot());
19
- return { ...plan, draft: this.snapshot() };
20
- });
21
- const signer = spyOn(Solard.prototype, "signer").mockImplementation(() => payer);
22
- const compile = spyOn(Solard.prototype, "compile").mockImplementation(async (_payer, draft) => ({ ...plan, draft }));
23
- let submissions = 0;
24
- const submit = spyOn(Solard.prototype, "submitPlan").mockImplementation(async () => {
25
- submissions += 1;
26
- if (submissions === 1) {
27
- throw new Error("Simulation failed: BuySlippageBelowMinBaseAmountOut");
28
- }
29
- return { signature: "test-signature", sender: "rpc", executionId: 42, plan };
30
- });
31
- const settle = spyOn(Solard.prototype, "settleSubmission").mockImplementation(async () => {
32
- throw new Error("Immediate submission must not wait for confirmation");
33
- });
34
- const sdk = createSolard({ dbPath: join(directory, "test.sqlite") });
35
- try {
36
- const options = {
37
- priorityFee: { cuLimit: 300_000, microLamports: 0 },
38
- waitForConfirmation: false,
39
- };
40
- const buy = await sdk.buy({ token: "token", wallet: "wallet", amount: 0.1 }, options);
41
- const sell = await sdk.sell({ token: "token", wallet: "wallet" }, options);
42
- expect(drafts).toHaveLength(3);
43
- for (const draft of drafts) {
44
- expect(draft.cuLimit).toBe(300_000);
45
- expect(draft.cuPriceMicroLamports).toBe(0);
46
- }
47
- expect(buy.status).toBe("submitted");
48
- expect(sell.status).toBe("submitted");
49
- expect(buy.executionId).toBe(42);
50
- expect(settle).not.toHaveBeenCalled();
51
- } finally {
52
- sdk.close();
53
- build.mockRestore();
54
- signer.mockRestore();
55
- compile.mockRestore();
56
- submit.mockRestore();
57
- settle.mockRestore();
58
- rmSync(directory, { recursive: true, force: true });
59
- }
60
- });
1
+ import { expect, spyOn, test } from "bun:test";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { Keypair } from "@solana/web3.js";
6
+ import { Solard } from "../../core/src/core/solard.ts";
7
+ import { TransactionComposer } from "../../core/src/tx/composer.ts";
8
+ import type { PlannedTransaction, TransactionDraft } from "../../core/src/tx/types.ts";
9
+ import { createSolard } from "./client.ts";
10
+
11
+ test("SDK buy rebuild and sell preserve custom fees in immediate submitted results", async () => {
12
+ const directory = mkdtempSync(join(tmpdir(), "solard-sdk-fees-"));
13
+ const drafts: Array<TransactionDraft> = [];
14
+ // A sentinel plan is never compiled, signed, or broadcast in this test.
15
+ const payer = Keypair.generate();
16
+ const plan = { payer: payer.publicKey } as PlannedTransaction;
17
+ const build = spyOn(TransactionComposer.prototype, "build").mockImplementation(async function (this: TransactionComposer) {
18
+ drafts.push(this.snapshot());
19
+ return { ...plan, draft: this.snapshot() };
20
+ });
21
+ const signer = spyOn(Solard.prototype, "signer").mockImplementation(() => payer);
22
+ const compile = spyOn(Solard.prototype, "compile").mockImplementation(async (_payer, draft) => ({ ...plan, draft }));
23
+ let submissions = 0;
24
+ const submit = spyOn(Solard.prototype, "submitPlan").mockImplementation(async () => {
25
+ submissions += 1;
26
+ if (submissions === 1) {
27
+ throw new Error("Simulation failed: BuySlippageBelowMinBaseAmountOut");
28
+ }
29
+ return { signature: "test-signature", sender: "rpc", executionId: 42, plan };
30
+ });
31
+ const settle = spyOn(Solard.prototype, "settleSubmission").mockImplementation(async () => {
32
+ throw new Error("Immediate submission must not wait for confirmation");
33
+ });
34
+ const sdk = createSolard({ dbPath: join(directory, "test.sqlite") });
35
+ try {
36
+ const options = {
37
+ priorityFee: { cuLimit: 300_000, microLamports: 0 },
38
+ waitForConfirmation: false,
39
+ };
40
+ const buy = await sdk.buy({ token: "token", wallet: "wallet", amount: 0.1 }, options);
41
+ const sell = await sdk.sell({ token: "token", wallet: "wallet" }, options);
42
+ expect(drafts).toHaveLength(3);
43
+ for (const draft of drafts) {
44
+ expect(draft.cuLimit).toBe(300_000);
45
+ expect(draft.cuPriceMicroLamports).toBe(0);
46
+ }
47
+ expect(buy.status).toBe("unresolved");
48
+ expect(sell.status).toBe("unresolved");
49
+ expect(buy.executionId).toBe(42);
50
+ expect(settle).not.toHaveBeenCalled();
51
+ } finally {
52
+ sdk.close();
53
+ build.mockRestore();
54
+ signer.mockRestore();
55
+ compile.mockRestore();
56
+ submit.mockRestore();
57
+ settle.mockRestore();
58
+ rmSync(directory, { recursive: true, force: true });
59
+ }
60
+ });
package/src/client.ts CHANGED
@@ -17,8 +17,11 @@ import {
17
17
  type ReplayOptions,
18
18
  type ReplayEventsOptions,
19
19
  type RecordConfirmedTradeInput,
20
- type SendReceipt,
21
- type TradeLandingPolicy,
20
+ type SendReceipt,
21
+ type TradeLandingPolicy,
22
+ type TradeResult,
23
+ type TradeExecutionOptions,
24
+ type LivePoolReserves,
22
25
  type SenderId,
23
26
  type SolardDecodedTransaction,
24
27
  type SolardTransactionOptions,
@@ -35,7 +38,8 @@ import {
35
38
  } from "@solard/core";
36
39
 
37
40
  export type SolardOptions = {
38
- rpcUrl?: string;
41
+ rpcUrl?: string;
42
+ rpcUrls?: readonly string[];
39
43
  dbPath?: string;
40
44
  cacheTtlMs?: number;
41
45
  };
@@ -59,48 +63,50 @@ export type SolardEventsApi = {
59
63
  merge(streams: readonly ReplayEventSubscription[]): MergedReplayEventStream;
60
64
  };
61
65
 
62
- export type SolardBuyInput = {
66
+ export type SolardBuyInput = {
67
+ reserves?: LivePoolReserves;
68
+ maxPriceSol?: string | number;
63
69
  wallet: WalletRef;
64
70
  token: TokenRef;
65
71
  amount: HumanAmount | string | number;
66
72
  slippageBps?: number;
73
+ minOutputRaw?: bigint | string;
67
74
  };
68
75
 
69
- export type SolardSellInput = {
76
+ export type SolardSellInput = {
77
+ closeTokenAccount?: boolean;
78
+ reserves?: LivePoolReserves;
79
+ minPriceSol?: string | number;
70
80
  wallet: WalletRef;
71
81
  token: TokenRef;
72
82
  amount?: "all" | { bps: number };
73
83
  slippageBps?: number;
84
+ minOutputLamports?: bigint | string;
74
85
  };
75
86
 
76
- export type SolardTradeExecutionOptions = {
77
- landing?: TradeLandingPolicy;
78
- waitForConfirmation?: boolean;
79
- priorityFee?: { cuLimit?: number; microLamports?: number };
80
- via?: SenderId;
81
- skipSimulation?: boolean;
82
- skipPreflight?: boolean;
83
- };
84
-
85
- export type SolardTradeExecutionResult = {
86
- attempts?: Array<{ executionId: number; signature: string; priorityMicroLamports: number }>;
87
- executionId: number;
88
- signature: string;
89
- sender: string;
90
- status: SendReceipt["status"];
91
- slot: number | null;
92
- error: string | null;
93
- feeLamports?: number;
94
- computeUnitsConsumed?: number;
95
- /** Signed economic SOL delta excluding network fee and token-account rent. */
96
- solPrincipalDeltaLamports?: bigint;
97
- /** Signed target-token delta for the confirmed atomic route. */
98
- targetTokenDeltaRaw?: bigint;
99
- /** Confirmed transaction network fee. */
100
- networkFeeLamports?: bigint;
101
- };
87
+ export type SolardTradeExecutionOptions = TradeExecutionOptions;
102
88
 
103
- export type Solard = {
89
+ export type SolardTradeExecutionResult = TradeResult;
90
+ export type Solard = {
91
+ resolveCurrentMarket: ReturnType<typeof createCoreSolard>["resolveCurrentMarket"];
92
+ getClaimableCreatorFees: ReturnType<typeof createCoreSolard>["getClaimableCreatorFees"];
93
+ claimAllCreatorFees: ReturnType<typeof createCoreSolard>["claimAllCreatorFees"];
94
+ walletLedger: ReturnType<typeof createCoreSolard>["walletLedger"];
95
+ historicalTape: ReturnType<typeof createCoreSolard>["historicalTape"];
96
+ getSupportedPumpPairs: ReturnType<typeof createCoreSolard>["getSupportedPumpPairs"];
97
+ listVanityMints: ReturnType<typeof createCoreSolard>["listVanityMints"];
98
+ releaseVanityMint: ReturnType<typeof createCoreSolard>["releaseVanityMint"];
99
+ closeEmptyTokenAccounts: ReturnType<typeof createCoreSolard>["closeEmptyTokenAccounts"];
100
+ prepareTokenDeployment: ReturnType<typeof createCoreSolard>["prepareTokenDeployment"];
101
+ deployToken: ReturnType<typeof createCoreSolard>["deployToken"];
102
+ warmBlockhash: ReturnType<typeof createCoreSolard>["warmBlockhash"];
103
+ curveLiquidity: ReturnType<typeof createCoreSolard>["curveLiquidity"];
104
+ maxSendableSol: ReturnType<typeof createCoreSolard>["maxSendableSol"];
105
+ transferToken: ReturnType<typeof createCoreSolard>["transferToken"];
106
+ exitWallet: ReturnType<typeof createCoreSolard>["exitWallet"];
107
+ resumeTrade(intentKey: string): Promise<SolardTradeExecutionResult>;
108
+ reconcile(intentKey: string): Promise<SolardTradeExecutionResult>;
109
+ quote: ReturnType<typeof createCoreSolard>["quote"];
104
110
  close(): void;
105
111
  history: SolardHistoryApi;
106
112
  events: SolardEventsApi;
@@ -183,49 +189,13 @@ async function executeTrade(
183
189
  input: SolardBuyInput | SolardSellInput,
184
190
  options: SolardTradeExecutionOptions = {},
185
191
  ): Promise<SolardTradeExecutionResult> {
186
- const { submission, receipt, attempts } = await core.executeTradePlan(input.wallet, async () => {
187
- const composer = core.tx(input.wallet).priorityFee(options.priorityFee ?? {});
188
- if (side === "buy") {
189
- const buy = input as SolardBuyInput;
190
- composer.buy(buy.token, tradeAmount(buy.amount), { slippageBps: buy.slippageBps });
191
- } else {
192
- const sell = input as SolardSellInput;
193
- composer.sell(sell.token, { bps: sellBps(sell.amount), slippageBps: sell.slippageBps });
194
- }
195
- return await composer.build();
196
- }, options.via ?? "rpc", side, options);
197
- const confirmedTrade =
198
- receipt.status === "confirmed"
199
- ? (
200
- await core.trades({
201
- wallet: input.wallet,
202
- token: input.token,
203
- side,
204
- status: "confirmed",
205
- limit: 32,
206
- })
207
- ).find((trade) => trade.signature === receipt.signature) ?? null
208
- : null;
209
- return {
210
- executionId: submission.executionId,
211
- signature: receipt.signature,
212
- sender: receipt.sender,
213
- status: receipt.status,
214
- slot: receipt.slot,
215
- error: receipt.error ?? null,
216
- feeLamports: receipt.feeLamports,
217
- computeUnitsConsumed: receipt.computeUnitsConsumed,
218
- attempts,
219
- ...(confirmedTrade?.solDeltaLamports != null
220
- ? { solPrincipalDeltaLamports: confirmedTrade.solDeltaLamports }
221
- : {}),
222
- ...(confirmedTrade?.tokenDeltaRaw != null
223
- ? { targetTokenDeltaRaw: confirmedTrade.tokenDeltaRaw }
224
- : {}),
225
- ...(confirmedTrade?.networkFeeLamports != null
226
- ? { networkFeeLamports: confirmedTrade.networkFeeLamports }
227
- : {}),
228
- };
192
+ if (side === "buy") {
193
+ const buy = input as SolardBuyInput;
194
+ return await core.buy(buy.token, buy.wallet, tradeAmount(buy.amount), { ...options, slippageBps: buy.slippageBps, minOutputRaw: buy.minOutputRaw, maxPriceSol: buy.maxPriceSol, reserves: buy.reserves });
195
+ }
196
+ const sell = input as SolardSellInput;
197
+ return await core.sell(sell.token, sell.wallet, { ...options, bps: sellBps(sell.amount), slippageBps: sell.slippageBps, minOutputLamports: sell.minOutputLamports, minPriceSol: sell.minPriceSol, reserves: sell.reserves, closeTokenAccount: sell.closeTokenAccount });
198
+
229
199
  }
230
200
 
231
201
  export function createSolard(options: SolardOptions = {}): Solard {
@@ -240,7 +210,26 @@ export function createSolard(options: SolardOptions = {}): Solard {
240
210
  core.events(token, eventOptions),
241
211
  { merge: core.events.merge },
242
212
  ) as SolardEventsApi;
243
- return Object.freeze({
213
+ return Object.freeze({
214
+ resolveCurrentMarket: core.resolveCurrentMarket.bind(core),
215
+ getClaimableCreatorFees: core.getClaimableCreatorFees.bind(core),
216
+ claimAllCreatorFees: core.claimAllCreatorFees.bind(core),
217
+ walletLedger: core.walletLedger.bind(core),
218
+ historicalTape: core.historicalTape.bind(core),
219
+ getSupportedPumpPairs: core.getSupportedPumpPairs.bind(core),
220
+ listVanityMints: core.listVanityMints.bind(core),
221
+ releaseVanityMint: core.releaseVanityMint.bind(core),
222
+ closeEmptyTokenAccounts: core.closeEmptyTokenAccounts.bind(core),
223
+ prepareTokenDeployment: core.prepareTokenDeployment.bind(core),
224
+ deployToken: core.deployToken.bind(core),
225
+ warmBlockhash: core.warmBlockhash.bind(core),
226
+ curveLiquidity: core.curveLiquidity.bind(core),
227
+ maxSendableSol: core.maxSendableSol.bind(core),
228
+ transferToken: core.transferToken.bind(core),
229
+ exitWallet: core.exitWallet.bind(core),
230
+ resumeTrade: core.resumeTrade.bind(core),
231
+ reconcile: core.reconcile.bind(core),
232
+ quote: core.quote.bind(core),
244
233
  close: core.close.bind(core),
245
234
  history,
246
235
  events: Object.freeze(events),
package/src/default.ts CHANGED
@@ -1,51 +1,51 @@
1
- import { getSolUsdPrice } from "@solard/core";
2
- import {
3
- listenTrades,
4
- subscribeLaunches,
5
- subscribeMigrations,
6
- } from "./live.ts";
7
- import { createSolard, type Solard } from "./client.ts";
8
-
9
- type DefaultSolard = Solard & {
10
- getSolUsdPrice: typeof getSolUsdPrice;
11
- listenTrades: typeof listenTrades;
12
- subscribeLaunches: typeof subscribeLaunches;
13
- subscribeMigrations: typeof subscribeMigrations;
14
- };
15
-
16
- let instance: Solard | undefined;
17
-
18
- function client(): Solard {
19
- instance ??= createSolard();
20
- return instance;
21
- }
22
-
23
- const operations = {
24
- getSolUsdPrice,
25
- listenTrades,
26
- subscribeLaunches,
27
- subscribeMigrations,
28
- } as const;
29
-
30
- const slrd = new Proxy({} as DefaultSolard, {
31
- get(_target, key) {
32
- if (key === "close") {
33
- return () => {
34
- const current = instance;
35
- instance = undefined;
36
- current?.close();
37
- };
38
- }
39
- if (typeof key === "string" && key in operations) {
40
- return operations[key as keyof typeof operations];
41
- }
42
- const current = client();
43
- const value = Reflect.get(current as object, key, current);
44
- return typeof value === "function" ? value.bind(current) : value;
45
- },
46
- set() {
47
- return false;
48
- },
49
- });
50
-
51
- export default slrd;
1
+ import { getSolUsdPrice } from "@solard/core";
2
+ import {
3
+ listenTrades,
4
+ subscribeLaunches,
5
+ subscribeMigrations,
6
+ } from "./live.ts";
7
+ import { createSolard, type Solard } from "./client.ts";
8
+
9
+ type DefaultSolard = Solard & {
10
+ getSolUsdPrice: typeof getSolUsdPrice;
11
+ listenTrades: typeof listenTrades;
12
+ subscribeLaunches: typeof subscribeLaunches;
13
+ subscribeMigrations: typeof subscribeMigrations;
14
+ };
15
+
16
+ let instance: Solard | undefined;
17
+
18
+ function client(): Solard {
19
+ instance ??= createSolard();
20
+ return instance;
21
+ }
22
+
23
+ const operations = {
24
+ getSolUsdPrice,
25
+ listenTrades,
26
+ subscribeLaunches,
27
+ subscribeMigrations,
28
+ } as const;
29
+
30
+ const slrd = new Proxy({} as DefaultSolard, {
31
+ get(_target, key) {
32
+ if (key === "close") {
33
+ return () => {
34
+ const current = instance;
35
+ instance = undefined;
36
+ current?.close();
37
+ };
38
+ }
39
+ if (typeof key === "string" && key in operations) {
40
+ return operations[key as keyof typeof operations];
41
+ }
42
+ const current = client();
43
+ const value = Reflect.get(current as object, key, current);
44
+ return typeof value === "function" ? value.bind(current) : value;
45
+ },
46
+ set() {
47
+ return false;
48
+ },
49
+ });
50
+
51
+ export default slrd;