@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 +7 -0
- package/dist/index.js +139 -50
- package/package.json +1 -1
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
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
estimatedGas
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
-
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
355
|
-
|
|
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
|
-
|
|
358
|
-
|
|
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
|
-
|
|
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);
|