@riftresearch/sdk 0.13.0 → 0.15.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
@@ -61,10 +61,10 @@ const swap = await executeSwap({
61
61
  // Call your bitcoin wallet's transfer function here
62
62
  },
63
63
  })
64
- console.log(`Swap ID: ${swap.swapId}`)
64
+ console.log(`Order ID: ${swap.orderId}`)
65
65
 
66
66
  // Check status
67
- const status = await sdk.getSwapStatus(swap.swapId)
67
+ const status = await sdk.getSwapStatus(swap.orderId)
68
68
  console.log(`Status: ${status.status}`)
69
69
  ```
70
70
 
@@ -98,6 +98,19 @@ const result = await sdk.createLimitOrder({
98
98
  validUntil: Math.floor(Date.now() / 1000) + 3600, // optional: order expires in 1 hour
99
99
  })
100
100
 
101
- console.log(`Order ID: ${result.swapId}`)
101
+ console.log(`Order ID: ${result.orderId}`)
102
102
  console.log(`Status: ${result.status}`)
103
103
  ```
104
+
105
+ For supported limit orders, you can later cancel the order. The SDK will fetch the
106
+ correct typed-data payload from the router, sign it with your wallet, and submit the
107
+ cancellation:
108
+
109
+ ```ts
110
+ const cancel = await sdk.cancelOrder({
111
+ orderId: result.orderId,
112
+ walletClient,
113
+ })
114
+
115
+ console.log(`Cancel accepted: ${cancel.accepted}`)
116
+ ```
package/dist/index.d.ts CHANGED
@@ -132,14 +132,57 @@ interface LimitPricing {
132
132
  buyAmount: U256;
133
133
  sellAmount: U256;
134
134
  }
