@riftresearch/sdk 0.10.0 → 0.11.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/dist/index.d.ts CHANGED
@@ -57,7 +57,6 @@ interface CalculatedInputAmount {
57
57
  /** Maximum input required after slippage (optional - not all routes provide this) */
58
58
  maximum?: U256;
59
59
  }
60
- type QuoteQuality = "fast" | "optimal";
61
60
  /**
62
61
  * A single fee component with amount in native currency and USD equivalent.
63
62
  */
@@ -175,6 +174,8 @@ interface EvmCallStep {
175
174
  spenderAddress?: string;
176
175
  /** Amount for display/verification (for approval, transfer_evm) */
177
176
  amount?: U256;
177
+ /** DEX execution venue label (for dex_swap, e.g. "spandex.paraswap") */
178
+ venue?: string;
178
179
  }
179
180
  /**
180
181
  * BTC Transfer step - send Bitcoin to an address.
@@ -296,8 +297,6 @@ interface QuoteParameters {
296
297
  * `minimum`/`maximum` bounds; direct OTC routes may ignore it.
297
298
  */
298
299
  slippageBps: number;
299
- /** Optional DEX quote quality target for DEX-backed routes. */
300
- quoteQuality?: QuoteQuality;
301
300
  /**
302
301
  * Approval amount strategy for ERC20 swaps.
303
302
  * - "full" (default): approve max uint256
@@ -402,12 +401,6 @@ interface RiftSdkOptions {
402
401
  apiUrl?: string;
403
402
  /** Enable verbose debug logging for swap execution */
404
403
  debug?: boolean;
405
- /**
406
- * Controls how EVM transactions are broadcast:
407
- * - "sdk" (default): wallet signs via `walletClient.signTransaction`, SDK simulates and broadcasts
408
- * - "wallet": wallet signs + broadcasts via `walletClient.sendTransaction`
409
- */
410
- evmBroadcastMode?: "wallet" | "sdk";
411
404
  /** Optional preflight checks before executing swaps */
