FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

50
node_modules/viem/utils/transaction/assertRequest.ts generated vendored Normal file
View File

@@ -0,0 +1,50 @@
import {
type ParseAccountErrorType,
parseAccount,
} from '../../accounts/utils/parseAccount.js'
import type { SendTransactionParameters } from '../../actions/wallet/sendTransaction.js'
import { maxUint256 } from '../../constants/number.js'
import {
InvalidAddressError,
type InvalidAddressErrorType,
} from '../../errors/address.js'
import {
FeeCapTooHighError,
type FeeCapTooHighErrorType,
TipAboveFeeCapError,
type TipAboveFeeCapErrorType,
} from '../../errors/node.js'
import type { FeeConflictErrorType } from '../../errors/transaction.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Chain } from '../../types/chain.js'
import type { ExactPartial } from '../../types/utils.js'
import { isAddress } from '../address/isAddress.js'
export type AssertRequestParameters = ExactPartial<
SendTransactionParameters<Chain>
>
export type AssertRequestErrorType =
| InvalidAddressErrorType
| FeeConflictErrorType
| FeeCapTooHighErrorType
| ParseAccountErrorType
| TipAboveFeeCapErrorType
| ErrorType
export function assertRequest(args: AssertRequestParameters) {
const { account: account_, maxFeePerGas, maxPriorityFeePerGas, to } = args
const account = account_ ? parseAccount(account_) : undefined
if (account && !isAddress(account.address))
throw new InvalidAddressError({ address: account.address })
if (to && !isAddress(to)) throw new InvalidAddressError({ address: to })
if (maxFeePerGas && maxFeePerGas > maxUint256)
throw new FeeCapTooHighError({ maxFeePerGas })
if (
maxPriorityFeePerGas &&
maxFeePerGas &&
maxPriorityFeePerGas > maxFeePerGas
)
throw new TipAboveFeeCapError({ maxFeePerGas, maxPriorityFeePerGas })
}

View File

@@ -0,0 +1,158 @@
import { versionedHashVersionKzg } from '../../constants/kzg.js'
import { maxUint256 } from '../../constants/number.js'
import {
InvalidAddressError,
type InvalidAddressErrorType,
} from '../../errors/address.js'
import { BaseError, type BaseErrorType } from '../../errors/base.js'
import {
EmptyBlobError,
type EmptyBlobErrorType,
InvalidVersionedHashSizeError,
type InvalidVersionedHashSizeErrorType,
InvalidVersionedHashVersionError,
type InvalidVersionedHashVersionErrorType,
} from '../../errors/blob.js'
import {
InvalidChainIdError,
type InvalidChainIdErrorType,
} from '../../errors/chain.js'
import {
FeeCapTooHighError,
type FeeCapTooHighErrorType,
TipAboveFeeCapError,
type TipAboveFeeCapErrorType,
} from '../../errors/node.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
TransactionSerializableEIP1559,
TransactionSerializableEIP2930,
TransactionSerializableEIP4844,
TransactionSerializableEIP7702,
TransactionSerializableLegacy,
} from '../../types/transaction.js'
import { type IsAddressErrorType, isAddress } from '../address/isAddress.js'
import { size } from '../data/size.js'
import { slice } from '../data/slice.js'
import { hexToNumber } from '../encoding/fromHex.js'
export type AssertTransactionEIP7702ErrorType =
| AssertTransactionEIP1559ErrorType
| InvalidAddressErrorType
| InvalidChainIdErrorType
| ErrorType
export function assertTransactionEIP7702(
transaction: TransactionSerializableEIP7702,
) {
const { authorizationList } = transaction
if (authorizationList) {
for (const authorization of authorizationList) {
const { chainId } = authorization
const address = authorization.address
if (!isAddress(address)) throw new InvalidAddressError({ address })
if (chainId < 0) throw new InvalidChainIdError({ chainId })
}
}
assertTransactionEIP1559(transaction as {} as TransactionSerializableEIP1559)
}
export type AssertTransactionEIP4844ErrorType =
| AssertTransactionEIP1559ErrorType
| EmptyBlobErrorType
| InvalidVersionedHashSizeErrorType
| InvalidVersionedHashVersionErrorType
| ErrorType
export function assertTransactionEIP4844(
transaction: TransactionSerializableEIP4844,
) {
const { blobVersionedHashes } = transaction
if (blobVersionedHashes) {
if (blobVersionedHashes.length === 0) throw new EmptyBlobError()
for (const hash of blobVersionedHashes) {
const size_ = size(hash)
const version = hexToNumber(slice(hash, 0, 1))
if (size_ !== 32)
throw new InvalidVersionedHashSizeError({ hash, size: size_ })
if (version !== versionedHashVersionKzg)
throw new InvalidVersionedHashVersionError({
hash,
version,
})
}
}
assertTransactionEIP1559(transaction as {} as TransactionSerializableEIP1559)
}
export type AssertTransactionEIP1559ErrorType =
| BaseErrorType
| IsAddressErrorType
| InvalidAddressErrorType
| InvalidChainIdErrorType
| FeeCapTooHighErrorType
| TipAboveFeeCapErrorType
| ErrorType
export function assertTransactionEIP1559(
transaction: TransactionSerializableEIP1559,
) {
const { chainId, maxPriorityFeePerGas, maxFeePerGas, to } = transaction
if (chainId <= 0) throw new InvalidChainIdError({ chainId })
if (to && !isAddress(to)) throw new InvalidAddressError({ address: to })
if (maxFeePerGas && maxFeePerGas > maxUint256)
throw new FeeCapTooHighError({ maxFeePerGas })
if (
maxPriorityFeePerGas &&
maxFeePerGas &&
maxPriorityFeePerGas > maxFeePerGas
)
throw new TipAboveFeeCapError({ maxFeePerGas, maxPriorityFeePerGas })
}
export type AssertTransactionEIP2930ErrorType =
| BaseErrorType
| IsAddressErrorType
| InvalidAddressErrorType
| InvalidChainIdErrorType
| FeeCapTooHighErrorType
| ErrorType
export function assertTransactionEIP2930(
transaction: TransactionSerializableEIP2930,
) {
const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to } =
transaction
if (chainId <= 0) throw new InvalidChainIdError({ chainId })
if (to && !isAddress(to)) throw new InvalidAddressError({ address: to })
if (maxPriorityFeePerGas || maxFeePerGas)
throw new BaseError(
'`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid EIP-2930 Transaction attribute.',
)
if (gasPrice && gasPrice > maxUint256)
throw new FeeCapTooHighError({ maxFeePerGas: gasPrice })
}
export type AssertTransactionLegacyErrorType =
| BaseErrorType
| IsAddressErrorType
| InvalidAddressErrorType
| InvalidChainIdErrorType
| FeeCapTooHighErrorType
| ErrorType
export function assertTransactionLegacy(
transaction: TransactionSerializableLegacy,
) {
const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to } =
transaction
if (to && !isAddress(to)) throw new InvalidAddressError({ address: to })
if (typeof chainId !== 'undefined' && chainId <= 0)
throw new InvalidChainIdError({ chainId })
if (maxPriorityFeePerGas || maxFeePerGas)
throw new BaseError(
'`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid Legacy Transaction attribute.',
)
if (gasPrice && gasPrice > maxUint256)
throw new FeeCapTooHighError({ maxFeePerGas: gasPrice })
}

