@solard/sdk 0.2.29 → 0.2.33

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,59 @@
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.
29
+
30
+ In 0.2.32, sell an exact positive integer number of raw token units with the
31
+ object API. For a six-decimal mint, `"1000000"` is one token:
32
+
33
+ ```ts
34
+ const sold = await slrd.sell(
35
+ { wallet: "trader-1", token: mint, amountRaw: "1000000", slippageBps: 500 },
36
+ { intentKey: "example:sell:1", priorityFee: { cuLimit: 200_000, microLamports: 20_000 } },
37
+ );
38
+ ```
39
+
40
+ `amountRaw` also accepts bigint and is mutually exclusive with `amount`.
41
+ An economic sell guard rejects output at or below the selected network fee plus
42
+ tip before broadcasting, with `UNECONOMIC_SELL`. Its `sellEconomics` fields describe
43
+ the expected output and selected costs; they are not charges already paid.
44
+ `networkFeeLamports` is the actual on-chain charge only when known. Selected
45
+ priority cost reserves the chosen CU limit, so it differs from consumed CUs.
46
+
47
+ The configured client inherits its database path and RPC endpoints for listeners.
48
+ Live history is recorded by default; disable recording with `history: false`:
49
+
50
+ ```ts
51
+ const slrd = createSolard({ dbPath: "./slrd.db", rpcUrl });
52
+ const listener = await slrd.listenTrades({ tokens: [mint] });
53
+ listener.onTrade((event) => console.log(event));
54
+ // await slrd.listenTrades({ tokens: [mint], history: false });
55
+ await listener.close(); // Wait for queued history writes to flush.
56
+ ```
57
+
58
+ The collected live tail is partial history, not a complete backfill. Processed
59
+ events can later disappear; `slrd.historicalTape(mint)` excludes them by default.
60
+ Pass `{ includeProcessed: true }` to inspect that provisional tail and check the
61
+ returned completeness information before using it for backtests.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solard/sdk",
3
- "version": "0.2.29",
3
+ "version": "0.2.33",
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.33"
31
31
  },
