Skip to content
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

feat(public): add createAccessList #2855

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

157 changes: 157 additions & 0 deletions src/actions/public/createAccessList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import type { Address } from 'abitype'

import type { Account } from '../../accounts/types.js'
import {
type ParseAccountErrorType,
parseAccount,
} from '../../accounts/utils/parseAccount.js'
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { BlockTag } from '../../types/block.js'
import type { Chain } from '../../types/chain.js'
import type { RpcTransactionRequest } from '../../types/rpc.js'
import type { AccessList, TransactionRequest } from '../../types/transaction.js'
import type { ExactPartial, UnionOmit } from '../../types/utils.js'
import type { RequestErrorType } from '../../utils/buildRequest.js'
import {
type NumberToHexErrorType,
numberToHex,
} from '../../utils/encoding/toHex.js'
import { getCallError } from '../../utils/errors/getCallError.js'
import type { GetCreateAccessListErrorReturnType } from '../../utils/errors/getCreateAccessList.js'
import { extract } from '../../utils/formatters/extract.js'
import {
type FormatTransactionRequestErrorType,
type FormattedTransactionRequest,
formatTransactionRequest,
} from '../../utils/formatters/transactionRequest.js'
import { assertRequest } from '../../utils/transaction/assertRequest.js'
import type {
AssertRequestErrorType,
AssertRequestParameters,
} from '../../utils/transaction/assertRequest.js'

export type CreateAccessListParameters<
chain extends Chain | undefined = Chain | undefined,
> = UnionOmit<
FormattedTransactionRequest<chain>,
'from' | 'nonce' | 'accessList'
> & {
/** Account attached to the call (msg.sender). */
account?: Account | Address | undefined
} & (
| {
/** The balance of the account at a block number. */
blockNumber?: bigint | undefined
blockTag?: undefined
}
| {
blockNumber?: undefined
/**
* The balance of the account at a block tag.
* @default 'latest'
*/
blockTag?: BlockTag | undefined
}
)

export type CreateAccessListReturnType = {
accessList: AccessList
gasUsed: bigint
}

export type CreateAccessListErrorType = GetCreateAccessListErrorReturnType<
| ParseAccountErrorType
| AssertRequestErrorType
| NumberToHexErrorType
| FormatTransactionRequestErrorType
| RequestErrorType
>