View File

@@ -0,0 +1,75 @@
import {
InvalidSerializedTransactionTypeError,
type InvalidSerializedTransactionTypeErrorType,
} from '../../errors/transaction.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Hex } from '../../index.js'
import type {
TransactionSerialized,
TransactionSerializedEIP1559,
TransactionSerializedEIP2930,
TransactionSerializedEIP4844,
TransactionSerializedEIP7702,
TransactionSerializedGeneric,
TransactionSerializedLegacy,
TransactionType,
} from '../../types/transaction.js'
import type { IsNarrowable, IsNever } from '../../types/utils.js'
import { type SliceHexErrorType, sliceHex } from '../data/slice.js'
import { type HexToNumberErrorType, hexToNumber } from '../encoding/fromHex.js'
export type GetSerializedTransactionType<
serializedTransaction extends
TransactionSerializedGeneric = TransactionSerialized,
result =
| (serializedTransaction extends TransactionSerializedEIP1559
? 'eip1559'
: never)
| (serializedTransaction extends TransactionSerializedEIP2930
? 'eip2930'
: never)
| (serializedTransaction extends TransactionSerializedEIP4844
? 'eip4844'
: never)
| (serializedTransaction extends TransactionSerializedEIP7702
? 'eip7702'
: never)
| (serializedTransaction extends TransactionSerializedLegacy
? 'legacy'
: never),
> = IsNarrowable<serializedTransaction, Hex> extends true
? IsNever<result> extends false
? result
: 'legacy'
: TransactionType
export type GetSerializedTransactionTypeErrorType =
| HexToNumberErrorType
| InvalidSerializedTransactionTypeErrorType
| SliceHexErrorType
| ErrorType
export function getSerializedTransactionType<
const serializedTransaction extends TransactionSerializedGeneric,
>(
serializedTransaction: serializedTransaction,
): GetSerializedTransactionType<serializedTransaction> {
const serializedType = sliceHex(serializedTransaction, 0, 1)
if (serializedType === '0x04')
return 'eip7702' as GetSerializedTransactionType<serializedTransaction>
if (serializedType === '0x03')
return 'eip4844' as GetSerializedTransactionType<serializedTransaction>
if (serializedType === '0x02')
return 'eip1559' as GetSerializedTransactionType<serializedTransaction>
if (serializedType === '0x01')
return 'eip2930' as GetSerializedTransactionType<serializedTransaction>
if (serializedType !== '0x' && hexToNumber(serializedType) >= 0xc0)
return 'legacy' as GetSerializedTransactionType<serializedTransaction>
throw new InvalidSerializedTransactionTypeError({ serializedType })
}

View File

@@ -0,0 +1,134 @@
import {
InvalidSerializableTransactionError,
type InvalidSerializableTransactionErrorType,
} from '../../errors/transaction.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
FeeValuesEIP1559,
FeeValuesEIP4844,
FeeValuesLegacy,
} from '../../index.js'
import type {
TransactionRequestGeneric,
TransactionSerializableEIP2930,
TransactionSerializableEIP4844,
TransactionSerializableEIP7702,
TransactionSerializableGeneric,
} from '../../types/transaction.js'
import type { Assign, ExactPartial, IsNever, OneOf } from '../../types/utils.js'
export type GetTransactionType<
transaction extends OneOf<
TransactionSerializableGeneric | TransactionRequestGeneric
> = TransactionSerializableGeneric,
result =
| (transaction extends LegacyProperties ? 'legacy' : never)
| (transaction extends EIP1559Properties ? 'eip1559' : never)
| (transaction extends EIP2930Properties ? 'eip2930' : never)
| (transaction extends EIP4844Properties ? 'eip4844' : never)
| (transaction extends EIP7702Properties ? 'eip7702' : never)
| (transaction['type'] extends TransactionSerializableGeneric['type']
? Extract<transaction['type'], string>
: never),
> = IsNever<keyof transaction> extends true
? string
: IsNever<result> extends false
? result
: string
export type GetTransactionTypeErrorType =
| InvalidSerializableTransactionErrorType
| ErrorType
export function getTransactionType<
const transaction extends OneOf<
TransactionSerializableGeneric | TransactionRequestGeneric
>,
>(transaction: transaction): GetTransactionType<transaction> {
if (transaction.type)
return transaction.type as GetTransactionType<transaction>
if (typeof transaction.authorizationList !== 'undefined')
return 'eip7702' as any
if (
typeof transaction.blobs !== 'undefined' ||
typeof transaction.blobVersionedHashes !== 'undefined' ||
typeof transaction.maxFeePerBlobGas !== 'undefined' ||
typeof transaction.sidecars !== 'undefined'
)
return 'eip4844' as any
if (
typeof transaction.maxFeePerGas !== 'undefined' ||
typeof transaction.maxPriorityFeePerGas !== 'undefined'
) {
return 'eip1559' as any
}
if (typeof transaction.gasPrice !== 'undefined') {
if (typeof transaction.accessList !== 'undefined') return 'eip2930' as any
return 'legacy' as any
}
throw new InvalidSerializableTransactionError({ transaction })
}
////////////////////////////////////////////////////////////////////////////////////////////
// Types
type BaseProperties = {
accessList?: undefined
authorizationList?: undefined
blobs?: undefined
blobVersionedHashes?: undefined
gasPrice?: undefined
maxFeePerBlobGas?: undefined
maxFeePerGas?: undefined
maxPriorityFeePerGas?: undefined
sidecars?: undefined
}
type LegacyProperties = Assign<BaseProperties, FeeValuesLegacy>
type EIP1559Properties = Assign<
BaseProperties,
OneOf<
| {
maxFeePerGas: FeeValuesEIP1559['maxFeePerGas']
}
| {
maxPriorityFeePerGas: FeeValuesEIP1559['maxPriorityFeePerGas']
},
FeeValuesEIP1559
> & {
accessList?: TransactionSerializableEIP2930['accessList'] | undefined
}
>
type EIP2930Properties = Assign<
ExactPartial<LegacyProperties>,
{
accessList: TransactionSerializableEIP2930['accessList']
}
>
type EIP4844Properties = Assign<
ExactPartial<EIP1559Properties>,
ExactPartial<FeeValuesEIP4844> &
OneOf<
| {
blobs: TransactionSerializableEIP4844['blobs']
}
| {
blobVersionedHashes: TransactionSerializableEIP4844['blobVersionedHashes']
}
| {
sidecars: TransactionSerializableEIP4844['sidecars']
},
TransactionSerializableEIP4844
>
>
type EIP7702Properties = Assign<
ExactPartial<EIP1559Properties>,
{
authorizationList: TransactionSerializableEIP7702['authorizationList']
}
>

