@riftresearch/sdk 0.12.2 → 0.14.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 +47 -0
- package/dist/index.d.ts +35 -2
- package/dist/index.js +93 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,3 +67,50 @@ console.log(`Swap ID: ${swap.swapId}`)
|
|
|
67
67
|
const status = await sdk.getSwapStatus(swap.swapId)
|
|
68
68
|
console.log(`Status: ${status.status}`)
|
|
69
69
|
```
|
|
70
|
+
|
|
71
|
+
## Limit Orders
|
|
72
|
+
|
|
73
|
+
You can place limit orders with a fixed price instead of using a market quote:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { RiftSdk, Currencies } from '@riftresearch/sdk'
|
|
77
|
+
import { createPublicClient, createWalletClient, http } from 'viem'
|
|
78
|
+
import { base } from 'viem/chains'
|
|
79
|
+
import { privateKeyToAccount } from 'viem/accounts'
|
|
80
|
+
|
|
81
|
+
const account = privateKeyToAccount('0x...')
|
|
82
|
+
const publicClient = createPublicClient({ chain: base, transport: http() })
|
|
83
|
+
const walletClient = createWalletClient({ account, chain: base, transport: http() })
|
|
84
|
+
|
|
85
|
+
const sdk = new RiftSdk({ integratorName: 'my-app' })
|
|
86
|
+
|
|
87
|
+
const result = await sdk.createLimitOrder({
|
|
88
|
+
from: Currencies.Base.USDC,
|
|
89
|
+
to: Currencies.Bitcoin.BTC,
|
|
90
|
+
pricing: {
|
|
91
|
+
sellAmount: '1000000', // 1 USDC (6 decimals)
|
|
92
|
+
buyAmount: '2500', // 2500 sats
|
|
93
|
+
},
|
|
94
|
+
destinationAddress: 'bc1q...',
|
|
95
|
+
refundAddress: '0x...',
|
|
96
|
+
walletClient,
|
|
97
|
+
publicClient,
|
|
98
|
+
validUntil: Math.floor(Date.now() / 1000) + 3600, // optional: order expires in 1 hour
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
console.log(`Order ID: ${result.swapId}`)
|
|
102
|
+
console.log(`Status: ${result.status}`)
|
|
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
|
+
swapId: result.swapId,
|
|
112
|
+
walletClient,
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
console.log(`Cancel accepted: ${cancel.accepted}`)
|
|
116
|
+
```
|
package/dist/index.d.ts
CHANGED
|
@@ -241,6 +241,14 @@ interface SwapResponse {
|
|
|
241
241
|
/** Ordered list of steps the client must execute */
|
|
242
242
|
executionSteps: ExecutionStep[];
|
|
243
243
|
}
|
|
244
|
+
interface CancelOrderResponse {
|
|
245
|
+
/** The public swap ID from the router API. */
|
|
246
|
+
swapId: string;
|
|
247
|
+
/** Whether the downstream cancellation request was accepted. */
|
|
248
|
+
accepted: boolean;
|
|
249
|
+
/** Optional downstream CoW order UID for the cancelled order. */
|
|
250
|
+
cowOrderUid?: string;
|
|
251
|
+
}
|
|
244
252
|
/** Create an ERC-20 currency on an EVM chain */
|
|
245
253
|
declare function createCurrency(params: {
|
|
246
254
|
chainId: number;
|
|
@@ -383,6 +391,7 @@ interface SwapResult {
|
|
|
383
391
|
status: SwapStatus;
|
|
384
392
|
rift: RiftSwap;
|
|
385
393
|
}
|
|
394
|
+
type CancelOrderResult = CancelOrderResponse;
|
|
386
395
|
/**
|
|
387
396
|
* Function type for sending Bitcoin.
|
|
388
397
|
* Implementers provide this function to handle BTC transactions in their app.
|
|
@@ -417,7 +426,8 @@ type CreateLimitOrderOptions<chain extends Chain2 | undefined = Chain2 | undefin
|
|
|
417
426
|
destinationAddress: string;
|
|
418
427
|
refundAddress?: string;
|
|
419
428
|
approvalMode?: "full" | "partial";
|
|
420
|
-
|
|
429
|
+
/** Absolute Unix timestamp in UTC seconds when the limit order expires. Defaults to now + 1 year if not provided. */
|
|
430
|
+
validUntil?: number;
|
|
421
431
|
};
|
|
422
432
|
type ExecuteSwapOnExecuteStepCallback = (type: ExecuteSwapStepType) => void | Promise<void>;
|
|
423
433
|
type ExecuteSwapContext<chain extends Chain2 | undefined = Chain2 | undefined> = {
|
|
@@ -439,6 +449,17 @@ type ExecuteSwapOptions<chain extends Chain2 | undefined = Chain2 | undefined> =
|
|
|
439
449
|
/** Address to receive refunds if swap fails. Defaults to source wallet address */
|
|
440
450
|
refundAddress?: string;
|
|
441
451
|
};
|
|
452
|
+
type CancelOrderOptions<chain extends Chain2 | undefined = Chain2 | undefined> = {
|
|
453
|
+
swapId: string;
|
|
454
|
+
/** Viem WalletClient for signing the router-prepared cancellation typed data */
|
|
455
|
+
walletClient: WalletClient<Transport, chain, Account | undefined>;
|
|
456
|
+
/**
|
|
457
|
+
* Optional absolute Unix timestamp in UTC seconds for tee-swapper
|
|
458
|
+
* cancellation signatures. Defaults to now + 5 minutes and is ignored for
|
|
459
|
+
* direct CoW-backed cancellations.
|
|
460
|
+
*/
|
|
461
|
+
deadline?: number;
|
|
462
|
+
};
|
|
442
463
|
interface RiftSdkOptions {
|
|
443
464
|
/** Rift API URL. Defaults to production API */
|
|
444
465
|
apiUrl?: string;
|
|
@@ -483,6 +504,10 @@ declare class RiftSdk {
|
|
|
483
504
|
getQuote(params: QuoteParameters): Promise<GetQuoteResult>;
|
|
484
505
|
createLimitOrder<chain extends Chain3 | undefined = Chain3 | undefined>(options: CreateLimitOrderOptions<chain>): Promise<SwapResult>;
|
|
485
506
|
private assertPositiveIntegerString;
|
|
507
|
+
private assertValidLimitValidUntil;
|
|
508
|
+
private assertValidCancelDeadline;
|
|
509
|
+
private assertCancelWalletChain;
|
|
510
|
+
private signPreparedCancellation;
|
|
486
511
|
private executeOrderFlow;
|
|
487
512
|
/**
|
|
488
513
|
* Execute a single step from the server's execution steps.
|
|
@@ -517,6 +542,14 @@ declare class RiftSdk {
|
|
|
517
542
|
* Get the current status of a swap by its ID.
|
|
518
543
|
*/
|
|
519
544
|
getSwapStatus(swapId: string): Promise<SwapStatusResponse>;
|
|
545
|
+
/**
|
|
546
|
+
* Cancel a supported limit order through the swap router.
|
|
547
|
+
*
|
|
548
|
+
* The SDK first requests the backend-specific typed-data payload from the
|
|
549
|
+
* router, signs it with the provided wallet, and then submits the signed
|
|
550
|
+
* cancellation request back to the router.
|
|
551
|
+
*/
|
|
552
|
+
cancelOrder<chain extends Chain3 | undefined = Chain3 | undefined>(options: CancelOrderOptions<chain>): Promise<CancelOrderResult>;
|
|
520
553
|
}
|
|
521
554
|
declare function createRiftSdk(options: RiftSdkOptions): RiftSdk;
|
|
522
|
-
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 };
|
|
555
|
+
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, CancelOrderResult, CancelOrderOptions, BtcTransferStep, BtcTransferKind, BitcoinChain };
|
package/dist/index.js
CHANGED
|
@@ -226,6 +226,12 @@ function createClient(baseUrl) {
|
|
|
226
226
|
const swapId = encodeURIComponent(params.swapId);
|
|
227
227
|
return {
|
|
228
228
|
get: () => get(normalizedBaseUrl, `/order/${swapId}`),
|
|
229
|
+
cancel: {
|
|
230
|
+
prepare: {
|
|
231
|
+
post: (body) => postJson(normalizedBaseUrl, `/order/${swapId}/cancel/prepare`, body)
|
|
232
|
+
},
|
|
233
|
+
post: (body) => postJson(normalizedBaseUrl, `/order/${swapId}/cancel`, body)
|
|
234
|
+
},
|
|
229
235
|
tx: {
|
|
230
236
|
post: (body) => postJson(normalizedBaseUrl, `/order/${swapId}/tx`, body)
|
|
231
237
|
},
|
|
@@ -255,6 +261,11 @@ function createClient(baseUrl) {
|
|
|
255
261
|
var GAS_LIMIT_MULTIPLIER_NUMERATOR = 3n;
|
|
256
262
|
var GAS_LIMIT_MULTIPLIER_DENOMINATOR = 2n;
|
|
257
263
|
var GPV2_SETTLEMENT = "0x9008d19f58aabd9ed0d60971565aa8510560ab41";
|
|
264
|
+
var DEFAULT_LIMIT_VALIDITY_WINDOW_SECONDS = 365 * 24 * 60 * 60;
|
|
265
|
+
var MIN_LIMIT_VALIDITY_LEAD_TIME_SECONDS = 60;
|
|
266
|
+
var MAX_LIMIT_VALIDITY_WINDOW_SECONDS = DEFAULT_LIMIT_VALIDITY_WINDOW_SECONDS;
|
|
267
|
+
var MAX_COW_VALID_TO = 4294967295;
|
|
268
|
+
var CANCEL_AUTH_WINDOW_SECONDS = 300;
|
|
258
269
|
|
|
259
270
|
class RiftSdk {
|
|
260
271
|
riftClient;
|
|
@@ -369,8 +380,8 @@ class RiftSdk {
|
|
|
369
380
|
}
|
|
370
381
|
this.assertPositiveIntegerString(options.pricing.buyAmount, "pricing.buyAmount");
|
|
371
382
|
this.assertPositiveIntegerString(options.pricing.sellAmount, "pricing.sellAmount");
|
|
372
|
-
if (options.
|
|
373
|
-
|
|
383
|
+
if (options.validUntil !== undefined) {
|
|
384
|
+
this.assertValidLimitValidUntil(options.validUntil, "validUntil");
|
|
374
385
|
}
|
|
375
386
|
let refundAddress;
|
|
376
387
|
let senderAddress;
|
|
@@ -431,7 +442,7 @@ class RiftSdk {
|
|
|
431
442
|
destinationAddress: options.destinationAddress,
|
|
432
443
|
refundAddress,
|
|
433
444
|
...options.approvalMode ? { approvalMode: options.approvalMode } : {},
|
|
434
|
-
...typeof options.
|
|
445
|
+
...typeof options.validUntil === "number" ? { validUntil: options.validUntil } : {},
|
|
435
446
|
integratorName: this.integratorName
|
|
436
447
|
};
|
|
437
448
|
this.logDebug("creating limit order", {
|
|
@@ -450,6 +461,64 @@ class RiftSdk {
|
|
|
450
461
|
throw new Error(`${field} must be greater than zero`);
|
|
451
462
|
}
|
|
452
463
|
}
|
|
464
|
+
assertValidLimitValidUntil(validUntil, field) {
|
|
465
|
+
if (!Number.isInteger(validUntil)) {
|
|
466
|
+
throw new Error(`${field} must be an integer when provided`);
|
|
467
|
+
}
|
|
468
|
+
if (validUntil > MAX_COW_VALID_TO) {
|
|
469
|
+
throw new Error(`${field} exceeds the maximum supported Unix timestamp`);
|
|
470
|
+
}
|
|
471
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
472
|
+
const secondsUntilExpiry = validUntil - nowSeconds;
|
|
473
|
+
if (secondsUntilExpiry < MIN_LIMIT_VALIDITY_LEAD_TIME_SECONDS || secondsUntilExpiry > MAX_LIMIT_VALIDITY_WINDOW_SECONDS) {
|
|
474
|
+
throw new Error(`${field} must be between 60 seconds and 31536000 seconds in the future`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
assertValidCancelDeadline(deadline) {
|
|
478
|
+
if (!Number.isInteger(deadline)) {
|
|
479
|
+
throw new Error("deadline must be an integer when provided");
|
|
480
|
+
}
|
|
481
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
482
|
+
if (deadline < nowSeconds) {
|
|
483
|
+
throw new Error("deadline must be in the future");
|
|
484
|
+
}
|
|
485
|
+
if (deadline > nowSeconds + CANCEL_AUTH_WINDOW_SECONDS) {
|
|
486
|
+
throw new Error(`deadline must be within ${CANCEL_AUTH_WINDOW_SECONDS} seconds`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
assertCancelWalletChain(walletClient, chainId) {
|
|
490
|
+
const walletChainId = walletClient.chain?.id;
|
|
491
|
+
if (!walletChainId) {
|
|
492
|
+
throw new Error("Wallet client is missing an EVM chain configuration");
|
|
493
|
+
}
|
|
494
|
+
if (walletChainId !== chainId) {
|
|
495
|
+
throw new Error(`Wallet client chain mismatch. Expected ${chainId}, got ${walletChainId}`);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
async signPreparedCancellation(walletClient, account, preparation) {
|
|
499
|
+
if (preparation.kind === "tee_swapper") {
|
|
500
|
+
return walletClient.signTypedData({
|
|
501
|
+
account,
|
|
502
|
+
domain: preparation.typedData.domain,
|
|
503
|
+
types: preparation.typedData.types,
|
|
504
|
+
primaryType: preparation.typedData.primaryType,
|
|
505
|
+
message: {
|
|
506
|
+
swapId: preparation.typedData.message.swapId,
|
|
507
|
+
deadline: BigInt(preparation.typedData.message.deadline)
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
return walletClient.signTypedData({
|
|
512
|
+
account,
|
|
513
|
+
domain: {
|
|
514
|
+
...preparation.typedData.domain,
|
|
515
|
+
verifyingContract: preparation.typedData.domain.verifyingContract
|
|
516
|
+
},
|
|
517
|
+
types: preparation.typedData.types,
|
|
518
|
+
primaryType: preparation.typedData.primaryType,
|
|
519
|
+
message: preparation.typedData.message
|
|
520
|
+
});
|
|
521
|
+
}
|
|
453
522
|
async executeOrderFlow(params) {
|
|
454
523
|
const swapResponse = await params.createOrder();
|
|
455
524
|
this.logDebug("order created", {
|
|
@@ -830,6 +899,27 @@ class RiftSdk {
|
|
|
830
899
|
async getSwapStatus(swapId) {
|
|
831
900
|
return this.unwrapEdenResult(await this.riftClient.swap({ swapId }).get());
|
|
832
901
|
}
|
|
902
|
+
async cancelOrder(options) {
|
|
903
|
+
const walletClient = options.walletClient;
|
|
904
|
+
const account = walletClient.account;
|
|
905
|
+
if (!account) {
|
|
906
|
+
throw new Error("No account configured on wallet client");
|
|
907
|
+
}
|
|
908
|
+
const deadline = options.deadline ?? Math.floor(Date.now() / 1000) + CANCEL_AUTH_WINDOW_SECONDS;
|
|
909
|
+
this.assertValidCancelDeadline(deadline);
|
|
910
|
+
const preparation = this.unwrapEdenResult(await this.riftClient.swap({ swapId: options.swapId }).cancel.prepare.post({
|
|
911
|
+
deadline
|
|
912
|
+
}));
|
|
913
|
+
this.assertCancelWalletChain(walletClient, preparation.cancellation.chainId);
|
|
914
|
+
const signature = await this.signPreparedCancellation(walletClient, account, preparation.cancellation);
|
|
915
|
+
const request2 = {
|
|
916
|
+
authorization: {
|
|
917
|
+
signature,
|
|
918
|
+
...preparation.cancellation.kind === "tee_swapper" ? { deadline: preparation.cancellation.deadline } : {}
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
return this.unwrapEdenResult(await this.riftClient.swap({ swapId: options.swapId }).cancel.post(request2));
|
|
922
|
+
}
|
|
833
923
|
}
|
|
834
924
|
function createRiftSdk(options) {
|
|
835
925
|
return new RiftSdk(options);
|