32
32
  "scripts": {
33
33
  "typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.browser.json",
@@ -0,0 +1,45 @@
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 { Solard } from "../../core/src/core/solard.ts";
6
+ import { tradeResult } from "../../core/src/tx/trade-result.ts";
7
+ import { createSolard, type SolardSellInput } from "./client.ts";
8
+ import { isDefinitivePreSubmissionError } from "@solard/core";
9
+
10
+ test("SDK exact raw sell forwards an isolated position without inserting default bps", async () => {
11
+ const folder = mkdtempSync(join(tmpdir(), "solard-sdk-exact-"));
12
+ const calls: Array<Record<string, unknown>> = [];
13
+ const sell = spyOn(Solard.prototype, "sell").mockImplementation(async (_token, _wallet, options) => {
14
+ calls.push(options as unknown as Record<string, unknown>);
15
+ return tradeResult({ signature: "fixture", sender: "rpc", status: "confirmed", slot: 1 });
16
+ });
17
+ const sdk = createSolard({ dbPath: join(folder, "test.sqlite") });
18
+ try {
19
+ await sdk.sell({ token: "mint", wallet: "wallet", amountRaw: "284725", slippageBps: 500, minOutputLamports: 1n }, { priorityFee: { microLamports: 20_000 } });
20
+ await sdk.sell({ token: "mint", wallet: "wallet", amount: { raw: 284725n } });
21
+ await sdk.sell({ token: "mint", wallet: "wallet", amount: { bps: 1000 } });
22
+ await sdk.sell({ token: "mint", wallet: "wallet", amount: "all" });
23
+ expect(calls[0]).toMatchObject({ amountRaw: "284725", slippageBps: 500, minOutputLamports: 1n, priorityFee: { microLamports: 20_000 } });
24
+ expect(calls[0]?.bps).toBeUndefined();
25
+ expect(calls[1]?.amountRaw).toBe(284725n);
26
+ expect(calls[1]?.bps).toBeUndefined();
27
+ expect(calls[2]?.bps).toBe(1000);
28
+ expect(calls[3]?.bps).toBe(10000);
29
+ } finally { sdk.close(); sell.mockRestore(); rmSync(folder, { recursive: true, force: true }); }
30
+ });
31
+
32
+ test("SDK rejects ambiguous and invalid raw quantities before invoking core sell", async () => {
33
+ const folder = mkdtempSync(join(tmpdir(), "solard-sdk-exact-invalid-"));
34
+ const sell = spyOn(Solard.prototype, "sell").mockImplementation(async () => { throw new Error("must not call core"); });
35
+ const sdk = createSolard({ dbPath: join(folder, "test.sqlite") });
36
+ try {
37
+ for (const size of [{ amountRaw: "284725", amount: { bps: 1000 } }, { amountRaw: 0n }, { amountRaw: "1.5" },
38
+ { amount: { raw: "-1" } }, { amount: { bps: 1.5 } }, { amount: { raw: "284725", bps: 1000 } }]) {
39
+ await expect(sdk.sell({ token: "mint", wallet: "wallet", ...size } as SolardSellInput)).rejects.toThrow();
40
+ }
41
+ expect(sell).not.toHaveBeenCalled();
42
+ try { await sdk.sell({ token: "mint", wallet: "wallet", amountRaw: 0n }); }
43
+ catch (error) { expect(isDefinitivePreSubmissionError(error)).toBe(true); }
44
+ } finally { sdk.close(); sell.mockRestore(); rmSync(folder, { recursive: true, force: true }); }
45
+ });
@@ -0,0 +1,34 @@
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 * as live from "./live.ts";
6
+ import { createSolard } from "./client.ts";
7
+
8
+ test("client listener inherits its configured database and endpoint pool", async () => {
9
+ const folder = mkdtempSync(join(tmpdir(), "solard-sdk-listener-"));
10
+ const dbPath = join(folder, "test.sqlite");
11
+ const seen: Array<live.ListenTradesOptions> = [];
12
+ const fixture = {} as live.TradeListener;
13
+ const listen = spyOn(live, "listenTrades").mockImplementation(async (options) => { seen.push(options); return fixture; });
14
+ const sdk = createSolard({ dbPath, rpcUrls: ["https://first.example.com", "https://second.example.com"] });
15
+ try {
16
+ expect(await sdk.listenTrades({ tokens: ["mint"] })).toBe(fixture);
17
+ expect(seen[0]).toMatchObject({ dbPath, rpcUrls: ["https://first.example.com", "https://second.example.com"], tokens: ["mint"] });
18
+ await sdk.listenTrades();
19
+ expect(seen[1]?.tokens).toEqual([]);
20
+ } finally { sdk.close(); listen.mockRestore(); rmSync(folder, { recursive: true, force: true }); }
21
+ });
22
+
23
+ test("single RPC configuration is forwarded and absent URLs preserve live environment fallback", async () => {
24
+ const folder = mkdtempSync(join(tmpdir(), "solard-sdk-listener-url-"));
25
+ const seen: Array<live.ListenTradesOptions> = [];
26
+ const listen = spyOn(live, "listenTrades").mockImplementation(async (options) => { seen.push(options); return {} as live.TradeListener; });
27
+ const first = createSolard({ dbPath: join(folder, "first.sqlite"), rpcUrl: "https://configured.example.com" });
28
+ const second = createSolard({ dbPath: join(folder, "second.sqlite") });
29
+ try {
30
+ await first.listenTrades(); await second.listenTrades();
31
+ expect(seen[0]?.rpcUrls).toEqual(["https://configured.example.com"]);
32
+ expect(seen[1]?.rpcUrls).toBeUndefined();
33
+ } finally { first.close(); second.close(); listen.mockRestore(); rmSync(folder, { recursive: true, force: true }); }
34
+ });
@@ -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,
@@ -31,11 +34,14 @@ import {
31
34
  type TokenRef,
32
35
  type WalletRef,
33
36
  type WalletPrivateKeyExport,
34
- type WalletPrivateKeyFormat,
35
- } from "@solard/core";
37
+ type WalletPrivateKeyFormat,
38
+ TradePreSubmissionError,
39
+ } from "@solard/core";
40
+ import { listenTrades as openTradeListener, type ListenTradesOptions, type TradeListener } from "./live.ts";
36
41
 
37
42
  export type SolardOptions = {
38
43
  rpcUrl?: string;
44
+ rpcUrls?: readonly string[];
39
45
  dbPath?: string;
40
46
  cacheTtlMs?: number;
41
47
  };
@@ -60,47 +66,53 @@ export type SolardEventsApi = {
60
66
  };
61
67
 
62
68
  export type SolardBuyInput = {
69
+ reserves?: LivePoolReserves;
70
+ maxPriceSol?: string | number;
63
71
  wallet: WalletRef;
64
72
  token: TokenRef;
65
73
  amount: HumanAmount | string | number;
66
74
  slippageBps?: number;
75
+ minOutputRaw?: bigint | string;
67
76
  };
68
77
 
69
78
  export type SolardSellInput = {
79
+ closeTokenAccount?: boolean;
80
+ reserves?: LivePoolReserves;
81
+ minPriceSol?: string | number;
70
82
  wallet: WalletRef;
71
83
  token: TokenRef;
72
- amount?: "all" | { bps: number };
84
+ amount?: "all" | { bps: number } | { raw: bigint | string };
85
+ /** Exact raw token units; mutually exclusive with amount. */
86
+ amountRaw?: bigint | string;
73
87
  slippageBps?: number;
88
+ minOutputLamports?: bigint | string;
74
89
  };