611
node_modules/viem/utils/transaction/parseTransaction.ts generated vendored Normal file
View File

@@ -0,0 +1,611 @@
import {
InvalidAddressError,
type InvalidAddressErrorType,
} from '../../errors/address.js'
import {
InvalidLegacyVError,
type InvalidLegacyVErrorType,
InvalidSerializedTransactionError,
type InvalidSerializedTransactionErrorType,
} from '../../errors/transaction.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
SerializedAuthorizationList,
SignedAuthorizationList,
} from '../../types/authorization.js'
import type { Hex, Signature } from '../../types/misc.js'
import type {
AccessList,
TransactionRequestEIP2930,
TransactionRequestLegacy,
TransactionSerializable,
TransactionSerializableEIP1559,
TransactionSerializableEIP2930,
TransactionSerializableEIP4844,
TransactionSerializableEIP7702,
TransactionSerializableLegacy,
TransactionSerialized,
TransactionSerializedEIP1559,
TransactionSerializedEIP2930,
TransactionSerializedEIP4844,
TransactionSerializedEIP7702,
TransactionSerializedGeneric,
TransactionType,
} from '../../types/transaction.js'
import type { IsNarrowable, Mutable } from '../../types/utils.js'
import { type IsAddressErrorType, isAddress } from '../address/isAddress.js'
import { toBlobSidecars } from '../blob/toBlobSidecars.js'
import { type IsHexErrorType, isHex } from '../data/isHex.js'
import { type PadHexErrorType, padHex } from '../data/pad.js'
import { trim } from '../data/trim.js'
import {
type HexToBigIntErrorType,
type HexToNumberErrorType,
hexToBigInt,
hexToNumber,
} from '../encoding/fromHex.js'
import { type FromRlpErrorType, fromRlp } from '../encoding/fromRlp.js'
import type { RecursiveArray } from '../encoding/toRlp.js'
import { isHash } from '../hash/isHash.js'
import {
type AssertTransactionEIP1559ErrorType,
type AssertTransactionEIP2930ErrorType,
type AssertTransactionEIP4844ErrorType,
type AssertTransactionEIP7702ErrorType,
type AssertTransactionLegacyErrorType,
assertTransactionEIP1559,
assertTransactionEIP2930,
assertTransactionEIP4844,
assertTransactionEIP7702,
assertTransactionLegacy,
} from './assertTransaction.js'
import {
type GetSerializedTransactionType,
type GetSerializedTransactionTypeErrorType,
getSerializedTransactionType,
} from './getSerializedTransactionType.js'
export type ParseTransactionReturnType<
serialized extends TransactionSerializedGeneric = TransactionSerialized,
type extends TransactionType = GetSerializedTransactionType<serialized>,
> = IsNarrowable<serialized, Hex> extends true
?
| (type extends 'eip1559' ? TransactionSerializableEIP1559 : never)
| (type extends 'eip2930' ? TransactionSerializableEIP2930 : never)
| (type extends 'eip4844'
? TransactionSerializableEIP4844<bigint, number, false>
: never)
| (type extends 'eip7702' ? TransactionSerializableEIP7702 : never)
| (type extends 'legacy' ? TransactionSerializableLegacy : never)
: TransactionSerializable
export type ParseTransactionErrorType =
| GetSerializedTransactionTypeErrorType
| ParseTransactionEIP1559ErrorType
| ParseTransactionEIP2930ErrorType
| ParseTransactionEIP4844ErrorType
| ParseTransactionEIP7702ErrorType
| ParseTransactionLegacyErrorType
export function parseTransaction<
const serialized extends TransactionSerializedGeneric,
>(serializedTransaction: serialized): ParseTransactionReturnType<serialized> {
const type = getSerializedTransactionType(serializedTransaction)
if (type === 'eip1559')
return parseTransactionEIP1559(
serializedTransaction as TransactionSerializedEIP1559,
) as ParseTransactionReturnType<serialized>
if (type === 'eip2930')
return parseTransactionEIP2930(
serializedTransaction as TransactionSerializedEIP2930,
) as ParseTransactionReturnType<serialized>
if (type === 'eip4844')
return parseTransactionEIP4844(
serializedTransaction as TransactionSerializedEIP4844,
) as ParseTransactionReturnType<serialized>
if (type === 'eip7702')
return parseTransactionEIP7702(
serializedTransaction as TransactionSerializedEIP7702,
) as ParseTransactionReturnType<serialized>
return parseTransactionLegacy(
serializedTransaction,
) as ParseTransactionReturnType<serialized>
}
type ParseTransactionEIP7702ErrorType =
| ToTransactionArrayErrorType
| AssertTransactionEIP7702ErrorType
| ToTransactionArrayErrorType
| HexToBigIntErrorType
| HexToNumberErrorType
| InvalidLegacyVErrorType
| InvalidSerializedTransactionErrorType
| IsHexErrorType
| ParseAuthorizationListErrorType
| ParseEIP155SignatureErrorType
| ErrorType
function parseTransactionEIP7702(
serializedTransaction: TransactionSerializedEIP7702,
): TransactionSerializableEIP7702 {
const transactionArray = toTransactionArray(serializedTransaction)
const [
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gas,
to,
value,
data,
accessList,
authorizationList,
v,
r,
s,
] = transactionArray
if (transactionArray.length !== 10 && transactionArray.length !== 13)
throw new InvalidSerializedTransactionError({
attributes: {
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gas,
to,
value,
data,
accessList,
authorizationList,
...(transactionArray.length > 9
? {
v,
r,
s,
}
: {}),
},
serializedTransaction,
type: 'eip7702',
})
const transaction = {
chainId: hexToNumber(chainId as Hex),
type: 'eip7702',
} as TransactionSerializableEIP7702
if (isHex(to) && to !== '0x') transaction.to = to
if (isHex(gas) && gas !== '0x') transaction.gas = hexToBigInt(gas)
if (isHex(data) && data !== '0x') transaction.data = data
if (isHex(nonce)) transaction.nonce = nonce === '0x' ? 0 : hexToNumber(nonce)
if (isHex(value) && value !== '0x') transaction.value = hexToBigInt(value)
if (isHex(maxFeePerGas) && maxFeePerGas !== '0x')
transaction.maxFeePerGas = hexToBigInt(maxFeePerGas)
if (isHex(maxPriorityFeePerGas) && maxPriorityFeePerGas !== '0x')
transaction.maxPriorityFeePerGas = hexToBigInt(maxPriorityFeePerGas)
if (accessList.length !== 0 && accessList !== '0x')
transaction.accessList = parseAccessList(accessList as RecursiveArray<Hex>)
if (authorizationList.length !== 0 && authorizationList !== '0x')
transaction.authorizationList = parseAuthorizationList(
authorizationList as SerializedAuthorizationList,
)
assertTransactionEIP7702(transaction)
const signature =
transactionArray.length === 13
? parseEIP155Signature(transactionArray as RecursiveArray<Hex>)
: undefined
return { ...signature, ...transaction }
}
type ParseTransactionEIP4844ErrorType =
| ToTransactionArrayErrorType
| AssertTransactionEIP4844ErrorType
| ToTransactionArrayErrorType
| HexToBigIntErrorType
| HexToNumberErrorType
| InvalidLegacyVErrorType
| InvalidSerializedTransactionErrorType
| IsHexErrorType
| ParseEIP155SignatureErrorType
| ErrorType
function parseTransactionEIP4844(
serializedTransaction: TransactionSerializedEIP4844,
): TransactionSerializableEIP4844 {
const transactionOrWrapperArray = toTransactionArray(serializedTransaction)
const hasNetworkWrapper = transactionOrWrapperArray.length === 4
const transactionArray = hasNetworkWrapper
? transactionOrWrapperArray[0]
: transactionOrWrapperArray
const wrapperArray = hasNetworkWrapper
? transactionOrWrapperArray.slice(1)
: []
const [
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gas,
to,
value,
data,
accessList,
maxFeePerBlobGas,
blobVersionedHashes,
v,
r,
s,
] = transactionArray
const [blobs, commitments, proofs] = wrapperArray
if (!(transactionArray.length === 11 || transactionArray.length === 14))
throw new InvalidSerializedTransactionError({
attributes: {
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gas,
to,
value,
data,
accessList,
...(transactionArray.length > 9
? {
v,
r,
s,
}
: {}),
},
serializedTransaction,
type: 'eip4844',
})
const transaction = {
blobVersionedHashes: blobVersionedHashes as Hex[],
chainId: hexToNumber(chainId as Hex),
to,
type: 'eip4844',
} as TransactionSerializableEIP4844
if (isHex(gas) && gas !== '0x') transaction.gas = hexToBigInt(gas)
if (isHex(data) && data !== '0x') transaction.data = data
if (isHex(nonce)) transaction.nonce = nonce === '0x' ? 0 : hexToNumber(nonce)
if (isHex(value) && value !== '0x') transaction.value = hexToBigInt(value)
if (isHex(maxFeePerBlobGas) && maxFeePerBlobGas !== '0x')
transaction.maxFeePerBlobGas = hexToBigInt(maxFeePerBlobGas)
if (isHex(maxFeePerGas) && maxFeePerGas !== '0x')
transaction.maxFeePerGas = hexToBigInt(maxFeePerGas)
if (isHex(maxPriorityFeePerGas) && maxPriorityFeePerGas !== '0x')
transaction.maxPriorityFeePerGas = hexToBigInt(maxPriorityFeePerGas)
if (accessList.length !== 0 && accessList !== '0x')
transaction.accessList = parseAccessList(accessList as RecursiveArray<Hex>)
if (blobs && commitments && proofs)
transaction.sidecars = toBlobSidecars({
blobs: blobs as Hex[],
commitments: commitments as Hex[],
proofs: proofs as Hex[],
})
assertTransactionEIP4844(transaction)
const signature =
transactionArray.length === 14
? parseEIP155Signature(transactionArray as RecursiveArray<Hex>)
: undefined
return { ...signature, ...transaction }
}
type ParseTransactionEIP1559ErrorType =
| ToTransactionArrayErrorType
| AssertTransactionEIP1559ErrorType
| ToTransactionArrayErrorType
| HexToBigIntErrorType
| HexToNumberErrorType
| InvalidLegacyVErrorType
| InvalidSerializedTransactionErrorType
| IsHexErrorType
| ParseEIP155SignatureErrorType
| ParseAccessListErrorType
| ErrorType
function parseTransactionEIP1559(
serializedTransaction: TransactionSerializedEIP1559,
): TransactionSerializableEIP1559 {
const transactionArray = toTransactionArray(serializedTransaction)
const [
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gas,
to,
value,
data,
accessList,
v,
r,
s,
] = transactionArray
if (!(transactionArray.length === 9 || transactionArray.length === 12))
throw new InvalidSerializedTransactionError({
attributes: {
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gas,
to,
value,
data,
accessList,
...(transactionArray.length > 9
? {
v,
r,
s,
}
: {}),
},
serializedTransaction,
type: 'eip1559',
})
const transaction: TransactionSerializableEIP1559 = {
chainId: hexToNumber(chainId as Hex),
type: 'eip1559',
}
if (isHex(to) && to !== '0x') transaction.to = to
if (isHex(gas) && gas !== '0x') transaction.gas = hexToBigInt(gas)
if (isHex(data) && data !== '0x') transaction.data = data
if (isHex(nonce)) transaction.nonce = nonce === '0x' ? 0 : hexToNumber(nonce)
if (isHex(value) && value !== '0x') transaction.value = hexToBigInt(value)
if (isHex(maxFeePerGas) && maxFeePerGas !== '0x')
transaction.maxFeePerGas = hexToBigInt(maxFeePerGas)
if (isHex(maxPriorityFeePerGas) && maxPriorityFeePerGas !== '0x')
transaction.maxPriorityFeePerGas = hexToBigInt(maxPriorityFeePerGas)
if (accessList.length !== 0 && accessList !== '0x')
transaction.accessList = parseAccessList(accessList as RecursiveArray<Hex>)
assertTransactionEIP1559(transaction)
const signature =
transactionArray.length === 12
? parseEIP155Signature(transactionArray)
: undefined
return { ...signature, ...transaction }
}
type ParseTransactionEIP2930ErrorType =
| ToTransactionArrayErrorType
| AssertTransactionEIP2930ErrorType
| ToTransactionArrayErrorType
| HexToBigIntErrorType
| HexToNumberErrorType
| InvalidLegacyVErrorType
| InvalidSerializedTransactionErrorType
| IsHexErrorType
| ParseEIP155SignatureErrorType
| ParseAccessListErrorType
| ErrorType
function parseTransactionEIP2930(
serializedTransaction: TransactionSerializedEIP2930,
): Omit<TransactionRequestEIP2930, 'from'> &
({ chainId: number } | ({ chainId: number } & Signature)) {
const transactionArray = toTransactionArray(serializedTransaction)
const [chainId, nonce, gasPrice, gas, to, value, data, accessList, v, r, s] =
transactionArray
if (!(transactionArray.length === 8 || transactionArray.length === 11))
throw new InvalidSerializedTransactionError({
attributes: {
chainId,
nonce,
gasPrice,
gas,
to,
value,
data,
accessList,
...(transactionArray.length > 8
? {
v,
r,
s,
}
: {}),
},
serializedTransaction,
type: 'eip2930',
})
const transaction: TransactionSerializableEIP2930 = {
chainId: hexToNumber(chainId as Hex),
type: 'eip2930',
}
if (isHex(to) && to !== '0x') transaction.to = to
if (isHex(gas) && gas !== '0x') transaction.gas = hexToBigInt(gas)
if (isHex(data) && data !== '0x') transaction.data = data
if (isHex(nonce)) transaction.nonce = nonce === '0x' ? 0 : hexToNumber(nonce)
if (isHex(value) && value !== '0x') transaction.value = hexToBigInt(value)
if (isHex(gasPrice) && gasPrice !== '0x')
transaction.gasPrice = hexToBigInt(gasPrice)
if (accessList.length !== 0 && accessList !== '0x')
transaction.accessList = parseAccessList(accessList as RecursiveArray<Hex>)
assertTransactionEIP2930(transaction)
const signature =
transactionArray.length === 11
? parseEIP155Signature(transactionArray)
: undefined
return { ...signature, ...transaction }
}
type ParseTransactionLegacyErrorType =
| AssertTransactionLegacyErrorType
| FromRlpErrorType
| HexToBigIntErrorType
| HexToNumberErrorType
| InvalidLegacyVErrorType
| InvalidSerializedTransactionErrorType
| IsHexErrorType
| ErrorType
function parseTransactionLegacy(
serializedTransaction: Hex,
): Omit<TransactionRequestLegacy, 'from'> &
({ chainId?: number | undefined } | ({ chainId: number } & Signature)) {
const transactionArray = fromRlp(serializedTransaction, 'hex')
const [nonce, gasPrice, gas, to, value, data, chainIdOrV_, r, s] =
transactionArray
if (!(transactionArray.length === 6 || transactionArray.length === 9))
throw new InvalidSerializedTransactionError({
attributes: {
nonce,
gasPrice,
gas,
to,
value,
data,
...(transactionArray.length > 6
? {
v: chainIdOrV_,
r,
s,
}
: {}),
},
serializedTransaction,
type: 'legacy',
})
const transaction: TransactionSerializableLegacy = {
type: 'legacy',
}
if (isHex(to) && to !== '0x') transaction.to = to
if (isHex(gas) && gas !== '0x') transaction.gas = hexToBigInt(gas)
if (isHex(data) && data !== '0x') transaction.data = data
if (isHex(nonce)) transaction.nonce = nonce === '0x' ? 0 : hexToNumber(nonce)
if (isHex(value) && value !== '0x') transaction.value = hexToBigInt(value)
if (isHex(gasPrice) && gasPrice !== '0x')
transaction.gasPrice = hexToBigInt(gasPrice)
assertTransactionLegacy(transaction)
if (transactionArray.length === 6) return transaction
const chainIdOrV =
isHex(chainIdOrV_) && chainIdOrV_ !== '0x'
? hexToBigInt(chainIdOrV_ as Hex)
: 0n
if (s === '0x' && r === '0x') {
if (chainIdOrV > 0) transaction.chainId = Number(chainIdOrV)
return transaction
}
const v = chainIdOrV
const chainId: number | undefined = Number((v - 35n) / 2n)
if (chainId > 0) transaction.chainId = chainId
else if (v !== 27n && v !== 28n) throw new InvalidLegacyVError({ v })
transaction.v = v
transaction.s = s as Hex
transaction.r = r as Hex
transaction.yParity = v % 2n === 0n ? 1 : 0
return transaction
}
type ToTransactionArrayErrorType = FromRlpErrorType | ErrorType
export function toTransactionArray(serializedTransaction: string) {
return fromRlp(`0x${serializedTransaction.slice(4)}` as Hex, 'hex')
}
type ParseAccessListErrorType =
| InvalidAddressErrorType
| IsAddressErrorType
| ErrorType
export function parseAccessList(accessList_: RecursiveArray<Hex>): AccessList {
const accessList: Mutable<AccessList> = []
for (let i = 0; i < accessList_.length; i++) {
const [address, storageKeys] = accessList_[i] as [Hex, Hex[]]
if (!isAddress(address, { strict: false }))
throw new InvalidAddressError({ address })
accessList.push({
address: address,
storageKeys: storageKeys.map((key) => (isHash(key) ? key : trim(key))),
})
}
return accessList
}
type ParseAuthorizationListErrorType =
| HexToNumberErrorType
| ParseEIP155SignatureErrorType
| ErrorType
function parseAuthorizationList(
serializedAuthorizationList: SerializedAuthorizationList,
): SignedAuthorizationList {
const authorizationList: Mutable<SignedAuthorizationList> = []
for (let i = 0; i < serializedAuthorizationList.length; i++) {
const [chainId, address, nonce, yParity, r, s] =
serializedAuthorizationList[i]
authorizationList.push({
address,
chainId: chainId === '0x' ? 0 : hexToNumber(chainId),
nonce: nonce === '0x' ? 0 : hexToNumber(nonce),
...parseEIP155Signature([yParity, r, s]),
})
}
return authorizationList
}
type ParseEIP155SignatureErrorType =
| HexToBigIntErrorType
| PadHexErrorType
| ErrorType
function parseEIP155Signature(
transactionArray: RecursiveArray<Hex>,
): Signature & { yParity: number } {
const signature = transactionArray.slice(-3)
const v =
signature[0] === '0x' || hexToBigInt(signature[0] as Hex) === 0n ? 27n : 28n
return {
r: padHex(signature[1] as Hex, { size: 32 }),
s: padHex(signature[2] as Hex, { size: 32 }),
v,
yParity: v === 27n ? 0 : 1,
}
}