135
- declare const SWAP_STATUSES: readonly ["waiting_for_deposit", "deposit_confirming", "initiating_payout", "confirming_payout", "swap_complete", "refunding_user", "failed"];
136
- type SwapStatus = (typeof SWAP_STATUSES)[number];
137
- interface SwapStatusResponse {
138
- status: SwapStatus;
139
- destinationAddress: Address;
140
- payoutTransaction?: TxHash;
141
- depositTransaction?: TxHash;
142
- quote: QuoteResponse;
135
+ declare const ANALYTICS_ORDER_STATUSES: readonly ["unfilled", "filled", "cancelled", "settled", "refunded", "failed"];
136
+ type AnalyticsOrderStatus = (typeof ANALYTICS_ORDER_STATUSES)[number];
137
+ interface ExternalDexStatus {
138
+ provider: string | null;
139
+ status: string | null;
140
+ }
141
+ interface TeeOtcStatus {
142
+ status: string | null;
143
+ }
144
+ interface TeeSwapperStatus {
145
+ status: string | null;
146
+ orderStatus: string | null;
147
+ refundState: string | null;
148
+ }
149
+ interface AnalyticsOrderResponse {
150
+ orderId: string;
151
+ routeType: string;
152
+ orderType: string | null;
153
+ provider: string | null;
154
+ path: string[] | null;
155
+ status: AnalyticsOrderStatus;
156
+ senderAddress: Address | null;
157
+ destinationAddress: Address | null;
158
+ refundAddress: Address | null;
159
+ otcSwapId: string | null;
160
+ swapperSwapId: string | null;
161
+ cowOrderUid: string | null;
162
+ sellCurrency: Currency | null;
163
+ buyCurrency: Currency | null;
164
+ quoteMode: "exact_input" | "exact_output" | null;
165
+ quotedSellAmount: U256 | null;
166
+ quotedBuyAmount: U256 | null;
167
+ quotedMinimumBuyAmount: U256 | null;
168
+ quotedMaximumSellAmount: U256 | null;
169
+ executedSellAmount: U256 | null;
170
+ executedBuyAmount: U256 | null;
171
+ executedFeeAmount: U256 | null;
172
+ depositTxHash: TxHash | null;
173
+ payoutTxHash: TxHash | null;
174
+ refundTxHash: TxHash | null;
175
+ rawRouterJson: Record<string, unknown> | null;
176
+ rawExternalDEXOrderJson: Record<string, unknown> | null;
177
+ rawOtcJson: Record<string, unknown> | null;
178
+ rawSwapperJson: Record<string, unknown> | null;
179
+ externalDexStatus: ExternalDexStatus | null;
180
+ teeOtcStatus: TeeOtcStatus | null;
181
+ teeSwapperStatus: TeeSwapperStatus | null;
182
+ createdAt: string;
183
+ updatedAt: string;
184
+ lastSourceUpdateAt: string | null;
185
+ terminalAt: string | null;
143
186
  }
144
187
  interface ErrorResponse {
145
188
  error: string;
@@ -234,13 +277,21 @@ type ExecutionStep = EvmCallStep | Eip712SignStep | BtcTransferStep;
234
277
  * Swap response with execution steps that the client must execute.
235
278
  */
236
279
  interface SwapResponse {
237
- /** The Rift swap ID */
238
- swapId: string;
280
+ /** The Rift order ID */
281
+ orderId: string;
239
282
  /** Normalized quote matching /quote response */
240
283
  quote: QuoteResponse;
241
284
  /** Ordered list of steps the client must execute */
242
285
  executionSteps: ExecutionStep[];
243
286
  }
287
+ interface CancelOrderResponse {
288
+ /** The public order ID from the router API. */
289
+ orderId: string;
290
+ /** Whether the downstream cancellation request was accepted. */
291
+ accepted: boolean;
292
+ /** Optional downstream CoW order UID for the cancelled order. */
293
+ cowOrderUid?: string;
294
+ }
244
295
  /** Create an ERC-20 currency on an EVM chain */
245
296
  declare function createCurrency(params: {
246
297
  chainId: number;
@@ -314,7 +365,7 @@ declare function getSupportedModes(from: Currency, to: Currency): SupportedModes
314
365
  import { Chain as Chain3 } from "viem";
315
366
  import { Account, PublicClient, Transport, WalletClient } from "viem";
316
367
  import { Chain as Chain2 } from "viem/chains";
317
- type RiftSwap = SwapStatusResponse;
368
+ type RiftSwap = AnalyticsOrderResponse;
318
369
  interface QuoteParameters {
319
370
  /** The currency to swap from */
320
371
  from: Currency;
@@ -379,10 +430,11 @@ interface GetQuoteResult {
379
430
  executeSwap: <chain extends Chain2 | undefined = Chain2 | undefined>(context: ExecuteSwapOptions<chain>) => Promise<SwapResult>;
380
431
  }
381
432
  interface SwapResult {
382
- swapId: string;
383
- status: SwapStatus;
433
+ orderId: string;
434
+ status: AnalyticsOrderStatus;
384
435
  rift: RiftSwap;
385
436
  }
437
+ type CancelOrderResult = CancelOrderResponse;
386
438
  /**
387
439
  * Function type for sending Bitcoin.
388
440
  * Implementers provide this function to handle BTC transactions in their app.
@@ -440,6 +492,17 @@ type ExecuteSwapOptions<chain extends Chain2 | undefined = Chain2 | undefined> =
440
492
  /** Address to receive refunds if swap fails. Defaults to source wallet address */
441
493
  refundAddress?: string;
442
494
  };
495
+ type CancelOrderOptions<chain extends Chain2 | undefined = Chain2 | undefined> = {
496
+ orderId: string;
497
+ /** Viem WalletClient for signing the router-prepared cancellation typed data */
498
+ walletClient: WalletClient<Transport, chain, Account | undefined>;
499
+ /**
500
+ * Optional absolute Unix timestamp in UTC seconds for tee-swapper
501
+ * cancellation signatures. Defaults to now + 5 minutes and is ignored for
502
+ * direct CoW-backed cancellations.
503
+ */
504
+ deadline?: number;
505
+ };
443
506
  interface RiftSdkOptions {
444
507
  /** Rift API URL. Defaults to production API */
445
508
  apiUrl?: string;
@@ -485,6 +548,9 @@ declare class RiftSdk {
485
548
  createLimitOrder<chain extends Chain3 | undefined = Chain3 | undefined>(options: CreateLimitOrderOptions<chain>): Promise<SwapResult>;
486
549
  private assertPositiveIntegerString;
487
550
  private assertValidLimitValidUntil;
551
+ private assertValidCancelDeadline;
552
+ private assertCancelWalletChain;
553
+ private signPreparedCancellation;
488
554
  private executeOrderFlow;
489
555
  /**
490
556
  * Execute a single step from the server's execution steps.
@@ -506,6 +572,7 @@ declare class RiftSdk {
506
572
  private buildDexSwapRevertMessage;
507
573
  private resolveDexVenueLabel;
508
574
  private shouldReportStepResult;
575
+ private deriveCowSwapEthFlowOrderId;
509
576
  private getCowSwapApiBase;
510
577
  private assertSufficientBalance;
511
578
  private getAddress;
@@ -516,9 +583,17 @@ declare class RiftSdk {
516
583
  private requireWalletClient;
517
584
  private requireSendBitcoin;
518
585
  /**
519
- * Get the current status of a swap by its ID.
586
+ * Get the current status of an order by its ID.
587
+ */
588
+ getSwapStatus(orderId: string): Promise<AnalyticsOrderResponse>;
589
+ /**
590
+ * Cancel a supported limit order through the swap router.
591
+ *
592
+ * The SDK first requests the backend-specific typed-data payload from the
593
+ * router, signs it with the provided wallet, and then submits the signed
594
+ * cancellation request back to the router.
520
595
  */
521
- getSwapStatus(swapId: string): Promise<SwapStatusResponse>;
596
+ cancelOrder<chain extends Chain3 | undefined = Chain3 | undefined>(options: CancelOrderOptions<chain>): Promise<CancelOrderResult>;
522
597
  }
523
598
  declare function createRiftSdk(options: RiftSdkOptions): RiftSdk;
524
- export { getSupportedModes, detectRoute, createRiftSdk, createCurrency, TokenIdentifier, SwapStatus, SwapRouterApiError, SwapRoute, SwapResult, SwapResponse, SupportedModes, SendBitcoinFn, RiftSwap, RiftSdkOptions, RiftSdk, QuoteResult, QuoteParameters, NativeToken, LimitPricing, GetQuoteResult, ExecutionStep, ExecutionAction, ExecuteSwapStepType, ExecuteSwapOptions, ExecuteSwapOnExecuteStepCallback, EvmChain, EvmCallStep, EvmCallKind, Erc20Token, Currency, Currencies, CreateLimitOrderOptions, Chain, BtcTransferStep, BtcTransferKind, BitcoinChain };
599
+ export { getSupportedModes, detectRoute, createRiftSdk, createCurrency, TokenIdentifier, AnalyticsOrderStatus as SwapStatus, SwapRouterApiError, SwapRoute, SwapResult, SwapResponse, SupportedModes, SendBitcoinFn, RiftSwap, RiftSdkOptions, RiftSdk, QuoteResult, QuoteParameters, NativeToken, LimitPricing, GetQuoteResult, ExecutionStep, ExecutionAction, ExecuteSwapStepType, ExecuteSwapOptions, ExecuteSwapOnExecuteStepCallback, EvmChain, EvmCallStep, EvmCallKind, Erc20Token, Currency, Currencies, CreateLimitOrderOptions, Chain, CancelOrderResult, CancelOrderOptions, BtcTransferStep, BtcTransferKind, BitcoinChain };
package/dist/index.js CHANGED
@@ -85,6 +85,29 @@ var Currencies = {
85
85
  }
86
86
  }
87
87
  };
88
+ // ../common/src/cowswap.ts
89
+ var COW_SWAP_ORDER_UID_REGEX = /^0x[0-9a-fA-F]{112}$/;
90
+ var COW_SWAP_ORDER_HASH_REGEX = /^0x[0-9a-fA-F]{64}$/;
91
+ var EVM_ADDRESS_REGEX = /^0x[0-9a-fA-F]{40}$/;
92
+ var COW_SWAP_ETH_FLOW_UID_VALID_TO = 4294967295;
93
+ function isCowSwapOrderUid(value) {
94
+ return COW_SWAP_ORDER_UID_REGEX.test(value);
95
+ }
96
+ function deriveCowSwapOrderUid(orderHash, owner, validTo) {
97
+ if (!COW_SWAP_ORDER_HASH_REGEX.test(orderHash)) {
98
+ throw new Error(`Invalid CowSwap order hash: ${orderHash}`);
99
+ }
100
+ if (!EVM_ADDRESS_REGEX.test(owner)) {
101
+ throw new Error(`Invalid CowSwap order owner address: ${owner}`);
102
+ }
103
+ if (!Number.isInteger(validTo) || validTo < 0 || validTo > COW_SWAP_ETH_FLOW_UID_VALID_TO) {
104
+ throw new Error(`Invalid CowSwap order validTo: ${validTo}`);
105
+ }
106
+ return `0x${orderHash.slice(2).toLowerCase()}${owner.slice(2).toLowerCase()}${validTo.toString(16).padStart(8, "0")}`;
107
+ }
108
+ function deriveCowSwapEthFlowOrderUid(orderHash, ethFlowContract) {
109
+ return deriveCowSwapOrderUid(orderHash, ethFlowContract, COW_SWAP_ETH_FLOW_UID_VALID_TO);
110
+ }
88
111
  // src/errors.ts
89
112
  class SwapRouterApiError extends Error {
90
113
  status;
@@ -153,7 +176,11 @@ function getSupportedModes(from, to) {
153
176
  return { exactInput: true, exactOutput: false };
154
177
  }
155
178
  // src/sdk.ts
156
- import { erc20Abi, isAddress } from "viem";
179
+ import {
180
+ decodeFunctionResult,
181
+ erc20Abi,
182
+ isAddress
183
+ } from "viem";
157
184
 
158
185
  // src/client.ts
159
186
  async function request(baseUrl, path, init) {
@@ -223,18 +250,24 @@ function postJson(baseUrl, path, body) {
223
250
  function createClient(baseUrl) {
224
251
  const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
225
252
  const swap = (params) => {
226
- const swapId = encodeURIComponent(params.swapId);
253
+ const orderId = encodeURIComponent(params.orderId);
227
254
  return {
228
- get: () => get(normalizedBaseUrl, `/order/${swapId}`),
255
+ get: () => get(normalizedBaseUrl, `/order/${orderId}`),
256
+ cancel: {
257
+ prepare: {
258
+ post: (body) => postJson(normalizedBaseUrl, `/order/${orderId}/cancel/prepare`, body)
259
+ },
260
+ post: (body) => postJson(normalizedBaseUrl, `/order/${orderId}/cancel`, body)
261
+ },
229
262
  tx: {
230
- post: (body) => postJson(normalizedBaseUrl, `/order/${swapId}/tx`, body)
263
+ post: (body) => postJson(normalizedBaseUrl, `/order/${orderId}/tx`, body)
231
264
  },
232
265
  "refresh-step": {
233
- post: (body) => postJson(normalizedBaseUrl, `/order/${swapId}/refresh-step`, body)
266
+ post: (body) => postJson(normalizedBaseUrl, `/order/${orderId}/refresh-step`, body)
234
267
  }
235
268
  };
236
269
  };
237
- swap.post = (body) => postJson(normalizedBaseUrl, "/swap", body);
270
+ swap.post = (body) => postJson(normalizedBaseUrl, "/order/market", body);
238
271
  const market = {
239
272
  post: (body) => postJson(normalizedBaseUrl, "/order/market", body)
240
273
  };
@@ -259,6 +292,32 @@ var DEFAULT_LIMIT_VALIDITY_WINDOW_SECONDS = 365 * 24 * 60 * 60;
259
292
  var MIN_LIMIT_VALIDITY_LEAD_TIME_SECONDS = 60;
260
293
  var MAX_LIMIT_VALIDITY_WINDOW_SECONDS = DEFAULT_LIMIT_VALIDITY_WINDOW_SECONDS;
261
294
  var MAX_COW_VALID_TO = 4294967295;
295
+ var CANCEL_AUTH_WINDOW_SECONDS = 300;
296
+ var ETHFLOW_ABI = [
297
+ {
298
+ name: "createOrder",
299
+ type: "function",
300
+ stateMutability: "payable",
301
+ inputs: [
302
+ {
303
+ name: "order",
304
+ type: "tuple",
305
+ components: [
306
+ { name: "buyToken", type: "address" },
307
+ { name: "receiver", type: "address" },
308
+ { name: "sellAmount", type: "uint256" },
309
+ { name: "buyAmount", type: "uint256" },
310
+ { name: "appData", type: "bytes32" },
311
+ { name: "feeAmount", type: "uint256" },
312
+ { name: "validTo", type: "uint32" },
313
+ { name: "partiallyFillable", type: "bool" },
314
+ { name: "quoteId", type: "int64" }
315
+ ]
316
+ }
317
+ ],
318
+ outputs: [{ name: "orderHash", type: "bytes32" }]
319
+ }
320
+ ];
262
321
 
263
322
  class RiftSdk {
264
323
  riftClient;
@@ -467,10 +526,55 @@ class RiftSdk {
467
526
  throw new Error(`${field} must be between 60 seconds and 31536000 seconds in the future`);
468
527
  }
469
528
  }
529
+ assertValidCancelDeadline(deadline) {
530
+ if (!Number.isInteger(deadline)) {
531
+ throw new Error("deadline must be an integer when provided");
532
+ }
533
+ const nowSeconds = Math.floor(Date.now() / 1000);
534
+ if (deadline < nowSeconds) {
535
+ throw new Error("deadline must be in the future");
536
+ }
537
+ if (deadline > nowSeconds + CANCEL_AUTH_WINDOW_SECONDS) {
538
+ throw new Error(`deadline must be within ${CANCEL_AUTH_WINDOW_SECONDS} seconds`);
539
+ }
540
+ }
541
+ assertCancelWalletChain(walletClient, chainId) {
542
+ const walletChainId = walletClient.chain?.id;
543
+ if (!walletChainId) {
544
+ throw new Error("Wallet client is missing an EVM chain configuration");
545
+ }
546
+ if (walletChainId !== chainId) {
547
+ throw new Error(`Wallet client chain mismatch. Expected ${chainId}, got ${walletChainId}`);
548
+ }
549
+ }
550
+ async signPreparedCancellation(walletClient, account, preparation) {
551
+ if (preparation.kind === "tee_swapper") {
552
+ return walletClient.signTypedData({
553
+ account,
554
+ domain: preparation.typedData.domain,
555
+ types: preparation.typedData.types,
556
+ primaryType: preparation.typedData.primaryType,
557
+ message: {
558
+ swapId: preparation.typedData.message.swapId,
559
+ deadline: BigInt(preparation.typedData.message.deadline)
560
+ }
561
+ });
562
+ }
563
+ return walletClient.signTypedData({
564
+ account,
565
+ domain: {
566
+ ...preparation.typedData.domain,
567
+ verifyingContract: preparation.typedData.domain.verifyingContract
568
+ },
569
+ types: preparation.typedData.types,
570
+ primaryType: preparation.typedData.primaryType,
571
+ message: preparation.typedData.message
572
+ });
573
+ }
470
574
  async executeOrderFlow(params) {
471
575
  const swapResponse = await params.createOrder();
472
576
  this.logDebug("order created", {
473
- swapId: swapResponse.swapId,
577
+ orderId: swapResponse.orderId,
474
578
  steps: swapResponse.executionSteps.length
475
579
  });
476
580
  this.assertEvmChainMatchForSteps(swapResponse.executionSteps, params.context);
@@ -481,7 +585,7 @@ class RiftSdk {
481
585
  kind: "kind" in step ? step.kind : undefined,
482
586
  chainId: "chainId" in step ? step.chainId : undefined
483
587
  });
484
- const result = await this.executeStep(step, params.context, swapResponse.swapId, params.route);
588
+ const result = await this.executeStep(step, params.context, swapResponse.orderId, params.route);
485
589
  this.logDebug("step completed", {
486
590
  stepId: step.id,
487
591
  txHash: result.txHash,
@@ -492,36 +596,37 @@ class RiftSdk {
492
596
  stepId: step.id,
493
597
  kind: "kind" in step ? step.kind : undefined
494
598
  });
495
- this.unwrapEdenResult(await this.riftClient.swap({ swapId: swapResponse.swapId }).tx.post({
599
+ this.unwrapEdenResult(await this.riftClient.swap({ orderId: swapResponse.orderId }).tx.post({
496
600
  stepId: step.id,
497
601
  ...result
498
602
  }));
499
603
  }
500
604
  }
501
605
  this.logDebug("fetching swap status", {
502
- swapId: swapResponse.swapId
606
+ orderId: swapResponse.orderId
503
607
  });
504
- const swap = this.unwrapEdenResult(await this.riftClient.swap({ swapId: swapResponse.swapId }).get());
608
+ const swap = this.unwrapEdenResult(await this.riftClient.swap({ orderId: swapResponse.orderId }).get());
505
609
  this.logDebug("swap fetched", {
506
- swapId: swapResponse.swapId,
610
+ orderId: swapResponse.orderId,
507
611
  status: swap.status
508
612
  });
509
613
  return this.buildSwapResult(swap, {
510
614
  chained: params.chained,
511
- riftSwapId: swapResponse.swapId
615
+ riftOrderId: swapResponse.orderId
512
616
  });
513
617
  }
514
- async executeStep(step, context, swapId, route) {
618
+ async executeStep(step, context, orderId, route) {
515
619
  switch (step.action) {
516
620
  case "evm_call":
517
- return this.executeEvmCallStep(step, context, swapId, route);
621
+ return this.executeEvmCallStep(step, context, orderId, route);
518
622
  case "eip712_sign":
519
623
  return this.executeEip712SignStep(step, context);
520
624
  case "btc_transfer":
521
625
  return this.executeBtcTransferStep(step, context);
522
626
  }
627
+ throw new Error(`Unsupported execution step action: ${String(step)}`);
523
628
  }
524
- async executeEvmCallStep(step, context, swapId, route) {
629
+ async executeEvmCallStep(step, context, orderId, route) {
525
630
  const walletClient = this.requireWalletClient(context);
526
631
  const publicClient = this.requirePublicClient(context);
527
632
  const account = walletClient.account;
@@ -554,12 +659,12 @@ class RiftSdk {
554
659
  throw estimateError;
555
660
  }
556
661
  this.logDebug("estimateGas failed; attempting refresh-step", {
557
- swapId,
662
+ orderId,
558
663
  stepId: effectiveStep.id,
559
664
  error: estimateError instanceof Error ? estimateError.message : String(estimateError)
560
665
  });
561
666
  try {
562
- const refreshed = this.unwrapEdenResult(await this.riftClient.swap({ swapId })["refresh-step"].post({ stepId: effectiveStep.id }));
667
+ const refreshed = this.unwrapEdenResult(await this.riftClient.swap({ orderId })["refresh-step"].post({ stepId: effectiveStep.id }));
563
668
  if (!refreshed?.step) {
564
669
  throw new Error("estimateGas failed for dex_swap step and refresh-step returned no step");
565
670
  }
@@ -576,7 +681,7 @@ class RiftSdk {
576
681
  estimatedGas = await publicClient.estimateGas(txRequest);
577
682
  } catch (retrySimulationError) {
578
683
  this.logDebug("dex_swap simulation failed after refresh-step attempt", {
579
- swapId,
684
+ orderId,
580
685
  stepId: effectiveStep.id,
581
686
  error: retrySimulationError instanceof Error ? retrySimulationError.message : String(retrySimulationError)
582
687
  });
@@ -589,6 +694,7 @@ class RiftSdk {
589
694
  estimatedGas: estimatedGas.toString(),
590
695
  gasLimit: gasLimit.toString()
591
696
  });
697
+ const cowswapOrderId = effectiveStep.kind === "cowswap_eth_order" ? await this.deriveCowSwapEthFlowOrderId(effectiveStep, publicClient, account.address) : undefined;
592
698
  await context.onExecuteStep?.(effectiveStep.kind === "approval" ? "approval" : "transaction");
593
699
  const txHash = await walletClient.sendTransaction({
594
700
  ...txRequest,
@@ -603,7 +709,10 @@ class RiftSdk {
603
709
  }
604
710
  throw new Error(`EVM step transaction reverted (${effectiveStep.kind}) with hash ${txHash}`);
605
711
  }
606
- return step.kind === "cowswap_eth_order" ? { txHash, cowswapOrderId: txHash } : { txHash };
712
+ return {
713
+ txHash,
714
+ ...cowswapOrderId ? { cowswapOrderId } : {}
715
+ };
607
716
  }
608
717
  async executeEip712SignStep(step, context) {
609
718
  const walletClient = this.requireWalletClient(context);
@@ -687,6 +796,9 @@ class RiftSdk {
687
796
  if (!orderId) {
688
797
  throw new Error("CowSwap order submission succeeded but returned no order id");
689
798
  }
799
+ if (!isCowSwapOrderUid(orderId)) {
800
+ throw new Error(`CowSwap order submission returned a non-UID identifier: ${orderId}`);
801
+ }
690
802
  return { cowswapOrderId: orderId };
691
803
  }
692
804
  async executeBtcTransferStep(step, context) {
@@ -720,12 +832,12 @@ class RiftSdk {
720
832
  }
721
833
  }
722
834
  buildSwapResult(swap, options) {
723
- const riftSwapId = options?.riftSwapId;
724
- if (!riftSwapId) {
725
- throw new Error("Missing rift swap id for swap result.");
835
+ const riftOrderId = options?.riftOrderId;
836
+ if (!riftOrderId) {
837
+ throw new Error("Missing rift order id for swap result.");
726
838
  }
727
839
  return {
728
- swapId: riftSwapId,
840
+ orderId: riftOrderId,
729
841
  status: swap.status,
730
842
  rift: swap
731
843
  };
@@ -750,6 +862,45 @@ class RiftSdk {
750
862
  }
751
863
  return false;
752
864
  }
865
+ async deriveCowSwapEthFlowOrderId(step, publicClient, accountAddress) {
866
+ this.logDebug("deriving CowSwap EthFlow order uid", {
867
+ stepId: step.id,
868
+ chainId: step.chainId,
869
+ to: step.to
870
+ });
871
+ let responseData;
872
+ try {
873
+ const response = await publicClient.call({
874
+ account: accountAddress,
875
+ to: step.to,
876
+ data: step.calldata,
877
+ value: step.value ? BigInt(step.value) : undefined
878
+ });
879
+ responseData = response.data;
880
+ } catch (error) {
881
+ throw new Error(`Failed to derive CowSwap EthFlow order id before submission: ${error instanceof Error ? error.message : String(error)}`);
882
+ }
883
+ if (!responseData) {
884
+ throw new Error("Failed to derive CowSwap EthFlow order id before submission: empty call result.");
885
+ }
886
+ let orderHash;
887
+ try {
888
+ orderHash = decodeFunctionResult({
889
+ abi: ETHFLOW_ABI,
890
+ functionName: "createOrder",
891
+ data: responseData
892
+ });
893
+ } catch (error) {
894
+ throw new Error(`Failed to decode CowSwap EthFlow order hash: ${error instanceof Error ? error.message : String(error)}`);
895
+ }
896
+ const cowswapOrderId = deriveCowSwapEthFlowOrderUid(orderHash, step.to);
897
+ this.logDebug("derived CowSwap EthFlow order uid", {
898
+ stepId: step.id,
899
+ chainId: step.chainId,
900
+ cowswapOrderId
901
+ });
902
+ return cowswapOrderId;
903
+ }
753
904
  getCowSwapApiBase(chainId) {
754
905
  if (chainId === 1)
755
906
  return "https://api.cow.fi/mainnet";
@@ -844,8 +995,29 @@ class RiftSdk {
844
995
  }
845
996
  return context.sendBitcoin;
846
997
  }
847
- async getSwapStatus(swapId) {
848
- return this.unwrapEdenResult(await this.riftClient.swap({ swapId }).get());
998
+ async getSwapStatus(orderId) {
999
+ return this.unwrapEdenResult(await this.riftClient.swap({ orderId }).get());
1000
+ }
1001
+ async cancelOrder(options) {
1002
+ const walletClient = options.walletClient;
1003
+ const account = walletClient.account;
1004
+ if (!account) {
1005
+ throw new Error("No account configured on wallet client");
1006
+ }
1007
+ const deadline = options.deadline ?? Math.floor(Date.now() / 1000) + CANCEL_AUTH_WINDOW_SECONDS;
1008
+ this.assertValidCancelDeadline(deadline);
1009
+ const preparation = this.unwrapEdenResult(await this.riftClient.swap({ orderId: options.orderId }).cancel.prepare.post({
1010
+ deadline
1011
+ }));
1012
+ this.assertCancelWalletChain(walletClient, preparation.cancellation.chainId);
1013
+ const signature = await this.signPreparedCancellation(walletClient, account, preparation.cancellation);
1014
+ const request2 = {
1015
+ authorization: {
1016
+ signature,
1017
+ ...preparation.cancellation.kind === "tee_swapper" ? { deadline: preparation.cancellation.deadline } : {}
1018
+ }
1019
+ };
1020
+ return this.unwrapEdenResult(await this.riftClient.swap({ orderId: options.orderId }).cancel.post(request2));
849
1021
  }
850
1022
  }
851
1023
  function createRiftSdk(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riftresearch/sdk",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "SDK for swapping between bitcoin and evm chains",
5
5
  "license": "MIT",
6
6
  "files": [