Skip to content

util: refactor 7702 authorization lists to util package #4032

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 26, 2025
27 changes: 16 additions & 11 deletions packages/tx/examples/setEOATx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,38 @@
// It will self-delegate. Note that in a 7702-tx, you are free to include any authorization item.
// If the authorization item is valid (it has the correct nonce, and matches the chainId (or the chainId is 0))
// then it will delegate the code of the account **who signed that authorization item** to the address in that authority item
import { createEOACode7702Tx } from '@ethereumjs/tx'
import type { EOACode7702AuthorizationListItemUnsigned } from '@ethereumjs/util'
import {
authorizationListBytesItemToJSON,
createEOACode7702Tx,
signAuthorization,
} from '@ethereumjs/tx'
import { Address, privateToAddress } from '@ethereumjs/util'
Address,
eoaCode7702AuthorizationListBytesItemToJSON,
eoaCode7702SignAuthorization,
privateToAddress,
} from '@ethereumjs/util'

const privateKey = new Uint8Array(32).fill(0x20)
const privateKeyOther = new Uint8Array(32).fill(0x99)

const myAddress = new Address(privateToAddress(privateKey))

const unsignedAuthorizationListItemSelf = {
const unsignedAuthorizationListItemSelf: EOACode7702AuthorizationListItemUnsigned = {
chainId: '0x1337', // This delegation will only work on the chain with chainId 0x1337
address: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
nonce: '0x01', // Since we are self-delegating we need account for the nonce being bumped of the account
}
const signedSelf = signAuthorization(unsignedAuthorizationListItemSelf, privateKey)
// To convert the bytes array to a human-readable form, use `authorizationListBytesItemToJSON`
console.log(authorizationListBytesItemToJSON(signedSelf))
const signedSelf = eoaCode7702SignAuthorization(unsignedAuthorizationListItemSelf, privateKey)
// To convert the bytes array to a human-readable form, use `eoaCode7702AuthorizationListBytesItemToJSON`
console.log(eoaCode7702AuthorizationListBytesItemToJSON(signedSelf))

const unsignedAuthorizationListItemOther = {
const unsignedAuthorizationListItemOther: EOACode7702AuthorizationListItemUnsigned = {
chainId: '0x', // The chainId 0 is special: this authorization will work on any chain which supports EIP-7702
address: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
nonce: '0x',
}
const signedOther = signAuthorization(unsignedAuthorizationListItemOther, privateKeyOther)
const signedOther = eoaCode7702SignAuthorization(
unsignedAuthorizationListItemOther,
privateKeyOther,
)

const authorizationList = [signedSelf, signedOther]

Expand Down
21 changes: 10 additions & 11 deletions packages/tx/src/7702/tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ import {
bigIntToHex,
bigIntToUnpaddedBytes,
bytesToBigInt,
eoaCode7702AuthorizationListBytesItemToJSON,
eoaCode7702AuthorizationListJSONItemToBytes,
isEOACode7702AuthorizationList,
toBytes,
} from '@ethereumjs/util'
import type { Address, EOACode7702AuthorizationListBytes } from '@ethereumjs/util'

import * as EIP1559 from '../capabilities/eip1559.ts'
import * as EIP2718 from '../capabilities/eip2718.ts'
import * as EIP2930 from '../capabilities/eip2930.ts'
import * as EIP7702 from '../capabilities/eip7702.ts'
import * as Legacy from '../capabilities/legacy.ts'
import { TransactionType, isAccessList, isAuthorizationList } from '../types.ts'
import { TransactionType, isAccessList } from '../types.ts'
import {
getBaseJSON,
sharedConstructor,
Expand All @@ -24,23 +28,18 @@ import {
import { createEOACode7702Tx } from './constructors.ts'

import type { Common } from '@ethereumjs/common'
import type { Address } from '@ethereumjs/util'

import type {
AccessListBytes,
TxData as AllTypesTxData,
TxValuesArray as AllTypesTxValuesArray,
AuthorizationListBytes,
Capability,
JSONTx,
TransactionCache,
TransactionInterface,
TxOptions,
} from '../types.ts'
import { accessListBytesToJSON, accessListJSONToBytes } from '../util/access.ts'
import {
authorizationListBytesItemToJSON,
authorizationListJSONItemToBytes,
} from '../util/authorization.ts'

export type TxData = AllTypesTxData[typeof TransactionType.EOACodeEIP7702]
export type TxValuesArray = AllTypesTxValuesArray[typeof TransactionType.EOACodeEIP7702]
Expand All @@ -61,7 +60,7 @@ export class EOACode7702Tx implements TransactionInterface<typeof TransactionTyp
public readonly data!: Uint8Array
public readonly to?: Address
public readonly accessList: AccessListBytes
public readonly authorizationList: AuthorizationListBytes
public readonly authorizationList: EOACode7702AuthorizationListBytes
public readonly chainId: bigint
public readonly maxPriorityFeePerGas: bigint
public readonly maxFeePerGas: bigint
Expand Down Expand Up @@ -123,8 +122,8 @@ export class EOACode7702Tx implements TransactionInterface<typeof TransactionTyp
EIP2930.verifyAccessList(this)

// Populate the authority list fields
this.authorizationList = isAuthorizationList(authorizationList)
? authorizationList.map((item) => authorizationListJSONItemToBytes(item))
this.authorizationList = isEOACode7702AuthorizationList(authorizationList)
? authorizationList.map((item) => eoaCode7702AuthorizationListJSONItemToBytes(item))
: authorizationList
// Verify the authority list format.
EIP7702.verifyAuthorizationList(this)
Expand Down Expand Up @@ -359,7 +358,7 @@ export class EOACode7702Tx implements TransactionInterface<typeof TransactionTyp
toJSON(): JSONTx {
const accessListJSON = accessListBytesToJSON(this.accessList)
const authorizationList = this.authorizationList.map((item) =>
authorizationListBytesItemToJSON(item),
eoaCode7702AuthorizationListBytesItemToJSON(item),
)

const baseJSON = getBaseJSON(this)
Expand Down
6 changes: 0 additions & 6 deletions packages/tx/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
/** EIP-4844 constants */

import { hexToBytes } from '@ethereumjs/util'

export const MAX_CALLDATA_SIZE = 16777216 // 2 ** 24
export const MAX_ACCESS_LIST_SIZE = 16777216 // 2 ** 24
export const MAX_VERSIONED_HASHES_LIST_SIZE = 16777216 // 2 ** 24
export const MAX_TX_WRAP_KZG_COMMITMENTS = 16777216 // 2 ** 24
export const FIELD_ELEMENTS_PER_BLOB = 4096 // This is also in the Common 4844 parameters but needed here since types can't access Common params
export const BYTES_PER_FIELD_ELEMENT = 32

/** EIP-7702 constants */

export const AUTHORITY_SIGNING_MAGIC = hexToBytes('0x05')
60 changes: 7 additions & 53 deletions packages/tx/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type {
AddressLike,
BigIntLike,
BytesLike,
EOACode7702AuthorizationList,
EOACode7702AuthorizationListBytes,
PrefixedHexString,
} from '@ethereumjs/util'
import type { FeeMarket1559Tx } from './1559/tx.ts'
Expand Down Expand Up @@ -118,25 +120,6 @@ export function isAccessList(input: AccessListBytes | AccessList): input is Acce
return !isAccessListBytes(input) // This is exactly the same method, except the output is negated.
}

export function isAuthorizationListBytes(
input: AuthorizationListBytes | AuthorizationList,
): input is AuthorizationListBytes {
if (input.length === 0) {
return true
}
const firstItem = input[0]
if (Array.isArray(firstItem)) {
return true
}
return false
}

export function isAuthorizationList(
input: AuthorizationListBytes | AuthorizationList,
): input is AuthorizationList {
return !isAuthorizationListBytes(input) // This is exactly the same method, except the output is negated.
}

export interface TransactionCache {
hash?: Uint8Array
dataFee?: {
Expand All @@ -147,7 +130,7 @@ export interface TransactionCache {
// TODO: re-add these cache items for the JSON
// See: https://github.com/ethereumjs/ethereumjs-monorepo/issues/3932
//accessListJSON?: AccessList
//authorityListJSON?: AuthorizationList
//authorityListJSON?: EOACode7702AuthorizationList
}

export type TransactionType = (typeof TransactionType)[keyof typeof TransactionType]
Expand Down Expand Up @@ -266,7 +249,7 @@ export interface EIP4844CompatibleTx<T extends TransactionType = TransactionType
export interface EIP7702CompatibleTx<T extends TransactionType = TransactionType>
extends EIP1559CompatibleTx<T> {
// ChainID, Address, [nonce], y_parity, r, s
readonly authorizationList: AuthorizationListBytes
readonly authorizationList: EOACode7702AuthorizationListBytes
}

export interface TxData {
Expand Down Expand Up @@ -428,7 +411,7 @@ export interface BlobEIP4844TxData extends FeeMarketEIP1559TxData {
* {@link EOACode7702Tx} data.
*/
export interface EOACode7702TxData extends FeeMarketEIP1559TxData {
authorizationList?: AuthorizationListBytes | AuthorizationList | never
authorizationList?: EOACode7702AuthorizationListBytes | EOACode7702AuthorizationList | never
}

export interface TxValuesArray {
Expand Down Expand Up @@ -492,7 +475,7 @@ type EOACode7702TxValuesArray = [
Uint8Array,
Uint8Array,
AccessListBytes,
AuthorizationListBytes,
EOACode7702AuthorizationListBytes,
Uint8Array?,
Uint8Array?,
Uint8Array?,
Expand Down Expand Up @@ -547,7 +530,7 @@ export interface JSONTx {
value?: PrefixedHexString
chainId?: PrefixedHexString
accessList?: JSONAccessListItem[] // TODO should this not be AccessList?
authorizationList?: AuthorizationList
authorizationList?: EOACode7702AuthorizationList
type?: PrefixedHexString
maxPriorityFeePerGas?: PrefixedHexString
maxFeePerGas?: PrefixedHexString
Expand Down Expand Up @@ -605,32 +588,3 @@ export type AccessListItem = {
export type AccessListBytesItem = [Uint8Array, Uint8Array[]]
export type AccessListBytes = AccessListBytesItem[]
export type AccessList = AccessListItem[]

/**
* Authorization list types
*/
export type AuthorizationListItemUnsigned = {
chainId: PrefixedHexString
address: PrefixedHexString
nonce: PrefixedHexString
}

export type AuthorizationListItem = {
yParity: PrefixedHexString
r: PrefixedHexString
s: PrefixedHexString
} & AuthorizationListItemUnsigned

// Tuple of [chain_id, address, [nonce], y_parity, r, s]
export type AuthorizationListBytesItem = [
Uint8Array,
Uint8Array,
Uint8Array,
Uint8Array,
Uint8Array,
Uint8Array,
]
export type AuthorizationListBytes = AuthorizationListBytesItem[]
export type AuthorizationList = AuthorizationListItem[]

export type AuthorizationListBytesItemUnsigned = [Uint8Array, Uint8Array, Uint8Array]
1 change: 0 additions & 1 deletion packages/tx/src/util/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// Do not add `./internal.ts`, this will export the internal helpers also at package level
export * from './general.ts'
export * from './access.ts'
export * from './authorization.ts'
9 changes: 4 additions & 5 deletions packages/tx/test/eip7702.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ import { assert, describe, it } from 'vitest'

import { createEOACode7702Tx } from '../src/index.ts'

import type { PrefixedHexString } from '@ethereumjs/util'
import type { EOACode7702AuthorizationListItem, PrefixedHexString } from '@ethereumjs/util'
import type { TxData } from '../src/7702/tx.ts'
import type { AuthorizationListItem } from '../src/index.ts'

const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun, eips: [7702] })

Expand All @@ -24,8 +23,8 @@ const addr = createAddressFromPrivateKey(pkey)

const ones32: PrefixedHexString = `0x${'01'.repeat(32)}`

function getTxData(override: Partial<AuthorizationListItem> = {}): TxData {
const validAuthorizationList: AuthorizationListItem = {
function getTxData(override: Partial<EOACode7702AuthorizationListItem> = {}): TxData {
const validAuthorizationList: EOACode7702AuthorizationListItem = {
chainId: '0x',
address: `0x${'20'.repeat(20)}`,
nonce: '0x1',
Expand Down Expand Up @@ -78,7 +77,7 @@ describe('[EOACode7702Transaction]', () => {
})

it('valid and invalid authorizationList values', () => {
const tests: [Partial<AuthorizationListItem>, string][] = [
const tests: [Partial<EOACode7702AuthorizationListItem>, string][] = [
[
{
address: `0x${'20'.repeat(21)}`,
Expand Down
Loading
Loading