@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 +16 -3
- package/dist/index.d.ts +91 -16
- package/dist/index.js +198 -26
- package/package.json +1 -1
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(`
|
|
64
|
+
console.log(`Order ID: ${swap.orderId}`)
|
|
65
65
|
|
|
66
66
|
// Check status
|
|
67
|
-
const status = await sdk.getSwapStatus(swap.
|
|
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.
|
|
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
|
|
136
|
-
type
|
|
137
|
-
interface
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
|
238
|
-
|
|
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 =
|
|
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
|
-
|
|
383
|
-
status:
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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
|
|
253
|
+
const orderId = encodeURIComponent(params.orderId);
|
|
227
254
|
return {
|
|
228
|
-
get: () => get(normalizedBaseUrl, `/order/${
|
|
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/${
|
|
263
|
+
post: (body) => postJson(normalizedBaseUrl, `/order/${orderId}/tx`, body)
|
|
231
264
|
},
|
|
232
265
|
"refresh-step": {
|
|
233
|
-
post: (body) => postJson(normalizedBaseUrl, `/order/${
|
|
266
|
+
post: (body) => postJson(normalizedBaseUrl, `/order/${orderId}/refresh-step`, body)
|
|
234
267
|
}
|
|
235
268
|
};
|
|
236
269
|
};
|
|
237
|
-
swap.post = (body) => postJson(normalizedBaseUrl, "/
|
|
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
|
-
|
|
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.
|
|
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({
|
|
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
|
-
|
|
606
|
+
orderId: swapResponse.orderId
|
|
503
607
|
});
|
|
504
|
-
const swap = this.unwrapEdenResult(await this.riftClient.swap({
|
|
608
|
+
const swap = this.unwrapEdenResult(await this.riftClient.swap({ orderId: swapResponse.orderId }).get());
|
|
505
609
|
this.logDebug("swap fetched", {
|
|
506
|
-
|
|
610
|
+
orderId: swapResponse.orderId,
|
|
507
611
|
status: swap.status
|
|
508
612
|
});
|
|
509
613
|
return this.buildSwapResult(swap, {
|
|
510
614
|
chained: params.chained,
|
|
511
|
-
|
|
615
|
+
riftOrderId: swapResponse.orderId
|
|
512
616
|
});
|
|
513
617
|
}
|
|
514
|
-
async executeStep(step, context,
|
|
618
|
+
async executeStep(step, context, orderId, route) {
|
|
515
619
|
switch (step.action) {
|
|
516
620
|
case "evm_call":
|
|
517
|
-
return this.executeEvmCallStep(step, context,
|
|
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,
|
|
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
|
-
|
|
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({
|
|
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
|
-
|
|
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
|
|
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
|
|
724
|
-
if (!
|
|
725
|
-
throw new Error("Missing rift
|
|
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
|
-
|
|
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(
|
|
848
|
-
return this.unwrapEdenResult(await this.riftClient.swap({
|
|
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) {
|