@riftresearch/sdk 0.9.0 → 0.10.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
@@ -402,6 +402,12 @@ interface RiftSdkOptions {
402
402
  apiUrl?: string;
403
403
  /** Enable verbose debug logging for swap execution */
404
404
  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";
405
411
  /** Optional preflight checks before executing swaps */
406
412
  preflight?: {
407
413
  /** Check sender balance before executing EVM steps (default: true) */
@@ -415,6 +421,7 @@ declare class RiftSdk {
415
421
  private preflightCheckBalances;
416
422
  private integratorName;
417
423
  private debug;
424
+ private evmBroadcastMode;
418
425
  constructor(options: RiftSdkOptions);
419
426
  private logDebug;
420
427
  private unwrapEdenResult;
package/dist/index.js CHANGED
@@ -164,18 +164,42 @@ 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
+ }
167
189
 
168
190
  class RiftSdk {
169
191
  riftClient;
170
192
  preflightCheckBalances;
171
193
  integratorName;
172
194
  debug;
195
+ evmBroadcastMode;
173
196
  constructor(options) {
174
197
  const baseUrl = (options.apiUrl ?? "https://api.rift.trade").replace(/\/$/, "");
175
198
  this.riftClient = createClient(baseUrl);
176
199
  this.preflightCheckBalances = options.preflight?.checkBalances !== false;
177
200
  this.integratorName = options.integratorName;
178
201
  this.debug = options.debug ?? false;
202
+ this.evmBroadcastMode = options.evmBroadcastMode ?? "sdk";
179
203
  }
180
204
  logDebug(message, data) {
181
205
  if (!this.debug)
@@ -327,63 +351,128 @@ class RiftSdk {
327
351
  }
328
352
  }
329
353
  let effectiveStep = step;
330
- let txRequest = {
331
- account,
332
- to: effectiveStep.to,
333
- data: effectiveStep.calldata,
334
- value: effectiveStep.value ? BigInt(effectiveStep.value) : undefined
335
- };
336
- let estimatedGas;
337
- try {
338
- estimatedGas = await publicClient.estimateGas(txRequest);
339
- } catch (estimateError) {
340
- if (effectiveStep.kind !== "dex_swap") {
341
- throw estimateError;
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;
363
+ 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
+ }
380
+ if (!refreshed?.step) {
381
+ throw new Error("estimateGas failed for dex_swap step and refresh-step returned no step");
382
+ }
383
+ if (refreshed.step.kind !== "dex_swap") {
384
+ throw new Error(`refresh-step returned unexpected step kind: ${refreshed.step.kind}`);
385
+ }
386
+ effectiveStep = refreshed.step;
387
+ txRequest = {
388
+ account,
389
+ to: effectiveStep.to,
390
+ data: effectiveStep.calldata,
391
+ value: effectiveStep.value ? BigInt(effectiveStep.value) : undefined
392
+ };
393
+ 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 };
342
414
  }
343
- this.logDebug("estimateGas failed; attempting refresh-step", {
344
- swapId,
345
- stepId: effectiveStep.id,
346
- error: estimateError instanceof Error ? estimateError.message : String(estimateError)
415
+ const nonce = await publicClient.getTransactionCount({
416
+ address: account.address,
417
+ blockTag: "pending"
418
+ });
419
+ const feeEstimate = await publicClient.estimateFeesPerGas().catch(() => {
420
+ return;
347
421
  });
348
- let refreshed;
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);
349
437
  try {
350
- refreshed = this.unwrapEdenResult(await this.riftClient.swap({ swapId })["refresh-step"].post({ stepId: effectiveStep.id }));
351
- } catch (refreshError) {
352
- throw new Error(`estimateGas failed for dex_swap step and refresh-step failed: ${refreshError instanceof Error ? refreshError.message : String(refreshError)}`);
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;
353
459
  }
354
- if (!refreshed?.step) {
355
- throw new Error("estimateGas failed for dex_swap step and refresh-step returned no step");
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
466
+ });
356
467
  }
357
- if (refreshed.step.kind !== "dex_swap") {
358
- throw new Error(`refresh-step returned unexpected step kind: ${refreshed.step.kind}`);
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}`);
359
473
  }
360
- effectiveStep = refreshed.step;
361
- txRequest = {
362
- account,
363
- to: effectiveStep.to,
364
- data: effectiveStep.calldata,
365
- value: effectiveStep.value ? BigInt(effectiveStep.value) : undefined
366
- };
367
- estimatedGas = await publicClient.estimateGas(txRequest);
368
- }
369
- const gasLimit = (estimatedGas * GAS_LIMIT_MULTIPLIER_NUMERATOR + GAS_LIMIT_MULTIPLIER_DENOMINATOR - 1n) / GAS_LIMIT_MULTIPLIER_DENOMINATOR;
370
- this.logDebug("using buffered gas limit", {
371
- stepId: step.id,
372
- estimatedGas: estimatedGas.toString(),
373
- gasLimit: gasLimit.toString()
374
- });
375
- await context.onExecuteStep?.(effectiveStep.kind === "approval" ? "approval" : "transaction");
376
- const txHash = await walletClient.sendTransaction({
377
- ...txRequest,
378
- gas: gasLimit
379
- });
380
- const receipt = await publicClient.waitForTransactionReceipt({
381
- hash: txHash
382
- });
383
- if (receipt.status !== "success") {
384
- throw new Error(`EVM step transaction reverted (${effectiveStep.kind}) with hash ${txHash}`);
474
+ return { txHash };
385
475
  }
386
- return { txHash };
387
476
  }
388
477
  async executeBtcTransferStep(step, context) {
389
478
  const sendBitcoin = this.requireSendBitcoin(context);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riftresearch/sdk",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "SDK for swapping between bitcoin and evm chains",
5
5
  "license": "MIT",
6
6
  "files": [