All files / evm-wallet-experiment/src/lib bundler.ts

88.88% Statements 24/27
82.35% Branches 14/17
85.71% Functions 6/7
92% Lines 23/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176                1x                                                                                                   14x           14x           14x         14x 1x     13x                             2x       1x                             1x       1x                             11x     11x 11x                                     3x         3x 3x   3x 9x 9x 2x   7x     1x        
/**
 * @deprecated Use `lib/bundler-client.ts` (`makeBundlerClient()`) instead.
 * This module is retained for backwards compatibility and will be removed
 * in a future release.
 */
 
import type { Hex, UserOperation } from '../types.ts';
 
const harden = globalThis.harden ?? (<T>(value: T): T => value);
 
/**
 * Configuration for connecting to an ERC-4337 bundler.
 *
 * @deprecated Use `BundlerClientConfig` from `lib/bundler-client.ts` instead.
 */
export type BundlerConfig = {
  url: string;
  entryPoint: Hex;
};
 
/**
 * Receipt returned after a UserOperation is included on-chain.
 */
export type UserOpReceipt = {
  userOpHash: Hex;
  sender: Hex;
  nonce: Hex;
  success: boolean;
  actualGasCost: Hex;
  actualGasUsed: Hex;
  receipt: {
    transactionHash: Hex;
    blockNumber: Hex;
  };
};
 
/**
 * Gas estimates returned by the bundler.
 */
export type UserOpGasEstimate = {
  callGasLimit: Hex;
  verificationGasLimit: Hex;
  preVerificationGas: Hex;
};
 
/**
 * Send a JSON-RPC request to the bundler.
 *
 * @param url - The bundler RPC URL.
 * @param method - The RPC method.
 * @param params - The RPC parameters.
 * @returns The RPC result.
 */
async function bundlerRpc(
  url: string,
  method: string,
  params: unknown[],
): Promise<unknown> {
  const response = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
  });
 
  Iif (!response.ok) {
    throw new Error(
      `Bundler HTTP error: ${response.status} ${response.statusText}`,
    );
  }
 
  const json = (await response.json()) as {
    result?: unknown;
    error?: { message: string; code: number };
  };
 
  if (json.error) {
    throw new Error(`Bundler RPC error: ${json.error.message}`);
  }
 
  return json.result;
}
 
/**
 * Submit a signed UserOperation to the bundler.
 *
 * @deprecated Use `makeBundlerClient().sendUserOperation()` instead.
 * @param config - The bundler configuration.
 * @param userOp - The signed UserOperation.
 * @returns The UserOperation hash.
 */
export async function submitUserOp(
  config: BundlerConfig,
  userOp: UserOperation,
): Promise<Hex> {
  const result = await bundlerRpc(config.url, 'eth_sendUserOperation', [
    userOp,
    config.entryPoint,
  ]);
  return harden(result as Hex);
}
 
/**
 * Estimate gas for a UserOperation.
 *
 * @deprecated Use `makeBundlerClient().estimateUserOperationGas()` instead.
 * @param config - The bundler configuration.
 * @param userOp - The UserOperation to estimate.
 * @returns The gas estimates.
 */
export async function estimateUserOpGas(
  config: BundlerConfig,
  userOp: UserOperation,
): Promise<UserOpGasEstimate> {
  const result = await bundlerRpc(config.url, 'eth_estimateUserOperationGas', [
    userOp,
    config.entryPoint,
  ]);
  return harden(result as UserOpGasEstimate);
}
 
/**
 * Get the receipt for a UserOperation.
 *
 * @deprecated Use `makeBundlerClient().getUserOperationReceipt()` instead.
 * @param config - The bundler configuration.
 * @param userOpHash - The UserOperation hash.
 * @returns The receipt, or null if not yet included.
 */
export async function getUserOpReceipt(
  config: BundlerConfig,
  userOpHash: Hex,
): Promise<UserOpReceipt | null> {
  const result = await bundlerRpc(config.url, 'eth_getUserOperationReceipt', [
    userOpHash,
  ]);
  const receipt = (result as UserOpReceipt) ?? null;
  return receipt ? harden(receipt) : null;
}
 
/**
 * Poll for a UserOperation receipt until it is included.
 *
 * @deprecated Use `makeBundlerClient().waitForUserOperationReceipt()` instead.
 * @param config - The bundler configuration.
 * @param userOpHash - The UserOperation hash.
 * @param options - Polling options.
 * @param options.pollIntervalMs - How often to poll (default: 2000ms).
 * @param options.timeoutMs - Maximum time to wait (default: 60000ms).
 * @returns The receipt.
 */
export async function waitForUserOp(
  config: BundlerConfig,
  userOpHash: Hex,
  options: { pollIntervalMs?: number; timeoutMs?: number } = {},
): Promise<UserOpReceipt> {
  Iif (typeof globalThis.setTimeout !== 'function') {
    throw new Error(
      'waitForUserOp requires timer support (not available in SES compartments)',
    );
  }
  const { pollIntervalMs = 2000, timeoutMs = 60000 } = options;
  const deadline = Date.now() + timeoutMs;
 
  while (Date.now() < deadline) {
    const receipt = await getUserOpReceipt(config, userOpHash);
    if (receipt) {
      return receipt;
    }
    await new Promise<void>((resolve) => setTimeout(resolve, pollIntervalMs));
  }
 
  throw new Error(
    `UserOperation ${userOpHash} not included after ${timeoutMs}ms`,
  );
}