@cowprotocol/sdk-trading-solana 0.4.0 → 0.5.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 CHANGED
@@ -19,11 +19,13 @@ the `cowswap` repo for background.
19
19
 
20
20
  ## Usage
21
21
 
22
+ Quoting needs no signer:
23
+
22
24
  ```ts
23
25
  import { SolanaTradingSdk } from '@cowprotocol/sdk-trading-solana'
24
26
 
25
- const sdk = new SolanaTradingSdk({ signAndSend })
26
- const { quoteResults, postSwapOrderFromQuote } = await sdk.getQuote({
27
+ const sdk = new SolanaTradingSdk()
28
+ const { quoteResults, solanaQuote, buildOrder, postSwapOrderFromQuote } = await sdk.getQuote({
27
29
  ownerAddress,
28
30
  receiverAddress,
29
31
  sellTokenAddress,
@@ -33,6 +35,26 @@ const { quoteResults, postSwapOrderFromQuote } = await sdk.getQuote({
33
35
  amount,
34
36
  kind,
35
37
  })
38
+ ```
39
+
40
+ ### Let the SDK submit the order
41
+
42
+ `signAndSend` is passed at the point of signing:
43
+
44
+ ```ts
45
+ const result = await postSwapOrderFromQuote(signAndSend)
46
+ ```
47
+
48
+ ### Bundle the order with your own instructions
49
+
50
+ A Solana order is created by a single instruction, so it can share a transaction with, say, a wrap and a
51
+ token delegation. `buildOrder` returns that instruction without sending it:
36
52
 
37
- const result = await postSwapOrderFromQuote()
53
+ ```ts
54
+ const { instruction, orderId } = await buildOrder()
55
+
56
+ await sendMyTransaction([...wrapInstructions, approveInstruction, instruction])
38
57
  ```
58
+
59
+ Both paths apply `advancedSettings` identically — overriding `receiver` or `validTo` re-derives the order's
60
+ `uid` and PDA so they still match the intent actually being created.
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { PublicKey, PublicKeyInitData, TransactionInstruction } from '@solana/web3.js';
2
- import { OrderKind } from '@cowprotocol/sdk-order-book';
2
+ import { OrderKind, SigningScheme } from '@cowprotocol/sdk-order-book';
3
3
  import { CowEnv } from '@cowprotocol/sdk-config';
4
- import { QuoteResults, SwapAdvancedSettings, SigningStepManager, OrderPostingResult, QuoteAndPost } from '@cowprotocol/sdk-trading';
4
+ import { QuoteResults, SwapAdvancedSettings, SigningStepManager, OrderPostingResult } from '@cowprotocol/sdk-trading';
5
5
 