View File

@@ -0,0 +1,52 @@
import {
InvalidAddressError,
type InvalidAddressErrorType,
} from '../../errors/address.js'
import {
InvalidStorageKeySizeError,
type InvalidStorageKeySizeErrorType,
} from '../../errors/transaction.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Hex } from '../../types/misc.js'
import type { AccessList } from '../../types/transaction.js'
import { type IsAddressErrorType, isAddress } from '../address/isAddress.js'
import type { RecursiveArray } from '../encoding/toRlp.js'
export type SerializeAccessListErrorType =
| InvalidStorageKeySizeErrorType
| InvalidAddressErrorType
| IsAddressErrorType
| ErrorType
/*
* Serialize an EIP-2930 access list
* @remarks
* Use to create a transaction serializer with support for EIP-2930 access lists
*
* @param accessList - Array of objects of address and arrays of Storage Keys
* @throws InvalidAddressError, InvalidStorageKeySizeError
* @returns Array of hex strings
*/
export function serializeAccessList(
accessList?: AccessList | undefined,
): RecursiveArray<Hex> {
if (!accessList || accessList.length === 0) return []
const serializedAccessList = []
for (let i = 0; i < accessList.length; i++) {
const { address, storageKeys } = accessList[i]
for (let j = 0; j < storageKeys.length; j++) {
if (storageKeys[j].length - 2 !== 64) {
throw new InvalidStorageKeySizeError({ storageKey: storageKeys[j] })
}
}
if (!isAddress(address, { strict: false })) {
throw new InvalidAddressError({ address })
}
serializedAccessList.push([address, storageKeys])
}
return serializedAccessList
}

