UNPKG

ox

Version:

Ethereum Standard Library

339 lines 13.4 kB
import * as Authorization from './Authorization.js'; import * as Blobs from './Blobs.js'; import * as Errors from './Errors.js'; import * as Hex from './Hex.js'; import * as Quantity from './internal/quantity.js'; import * as Transaction from './Transaction.js'; import * as TxEnvelope from './TxEnvelope.js'; /** * Converts a {@link ox#TransactionRequest.Rpc} to a {@link ox#TransactionRequest.TransactionRequest}. * * @example * ```ts twoslash * import { TransactionRequest } from 'ox' * * const request = TransactionRequest.fromRpc({ * to: '0x0000000000000000000000000000000000000000', * value: '0x2386f26fc10000' * }) * ``` * * @param request - The RPC request to convert. * @returns A transaction request. */ export function fromRpc(request) { const request_ = { ...request }; if (typeof request.authorizationList !== 'undefined') request_.authorizationList = Authorization.fromRpcList(request.authorizationList); if (typeof request.chainId !== 'undefined') request_.chainId = Hex.toNumber(request.chainId); if (typeof request.gas !== 'undefined') request_.gas = Hex.toBigInt(request.gas); if (typeof request.gasPrice !== 'undefined') request_.gasPrice = Hex.toBigInt(request.gasPrice); if (typeof request.maxFeePerBlobGas !== 'undefined') request_.maxFeePerBlobGas = Hex.toBigInt(request.maxFeePerBlobGas); if (typeof request.maxFeePerGas !== 'undefined') request_.maxFeePerGas = Hex.toBigInt(request.maxFeePerGas); if (typeof request.maxPriorityFeePerGas !== 'undefined') request_.maxPriorityFeePerGas = Hex.toBigInt(request.maxPriorityFeePerGas); if (typeof request.nonce !== 'undefined') request_.nonce = Hex.toBigInt(request.nonce); if (typeof request.type !== 'undefined') request_.type = Transaction.fromRpcType[request.type] || request.type; if (typeof request.value !== 'undefined') request_.value = Hex.toBigInt(request.value); if (typeof request.yParity !== 'undefined') request_.yParity = Hex.toNumber(request.yParity); if (typeof request.v !== 'undefined') request_.v = Hex.toNumber(request.v); return request_; } /** * Converts a {@link ox#TransactionRequest.TransactionRequest} to a {@link ox#TransactionRequest.Rpc}. * * @example * ```ts twoslash * import { TransactionRequest, Value } from 'ox' * * const request = TransactionRequest.toRpc({ * to: '0x0000000000000000000000000000000000000000', * value: Value.fromEther('0.01') * }) * ``` * * @example * ### Using with a Provider * * You can use {@link ox#Provider.(from:function)} to instantiate an EIP-1193 Provider and * send a transaction to the Wallet using the `eth_sendTransaction` method. * * ```ts twoslash * import 'ox/window' * import { Provider, TransactionRequest, Value } from 'ox' * * const provider = Provider.from(window.ethereum!) * * const request = TransactionRequest.toRpc({ * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', * value: Value.fromEther('0.01') * }) * * const hash = await provider.request({ * // [!code focus] * method: 'eth_sendTransaction', // [!code focus] * params: [request] // [!code focus] * }) // [!code focus] * ``` * * @param request - The request to convert. * @returns An RPC request. */ export function toRpc(request) { const request_rpc = {}; if (typeof request.accessList !== 'undefined') request_rpc.accessList = request.accessList; if (typeof request.authorizationList !== 'undefined') request_rpc.authorizationList = Authorization.toRpcList(request.authorizationList); if (typeof request.blobVersionedHashes !== 'undefined') request_rpc.blobVersionedHashes = request.blobVersionedHashes; if (typeof request.blobs !== 'undefined') request_rpc.blobs = request.blobs; if (typeof request.chainId !== 'undefined') request_rpc.chainId = Quantity.fromNumberish(request.chainId); if (typeof request.data !== 'undefined') { request_rpc.data = request.data; request_rpc.input = request.data; } else if (typeof request.input !== 'undefined') { request_rpc.data = request.input; request_rpc.input = request.input; } if (typeof request.from !== 'undefined') request_rpc.from = request.from; if (typeof request.gas !== 'undefined') request_rpc.gas = Quantity.fromNumberish(request.gas); if (typeof request.gasPrice !== 'undefined') request_rpc.gasPrice = Quantity.fromNumberish(request.gasPrice); if (typeof request.maxFeePerBlobGas !== 'undefined') request_rpc.maxFeePerBlobGas = Quantity.fromNumberish(request.maxFeePerBlobGas); if (typeof request.maxFeePerGas !== 'undefined') request_rpc.maxFeePerGas = Quantity.fromNumberish(request.maxFeePerGas); if (typeof request.maxPriorityFeePerGas !== 'undefined') request_rpc.maxPriorityFeePerGas = Quantity.fromNumberish(request.maxPriorityFeePerGas); if (typeof request.nonce !== 'undefined') request_rpc.nonce = Quantity.fromNumberish(request.nonce); if (typeof request.to !== 'undefined') request_rpc.to = request.to; if (typeof request.type !== 'undefined') request_rpc.type = Transaction.toRpcType[request.type] || request.type; if (typeof request.value !== 'undefined') request_rpc.value = Quantity.fromNumberish(request.value); if (typeof request.r !== 'undefined') request_rpc.r = request.r; if (typeof request.s !== 'undefined') request_rpc.s = request.s; if (typeof request.yParity !== 'undefined') request_rpc.yParity = Quantity.fromNumberish(request.yParity); if (typeof request.v !== 'undefined') request_rpc.v = Quantity.fromNumberish(request.v); return request_rpc; } /** * Converts a {@link ox#TransactionRequest.TransactionRequest} to a {@link ox#(TransactionEnvelope:namespace).TxEnvelope}. * * Dispatches to the correct concrete envelope type via * {@link ox#(TransactionEnvelope:namespace).(getType:function)} (using `request.type` * when present, otherwise inferring from fee/blob/authorization fields), and * drops fields that do not belong to the chosen type. * * For EIP-4844, if `blobs` is provided without `sidecars`, sidecars and * `blobVersionedHashes` are derived via the `kzg` option. If `sidecars` is * already provided, it is passed through unchanged. * * Inputs are expected to be in canonical form (`bigint` numerics and * `'eip1559'`-style `type` strings). Pass RPC-shaped payloads through * {@link ox#TransactionRequest.(fromRpc:function)} first. * * @example * ```ts twoslash * import { TransactionRequest } from 'ox' * * const envelope = TransactionRequest.toEnvelope({ * chainId: 1, * maxFeePerGas: 1n, * to: '0x0000000000000000000000000000000000000000', * value: 1n * }) * // @log: { * // @log: chainId: 1, * // @log: maxFeePerGas: 1n, * // @log: to: '0x0000000000000000000000000000000000000000', * // @log: type: 'eip1559', * // @log: value: 1n, * // @log: } * ``` * * @param request - The transaction request to convert. * @param options - Options. * @returns A transaction envelope. */ export function toEnvelope(request, options = {}) { const type = TxEnvelope.getType(request); if (type === 'legacy') return TxEnvelope.from({ type: 'legacy', ...pickBase(request), ...(typeof request.chainId !== 'undefined' ? { chainId: request.chainId } : {}), ...(typeof request.gasPrice !== 'undefined' ? { gasPrice: request.gasPrice } : {}), }); if (type === 'eip2930') return TxEnvelope.from({ type: 'eip2930', ...pickBase(request), chainId: request.chainId, ...(typeof request.accessList !== 'undefined' ? { accessList: request.accessList } : {}), ...(typeof request.gasPrice !== 'undefined' ? { gasPrice: request.gasPrice } : {}), }); if (type === 'eip4844') { const { kzg } = options; const blobs = request.blobs; const hasSidecars = request.sidecars !== undefined; // Derive sidecars + versioned hashes from raw blobs when possible. const sidecars = (() => { if (hasSidecars) return request.sidecars; if (!blobs || blobs.length === 0) return undefined; if (!kzg) throw new MissingKzgError(); const commitments = Blobs.toCommitments(blobs, { kzg, as: 'Hex' }); const cellProofs = Blobs.toCellProofs(blobs, { kzg, as: 'Hex' }); return { blobs, commitments, cellProofs, }; })(); const blobVersionedHashes = (() => { if (request.blobVersionedHashes) return request.blobVersionedHashes; if (sidecars) { if (!kzg) throw new MissingKzgError(); return Blobs.commitmentsToVersionedHashes(sidecars.commitments, { as: 'Hex', }); } return []; })(); return TxEnvelope.from({ type: 'eip4844', ...pickBase(request), chainId: request.chainId, blobVersionedHashes, ...(typeof request.accessList !== 'undefined' ? { accessList: request.accessList } : {}), ...(typeof request.maxFeePerBlobGas !== 'undefined' ? { maxFeePerBlobGas: request.maxFeePerBlobGas } : {}), ...(typeof request.maxFeePerGas !== 'undefined' ? { maxFeePerGas: request.maxFeePerGas } : {}), ...(typeof request.maxPriorityFeePerGas !== 'undefined' ? { maxPriorityFeePerGas: request.maxPriorityFeePerGas } : {}), ...(sidecars ? { sidecars } : {}), }); } if (type === 'eip7702') { if (!request.authorizationList) throw new MissingAuthorizationListError(); return TxEnvelope.from({ type: 'eip7702', ...pickBase(request), chainId: request.chainId, authorizationList: request.authorizationList, ...(typeof request.accessList !== 'undefined' ? { accessList: request.accessList } : {}), ...(typeof request.maxFeePerGas !== 'undefined' ? { maxFeePerGas: request.maxFeePerGas } : {}), ...(typeof request.maxPriorityFeePerGas !== 'undefined' ? { maxPriorityFeePerGas: request.maxPriorityFeePerGas } : {}), }); } // EIP-1559 (default) return TxEnvelope.from({ type: 'eip1559', ...pickBase(request), chainId: request.chainId, ...(typeof request.accessList !== 'undefined' ? { accessList: request.accessList } : {}), ...(typeof request.maxFeePerGas !== 'undefined' ? { maxFeePerGas: request.maxFeePerGas } : {}), ...(typeof request.maxPriorityFeePerGas !== 'undefined' ? { maxPriorityFeePerGas: request.maxPriorityFeePerGas } : {}), }); } // Picks the fields common to every `TxEnvelope.Base` shape off a // `TransactionRequest`, dropping anything that is `undefined`. function pickBase(request) { const base = {}; if (typeof request.data !== 'undefined') base.data = request.data; if (typeof request.input !== 'undefined') base.input = request.input; if (typeof request.from !== 'undefined') base.from = request.from; if (typeof request.gas !== 'undefined') base.gas = request.gas; if (typeof request.nonce !== 'undefined') base.nonce = request.nonce; if (typeof request.to !== 'undefined') base.to = request.to; if (typeof request.value !== 'undefined') base.value = request.value; if (typeof request.r !== 'undefined') base.r = request.r; if (typeof request.s !== 'undefined') base.s = request.s; if (typeof request.yParity !== 'undefined') base.yParity = request.yParity; if (typeof request.v !== 'undefined') base.v = request.v; return base; } /** Thrown when a 4844 conversion is requested but no `kzg` context is provided. */ export class MissingKzgError extends Errors.BaseError { name = 'TransactionRequest.MissingKzgError'; constructor() { super('A `kzg` option is required to derive 4844 sidecars or `blobVersionedHashes` from `blobs`.', { docsPath: '/api/TransactionRequest/toEnvelope', }); } } /** Thrown when a 7702 conversion is requested but no `authorizationList` is provided. */ export class MissingAuthorizationListError extends Errors.BaseError { name = 'TransactionRequest.MissingAuthorizationListError'; constructor() { super('An `authorizationList` is required to convert a TransactionRequest into an EIP-7702 transaction envelope.'); } } //# sourceMappingURL=TransactionRequest.js.map