/**
* Creates an EIP-2930 access list that you can include in a transaction.
*
* - Docs: https://viem.sh/docs/actions/public/call
* - JSON-RPC Methods: [`eth_createAccessList`](https://docs.infura.io/api/networks/ethereum/json-rpc-methods/eth_createaccesslist)
*
* @param client - Client to use
* @param parameters - {@link CreateAccessListParameters}
* @returns The access list. {@link CreateAccessListReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { createAccessList } from 'viem/public'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const data = await createAccessList(client, {
* account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
* data: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* })
*/
export async function createAccessList<chain extends Chain | undefined>(
client: Client<Transport, chain>,
args: CreateAccessListParameters<chain>,
): Promise<CreateAccessListReturnType> {
const {
account: account_ = client.account,
blockNumber,
blockTag = 'latest',
blobs,
data,
gas,
gasPrice,
maxFeePerBlobGas,
maxFeePerGas,
maxPriorityFeePerGas,
to,
value,
...rest
} = args
const account = account_ ? parseAccount(account_) : undefined

try {
assertRequest(args as AssertRequestParameters)

const blockNumberHex = blockNumber ? numberToHex(blockNumber) : undefined
const block = blockNumberHex || blockTag

const chainFormat = client.chain?.formatters?.transactionRequest?.format
const format = chainFormat || formatTransactionRequest

const request = format({
// Pick out extra data that might exist on the chain's transaction request type.
...extract(rest, { format: chainFormat }),
from: account?.address,
blobs,
data,
gas,
gasPrice,
maxFeePerBlobGas,
maxFeePerGas,
maxPriorityFeePerGas,
to,
value,
} as TransactionRequest) as TransactionRequest

const response = await client.request({
method: 'eth_createAccessList',
params: [request as ExactPartial<RpcTransactionRequest>, block],
})
return {
accessList: response.accessList,
gasUsed: BigInt(response.gasUsed),
}
} catch (err) {
throw getCallError(err as ErrorType, {
...args,
account,
chain: client.chain,
})
}
}
32 changes: 32 additions & 0 deletions src/clients/decorators/public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ import {
type CallReturnType,
call,
} from '../../actions/public/call.js'
import {
type CreateAccessListParameters,
type CreateAccessListReturnType,
createAccessList,
} from '../../actions/public/createAccessList.js'
import {
type CreateBlockFilterReturnType,
createBlockFilter,
Expand Down Expand Up @@ -285,6 +290,32 @@ export type PublicActions<
* })
*/
call: (parameters: CallParameters<chain>) => Promise<CallReturnType>
/**
* Creates an EIP-2930 access list that you can include in a transaction.
*
* - Docs: https://viem.sh/docs/actions/public/createAccessList
* - JSON-RPC Methods: [`eth_createAccessList`](https://docs.infura.io/api/networks/ethereum/json-rpc-methods/eth_createaccesslist)
*
* @param args - {@link CreateAccessListParameters}
* @returns The call data. {@link CreateAccessListReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const data = await client.createAccessList({
* account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
* data: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* })
*/
createAccessList: (
parameters: CreateAccessListParameters<chain>,
) => Promise<CreateAccessListReturnType>
/**
* Creates a Filter to listen for new block hashes that can be used with [`getFilterChanges`](https://viem.sh/docs/actions/public/getFilterChanges).
*
Expand Down Expand Up @@ -1833,6 +1864,7 @@ export function publicActions<
): PublicActions<transport, chain, account> {
return {
call: (args) => call(client, args),
createAccessList: (args) => createAccessList(client, args),
createBlockFilter: () => createBlockFilter(client),
createContractEventFilter: (args) =>
createContractEventFilter(client, args),
Expand Down
21 changes: 21 additions & 0 deletions src/types/eip1193.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {
RpcTransactionRequest as TransactionRequest,
RpcUncle as Uncle,
} from './rpc.js'
import type { AccessList } from './transaction.js'
import type { ExactPartial, OneOf, PartialBy, Prettify } from './utils.js'

//////////////////////////////////////////////////
Expand Down Expand Up @@ -644,6 +645,26 @@ export type PublicRpcSchema = [
]
ReturnType: Hex
},
/**
* @description Executes a new message call immediately without submitting a transaction to the network
*
* @example
* provider.request({ method: 'eth_call', params: [{ to: '0x...', data: '0x...' }] })
* // => '0x...'
*/
{
Method: 'eth_createAccessList'
Parameters:
| [transaction: ExactPartial<TransactionRequest>]
| [
transaction: ExactPartial<TransactionRequest>,
block: BlockNumber | BlockTag | BlockIdentifier,
]
ReturnType: {
accessList: AccessList
gasUsed: Quantity
}
},
/**
* @description Returns the chain ID associated with the current network
* @example
Expand Down
46 changes: 46 additions & 0 deletions src/utils/errors/getCreateAccessList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { CreateAccessListParameters } from '../../actions/public/createAccessList.js'
import type { BaseError } from '../../errors/base.js'
import {
CallExecutionError,
type CallExecutionErrorType,
} from '../../errors/contract.js'
import { UnknownNodeError } from '../../errors/node.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Chain } from '../../types/chain.js'

import {
type GetNodeErrorParameters,
type GetNodeErrorReturnType,
getNodeError,
} from './getNodeError.js'

export type GetCreateAccessListErrorReturnType<cause = ErrorType> = Omit<
CallExecutionErrorType,
'cause'
> & {
cause: cause | GetNodeErrorReturnType
}

export function getCreateAccessListError<err extends ErrorType<string>>(
err: err,
{
docsPath,
...args
}: CreateAccessListParameters & {
chain?: Chain | undefined
docsPath?: string | undefined
},
): GetCreateAccessListErrorReturnType<err> {
const cause = (() => {
const cause = getNodeError(
err as {} as BaseError,
args as GetNodeErrorParameters,
)
if (cause instanceof UnknownNodeError) return err as {} as BaseError
return cause
})()
return new CallExecutionError(cause, {
docsPath,
...args,
}) as GetCreateAccessListErrorReturnType<err>
}