412
405
  preflight?: {
413
406
  /** Check sender balance before executing EVM steps (default: true) */
@@ -421,7 +414,6 @@ declare class RiftSdk {
421
414
  private preflightCheckBalances;
422
415
  private integratorName;
423
416
  private debug;
424
- private evmBroadcastMode;
425
417
  constructor(options: RiftSdkOptions);
426
418
  private logDebug;
427
419
  private unwrapEdenResult;
@@ -462,6 +454,8 @@ declare class RiftSdk {
462
454
  private executeBtcTransferStep;
463
455
  private buildQuoteResult;
464
456
  private buildSwapResult;
457
+ private buildDexSwapRevertMessage;
458
+ private resolveDexVenueLabel;
465
459
  private assertSufficientBalance;
466
460
  private getAddress;
467
461
  private getRefundAddress;
@@ -476,4 +470,4 @@ declare class RiftSdk {
476
470
  getSwapStatus(swapId: string): Promise<SwapStatusResponse>;
477
471
  }
478
472
  declare function createRiftSdk(options: RiftSdkOptions): RiftSdk;
479
- export { getSupportedModes, detectRoute, createRiftSdk, createCurrency, TokenIdentifier, SwapStatus, SwapRouterApiError, SwapRoute, SwapResult, SwapResponse, SupportedModes, SendBitcoinFn, RiftSwap, RiftSdkOptions, RiftSdk, QuoteResult, QuoteQuality, QuoteParameters, NativeToken, GetQuoteResult, ExecutionStep, ExecutionAction, ExecuteSwapStepType, ExecuteSwapOptions, ExecuteSwapOnExecuteStepCallback, EvmChain, EvmCallStep, EvmCallKind, Erc20Token, Currency, Currencies, Chain, BtcTransferStep, BtcTransferKind, BitcoinChain };
473
+ export { getSupportedModes, detectRoute, createRiftSdk, createCurrency, TokenIdentifier, SwapStatus, SwapRouterApiError, SwapRoute, SwapResult, SwapResponse, SupportedModes, SendBitcoinFn, RiftSwap, RiftSdkOptions, RiftSdk, QuoteResult, QuoteParameters, NativeToken, GetQuoteResult, ExecutionStep, ExecutionAction, ExecuteSwapStepType, ExecuteSwapOptions, ExecuteSwapOnExecuteStepCallback, EvmChain, EvmCallStep, EvmCallKind, Erc20Token, Currency, Currencies, Chain, BtcTransferStep, BtcTransferKind, BitcoinChain };
package/dist/index.js CHANGED
@@ -164,42 +164,18 @@ function createClient(baseUrl) {
164
164
  // src/sdk.ts
165
165
  var GAS_LIMIT_MULTIPLIER_NUMERATOR = 3n;
166
166
  var GAS_LIMIT_MULTIPLIER_DENOMINATOR = 2n;
167
- var FLASHBOTS_PROTECT_RPC_URL = "https://rpc.flashbots.net";
168
- async function sendFlashbotsRawTransaction(serializedTransaction) {
169
- const response = await fetch(FLASHBOTS_PROTECT_RPC_URL, {
170
- method: "POST",
171
- headers: { "content-type": "application/json" },
172
- body: JSON.stringify({
173
- jsonrpc: "2.0",
174
- id: 1,
175
- method: "eth_sendRawTransaction",
176
- params: [serializedTransaction]
177
- })
178
- });
179
- const json = await response.json().catch(() => null);
180
- const errorMessage = typeof json?.error?.message === "string" ? json.error.message : undefined;
181
- if (!response.ok || errorMessage) {
182
- throw new Error(`Flashbots submission failed: ${errorMessage ?? `HTTP ${response.status}`}`);
183
- }
184
- if (!json || typeof json.result !== "string") {
185
- throw new Error("Flashbots submission failed: invalid response");
186
- }
187
- return json.result;
188
- }
189
167
 
190
168
  class RiftSdk {
191
169
  riftClient;
192
170
  preflightCheckBalances;
193
171
  integratorName;
194
172
  debug;
195
- evmBroadcastMode;
196
173
  constructor(options) {
197
174
  const baseUrl = (options.apiUrl ?? "https://api.rift.trade").replace(/\/$/, "");
198
175
  this.riftClient = createClient(baseUrl);
199
176
  this.preflightCheckBalances = options.preflight?.checkBalances !== false;
200
177
  this.integratorName = options.integratorName;
201
178
  this.debug = options.debug ?? false;
202
- this.evmBroadcastMode = options.evmBroadcastMode ?? "sdk";
203
179
  }
204
180
  logDebug(message, data) {
205
181
  if (!this.debug)
@@ -242,8 +218,7 @@ class RiftSdk {
242
218
  from: params.from,
243
219
  to: params.to,
244
220
  amount: params.amount,
245
- slippageBps: params.slippageBps,
246
- quoteQuality: params.quoteQuality
221
+ slippageBps: params.slippageBps
247
222
  };
248
223
  const riftQuote = this.unwrapEdenResult(await this.riftClient.quote.post(quoteRequest));
249
224
  const quote = this.buildQuoteResult(riftQuote, params);
@@ -292,7 +267,7 @@ class RiftSdk {
292
267
  kind: "kind" in step ? step.kind : undefined,
293
268
  chainId: "chainId" in step ? step.chainId : undefined
294
269
  });
295
- const result = await this.executeStep(step, context, swapResponse.swapId);
270
+ const result = await this.executeStep(step, context, swapResponse.swapId, route);
296
271
  this.logDebug("step completed", {
297
272
  stepId: step.id,
298
273
  txHash: result.txHash
@@ -324,15 +299,15 @@ class RiftSdk {
324
299
  }
325
300
  };
326
301
  }
327
- async executeStep(step, context, swapId) {
302
+ async executeStep(step, context, swapId, route) {
328
303
  switch (step.action) {
329
304
  case "evm_call":
330
- return this.executeEvmCallStep(step, context, swapId);
305
+ return this.executeEvmCallStep(step, context, swapId, route);
331
306
  case "btc_transfer":
332
307
  return this.executeBtcTransferStep(step, context);
333
308
  }
334
309
  }
335
- async executeEvmCallStep(step, context, swapId) {
310
+ async executeEvmCallStep(step, context, swapId, route) {
336
311
  const walletClient = this.requireWalletClient(context);
337
312
  const publicClient = this.requirePublicClient(context);
338
313
  const account = walletClient.account;
@@ -351,32 +326,26 @@ class RiftSdk {
351
326
  }
352
327
  }
353
328
  let effectiveStep = step;
354
- let refreshedForFinalSimulation = false;
355
- while (true) {
356
- let txRequest = {
357
- account,
358
- to: effectiveStep.to,
359
- data: effectiveStep.calldata,
360
- value: effectiveStep.value ? BigInt(effectiveStep.value) : undefined
361
- };
362
- let estimatedGas;
329
+ let txRequest = {
330
+ account,
331
+ to: effectiveStep.to,
332
+ data: effectiveStep.calldata,
333
+ value: effectiveStep.value ? BigInt(effectiveStep.value) : undefined
334
+ };
335
+ let estimatedGas;
336
+ try {
337
+ estimatedGas = await publicClient.estimateGas(txRequest);
338
+ } catch (estimateError) {
339
+ if (effectiveStep.kind !== "dex_swap") {
340
+ throw estimateError;
341
+ }
342
+ this.logDebug("estimateGas failed; attempting refresh-step", {
343
+ swapId,
344
+ stepId: effectiveStep.id,
345
+ error: estimateError instanceof Error ? estimateError.message : String(estimateError)
346
+ });
363
347
  try {
364
- estimatedGas = await publicClient.estimateGas(txRequest);
365
- } catch (estimateError) {
366
- if (effectiveStep.kind !== "dex_swap") {
367
- throw estimateError;
368
- }
369
- this.logDebug("estimateGas failed; attempting refresh-step", {
370
- swapId,
371
- stepId: effectiveStep.id,
372
- error: estimateError instanceof Error ? estimateError.message : String(estimateError)
373
- });
374
- let refreshed;
375
- try {
376
- refreshed = this.unwrapEdenResult(await this.riftClient.swap({ swapId })["refresh-step"].post({ stepId: effectiveStep.id }));
377
- } catch (refreshError) {
378
- throw new Error(`estimateGas failed for dex_swap step and refresh-step failed: ${refreshError instanceof Error ? refreshError.message : String(refreshError)}`);
379
- }
348
+ const refreshed = this.unwrapEdenResult(await this.riftClient.swap({ swapId })["refresh-step"].post({ stepId: effectiveStep.id }));
380
349
  if (!refreshed?.step) {
381
350
  throw new Error("estimateGas failed for dex_swap step and refresh-step returned no step");
382
351
  }
@@ -391,88 +360,36 @@ class RiftSdk {
391
360
  value: effectiveStep.value ? BigInt(effectiveStep.value) : undefined
392
361
  };
393
362
  estimatedGas = await publicClient.estimateGas(txRequest);
394
- }
395
- const gasLimit = (estimatedGas * GAS_LIMIT_MULTIPLIER_NUMERATOR + GAS_LIMIT_MULTIPLIER_DENOMINATOR - 1n) / GAS_LIMIT_MULTIPLIER_DENOMINATOR;
396
- this.logDebug("using buffered gas limit", {
397
- stepId: step.id,
398
- estimatedGas: estimatedGas.toString(),
399
- gasLimit: gasLimit.toString()
400
- });
401
- if (this.evmBroadcastMode === "wallet") {
402
- await context.onExecuteStep?.(effectiveStep.kind === "approval" ? "approval" : "transaction");
403
- const txHash2 = await walletClient.sendTransaction({
404
- ...txRequest,
405
- gas: gasLimit
406
- });
407
- const receipt2 = await publicClient.waitForTransactionReceipt({
408
- hash: txHash2
409
- });
410
- if (receipt2.status !== "success") {
411
- throw new Error(`EVM step transaction reverted (${effectiveStep.kind}) with hash ${txHash2}`);
412
- }
413
- return { txHash: txHash2 };
414
- }
415
- const nonce = await publicClient.getTransactionCount({
416
- address: account.address,
417
- blockTag: "pending"
418
- });
419
- const feeEstimate = await publicClient.estimateFeesPerGas().catch(() => {
420
- return;
421
- });
422
- const feeParams = {};
423
- if (feeEstimate?.maxFeePerGas !== undefined && feeEstimate?.maxPriorityFeePerGas !== undefined) {
424
- feeParams.maxFeePerGas = feeEstimate.maxFeePerGas;
425
- feeParams.maxPriorityFeePerGas = feeEstimate.maxPriorityFeePerGas;
426
- } else if (feeEstimate?.gasPrice !== undefined) {
427
- feeParams.gasPrice = feeEstimate.gasPrice;
428
- }
429
- const txToSign = {
430
- ...txRequest,
431
- gas: gasLimit,
432
- nonce,
433
- ...feeParams
434
- };
435
- await context.onExecuteStep?.(effectiveStep.kind === "approval" ? "approval" : "transaction");
436
- const serializedTransaction = await walletClient.signTransaction(txToSign);
437
- try {
438
- const { nonce: _nonce, ...callRequest } = txToSign;
439
- await publicClient.call({
440
- ...callRequest,
441
- blockTag: "pending"
442
- });
443
- } catch (callError) {
444
- if (effectiveStep.kind === "dex_swap" && !refreshedForFinalSimulation) {
445
- refreshedForFinalSimulation = true;
446
- this.logDebug("final simulation failed for dex_swap; attempting refresh-step before broadcast", {
447
- swapId,
448
- stepId: effectiveStep.id,
449
- error: callError instanceof Error ? callError.message : String(callError)
450
- });
451
- const refreshed = this.unwrapEdenResult(await this.riftClient.swap({ swapId })["refresh-step"].post({ stepId: effectiveStep.id }));
452
- if (!refreshed?.step || refreshed.step.kind !== "dex_swap") {
453
- throw new Error("refresh-step returned invalid step");
454
- }
455
- effectiveStep = refreshed.step;
456
- continue;
457
- }
458
- throw callError;
459
- }
460
- let txHash;
461
- if (effectiveStep.kind === "dex_swap" && effectiveStep.chainId === 1) {
462
- txHash = await sendFlashbotsRawTransaction(serializedTransaction);
463
- } else {
464
- txHash = await publicClient.sendRawTransaction({
465
- serializedTransaction
363
+ } catch (retrySimulationError) {
364
+ this.logDebug("dex_swap simulation failed after refresh-step attempt", {
365
+ swapId,
366
+ stepId: effectiveStep.id,
367
+ error: retrySimulationError instanceof Error ? retrySimulationError.message : String(retrySimulationError)
466
368
  });
369
+ throw new Error(this.buildDexSwapRevertMessage(route, effectiveStep.venue));
467
370
  }
468
- const receipt = await publicClient.waitForTransactionReceipt({
469
- hash: txHash
470
- });
471
- if (receipt.status !== "success") {
472
- throw new Error(`EVM step transaction reverted (${effectiveStep.kind}) with hash ${txHash}`);
371
+ }
372
+ const gasLimit = (estimatedGas * GAS_LIMIT_MULTIPLIER_NUMERATOR + GAS_LIMIT_MULTIPLIER_DENOMINATOR - 1n) / GAS_LIMIT_MULTIPLIER_DENOMINATOR;
373
+ this.logDebug("using buffered gas limit", {
374
+ stepId: step.id,
375
+ estimatedGas: estimatedGas.toString(),
376
+ gasLimit: gasLimit.toString()
377
+ });
378
+ await context.onExecuteStep?.(effectiveStep.kind === "approval" ? "approval" : "transaction");
379
+ const txHash = await walletClient.sendTransaction({
380
+ ...txRequest,
381
+ gas: gasLimit
382
+ });
383
+ const receipt = await publicClient.waitForTransactionReceipt({
384
+ hash: txHash
385
+ });
386
+ if (receipt.status !== "success") {
387
+ if (effectiveStep.kind === "dex_swap") {
388
+ throw new Error(this.buildDexSwapRevertMessage(route, effectiveStep.venue, txHash));
473
389
  }
474
- return { txHash };
390
+ throw new Error(`EVM step transaction reverted (${effectiveStep.kind}) with hash ${txHash}`);
475
391
  }
392
+ return { txHash };
476
393
  }
477
394
  async executeBtcTransferStep(step, context) {
478
395
  const sendBitcoin = this.requireSendBitcoin(context);
@@ -515,6 +432,17 @@ class RiftSdk {
515
432
  rift: swap
516
433
  };
517
434
  }
435
+ buildDexSwapRevertMessage(route, venue, txHash) {
436
+ const venueLabel = this.resolveDexVenueLabel(venue);
437
+ const baseMessage = route?.type === "dex_then_rift" ? `The pre-swap into cbBTC failed on ${venueLabel}; consider increasing slippage tolerance.` : `The DEX swap failed on ${venueLabel}; consider increasing slippage tolerance.`;
438
+ return txHash ? `${baseMessage} Transaction hash: ${txHash}` : baseMessage;
439
+ }
440
+ resolveDexVenueLabel(venue) {
441
+ if (typeof venue !== "string")
442
+ return "the selected venue";
443
+ const trimmed = venue.trim();
444
+ return trimmed.length > 0 ? trimmed : "the selected venue";
445
+ }
518
446
  async assertSufficientBalance(currency, amount, context) {
519
447
  if (currency.chain.kind !== "EVM")
520
448
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riftresearch/sdk",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "SDK for swapping between bitcoin and evm chains",
5
5
  "license": "MIT",
6
6
  "files": [