View File

@@ -0,0 +1,472 @@
import {
InvalidLegacyVError,
type InvalidLegacyVErrorType,
} from '../../errors/transaction.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
ByteArray,
Hex,
Signature,
SignatureLegacy,
} from '../../types/misc.js'
import type {
TransactionSerializable,
TransactionSerializableEIP1559,
TransactionSerializableEIP2930,
TransactionSerializableEIP4844,
TransactionSerializableEIP7702,
TransactionSerializableGeneric,
TransactionSerializableLegacy,
TransactionSerialized,
TransactionSerializedEIP1559,
TransactionSerializedEIP2930,
TransactionSerializedEIP4844,
TransactionSerializedEIP7702,
TransactionSerializedLegacy,
TransactionType,
} from '../../types/transaction.js'
import type { MaybePromise, OneOf } from '../../types/utils.js'
import {
type SerializeAuthorizationListErrorType,
serializeAuthorizationList,
} from '../authorization/serializeAuthorizationList.js'
import {
type BlobsToCommitmentsErrorType,
blobsToCommitments,
} from '../blob/blobsToCommitments.js'
import {
blobsToProofs,
type blobsToProofsErrorType,
} from '../blob/blobsToProofs.js'
import {
type CommitmentsToVersionedHashesErrorType,
commitmentsToVersionedHashes,
} from '../blob/commitmentsToVersionedHashes.js'
import {
type ToBlobSidecarsErrorType,
toBlobSidecars,
} from '../blob/toBlobSidecars.js'
import { type ConcatHexErrorType, concatHex } from '../data/concat.js'
import { trim } from '../data/trim.js'
import {
bytesToHex,
type NumberToHexErrorType,
numberToHex,
} from '../encoding/toHex.js'
import { type ToRlpErrorType, toRlp } from '../encoding/toRlp.js'
import {
type AssertTransactionEIP1559ErrorType,
type AssertTransactionEIP2930ErrorType,
type AssertTransactionEIP4844ErrorType,
type AssertTransactionEIP7702ErrorType,
type AssertTransactionLegacyErrorType,
assertTransactionEIP1559,
assertTransactionEIP2930,
assertTransactionEIP4844,
assertTransactionEIP7702,
assertTransactionLegacy,
} from './assertTransaction.js'
import {
type GetTransactionType,
type GetTransactionTypeErrorType,
getTransactionType,
} from './getTransactionType.js'
import {
type SerializeAccessListErrorType,
serializeAccessList,
} from './serializeAccessList.js'
export type SerializedTransactionReturnType<
transaction extends TransactionSerializable = TransactionSerializable,
///
_transactionType extends TransactionType = GetTransactionType<transaction>,
> = TransactionSerialized<_transactionType>
export type SerializeTransactionFn<
transaction extends TransactionSerializableGeneric = TransactionSerializable,
///
_transactionType extends TransactionType = never,
> = (
transaction: OneOf<TransactionSerializable | transaction>,
signature?: Signature | undefined,
) => MaybePromise<
SerializedTransactionReturnType<
OneOf<TransactionSerializable | transaction>,
_transactionType
>
>
export type SerializeTransactionErrorType =
| GetTransactionTypeErrorType
| SerializeTransactionEIP1559ErrorType
| SerializeTransactionEIP2930ErrorType
| SerializeTransactionEIP4844ErrorType
| SerializeTransactionEIP7702ErrorType
| SerializeTransactionLegacyErrorType
| ErrorType
export function serializeTransaction<
const transaction extends TransactionSerializable,
///
_transactionType extends TransactionType = GetTransactionType<transaction>,
>(
transaction: transaction,
signature?: Signature | undefined,
): SerializedTransactionReturnType<transaction, _transactionType> {
const type = getTransactionType(transaction) as GetTransactionType
if (type === 'eip1559')
return serializeTransactionEIP1559(
transaction as TransactionSerializableEIP1559,
signature,
) as SerializedTransactionReturnType<transaction>
if (type === 'eip2930')
return serializeTransactionEIP2930(
transaction as TransactionSerializableEIP2930,
signature,
) as SerializedTransactionReturnType<transaction>
if (type === 'eip4844')
return serializeTransactionEIP4844(
transaction as TransactionSerializableEIP4844,
signature,
) as SerializedTransactionReturnType<transaction>
if (type === 'eip7702')
return serializeTransactionEIP7702(
transaction as TransactionSerializableEIP7702,
signature,
) as SerializedTransactionReturnType<transaction>
return serializeTransactionLegacy(
transaction as TransactionSerializableLegacy,
signature as SignatureLegacy,
) as SerializedTransactionReturnType<transaction>
}
type SerializeTransactionEIP7702ErrorType =
| AssertTransactionEIP7702ErrorType
| SerializeAuthorizationListErrorType
| ConcatHexErrorType
| InvalidLegacyVErrorType
| NumberToHexErrorType
| ToRlpErrorType
| SerializeAccessListErrorType
| ErrorType
function serializeTransactionEIP7702(
transaction: TransactionSerializableEIP7702,
signature?: Signature | undefined,
): TransactionSerializedEIP7702 {
const {
authorizationList,
chainId,
gas,
nonce,
to,
value,
maxFeePerGas,
maxPriorityFeePerGas,
accessList,
data,
} = transaction
assertTransactionEIP7702(transaction)
const serializedAccessList = serializeAccessList(accessList)
const serializedAuthorizationList =
serializeAuthorizationList(authorizationList)
return concatHex([
'0x04',
toRlp([
numberToHex(chainId),
nonce ? numberToHex(nonce) : '0x',
maxPriorityFeePerGas ? numberToHex(maxPriorityFeePerGas) : '0x',
maxFeePerGas ? numberToHex(maxFeePerGas) : '0x',
gas ? numberToHex(gas) : '0x',
to ?? '0x',
value ? numberToHex(value) : '0x',
data ?? '0x',
serializedAccessList,
serializedAuthorizationList,
...toYParitySignatureArray(transaction, signature),
]),
]) as TransactionSerializedEIP7702
}
type SerializeTransactionEIP4844ErrorType =
| AssertTransactionEIP4844ErrorType
| BlobsToCommitmentsErrorType
| CommitmentsToVersionedHashesErrorType
| blobsToProofsErrorType
| ToBlobSidecarsErrorType
| ConcatHexErrorType
| InvalidLegacyVErrorType
| NumberToHexErrorType
| ToRlpErrorType
| SerializeAccessListErrorType
| ErrorType
function serializeTransactionEIP4844(
transaction: TransactionSerializableEIP4844,
signature?: Signature | undefined,
): TransactionSerializedEIP4844 {
const {
chainId,
gas,
nonce,
to,
value,
maxFeePerBlobGas,
maxFeePerGas,
maxPriorityFeePerGas,
accessList,
data,
} = transaction
assertTransactionEIP4844(transaction)
let blobVersionedHashes = transaction.blobVersionedHashes
let sidecars = transaction.sidecars
// If `blobs` are passed, we will need to compute the KZG commitments & proofs.
if (
transaction.blobs &&
(typeof blobVersionedHashes === 'undefined' ||
typeof sidecars === 'undefined')
) {
const blobs = (
typeof transaction.blobs[0] === 'string'
? transaction.blobs
: (transaction.blobs as ByteArray[]).map((x) => bytesToHex(x))
) as Hex[]
const kzg = transaction.kzg!
const commitments = blobsToCommitments({
blobs,
kzg,
})
if (typeof blobVersionedHashes === 'undefined')
blobVersionedHashes = commitmentsToVersionedHashes({
commitments,
})
if (typeof sidecars === 'undefined') {
const proofs = blobsToProofs({ blobs, commitments, kzg })
sidecars = toBlobSidecars({ blobs, commitments, proofs })
}
}
const serializedAccessList = serializeAccessList(accessList)
const serializedTransaction = [
numberToHex(chainId),
nonce ? numberToHex(nonce) : '0x',
maxPriorityFeePerGas ? numberToHex(maxPriorityFeePerGas) : '0x',
maxFeePerGas ? numberToHex(maxFeePerGas) : '0x',
gas ? numberToHex(gas) : '0x',
to ?? '0x',
value ? numberToHex(value) : '0x',
data ?? '0x',
serializedAccessList,
maxFeePerBlobGas ? numberToHex(maxFeePerBlobGas) : '0x',
blobVersionedHashes ?? [],
...toYParitySignatureArray(transaction, signature),
] as const
const blobs: Hex[] = []
const commitments: Hex[] = []
const proofs: Hex[] = []
if (sidecars)
for (let i = 0; i < sidecars.length; i++) {
const { blob, commitment, proof } = sidecars[i]
blobs.push(blob)
commitments.push(commitment)
proofs.push(proof)
}
return concatHex([
'0x03',
sidecars
? // If sidecars are enabled, envelope turns into a "wrapper":
toRlp([serializedTransaction, blobs, commitments, proofs])
: // If sidecars are disabled, standard envelope is used:
toRlp(serializedTransaction),
]) as TransactionSerializedEIP4844
}
type SerializeTransactionEIP1559ErrorType =
| AssertTransactionEIP1559ErrorType
| ConcatHexErrorType
| InvalidLegacyVErrorType
| NumberToHexErrorType
| ToRlpErrorType
| SerializeAccessListErrorType
| ErrorType
function serializeTransactionEIP1559(
transaction: TransactionSerializableEIP1559,
signature?: Signature | undefined,
): TransactionSerializedEIP1559 {
const {
chainId,
gas,
nonce,
to,
value,
maxFeePerGas,
maxPriorityFeePerGas,
accessList,
data,
} = transaction
assertTransactionEIP1559(transaction)
const serializedAccessList = serializeAccessList(accessList)
const serializedTransaction = [
numberToHex(chainId),
nonce ? numberToHex(nonce) : '0x',
maxPriorityFeePerGas ? numberToHex(maxPriorityFeePerGas) : '0x',
maxFeePerGas ? numberToHex(maxFeePerGas) : '0x',
gas ? numberToHex(gas) : '0x',
to ?? '0x',
value ? numberToHex(value) : '0x',
data ?? '0x',
serializedAccessList,
...toYParitySignatureArray(transaction, signature),
]
return concatHex([
'0x02',
toRlp(serializedTransaction),
]) as TransactionSerializedEIP1559
}
type SerializeTransactionEIP2930ErrorType =
| AssertTransactionEIP2930ErrorType
| ConcatHexErrorType
| InvalidLegacyVErrorType
| NumberToHexErrorType
| ToRlpErrorType
| SerializeAccessListErrorType
| ErrorType
function serializeTransactionEIP2930(
transaction: TransactionSerializableEIP2930,
signature?: Signature | undefined,
): TransactionSerializedEIP2930 {
const { chainId, gas, data, nonce, to, value, accessList, gasPrice } =
transaction
assertTransactionEIP2930(transaction)
const serializedAccessList = serializeAccessList(accessList)
const serializedTransaction = [
numberToHex(chainId),
nonce ? numberToHex(nonce) : '0x',
gasPrice ? numberToHex(gasPrice) : '0x',
gas ? numberToHex(gas) : '0x',
to ?? '0x',
value ? numberToHex(value) : '0x',
data ?? '0x',
serializedAccessList,
...toYParitySignatureArray(transaction, signature),
]
return concatHex([
'0x01',
toRlp(serializedTransaction),
]) as TransactionSerializedEIP2930
}
type SerializeTransactionLegacyErrorType =
| AssertTransactionLegacyErrorType
| InvalidLegacyVErrorType
| NumberToHexErrorType
| ToRlpErrorType
| ErrorType
function serializeTransactionLegacy(
transaction: TransactionSerializableLegacy,
signature?: SignatureLegacy | undefined,
): TransactionSerializedLegacy {
const { chainId = 0, gas, data, nonce, to, value, gasPrice } = transaction
assertTransactionLegacy(transaction)
let serializedTransaction = [
nonce ? numberToHex(nonce) : '0x',
gasPrice ? numberToHex(gasPrice) : '0x',
gas ? numberToHex(gas) : '0x',
to ?? '0x',
value ? numberToHex(value) : '0x',
data ?? '0x',
]
if (signature) {
const v = (() => {
// EIP-155 (inferred chainId)
if (signature.v >= 35n) {
const inferredChainId = (signature.v - 35n) / 2n
if (inferredChainId > 0) return signature.v
return 27n + (signature.v === 35n ? 0n : 1n)
}
// EIP-155 (explicit chainId)
if (chainId > 0)
return BigInt(chainId * 2) + BigInt(35n + signature.v - 27n)
// Pre-EIP-155 (no chainId)
const v = 27n + (signature.v === 27n ? 0n : 1n)
if (signature.v !== v) throw new InvalidLegacyVError({ v: signature.v })
return v
})()
const r = trim(signature.r)
const s = trim(signature.s)
serializedTransaction = [
...serializedTransaction,
numberToHex(v),
r === '0x00' ? '0x' : r,
s === '0x00' ? '0x' : s,
]
} else if (chainId > 0) {
serializedTransaction = [
...serializedTransaction,
numberToHex(chainId),
'0x',
'0x',
]
}
return toRlp(serializedTransaction) as TransactionSerializedLegacy
}
export function toYParitySignatureArray(
transaction: TransactionSerializableGeneric,
signature_?: Signature | undefined,
) {
const signature = signature_ ?? transaction
const { v, yParity } = signature
if (typeof signature.r === 'undefined') return []
if (typeof signature.s === 'undefined') return []
if (typeof v === 'undefined' && typeof yParity === 'undefined') return []
const r = trim(signature.r)
const s = trim(signature.s)
const yParity_ = (() => {
if (typeof yParity === 'number') return yParity ? numberToHex(1) : '0x'
if (v === 0n) return '0x'
if (v === 1n) return numberToHex(1)
return v === 27n ? '0x' : numberToHex(1)
})()
return [yParity_, r === '0x00' ? '0x' : r, s === '0x00' ? '0x' : s]
}