@volr/react 0.1.21 → 0.1.22

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.cjs CHANGED
@@ -17945,6 +17945,8 @@ function http(url, config = {}) {
17945
17945
  // ../node_modules/viem/_esm/index.js
17946
17946
  init_encodeFunctionData();
17947
17947
  init_getAddress();
17948
+ init_formatEther();
17949
+ init_formatUnits();
17948
17950
 
17949
17951
  // src/utils/normalize.ts
17950
17952
  function normalizeHex(value) {
@@ -18102,112 +18104,6 @@ function normalizeCall(call2) {
18102
18104
  function normalizeCalls(calls) {
18103
18105
  return calls.map(normalizeCall);
18104
18106
  }
18105
-
18106
- // src/wallet/preflight.ts
18107
- function extractErrorMessage(err) {
18108
- if (err && typeof err === "object") {
18109
- const e = err;
18110
- const parts = [];
18111
- if (typeof e.shortMessage === "string") parts.push(e.shortMessage);
18112
- if (typeof e.message === "string") parts.push(e.message);
18113
- if (typeof e.cause?.message === "string") parts.push(e.cause.message);
18114
- if (typeof e.details === "string") parts.push(e.details);
18115
- const msg = parts.filter(Boolean).join(" | ");
18116
- if (msg) return msg;
18117
- }
18118
- try {
18119
- return String(err);
18120
- } catch {
18121
- return "Unknown error";
18122
- }
18123
- }
18124
- function isIgnorableFundingError(err, message) {
18125
- const m = message.toLowerCase();
18126
- const e = err;
18127
- const name = typeof e?.name === "string" ? e.name.toLowerCase() : "";
18128
- if (name === "insufficientfundserror" || name.includes("insufficientfunds")) {
18129
- return true;
18130
- }
18131
- if (typeof e?.code === "string") {
18132
- const code = e.code.toLowerCase();
18133
- if (code.includes("execution_reverted") || code.includes("call_exception")) {
18134
- return false;
18135
- }
18136
- if (code.includes("insufficient_funds")) {
18137
- return true;
18138
- }
18139
- }
18140
- const fundingPatterns = [
18141
- "insufficient funds for gas * price",
18142
- "insufficient funds for gas",
18143
- "for gas * price + value",
18144
- "not enough funds for l1 fee",
18145
- "insufficient funds for l1 fee",
18146
- "insufficient balance for gas",
18147
- "account balance too low"
18148
- ];
18149
- const hasFundingPattern = fundingPatterns.some((pattern) => m.includes(pattern));
18150
- const hasRevertIndicator = m.includes("revert") || m.includes("execution reverted") || m.includes("call exception") || m.includes("vm execution error");
18151
- return hasFundingPattern && !hasRevertIndicator;
18152
- }
18153
- async function preflightEstimate(publicClient, from14, calls, opts) {
18154
- for (let i = 0; i < calls.length; i++) {
18155
- const c = calls[i];
18156
- try {
18157
- await publicClient.estimateGas({
18158
- account: from14,
18159
- to: c.target,
18160
- data: c.data,
18161
- value: c.value ?? 0n
18162
- });
18163
- } catch (e) {
18164
- const message = extractErrorMessage(e);
18165
- const tolerateFunding = opts?.tolerateFundingErrors !== false;
18166
- const isFundingError = isIgnorableFundingError(e, message);
18167
- if (tolerateFunding && isFundingError) {
18168
- try {
18169
- await publicClient.call({
18170
- account: from14,
18171
- to: c.target,
18172
- data: c.data,
18173
- value: c.value ?? 0n
18174
- });
18175
- console.log(
18176
- `[preflightEstimate] Ignoring funding error for call #${i} (target ${c.target}): ${message}`
18177
- );
18178
- continue;
18179
- } catch (callError) {
18180
- const callMessage = extractErrorMessage(callError);
18181
- console.error(
18182
- `[preflightEstimate] Static call after funding error also failed for call #${i} (target ${c.target}):`,
18183
- {
18184
- originalError: { message, errorName: e?.name, errorCode: e?.code },
18185
- callError: {
18186
- message: callMessage,
18187
- name: callError?.name,
18188
- code: callError?.code
18189
- }
18190
- }
18191
- );
18192
- throw new Error(
18193
- `Preflight failed (call #${i} target ${c.target}): ${callMessage}`
18194
- );
18195
- }
18196
- }
18197
- console.error(
18198
- `[preflightEstimate] Preflight failed for call #${i} (target ${c.target}):`,
18199
- {
18200
- message,
18201
- errorName: e?.name,
18202
- errorCode: e?.code,
18203
- isFundingError,
18204
- tolerateFunding
18205
- }
18206
- );
18207
- throw new Error(`Preflight failed (call #${i} target ${c.target}): ${message}`);
18208
- }
18209
- }
18210
- }
18211
18107
  async function resolveSigner(input) {
18212
18108
  const { explicitSigner, provider, chainId, client, user, setProvider } = input;
18213
18109
  if (explicitSigner) {
@@ -18304,6 +18200,40 @@ function finalizeAuthWithNonce(tempAuth, quote) {
18304
18200
  }
18305
18201
 
18306
18202
  // src/utils/tx-polling.ts
18203
+ function extractFailureReason(response) {
18204
+ const reasons = [];
18205
+ const meta = response.meta;
18206
+ if (meta) {
18207
+ if (typeof meta.revertReason === "string" && meta.revertReason) {
18208
+ reasons.push(meta.revertReason);
18209
+ }
18210
+ if (typeof meta.failureReason === "string" && meta.failureReason) {
18211
+ reasons.push(meta.failureReason);
18212
+ }
18213
+ const diag = meta.developerDiagnostics;
18214
+ if (diag) {
18215
+ if (typeof diag.revertReason === "string" && diag.revertReason) {
18216
+ reasons.push(diag.revertReason);
18217
+ }
18218
+ if (typeof diag.error === "string" && diag.error) {
18219
+ reasons.push(diag.error);
18220
+ }
18221
+ const simErrors = diag.simulationErrors;
18222
+ if (Array.isArray(simErrors)) {
18223
+ for (const err of simErrors) {
18224
+ if (typeof err.error === "string") {
18225
+ reasons.push(`Simulation error: ${err.error}`);
18226
+ }
18227
+ }
18228
+ }
18229
+ }
18230
+ }
18231
+ const uniqueReasons = [...new Set(reasons)];
18232
+ if (uniqueReasons.length > 0) {
18233
+ return uniqueReasons.join("; ");
18234
+ }
18235
+ return "Transaction failed on-chain (no specific reason provided)";
18236
+ }
18307
18237
  async function pollTransactionStatus(txId, client, maxAttempts = 60, intervalMs = 5e3) {
18308
18238
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
18309
18239
  try {
@@ -18320,15 +18250,17 @@ async function pollTransactionStatus(txId, client, maxAttempts = 60, intervalMs
18320
18250
  };
18321
18251
  }
18322
18252
  if (status === "FAILED") {
18323
- console.error(`[pollTransactionStatus] Transaction ${txId} failed`);
18324
- const diag = response.meta?.developerDiagnostics;
18325
- if (diag) {
18326
- console.error("[volr][relay] developerDiagnostics:", diag);
18253
+ const failureReason = extractFailureReason(response);
18254
+ console.error(`[pollTransactionStatus] Transaction ${txId} failed: ${failureReason}`);
18255
+ const meta = response.meta;
18256
+ if (meta) {
18257
+ console.error("[pollTransactionStatus] Full meta:", JSON.stringify(meta, null, 2));
18327
18258
  }
18328
18259
  return {
18329
18260
  txId,
18330
18261
  status: "FAILED",
18331
- txHash
18262
+ txHash,
18263
+ reason: failureReason
18332
18264
  };
18333
18265
  }
18334
18266
  console.log(`[pollTransactionStatus] Transaction ${txId} is ${status}, attempt ${attempt + 1}/${maxAttempts}`);
@@ -18378,19 +18310,6 @@ async function sendCalls(args) {
18378
18310
  keyStorageType: deps.user?.keyStorageType
18379
18311
  });
18380
18312
  }
18381
- if (opts.preflight !== false) {
18382
- console.log("[sendCalls] Running preflight estimate...");
18383
- const tolerateFundingErrors = (opts.mode ?? "sponsored") !== "self";
18384
- await preflightEstimate(
18385
- deps.publicClient,
18386
- normalizedFrom,
18387
- normalizedCalls,
18388
- { tolerateFundingErrors }
18389
- );
18390
- console.log("[sendCalls] Preflight estimate done");
18391
- } else {
18392
- console.log("[sendCalls] Preflight skipped");
18393
- }
18394
18313
  console.log("[sendCalls] Building temp auth for first precheck...");
18395
18314
  const tempAuthForPrecheck = buildTempAuth({
18396
18315
  chainId,
@@ -18406,11 +18325,34 @@ async function sendCalls(args) {
18406
18325
  auth: tempAuthForPrecheck,
18407
18326
  calls: normalizedCalls
18408
18327
  });
18409
- console.log("[sendCalls] First precheck done, policyId:", precheckQuote.policyId);
18328
+ console.log("[sendCalls] First precheck done:", {
18329
+ policyId: precheckQuote.policyId,
18330
+ simulationSuccess: precheckQuote.simulationSuccess,
18331
+ estimatedGasPerCall: precheckQuote.estimatedGasPerCall
18332
+ });
18410
18333
  } catch (err) {
18411
18334
  console.error("[sendCalls] First precheck failed:", err);
18412
18335
  throw err instanceof Error ? err : new Error(String(err));
18413
18336
  }
18337
+ if (precheckQuote.simulationSuccess === false && precheckQuote.simulationErrors?.length) {
18338
+ const errors = precheckQuote.simulationErrors;
18339
+ console.error("[sendCalls] Backend simulation failed:", errors);
18340
+ const errorMessages = errors.map(
18341
+ (e) => `Call #${e.index}: ${e.error}${e.revertData ? ` (data: ${e.revertData})` : ""}`
18342
+ ).join("; ");
18343
+ throw new Error(`Transaction simulation failed: ${errorMessages}`);
18344
+ }
18345
+ if (precheckQuote.estimatedGasPerCall?.length) {
18346
+ console.log("[sendCalls] Applying estimated gas limits from backend...");
18347
+ for (let i = 0; i < normalizedCalls.length && i < precheckQuote.estimatedGasPerCall.length; i++) {
18348
+ const estimatedGas = BigInt(precheckQuote.estimatedGasPerCall[i] ?? "0");
18349
+ const currentGas = normalizedCalls[i]?.gasLimit ?? 0n;
18350
+ if (estimatedGas > currentGas) {
18351
+ console.log(`[sendCalls] Call #${i}: updating gasLimit from ${currentGas} to ${estimatedGas}`);
18352
+ normalizedCalls[i].gasLimit = estimatedGas;
18353
+ }
18354
+ }
18355
+ }
18414
18356
  const quotePolicyId = precheckQuote.policyId;
18415
18357
  if (!quotePolicyId) {
18416
18358
  throw new Error("Backend did not return policyId in precheck response");
@@ -18423,6 +18365,7 @@ async function sendCalls(args) {
18423
18365
  from: normalizedFrom,
18424
18366
  policyId: projectPolicyId,
18425
18367
  calls: normalizedCalls,
18368
+ // Now with updated gas limits
18426
18369
  expiresInSec: opts.expiresInSec ?? DEFAULT_EXPIRES_IN_SEC
18427
18370
  });
18428
18371
  let quote;
@@ -19390,6 +19333,219 @@ async function uploadBlobViaPresign(params) {
19390
19333
  }
19391
19334
  return { s3Key };
19392
19335
  }
19336
+
19337
+ // src/utils/contract-analysis.ts
19338
+ var EIP7702_PREFIX = "0xef0100";
19339
+ async function isEIP7702Delegated(publicClient, address) {
19340
+ try {
19341
+ const code = await publicClient.getCode({ address });
19342
+ if (!code || code === "0x" || code.length < 8) {
19343
+ return false;
19344
+ }
19345
+ return code.toLowerCase().startsWith(EIP7702_PREFIX);
19346
+ } catch {
19347
+ return false;
19348
+ }
19349
+ }
19350
+ async function analyzeContractForEIP7702(publicClient, target, calldata) {
19351
+ const notes = [];
19352
+ let isContract = false;
19353
+ let codeSize = 0;
19354
+ let mayCheckSenderCode = false;
19355
+ let mayUseDelegationGuard = false;
19356
+ try {
19357
+ const code = await publicClient.getCode({ address: target });
19358
+ isContract = !!code && code !== "0x";
19359
+ codeSize = code ? (code.length - 2) / 2 : 0;
19360
+ if (!isContract || !code) {
19361
+ notes.push("Target is not a contract (EOA or empty address)");
19362
+ } else {
19363
+ const codeLower = code.toLowerCase();
19364
+ if (codeLower.includes("3b")) {
19365
+ mayCheckSenderCode = true;
19366
+ notes.push("Contract contains EXTCODESIZE opcode - may check msg.sender.code.length");
19367
+ }
19368
+ if (codeLower.includes("ef0100")) {
19369
+ mayUseDelegationGuard = true;
19370
+ notes.push("Contract contains EIP-7702 prefix bytes - may use DelegationGuard pattern");
19371
+ }
19372
+ }
19373
+ } catch (error) {
19374
+ notes.push(`Failed to fetch contract code: ${error instanceof Error ? error.message : "Unknown error"}`);
19375
+ }
19376
+ const functionSelector = calldata && calldata.length >= 10 ? calldata.slice(0, 10) : "0x";
19377
+ return {
19378
+ target,
19379
+ isContract,
19380
+ codeSize,
19381
+ mayCheckSenderCode,
19382
+ mayUseDelegationGuard,
19383
+ functionSelector,
19384
+ notes
19385
+ };
19386
+ }
19387
+ async function diagnoseTransactionFailure(publicClient, from14, target, calldata) {
19388
+ const recommendations = [];
19389
+ const fromCode = await publicClient.getCode({ address: from14 });
19390
+ const fromHasCode = !!fromCode && fromCode !== "0x";
19391
+ const fromIsDelegated = await isEIP7702Delegated(publicClient, from14);
19392
+ const targetAnalysis = await analyzeContractForEIP7702(publicClient, target, calldata);
19393
+ if (!targetAnalysis.isContract) {
19394
+ recommendations.push("Target is not a contract. Verify the target address is correct.");
19395
+ }
19396
+ if (targetAnalysis.mayCheckSenderCode) {
19397
+ recommendations.push(
19398
+ "Target contract may check msg.sender.code.length. This can cause issues because in EIP-7702, the sender EOA temporarily has code."
19399
+ );
19400
+ }
19401
+ if (targetAnalysis.mayUseDelegationGuard) {
19402
+ recommendations.push(
19403
+ "Target contract may use DelegationGuard pattern to block EIP-7702 delegated accounts. This is a security measure some contracts use. You may need to contact the contract developers or use a different approach."
19404
+ );
19405
+ }
19406
+ if (!fromIsDelegated && fromHasCode) {
19407
+ recommendations.push(
19408
+ "Sender address has code but is not EIP-7702 delegated. This may indicate the address is a contract, not an EOA."
19409
+ );
19410
+ }
19411
+ if (recommendations.length === 0) {
19412
+ recommendations.push(
19413
+ "No obvious EIP-7702 compatibility issues detected. The failure may be due to contract logic (e.g., insufficient balance, invalid parameters). Check the contract's requirements and your transaction parameters."
19414
+ );
19415
+ }
19416
+ return {
19417
+ fromAnalysis: {
19418
+ isDelegated: fromIsDelegated,
19419
+ hasCode: fromHasCode
19420
+ },
19421
+ targetAnalysis,
19422
+ recommendations
19423
+ };
19424
+ }
19425
+
19426
+ // src/utils/wallet-debug.ts
19427
+ async function getWalletState(publicClient, address) {
19428
+ const [balance, code, nonce] = await Promise.all([
19429
+ publicClient.getBalance({ address }),
19430
+ publicClient.getCode({ address }),
19431
+ publicClient.getTransactionCount({ address })
19432
+ ]);
19433
+ const hasCode = !!code && code !== "0x";
19434
+ const codeLength = code ? (code.length - 2) / 2 : 0;
19435
+ const isEIP7702Delegated2 = hasCode && code.toLowerCase().startsWith("0xef0100");
19436
+ return {
19437
+ address,
19438
+ ethBalance: formatEther(balance),
19439
+ ethBalanceWei: balance,
19440
+ hasCode,
19441
+ codeLength,
19442
+ nonce,
19443
+ isEIP7702Delegated: isEIP7702Delegated2
19444
+ };
19445
+ }
19446
+ async function getERC20Balance(publicClient, walletAddress, tokenAddress, decimals = 18) {
19447
+ const balance = await publicClient.readContract({
19448
+ address: tokenAddress,
19449
+ abi: [
19450
+ {
19451
+ name: "balanceOf",
19452
+ type: "function",
19453
+ stateMutability: "view",
19454
+ inputs: [{ name: "account", type: "address" }],
19455
+ outputs: [{ type: "uint256" }]
19456
+ }
19457
+ ],
19458
+ functionName: "balanceOf",
19459
+ args: [walletAddress]
19460
+ });
19461
+ return {
19462
+ address: walletAddress,
19463
+ tokenAddress,
19464
+ balance: formatUnits(balance, decimals),
19465
+ balanceRaw: balance,
19466
+ decimals
19467
+ };
19468
+ }
19469
+ async function compareWalletStates(publicClient, address1, address2) {
19470
+ const [wallet1, wallet2] = await Promise.all([
19471
+ getWalletState(publicClient, address1),
19472
+ getWalletState(publicClient, address2)
19473
+ ]);
19474
+ const differences = [];
19475
+ if (wallet1.hasCode !== wallet2.hasCode) {
19476
+ differences.push(
19477
+ `Code presence differs: ${address1} has ${wallet1.hasCode ? "code" : "no code"}, ${address2} has ${wallet2.hasCode ? "code" : "no code"}`
19478
+ );
19479
+ }
19480
+ if (wallet1.isEIP7702Delegated !== wallet2.isEIP7702Delegated) {
19481
+ differences.push(
19482
+ `EIP-7702 delegation differs: ${address1} is ${wallet1.isEIP7702Delegated ? "" : "not "}delegated, ${address2} is ${wallet2.isEIP7702Delegated ? "" : "not "}delegated`
19483
+ );
19484
+ }
19485
+ const balanceDiff = wallet1.ethBalanceWei - wallet2.ethBalanceWei;
19486
+ if (balanceDiff !== 0n) {
19487
+ differences.push(
19488
+ `ETH balance differs by ${formatEther(balanceDiff > 0n ? balanceDiff : -balanceDiff)} ETH`
19489
+ );
19490
+ }
19491
+ return { wallet1, wallet2, differences };
19492
+ }
19493
+ async function compareERC20Balances(publicClient, address1, address2, tokenAddress, decimals = 18) {
19494
+ const [wallet1, wallet2] = await Promise.all([
19495
+ getERC20Balance(publicClient, address1, tokenAddress, decimals),
19496
+ getERC20Balance(publicClient, address2, tokenAddress, decimals)
19497
+ ]);
19498
+ const differenceRaw = wallet1.balanceRaw - wallet2.balanceRaw;
19499
+ return {
19500
+ wallet1,
19501
+ wallet2,
19502
+ difference: formatUnits(differenceRaw > 0n ? differenceRaw : -differenceRaw, decimals),
19503
+ differenceRaw
19504
+ };
19505
+ }
19506
+ async function debugTransactionFailure(publicClient, failingWallet, workingWallet, tokenAddress, tokenDecimals = 18) {
19507
+ const analysis = [];
19508
+ const stateComparison = await compareWalletStates(
19509
+ publicClient,
19510
+ failingWallet,
19511
+ workingWallet
19512
+ );
19513
+ if (stateComparison.wallet1.isEIP7702Delegated && !stateComparison.wallet2.isEIP7702Delegated) {
19514
+ analysis.push(
19515
+ "The failing wallet has EIP-7702 delegation while the working wallet does not. This may cause issues if the target contract checks msg.sender.code.length or uses DelegationGuard."
19516
+ );
19517
+ }
19518
+ if (stateComparison.wallet1.hasCode && !stateComparison.wallet2.hasCode) {
19519
+ analysis.push(
19520
+ "The failing wallet has code while the working wallet is a pure EOA. Some contracts reject calls from addresses with code."
19521
+ );
19522
+ }
19523
+ let tokenComparison;
19524
+ if (tokenAddress) {
19525
+ tokenComparison = await compareERC20Balances(
19526
+ publicClient,
19527
+ failingWallet,
19528
+ workingWallet,
19529
+ tokenAddress,
19530
+ tokenDecimals
19531
+ );
19532
+ if (tokenComparison.wallet1.balanceRaw < tokenComparison.wallet2.balanceRaw) {
19533
+ analysis.push(
19534
+ `The failing wallet has less token balance (${tokenComparison.wallet1.balance}) than the working wallet (${tokenComparison.wallet2.balance}). Ensure sufficient balance for the transaction.`
19535
+ );
19536
+ }
19537
+ }
19538
+ if (analysis.length === 0) {
19539
+ analysis.push(
19540
+ "No obvious differences found between wallets. The issue may be related to contract-specific state (e.g., positions, allowances, whitelists) or transaction parameters."
19541
+ );
19542
+ }
19543
+ return {
19544
+ stateComparison,
19545
+ tokenComparison,
19546
+ analysis
19547
+ };
19548
+ }
19393
19549
  /*! Bundled license information:
19394
19550
 
19395
19551
  @noble/hashes/esm/utils.js:
@@ -19435,11 +19591,19 @@ Object.defineProperty(exports, "uploadBlob", {
19435
19591
  exports.DEFAULT_EXPIRES_IN_SEC = DEFAULT_EXPIRES_IN_SEC;
19436
19592
  exports.DEFAULT_MODE = DEFAULT_MODE;
19437
19593
  exports.VolrProvider = VolrProvider;
19594
+ exports.analyzeContractForEIP7702 = analyzeContractForEIP7702;
19438
19595
  exports.buildCall = buildCall;
19439
19596
  exports.buildCalls = buildCalls;
19597
+ exports.compareERC20Balances = compareERC20Balances;
19598
+ exports.compareWalletStates = compareWalletStates;
19440
19599
  exports.createGetNetworkInfo = createGetNetworkInfo;
19441
19600
  exports.createPasskeyAdapter = createPasskeyAdapter;
19601
+ exports.debugTransactionFailure = debugTransactionFailure;
19442
19602
  exports.defaultIdempotencyKey = defaultIdempotencyKey;
19603
+ exports.diagnoseTransactionFailure = diagnoseTransactionFailure;
19604
+ exports.getERC20Balance = getERC20Balance;
19605
+ exports.getWalletState = getWalletState;
19606
+ exports.isEIP7702Delegated = isEIP7702Delegated;
19443
19607
  exports.normalizeHex = normalizeHex;
19444
19608
  exports.normalizeHexArray = normalizeHexArray;
19445
19609
  exports.uploadBlobViaPresign = uploadBlobViaPresign;