From 36e86da58ef0603e0c1fd8db0b9c384ea406b1cd Mon Sep 17 00:00:00 2001 From: jxom Date: Wed, 30 Oct 2024 11:55:47 +1100 Subject: [PATCH] wip: envelope --- src/TransactionEnvelopeEip1559.test-d.ts | 12 +- src/TransactionEnvelopeEip1559.test.ts | 6 +- src/TransactionEnvelopeEip1559.ts | 1205 ++++++++-------- src/TransactionEnvelopeEip2930.test-d.ts | 16 +- src/TransactionEnvelopeEip2930.test.ts | 8 +- src/TransactionEnvelopeEip2930.ts | 1161 ++++++++------- src/TransactionEnvelopeLegacy.test-d.ts | 16 +- src/TransactionEnvelopeLegacy.test.ts | 8 +- src/TransactionEnvelopeLegacy.ts | 1252 ++++++++--------- src/index.ts | 30 +- .../TransactionEnvelope/eip4844/assert.ts | 4 +- .../TransactionEnvelope/eip7702/assert.ts | 4 +- 12 files changed, 1845 insertions(+), 1877 deletions(-) diff --git a/src/TransactionEnvelopeEip1559.test-d.ts b/src/TransactionEnvelopeEip1559.test-d.ts index c4e94ad3..2a0fa41e 100644 --- a/src/TransactionEnvelopeEip1559.test-d.ts +++ b/src/TransactionEnvelopeEip1559.test-d.ts @@ -14,14 +14,18 @@ test('default', () => { readonly value: 69n readonly type: 'eip1559' }>() - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() } { const envelope = TransactionEnvelopeEip1559.from( '0x123' as TransactionEnvelopeEip1559.Serialized, ) - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() } { @@ -42,6 +46,8 @@ test('default', () => { readonly yParity: 0 readonly type: 'eip1559' }>() - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() } }) diff --git a/src/TransactionEnvelopeEip1559.test.ts b/src/TransactionEnvelopeEip1559.test.ts index a997a764..5490f84c 100644 --- a/src/TransactionEnvelopeEip1559.test.ts +++ b/src/TransactionEnvelopeEip1559.test.ts @@ -59,7 +59,9 @@ describe('deserialize', () => { test('default', () => { const serialized = TransactionEnvelopeEip1559.serialize(transaction) const deserialized = TransactionEnvelopeEip1559.deserialize(serialized) - assertType(deserialized) + assertType( + deserialized, + ) expect(deserialized).toEqual(transaction) }) @@ -551,7 +553,7 @@ describe('serialize', () => { const transaction_data = { ...transaction, data: '0x1234', - } satisfies TransactionEnvelopeEip1559 + } satisfies TransactionEnvelopeEip1559.TransactionEnvelopeEip1559 const serialized = TransactionEnvelopeEip1559.serialize(transaction_data) expect(serialized).toEqual( '0x02f10182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000821234c0', diff --git a/src/TransactionEnvelopeEip1559.ts b/src/TransactionEnvelopeEip1559.ts index 6eb8be51..9417cd9a 100644 --- a/src/TransactionEnvelopeEip1559.ts +++ b/src/TransactionEnvelopeEip1559.ts @@ -25,7 +25,7 @@ export type TransactionEnvelopeEip1559< signed extends boolean = boolean, bigintType = bigint, numberType = number, - type extends string = TransactionEnvelopeEip1559.Type, + type extends string = Type, > = Compute< TransactionEnvelope.Base & { /** EIP-2930 Access List. */ @@ -36,642 +36,631 @@ export type TransactionEnvelopeEip1559< maxPriorityFeePerGas?: bigintType | undefined } > -export namespace TransactionEnvelopeEip1559 { - // #region Types - - export type Rpc = - TransactionEnvelopeEip1559 - - export type Serialized = `${SerializedType}${string}` - - export const serializedType = '0x02' as const - export type SerializedType = typeof serializedType - - export type Signed = TransactionEnvelopeEip1559 - - export const type = 'eip1559' as const - export type Type = typeof type - - // #endregion - - // #region Functions - - /** - * Asserts a {@link ox#(TransactionEnvelopeEip1559:type)} is valid. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeEip1559, Value } from 'ox' - * - * TransactionEnvelopeEip1559.assert({ - * maxFeePerGas: 2n ** 256n - 1n + 1n, - * chainId: 1, - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * // @error: FeeCapTooHighError: - * // @error: The fee cap (`masFeePerGas` = 115792089237316195423570985008687907853269984665640564039457584007913 gwei) cannot be - * // @error: higher than the maximum allowed value (2^256-1). - * ``` - * - * @param envelope - The transaction envelope to assert. - */ - export function assert( - envelope: PartialBy, - ) { - const { chainId, maxPriorityFeePerGas, maxFeePerGas, to } = envelope - if (chainId <= 0) - throw new TransactionEnvelope.InvalidChainIdError({ chainId }) - if (to) Address_assert(to, { strict: false }) - if (maxFeePerGas && BigInt(maxFeePerGas) > 2n ** 256n - 1n) - throw new TransactionEnvelope.FeeCapTooHighError({ feeCap: maxFeePerGas }) - if ( - maxPriorityFeePerGas && - maxFeePerGas && - maxPriorityFeePerGas > maxFeePerGas - ) - throw new TransactionEnvelope.TipAboveFeeCapError({ - maxFeePerGas, - maxPriorityFeePerGas, - }) - } - export declare namespace assert { - type ErrorType = - | Address_assert.ErrorType - | TransactionEnvelope.InvalidChainIdError - | TransactionEnvelope.FeeCapTooHighError - | TransactionEnvelope.TipAboveFeeCapError - | Errors.GlobalErrorType - } +export type Rpc = TransactionEnvelopeEip1559< + signed, + Hex.Hex, + Hex.Hex, + '0x2' +> - assert.parseError = (error: unknown) => - /* v8 ignore next */ - error as assert.ErrorType - - /** - * Deserializes a {@link ox#(TransactionEnvelopeEip1559:type)} from its serialized form. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeEip1559 } from 'ox' - * - * const envelope = TransactionEnvelopeEip1559.deserialize('0x02ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0') - * // @log: { - * // @log: type: 'eip1559', - * // @log: nonce: 785n, - * // @log: maxFeePerGas: 2000000000n, - * // @log: gas: 1000000n, - * // @log: to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * // @log: value: 1000000000000000000n, - * // @log: } - * ``` - * - * @param serializedTransaction - The serialized transaction. - * @returns Deserialized Transaction Envelope. - */ - export function deserialize( - serializedTransaction: TransactionEnvelopeEip1559.Serialized, - ): Compute { - const transactionArray = Rlp_toHex(Hex.slice(serializedTransaction, 1)) - - const [ - chainId, - nonce, - maxPriorityFeePerGas, +export type Serialized = `${SerializedType}${string}` + +export const serializedType = '0x02' as const +export type SerializedType = typeof serializedType + +export type Signed = TransactionEnvelopeEip1559 + +export const type = 'eip1559' as const +export type Type = typeof type + +/** + * Asserts a {@link ox#TransactionEnvelopeEip1559.TransactionEnvelopeEip1559} is valid. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeEip1559, Value } from 'ox' + * + * TransactionEnvelopeEip1559.assert({ + * maxFeePerGas: 2n ** 256n - 1n + 1n, + * chainId: 1, + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * // @error: FeeCapTooHighError: + * // @error: The fee cap (`masFeePerGas` = 115792089237316195423570985008687907853269984665640564039457584007913 gwei) cannot be + * // @error: higher than the maximum allowed value (2^256-1). + * ``` + * + * @param envelope - The transaction envelope to assert. + */ +export function assert( + envelope: PartialBy, +) { + const { chainId, maxPriorityFeePerGas, maxFeePerGas, to } = envelope + if (chainId <= 0) + throw new TransactionEnvelope.InvalidChainIdError({ chainId }) + if (to) Address_assert(to, { strict: false }) + if (maxFeePerGas && BigInt(maxFeePerGas) > 2n ** 256n - 1n) + throw new TransactionEnvelope.FeeCapTooHighError({ feeCap: maxFeePerGas }) + if ( + maxPriorityFeePerGas && + maxFeePerGas && + maxPriorityFeePerGas > maxFeePerGas + ) + throw new TransactionEnvelope.TipAboveFeeCapError({ maxFeePerGas, - gas, - to, - value, - data, - accessList, - yParity, - r, - s, - ] = transactionArray as readonly Hex.Hex[] - - if (!(transactionArray.length === 9 || transactionArray.length === 12)) - throw new TransactionEnvelope.InvalidSerializedError({ - attributes: { - chainId, - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - gas, - to, - value, - data, - accessList, - ...(transactionArray.length > 9 - ? { - yParity, - r, - s, - } - : {}), - }, - serializedTransaction, - type: 'eip1559', - }) - - let transaction = { - chainId: Number(chainId), - type: 'eip1559', - } as TransactionEnvelopeEip1559 - if (Hex.validate(to) && to !== '0x') transaction.to = to - if (Hex.validate(gas) && gas !== '0x') transaction.gas = BigInt(gas) - if (Hex.validate(data) && data !== '0x') transaction.data = data - if (Hex.validate(nonce) && nonce !== '0x') transaction.nonce = BigInt(nonce) - if (Hex.validate(value) && value !== '0x') transaction.value = BigInt(value) - if (Hex.validate(maxFeePerGas) && maxFeePerGas !== '0x') - transaction.maxFeePerGas = BigInt(maxFeePerGas) - if (Hex.validate(maxPriorityFeePerGas) && maxPriorityFeePerGas !== '0x') - transaction.maxPriorityFeePerGas = BigInt(maxPriorityFeePerGas) - if (accessList!.length !== 0 && accessList !== '0x') - transaction.accessList = AccessList_fromTupleList(accessList as any) - - const signature = - r && s && yParity ? Signature_fromTuple([yParity, r, s]) : undefined - if (signature) - transaction = { - ...transaction, - ...signature, - } as TransactionEnvelopeEip1559 - - TransactionEnvelopeEip1559.assert(transaction) - - return transaction - } + maxPriorityFeePerGas, + }) +} - export declare namespace deserialize { - type ErrorType = Errors.GlobalErrorType - } +export declare namespace assert { + type ErrorType = + | Address_assert.ErrorType + | TransactionEnvelope.InvalidChainIdError + | TransactionEnvelope.FeeCapTooHighError + | TransactionEnvelope.TipAboveFeeCapError + | Errors.GlobalErrorType +} - deserialize.parseError = (error: unknown) => - /* v8 ignore next */ - error as deserialize.ErrorType - - /** - * Converts an arbitrary transaction object into an EIP-1559 Transaction Envelope. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeEip1559, Value } from 'ox' - * - * const envelope = TransactionEnvelopeEip1559.from({ - * chainId: 1, - * maxFeePerGas: Value.fromGwei('10'), - * maxPriorityFeePerGas: Value.fromGwei('1'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * ``` - * - * @example - * ### Attaching Signatures - * - * It is possible to attach a `signature` to the transaction envelope. - * - * ```ts twoslash - * import { Secp256k1, TransactionEnvelopeEip1559, Value } from 'ox' - * - * const envelope = TransactionEnvelopeEip1559.from({ - * chainId: 1, - * maxFeePerGas: Value.fromGwei('10'), - * maxPriorityFeePerGas: Value.fromGwei('1'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * - * const signature = Secp256k1.sign({ - * payload: TransactionEnvelopeEip1559.getSignPayload(envelope), - * privateKey: '0x...', - * }) - * - * const envelope_signed = TransactionEnvelopeEip1559.from(envelope, { // [!code focus] - * signature, // [!code focus] - * }) // [!code focus] - * // @log: { - * // @log: chainId: 1, - * // @log: maxFeePerGas: 10000000000n, - * // @log: maxPriorityFeePerGas: 1000000000n, - * // @log: to: '0x0000000000000000000000000000000000000000', - * // @log: type: 'eip1559', - * // @log: value: 1000000000000000000n, - * // @log: r: 125...n, - * // @log: s: 642...n, - * // @log: yParity: 0, - * // @log: } - * ``` - * - * @example - * ### From Serialized - * - * It is possible to instantiate an EIP-1559 Transaction Envelope from a {@link ox#(TransactionEnvelopeEip1559:namespace).Serialized} value. - * - * ```ts twoslash - * import { TransactionEnvelopeEip1559 } from 'ox' - * - * const envelope = TransactionEnvelopeEip1559.from('0x02f858018203118502540be4008504a817c800809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c08477359400e1a001627c687261b0e7f8638af1112efa8a77e23656f6e7945275b19e9deed80261') - * // @log: { - * // @log: chainId: 1, - * // @log: maxFeePerGas: 10000000000n, - * // @log: maxPriorityFeePerGas: 1000000000n, - * // @log: to: '0x0000000000000000000000000000000000000000', - * // @log: type: 'eip1559', - * // @log: value: 1000000000000000000n, - * // @log: } - * ``` - * - * @param envelope - The transaction object to convert. - * @param options - Options. - * @returns An EIP-1559 Transaction Envelope. - */ - export function from< - const envelope extends - | UnionPartialBy - | TransactionEnvelopeEip1559.Serialized, - const signature extends Signature | undefined = undefined, - >( - envelope: - | envelope - | UnionPartialBy - | TransactionEnvelopeEip1559.Serialized, - options: TransactionEnvelopeEip1559.from.Options = {}, - ): TransactionEnvelopeEip1559.from.ReturnType { - const { signature } = options - - const envelope_ = ( - typeof envelope === 'string' - ? TransactionEnvelopeEip1559.deserialize(envelope) - : envelope - ) as TransactionEnvelopeEip1559 - - TransactionEnvelopeEip1559.assert(envelope_) - - return { - ...envelope_, - ...(signature ? Signature_from(signature) : {}), +assert.parseError = (error: unknown) => + /* v8 ignore next */ + error as assert.ErrorType + +/** + * Deserializes a {@link ox#TransactionEnvelopeEip1559.TransactionEnvelopeEip1559} from its serialized form. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeEip1559 } from 'ox' + * + * const envelope = TransactionEnvelopeEip1559.deserialize('0x02ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0') + * // @log: { + * // @log: type: 'eip1559', + * // @log: nonce: 785n, + * // @log: maxFeePerGas: 2000000000n, + * // @log: gas: 1000000n, + * // @log: to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * // @log: value: 1000000000000000000n, + * // @log: } + * ``` + * + * @param serializedTransaction - The serialized transaction. + * @returns Deserialized Transaction Envelope. + */ +export function deserialize( + serializedTransaction: Serialized, +): Compute { + const transactionArray = Rlp_toHex(Hex.slice(serializedTransaction, 1)) + + const [ + chainId, + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gas, + to, + value, + data, + accessList, + yParity, + r, + s, + ] = transactionArray as readonly Hex.Hex[] + + if (!(transactionArray.length === 9 || transactionArray.length === 12)) + throw new TransactionEnvelope.InvalidSerializedError({ + attributes: { + chainId, + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gas, + to, + value, + data, + accessList, + ...(transactionArray.length > 9 + ? { + yParity, + r, + s, + } + : {}), + }, + serializedTransaction, type: 'eip1559', - } as never - } + }) + + let transaction = { + chainId: Number(chainId), + type: 'eip1559', + } as TransactionEnvelopeEip1559 + if (Hex.validate(to) && to !== '0x') transaction.to = to + if (Hex.validate(gas) && gas !== '0x') transaction.gas = BigInt(gas) + if (Hex.validate(data) && data !== '0x') transaction.data = data + if (Hex.validate(nonce) && nonce !== '0x') transaction.nonce = BigInt(nonce) + if (Hex.validate(value) && value !== '0x') transaction.value = BigInt(value) + if (Hex.validate(maxFeePerGas) && maxFeePerGas !== '0x') + transaction.maxFeePerGas = BigInt(maxFeePerGas) + if (Hex.validate(maxPriorityFeePerGas) && maxPriorityFeePerGas !== '0x') + transaction.maxPriorityFeePerGas = BigInt(maxPriorityFeePerGas) + if (accessList!.length !== 0 && accessList !== '0x') + transaction.accessList = AccessList_fromTupleList(accessList as any) + + const signature = + r && s && yParity ? Signature_fromTuple([yParity, r, s]) : undefined + if (signature) + transaction = { + ...transaction, + ...signature, + } as TransactionEnvelopeEip1559 - export declare namespace from { - type Options = { - signature?: signature | Signature | undefined - } - - type ReturnType< - envelope extends - | UnionPartialBy - | Hex.Hex = TransactionEnvelopeEip1559 | Hex.Hex, - signature extends Signature | undefined = undefined, - > = Compute< - envelope extends Hex.Hex - ? TransactionEnvelopeEip1559 - : Assign< - envelope, - (signature extends Signature ? Readonly : {}) & { - readonly type: 'eip1559' - } - > - > + assert(transaction) - type ErrorType = - | TransactionEnvelopeEip1559.deserialize.ErrorType - | TransactionEnvelopeEip1559.assert.ErrorType - | Errors.GlobalErrorType - } + return transaction +} - from.parseError = (error: unknown) => - /* v8 ignore next */ - error as from.ErrorType - - /** - * Returns the payload to sign for a {@link ox#(TransactionEnvelopeEip1559:type)}. - * - * @example - * The example below demonstrates how to compute the sign payload which can be used - * with ECDSA signing utilities like {@link ox#Secp256k1.(sign:function)}. - * - * ```ts twoslash - * import { Secp256k1, TransactionEnvelopeEip1559 } from 'ox' - * - * const envelope = TransactionEnvelopeEip1559.from({ - * chainId: 1, - * nonce: 0n, - * maxFeePerGas: 1000000000n, - * gas: 21000n, - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: 1000000000000000000n, - * }) - * - * const payload = TransactionEnvelopeEip1559.getSignPayload(envelope) // [!code focus] - * // @log: '0x...' - * - * const signature = Secp256k1.sign({ payload, privateKey: '0x...' }) - * ``` - * - * @param envelope - The transaction envelope to get the sign payload for. - * @returns The sign payload. - */ - export function getSignPayload( - envelope: TransactionEnvelopeEip1559, - ): TransactionEnvelopeEip1559.getSignPayload.ReturnType { - return TransactionEnvelopeEip1559.hash(envelope, { presign: true }) - } +export declare namespace deserialize { + type ErrorType = Errors.GlobalErrorType +} - export declare namespace getSignPayload { - type ReturnType = Hex.Hex +deserialize.parseError = (error: unknown) => + /* v8 ignore next */ + error as deserialize.ErrorType + +/** + * Converts an arbitrary transaction object into an EIP-1559 Transaction Envelope. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeEip1559, Value } from 'ox' + * + * const envelope = TransactionEnvelopeEip1559.from({ + * chainId: 1, + * maxFeePerGas: Value.fromGwei('10'), + * maxPriorityFeePerGas: Value.fromGwei('1'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * ``` + * + * @example + * ### Attaching Signatures + * + * It is possible to attach a `signature` to the transaction envelope. + * + * ```ts twoslash + * import { Secp256k1, TransactionEnvelopeEip1559, Value } from 'ox' + * + * const envelope = TransactionEnvelopeEip1559.from({ + * chainId: 1, + * maxFeePerGas: Value.fromGwei('10'), + * maxPriorityFeePerGas: Value.fromGwei('1'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * + * const signature = Secp256k1.sign({ + * payload: TransactionEnvelopeEip1559.getSignPayload(envelope), + * privateKey: '0x...', + * }) + * + * const envelope_signed = TransactionEnvelopeEip1559.from(envelope, { // [!code focus] + * signature, // [!code focus] + * }) // [!code focus] + * // @log: { + * // @log: chainId: 1, + * // @log: maxFeePerGas: 10000000000n, + * // @log: maxPriorityFeePerGas: 1000000000n, + * // @log: to: '0x0000000000000000000000000000000000000000', + * // @log: type: 'eip1559', + * // @log: value: 1000000000000000000n, + * // @log: r: 125...n, + * // @log: s: 642...n, + * // @log: yParity: 0, + * // @log: } + * ``` + * + * @example + * ### From Serialized + * + * It is possible to instantiate an EIP-1559 Transaction Envelope from a {@link ox#TransactionEnvelopeEip1559.Serialized} value. + * + * ```ts twoslash + * import { TransactionEnvelopeEip1559 } from 'ox' + * + * const envelope = TransactionEnvelopeEip1559.from('0x02f858018203118502540be4008504a817c800809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c08477359400e1a001627c687261b0e7f8638af1112efa8a77e23656f6e7945275b19e9deed80261') + * // @log: { + * // @log: chainId: 1, + * // @log: maxFeePerGas: 10000000000n, + * // @log: maxPriorityFeePerGas: 1000000000n, + * // @log: to: '0x0000000000000000000000000000000000000000', + * // @log: type: 'eip1559', + * // @log: value: 1000000000000000000n, + * // @log: } + * ``` + * + * @param envelope - The transaction object to convert. + * @param options - Options. + * @returns An EIP-1559 Transaction Envelope. + */ +export function from< + const envelope extends + | UnionPartialBy + | Serialized, + const signature extends Signature | undefined = undefined, +>( + envelope: + | envelope + | UnionPartialBy + | Serialized, + options: from.Options = {}, +): from.ReturnType { + const { signature } = options + + const envelope_ = ( + typeof envelope === 'string' ? deserialize(envelope) : envelope + ) as TransactionEnvelopeEip1559 + + assert(envelope_) + + return { + ...envelope_, + ...(signature ? Signature_from(signature) : {}), + type: 'eip1559', + } as never +} - type ErrorType = - | TransactionEnvelopeEip1559.hash.ErrorType - | Errors.GlobalErrorType +export declare namespace from { + type Options = { + signature?: signature | Signature | undefined } - getSignPayload.parseError = (error: unknown) => - /* v8 ignore next */ - error as getSignPayload.ErrorType - - /** - * Hashes a {@link ox#(TransactionEnvelopeEip1559:type)}. This is the "transaction hash". - * - * @example - * ```ts twoslash - * import { Secp256k1, TransactionEnvelopeEip1559 } from 'ox' - * - * const envelope = TransactionEnvelopeEip1559.from({ - * chainId: 1, - * nonce: 0n, - * maxFeePerGas: 1000000000n, - * gas: 21000n, - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: 1000000000000000000n, - * }) - * - * const signature = Secp256k1.sign({ - * payload: TransactionEnvelopeEip1559.getSignPayload(envelope), - * privateKey: '0x...' - * }) - * - * const envelope_signed = TransactionEnvelopeEip1559.from(envelope, { signature }) - * - * const hash = TransactionEnvelopeEip1559.hash(envelope_signed) // [!code focus] - * ``` - * - * @param envelope - The EIP-1559 Transaction Envelope to hash. - * @param options - Options. - * @returns The hash of the transaction envelope. - */ - export function hash( - envelope: TransactionEnvelopeEip1559, - options: TransactionEnvelopeEip1559.hash.Options = {}, - ): TransactionEnvelopeEip1559.hash.ReturnType { - const { presign } = options - return Hash_keccak256( - TransactionEnvelopeEip1559.serialize({ - ...envelope, - ...(presign - ? { - r: undefined, - s: undefined, - yParity: undefined, - v: undefined, - } - : {}), - }), - ) - } + type ReturnType< + envelope extends + | UnionPartialBy + | Hex.Hex = TransactionEnvelopeEip1559 | Hex.Hex, + signature extends Signature | undefined = undefined, + > = Compute< + envelope extends Hex.Hex + ? TransactionEnvelopeEip1559 + : Assign< + envelope, + (signature extends Signature ? Readonly : {}) & { + readonly type: 'eip1559' + } + > + > - export declare namespace hash { - type Options = { - /** Whether to hash this transaction for signing. @default false */ - presign?: presign | boolean | undefined - } + type ErrorType = + | deserialize.ErrorType + | assert.ErrorType + | Errors.GlobalErrorType +} - type ReturnType = Hex.Hex +from.parseError = (error: unknown) => + /* v8 ignore next */ + error as from.ErrorType + +/** + * Returns the payload to sign for a {@link ox#TransactionEnvelopeEip1559.TransactionEnvelopeEip1559}. + * + * @example + * The example below demonstrates how to compute the sign payload which can be used + * with ECDSA signing utilities like {@link ox#Secp256k1.(sign:function)}. + * + * ```ts twoslash + * import { Secp256k1, TransactionEnvelopeEip1559 } from 'ox' + * + * const envelope = TransactionEnvelopeEip1559.from({ + * chainId: 1, + * nonce: 0n, + * maxFeePerGas: 1000000000n, + * gas: 21000n, + * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * value: 1000000000000000000n, + * }) + * + * const payload = TransactionEnvelopeEip1559.getSignPayload(envelope) // [!code focus] + * // @log: '0x...' + * + * const signature = Secp256k1.sign({ payload, privateKey: '0x...' }) + * ``` + * + * @param envelope - The transaction envelope to get the sign payload for. + * @returns The sign payload. + */ +export function getSignPayload( + envelope: TransactionEnvelopeEip1559, +): getSignPayload.ReturnType { + return hash(envelope, { presign: true }) +} - type ErrorType = - | Hash_keccak256.ErrorType - | TransactionEnvelopeEip1559.serialize.ErrorType - | Errors.GlobalErrorType - } +export declare namespace getSignPayload { + type ReturnType = Hex.Hex - hash.parseError = (error: unknown) => - /* v8 ignore next */ - error as hash.ErrorType - - /** - * Serializes a {@link ox#(TransactionEnvelopeEip1559:type)}. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeEip1559, Value } from 'ox' - * - * const envelope = TransactionEnvelopeEip1559.from({ - * chainId: 1, - * maxFeePerGas: Value.fromGwei('10'), - * maxPriorityFeePerGas: Value.fromGwei('1'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * - * const serialized = TransactionEnvelopeEip1559.serialize(envelope) // [!code focus] - * ``` - * - * @example - * ### Attaching Signatures - * - * It is possible to attach a `signature` to the serialized Transaction Envelope. - * - * ```ts twoslash - * import { Secp256k1, TransactionEnvelopeEip1559, Value } from 'ox' - * - * const envelope = TransactionEnvelopeEip1559.from({ - * chainId: 1, - * maxFeePerGas: Value.fromGwei('10'), - * maxPriorityFeePerGas: Value.fromGwei('1'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * - * const signature = Secp256k1.sign({ - * payload: TransactionEnvelopeEip1559.getSignPayload(envelope), - * privateKey: '0x...', - * }) - * - * const serialized = TransactionEnvelopeEip1559.serialize(envelope, { // [!code focus] - * signature, // [!code focus] - * }) // [!code focus] - * - * // ... send `serialized` transaction to JSON-RPC `eth_sendRawTransaction` - * ``` - * - * @param envelope - The Transaction Envelope to serialize. - * @param options - Options. - * @returns The serialized Transaction Envelope. - */ - export function serialize( - envelope: PartialBy, - options: TransactionEnvelopeEip1559.serialize.Options = {}, - ): TransactionEnvelopeEip1559.Serialized { - const { - chainId, - gas, - nonce, - to, - value, - maxFeePerGas, - maxPriorityFeePerGas, - accessList, - data, - input, - } = envelope - - TransactionEnvelopeEip1559.assert(envelope) - - const accessTupleList = AccessList_toTupleList(accessList) - - const signature = Signature_extract(options.signature || envelope) - - const serializedTransaction = [ - Hex.fromNumber(chainId), - nonce ? Hex.fromNumber(nonce) : '0x', - maxPriorityFeePerGas ? Hex.fromNumber(maxPriorityFeePerGas) : '0x', - maxFeePerGas ? Hex.fromNumber(maxFeePerGas) : '0x', - gas ? Hex.fromNumber(gas) : '0x', - to ?? '0x', - value ? Hex.fromNumber(value) : '0x', - data ?? input ?? '0x', - accessTupleList, - ...(signature ? Signature_toTuple(signature) : []), - ] - - return Hex.concat( - TransactionEnvelopeEip1559.serializedType, - Rlp_fromHex(serializedTransaction), - ) as TransactionEnvelopeEip1559.Serialized - } - - export declare namespace serialize { - type Options = { - /** Signature to append to the serialized Transaction Envelope. */ - signature?: Signature | undefined - } - - type ErrorType = - | TransactionEnvelopeEip1559.assert.ErrorType - | Hex.fromNumber.ErrorType - | Signature_toTuple.ErrorType - | Hex.concat.ErrorType - | Rlp_fromHex.ErrorType - | Errors.GlobalErrorType - } + type ErrorType = hash.ErrorType | Errors.GlobalErrorType +} +getSignPayload.parseError = (error: unknown) => /* v8 ignore next */ - serialize.parseError = (error: unknown) => error as serialize.ErrorType - - /** - * Converts an {@link ox#(TransactionEnvelopeEip1559:type)} to an {@link ox#(TransactionEnvelopeEip1559:namespace).Rpc}. - * - * @example - * ```ts twoslash - * import { RpcRequest, TransactionEnvelopeEip1559, Value } from 'ox' - * - * const envelope = TransactionEnvelopeEip1559.from({ - * chainId: 1, - * nonce: 0n, - * gas: 21000n, - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: Value.fromEther('1'), - * }) - * - * const envelope_rpc = TransactionEnvelopeEip1559.toRpc(envelope) // [!code focus] - * - * const request = RpcRequest.from({ - * id: 0, - * method: 'eth_sendTransaction', - * params: [envelope_rpc], - * }) - * ``` - * - * @param envelope - The EIP-1559 transaction envelope to convert. - * @returns An RPC-formatted EIP-1559 transaction envelope. - */ - export function toRpc( - envelope: Omit, - ): TransactionEnvelopeEip1559.Rpc { - const signature = Signature_extract(envelope) - - return { + error as getSignPayload.ErrorType + +/** + * Hashes a {@link ox#TransactionEnvelopeEip1559.TransactionEnvelopeEip1559}. This is the "transaction hash". + * + * @example + * ```ts twoslash + * import { Secp256k1, TransactionEnvelopeEip1559 } from 'ox' + * + * const envelope = TransactionEnvelopeEip1559.from({ + * chainId: 1, + * nonce: 0n, + * maxFeePerGas: 1000000000n, + * gas: 21000n, + * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * value: 1000000000000000000n, + * }) + * + * const signature = Secp256k1.sign({ + * payload: TransactionEnvelopeEip1559.getSignPayload(envelope), + * privateKey: '0x...' + * }) + * + * const envelope_signed = TransactionEnvelopeEip1559.from(envelope, { signature }) + * + * const hash = TransactionEnvelopeEip1559.hash(envelope_signed) // [!code focus] + * ``` + * + * @param envelope - The EIP-1559 Transaction Envelope to hash. + * @param options - Options. + * @returns The hash of the transaction envelope. + */ +export function hash( + envelope: TransactionEnvelopeEip1559, + options: hash.Options = {}, +): hash.ReturnType { + const { presign } = options + return Hash_keccak256( + serialize({ ...envelope, - chainId: Hex.fromNumber(envelope.chainId), - data: envelope.data ?? envelope.input, - type: '0x2', - ...(typeof envelope.gas === 'bigint' - ? { gas: Hex.fromNumber(envelope.gas) } - : {}), - ...(typeof envelope.nonce === 'bigint' - ? { nonce: Hex.fromNumber(envelope.nonce) } - : {}), - ...(typeof envelope.value === 'bigint' - ? { value: Hex.fromNumber(envelope.value) } - : {}), - ...(typeof envelope.maxFeePerGas === 'bigint' - ? { maxFeePerGas: Hex.fromNumber(envelope.maxFeePerGas) } - : {}), - ...(typeof envelope.maxPriorityFeePerGas === 'bigint' + ...(presign ? { - maxPriorityFeePerGas: Hex.fromNumber(envelope.maxPriorityFeePerGas), + r: undefined, + s: undefined, + yParity: undefined, + v: undefined, } : {}), - ...(signature ? Signature_toRpc(signature) : {}), - } as never - } + }), + ) +} - export declare namespace toRpc { - export type ErrorType = Signature_extract.ErrorType | Errors.GlobalErrorType +export declare namespace hash { + type Options = { + /** Whether to hash this transaction for signing. @default false */ + presign?: presign | boolean | undefined } + type ReturnType = Hex.Hex + + type ErrorType = + | Hash_keccak256.ErrorType + | serialize.ErrorType + | Errors.GlobalErrorType +} + +hash.parseError = (error: unknown) => /* v8 ignore next */ - toRpc.parseError = (error: unknown) => error as toRpc.ErrorType - - /** - * Validates a {@link ox#(TransactionEnvelopeEip1559:type)}. Returns `true` if the envelope is valid, `false` otherwise. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeEip1559, Value } from 'ox' - * - * const valid = TransactionEnvelopeEip1559.assert({ - * maxFeePerGas: 2n ** 256n - 1n + 1n, - * chainId: 1, - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * // @log: false - * ``` - * - * @param envelope - The transaction envelope to validate. - */ - export function validate( - envelope: PartialBy, - ) { - try { - TransactionEnvelopeEip1559.assert(envelope) - return true - } catch { - return false - } - } + error as hash.ErrorType + +/** + * Serializes a {@link ox#TransactionEnvelopeEip1559.TransactionEnvelopeEip1559}. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeEip1559, Value } from 'ox' + * + * const envelope = TransactionEnvelopeEip1559.from({ + * chainId: 1, + * maxFeePerGas: Value.fromGwei('10'), + * maxPriorityFeePerGas: Value.fromGwei('1'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * + * const serialized = TransactionEnvelopeEip1559.serialize(envelope) // [!code focus] + * ``` + * + * @example + * ### Attaching Signatures + * + * It is possible to attach a `signature` to the serialized Transaction Envelope. + * + * ```ts twoslash + * import { Secp256k1, TransactionEnvelopeEip1559, Value } from 'ox' + * + * const envelope = TransactionEnvelopeEip1559.from({ + * chainId: 1, + * maxFeePerGas: Value.fromGwei('10'), + * maxPriorityFeePerGas: Value.fromGwei('1'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * + * const signature = Secp256k1.sign({ + * payload: TransactionEnvelopeEip1559.getSignPayload(envelope), + * privateKey: '0x...', + * }) + * + * const serialized = TransactionEnvelopeEip1559.serialize(envelope, { // [!code focus] + * signature, // [!code focus] + * }) // [!code focus] + * + * // ... send `serialized` transaction to JSON-RPC `eth_sendRawTransaction` + * ``` + * + * @param envelope - The Transaction Envelope to serialize. + * @param options - Options. + * @returns The serialized Transaction Envelope. + */ +export function serialize( + envelope: PartialBy, + options: serialize.Options = {}, +): Serialized { + const { + chainId, + gas, + nonce, + to, + value, + maxFeePerGas, + maxPriorityFeePerGas, + accessList, + data, + input, + } = envelope + + assert(envelope) + + const accessTupleList = AccessList_toTupleList(accessList) + + const signature = Signature_extract(options.signature || envelope) + + const serializedTransaction = [ + Hex.fromNumber(chainId), + nonce ? Hex.fromNumber(nonce) : '0x', + maxPriorityFeePerGas ? Hex.fromNumber(maxPriorityFeePerGas) : '0x', + maxFeePerGas ? Hex.fromNumber(maxFeePerGas) : '0x', + gas ? Hex.fromNumber(gas) : '0x', + to ?? '0x', + value ? Hex.fromNumber(value) : '0x', + data ?? input ?? '0x', + accessTupleList, + ...(signature ? Signature_toTuple(signature) : []), + ] + + return Hex.concat( + serializedType, + Rlp_fromHex(serializedTransaction), + ) as Serialized +} - export declare namespace validate { - type ErrorType = Errors.GlobalErrorType +export declare namespace serialize { + type Options = { + /** Signature to append to the serialized Transaction Envelope. */ + signature?: Signature | undefined } - validate.parseError = (error: unknown) => - /* v8 ignore next */ - error as validate.ErrorType + type ErrorType = + | assert.ErrorType + | Hex.fromNumber.ErrorType + | Signature_toTuple.ErrorType + | Hex.concat.ErrorType + | Rlp_fromHex.ErrorType + | Errors.GlobalErrorType +} + +/* v8 ignore next */ +serialize.parseError = (error: unknown) => error as serialize.ErrorType + +/** + * Converts an {@link ox#TransactionEnvelopeEip1559.TransactionEnvelopeEip1559} to an {@link ox#TransactionEnvelopeEip1559.Rpc}. + * + * @example + * ```ts twoslash + * import { RpcRequest, TransactionEnvelopeEip1559, Value } from 'ox' + * + * const envelope = TransactionEnvelopeEip1559.from({ + * chainId: 1, + * nonce: 0n, + * gas: 21000n, + * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * value: Value.fromEther('1'), + * }) + * + * const envelope_rpc = TransactionEnvelopeEip1559.toRpc(envelope) // [!code focus] + * + * const request = RpcRequest.from({ + * id: 0, + * method: 'eth_sendTransaction', + * params: [envelope_rpc], + * }) + * ``` + * + * @param envelope - The EIP-1559 transaction envelope to convert. + * @returns An RPC-formatted EIP-1559 transaction envelope. + */ +export function toRpc(envelope: Omit): Rpc { + const signature = Signature_extract(envelope) + + return { + ...envelope, + chainId: Hex.fromNumber(envelope.chainId), + data: envelope.data ?? envelope.input, + type: '0x2', + ...(typeof envelope.gas === 'bigint' + ? { gas: Hex.fromNumber(envelope.gas) } + : {}), + ...(typeof envelope.nonce === 'bigint' + ? { nonce: Hex.fromNumber(envelope.nonce) } + : {}), + ...(typeof envelope.value === 'bigint' + ? { value: Hex.fromNumber(envelope.value) } + : {}), + ...(typeof envelope.maxFeePerGas === 'bigint' + ? { maxFeePerGas: Hex.fromNumber(envelope.maxFeePerGas) } + : {}), + ...(typeof envelope.maxPriorityFeePerGas === 'bigint' + ? { + maxPriorityFeePerGas: Hex.fromNumber(envelope.maxPriorityFeePerGas), + } + : {}), + ...(signature ? Signature_toRpc(signature) : {}), + } as never +} + +export declare namespace toRpc { + export type ErrorType = Signature_extract.ErrorType | Errors.GlobalErrorType +} + +/* v8 ignore next */ +toRpc.parseError = (error: unknown) => error as toRpc.ErrorType + +/** + * Validates a {@link ox#TransactionEnvelopeEip1559.TransactionEnvelopeEip1559}. Returns `true` if the envelope is valid, `false` otherwise. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeEip1559, Value } from 'ox' + * + * const valid = TransactionEnvelopeEip1559.assert({ + * maxFeePerGas: 2n ** 256n - 1n + 1n, + * chainId: 1, + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * // @log: false + * ``` + * + * @param envelope - The transaction envelope to validate. + */ +export function validate( + envelope: PartialBy, +) { + try { + assert(envelope) + return true + } catch { + return false + } +} - // #endregion +export declare namespace validate { + type ErrorType = Errors.GlobalErrorType } + +validate.parseError = (error: unknown) => + /* v8 ignore next */ + error as validate.ErrorType diff --git a/src/TransactionEnvelopeEip2930.test-d.ts b/src/TransactionEnvelopeEip2930.test-d.ts index 0fd9607a..b61d0f7f 100644 --- a/src/TransactionEnvelopeEip2930.test-d.ts +++ b/src/TransactionEnvelopeEip2930.test-d.ts @@ -14,14 +14,18 @@ test('default', () => { readonly value: 69n readonly type: 'eip2930' }>() - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() } { const envelope = TransactionEnvelopeEip2930.from( '0x123' as TransactionEnvelopeEip2930.Serialized, ) - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() } { @@ -42,7 +46,9 @@ test('default', () => { readonly yParity: 0 readonly type: 'eip2930' }>() - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() } }) @@ -70,5 +76,7 @@ test('options: signature', () => { readonly yParity: 0 readonly type: 'eip2930' }>() - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() }) diff --git a/src/TransactionEnvelopeEip2930.test.ts b/src/TransactionEnvelopeEip2930.test.ts index 58ff5c2c..1668bcd5 100644 --- a/src/TransactionEnvelopeEip2930.test.ts +++ b/src/TransactionEnvelopeEip2930.test.ts @@ -54,7 +54,9 @@ describe('deserialize', () => { test('default', () => { const serialized = TransactionEnvelopeEip2930.serialize(transaction) const deserialized = TransactionEnvelopeEip2930.deserialize(serialized) - assertType(deserialized) + assertType( + deserialized, + ) expect(deserialized).toEqual(transaction) }) @@ -184,7 +186,9 @@ describe('from', () => { chainId: 1, gasPrice: 69420n, }) - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() expect(envelope).toMatchInlineSnapshot(` { "accessList": [], diff --git a/src/TransactionEnvelopeEip2930.ts b/src/TransactionEnvelopeEip2930.ts index 5b998f7d..5ff2d0ee 100644 --- a/src/TransactionEnvelopeEip2930.ts +++ b/src/TransactionEnvelopeEip2930.ts @@ -25,7 +25,7 @@ export type TransactionEnvelopeEip2930< signed extends boolean = boolean, bigintType = bigint, numberType = number, - type extends string = TransactionEnvelopeEip2930.Type, + type extends string = Type, > = Compute< TransactionEnvelope.Base & { /** EIP-2930 Access List. */ @@ -34,621 +34,598 @@ export type TransactionEnvelopeEip2930< gasPrice?: bigintType | undefined } > -export namespace TransactionEnvelopeEip2930 { - // #region Types - - export type Rpc = - TransactionEnvelopeEip2930 - - export type Serialized = `${SerializedType}${string}` - - export const serializedType = '0x01' as const - export type SerializedType = typeof serializedType - - export type Signed = TransactionEnvelopeEip2930 - - export const type = 'eip2930' as const - export type Type = typeof type - - // #endregion - - // #region Functions - - /** - * Asserts a {@link ox#(TransactionEnvelopeEip2930:type)} is valid. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeEip2930, Value } from 'ox' - * - * TransactionEnvelopeEip2930.assert({ - * gasPrice: 2n ** 256n - 1n + 1n, - * chainId: 1, - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * // @error: GasPriceTooHighError: - * // @error: The gas price (`gasPrice` = 115792089237316195423570985008687907853269984665640564039457584007913 gwei) cannot be - * // @error: higher than the maximum allowed value (2^256-1). - * ``` - * - * @param envelope - The transaction envelope to assert. - */ - export function assert( - envelope: PartialBy, - ) { - const { chainId, gasPrice, to } = envelope - if (chainId <= 0) - throw new TransactionEnvelope.InvalidChainIdError({ chainId }) - if (to) Address_assert(to, { strict: false }) - if (gasPrice && BigInt(gasPrice) > 2n ** 256n - 1n) - throw new TransactionEnvelope.GasPriceTooHighError({ gasPrice }) - } - export declare namespace assert { - type ErrorType = - | Address_assert.ErrorType - | TransactionEnvelope.InvalidChainIdError - | TransactionEnvelope.GasPriceTooHighError - | Errors.GlobalErrorType - } +export type Rpc = TransactionEnvelopeEip2930< + signed, + Hex.Hex, + Hex.Hex, + '0x1' +> - assert.parseError = (error: unknown) => - /* v8 ignore next */ - error as assert.ErrorType - - /** - * Deserializes a {@link ox#(TransactionEnvelopeEip2930:type)} from its serialized form. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeEip2930 } from 'ox' - * - * const envelope = TransactionEnvelopeEip2930.deserialize('0x01ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0') - * // @log: { - * // @log: type: 'eip2930', - * // @log: nonce: 785n, - * // @log: gasPrice: 2000000000n, - * // @log: gas: 1000000n, - * // @log: to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * // @log: value: 1000000000000000000n, - * // @log: } - * ``` - * - * @param serializedTransaction - The serialized transaction. - * @returns Deserialized Transaction Envelope. - */ - export function deserialize( - serializedTransaction: TransactionEnvelopeEip2930.Serialized, - ): TransactionEnvelopeEip2930 { - const transactionArray = Rlp_toHex(Hex.slice(serializedTransaction, 1)) - - const [ - chainId, - nonce, - gasPrice, - gas, - to, - value, - data, - accessList, - yParity, - r, - s, - ] = transactionArray as readonly Hex.Hex[] - - if (!(transactionArray.length === 8 || transactionArray.length === 11)) - throw new TransactionEnvelope.InvalidSerializedError({ - attributes: { - chainId, - nonce, - gasPrice, - gas, - to, - value, - data, - accessList, - ...(transactionArray.length > 8 - ? { - yParity, - r, - s, - } - : {}), - }, - serializedTransaction, - type: 'eip2930', - }) - - let transaction = { - chainId: Number(chainId as Hex.Hex), - type: 'eip2930', - } as TransactionEnvelopeEip2930 - if (Hex.validate(to) && to !== '0x') transaction.to = to - if (Hex.validate(gas) && gas !== '0x') transaction.gas = BigInt(gas) - if (Hex.validate(data) && data !== '0x') transaction.data = data - if (Hex.validate(nonce) && nonce !== '0x') transaction.nonce = BigInt(nonce) - if (Hex.validate(value) && value !== '0x') transaction.value = BigInt(value) - if (Hex.validate(gasPrice) && gasPrice !== '0x') - transaction.gasPrice = BigInt(gasPrice) - if (accessList!.length !== 0 && accessList !== '0x') - transaction.accessList = AccessList_fromTupleList(accessList as any) - - const signature = - r && s && yParity ? Signature_fromTuple([yParity, r, s]) : undefined - if (signature) - transaction = { - ...transaction, - ...signature, - } as TransactionEnvelopeEip2930 - - TransactionEnvelopeEip2930.assert(transaction) - - return transaction - } +export type Serialized = `${SerializedType}${string}` + +export const serializedType = '0x01' as const +export type SerializedType = typeof serializedType + +export type Signed = TransactionEnvelopeEip2930 + +export const type = 'eip2930' as const +export type Type = typeof type + +/** + * Asserts a {@link ox#TransactionEnvelopeEip2930.TransactionEnvelopeEip2930} is valid. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeEip2930, Value } from 'ox' + * + * TransactionEnvelopeEip2930.assert({ + * gasPrice: 2n ** 256n - 1n + 1n, + * chainId: 1, + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * // @error: GasPriceTooHighError: + * // @error: The gas price (`gasPrice` = 115792089237316195423570985008687907853269984665640564039457584007913 gwei) cannot be + * // @error: higher than the maximum allowed value (2^256-1). + * ``` + * + * @param envelope - The transaction envelope to assert. + */ +export function assert( + envelope: PartialBy, +) { + const { chainId, gasPrice, to } = envelope + if (chainId <= 0) + throw new TransactionEnvelope.InvalidChainIdError({ chainId }) + if (to) Address_assert(to, { strict: false }) + if (gasPrice && BigInt(gasPrice) > 2n ** 256n - 1n) + throw new TransactionEnvelope.GasPriceTooHighError({ gasPrice }) +} - export declare namespace deserialize { - type ErrorType = Errors.GlobalErrorType - } +export declare namespace assert { + type ErrorType = + | Address_assert.ErrorType + | TransactionEnvelope.InvalidChainIdError + | TransactionEnvelope.GasPriceTooHighError + | Errors.GlobalErrorType +} +assert.parseError = (error: unknown) => /* v8 ignore next */ - deserialize.parseError = (error: unknown) => error as deserialize.ErrorType - - /** - * Converts an arbitrary transaction object into an EIP-2930 Transaction Envelope. - * - * @example - * ```ts twoslash - * // @noErrors - * import { TransactionEnvelopeEip2930, Value } from 'ox' - * - * const envelope = TransactionEnvelopeEip2930.from({ - * chainId: 1, - * accessList: [...], - * gasPrice: Value.fromGwei('10'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * ``` - * - * @example - * ### Attaching Signatures - * - * It is possible to attach a `signature` to the transaction envelope. - * - * ```ts twoslash - * import { Secp256k1, TransactionEnvelopeEip2930, Value } from 'ox' - * - * const envelope = TransactionEnvelopeEip2930.from({ - * chainId: 1, - * gasPrice: Value.fromGwei('10'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * - * const signature = Secp256k1.sign({ - * payload: TransactionEnvelopeEip2930.getSignPayload(envelope), - * privateKey: '0x...', - * }) - * - * const envelope_signed = TransactionEnvelopeEip2930.from(envelope, { // [!code focus] - * signature, // [!code focus] - * }) // [!code focus] - * // @log: { - * // @log: chainId: 1, - * // @log: gasPrice: 10000000000n, - * // @log: to: '0x0000000000000000000000000000000000000000', - * // @log: type: 'eip2930', - * // @log: value: 1000000000000000000n, - * // @log: r: 125...n, - * // @log: s: 642...n, - * // @log: yParity: 0, - * // @log: } - * ``` - * - * @example - * ### From Serialized - * - * It is possible to instantiate an EIP-2930 Transaction Envelope from a {@link ox#(TransactionEnvelopeEip2930:namespace).Serialized} value. - * - * ```ts twoslash - * import { TransactionEnvelopeEip2930 } from 'ox' - * - * const envelope = TransactionEnvelopeEip2930.from('0x01f858018203118502540be4008504a817c800809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c08477359400e1a001627c687261b0e7f8638af1112efa8a77e23656f6e7945275b19e9deed80261') - * // @log: { - * // @log: chainId: 1, - * // @log: gasPrice: 10000000000n, - * // @log: to: '0x0000000000000000000000000000000000000000', - * // @log: type: 'eip2930', - * // @log: value: 1000000000000000000n, - * // @log: } - * ``` - * - * @param envelope - The transaction object to convert. - * @param options - Options. - * @returns A {@link ox#(TransactionEnvelopeEip2930:type)} - */ - export function from< - const envelope extends - | UnionPartialBy - | TransactionEnvelopeEip2930.Serialized, - const signature extends Signature | undefined = undefined, - >( - envelope: - | envelope - | UnionPartialBy - | TransactionEnvelopeEip2930.Serialized, - options: TransactionEnvelopeEip2930.from.Options = {}, - ): TransactionEnvelopeEip2930.from.ReturnType { - const { signature } = options - - const envelope_ = ( - typeof envelope === 'string' - ? TransactionEnvelopeEip2930.deserialize(envelope) - : envelope - ) as TransactionEnvelopeEip2930 - - TransactionEnvelopeEip2930.assert(envelope_) - - return { - ...envelope_, - ...(signature ? Signature_from(signature) : {}), - type: 'eip2930', - } as never - } - - export declare namespace from { - type Options = { - signature?: signature | Signature | undefined - } - - type ReturnType< - envelope extends - | UnionPartialBy - | Hex.Hex = TransactionEnvelopeEip2930 | Hex.Hex, - signature extends Signature | undefined = undefined, - > = Compute< - envelope extends Hex.Hex - ? TransactionEnvelopeEip2930 - : Assign< - envelope, - (signature extends Signature ? Readonly : {}) & { - readonly type: 'eip2930' + error as assert.ErrorType + +/** + * Deserializes a {@link ox#TransactionEnvelopeEip2930.TransactionEnvelopeEip2930} from its serialized form. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeEip2930 } from 'ox' + * + * const envelope = TransactionEnvelopeEip2930.deserialize('0x01ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0') + * // @log: { + * // @log: type: 'eip2930', + * // @log: nonce: 785n, + * // @log: gasPrice: 2000000000n, + * // @log: gas: 1000000n, + * // @log: to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * // @log: value: 1000000000000000000n, + * // @log: } + * ``` + * + * @param serializedTransaction - The serialized transaction. + * @returns Deserialized Transaction Envelope. + */ +export function deserialize( + serializedTransaction: Serialized, +): TransactionEnvelopeEip2930 { + const transactionArray = Rlp_toHex(Hex.slice(serializedTransaction, 1)) + + const [ + chainId, + nonce, + gasPrice, + gas, + to, + value, + data, + accessList, + yParity, + r, + s, + ] = transactionArray as readonly Hex.Hex[] + + if (!(transactionArray.length === 8 || transactionArray.length === 11)) + throw new TransactionEnvelope.InvalidSerializedError({ + attributes: { + chainId, + nonce, + gasPrice, + gas, + to, + value, + data, + accessList, + ...(transactionArray.length > 8 + ? { + yParity, + r, + s, } - > - > + : {}), + }, + serializedTransaction, + type: 'eip2930', + }) + + let transaction = { + chainId: Number(chainId as Hex.Hex), + type: 'eip2930', + } as TransactionEnvelopeEip2930 + if (Hex.validate(to) && to !== '0x') transaction.to = to + if (Hex.validate(gas) && gas !== '0x') transaction.gas = BigInt(gas) + if (Hex.validate(data) && data !== '0x') transaction.data = data + if (Hex.validate(nonce) && nonce !== '0x') transaction.nonce = BigInt(nonce) + if (Hex.validate(value) && value !== '0x') transaction.value = BigInt(value) + if (Hex.validate(gasPrice) && gasPrice !== '0x') + transaction.gasPrice = BigInt(gasPrice) + if (accessList!.length !== 0 && accessList !== '0x') + transaction.accessList = AccessList_fromTupleList(accessList as any) + + const signature = + r && s && yParity ? Signature_fromTuple([yParity, r, s]) : undefined + if (signature) + transaction = { + ...transaction, + ...signature, + } as TransactionEnvelopeEip2930 - type ErrorType = - | TransactionEnvelopeEip2930.deserialize.ErrorType - | TransactionEnvelopeEip2930.assert.ErrorType - | Errors.GlobalErrorType - } + assert(transaction) - from.parseError = (error: unknown) => - /* v8 ignore next */ - error as from.ErrorType - - /** - * Returns the payload to sign for a {@link ox#(TransactionEnvelopeEip2930:type)}. - * - * @example - * The example below demonstrates how to compute the sign payload which can be used - * with ECDSA signing utilities like {@link ox#Secp256k1.(sign:function)}. - * - * ```ts twoslash - * import { Secp256k1, TransactionEnvelopeEip2930 } from 'ox' - * - * const envelope = TransactionEnvelopeEip2930.from({ - * chainId: 1, - * nonce: 0n, - * gasPrice: 1000000000n, - * gas: 21000n, - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: 1000000000000000000n, - * }) - * - * const payload = TransactionEnvelopeEip2930.getSignPayload(envelope) // [!code focus] - * // @log: '0x...' - * - * const signature = Secp256k1.sign({ payload, privateKey: '0x...' }) - * ``` - * - * @param envelope - The transaction envelope to get the sign payload for. - * @returns The sign payload. - */ - export function getSignPayload( - envelope: TransactionEnvelopeEip2930, - ): getSignPayload.ReturnType { - return TransactionEnvelopeEip2930.hash(envelope, { presign: true }) - } + return transaction +} - export declare namespace getSignPayload { - type ReturnType = Hex.Hex +export declare namespace deserialize { + type ErrorType = Errors.GlobalErrorType +} - type ErrorType = - | TransactionEnvelopeEip2930.hash.ErrorType - | Errors.GlobalErrorType - } +/* v8 ignore next */ +deserialize.parseError = (error: unknown) => error as deserialize.ErrorType + +/** + * Converts an arbitrary transaction object into an EIP-2930 Transaction Envelope. + * + * @example + * ```ts twoslash + * // @noErrors + * import { TransactionEnvelopeEip2930, Value } from 'ox' + * + * const envelope = TransactionEnvelopeEip2930.from({ + * chainId: 1, + * accessList: [...], + * gasPrice: Value.fromGwei('10'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * ``` + * + * @example + * ### Attaching Signatures + * + * It is possible to attach a `signature` to the transaction envelope. + * + * ```ts twoslash + * import { Secp256k1, TransactionEnvelopeEip2930, Value } from 'ox' + * + * const envelope = TransactionEnvelopeEip2930.from({ + * chainId: 1, + * gasPrice: Value.fromGwei('10'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * + * const signature = Secp256k1.sign({ + * payload: TransactionEnvelopeEip2930.getSignPayload(envelope), + * privateKey: '0x...', + * }) + * + * const envelope_signed = TransactionEnvelopeEip2930.from(envelope, { // [!code focus] + * signature, // [!code focus] + * }) // [!code focus] + * // @log: { + * // @log: chainId: 1, + * // @log: gasPrice: 10000000000n, + * // @log: to: '0x0000000000000000000000000000000000000000', + * // @log: type: 'eip2930', + * // @log: value: 1000000000000000000n, + * // @log: r: 125...n, + * // @log: s: 642...n, + * // @log: yParity: 0, + * // @log: } + * ``` + * + * @example + * ### From Serialized + * + * It is possible to instantiate an EIP-2930 Transaction Envelope from a {@link ox#TransactionEnvelopeEip2930.Serialized} value. + * + * ```ts twoslash + * import { TransactionEnvelopeEip2930 } from 'ox' + * + * const envelope = TransactionEnvelopeEip2930.from('0x01f858018203118502540be4008504a817c800809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c08477359400e1a001627c687261b0e7f8638af1112efa8a77e23656f6e7945275b19e9deed80261') + * // @log: { + * // @log: chainId: 1, + * // @log: gasPrice: 10000000000n, + * // @log: to: '0x0000000000000000000000000000000000000000', + * // @log: type: 'eip2930', + * // @log: value: 1000000000000000000n, + * // @log: } + * ``` + * + * @param envelope - The transaction object to convert. + * @param options - Options. + * @returns A {@link ox#TransactionEnvelopeEip2930.TransactionEnvelopeEip2930} + */ +export function from< + const envelope extends + | UnionPartialBy + | Serialized, + const signature extends Signature | undefined = undefined, +>( + envelope: + | envelope + | UnionPartialBy + | Serialized, + options: from.Options = {}, +): from.ReturnType { + const { signature } = options + + const envelope_ = ( + typeof envelope === 'string' ? deserialize(envelope) : envelope + ) as TransactionEnvelopeEip2930 + + assert(envelope_) + + return { + ...envelope_, + ...(signature ? Signature_from(signature) : {}), + type: 'eip2930', + } as never +} - getSignPayload.parseError = (error: unknown) => - /* v8 ignore next */ - error as getSignPayload.ErrorType - - /** - * Hashes a {@link ox#(TransactionEnvelopeEip2930:type)}. This is the "transaction hash". - * - * @example - * ```ts twoslash - * import { Secp256k1, TransactionEnvelopeEip2930 } from 'ox' - * - * const envelope = TransactionEnvelopeEip2930.from({ - * chainId: 1, - * nonce: 0n, - * gasPrice: 1000000000n, - * gas: 21000n, - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: 1000000000000000000n, - * }) - * - * const signature = Secp256k1.sign({ - * payload: TransactionEnvelopeEip2930.getSignPayload(envelope), - * privateKey: '0x...', - * }) - * - * const envelope_signed = TransactionEnvelopeEip2930.from(envelope, { - * signature, - * }) - * - * const hash = TransactionEnvelopeEip2930.hash(envelope_signed) // [!code focus] - * ``` - * - * @param envelope - The EIP-2930 Transaction Envelope to hash. - * @param options - Options. - * @returns The hash of the transaction envelope. - */ - export function hash( - envelope: TransactionEnvelopeEip2930, - options: TransactionEnvelopeEip2930.hash.Options = {}, - ): hash.ReturnType { - const { presign } = options - return Hash_keccak256( - TransactionEnvelopeEip2930.serialize({ - ...envelope, - ...(presign - ? { - r: undefined, - s: undefined, - yParity: undefined, - v: undefined, - } - : {}), - }), - ) +export declare namespace from { + type Options = { + signature?: signature | Signature | undefined } - export declare namespace hash { - type Options = { - /** Whether to hash this transaction for signing. @default false */ - presign?: presign | boolean | undefined - } - - type ReturnType = Hex.Hex + type ReturnType< + envelope extends + | UnionPartialBy + | Hex.Hex = TransactionEnvelopeEip2930 | Hex.Hex, + signature extends Signature | undefined = undefined, + > = Compute< + envelope extends Hex.Hex + ? TransactionEnvelopeEip2930 + : Assign< + envelope, + (signature extends Signature ? Readonly : {}) & { + readonly type: 'eip2930' + } + > + > + + type ErrorType = + | deserialize.ErrorType + | assert.ErrorType + | Errors.GlobalErrorType +} - type ErrorType = - | Hash_keccak256.ErrorType - | TransactionEnvelopeEip2930.serialize.ErrorType - | Errors.GlobalErrorType - } +from.parseError = (error: unknown) => + /* v8 ignore next */ + error as from.ErrorType + +/** + * Returns the payload to sign for a {@link ox#TransactionEnvelopeEip2930.TransactionEnvelopeEip2930}. + * + * @example + * The example below demonstrates how to compute the sign payload which can be used + * with ECDSA signing utilities like {@link ox#Secp256k1.(sign:function)}. + * + * ```ts twoslash + * import { Secp256k1, TransactionEnvelopeEip2930 } from 'ox' + * + * const envelope = TransactionEnvelopeEip2930.from({ + * chainId: 1, + * nonce: 0n, + * gasPrice: 1000000000n, + * gas: 21000n, + * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * value: 1000000000000000000n, + * }) + * + * const payload = TransactionEnvelopeEip2930.getSignPayload(envelope) // [!code focus] + * // @log: '0x...' + * + * const signature = Secp256k1.sign({ payload, privateKey: '0x...' }) + * ``` + * + * @param envelope - The transaction envelope to get the sign payload for. + * @returns The sign payload. + */ +export function getSignPayload( + envelope: TransactionEnvelopeEip2930, +): getSignPayload.ReturnType { + return hash(envelope, { presign: true }) +} - hash.parseError = (error: unknown) => - /* v8 ignore next */ - error as hash.ErrorType - - /** - * Serializes a {@link ox#(TransactionEnvelopeEip2930:type)}. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeEip2930, Value } from 'ox' - * - * const envelope = TransactionEnvelopeEip2930.from({ - * chainId: 1, - * gasPrice: Value.fromGwei('10'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * - * const serialized = TransactionEnvelopeEip2930.serialize(envelope) // [!code focus] - * ``` - * - * @example - * ### Attaching Signatures - * - * It is possible to attach a `signature` to the serialized Transaction Envelope. - * - * ```ts twoslash - * import { Secp256k1, TransactionEnvelopeEip2930, Value } from 'ox' - * - * const envelope = TransactionEnvelopeEip2930.from({ - * chainId: 1, - * gasPrice: Value.fromGwei('10'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * - * const signature = Secp256k1.sign({ - * payload: TransactionEnvelopeEip2930.getSignPayload(envelope), - * privateKey: '0x...', - * }) - * - * const serialized = TransactionEnvelopeEip2930.serialize(envelope, { // [!code focus] - * signature, // [!code focus] - * }) // [!code focus] - * - * // ... send `serialized` transaction to JSON-RPC `eth_sendRawTransaction` - * ``` - * - * @param envelope - The Transaction Envelope to serialize. - * @param options - Options. - * @returns The serialized Transaction Envelope. - */ - export function serialize( - envelope: PartialBy, - options: TransactionEnvelopeEip2930.serialize.Options = {}, - ): TransactionEnvelopeEip2930.Serialized { - const { - chainId, - gas, - data, - input, - nonce, - to, - value, - accessList, - gasPrice, - } = envelope - - TransactionEnvelopeEip2930.assert(envelope) - - const accessTupleList = AccessList_toTupleList(accessList) - - const signature = Signature_extract(options.signature || (envelope as any)) - - const serializedTransaction = [ - Hex.fromNumber(chainId), - nonce ? Hex.fromNumber(nonce) : '0x', - gasPrice ? Hex.fromNumber(gasPrice) : '0x', - gas ? Hex.fromNumber(gas) : '0x', - to ?? '0x', - value ? Hex.fromNumber(value) : '0x', - data ?? input ?? '0x', - accessTupleList, - ...(signature ? Signature_toTuple(signature) : []), - ] as const - - return Hex.concat( - '0x01', - Rlp_fromHex(serializedTransaction), - ) as TransactionEnvelopeEip2930.Serialized - } +export declare namespace getSignPayload { + type ReturnType = Hex.Hex - export declare namespace serialize { - type Options = { - /** Signature to append to the serialized Transaction Envelope. */ - signature?: Signature | undefined - } - - type ErrorType = - | TransactionEnvelopeEip2930.assert.ErrorType - | Hex.fromNumber.ErrorType - | Signature_toTuple.ErrorType - | Hex.concat.ErrorType - | Rlp_fromHex.ErrorType - | Errors.GlobalErrorType - } + type ErrorType = hash.ErrorType | Errors.GlobalErrorType +} - serialize.parseError = (error: unknown) => - /* v8 ignore next */ - error as serialize.ErrorType - - /** - * Converts an {@link ox#(TransactionEnvelopeEip2930:type)} to an {@link ox#(TransactionEnvelopeEip2930:namespace).Rpc}. - * - * @example - * ```ts twoslash - * import { RpcRequest, TransactionEnvelopeEip2930, Value } from 'ox' - * - * const envelope = TransactionEnvelopeEip2930.from({ - * chainId: 1, - * nonce: 0n, - * gas: 21000n, - * maxFeePerGas: Value.fromGwei('20'), - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: Value.fromEther('1'), - * }) - * - * const envelope_rpc = TransactionEnvelopeEip2930.toRpc(envelope) // [!code focus] - * - * const request = RpcRequest.from({ - * id: 0, - * method: 'eth_sendTransaction', - * params: [envelope_rpc], - * }) - * ``` - * - * @param envelope - The EIP-2930 transaction envelope to convert. - * @returns An RPC-formatted EIP-2930 transaction envelope. - */ - export function toRpc( - envelope: Omit, - ): TransactionEnvelopeEip2930.Rpc { - const signature = Signature_extract(envelope)! - - return { +getSignPayload.parseError = (error: unknown) => + /* v8 ignore next */ + error as getSignPayload.ErrorType + +/** + * Hashes a {@link ox#TransactionEnvelopeEip2930.TransactionEnvelopeEip2930}. This is the "transaction hash". + * + * @example + * ```ts twoslash + * import { Secp256k1, TransactionEnvelopeEip2930 } from 'ox' + * + * const envelope = TransactionEnvelopeEip2930.from({ + * chainId: 1, + * nonce: 0n, + * gasPrice: 1000000000n, + * gas: 21000n, + * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * value: 1000000000000000000n, + * }) + * + * const signature = Secp256k1.sign({ + * payload: TransactionEnvelopeEip2930.getSignPayload(envelope), + * privateKey: '0x...', + * }) + * + * const envelope_signed = TransactionEnvelopeEip2930.from(envelope, { + * signature, + * }) + * + * const hash = TransactionEnvelopeEip2930.hash(envelope_signed) // [!code focus] + * ``` + * + * @param envelope - The EIP-2930 Transaction Envelope to hash. + * @param options - Options. + * @returns The hash of the transaction envelope. + */ +export function hash( + envelope: TransactionEnvelopeEip2930, + options: hash.Options = {}, +): hash.ReturnType { + const { presign } = options + return Hash_keccak256( + serialize({ ...envelope, - chainId: Hex.fromNumber(envelope.chainId), - data: envelope.data ?? envelope.input, - ...(typeof envelope.gas === 'bigint' - ? { gas: Hex.fromNumber(envelope.gas) } - : {}), - ...(typeof envelope.nonce === 'bigint' - ? { nonce: Hex.fromNumber(envelope.nonce) } + ...(presign + ? { + r: undefined, + s: undefined, + yParity: undefined, + v: undefined, + } : {}), - ...(typeof envelope.value === 'bigint' - ? { value: Hex.fromNumber(envelope.value) } - : {}), - ...(typeof envelope.gasPrice === 'bigint' - ? { gasPrice: Hex.fromNumber(envelope.gasPrice) } - : {}), - type: '0x1', - ...(signature ? Signature_toRpc(signature) : {}), - } as never - } + }), + ) +} - export declare namespace toRpc { - export type ErrorType = Signature_extract.ErrorType | Errors.GlobalErrorType +export declare namespace hash { + type Options = { + /** Whether to hash this transaction for signing. @default false */ + presign?: presign | boolean | undefined } - toRpc.parseError = (error: unknown) => - /* v8 ignore next */ - error as toRpc.ErrorType - - /** - * Validates a {@link ox#(TransactionEnvelopeEip2930:type)}. Returns `true` if the envelope is valid, `false` otherwise. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeEip2930, Value } from 'ox' - * - * const valid = TransactionEnvelopeEip2930.assert({ - * gasPrice: 2n ** 256n - 1n + 1n, - * chainId: 1, - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * // @log: false - * ``` - * - * @param envelope - The transaction envelope to validate. - */ - export function validate( - envelope: PartialBy, - ) { - try { - TransactionEnvelopeEip2930.assert(envelope) - return true - } catch { - return false - } - } + type ReturnType = Hex.Hex + + type ErrorType = + | Hash_keccak256.ErrorType + | serialize.ErrorType + | Errors.GlobalErrorType +} - export declare namespace validate { - type ErrorType = Errors.GlobalErrorType +hash.parseError = (error: unknown) => + /* v8 ignore next */ + error as hash.ErrorType + +/** + * Serializes a {@link ox#TransactionEnvelopeEip2930.TransactionEnvelopeEip2930}. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeEip2930, Value } from 'ox' + * + * const envelope = TransactionEnvelopeEip2930.from({ + * chainId: 1, + * gasPrice: Value.fromGwei('10'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * + * const serialized = TransactionEnvelopeEip2930.serialize(envelope) // [!code focus] + * ``` + * + * @example + * ### Attaching Signatures + * + * It is possible to attach a `signature` to the serialized Transaction Envelope. + * + * ```ts twoslash + * import { Secp256k1, TransactionEnvelopeEip2930, Value } from 'ox' + * + * const envelope = TransactionEnvelopeEip2930.from({ + * chainId: 1, + * gasPrice: Value.fromGwei('10'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * + * const signature = Secp256k1.sign({ + * payload: TransactionEnvelopeEip2930.getSignPayload(envelope), + * privateKey: '0x...', + * }) + * + * const serialized = TransactionEnvelopeEip2930.serialize(envelope, { // [!code focus] + * signature, // [!code focus] + * }) // [!code focus] + * + * // ... send `serialized` transaction to JSON-RPC `eth_sendRawTransaction` + * ``` + * + * @param envelope - The Transaction Envelope to serialize. + * @param options - Options. + * @returns The serialized Transaction Envelope. + */ +export function serialize( + envelope: PartialBy, + options: serialize.Options = {}, +): Serialized { + const { chainId, gas, data, input, nonce, to, value, accessList, gasPrice } = + envelope + + assert(envelope) + + const accessTupleList = AccessList_toTupleList(accessList) + + const signature = Signature_extract(options.signature || (envelope as any)) + + const serializedTransaction = [ + Hex.fromNumber(chainId), + nonce ? Hex.fromNumber(nonce) : '0x', + gasPrice ? Hex.fromNumber(gasPrice) : '0x', + gas ? Hex.fromNumber(gas) : '0x', + to ?? '0x', + value ? Hex.fromNumber(value) : '0x', + data ?? input ?? '0x', + accessTupleList, + ...(signature ? Signature_toTuple(signature) : []), + ] as const + + return Hex.concat('0x01', Rlp_fromHex(serializedTransaction)) as Serialized +} + +export declare namespace serialize { + type Options = { + /** Signature to append to the serialized Transaction Envelope. */ + signature?: Signature | undefined } - validate.parseError = (error: unknown) => - /* v8 ignore next */ - error as validate.ErrorType + type ErrorType = + | assert.ErrorType + | Hex.fromNumber.ErrorType + | Signature_toTuple.ErrorType + | Hex.concat.ErrorType + | Rlp_fromHex.ErrorType + | Errors.GlobalErrorType +} + +serialize.parseError = (error: unknown) => + /* v8 ignore next */ + error as serialize.ErrorType + +/** + * Converts an {@link ox#TransactionEnvelopeEip2930.TransactionEnvelopeEip2930} to an {@link ox#TransactionEnvelopeEip2930.Rpc}. + * + * @example + * ```ts twoslash + * import { RpcRequest, TransactionEnvelopeEip2930, Value } from 'ox' + * + * const envelope = TransactionEnvelopeEip2930.from({ + * chainId: 1, + * nonce: 0n, + * gas: 21000n, + * maxFeePerGas: Value.fromGwei('20'), + * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * value: Value.fromEther('1'), + * }) + * + * const envelope_rpc = TransactionEnvelopeEip2930.toRpc(envelope) // [!code focus] + * + * const request = RpcRequest.from({ + * id: 0, + * method: 'eth_sendTransaction', + * params: [envelope_rpc], + * }) + * ``` + * + * @param envelope - The EIP-2930 transaction envelope to convert. + * @returns An RPC-formatted EIP-2930 transaction envelope. + */ +export function toRpc(envelope: Omit): Rpc { + const signature = Signature_extract(envelope)! + + return { + ...envelope, + chainId: Hex.fromNumber(envelope.chainId), + data: envelope.data ?? envelope.input, + ...(typeof envelope.gas === 'bigint' + ? { gas: Hex.fromNumber(envelope.gas) } + : {}), + ...(typeof envelope.nonce === 'bigint' + ? { nonce: Hex.fromNumber(envelope.nonce) } + : {}), + ...(typeof envelope.value === 'bigint' + ? { value: Hex.fromNumber(envelope.value) } + : {}), + ...(typeof envelope.gasPrice === 'bigint' + ? { gasPrice: Hex.fromNumber(envelope.gasPrice) } + : {}), + type: '0x1', + ...(signature ? Signature_toRpc(signature) : {}), + } as never +} - // #endregion +export declare namespace toRpc { + export type ErrorType = Signature_extract.ErrorType | Errors.GlobalErrorType } + +toRpc.parseError = (error: unknown) => + /* v8 ignore next */ + error as toRpc.ErrorType + +/** + * Validates a {@link ox#TransactionEnvelopeEip2930.TransactionEnvelopeEip2930}. Returns `true` if the envelope is valid, `false` otherwise. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeEip2930, Value } from 'ox' + * + * const valid = TransactionEnvelopeEip2930.assert({ + * gasPrice: 2n ** 256n - 1n + 1n, + * chainId: 1, + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * // @log: false + * ``` + * + * @param envelope - The transaction envelope to validate. + */ +export function validate( + envelope: PartialBy, +) { + try { + assert(envelope) + return true + } catch { + return false + } +} + +export declare namespace validate { + type ErrorType = Errors.GlobalErrorType +} + +validate.parseError = (error: unknown) => + /* v8 ignore next */ + error as validate.ErrorType diff --git a/src/TransactionEnvelopeLegacy.test-d.ts b/src/TransactionEnvelopeLegacy.test-d.ts index 89872745..4f1f4c95 100644 --- a/src/TransactionEnvelopeLegacy.test-d.ts +++ b/src/TransactionEnvelopeLegacy.test-d.ts @@ -14,14 +14,18 @@ test('default', () => { readonly value: 69n readonly type: 'legacy' }>() - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() } { const envelope = TransactionEnvelopeLegacy.from( '0x123' as TransactionEnvelopeLegacy.Serialized, ) - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() } { @@ -40,7 +44,9 @@ test('default', () => { readonly v: 37 readonly type: 'legacy' }>() - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() } }) @@ -67,5 +73,7 @@ test('options: signature', () => { readonly yParity: 0 readonly type: 'legacy' }>() - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() }) diff --git a/src/TransactionEnvelopeLegacy.test.ts b/src/TransactionEnvelopeLegacy.test.ts index 7e1ea34c..41fd00c7 100644 --- a/src/TransactionEnvelopeLegacy.test.ts +++ b/src/TransactionEnvelopeLegacy.test.ts @@ -45,7 +45,9 @@ describe('deserialize', () => { test('default', () => { const serialized = TransactionEnvelopeLegacy.serialize(transaction) const deserialized = TransactionEnvelopeLegacy.deserialize(serialized) - assertType(deserialized) + assertType( + deserialized, + ) expect(deserialized).toEqual(transaction) }) @@ -384,7 +386,9 @@ describe('from', () => { test('default', () => { { const envelope = TransactionEnvelopeLegacy.from({}) - expectTypeOf(envelope).toMatchTypeOf() + expectTypeOf( + envelope, + ).toMatchTypeOf() expect(envelope).toMatchInlineSnapshot(` { "type": "legacy", diff --git a/src/TransactionEnvelopeLegacy.ts b/src/TransactionEnvelopeLegacy.ts index 87f22759..6d4b5088 100644 --- a/src/TransactionEnvelopeLegacy.ts +++ b/src/TransactionEnvelopeLegacy.ts @@ -23,7 +23,7 @@ export type TransactionEnvelopeLegacy< signed extends boolean = boolean, bigintType = bigint, numberType = number, - type extends string = TransactionEnvelopeLegacy.Type, + type extends string = Type, > = Compute< PartialBy< TransactionEnvelope.Base, @@ -33,682 +33,652 @@ export type TransactionEnvelopeLegacy< gasPrice?: bigintType | undefined } > -export namespace TransactionEnvelopeLegacy { - // #region Types - - export type Rpc = TransactionEnvelopeLegacy< - signed, - Hex.Hex, - Hex.Hex, - '0x0' - > - - export type Serialized = Branded<`0x${string}`, 'legacy'> - - export type Signed = TransactionEnvelopeLegacy - - export const type = 'legacy' - export type Type = typeof type - - // #endregion - - // #region Functions - - /** - * Asserts a {@link ox#(TransactionEnvelopeLegacy:type)} is valid. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeLegacy, Value } from 'ox' - * - * TransactionEnvelopeLegacy.assert({ - * gasPrice: 2n ** 256n - 1n + 1n, - * chainId: 1, - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * // @error: GasPriceTooHighError: - * // @error: The gas price (`gasPrice` = 115792089237316195423570985008687907853269984665640564039457584007913 gwei) cannot be - * // @error: higher than the maximum allowed value (2^256-1). - * ``` - * - * @param envelope - The transaction envelope to assert. - */ - export function assert( - envelope: PartialBy, - ) { - const { chainId, gasPrice, to } = envelope - if (to) Address_assert(to, { strict: false }) - if (typeof chainId !== 'undefined' && chainId <= 0) - throw new TransactionEnvelope.InvalidChainIdError({ chainId }) - if (gasPrice && BigInt(gasPrice) > 2n ** 256n - 1n) - throw new TransactionEnvelope.GasPriceTooHighError({ gasPrice }) - } - export declare namespace assert { - type ErrorType = - | Address_assert.ErrorType - | TransactionEnvelope.InvalidChainIdError - | TransactionEnvelope.GasPriceTooHighError - | Errors.GlobalErrorType - } - - assert.parseError = (error: unknown) => - /* v8 ignore next */ - error as assert.ErrorType - - /** - * Deserializes a {@link ox#(TransactionEnvelopeLegacy:type)} from its serialized form. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeLegacy } from 'ox' - * - * const envelope = TransactionEnvelopeLegacy.deserialize('0x01ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0') - * // @log: { - * // @log: type: 'legacy', - * // @log: nonce: 785n, - * // @log: gasPrice: 2000000000n, - * // @log: gas: 1000000n, - * // @log: to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * // @log: value: 1000000000000000000n, - * // @log: } - * ``` - * - * @param serializedTransaction - The serialized transaction. - * @returns Deserialized Transaction Envelope. - */ - export function deserialize( - serializedTransaction: Hex.Hex, - ): Compute { - const tuple = Rlp_toHex(serializedTransaction) - - const [nonce, gasPrice, gas, to, value, data, chainIdOrV_, r, s] = - tuple as readonly Hex.Hex[] - - if (!(tuple.length === 6 || tuple.length === 9)) - throw new TransactionEnvelope.InvalidSerializedError({ - attributes: { - nonce, - gasPrice, - gas, - to, - value, - data, - ...(tuple.length > 6 - ? { - v: chainIdOrV_, - r, - s, - } - : {}), - }, - serializedTransaction, - type: 'legacy', - }) - - const transaction = { - type: 'legacy', - } as TransactionEnvelopeLegacy - if (Hex.validate(to) && to !== '0x') transaction.to = to - if (Hex.validate(gas) && gas !== '0x') transaction.gas = BigInt(gas) - if (Hex.validate(data) && data !== '0x') transaction.data = data - if (Hex.validate(nonce) && nonce !== '0x') transaction.nonce = BigInt(nonce) - if (Hex.validate(value) && value !== '0x') transaction.value = BigInt(value) - if (Hex.validate(gasPrice) && gasPrice !== '0x') - transaction.gasPrice = BigInt(gasPrice) - - if (tuple.length === 6) return transaction - - const chainIdOrV = - Hex.validate(chainIdOrV_) && chainIdOrV_ !== '0x' - ? Number(chainIdOrV_ as Hex.Hex) - : 0 - - if (s === '0x' && r === '0x') { - if (chainIdOrV > 0) transaction.chainId = Number(chainIdOrV) - return transaction - } - - const v = chainIdOrV - const chainId: number | undefined = Math.floor((v - 35) / 2) - if (chainId > 0) transaction.chainId = chainId - else if (v !== 27 && v !== 28) - throw new Signature_InvalidVError({ value: v }) +export type Rpc = TransactionEnvelopeLegacy< + signed, + Hex.Hex, + Hex.Hex, + '0x0' +> - transaction.yParity = Signature_vToYParity(v) - transaction.v = v - transaction.s = s === '0x' ? 0n : BigInt(s!) - transaction.r = r === '0x' ? 0n : BigInt(r!) +export type Serialized = Branded<`0x${string}`, 'legacy'> + +export type Signed = TransactionEnvelopeLegacy + +export const type = 'legacy' +export type Type = typeof type + +/** + * Asserts a {@link ox#TransactionEnvelopeLegacy.TransactionEnvelopeLegacy} is valid. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeLegacy, Value } from 'ox' + * + * TransactionEnvelopeLegacy.assert({ + * gasPrice: 2n ** 256n - 1n + 1n, + * chainId: 1, + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * // @error: GasPriceTooHighError: + * // @error: The gas price (`gasPrice` = 115792089237316195423570985008687907853269984665640564039457584007913 gwei) cannot be + * // @error: higher than the maximum allowed value (2^256-1). + * ``` + * + * @param envelope - The transaction envelope to assert. + */ +export function assert(envelope: PartialBy) { + const { chainId, gasPrice, to } = envelope + if (to) Address_assert(to, { strict: false }) + if (typeof chainId !== 'undefined' && chainId <= 0) + throw new TransactionEnvelope.InvalidChainIdError({ chainId }) + if (gasPrice && BigInt(gasPrice) > 2n ** 256n - 1n) + throw new TransactionEnvelope.GasPriceTooHighError({ gasPrice }) +} - TransactionEnvelopeLegacy.assert(transaction) +export declare namespace assert { + type ErrorType = + | Address_assert.ErrorType + | TransactionEnvelope.InvalidChainIdError + | TransactionEnvelope.GasPriceTooHighError + | Errors.GlobalErrorType +} +assert.parseError = (error: unknown) => + /* v8 ignore next */ + error as assert.ErrorType + +/** + * Deserializes a {@link ox#TransactionEnvelopeLegacy.TransactionEnvelopeLegacy} from its serialized form. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeLegacy } from 'ox' + * + * const envelope = TransactionEnvelopeLegacy.deserialize('0x01ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0') + * // @log: { + * // @log: type: 'legacy', + * // @log: nonce: 785n, + * // @log: gasPrice: 2000000000n, + * // @log: gas: 1000000n, + * // @log: to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * // @log: value: 1000000000000000000n, + * // @log: } + * ``` + * + * @param serializedTransaction - The serialized transaction. + * @returns Deserialized Transaction Envelope. + */ +export function deserialize( + serializedTransaction: Hex.Hex, +): Compute { + const tuple = Rlp_toHex(serializedTransaction) + + const [nonce, gasPrice, gas, to, value, data, chainIdOrV_, r, s] = + tuple as readonly Hex.Hex[] + + if (!(tuple.length === 6 || tuple.length === 9)) + throw new TransactionEnvelope.InvalidSerializedError({ + attributes: { + nonce, + gasPrice, + gas, + to, + value, + data, + ...(tuple.length > 6 + ? { + v: chainIdOrV_, + r, + s, + } + : {}), + }, + serializedTransaction, + type: 'legacy', + }) + + const transaction = { + type: 'legacy', + } as TransactionEnvelopeLegacy + if (Hex.validate(to) && to !== '0x') transaction.to = to + if (Hex.validate(gas) && gas !== '0x') transaction.gas = BigInt(gas) + if (Hex.validate(data) && data !== '0x') transaction.data = data + if (Hex.validate(nonce) && nonce !== '0x') transaction.nonce = BigInt(nonce) + if (Hex.validate(value) && value !== '0x') transaction.value = BigInt(value) + if (Hex.validate(gasPrice) && gasPrice !== '0x') + transaction.gasPrice = BigInt(gasPrice) + + if (tuple.length === 6) return transaction + + const chainIdOrV = + Hex.validate(chainIdOrV_) && chainIdOrV_ !== '0x' + ? Number(chainIdOrV_ as Hex.Hex) + : 0 + + if (s === '0x' && r === '0x') { + if (chainIdOrV > 0) transaction.chainId = Number(chainIdOrV) return transaction } - export declare namespace deserialize { - type ErrorType = Errors.GlobalErrorType - } + const v = chainIdOrV + const chainId: number | undefined = Math.floor((v - 35) / 2) + if (chainId > 0) transaction.chainId = chainId + else if (v !== 27 && v !== 28) throw new Signature_InvalidVError({ value: v }) - deserialize.parseError = (error: unknown) => - /* v8 ignore next */ - error as deserialize.ErrorType - - /** - * Converts an arbitrary transaction object into a legacy Transaction Envelope. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeLegacy, Value } from 'ox' - * - * const envelope = TransactionEnvelopeLegacy.from({ - * gasPrice: Value.fromGwei('10'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * ``` - * - * @example - * ### Attaching Signatures - * - * It is possible to attach a `signature` to the transaction envelope. - * - * ```ts twoslash - * import { Secp256k1, TransactionEnvelopeLegacy, Value } from 'ox' - * - * const envelope = TransactionEnvelopeLegacy.from({ - * chainId: 1, - * gasPrice: Value.fromGwei('10'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * - * const signature = Secp256k1.sign({ - * payload: TransactionEnvelopeLegacy.getSignPayload(envelope), - * privateKey: '0x...', - * }) - * - * const envelope_signed = TransactionEnvelopeLegacy.from(envelope, { // [!code focus] - * signature, // [!code focus] - * }) // [!code focus] - * // @log: { - * // @log: authorizationList: [...], - * // @log: chainId: 1, - * // @log: gasPrice: 10000000000n, - * // @log: to: '0x0000000000000000000000000000000000000000', - * // @log: type: 'eip7702', - * // @log: value: 1000000000000000000n, - * // @log: r: 125...n, - * // @log: s: 642...n, - * // @log: yParity: 0, - * // @log: } - * ``` - * - * @example - * ### From Serialized - * - * It is possible to instantiate an legacy Transaction Envelope from a {@link ox#(TransactionEnvelopeLegacy:namespace).Serialized} value. - * - * ```ts twoslash - * import { TransactionEnvelopeLegacy } from 'ox' - * - * const envelope = TransactionEnvelopeLegacy.from('0xf858018203118502540be4008504a817c800809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c08477359400e1a001627c687261b0e7f8638af1112efa8a77e23656f6e7945275b19e9deed80261') - * // @log: { - * // @log: chainId: 1, - * // @log: gasPrice: 10000000000n, - * // @log: to: '0x0000000000000000000000000000000000000000', - * // @log: type: 'legacy', - * // @log: value: 1000000000000000000n, - * // @log: } - * ``` - * - * @param envelope - The transaction object to convert. - * @param options - Options. - * @returns A legacy Transaction Envelope. - */ - export function from< - const envelope extends - | UnionPartialBy - | Hex.Hex, - const signature extends Signature | undefined = undefined, - >( - envelope: - | envelope - | UnionPartialBy - | Hex.Hex, - options: from.Options = {}, - ): from.ReturnType { - const { signature } = options - - const envelope_ = ( - typeof envelope === 'string' - ? TransactionEnvelopeLegacy.deserialize(envelope) - : envelope - ) as TransactionEnvelopeLegacy - - TransactionEnvelopeLegacy.assert(envelope_) - - const signature_ = (() => { - if (!signature) return {} - const s = Signature_from(signature) as any - s.v = s.yParity === 0 ? 27 : 28 - return s - })() + transaction.yParity = Signature_vToYParity(v) + transaction.v = v + transaction.s = s === '0x' ? 0n : BigInt(s!) + transaction.r = r === '0x' ? 0n : BigInt(r!) - return { - ...envelope_, - ...signature_, - type: 'legacy', - } as never - } + assert(transaction) - export declare namespace from { - type Options = { - signature?: signature | Signature | undefined - } + return transaction +} - type ReturnType< - envelope extends - | UnionPartialBy - | Hex.Hex = TransactionEnvelopeLegacy | Hex.Hex, - signature extends Signature | undefined = undefined, - > = Compute< - envelope extends Hex.Hex - ? TransactionEnvelopeLegacy - : Assign< - envelope, - (signature extends Signature - ? Readonly< - signature & { - v: signature['yParity'] extends 0 ? 27 : 28 - } - > - : {}) & { - readonly type: 'legacy' - } - > - > +export declare namespace deserialize { + type ErrorType = Errors.GlobalErrorType +} - type ErrorType = - | TransactionEnvelopeLegacy.deserialize.ErrorType - | TransactionEnvelopeLegacy.assert.ErrorType - | Errors.GlobalErrorType - } +deserialize.parseError = (error: unknown) => + /* v8 ignore next */ + error as deserialize.ErrorType + +/** + * Converts an arbitrary transaction object into a legacy Transaction Envelope. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeLegacy, Value } from 'ox' + * + * const envelope = TransactionEnvelopeLegacy.from({ + * gasPrice: Value.fromGwei('10'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * ``` + * + * @example + * ### Attaching Signatures + * + * It is possible to attach a `signature` to the transaction envelope. + * + * ```ts twoslash + * import { Secp256k1, TransactionEnvelopeLegacy, Value } from 'ox' + * + * const envelope = TransactionEnvelopeLegacy.from({ + * chainId: 1, + * gasPrice: Value.fromGwei('10'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * + * const signature = Secp256k1.sign({ + * payload: TransactionEnvelopeLegacy.getSignPayload(envelope), + * privateKey: '0x...', + * }) + * + * const envelope_signed = TransactionEnvelopeLegacy.from(envelope, { // [!code focus] + * signature, // [!code focus] + * }) // [!code focus] + * // @log: { + * // @log: authorizationList: [...], + * // @log: chainId: 1, + * // @log: gasPrice: 10000000000n, + * // @log: to: '0x0000000000000000000000000000000000000000', + * // @log: type: 'eip7702', + * // @log: value: 1000000000000000000n, + * // @log: r: 125...n, + * // @log: s: 642...n, + * // @log: yParity: 0, + * // @log: } + * ``` + * + * @example + * ### From Serialized + * + * It is possible to instantiate an legacy Transaction Envelope from a {@link ox#TransactionEnvelopeLegacy.Serialized} value. + * + * ```ts twoslash + * import { TransactionEnvelopeLegacy } from 'ox' + * + * const envelope = TransactionEnvelopeLegacy.from('0xf858018203118502540be4008504a817c800809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c08477359400e1a001627c687261b0e7f8638af1112efa8a77e23656f6e7945275b19e9deed80261') + * // @log: { + * // @log: chainId: 1, + * // @log: gasPrice: 10000000000n, + * // @log: to: '0x0000000000000000000000000000000000000000', + * // @log: type: 'legacy', + * // @log: value: 1000000000000000000n, + * // @log: } + * ``` + * + * @param envelope - The transaction object to convert. + * @param options - Options. + * @returns A legacy Transaction Envelope. + */ +export function from< + const envelope extends + | UnionPartialBy + | Hex.Hex, + const signature extends Signature | undefined = undefined, +>( + envelope: + | envelope + | UnionPartialBy + | Hex.Hex, + options: from.Options = {}, +): from.ReturnType { + const { signature } = options + + const envelope_ = ( + typeof envelope === 'string' ? deserialize(envelope) : envelope + ) as TransactionEnvelopeLegacy + + assert(envelope_) + + const signature_ = (() => { + if (!signature) return {} + const s = Signature_from(signature) as any + s.v = s.yParity === 0 ? 27 : 28 + return s + })() + + return { + ...envelope_, + ...signature_, + type: 'legacy', + } as never +} - from.parseError = (error: unknown) => - /* v8 ignore next */ - error as from.ErrorType - - /** - * Returns the payload to sign for a {@link ox#(TransactionEnvelopeLegacy:type)}. - * - * @example - * The example below demonstrates how to compute the sign payload which can be used - * with ECDSA signing utilities like {@link ox#Secp256k1.(sign:function)}. - * - * ```ts twoslash - * // @noErrors - * import { Secp256k1, TransactionEnvelopeLegacy } from 'ox' - * - * const envelope = TransactionEnvelopeLegacy.from({ - * nonce: 0n, - * gasPrice: 1000000000n, - * gas: 21000n, - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: 1000000000000000000n, - * }) - * - * const payload = TransactionEnvelopeLegacy.getSignPayload(envelope) // [!code focus] - * // @log: '0x...' - * - * const signature = Secp256k1.sign({ payload, privateKey: '0x...' }) - * ``` - * - * @param envelope - The transaction envelope to get the sign payload for. - * @returns The sign payload. - */ - export function getSignPayload( - envelope: TransactionEnvelopeLegacy, - ): TransactionEnvelopeLegacy.getSignPayload.ReturnType { - return TransactionEnvelopeLegacy.hash(envelope, { presign: true }) +export declare namespace from { + type Options = { + signature?: signature | Signature | undefined } - export declare namespace getSignPayload { - type ReturnType = Hex.Hex + type ReturnType< + envelope extends + | UnionPartialBy + | Hex.Hex = TransactionEnvelopeLegacy | Hex.Hex, + signature extends Signature | undefined = undefined, + > = Compute< + envelope extends Hex.Hex + ? TransactionEnvelopeLegacy + : Assign< + envelope, + (signature extends Signature + ? Readonly< + signature & { + v: signature['yParity'] extends 0 ? 27 : 28 + } + > + : {}) & { + readonly type: 'legacy' + } + > + > - type ErrorType = - | TransactionEnvelopeLegacy.hash.ErrorType - | Errors.GlobalErrorType - } + type ErrorType = + | deserialize.ErrorType + | assert.ErrorType + | Errors.GlobalErrorType +} - getSignPayload.parseError = (error: unknown) => - /* v8 ignore next */ - error as getSignPayload.ErrorType - - /** - * Hashes a {@link ox#(TransactionEnvelopeLegacy:type)}. This is the "transaction hash". - * - * @example - * ```ts twoslash - * import { Secp256k1, TransactionEnvelopeLegacy } from 'ox' - * - * const envelope = TransactionEnvelopeLegacy.from({ - * chainId: 1, - * nonce: 0n, - * gasPrice: 1000000000n, - * gas: 21000n, - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: 1000000000000000000n, - * }) - * - * const signature = Secp256k1.sign({ - * payload: TransactionEnvelopeLegacy.getSignPayload(envelope), - * privateKey: '0x...' - * }) - * - * const envelope_signed = TransactionEnvelopeLegacy.from(envelope, { signature }) - * - * const hash = TransactionEnvelopeLegacy.hash(envelope_signed) // [!code focus] - * ``` - * - * @param envelope - The Legacy Transaction Envelope to hash. - * @param options - Options. - * @returns The hash of the transaction envelope. - */ - export function hash( - envelope: TransactionEnvelopeLegacy, - options: TransactionEnvelopeLegacy.hash.Options = {}, - ): TransactionEnvelopeLegacy.hash.ReturnType { - const { presign } = options - return Hash_keccak256( - TransactionEnvelopeLegacy.serialize({ - ...envelope, - ...(presign - ? { - r: undefined, - s: undefined, - yParity: undefined, - v: undefined, - } - : {}), - }), - ) - } +from.parseError = (error: unknown) => + /* v8 ignore next */ + error as from.ErrorType + +/** + * Returns the payload to sign for a {@link ox#TransactionEnvelopeLegacy.TransactionEnvelopeLegacy}. + * + * @example + * The example below demonstrates how to compute the sign payload which can be used + * with ECDSA signing utilities like {@link ox#Secp256k1.(sign:function)}. + * + * ```ts twoslash + * // @noErrors + * import { Secp256k1, TransactionEnvelopeLegacy } from 'ox' + * + * const envelope = TransactionEnvelopeLegacy.from({ + * nonce: 0n, + * gasPrice: 1000000000n, + * gas: 21000n, + * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * value: 1000000000000000000n, + * }) + * + * const payload = TransactionEnvelopeLegacy.getSignPayload(envelope) // [!code focus] + * // @log: '0x...' + * + * const signature = Secp256k1.sign({ payload, privateKey: '0x...' }) + * ``` + * + * @param envelope - The transaction envelope to get the sign payload for. + * @returns The sign payload. + */ +export function getSignPayload( + envelope: TransactionEnvelopeLegacy, +): getSignPayload.ReturnType { + return hash(envelope, { presign: true }) +} - export declare namespace hash { - type Options = { - /** Whether to hash this transaction for signing. @default false */ - presign?: presign | boolean | undefined - } +export declare namespace getSignPayload { + type ReturnType = Hex.Hex - type ReturnType = Hex.Hex + type ErrorType = hash.ErrorType | Errors.GlobalErrorType +} - type ErrorType = - | Hash_keccak256.ErrorType - | TransactionEnvelopeLegacy.serialize.ErrorType - | Errors.GlobalErrorType +getSignPayload.parseError = (error: unknown) => + /* v8 ignore next */ + error as getSignPayload.ErrorType + +/** + * Hashes a {@link ox#TransactionEnvelopeLegacy.TransactionEnvelopeLegacy}. This is the "transaction hash". + * + * @example + * ```ts twoslash + * import { Secp256k1, TransactionEnvelopeLegacy } from 'ox' + * + * const envelope = TransactionEnvelopeLegacy.from({ + * chainId: 1, + * nonce: 0n, + * gasPrice: 1000000000n, + * gas: 21000n, + * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * value: 1000000000000000000n, + * }) + * + * const signature = Secp256k1.sign({ + * payload: TransactionEnvelopeLegacy.getSignPayload(envelope), + * privateKey: '0x...' + * }) + * + * const envelope_signed = TransactionEnvelopeLegacy.from(envelope, { signature }) + * + * const hash = TransactionEnvelopeLegacy.hash(envelope_signed) // [!code focus] + * ``` + * + * @param envelope - The Legacy Transaction Envelope to hash. + * @param options - Options. + * @returns The hash of the transaction envelope. + */ +export function hash( + envelope: TransactionEnvelopeLegacy, + options: hash.Options = {}, +): hash.ReturnType { + const { presign } = options + return Hash_keccak256( + serialize({ + ...envelope, + ...(presign + ? { + r: undefined, + s: undefined, + yParity: undefined, + v: undefined, + } + : {}), + }), + ) +} + +export declare namespace hash { + type Options = { + /** Whether to hash this transaction for signing. @default false */ + presign?: presign | boolean | undefined } - hash.parseError = (error: unknown) => - /* v8 ignore next */ - error as hash.ErrorType - - /** - * Serializes a {@link ox#(TransactionEnvelopeLegacy:type)}. - * - * @example - * ```ts twoslash - * // @noErrors - * import { TransactionEnvelopeLegacy } from 'ox' - * - * const envelope = TransactionEnvelopeLegacy.from({ - * chainId: 1, - * gasPrice: Value.fromGwei('10'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * - * const serialized = TransactionEnvelopeLegacy.serialize(envelope) // [!code focus] - * ``` - * - * @example - * ### Attaching Signatures - * - * It is possible to attach a `signature` to the serialized Transaction Envelope. - * - * ```ts twoslash - * // @noErrors - * import { Secp256k1, TransactionEnvelopeLegacy, Value } from 'ox' - * - * const envelope = TransactionEnvelopeLegacy.from({ - * chainId: 1, - * gasPrice: Value.fromGwei('10'), - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * - * const signature = Secp256k1.sign({ - * payload: TransactionEnvelopeLegacy.getSignPayload(envelope), - * privateKey: '0x...', - * }) - * - * const serialized = TransactionEnvelopeLegacy.serialize(envelope, { // [!code focus] - * signature, // [!code focus] - * }) // [!code focus] - * - * // ... send `serialized` transaction to JSON-RPC `eth_sendRawTransaction` - * ``` - * - * @param envelope - The Transaction Envelope to serialize. - * @param options - Options. - * @returns The serialized Transaction Envelope. - */ - export function serialize( - envelope: PartialBy, - options: TransactionEnvelopeLegacy.serialize.Options = {}, - ): TransactionEnvelopeLegacy.Serialized { - const { - chainId = 0, - gas, - data, - input, - nonce, - to, - value, - gasPrice, - } = envelope - - TransactionEnvelopeLegacy.assert(envelope) - - let serializedTransaction = [ - nonce ? Hex.fromNumber(nonce) : '0x', - gasPrice ? Hex.fromNumber(gasPrice) : '0x', - gas ? Hex.fromNumber(gas) : '0x', - to ?? '0x', - value ? Hex.fromNumber(value) : '0x', - data ?? input ?? '0x', - ] + type ReturnType = Hex.Hex - const signature = (() => { - if (options.signature) - return { - r: options.signature.r, - s: options.signature.s, - v: options.signature.yParity === 0 ? 27 : 28, - } + type ErrorType = + | Hash_keccak256.ErrorType + | serialize.ErrorType + | Errors.GlobalErrorType +} - if ( - typeof envelope.r === 'undefined' || - typeof envelope.s === 'undefined' - ) - return undefined +hash.parseError = (error: unknown) => + /* v8 ignore next */ + error as hash.ErrorType + +/** + * Serializes a {@link ox#TransactionEnvelopeLegacy.TransactionEnvelopeLegacy}. + * + * @example + * ```ts twoslash + * // @noErrors + * import { TransactionEnvelopeLegacy } from 'ox' + * + * const envelope = TransactionEnvelopeLegacy.from({ + * chainId: 1, + * gasPrice: Value.fromGwei('10'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * + * const serialized = TransactionEnvelopeLegacy.serialize(envelope) // [!code focus] + * ``` + * + * @example + * ### Attaching Signatures + * + * It is possible to attach a `signature` to the serialized Transaction Envelope. + * + * ```ts twoslash + * // @noErrors + * import { Secp256k1, TransactionEnvelopeLegacy, Value } from 'ox' + * + * const envelope = TransactionEnvelopeLegacy.from({ + * chainId: 1, + * gasPrice: Value.fromGwei('10'), + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * + * const signature = Secp256k1.sign({ + * payload: TransactionEnvelopeLegacy.getSignPayload(envelope), + * privateKey: '0x...', + * }) + * + * const serialized = TransactionEnvelopeLegacy.serialize(envelope, { // [!code focus] + * signature, // [!code focus] + * }) // [!code focus] + * + * // ... send `serialized` transaction to JSON-RPC `eth_sendRawTransaction` + * ``` + * + * @param envelope - The Transaction Envelope to serialize. + * @param options - Options. + * @returns The serialized Transaction Envelope. + */ +export function serialize( + envelope: PartialBy, + options: serialize.Options = {}, +): Serialized { + const { chainId = 0, gas, data, input, nonce, to, value, gasPrice } = envelope + + assert(envelope) + + let serializedTransaction = [ + nonce ? Hex.fromNumber(nonce) : '0x', + gasPrice ? Hex.fromNumber(gasPrice) : '0x', + gas ? Hex.fromNumber(gas) : '0x', + to ?? '0x', + value ? Hex.fromNumber(value) : '0x', + data ?? input ?? '0x', + ] + + const signature = (() => { + if (options.signature) return { - r: envelope.r, - s: envelope.s, - v: envelope.v!, + r: options.signature.r, + s: options.signature.s, + v: options.signature.yParity === 0 ? 27 : 28, } - })() - if (signature) { - const v = (() => { - // EIP-155 (inferred chainId) - if (signature.v >= 35) { - const inferredChainId = Math.floor((signature.v - 35) / 2) - if (inferredChainId > 0) return signature.v - return 27 + (signature.v === 35 ? 0 : 1) - } + if (typeof envelope.r === 'undefined' || typeof envelope.s === 'undefined') + return undefined + return { + r: envelope.r, + s: envelope.s, + v: envelope.v!, + } + })() + + if (signature) { + const v = (() => { + // EIP-155 (inferred chainId) + if (signature.v >= 35) { + const inferredChainId = Math.floor((signature.v - 35) / 2) + if (inferredChainId > 0) return signature.v + return 27 + (signature.v === 35 ? 0 : 1) + } - // EIP-155 (explicit chainId) - if (chainId > 0) return chainId * 2 + 35 + signature.v - 27 - - // Pre-EIP-155 (no chainId) - const v = 27 + (signature.v === 27 ? 0 : 1) - if (signature.v !== v) - throw new Signature_InvalidVError({ value: signature.v }) - return v - })() - - serializedTransaction = [ - ...serializedTransaction, - Hex.fromNumber(v), - signature.r === 0n ? '0x' : Hex.trimLeft(Hex.fromNumber(signature.r)), - signature.s === 0n ? '0x' : Hex.trimLeft(Hex.fromNumber(signature.s)), - ] - } else if (chainId > 0) - serializedTransaction = [ - ...serializedTransaction, - Hex.fromNumber(chainId), - '0x', - '0x', - ] - - return Rlp_fromHex(serializedTransaction) as never - } + // EIP-155 (explicit chainId) + if (chainId > 0) return chainId * 2 + 35 + signature.v - 27 - export declare namespace serialize { - type Options = { - /** Signature to append to the serialized Transaction Envelope. */ - signature?: Signature | undefined - } + // Pre-EIP-155 (no chainId) + const v = 27 + (signature.v === 27 ? 0 : 1) + if (signature.v !== v) + throw new Signature_InvalidVError({ value: signature.v }) + return v + })() - type ErrorType = - | TransactionEnvelopeLegacy.assert.ErrorType - | Hex.fromNumber.ErrorType - | Hex.trimLeft.ErrorType - | Rlp_fromHex.ErrorType - | Signature_InvalidVError - | Errors.GlobalErrorType - } + serializedTransaction = [ + ...serializedTransaction, + Hex.fromNumber(v), + signature.r === 0n ? '0x' : Hex.trimLeft(Hex.fromNumber(signature.r)), + signature.s === 0n ? '0x' : Hex.trimLeft(Hex.fromNumber(signature.s)), + ] + } else if (chainId > 0) + serializedTransaction = [ + ...serializedTransaction, + Hex.fromNumber(chainId), + '0x', + '0x', + ] - serialize.parseError = (error: unknown) => - /* v8 ignore next */ - error as serialize.ErrorType - - /** - * Converts an {@link ox#(TransactionEnvelopeLegacy:type)} to an {@link ox#(TransactionEnvelopeLegacy:namespace).Rpc}. - * - * @example - * ```ts twoslash - * import { RpcRequest, TransactionEnvelopeLegacy, Value } from 'ox' - * - * const envelope = TransactionEnvelopeLegacy.from({ - * chainId: 1, - * nonce: 0n, - * gas: 21000n, - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: Value.fromEther('1'), - * }) - * - * const envelope_rpc = TransactionEnvelopeLegacy.toRpc(envelope) // [!code focus] - * - * const request = RpcRequest.from({ - * id: 0, - * method: 'eth_sendTransaction', - * params: [envelope_rpc], - * }) - * ``` - * - * @param envelope - The legacy transaction envelope to convert. - * @returns An RPC-formatted legacy transaction envelope. - */ - export function toRpc( - envelope: Omit, - ): TransactionEnvelopeLegacy.Rpc { - const signature = Signature_extract(envelope)! + return Rlp_fromHex(serializedTransaction) as never +} - return { - ...envelope, - chainId: - typeof envelope.chainId === 'number' - ? Hex.fromNumber(envelope.chainId) - : undefined, - data: envelope.data ?? envelope.input, - type: '0x0', - ...(typeof envelope.gas === 'bigint' - ? { gas: Hex.fromNumber(envelope.gas) } - : {}), - ...(typeof envelope.nonce === 'bigint' - ? { nonce: Hex.fromNumber(envelope.nonce) } - : {}), - ...(typeof envelope.value === 'bigint' - ? { value: Hex.fromNumber(envelope.value) } - : {}), - ...(typeof envelope.gasPrice === 'bigint' - ? { gasPrice: Hex.fromNumber(envelope.gasPrice) } - : {}), - ...(signature - ? { - ...Signature_toRpc(signature), - v: signature.yParity === 0 ? '0x1b' : '0x1c', - } - : {}), - } as never +export declare namespace serialize { + type Options = { + /** Signature to append to the serialized Transaction Envelope. */ + signature?: Signature | undefined } - export declare namespace toRpc { - export type ErrorType = Signature_extract.ErrorType | Errors.GlobalErrorType - } + type ErrorType = + | assert.ErrorType + | Hex.fromNumber.ErrorType + | Hex.trimLeft.ErrorType + | Rlp_fromHex.ErrorType + | Signature_InvalidVError + | Errors.GlobalErrorType +} - toRpc.parseError = (error: unknown) => - /* v8 ignore next */ - error as toRpc.ErrorType - - /** - * Validates a {@link ox#(TransactionEnvelopeLegacy:type)}. Returns `true` if the envelope is valid, `false` otherwise. - * - * @example - * ```ts twoslash - * import { TransactionEnvelopeLegacy, Value } from 'ox' - * - * const valid = TransactionEnvelopeLegacy.assert({ - * gasPrice: 2n ** 256n - 1n + 1n, - * chainId: 1, - * to: '0x0000000000000000000000000000000000000000', - * value: Value.fromEther('1'), - * }) - * // @log: false - * ``` - * - * @param envelope - The transaction envelope to validate. - */ - export function validate( - envelope: PartialBy, - ) { - try { - assert(envelope) - return true - } catch { - return false - } - } +serialize.parseError = (error: unknown) => + /* v8 ignore next */ + error as serialize.ErrorType + +/** + * Converts an {@link ox#TransactionEnvelopeLegacy.TransactionEnvelopeLegacy} to an {@link ox#TransactionEnvelopeLegacy.Rpc}. + * + * @example + * ```ts twoslash + * import { RpcRequest, TransactionEnvelopeLegacy, Value } from 'ox' + * + * const envelope = TransactionEnvelopeLegacy.from({ + * chainId: 1, + * nonce: 0n, + * gas: 21000n, + * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + * value: Value.fromEther('1'), + * }) + * + * const envelope_rpc = TransactionEnvelopeLegacy.toRpc(envelope) // [!code focus] + * + * const request = RpcRequest.from({ + * id: 0, + * method: 'eth_sendTransaction', + * params: [envelope_rpc], + * }) + * ``` + * + * @param envelope - The legacy transaction envelope to convert. + * @returns An RPC-formatted legacy transaction envelope. + */ +export function toRpc(envelope: Omit): Rpc { + const signature = Signature_extract(envelope)! + + return { + ...envelope, + chainId: + typeof envelope.chainId === 'number' + ? Hex.fromNumber(envelope.chainId) + : undefined, + data: envelope.data ?? envelope.input, + type: '0x0', + ...(typeof envelope.gas === 'bigint' + ? { gas: Hex.fromNumber(envelope.gas) } + : {}), + ...(typeof envelope.nonce === 'bigint' + ? { nonce: Hex.fromNumber(envelope.nonce) } + : {}), + ...(typeof envelope.value === 'bigint' + ? { value: Hex.fromNumber(envelope.value) } + : {}), + ...(typeof envelope.gasPrice === 'bigint' + ? { gasPrice: Hex.fromNumber(envelope.gasPrice) } + : {}), + ...(signature + ? { + ...Signature_toRpc(signature), + v: signature.yParity === 0 ? '0x1b' : '0x1c', + } + : {}), + } as never +} - export declare namespace validate { - type ErrorType = Errors.GlobalErrorType - } +export declare namespace toRpc { + export type ErrorType = Signature_extract.ErrorType | Errors.GlobalErrorType +} - validate.parseError = (error: unknown) => - /* v8 ignore next */ - error as validate.ErrorType +toRpc.parseError = (error: unknown) => + /* v8 ignore next */ + error as toRpc.ErrorType + +/** + * Validates a {@link ox#TransactionEnvelopeLegacy.TransactionEnvelopeLegacy}. Returns `true` if the envelope is valid, `false` otherwise. + * + * @example + * ```ts twoslash + * import { TransactionEnvelopeLegacy, Value } from 'ox' + * + * const valid = TransactionEnvelopeLegacy.assert({ + * gasPrice: 2n ** 256n - 1n + 1n, + * chainId: 1, + * to: '0x0000000000000000000000000000000000000000', + * value: Value.fromEther('1'), + * }) + * // @log: false + * ``` + * + * @param envelope - The transaction envelope to validate. + */ +export function validate( + envelope: PartialBy, +) { + try { + assert(envelope) + return true + } catch { + return false + } +} - // #endregion +export declare namespace validate { + type ErrorType = Errors.GlobalErrorType } + +validate.parseError = (error: unknown) => + /* v8 ignore next */ + error as validate.ErrorType diff --git a/src/index.ts b/src/index.ts index 6f71e35b..a4f01aa4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2326,7 +2326,7 @@ export * as TransactionEnvelope from './TransactionEnvelope.js' * @example * ### Instantiating Transaction Envelopes * - * Transaction Envelopes can be instantiated using {@link ox#(TransactionEnvelopeLegacy:namespace).(from:function)}: + * Transaction Envelopes can be instantiated using {@link ox#TransactionEnvelopeLegacy.(from:function)}: * * ```ts twoslash * import { TransactionEnvelopeLegacy, Value } from 'ox' @@ -2341,7 +2341,7 @@ export * as TransactionEnvelope from './TransactionEnvelope.js' * * @example * ### Signing Transaction Envelopes * - * Transaction Envelopes can be signed using {@link ox#(TransactionEnvelopeLegacy:namespace).(getSignPayload:function)} and a signing function such as {@link ox#Secp256k1.(sign:function)} or {@link ox#P256.(sign:function)}: + * Transaction Envelopes can be signed using {@link ox#TransactionEnvelopeLegacy.(getSignPayload:function)} and a signing function such as {@link ox#Secp256k1.(sign:function)} or {@link ox#P256.(sign:function)}: * * ```ts twoslash * // @noErrors @@ -2366,7 +2366,7 @@ export * as TransactionEnvelope from './TransactionEnvelope.js' * @example * ### Serializing Transaction Envelopes * - * Transaction Envelopes can be serialized using {@link ox#(TransactionEnvelopeLegacy:namespace).(serialize:function)}: + * Transaction Envelopes can be serialized using {@link ox#TransactionEnvelopeLegacy.(serialize:function)}: * * ```ts twoslash * import { TransactionEnvelopeLegacy, Value } from 'ox' @@ -2384,7 +2384,7 @@ export * as TransactionEnvelope from './TransactionEnvelope.js' * @example * ### Computing Transaction Hashes * - * Transaction Hashes can be computed using {@link ox#(TransactionEnvelopeLegacy:namespace).(hash:function)}: + * Transaction Hashes can be computed using {@link ox#TransactionEnvelopeLegacy.(hash:function)}: * * ```ts twoslash * import { Secp256k1, TransactionEnvelopeLegacy } from 'ox' @@ -2411,7 +2411,7 @@ export * as TransactionEnvelope from './TransactionEnvelope.js' * * @category Transaction Envelopes */ -export { TransactionEnvelopeLegacy } from './TransactionEnvelopeLegacy.js' +export * as TransactionEnvelopeLegacy from './TransactionEnvelopeLegacy.js' /** * Utility functions for working with [EIP-1559 Typed Transaction Envelopes](https://eips.ethereum.org/EIPS/eip-1559) @@ -2419,7 +2419,7 @@ export { TransactionEnvelopeLegacy } from './TransactionEnvelopeLegacy.js' * @example * ### Instantiating Transaction Envelopes * - * Transaction Envelopes can be instantiated using {@link ox#(TransactionEnvelopeEip1559:namespace).(from:function)}: + * Transaction Envelopes can be instantiated using {@link ox#TransactionEnvelopeEip1559.(from:function)}: * * ```ts twoslash * import { TransactionEnvelopeEip1559, Value } from 'ox' @@ -2444,7 +2444,7 @@ export { TransactionEnvelopeLegacy } from './TransactionEnvelopeLegacy.js' * @example * ### Signing Transaction Envelopes * - * Transaction Envelopes can be signed using {@link ox#(TransactionEnvelopeEip1559:namespace).(getSignPayload:function)} and a signing function such as {@link ox#Secp256k1.(sign:function)} or {@link ox#P256.(sign:function)}: + * Transaction Envelopes can be signed using {@link ox#TransactionEnvelopeEip1559.(getSignPayload:function)} and a signing function such as {@link ox#Secp256k1.(sign:function)} or {@link ox#P256.(sign:function)}: * * ```ts twoslash * import { Secp256k1, TransactionEnvelopeEip1559 } from 'ox' @@ -2469,7 +2469,7 @@ export { TransactionEnvelopeLegacy } from './TransactionEnvelopeLegacy.js' * @example * ### Serializing Transaction Envelopes * - * Transaction Envelopes can be serialized using {@link ox#(TransactionEnvelopeEip1559:namespace).(serialize:function)}: + * Transaction Envelopes can be serialized using {@link ox#TransactionEnvelopeEip1559.(serialize:function)}: * * @example * ```ts twoslash @@ -2489,7 +2489,7 @@ export { TransactionEnvelopeLegacy } from './TransactionEnvelopeLegacy.js' * @example * ### Computing Transaction Hashes * - * Transaction Hashes can be computed using {@link ox#(TransactionEnvelopeEip1559:namespace).(hash:function)}: + * Transaction Hashes can be computed using {@link ox#TransactionEnvelopeEip1559.(hash:function)}: * * ```ts twoslash * import { Secp256k1, TransactionEnvelopeEip1559, Value } from 'ox' @@ -2517,7 +2517,7 @@ export { TransactionEnvelopeLegacy } from './TransactionEnvelopeLegacy.js' * * @category Transaction Envelopes */ -export { TransactionEnvelopeEip1559 } from './TransactionEnvelopeEip1559.js' +export * as TransactionEnvelopeEip1559 from './TransactionEnvelopeEip1559.js' /** * Utility functions for working with [EIP-2930 Typed Transaction Envelopes](https://eips.ethereum.org/EIPS/eip-2930) @@ -2525,7 +2525,7 @@ export { TransactionEnvelopeEip1559 } from './TransactionEnvelopeEip1559.js' * @example * ### Instantiating Transaction Envelopes * - * Transaction Envelopes can be instantiated using {@link ox#(TransactionEnvelopeEip2930:namespace).(from:function)}: + * Transaction Envelopes can be instantiated using {@link ox#TransactionEnvelopeEip2930.(from:function)}: * * ```ts twoslash * // @noErrors @@ -2543,7 +2543,7 @@ export { TransactionEnvelopeEip1559 } from './TransactionEnvelopeEip1559.js' * @example * ### Signing Transaction Envelopes * - * Transaction Envelopes can be signed using {@link ox#(TransactionEnvelopeEip2930:namespace).(getSignPayload:function)} and a signing function such as {@link ox#Secp256k1.(sign:function)} or {@link ox#P256.(sign:function)}: + * Transaction Envelopes can be signed using {@link ox#TransactionEnvelopeEip2930.(getSignPayload:function)} and a signing function such as {@link ox#Secp256k1.(sign:function)} or {@link ox#P256.(sign:function)}: * * ```ts twoslash * import { Secp256k1, TransactionEnvelopeEip2930 } from 'ox' @@ -2566,7 +2566,7 @@ export { TransactionEnvelopeEip1559 } from './TransactionEnvelopeEip1559.js' * @example * ### Serializing Transaction Envelopes * - * Transaction Envelopes can be serialized using {@link ox#(TransactionEnvelopeEip2930:namespace).(serialize:function)}: + * Transaction Envelopes can be serialized using {@link ox#TransactionEnvelopeEip2930.(serialize:function)}: * * ```ts twoslash * import { Secp256k1, TransactionEnvelopeEip2930, Value } from 'ox' @@ -2586,7 +2586,7 @@ export { TransactionEnvelopeEip1559 } from './TransactionEnvelopeEip1559.js' * @example * ### Computing Transaction Hashes * - * Transaction Hashes can be computed using {@link ox#(TransactionEnvelopeEip2930:namespace).(hash:function)}: + * Transaction Hashes can be computed using {@link ox#TransactionEnvelopeEip2930.(hash:function)}: * * ```ts twoslash * import { Secp256k1, TransactionEnvelopeEip2930 } from 'ox' @@ -2613,7 +2613,7 @@ export { TransactionEnvelopeEip1559 } from './TransactionEnvelopeEip1559.js' * * @category Transaction Envelopes */ -export { TransactionEnvelopeEip2930 } from './TransactionEnvelopeEip2930.js' +export * as TransactionEnvelopeEip2930 from './TransactionEnvelopeEip2930.js' /** * Utility functions for working with [EIP-4844 Typed Transaction Envelopes](https://eips.ethereum.org/EIPS/eip-4844) diff --git a/src/internal/TransactionEnvelope/eip4844/assert.ts b/src/internal/TransactionEnvelope/eip4844/assert.ts index d356accf..f65b6f4d 100644 --- a/src/internal/TransactionEnvelope/eip4844/assert.ts +++ b/src/internal/TransactionEnvelope/eip4844/assert.ts @@ -1,6 +1,6 @@ import type * as Errors from '../../../Errors.js' import * as Hex from '../../../Hex.js' -import { TransactionEnvelopeEip1559 } from '../../../TransactionEnvelopeEip1559.js' +import * as TransactionEnvelopeEip1559 from '../../../TransactionEnvelopeEip1559.js' import { Blobs_EmptyBlobVersionedHashesError, Blobs_InvalidVersionedHashSizeError, @@ -48,7 +48,7 @@ export function TransactionEnvelopeEip4844_assert( } } TransactionEnvelopeEip1559.assert( - envelope as {} as TransactionEnvelopeEip1559, + envelope as {} as TransactionEnvelopeEip1559.TransactionEnvelopeEip1559, ) } diff --git a/src/internal/TransactionEnvelope/eip7702/assert.ts b/src/internal/TransactionEnvelope/eip7702/assert.ts index d78d302c..733288b2 100644 --- a/src/internal/TransactionEnvelope/eip7702/assert.ts +++ b/src/internal/TransactionEnvelope/eip7702/assert.ts @@ -1,6 +1,6 @@ import type * as Errors from '../../../Errors.js' import * as TransactionEnvelope from '../../../TransactionEnvelope.js' -import { TransactionEnvelopeEip1559 } from '../../../TransactionEnvelopeEip1559.js' +import * as TransactionEnvelopeEip1559 from '../../../TransactionEnvelopeEip1559.js' import { Address_assert } from '../../Address/assert.js' import type { PartialBy } from '../../types.js' import type { TransactionEnvelopeEip7702 } from './types.js' @@ -39,7 +39,7 @@ export function TransactionEnvelopeEip7702_assert( } } TransactionEnvelopeEip1559.assert( - envelope as {} as TransactionEnvelopeEip1559, + envelope as {} as TransactionEnvelopeEip1559.TransactionEnvelopeEip1559, ) }