@cowprotocol/sdk-trading-solana 0.4.1 → 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;
@@ -176,32 +176,68 @@ declare function getSolanaQuote(params: SolanaQuoteParameters, options?: {
176
176
  solanaQuote: SolanaQuote;
177
177
  }>;
178
178
 
179
- /**
180
- * Builds the real `CreateOrder` instruction for `quote` and has the caller sign and submit it. This is
181
- * the Solana analogue of `postSwapOrderFromQuote` in `postSwapOrder.ts` — but where the EVM version signs
182
- * order data and POSTs it to the CoW order-book, Solana orders are created entirely on-chain, so this
183
- * builds a transaction instruction instead of a signed order body. `createdBy` is always `quote.intent.owner`:
184
- * a single connected wallet both authenticates and funds the order's rent.
185
- */
186
- declare function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }: {
179
+ interface SolanaSwapOrderQuote {
187
180
  quoteResults: QuoteResults;
188
181
  solanaQuote: SolanaQuote;
189
- }, 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>;
190
214
 
191
215
  interface SolanaTradingSdkOptions {
192
- signAndSend: SolanaSignAndSend;
193
216
  env?: CowEnv;
194
217
  }
195
218
  /**
196
- * Solana counterpart to `TradingSdk`. Unlike the EVM SDK, which gets its signer implicitly from a
197
- * global adapter set once at app startup, Solana has no such adapter `signAndSend` is bound at
198
- * construction instead, so callers get the same `sdk.getQuote(...)` `.postSwapOrderFromQuote()`
199
- * 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`.
200
236
  */
201
237
  declare class SolanaTradingSdk {
202
238
  private readonly options;
203
- constructor(options: SolanaTradingSdkOptions);
204
- getQuote(params: SolanaQuoteParameters): Promise<QuoteAndPost>;
239
+ constructor(options?: SolanaTradingSdkOptions);
240
+ getQuote(params: SolanaQuoteParameters): Promise<SolanaQuoteAndPost>;
205
241
  }
206
242
 
207
- 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, toOrderId };
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;
@@ -176,32 +176,68 @@ declare function getSolanaQuote(params: SolanaQuoteParameters, options?: {
176
176
  solanaQuote: SolanaQuote;
177
177
  }>;
178
178
 
179
- /**
180
- * Builds the real `CreateOrder` instruction for `quote` and has the caller sign and submit it. This is
181
- * the Solana analogue of `postSwapOrderFromQuote` in `postSwapOrder.ts` — but where the EVM version signs
182
- * order data and POSTs it to the CoW order-book, Solana orders are created entirely on-chain, so this
183
- * builds a transaction instruction instead of a signed order body. `createdBy` is always `quote.intent.owner`:
184
- * a single connected wallet both authenticates and funds the order's rent.
185
- */
186
- declare function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }: {
179
+ interface SolanaSwapOrderQuote {
187
180
  quoteResults: QuoteResults;
188
181
  solanaQuote: SolanaQuote;
189
- }, 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>;
190
214
 
191
215
  interface SolanaTradingSdkOptions {
192
- signAndSend: SolanaSignAndSend;
193
216
  env?: CowEnv;
194
217
  }
195
218
  /**
196
- * Solana counterpart to `TradingSdk`. Unlike the EVM SDK, which gets its signer implicitly from a
197
- * global adapter set once at app startup, Solana has no such adapter `signAndSend` is bound at
198
- * construction instead, so callers get the same `sdk.getQuote(...)` `.postSwapOrderFromQuote()`
199
- * 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`.
200
236
  */
201
237
  declare class SolanaTradingSdk {
202
238
  private readonly options;
203
- constructor(options: SolanaTradingSdkOptions);
204
- getQuote(params: SolanaQuoteParameters): Promise<QuoteAndPost>;
239
+ constructor(options?: SolanaTradingSdkOptions);
240
+ getQuote(params: SolanaQuoteParameters): Promise<SolanaQuoteAndPost>;
205
241
  }
206
242
 
207
- 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, toOrderId };
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,
@@ -206,9 +207,20 @@ function isJupiterOrderResponse(body, request) {
206
207
  }
207
208
 
208
209
  // src/getSolanaQuote.ts
209
- var import_web34 = require("@solana/web3.js");
210
+ var import_web35 = require("@solana/web3.js");
210
211
  var import_spl_token = require("@solana/spl-token");
211
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
212
224
  var DEFAULT_VALID_FOR_SECONDS = 30 * 60;
