@riftresearch/sdk 0.8.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 +16 -2
- package/dist/index.js +154 -29
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -148,7 +148,7 @@ type ExecutionAction = "evm_call" | "btc_transfer";
|
|
|
148
148
|
/**
|
|
149
149
|
* Step kinds grouped by action type.
|
|
150
150
|
*/
|
|
151
|
-
type EvmCallKind = "approval" | "transfer_erc20" | "
|
|
151
|
+
type EvmCallKind = "approval" | "transfer_erc20" | "dex_swap";
|
|
152
152
|
type BtcTransferKind = "transfer_btc";
|
|
153
153
|
/**
|
|
154
154
|
* EVM Call step - execute calldata on an EVM chain.
|
|
@@ -376,6 +376,8 @@ type SendBitcoinFn = (params: {
|
|
|
376
376
|
/** Amount to send in satoshis */
|
|
377
377
|
amountSats: string;
|
|
378
378
|
}) => Promise<void>;
|
|
379
|
+
type ExecuteSwapStepType = "approval" | "transaction";
|
|
380
|
+
type ExecuteSwapOnExecuteStepCallback = (type: ExecuteSwapStepType) => void | Promise<void>;
|
|
379
381
|
type ExecuteSwapContext<chain extends Chain2 | undefined = Chain2 | undefined> = {
|
|
380
382
|
/** Viem PublicClient for reading chain data */
|
|
381
383
|
publicClient?: PublicClient<Transport, chain>;
|
|
@@ -383,6 +385,11 @@ type ExecuteSwapContext<chain extends Chain2 | undefined = Chain2 | undefined> =
|
|
|
383
385
|
walletClient?: WalletClient<Transport, chain, Account | undefined>;
|
|
384
386
|
/** Function to send Bitcoin (implement using your preferred wallet) */
|
|
385
387
|
sendBitcoin?: SendBitcoinFn;
|
|
388
|
+
/**
|
|
389
|
+
* Optional callback invoked immediately before prompting the user's wallet to
|
|
390
|
+
* sign/send a transaction (after estimateGas/simulation has succeeded).
|
|
391
|
+
*/
|
|
392
|
+
onExecuteStep?: ExecuteSwapOnExecuteStepCallback;
|
|
386
393
|
};
|
|
387
394
|
type ExecuteSwapOptions<chain extends Chain2 | undefined = Chain2 | undefined> = ExecuteSwapContext<chain> & {
|
|
388
395
|
/** Address to receive the output tokens */
|
|
@@ -395,6 +402,12 @@ interface RiftSdkOptions {
|
|
|
395
402
|
apiUrl?: string;
|
|
396
403
|
/** Enable verbose debug logging for swap execution */
|
|
397
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";
|
|
398
411
|
/** Optional preflight checks before executing swaps */
|
|
399
412
|
preflight?: {
|
|
400
413
|
/** Check sender balance before executing EVM steps (default: true) */
|
|
@@ -408,6 +421,7 @@ declare class RiftSdk {
|
|
|
408
421
|
private preflightCheckBalances;
|
|
409
422
|
private integratorName;
|
|
410
423
|
private debug;
|
|
424
|
+
private evmBroadcastMode;
|
|
411
425
|
constructor(options: RiftSdkOptions);
|
|
412
426
|
private logDebug;
|
|
413
427
|
private unwrapEdenResult;
|
|
@@ -462,4 +476,4 @@ declare class RiftSdk {
|
|
|
462
476
|
getSwapStatus(swapId: string): Promise<SwapStatusResponse>;
|
|
463
477
|
}
|
|
464
478
|
declare function createRiftSdk(options: RiftSdkOptions): RiftSdk;
|
|
465
|
-
export { getSupportedModes, detectRoute, createRiftSdk, createCurrency, TokenIdentifier, SwapStatus, SwapRouterApiError, SwapRoute, SwapResult, SwapResponse, SupportedModes, SendBitcoinFn, RiftSwap, RiftSdkOptions, RiftSdk, QuoteResult, QuoteQuality, QuoteParameters, NativeToken, GetQuoteResult, ExecutionStep, ExecutionAction, ExecuteSwapOptions, EvmChain, EvmCallStep, EvmCallKind, Erc20Token, Currency, Currencies, Chain, BtcTransferStep, BtcTransferKind, BitcoinChain };
|
|
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 };
|
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)
|
|
@@ -268,15 +292,16 @@ class RiftSdk {
|
|
|
268
292
|
kind: "kind" in step ? step.kind : undefined,
|
|
269
293
|
chainId: "chainId" in step ? step.chainId : undefined
|
|
270
294
|
});
|
|
271
|
-
const result = await this.executeStep(step, context);
|
|
295
|
+
const result = await this.executeStep(step, context, swapResponse.swapId);
|
|
272
296
|
this.logDebug("step completed", {
|
|
273
297
|
stepId: step.id,
|
|
274
298
|
txHash: result.txHash
|
|
275
299
|
});
|
|
276
|
-
if (
|
|
300
|
+
if (step.action === "evm_call" && step.kind === "dex_swap" && result.txHash) {
|
|
277
301
|
this.logDebug("reporting step result", {
|
|
278
302
|
stepId: step.id,
|
|
279
|
-
dexSwap: true
|
|
303
|
+
dexSwap: true,
|
|
304
|
+
monochain: isMonochain
|
|
280
305
|
});
|
|
281
306
|
this.unwrapEdenResult(await this.riftClient.swap({ swapId: swapResponse.swapId }).tx.post({
|
|
282
307
|
stepId: step.id,
|
|
@@ -299,15 +324,15 @@ class RiftSdk {
|
|
|
299
324
|
}
|
|
300
325
|
};
|
|
301
326
|
}
|
|
302
|
-
async executeStep(step, context) {
|
|
327
|
+
async executeStep(step, context, swapId) {
|
|
303
328
|
switch (step.action) {
|
|
304
329
|
case "evm_call":
|
|
305
|
-
return this.executeEvmCallStep(step, context);
|
|
330
|
+
return this.executeEvmCallStep(step, context, swapId);
|
|
306
331
|
case "btc_transfer":
|
|
307
332
|
return this.executeBtcTransferStep(step, context);
|
|
308
333
|
}
|
|
309
334
|
}
|
|
310
|
-
async executeEvmCallStep(step, context) {
|
|
335
|
+
async executeEvmCallStep(step, context, swapId) {
|
|
311
336
|
const walletClient = this.requireWalletClient(context);
|
|
312
337
|
const publicClient = this.requirePublicClient(context);
|
|
313
338
|
const account = walletClient.account;
|
|
@@ -325,33 +350,133 @@ class RiftSdk {
|
|
|
325
350
|
return {};
|
|
326
351
|
}
|
|
327
352
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
353
|
+
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;
|
|
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 };
|
|
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
|
|
466
|
+
});
|
|
467
|
+
}
|
|
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}`);
|
|
473
|
+
}
|
|
474
|
+
return { txHash };
|
|
350
475
|
}
|
|
351
|
-
return { txHash };
|
|
352
476
|
}
|
|
353
477
|
async executeBtcTransferStep(step, context) {
|
|
354
478
|
const sendBitcoin = this.requireSendBitcoin(context);
|
|
479
|
+
await context.onExecuteStep?.("transaction");
|
|
355
480
|
await sendBitcoin({
|
|
356
481
|
recipient: step.toAddress,
|
|
357
482
|
amountSats: step.amountSats
|