75
90
 
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
- };
91
+ export type SolardTradeExecutionOptions = TradeExecutionOptions;
102
92
 
103
- export type Solard = {
93
+ export type SolardTradeExecutionResult = TradeResult;
94
+ export type Solard = {
95
+ listenTrades(options?: Omit<ListenTradesOptions, "tokens"> & { tokens?: readonly string[] }): Promise<TradeListener>;
96
+ resolveCurrentMarket: ReturnType<typeof createCoreSolard>["resolveCurrentMarket"];
97
+ getClaimableCreatorFees: ReturnType<typeof createCoreSolard>["getClaimableCreatorFees"];
98
+ claimAllCreatorFees: ReturnType<typeof createCoreSolard>["claimAllCreatorFees"];
99
+ walletLedger: ReturnType<typeof createCoreSolard>["walletLedger"];
100
+ historicalTape: ReturnType<typeof createCoreSolard>["historicalTape"];
101
+ getSupportedPumpPairs: ReturnType<typeof createCoreSolard>["getSupportedPumpPairs"];
102
+ getSupportedLaunchLabPairs: ReturnType<typeof createCoreSolard>["getSupportedLaunchLabPairs"];
103
+ listVanityMints: ReturnType<typeof createCoreSolard>["listVanityMints"];
104
+ releaseVanityMint: ReturnType<typeof createCoreSolard>["releaseVanityMint"];
105
+ closeEmptyTokenAccounts: ReturnType<typeof createCoreSolard>["closeEmptyTokenAccounts"];
106
+ prepareTokenDeployment: ReturnType<typeof createCoreSolard>["prepareTokenDeployment"];
107
+ deployToken: ReturnType<typeof createCoreSolard>["deployToken"];
108
+ warmBlockhash: ReturnType<typeof createCoreSolard>["warmBlockhash"];
109
+ curveLiquidity: ReturnType<typeof createCoreSolard>["curveLiquidity"];
110
+ maxSendableSol: ReturnType<typeof createCoreSolard>["maxSendableSol"];
111
+ transferToken: ReturnType<typeof createCoreSolard>["transferToken"];
112
+ exitWallet: ReturnType<typeof createCoreSolard>["exitWallet"];
113
+ resumeTrade(intentKey: string): Promise<SolardTradeExecutionResult>;
114
+ reconcile(intentKey: string): Promise<SolardTradeExecutionResult>;
115
+ quote: ReturnType<typeof createCoreSolard>["quote"];
104
116
  close(): void;
105
117
  history: SolardHistoryApi;
106
118
  events: SolardEventsApi;
@@ -168,12 +180,22 @@ function tradeAmount(value: HumanAmount | string | number): HumanAmount {
168
180
  : value;
169
181
  }
170
182
 
171
- function sellBps(value: SolardSellInput["amount"]): number {
172
- if (value == null || value === "all") return 10_000;
173
- const bps = Math.trunc(value.bps);
174
- if (!(bps > 0 && bps <= 10_000))
175
- throw new Error("sell amount.bps must be between 1 and 10000");
176
- return bps;
183
+ function sellSize(input: SolardSellInput): { bps?: number; amountRaw?: bigint | string } {
184
+ const value = input.amount;
185
+ if (input.amountRaw !== undefined && value !== undefined) throw new Error("sell amountRaw and amount are mutually exclusive");
186
+ const raw = input.amountRaw !== undefined ? input.amountRaw : typeof value === "object" && value !== null && "raw" in value ? value.raw : undefined;
187
+ if (raw !== undefined) {
188
+ if (typeof value === "object" && value !== null && "bps" in value) throw new Error("sell raw and bps are mutually exclusive");
189
+ if ((typeof raw !== "bigint" && (typeof raw !== "string" || !/^\d+$/.test(raw))) || BigInt(raw) <= 0n)
190
+ throw new Error("sell amountRaw must be a positive raw integer");
191
+ return { amountRaw: raw };
192
+ }
193
+ if (value == null || value === "all") return { bps: 10_000 };
194
+ if (!("bps" in value)) throw new Error("sell amount must specify bps or raw");
195
+ const bps = value.bps;
196
+ if (!Number.isInteger(bps) || !(bps > 0 && bps <= 10_000))
197
+ throw new Error("sell amount.bps must be between 1 and 10000");
198
+ return { bps };
177
199
  }
178
200
 
179
201
 
@@ -183,49 +205,15 @@ async function executeTrade(
183
205
  input: SolardBuyInput | SolardSellInput,
184
206
  options: SolardTradeExecutionOptions = {},
185
207
  ): 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
- };
208
+ if (side === "buy") {
209
+ const buy = input as SolardBuyInput;
210
+ return await core.buy(buy.token, buy.wallet, tradeAmount(buy.amount), { ...options, slippageBps: buy.slippageBps, minOutputRaw: buy.minOutputRaw, maxPriceSol: buy.maxPriceSol, reserves: buy.reserves });
211
+ }
212
+ const sell = input as SolardSellInput;
213
+ let size: ReturnType<typeof sellSize>;
214
+ try { size = sellSize(sell); } catch (error) { throw new TradePreSubmissionError(error); }
215
+ return await core.sell(sell.token, sell.wallet, { ...options, ...size, slippageBps: sell.slippageBps, minOutputLamports: sell.minOutputLamports, minPriceSol: sell.minPriceSol, reserves: sell.reserves, closeTokenAccount: sell.closeTokenAccount });
216
+
229
217
  }
230
218
 
231
219
  export function createSolard(options: SolardOptions = {}): Solard {
@@ -240,7 +228,33 @@ export function createSolard(options: SolardOptions = {}): Solard {
240
228
  core.events(token, eventOptions),
241
229
  { merge: core.events.merge },
242
230
  ) as SolardEventsApi;
243
- return Object.freeze({
231
+ return Object.freeze({
232
+ listenTrades: (listenerOptions: Omit<ListenTradesOptions, "tokens"> & { tokens?: readonly string[] } = {}) => openTradeListener({
233
+ rpcUrls: options.rpcUrls ?? (options.rpcUrl ? [options.rpcUrl] : undefined),
234
+ dbPath: options.dbPath,
235
+ ...listenerOptions,
236
+ tokens: listenerOptions.tokens ?? [],
237
+ }),
238
+ resolveCurrentMarket: core.resolveCurrentMarket.bind(core),
239
+ getClaimableCreatorFees: core.getClaimableCreatorFees.bind(core),
240
+ claimAllCreatorFees: core.claimAllCreatorFees.bind(core),
241
+ walletLedger: core.walletLedger.bind(core),
242
+ historicalTape: core.historicalTape.bind(core),
243
+ getSupportedPumpPairs: core.getSupportedPumpPairs.bind(core),
244
+ getSupportedLaunchLabPairs: core.getSupportedLaunchLabPairs.bind(core),
245
+ listVanityMints: core.listVanityMints.bind(core),
246
+ releaseVanityMint: core.releaseVanityMint.bind(core),
247
+ closeEmptyTokenAccounts: core.closeEmptyTokenAccounts.bind(core),
248
+ prepareTokenDeployment: core.prepareTokenDeployment.bind(core),
249
+ deployToken: core.deployToken.bind(core),
250
+ warmBlockhash: core.warmBlockhash.bind(core),
251
+ curveLiquidity: core.curveLiquidity.bind(core),
252
+ maxSendableSol: core.maxSendableSol.bind(core),
253
+ transferToken: core.transferToken.bind(core),
254
+ exitWallet: core.exitWallet.bind(core),
255
+ resumeTrade: core.resumeTrade.bind(core),
256
+ reconcile: core.reconcile.bind(core),
257
+ quote: core.quote.bind(core),
244
258
  close: core.close.bind(core),
245
259
  history,
246
260
  events: Object.freeze(events),
package/src/default.ts CHANGED
@@ -1,51 +1,48 @@
1
- import { getSolUsdPrice } from "@solard/core";
2
- import {
3
- listenTrades,
1
+ import { getSolUsdPrice } from "@solard/core";
2
+ import {
4
3
  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,
4
+ subscribeMigrations,
5
+ } from "./live.ts";
6
+ import { createSolard, type Solard } from "./client.ts";
7
+
8
+ type DefaultSolard = Solard & {
9
+ getSolUsdPrice: typeof getSolUsdPrice;
10
+ subscribeLaunches: typeof subscribeLaunches;
11
+ subscribeMigrations: typeof subscribeMigrations;
12
+ };
13
+
14
+ let instance: Solard | undefined;
15
+
16
+ function client(): Solard {
17
+ instance ??= createSolard();
18
+ return instance;
19
+ }
20
+
21
+ const operations = {
22
+ getSolUsdPrice,
26
23
  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;
24
+ subscribeMigrations,
25
+ } as const;
26
+
27
+ const slrd = new Proxy({} as DefaultSolard, {
28
+ get(_target, key) {
29
+ if (key === "close") {
30
+ return () => {
31
+ const current = instance;
32
+ instance = undefined;
33
+ current?.close();
34
+ };
35
+ }
36
+ if (typeof key === "string" && key in operations) {
37
+ return operations[key as keyof typeof operations];
38
+ }
39
+ const current = client();
40
+ const value = Reflect.get(current as object, key, current);
41
+ return typeof value === "function" ? value.bind(current) : value;
42
+ },
43
+ set() {
44
+ return false;
45
+ },
46
+ });
47
+
48
+ export default slrd;