213
225
  var ZERO_APP_DATA = new Uint8Array(32);
214
226
  var jupiterApi = new JupiterAPI();
@@ -228,12 +240,13 @@ async function getSolanaQuote(params, options = {}) {
228
240
  if (!Number.isFinite(validForSeconds) || validForSeconds <= 0) {
229
241
  throw new Error("validForSeconds must be a finite number greater than zero");
230
242
  }
231
- const owner = new import_web34.PublicKey(ownerAddress);
232
- const receiver = new import_web34.PublicKey(receiverAddress);
233
- const sellMint = new import_web34.PublicKey(params.sellTokenAddress);
234
- const buyMint = new import_web34.PublicKey(params.buyTokenAddress);
235
- const sellTokenProgram = sellTokenProgramId ? new import_web34.PublicKey(sellTokenProgramId) : void 0;
236
- 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;
237
250
  const sellTokenAddress = sellMint.toBase58();
238
251
  const buyTokenAddress = buyMint.toBase58();
239
252
  const jupiterOrder = await jupiterApi.getOrder({
@@ -303,7 +316,10 @@ async function getSolanaQuote(params, options = {}) {
303
316
  const tradeParameters = {
304
317
  kind,
305
318
  owner: owner.toBase58(),
306
- 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(),
307
323
  sellTokenDecimals,
308
324
  buyToken: buyTokenAddress,
309
325
  buyTokenDecimals,
@@ -324,11 +340,11 @@ async function getSolanaQuote(params, options = {}) {
324
340
  return { quoteResults, solanaQuote };
325
341
  }
326
342
 
327
- // src/postSwapOrderFromQuote.ts
328
- var import_spl_token2 = require("@solana/spl-token");
343
+ // src/buildSwapOrder.ts
329
344
  var import_sdk_order_book3 = require("@cowprotocol/sdk-order-book");
330
- var import_web35 = require("@solana/web3.js");
331
- 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) {
332
348
  const intent = { ...solanaQuote.intent };
333
349
  let uid = solanaQuote.uid;
334
350
  let orderPda = solanaQuote.orderPda;
@@ -337,7 +353,7 @@ async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signA
337
353
  if (receiver) {
338
354
  intent.buyTokenAccount = (0, import_spl_token2.getAssociatedTokenAddressSync)(
339
355
  intent.buyMint,
340
- new import_web35.PublicKey(receiver),
356
+ new import_web36.PublicKey(receiver),
341
357
  false,
342
358
  solanaQuote.buyTokenProgramId
343
359
  );
@@ -357,29 +373,44 @@ async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signA
357
373
  orderPda,
358
374
  intent
359
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);
360
390
  await signingStepManager?.beforeOrderSign?.();
361
391
  const { signature } = await signAndSend(instruction);
362
392
  await signingStepManager?.afterOrderSign?.();
363
- const orderToSign = quoteResults.orderToSign;
364
393
  return {
365
- orderId: toOrderId(uid),
394
+ orderId,
366
395
  txHash: signature,
367
396
  signature,
368
- signingScheme: import_sdk_order_book3.SigningScheme.PRESIGN,
397
+ signingScheme,
369
398
  orderToSign
370
399
  };
371
400
  }
372
401
 
373
402
  // src/solanaTradingSdk.ts
374
403
  var SolanaTradingSdk = class {
375
- constructor(options) {
404
+ constructor(options = {}) {
376
405
  this.options = options;
377
406
  }
378
407
  async getQuote(params) {
379
408
  const quote = await getSolanaQuote(params, { env: this.options.env });
380
409
  return {
381
410
  quoteResults: quote.quoteResults,
382
- 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)
383
414
  };
384
415
  }
385
416
  };
@@ -390,6 +421,7 @@ var SolanaTradingSdk = class {
390
421
  ORDER_SEED,
391
422
  SolanaTradingSdk,
392
423
  buildCreateOrderInstruction,
424
+ buildSolanaSwapOrder,
393
425
  encodeOrderIntent,
394
426
  findOrderPda,
395
427
  findSettlementStatePda,
package/dist/index.mjs CHANGED
@@ -168,9 +168,20 @@ function isJupiterOrderResponse(body, request) {
168
168
  }
169
169
 
170
170
  // src/getSolanaQuote.ts
171
- import { PublicKey as PublicKey4 } from "@solana/web3.js";
171
+ import { PublicKey as PublicKey5 } from "@solana/web3.js";
172
172
  import { getAssociatedTokenAddressSync } from "@solana/spl-token";
173
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
174
185
  var DEFAULT_VALID_FOR_SECONDS = 30 * 60;
175
186
  var ZERO_APP_DATA = new Uint8Array(32);
176
187
  var jupiterApi = new JupiterAPI();
@@ -190,12 +201,13 @@ async function getSolanaQuote(params, options = {}) {
190
201
  if (!Number.isFinite(validForSeconds) || validForSeconds <= 0) {
191
202
  throw new Error("validForSeconds must be a finite number greater than zero");
192
203
  }
193
- const owner = new PublicKey4(ownerAddress);
194
- const receiver = new PublicKey4(receiverAddress);
195
- const sellMint = new PublicKey4(params.sellTokenAddress);
196
- const buyMint = new PublicKey4(params.buyTokenAddress);
197
- const sellTokenProgram = sellTokenProgramId ? new PublicKey4(sellTokenProgramId) : void 0;
198
- 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;
199
211
  const sellTokenAddress = sellMint.toBase58();
200
212
  const buyTokenAddress = buyMint.toBase58();
201
213
  const jupiterOrder = await jupiterApi.getOrder({
@@ -265,7 +277,10 @@ async function getSolanaQuote(params, options = {}) {
265
277
  const tradeParameters = {
266
278
  kind,
267
279
  owner: owner.toBase58(),
268
- 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(),
269
284
  sellTokenDecimals,
270
285
  buyToken: buyTokenAddress,
271
286
  buyTokenDecimals,
@@ -286,11 +301,11 @@ async function getSolanaQuote(params, options = {}) {
286
301
  return { quoteResults, solanaQuote };
287
302
  }
288
303
 
289
- // src/postSwapOrderFromQuote.ts
290
- import { getAssociatedTokenAddressSync as getAssociatedTokenAddressSync2 } from "@solana/spl-token";
304
+ // src/buildSwapOrder.ts
291
305
  import { SigningScheme } from "@cowprotocol/sdk-order-book";
292
- import { PublicKey as PublicKey5 } from "@solana/web3.js";
293
- 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) {
294
309
  const intent = { ...solanaQuote.intent };
295
310
  let uid = solanaQuote.uid;
296
311
  let orderPda = solanaQuote.orderPda;
@@ -299,7 +314,7 @@ async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signA
299
314
  if (receiver) {
300
315
  intent.buyTokenAccount = getAssociatedTokenAddressSync2(
301
316
  intent.buyMint,
302
- new PublicKey5(receiver),
317
+ new PublicKey6(receiver),
303
318
  false,
304
319
  solanaQuote.buyTokenProgramId
305
320
  );
@@ -319,29 +334,44 @@ async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signA
319
334
  orderPda,
320
335
  intent
321
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);
322
351
  await signingStepManager?.beforeOrderSign?.();
323
352
  const { signature } = await signAndSend(instruction);
324
353
  await signingStepManager?.afterOrderSign?.();
325
- const orderToSign = quoteResults.orderToSign;
326
354
  return {
327
- orderId: toOrderId(uid),
355
+ orderId,
328
356
  txHash: signature,
329
357
  signature,
330
- signingScheme: SigningScheme.PRESIGN,
358
+ signingScheme,
331
359
  orderToSign
332
360
  };
333
361
  }
334
362
 
335
363
  // src/solanaTradingSdk.ts
336
364
  var SolanaTradingSdk = class {
337
- constructor(options) {
365
+ constructor(options = {}) {
338
366
  this.options = options;
339
367
  }
340
368
  async getQuote(params) {
341
369
  const quote = await getSolanaQuote(params, { env: this.options.env });
342
370
  return {
343
371
  quoteResults: quote.quoteResults,
344
- 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)
345
375
  };
346
376
  }
347
377
  };
@@ -351,6 +381,7 @@ export {
351
381
  ORDER_SEED,
352
382
  SolanaTradingSdk,
353
383
  buildCreateOrderInstruction,
384
+ buildSolanaSwapOrder,
354
385
  encodeOrderIntent,
355
386
  findOrderPda,
356
387
  findSettlementStatePda,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cowprotocol/sdk-trading-solana",
3
- "version": "0.4.1",
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
- "@cowprotocol/sdk-trading": "2.4.1",
43
40
  "@cowprotocol/sdk-config": "2.6.0",
44
- "@cowprotocol/sdk-order-book": "4.0.4"
41
+ "@cowprotocol/sdk-order-book": "4.0.4",
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",