6
6
  interface JupiterOrderRequest {
7
7
  inputMint: string;
@@ -64,6 +64,12 @@ declare function encodeOrderIntent(intent: SolanaOrderIntent): Uint8Array;
64
64
  */
65
65
  declare function hashOrderIntent(encoded: Uint8Array): Promise<Uint8Array>;
66
66
  declare function toHex(bytes: Uint8Array): string;
67
+ /**
68
+ * Formats a uid as an order id in the same "0x"-prefixed form the order-book API returns in
69
+ * `EnrichedOrder.uid` (matching EVM order uids). Code that looks an order up by the API's uid
70
+ * (reducer batch actions, notifications, etc.) needs this to match whatever id was stored locally.
71
+ */
72
+ declare function toOrderId(uid: Uint8Array): string;
67
73
 
68
74
  interface SolanaQuoteParameters {
69
75
  ownerAddress: PublicKeyInitData;
@@ -170,32 +176,68 @@ declare function getSolanaQuote(params: SolanaQuoteParameters, options?: {
170
176
  solanaQuote: SolanaQuote;
171
177
  }>;
172
178
 
173
- /**
174
- * Builds the real `CreateOrder` instruction for `quote` and has the caller sign and submit it. This is
175
- * the Solana analogue of `postSwapOrderFromQuote` in `postSwapOrder.ts` — but where the EVM version signs
176
- * order data and POSTs it to the CoW order-book, Solana orders are created entirely on-chain, so this
177
- * builds a transaction instruction instead of a signed order body. `createdBy` is always `quote.intent.owner`:
178
- * a single connected wallet both authenticates and funds the order's rent.
179
- */
180
- declare function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }: {
179
+ interface SolanaSwapOrderQuote {
181
180
  quoteResults: QuoteResults;
182
181
  solanaQuote: SolanaQuote;
183
- }, signAndSend: SolanaSignAndSend, advancedSettings?: SwapAdvancedSettings, signingStepManager?: SigningStepManager): Promise<OrderPostingResult>;
182
+ }
183
+ interface SolanaSwapOrder {
184
+ /** The `CreateOrder` instruction. Send it on its own, or bundle it with other instructions. */
185
+ instruction: TransactionInstruction;
186
+ /** `uid` as the order-book's `0x`-prefixed uid — see `toOrderId`. */
187
+ orderId: string;
188
+ uid: Uint8Array;
189
+ orderPda: PublicKey;
190
+ /** The intent actually encoded into `instruction` — `advancedSettings` may have overridden the quoted one. */
191
+ intent: SolanaOrderIntent;
192
+ signingScheme: SigningScheme;
193
+ orderToSign: QuoteResults['orderToSign'];
194
+ }
195
+ /**
196
+ * Builds everything needed to create `quote`'s order on-chain, without signing or sending it. Callers that
197
+ * want the SDK to submit it should use `postSolanaSwapOrderFromQuote`; callers bundling the order with
198
+ * other instructions (a wrap, a token delegation) take `instruction` from here and send it themselves.
199
+ *
200
+ * Solana orders are created entirely on-chain, so — unlike the EVM `postSwapOrderFromQuote` — there is no
201
+ * signed order body to POST, and `createdBy` is always `intent.owner`: a single connected wallet both
202
+ * authenticates the order and funds its PDA's rent.
203
+ */
204
+ declare function buildSolanaSwapOrder({ quoteResults, solanaQuote }: SolanaSwapOrderQuote, advancedSettings?: SwapAdvancedSettings): Promise<SolanaSwapOrder>;
205
+
206
+ /**
207
+ * Builds `quote`'s `CreateOrder` instruction and has `signAndSend` submit it as its own transaction. This
208
+ * is the Solana analogue of `postSwapOrderFromQuote` in `postSwapOrder.ts` — but where the EVM version
209
+ * signs order data and POSTs it to the CoW order-book, Solana orders are created entirely on-chain.
210
+ *
211
+ * To bundle the order with other instructions instead of sending it alone, use `buildSolanaSwapOrder`.
212
+ */
213
+ declare function postSolanaSwapOrderFromQuote(quote: SolanaSwapOrderQuote, signAndSend: SolanaSignAndSend, advancedSettings?: SwapAdvancedSettings, signingStepManager?: SigningStepManager): Promise<OrderPostingResult>;
184
214
 
185
215
  interface SolanaTradingSdkOptions {
186
- signAndSend: SolanaSignAndSend;
187
216
  env?: CowEnv;
188
217
  }
189
218
  /**
190
- * Solana counterpart to `TradingSdk`. Unlike the EVM SDK, which gets its signer implicitly from a
191
- * global adapter set once at app startup, Solana has no such adapter `signAndSend` is bound at
192
- * construction instead, so callers get the same `sdk.getQuote(...)` `.postSwapOrderFromQuote()`
193
- * shape without threading a signer through every call.
219
+ * Solana counterpart to `QuoteAndPost`. It additionally exposes `solanaQuote` and `buildOrder`, because a
220
+ * Solana order is a plain instruction: callers may want to bundle it with a wrap or a token delegation and
221
+ * submit one transaction, rather than have the SDK send it alone.
222
+ */
223
+ interface SolanaQuoteAndPost {
224
+ quoteResults: QuoteResults;
225
+ solanaQuote: SolanaQuote;
226
+ /** Build the `CreateOrder` instruction without sending it, to bundle with other instructions. */
227
+ buildOrder(advancedSettings?: SwapAdvancedSettings): Promise<SolanaSwapOrder>;
228
+ /** Build, sign and submit the order as its own transaction. */
229
+ postSwapOrderFromQuote(signAndSend: SolanaSignAndSend, advancedSettings?: SwapAdvancedSettings, signingStepManager?: SigningStepManager): Promise<OrderPostingResult>;
230
+ }
231
+ /**
232
+ * Solana counterpart to `TradingSdk`. Unlike the EVM SDK, which gets its signer implicitly from a global
233
+ * adapter set once at app startup, Solana has no such adapter — so the signer is passed to
234
+ * `postSwapOrderFromQuote` at the point of signing. Quoting therefore needs no signer at all, which also
235
+ * lets a caller quote and then bundle the order instruction itself via `buildOrder`.
194
236
  */
195
237
  declare class SolanaTradingSdk {
196
238
  private readonly options;
197
- constructor(options: SolanaTradingSdkOptions);
198
- getQuote(params: SolanaQuoteParameters): Promise<QuoteAndPost>;
239
+ constructor(options?: SolanaTradingSdkOptions);
240
+ getQuote(params: SolanaQuoteParameters): Promise<SolanaQuoteAndPost>;
199
241
  }
200
242
 
201
- export { type CreateOrderInstructionParams, ENCODED_ORDER_INTENT_SIZE, JupiterAPI, type JupiterOrderRequest, type JupiterOrderResponse, ORDER_SEED, type SolanaOrderIntent, type SolanaQuote, type SolanaQuoteParameters, type SolanaSignAndSend, SolanaTradingSdk, type SolanaTradingSdkOptions, buildCreateOrderInstruction, encodeOrderIntent, findOrderPda, findSettlementStatePda, getSettlementSeed, getSolanaDelegateAuthority, getSolanaQuote, getSolanaSettlementProgramId, hashOrderIntent, postSolanaSwapOrderFromQuote, toHex };
243
+ export { type CreateOrderInstructionParams, ENCODED_ORDER_INTENT_SIZE, JupiterAPI, type JupiterOrderRequest, type JupiterOrderResponse, ORDER_SEED, type SolanaOrderIntent, type SolanaQuote, type SolanaQuoteAndPost, type SolanaQuoteParameters, type SolanaSignAndSend, type SolanaSwapOrder, type SolanaSwapOrderQuote, SolanaTradingSdk, type SolanaTradingSdkOptions, buildCreateOrderInstruction, buildSolanaSwapOrder, encodeOrderIntent, findOrderPda, findSettlementStatePda, getSettlementSeed, getSolanaDelegateAuthority, getSolanaQuote, getSolanaSettlementProgramId, hashOrderIntent, postSolanaSwapOrderFromQuote, toHex, toOrderId };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { PublicKey, PublicKeyInitData, TransactionInstruction } from '@solana/web3.js';
2
- import { OrderKind } from '@cowprotocol/sdk-order-book';
2
+ import { OrderKind, SigningScheme } from '@cowprotocol/sdk-order-book';
3
3
  import { CowEnv } from '@cowprotocol/sdk-config';
4
- import { QuoteResults, SwapAdvancedSettings, SigningStepManager, OrderPostingResult, QuoteAndPost } from '@cowprotocol/sdk-trading';
4
+ import { QuoteResults, SwapAdvancedSettings, SigningStepManager, OrderPostingResult } from '@cowprotocol/sdk-trading';
5
5
 
6
6
  interface JupiterOrderRequest {
7
7
  inputMint: string;
@@ -64,6 +64,12 @@ declare function encodeOrderIntent(intent: SolanaOrderIntent): Uint8Array;
64
64
  */
65
65
  declare function hashOrderIntent(encoded: Uint8Array): Promise<Uint8Array>;
66
66
  declare function toHex(bytes: Uint8Array): string;
67
+ /**
68
+ * Formats a uid as an order id in the same "0x"-prefixed form the order-book API returns in
69
+ * `EnrichedOrder.uid` (matching EVM order uids). Code that looks an order up by the API's uid
70
+ * (reducer batch actions, notifications, etc.) needs this to match whatever id was stored locally.
71
+ */
72
+ declare function toOrderId(uid: Uint8Array): string;
67
73
 
68
74
  interface SolanaQuoteParameters {
69
75
  ownerAddress: PublicKeyInitData;
@@ -170,32 +176,68 @@ declare function getSolanaQuote(params: SolanaQuoteParameters, options?: {
170
176
  solanaQuote: SolanaQuote;
171
177
  }>;
172
178
 
173
- /**
174
- * Builds the real `CreateOrder` instruction for `quote` and has the caller sign and submit it. This is
175
- * the Solana analogue of `postSwapOrderFromQuote` in `postSwapOrder.ts` — but where the EVM version signs
176
- * order data and POSTs it to the CoW order-book, Solana orders are created entirely on-chain, so this
177
- * builds a transaction instruction instead of a signed order body. `createdBy` is always `quote.intent.owner`:
178
- * a single connected wallet both authenticates and funds the order's rent.
179
- */
180
- declare function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }: {
179
+ interface SolanaSwapOrderQuote {
181
180
  quoteResults: QuoteResults;
182
181
  solanaQuote: SolanaQuote;
183
- }, signAndSend: SolanaSignAndSend, advancedSettings?: SwapAdvancedSettings, signingStepManager?: SigningStepManager): Promise<OrderPostingResult>;
182
+ }
183
+ interface SolanaSwapOrder {
184
+ /** The `CreateOrder` instruction. Send it on its own, or bundle it with other instructions. */
185
+ instruction: TransactionInstruction;
186
+ /** `uid` as the order-book's `0x`-prefixed uid — see `toOrderId`. */
187
+ orderId: string;
188
+ uid: Uint8Array;
189
+ orderPda: PublicKey;
190
+ /** The intent actually encoded into `instruction` — `advancedSettings` may have overridden the quoted one. */
191
+ intent: SolanaOrderIntent;
192
+ signingScheme: SigningScheme;
193
+ orderToSign: QuoteResults['orderToSign'];
194
+ }
195
+ /**
196
+ * Builds everything needed to create `quote`'s order on-chain, without signing or sending it. Callers that
197
+ * want the SDK to submit it should use `postSolanaSwapOrderFromQuote`; callers bundling the order with
198
+ * other instructions (a wrap, a token delegation) take `instruction` from here and send it themselves.
199
+ *
200
+ * Solana orders are created entirely on-chain, so — unlike the EVM `postSwapOrderFromQuote` — there is no
201
+ * signed order body to POST, and `createdBy` is always `intent.owner`: a single connected wallet both
202
+ * authenticates the order and funds its PDA's rent.
203
+ */
204
+ declare function buildSolanaSwapOrder({ quoteResults, solanaQuote }: SolanaSwapOrderQuote, advancedSettings?: SwapAdvancedSettings): Promise<SolanaSwapOrder>;
205
+
206
+ /**
207
+ * Builds `quote`'s `CreateOrder` instruction and has `signAndSend` submit it as its own transaction. This
208
+ * is the Solana analogue of `postSwapOrderFromQuote` in `postSwapOrder.ts` — but where the EVM version
209
+ * signs order data and POSTs it to the CoW order-book, Solana orders are created entirely on-chain.
210
+ *
211
+ * To bundle the order with other instructions instead of sending it alone, use `buildSolanaSwapOrder`.
212
+ */
213
+ declare function postSolanaSwapOrderFromQuote(quote: SolanaSwapOrderQuote, signAndSend: SolanaSignAndSend, advancedSettings?: SwapAdvancedSettings, signingStepManager?: SigningStepManager): Promise<OrderPostingResult>;
184
214
 
185
215
  interface SolanaTradingSdkOptions {
186
- signAndSend: SolanaSignAndSend;
187
216
  env?: CowEnv;
188
217
  }
189
218
  /**
190
- * Solana counterpart to `TradingSdk`. Unlike the EVM SDK, which gets its signer implicitly from a
191
- * global adapter set once at app startup, Solana has no such adapter `signAndSend` is bound at
192
- * construction instead, so callers get the same `sdk.getQuote(...)` `.postSwapOrderFromQuote()`
193
- * shape without threading a signer through every call.
219
+ * Solana counterpart to `QuoteAndPost`. It additionally exposes `solanaQuote` and `buildOrder`, because a
220
+ * Solana order is a plain instruction: callers may want to bundle it with a wrap or a token delegation and
221
+ * submit one transaction, rather than have the SDK send it alone.
222
+ */
223
+ interface SolanaQuoteAndPost {
224
+ quoteResults: QuoteResults;
225
+ solanaQuote: SolanaQuote;
226
+ /** Build the `CreateOrder` instruction without sending it, to bundle with other instructions. */
227
+ buildOrder(advancedSettings?: SwapAdvancedSettings): Promise<SolanaSwapOrder>;
228
+ /** Build, sign and submit the order as its own transaction. */
229
+ postSwapOrderFromQuote(signAndSend: SolanaSignAndSend, advancedSettings?: SwapAdvancedSettings, signingStepManager?: SigningStepManager): Promise<OrderPostingResult>;
230
+ }
231
+ /**
232
+ * Solana counterpart to `TradingSdk`. Unlike the EVM SDK, which gets its signer implicitly from a global
233
+ * adapter set once at app startup, Solana has no such adapter — so the signer is passed to
234
+ * `postSwapOrderFromQuote` at the point of signing. Quoting therefore needs no signer at all, which also
235
+ * lets a caller quote and then bundle the order instruction itself via `buildOrder`.
194
236
  */
195
237
  declare class SolanaTradingSdk {
196
238
  private readonly options;
197
- constructor(options: SolanaTradingSdkOptions);
198
- getQuote(params: SolanaQuoteParameters): Promise<QuoteAndPost>;
239
+ constructor(options?: SolanaTradingSdkOptions);
240
+ getQuote(params: SolanaQuoteParameters): Promise<SolanaQuoteAndPost>;
199
241
  }
200
242
 
201
- export { type CreateOrderInstructionParams, ENCODED_ORDER_INTENT_SIZE, JupiterAPI, type JupiterOrderRequest, type JupiterOrderResponse, ORDER_SEED, type SolanaOrderIntent, type SolanaQuote, type SolanaQuoteParameters, type SolanaSignAndSend, SolanaTradingSdk, type SolanaTradingSdkOptions, buildCreateOrderInstruction, encodeOrderIntent, findOrderPda, findSettlementStatePda, getSettlementSeed, getSolanaDelegateAuthority, getSolanaQuote, getSolanaSettlementProgramId, hashOrderIntent, postSolanaSwapOrderFromQuote, toHex };
243
+ export { type CreateOrderInstructionParams, ENCODED_ORDER_INTENT_SIZE, JupiterAPI, type JupiterOrderRequest, type JupiterOrderResponse, ORDER_SEED, type SolanaOrderIntent, type SolanaQuote, type SolanaQuoteAndPost, type SolanaQuoteParameters, type SolanaSignAndSend, type SolanaSwapOrder, type SolanaSwapOrderQuote, SolanaTradingSdk, type SolanaTradingSdkOptions, buildCreateOrderInstruction, buildSolanaSwapOrder, encodeOrderIntent, findOrderPda, findSettlementStatePda, getSettlementSeed, getSolanaDelegateAuthority, getSolanaQuote, getSolanaSettlementProgramId, hashOrderIntent, postSolanaSwapOrderFromQuote, toHex, toOrderId };
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ __export(src_exports, {
25
25
  ORDER_SEED: () => ORDER_SEED,
26
26
  SolanaTradingSdk: () => SolanaTradingSdk,
27
27
  buildCreateOrderInstruction: () => buildCreateOrderInstruction,
28
+ buildSolanaSwapOrder: () => buildSolanaSwapOrder,
28
29
  encodeOrderIntent: () => encodeOrderIntent,
29
30
  findOrderPda: () => findOrderPda,
30
31
  findSettlementStatePda: () => findSettlementStatePda,
@@ -34,7 +35,8 @@ __export(src_exports, {
34
35
  getSolanaSettlementProgramId: () => getSolanaSettlementProgramId,
35
36
  hashOrderIntent: () => hashOrderIntent,
36
37
  postSolanaSwapOrderFromQuote: () => postSolanaSwapOrderFromQuote,
37
- toHex: () => toHex
38
+ toHex: () => toHex,
39
+ toOrderId: () => toOrderId
38
40
  });
39
41
  module.exports = __toCommonJS(src_exports);
40
42
 
@@ -91,6 +93,9 @@ async function hashOrderIntent(encoded) {
91
93
  function toHex(bytes) {
92
94
  return Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
93
95
  }
96
+ function toOrderId(uid) {
97
+ return `0x${toHex(uid)}`;
98
+ }
94
99
 
95
100
  // src/orderPda.ts
96
101
  var import_web3 = require("@solana/web3.js");
@@ -202,9 +207,20 @@ function isJupiterOrderResponse(body, request) {
202
207
  }
203
208
 
204
209
  // src/getSolanaQuote.ts
205
- var import_web34 = require("@solana/web3.js");
210
+ var import_web35 = require("@solana/web3.js");
206
211
  var import_spl_token = require("@solana/spl-token");
207
212
  var import_sdk_order_book2 = require("@cowprotocol/sdk-order-book");
213
+
214
+ // src/splMint.ts
215
+ var import_web34 = require("@solana/web3.js");
216
+ var import_sdk_config3 = require("@cowprotocol/sdk-config");
217
+ var NATIVE_SOL_MINT = new import_web34.PublicKey(import_sdk_config3.SOL_NATIVE_CURRENCY_ADDRESS);
218
+ var WSOL_MINT = new import_web34.PublicKey(import_sdk_config3.WRAPPED_NATIVE_CURRENCIES[import_sdk_config3.SupportedChainId.SOLANA].address);
219
+ function toSplMint(mint) {
220
+ return mint.equals(NATIVE_SOL_MINT) ? WSOL_MINT : mint;
221
+ }
222
+
223
+ // src/getSolanaQuote.ts
208
224
  var DEFAULT_VALID_FOR_SECONDS = 30 * 60;
209
225
  var ZERO_APP_DATA = new Uint8Array(32);
210
226
  var jupiterApi = new JupiterAPI();
@@ -224,12 +240,13 @@ async function getSolanaQuote(params, options = {}) {
224
240
  if (!Number.isFinite(validForSeconds) || validForSeconds <= 0) {
225
241
  throw new Error("validForSeconds must be a finite number greater than zero");
226
242
  }
227
- const owner = new import_web34.PublicKey(ownerAddress);
228
- const receiver = new import_web34.PublicKey(receiverAddress);
229
- const sellMint = new import_web34.PublicKey(params.sellTokenAddress);
230
- const buyMint = new import_web34.PublicKey(params.buyTokenAddress);
231
- const sellTokenProgram = sellTokenProgramId ? new import_web34.PublicKey(sellTokenProgramId) : void 0;
232
- const buyTokenProgram = buyTokenProgramId ? new import_web34.PublicKey(buyTokenProgramId) : void 0;
243
+ const owner = new import_web35.PublicKey(ownerAddress);
244
+ const receiver = new import_web35.PublicKey(receiverAddress);
245
+ const requestedSellMint = new import_web35.PublicKey(params.sellTokenAddress);
246
+ const sellMint = toSplMint(requestedSellMint);
247
+ const buyMint = new import_web35.PublicKey(params.buyTokenAddress);
248
+ const sellTokenProgram = sellTokenProgramId ? new import_web35.PublicKey(sellTokenProgramId) : void 0;
249
+ const buyTokenProgram = buyTokenProgramId ? new import_web35.PublicKey(buyTokenProgramId) : void 0;
233
250
  const sellTokenAddress = sellMint.toBase58();
234
251
  const buyTokenAddress = buyMint.toBase58();
235
252
  const jupiterOrder = await jupiterApi.getOrder({
@@ -299,7 +316,10 @@ async function getSolanaQuote(params, options = {}) {
299
316
  const tradeParameters = {
300
317
  kind,
301
318
  owner: owner.toBase58(),
302
- sellToken: sellTokenAddress,
319
+ // The mint the caller asked for, not the substituted one: callers compare the returned parameters
320
+ // against the ones they passed to decide whether a quote is still current, and reporting WSOL for a
321
+ // native-SOL request would read as a changed sell token and requote forever.
322
+ sellToken: requestedSellMint.toBase58(),
303
323
  sellTokenDecimals,
304
324
  buyToken: buyTokenAddress,
305
325
  buyTokenDecimals,
@@ -320,11 +340,11 @@ async function getSolanaQuote(params, options = {}) {
320
340
  return { quoteResults, solanaQuote };
321
341
  }
322
342
 
323
- // src/postSwapOrderFromQuote.ts
324
- var import_spl_token2 = require("@solana/spl-token");
343
+ // src/buildSwapOrder.ts
325
344
  var import_sdk_order_book3 = require("@cowprotocol/sdk-order-book");
326
- var import_web35 = require("@solana/web3.js");
327
- async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signAndSend, advancedSettings, signingStepManager) {
345
+ var import_spl_token2 = require("@solana/spl-token");
346
+ var import_web36 = require("@solana/web3.js");
347
+ async function buildSolanaSwapOrder({ quoteResults, solanaQuote }, advancedSettings) {
328
348
  const intent = { ...solanaQuote.intent };
329
349
  let uid = solanaQuote.uid;
330
350
  let orderPda = solanaQuote.orderPda;
@@ -333,7 +353,7 @@ async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signA
333
353
  if (receiver) {
334
354
  intent.buyTokenAccount = (0, import_spl_token2.getAssociatedTokenAddressSync)(
335
355
  intent.buyMint,
336
- new import_web35.PublicKey(receiver),
356
+ new import_web36.PublicKey(receiver),
337
357
  false,
338
358
  solanaQuote.buyTokenProgramId
339
359
  );
@@ -353,29 +373,44 @@ async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signA
353
373
  orderPda,
354
374
  intent
355
375
  });
376
+ return {
377
+ instruction,
378
+ orderId: toOrderId(uid),
379
+ uid,
380
+ orderPda,
381
+ intent,
382
+ signingScheme: import_sdk_order_book3.SigningScheme.PRESIGN,
383
+ orderToSign: quoteResults.orderToSign
384
+ };
385
+ }
386
+
387
+ // src/postSwapOrderFromQuote.ts
388
+ async function postSolanaSwapOrderFromQuote(quote, signAndSend, advancedSettings, signingStepManager) {
389
+ const { instruction, orderId, signingScheme, orderToSign } = await buildSolanaSwapOrder(quote, advancedSettings);
356
390
  await signingStepManager?.beforeOrderSign?.();
357
391
  const { signature } = await signAndSend(instruction);
358
392
  await signingStepManager?.afterOrderSign?.();
359
- const orderToSign = quoteResults.orderToSign;
360
393
  return {
361
- orderId: toHex(uid),
394
+ orderId,
362
395
  txHash: signature,
363
396
  signature,
364
- signingScheme: import_sdk_order_book3.SigningScheme.PRESIGN,
397
+ signingScheme,
365
398
  orderToSign
366
399
  };
367
400
  }
368
401
 
369
402
  // src/solanaTradingSdk.ts
370
403
  var SolanaTradingSdk = class {
371
- constructor(options) {
404
+ constructor(options = {}) {
372
405
  this.options = options;
373
406
  }
374
407
  async getQuote(params) {
375
408
  const quote = await getSolanaQuote(params, { env: this.options.env });
376
409
  return {
377
410
  quoteResults: quote.quoteResults,
378
- postSwapOrderFromQuote: (advancedSettings, signingStepManager) => postSolanaSwapOrderFromQuote(quote, this.options.signAndSend, advancedSettings, signingStepManager)
411
+ solanaQuote: quote.solanaQuote,
412
+ buildOrder: (advancedSettings) => buildSolanaSwapOrder(quote, advancedSettings),
413
+ postSwapOrderFromQuote: (signAndSend, advancedSettings, signingStepManager) => postSolanaSwapOrderFromQuote(quote, signAndSend, advancedSettings, signingStepManager)
379
414
  };
380
415
  }
381
416
  };
@@ -386,6 +421,7 @@ var SolanaTradingSdk = class {
386
421
  ORDER_SEED,
387
422
  SolanaTradingSdk,
388
423
  buildCreateOrderInstruction,
424
+ buildSolanaSwapOrder,
389
425
  encodeOrderIntent,
390
426
  findOrderPda,
391
427
  findSettlementStatePda,
@@ -395,5 +431,6 @@ var SolanaTradingSdk = class {
395
431
  getSolanaSettlementProgramId,
396
432
  hashOrderIntent,
397
433
  postSolanaSwapOrderFromQuote,
398
- toHex
434
+ toHex,
435
+ toOrderId
399
436
  });
package/dist/index.mjs CHANGED
@@ -51,6 +51,9 @@ async function hashOrderIntent(encoded) {
51
51
  function toHex(bytes) {
52
52
  return Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
53
53
  }
54
+ function toOrderId(uid) {
55
+ return `0x${toHex(uid)}`;
56
+ }
54
57
 
55
58
  // src/orderPda.ts
56
59
  import { PublicKey } from "@solana/web3.js";
@@ -165,9 +168,20 @@ function isJupiterOrderResponse(body, request) {
165
168
  }
166
169
 
167
170
  // src/getSolanaQuote.ts
168
- import { PublicKey as PublicKey4 } from "@solana/web3.js";
171
+ import { PublicKey as PublicKey5 } from "@solana/web3.js";
169
172
  import { getAssociatedTokenAddressSync } from "@solana/spl-token";
170
173
  import { getQuoteAmountsAndCosts, OrderKind as OrderKind2 } from "@cowprotocol/sdk-order-book";
174
+
175
+ // src/splMint.ts
176
+ import { PublicKey as PublicKey4 } from "@solana/web3.js";
177
+ import { SOL_NATIVE_CURRENCY_ADDRESS, SupportedChainId, WRAPPED_NATIVE_CURRENCIES } from "@cowprotocol/sdk-config";
178
+ var NATIVE_SOL_MINT = new PublicKey4(SOL_NATIVE_CURRENCY_ADDRESS);
179
+ var WSOL_MINT = new PublicKey4(WRAPPED_NATIVE_CURRENCIES[SupportedChainId.SOLANA].address);
180
+ function toSplMint(mint) {
181
+ return mint.equals(NATIVE_SOL_MINT) ? WSOL_MINT : mint;
182
+ }
183
+
184
+ // src/getSolanaQuote.ts
171
185
  var DEFAULT_VALID_FOR_SECONDS = 30 * 60;
172
186
  var ZERO_APP_DATA = new Uint8Array(32);
173
187
  var jupiterApi = new JupiterAPI();
@@ -187,12 +201,13 @@ async function getSolanaQuote(params, options = {}) {
187
201
  if (!Number.isFinite(validForSeconds) || validForSeconds <= 0) {
188
202
  throw new Error("validForSeconds must be a finite number greater than zero");
189
203
  }
190
- const owner = new PublicKey4(ownerAddress);
191
- const receiver = new PublicKey4(receiverAddress);
192
- const sellMint = new PublicKey4(params.sellTokenAddress);
193
- const buyMint = new PublicKey4(params.buyTokenAddress);
194
- const sellTokenProgram = sellTokenProgramId ? new PublicKey4(sellTokenProgramId) : void 0;
195
- const buyTokenProgram = buyTokenProgramId ? new PublicKey4(buyTokenProgramId) : void 0;
204
+ const owner = new PublicKey5(ownerAddress);
205
+ const receiver = new PublicKey5(receiverAddress);
206
+ const requestedSellMint = new PublicKey5(params.sellTokenAddress);
207
+ const sellMint = toSplMint(requestedSellMint);
208
+ const buyMint = new PublicKey5(params.buyTokenAddress);
209
+ const sellTokenProgram = sellTokenProgramId ? new PublicKey5(sellTokenProgramId) : void 0;
210
+ const buyTokenProgram = buyTokenProgramId ? new PublicKey5(buyTokenProgramId) : void 0;
196
211
  const sellTokenAddress = sellMint.toBase58();
197
212
  const buyTokenAddress = buyMint.toBase58();
198
213
  const jupiterOrder = await jupiterApi.getOrder({
@@ -262,7 +277,10 @@ async function getSolanaQuote(params, options = {}) {
262
277
  const tradeParameters = {
263
278
  kind,
264
279
  owner: owner.toBase58(),
265
- sellToken: sellTokenAddress,
280
+ // The mint the caller asked for, not the substituted one: callers compare the returned parameters
281
+ // against the ones they passed to decide whether a quote is still current, and reporting WSOL for a
282
+ // native-SOL request would read as a changed sell token and requote forever.
283
+ sellToken: requestedSellMint.toBase58(),
266
284
  sellTokenDecimals,
267
285
  buyToken: buyTokenAddress,
268
286
  buyTokenDecimals,
@@ -283,11 +301,11 @@ async function getSolanaQuote(params, options = {}) {
283
301
  return { quoteResults, solanaQuote };
284
302
  }
285
303
 
286
- // src/postSwapOrderFromQuote.ts
287
- import { getAssociatedTokenAddressSync as getAssociatedTokenAddressSync2 } from "@solana/spl-token";
304
+ // src/buildSwapOrder.ts
288
305
  import { SigningScheme } from "@cowprotocol/sdk-order-book";
289
- import { PublicKey as PublicKey5 } from "@solana/web3.js";
290
- async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signAndSend, advancedSettings, signingStepManager) {
306
+ import { getAssociatedTokenAddressSync as getAssociatedTokenAddressSync2 } from "@solana/spl-token";
307
+ import { PublicKey as PublicKey6 } from "@solana/web3.js";
308
+ async function buildSolanaSwapOrder({ quoteResults, solanaQuote }, advancedSettings) {
291
309
  const intent = { ...solanaQuote.intent };
292
310
  let uid = solanaQuote.uid;
293
311
  let orderPda = solanaQuote.orderPda;
@@ -296,7 +314,7 @@ async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signA
296
314
  if (receiver) {
297
315
  intent.buyTokenAccount = getAssociatedTokenAddressSync2(
298
316
  intent.buyMint,
299
- new PublicKey5(receiver),
317
+ new PublicKey6(receiver),
300
318
  false,
301
319
  solanaQuote.buyTokenProgramId
302
320
  );
@@ -316,29 +334,44 @@ async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signA
316
334
  orderPda,
317
335
  intent
318
336
  });
337
+ return {
338
+ instruction,
339
+ orderId: toOrderId(uid),
340
+ uid,
341
+ orderPda,
342
+ intent,
343
+ signingScheme: SigningScheme.PRESIGN,
344
+ orderToSign: quoteResults.orderToSign
345
+ };
346
+ }
347
+
348
+ // src/postSwapOrderFromQuote.ts
349
+ async function postSolanaSwapOrderFromQuote(quote, signAndSend, advancedSettings, signingStepManager) {
350
+ const { instruction, orderId, signingScheme, orderToSign } = await buildSolanaSwapOrder(quote, advancedSettings);
319
351
  await signingStepManager?.beforeOrderSign?.();
320
352
  const { signature } = await signAndSend(instruction);
321
353
  await signingStepManager?.afterOrderSign?.();
322
- const orderToSign = quoteResults.orderToSign;
323
354
  return {
324
- orderId: toHex(uid),
355
+ orderId,
325
356
  txHash: signature,
326
357
  signature,
327
- signingScheme: SigningScheme.PRESIGN,
358
+ signingScheme,
328
359
  orderToSign
329
360
  };
330
361
  }
331
362
 
332
363
  // src/solanaTradingSdk.ts
333
364
  var SolanaTradingSdk = class {
334
- constructor(options) {
365
+ constructor(options = {}) {
335
366
  this.options = options;
336
367
  }
337
368
  async getQuote(params) {
338
369
  const quote = await getSolanaQuote(params, { env: this.options.env });
339
370
  return {
340
371
  quoteResults: quote.quoteResults,
341
- postSwapOrderFromQuote: (advancedSettings, signingStepManager) => postSolanaSwapOrderFromQuote(quote, this.options.signAndSend, advancedSettings, signingStepManager)
372
+ solanaQuote: quote.solanaQuote,
373
+ buildOrder: (advancedSettings) => buildSolanaSwapOrder(quote, advancedSettings),
374
+ postSwapOrderFromQuote: (signAndSend, advancedSettings, signingStepManager) => postSolanaSwapOrderFromQuote(quote, signAndSend, advancedSettings, signingStepManager)
342
375
  };
343
376
  }
344
377
  };
@@ -348,6 +381,7 @@ export {
348
381
  ORDER_SEED,
349
382
  SolanaTradingSdk,
350
383
  buildCreateOrderInstruction,
384
+ buildSolanaSwapOrder,
351
385
  encodeOrderIntent,
352
386
  findOrderPda,
353
387
  findSettlementStatePda,
@@ -357,5 +391,6 @@ export {
357
391
  getSolanaSettlementProgramId,
358
392
  hashOrderIntent,
359
393
  postSolanaSwapOrderFromQuote,
360
- toHex
394
+ toHex,
395
+ toOrderId
361
396
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cowprotocol/sdk-trading-solana",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "CowProtocol Solana trading: Jupiter-sourced quotes and on-chain CreateOrder posting against the Solana settlement program",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,21 +27,21 @@
27
27
  "access": "public"
28
28
  },
29
29
  "devDependencies": {
30
+ "@cow-sdk/typescript-config": "0.0.0-beta.0",
30
31
  "@types/jest": "^29.5.12",
31
32
  "@types/node": "^20.17.31",
32
33
  "tsup": "^7.2.0",
33
34
  "typescript": "^5.2.2",
34
35
  "jest": "^29.7.0",
35
36
  "jest-fetch-mock": "^3.0.3",
36
- "ts-jest": "^29.0.0",
37
- "@cow-sdk/typescript-config": "0.0.0-beta.0"
37
+ "ts-jest": "^29.0.0"
38
38
  },
39
39
  "dependencies": {
40
- "@solana/spl-token": "0.4.14",
41
- "@solana/web3.js": "1.98.4",
42
40
  "@cowprotocol/sdk-config": "2.6.0",
43
41
  "@cowprotocol/sdk-order-book": "4.0.4",
44
- "@cowprotocol/sdk-trading": "2.4.1"
42
+ "@cowprotocol/sdk-trading": "2.4.1",
43
+ "@solana/spl-token": "0.4.14",
44
+ "@solana/web3.js": "1.98.4"
45
45
  },
46
46
  "scripts": {
47
47
  "build": "tsup src/index.ts --format esm,cjs --dts",