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

View File

@@ -0,0 +1,156 @@
import type { Address } from 'abitype'
import {
type ParseAccountErrorType,
parseAccount,
} from '../../accounts/utils/parseAccount.js'
import {
type PrepareTransactionRequestErrorType,
type PrepareTransactionRequestParameters,
prepareTransactionRequest,
} from '../../actions/wallet/prepareTransactionRequest.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 {
Account,
DeriveAccount,
GetAccountParameter,
} from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { Hex } from '../../types/misc.js'
import type { Prettify, UnionOmit } from '../../types/utils.js'
import type { DepositTransactionParameters } from './depositTransaction.js'
export type BuildDepositTransactionParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
accountOverride extends Account | Address | undefined =
| Account
| Address
| undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetAccountParameter<account, accountOverride, false> &
GetChainParameter<chain, chainOverride> & {
/** Gas limit for transaction execution on the L2. */
gas?: bigint | undefined
/** Value in wei to mint (deposit) on the L2. Debited from the caller's L1 balance. */
mint?: bigint | undefined
/** Value in wei sent with this transaction on the L2. Debited from the caller's L2 balance. */
value?: bigint | undefined
} & (
| {
/** Encoded contract method & arguments. */
data?: Hex | undefined
/** Whether or not this is a contract deployment transaction. */
isCreation?: false | undefined
/** L2 Transaction recipient. */
to?: Address | undefined
}
| {
/** Contract deployment bytecode. Required for contract deployment transactions. */
data: Hex
/** Whether or not this is a contract deployment transaction. */
isCreation: true
/** L2 Transaction recipient. Cannot exist for contract deployment transactions. */
to?: undefined
}
)
export type BuildDepositTransactionReturnType<
account extends Account | undefined = Account | undefined,
accountOverride extends Account | Address | undefined =
| Account
| Address
| undefined,
> = Prettify<
UnionOmit<DepositTransactionParameters<Chain, account, Chain>, 'account'> & {
account: DeriveAccount<account, accountOverride>
}
>
export type BuildDepositTransactionErrorType =
| ParseAccountErrorType
| PrepareTransactionRequestErrorType
| ErrorType
/**
* Prepares parameters for a [deposit transaction](https://github.com/ethereum-optimism/optimism/blob/develop/specs/deposits.md) to be initiated on an L1.
*
* - Docs: https://viem.sh/op-stack/actions/buildDepositTransaction
*
* @param client - Client to use
* @param parameters - {@link BuildDepositTransactionParameters}
* @returns Parameters for `depositTransaction`. {@link DepositTransactionReturnType}
*
* @example
* import { createWalletClient, http, parseEther } from 'viem'
* import { base } from 'viem/chains'
* import { publicActionsL2 } from 'viem/op-stack'
* import { buildDepositTransaction } from 'viem/wallet'
*
* const client = createWalletClient({
* chain: base,
* transport: http(),
* }).extend(publicActionsL2())
*
* const args = await buildDepositTransaction(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* })
*/
export async function buildDepositTransaction<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
accountOverride extends Account | Address | undefined = undefined,
>(
client: Client<Transport, chain, account>,
args: BuildDepositTransactionParameters<
chain,
account,
chainOverride,
accountOverride
>,
): Promise<BuildDepositTransactionReturnType<account, accountOverride>> {
const {
account: account_,
chain = client.chain,
gas,
data,
isCreation,
mint,
to,
value,
} = args
const account = account_ ? parseAccount(account_) : undefined
const request = await prepareTransactionRequest(client, {
account: mint ? undefined : account,
chain,
gas,
data,
parameters: ['gas'],
to,
value,
} as PrepareTransactionRequestParameters)
return {
account,
request: {
data: request.data,
gas: request.gas,
mint,
isCreation,
to: request.to,
value: request.value,
},
targetChain: chain,
} as unknown as BuildDepositTransactionReturnType<account, accountOverride>
}

View File

@@ -0,0 +1,118 @@
import type { Address } from 'abitype'
import {
type ParseAccountErrorType,
parseAccount,
} from '../../accounts/utils/parseAccount.js'
import {
type PrepareTransactionRequestErrorType,
type PrepareTransactionRequestParameters,
prepareTransactionRequest,
} from '../../actions/wallet/prepareTransactionRequest.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 { Account, GetAccountParameter } from '../../types/account.js'
import type { Chain, GetChainParameter } from '../../types/chain.js'
import type { Hex } from '../../types/misc.js'
import type { Prettify, UnionOmit } from '../../types/utils.js'
import type { InitiateWithdrawalParameters } from './initiateWithdrawal.js'
export type BuildInitiateWithdrawalParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
accountOverride extends Account | Address | undefined =
| Account
| Address
| undefined,
> = GetAccountParameter<account, accountOverride, false> &
GetChainParameter<chain, chainOverride> & {
/** Encoded contract method & arguments. */
data?: Hex | undefined
/** Gas limit for transaction execution on the L1. */
gas?: bigint | undefined
/** L1 Transaction recipient. */
to: Address
/** Value in wei to withdrawal to the L1. Debited from the caller's L2 balance. */
value?: bigint | undefined
}
export type BuildInitiateWithdrawalReturnType<
account extends Account | undefined = Account | undefined,
accountOverride extends Account | Address | undefined =
| Account
| Address
| undefined,
> = Prettify<
UnionOmit<InitiateWithdrawalParameters<Chain, account, Chain>, 'account'> &
GetAccountParameter<account, accountOverride>
>
export type BuildInitiateWithdrawalErrorType =
| ParseAccountErrorType
| PrepareTransactionRequestErrorType
| ErrorType
/**
* Prepares parameters for a [withdrawal](https://community.optimism.io/docs/protocol/withdrawal-flow/#withdrawal-initiating-transaction) from an L2 to the L1.
*
* - Docs: https://viem.sh/op-stack/actions/buildInitiateWithdrawal
*
* @param client - Client to use
* @param parameters - {@link BuildInitiateWithdrawalParameters}
* @returns Parameters for `depositTransaction`. {@link DepositTransactionReturnType}
*
* @example
* import { createPublicClient, http, parseEther } from 'viem'
* import { mainnet } from 'viem/chains'
* import { buildInitiateWithdrawal } from 'viem/wallet'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
*
* const args = await buildInitiateWithdrawal(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* })
*/
export async function buildInitiateWithdrawal<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
accountOverride extends Account | Address | undefined = undefined,
>(
client: Client<Transport, chain, account>,
args: BuildInitiateWithdrawalParameters<
chain,
account,
chainOverride,
accountOverride
>,
): Promise<BuildInitiateWithdrawalReturnType<account, accountOverride>> {
const { account: account_, chain = client.chain, gas, data, to, value } = args
const account = account_ ? parseAccount(account_) : undefined
const request = await prepareTransactionRequest(client, {
account: null,
chain,
gas,
data,
parameters: ['gas'],
to,
value,
} as PrepareTransactionRequestParameters)
return {
account,
request: {
data: request.data,
gas: request.gas,
to: request.to,
value: request.value,
},
} as BuildInitiateWithdrawalReturnType<account, accountOverride>
}

View File

@@ -0,0 +1,181 @@
import type { Address } from 'abitype'
import {
type GetBlockErrorType,
getBlock,
} from '../../actions/public/getBlock.js'
import {
type GetProofErrorType,
getProof,
} from '../../actions/public/getProof.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 {
Account,
DeriveAccount,
GetAccountParameter,
} from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { Hex } from '../../types/misc.js'
import type { OneOf, Prettify } from '../../types/utils.js'
import { fromRlp } from '../../utils/encoding/fromRlp.js'
import { toRlp } from '../../utils/encoding/toRlp.js'
import { keccak256 } from '../../utils/hash/keccak256.js'
import { contracts } from '../contracts.js'
import type { Withdrawal } from '../types/withdrawal.js'
import {
type GetWithdrawalHashStorageSlotErrorType,
getWithdrawalHashStorageSlot,
} from '../utils/getWithdrawalHashStorageSlot.js'
import type { GetGameReturnType } from './getGame.js'
import type { GetL2OutputReturnType } from './getL2Output.js'
import type { ProveWithdrawalParameters } from './proveWithdrawal.js'
const outputRootProofVersion =
'0x0000000000000000000000000000000000000000000000000000000000000000' as const
export type BuildProveWithdrawalParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
accountOverride extends Account | Address | undefined =
| Account
| Address
| undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetAccountParameter<account, accountOverride, false> &
GetChainParameter<chain, chainOverride> & {
withdrawal: Withdrawal
} & OneOf<{ output: GetL2OutputReturnType } | { game: GetGameReturnType }>
export type BuildProveWithdrawalReturnType<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
accountOverride extends Account | Address | undefined =
| Account
| Address
| undefined,
> = Prettify<
Pick<
ProveWithdrawalParameters,
'l2OutputIndex' | 'outputRootProof' | 'withdrawalProof' | 'withdrawal'
> & {
account: DeriveAccount<account, accountOverride>
targetChain: DeriveChain<chain, chainOverride>
}
>
export type BuildProveWithdrawalErrorType =
| GetBlockErrorType
| GetProofErrorType
| GetWithdrawalHashStorageSlotErrorType
| ErrorType
/**
* Builds the transaction that proves a withdrawal was initiated on an L2. Used in the Withdrawal flow.
*
* - Docs: https://viem.sh/op-stack/actions/buildProveWithdrawal
*
* @param client - Client to use
* @param parameters - {@link BuildProveWithdrawalParameters}
* @returns The prove withdraw transaction request. {@link BuildProveWithdrawalReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { optimism } from 'viem/chains'
* import { buildProveWithdrawal } from 'viem/op-stack'
*
* const publicClientL2 = createPublicClient({
* chain: optimism,
* transport: http(),
* })
*
* const args = await buildProveWithdrawal(publicClientL2, {
* output: { ... },
* withdrawal: { ... },
* })
*/
export async function buildProveWithdrawal<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
accountOverride extends Account | Address | undefined = undefined,
>(
client: Client<Transport, chain, account>,
args: BuildProveWithdrawalParameters<
chain,
account,
chainOverride,
accountOverride
>,
): Promise<
BuildProveWithdrawalReturnType<chain, account, chainOverride, accountOverride>
> {
const { account, chain = client.chain, game, output, withdrawal } = args
const { withdrawalHash } = withdrawal
const { l2BlockNumber } = game ?? output
const slot = getWithdrawalHashStorageSlot({ withdrawalHash })
const [proof, block] = await Promise.all([
getProof(client, {
address: contracts.l2ToL1MessagePasser.address,
storageKeys: [slot],
blockNumber: l2BlockNumber,
}),
getBlock(client, {
blockNumber: l2BlockNumber,
}),
])
return {
account,
l2OutputIndex: game?.index ?? output?.outputIndex,
outputRootProof: {
latestBlockhash: block.hash,
messagePasserStorageRoot: proof.storageHash,
stateRoot: block.stateRoot,
version: outputRootProofVersion,
},
targetChain: chain,
withdrawalProof: maybeAddProofNode(
keccak256(slot),
proof.storageProof[0].proof,
),
withdrawal,
} as unknown as BuildProveWithdrawalReturnType<
chain,
account,
chainOverride,
accountOverride
>
}
/** @internal */
export function maybeAddProofNode(key: string, proof: readonly Hex[]) {
const lastProofRlp = proof[proof.length - 1]
const lastProof = fromRlp(lastProofRlp)
if (lastProof.length !== 17) return proof
const modifiedProof = [...proof]
for (const item of lastProof) {
// Find any nodes located inside of the branch node.
if (!Array.isArray(item)) continue
// Check if the key inside the node matches the key we're looking for. We remove the first
// two characters (0x) and then we remove one more character (the first nibble) since this
// is the identifier for the type of node we're looking at. In this case we don't actually
// care what type of node it is because a branch node would only ever be the final proof
// element if (1) it includes the leaf node we're looking for or (2) it stores the value
// within itself. If (1) then this logic will work, if (2) then this won't find anything
// and we won't append any proof elements, which is exactly what we would want.
const suffix = item[0].slice(3)
if (typeof suffix !== 'string' || !key.endsWith(suffix)) continue
modifiedProof.push(toRlp(item))
}
return modifiedProof
}

View File

@@ -0,0 +1,165 @@
import type { Address } from 'abitype'
import {
type WriteContractErrorType,
type WriteContractParameters,
writeContract,
} from '../../actions/wallet/writeContract.js'
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import { zeroAddress } from '../../constants/address.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Account, GetAccountParameter } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { Hash } from '../../types/misc.js'
import type { UnionEvaluate, UnionOmit } from '../../types/utils.js'
import type { FormattedTransactionRequest } from '../../utils/formatters/transactionRequest.js'
import { portalAbi } from '../abis.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import type { DepositRequest } from '../types/deposit.js'
import type { EstimateDepositTransactionGasErrorType } from './estimateDepositTransactionGas.js'
export type DepositTransactionParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = UnionEvaluate<
UnionOmit<
FormattedTransactionRequest<_derivedChain>,
| 'accessList'
| 'data'
| 'from'
| 'gas'
| 'gasPrice'
| 'to'
| 'type'
| 'value'
>
> &
GetAccountParameter<account, Account | Address> &
GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<_derivedChain, 'portal'> & {
/** L2 transaction request. */
request: DepositRequest
/**
* Gas limit for transaction execution on the L1.
* `null` to skip gas estimation & defer calculation to signer.
*/
gas?: bigint | null | undefined
}
export type DepositTransactionReturnType = Hash
export type DepositTransactionErrorType =
| EstimateDepositTransactionGasErrorType
| WriteContractErrorType
| ErrorType
/**
* Initiates a [deposit transaction](https://github.com/ethereum-optimism/optimism/blob/develop/specs/deposits.md) on an L1, which executes a transaction on L2.
*
* Internally performs a contract write to the [`depositTransaction` function](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/OptimismPortal.sol#L378)
* on the [Optimism Portal contract](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/OptimismPortal.sol).
*
* - Docs: https://viem.sh/op-stack/actions/depositTransaction
*
* @param client - Client to use
* @param parameters - {@link DepositTransactionParameters}
* @returns The L1 transaction hash. {@link DepositTransactionReturnType}
*
* @example
* import { createWalletClient, custom, parseEther } from 'viem'
* import { base, mainnet } from 'viem/chains'
* import { depositTransaction } from 'viem/op-stack'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* })
*
* const hash = await depositTransaction(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* request: {
* gas: 21_000n,
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* targetChain: base,
* })
*
* @example
* // Account Hoisting
* import { createWalletClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { base, mainnet } from 'viem/chains'
* import { depositTransaction } from 'viem/op-stack'
*
* const client = createWalletClient({
* account: privateKeyToAccount('0x…'),
* chain: mainnet,
* transport: http(),
* })
*
* const hash = await depositTransaction(client, {
* request: {
* gas: 21_000n,
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* targetChain: base,
* })
*/
export async function depositTransaction<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: DepositTransactionParameters<chain, account, chainOverride>,
) {
const {
account,
chain = client.chain,
gas,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
request: {
data = '0x',
gas: l2Gas,
isCreation = false,
mint,
to = '0x',
value,
},
targetChain,
} = parameters
const portalAddress = (() => {
if (parameters.portalAddress) return parameters.portalAddress
if (chain) return targetChain!.contracts.portal[chain.id].address
return Object.values(targetChain!.contracts.portal)[0].address
})()
return writeContract(client, {
account: account!,
abi: portalAbi,
address: portalAddress,
chain,
functionName: 'depositTransaction',
args: [
isCreation ? zeroAddress : to,
value ?? mint ?? 0n,
l2Gas,
isCreation,
data,
],
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
value: mint,
gas: gas ?? undefined,
} satisfies WriteContractParameters as any)
}

View File

@@ -0,0 +1,142 @@
import type { Abi } 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 { BaseError } from '../../errors/base.js'
import type { Chain } from '../../types/chain.js'
import type {
ContractFunctionArgs,
ContractFunctionName,
ContractFunctionParameters,
GetValue,
} from '../../types/contract.js'
import type { UnionOmit } from '../../types/utils.js'
import {
type EncodeFunctionDataErrorType,
type EncodeFunctionDataParameters,
encodeFunctionData,
} from '../../utils/abi/encodeFunctionData.js'
import {
type GetContractErrorReturnType,
getContractError,
} from '../../utils/errors/getContractError.js'
import {
type EstimateL1FeeErrorType,
type EstimateL1FeeParameters,
estimateL1Fee,
} from './estimateL1Fee.js'
export type EstimateContractL1FeeParameters<
abi extends Abi | readonly unknown[] = Abi,
functionName extends ContractFunctionName<
abi,
'nonpayable' | 'payable'
> = ContractFunctionName<abi, 'nonpayable' | 'payable'>,
args extends ContractFunctionArgs<
abi,
'nonpayable' | 'payable',
functionName
> = ContractFunctionArgs<abi, 'nonpayable' | 'payable', functionName>,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = undefined,
chainOverride extends Chain | undefined = Chain | undefined,
> = ContractFunctionParameters<
abi,
'nonpayable' | 'payable',
functionName,
args
> &
UnionOmit<
EstimateL1FeeParameters<chain, account, chainOverride>,
'data' | 'to' | 'value'
> &
GetValue<
abi,
functionName,
EstimateL1FeeParameters<
chain,
account,
chainOverride
> extends EstimateL1FeeParameters
? EstimateL1FeeParameters<chain, account, chainOverride>['value']
: EstimateL1FeeParameters['value']
>
export type EstimateContractL1FeeReturnType = bigint
export type EstimateContractL1FeeErrorType = GetContractErrorReturnType<
EncodeFunctionDataErrorType | EstimateL1FeeErrorType | ParseAccountErrorType
>
/**
* Estimates the L1 data fee required to execute an L2 contract write.
*
* @param client - Client to use
* @param parameters - {@link EstimateContractL1FeeParameters}
* @returns The gas estimate (in wei). {@link EstimateContractL1FeeReturnType}
*
* @example
* import { createPublicClient, http, parseAbi } from 'viem'
* import { optimism } from 'viem/chains'
* import { estimateContractL1Fee } from 'viem/op-stack'
*
* const client = createPublicClient({
* chain: optimism,
* transport: http(),
* })
* const l1Fee = await estimateContractL1Fee(client, {
* address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
* abi: parseAbi(['function mint() public']),
* functionName: 'mint',
* account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
* })
*/
export async function estimateContractL1Fee<
const abi extends Abi | readonly unknown[],
functionName extends ContractFunctionName<abi, 'nonpayable' | 'payable'>,
args extends ContractFunctionArgs<abi, 'pure' | 'view', functionName>,
chain extends Chain | undefined,
account extends Account | undefined = undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: EstimateContractL1FeeParameters<
abi,
functionName,
args,
chain,
account,
chainOverride
>,
): Promise<EstimateContractL1FeeReturnType> {
const { abi, address, args, functionName, ...request } =
parameters as EstimateContractL1FeeParameters
const data = encodeFunctionData({
abi,
args,
functionName,
} as EncodeFunctionDataParameters)
try {
const fee = await estimateL1Fee(client, {
data,
to: address,
...request,
} as unknown as EstimateL1FeeParameters)
return fee
} catch (error) {
const account = request.account ? parseAccount(request.account) : undefined
throw getContractError(error as BaseError, {
abi,
address,
args,
docsPath: '/docs/chains/op-stack/estimateContractL1Fee',
functionName,
sender: account?.address,
})
}
}

View File

@@ -0,0 +1,142 @@
import type { Abi } 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 { BaseError } from '../../errors/base.js'
import type { Chain } from '../../types/chain.js'
import type {
ContractFunctionArgs,
ContractFunctionName,
ContractFunctionParameters,
GetValue,
} from '../../types/contract.js'
import type { UnionOmit } from '../../types/utils.js'
import {
type EncodeFunctionDataErrorType,
type EncodeFunctionDataParameters,
encodeFunctionData,
} from '../../utils/abi/encodeFunctionData.js'
import {
type GetContractErrorReturnType,
getContractError,
} from '../../utils/errors/getContractError.js'
import {
type EstimateL1GasErrorType,
type EstimateL1GasParameters,
estimateL1Gas,
} from './estimateL1Gas.js'
export type EstimateContractL1GasParameters<
abi extends Abi | readonly unknown[] = Abi,
functionName extends ContractFunctionName<
abi,
'nonpayable' | 'payable'
> = ContractFunctionName<abi, 'nonpayable' | 'payable'>,
args extends ContractFunctionArgs<
abi,
'nonpayable' | 'payable',
functionName
> = ContractFunctionArgs<abi, 'nonpayable' | 'payable', functionName>,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = undefined,
chainOverride extends Chain | undefined = Chain | undefined,
> = ContractFunctionParameters<
abi,
'nonpayable' | 'payable',
functionName,
args
> &
UnionOmit<
EstimateL1GasParameters<chain, account, chainOverride>,
'data' | 'to' | 'value'
> &
GetValue<
abi,
functionName,
EstimateL1GasParameters<
chain,
account,
chainOverride
> extends EstimateL1GasParameters
? EstimateL1GasParameters<chain, account, chainOverride>['value']
: EstimateL1GasParameters['value']
>
export type EstimateContractL1GasReturnType = bigint
export type EstimateContractL1GasErrorType = GetContractErrorReturnType<
EncodeFunctionDataErrorType | EstimateL1GasErrorType | ParseAccountErrorType
>
/**
* Estimates the L1 data gas required to successfully execute a contract write function call.
*
* @param client - Client to use
* @param parameters - {@link EstimateContractL1GasParameters}
* @returns The gas estimate (in wei). {@link EstimateContractL1GasReturnType}
*
* @example
* import { createPublicClient, http, parseAbi } from 'viem'
* import { optimism } from 'viem/chains'
* import { estimateContractL1Gas } from 'viem/op-stack'
*
* const client = createPublicClient({
* chain: optimism,
* transport: http(),
* })
* const l1Gas = await estimateContractL1Gas(client, {
* address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
* abi: parseAbi(['function mint() public']),
* functionName: 'mint',
* account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
* })
*/
export async function estimateContractL1Gas<
const abi extends Abi | readonly unknown[],
functionName extends ContractFunctionName<abi, 'nonpayable' | 'payable'>,
args extends ContractFunctionArgs<abi, 'pure' | 'view', functionName>,
chain extends Chain | undefined,
account extends Account | undefined = undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: EstimateContractL1GasParameters<
abi,
functionName,
args,
chain,
account,
chainOverride
>,
): Promise<EstimateContractL1GasReturnType> {
const { abi, address, args, functionName, ...request } =
parameters as EstimateContractL1GasParameters
const data = encodeFunctionData({
abi,
args,
functionName,
} as EncodeFunctionDataParameters)
try {
const gas = await estimateL1Gas(client, {
data,
to: address,
...request,
} as unknown as EstimateL1GasParameters)
return gas
} catch (error) {
const account = request.account ? parseAccount(request.account) : undefined
throw getContractError(error as BaseError, {
abi,
address,
args,
docsPath: '/docs/chains/op-stack/estimateContractL1Gas',
functionName,
sender: account?.address,
})
}
}

View File

@@ -0,0 +1,144 @@
import type { Abi } 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 { BaseError } from '../../errors/base.js'
import type { Chain } from '../../types/chain.js'
import type {
ContractFunctionArgs,
ContractFunctionName,
ContractFunctionParameters,
GetValue,
} from '../../types/contract.js'
import type { UnionOmit } from '../../types/utils.js'
import {
type EncodeFunctionDataErrorType,
type EncodeFunctionDataParameters,
encodeFunctionData,
} from '../../utils/abi/encodeFunctionData.js'
import {
type GetContractErrorReturnType,
getContractError,
} from '../../utils/errors/getContractError.js'
import {
type EstimateTotalFeeErrorType,
type EstimateTotalFeeParameters,
estimateTotalFee,
} from './estimateTotalFee.js'
export type EstimateContractTotalFeeParameters<
abi extends Abi | readonly unknown[] = Abi,
functionName extends ContractFunctionName<
abi,
'nonpayable' | 'payable'
> = ContractFunctionName<abi, 'nonpayable' | 'payable'>,
args extends ContractFunctionArgs<
abi,
'nonpayable' | 'payable',
functionName
> = ContractFunctionArgs<abi, 'nonpayable' | 'payable', functionName>,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = undefined,
chainOverride extends Chain | undefined = Chain | undefined,
> = ContractFunctionParameters<
abi,
'nonpayable' | 'payable',
functionName,
args
> &
UnionOmit<
EstimateTotalFeeParameters<chain, account, chainOverride>,
'data' | 'to' | 'value'
> &
GetValue<
abi,
functionName,
EstimateTotalFeeParameters<
chain,
account,
chainOverride
> extends EstimateTotalFeeParameters
? EstimateTotalFeeParameters<chain, account, chainOverride>['value']
: EstimateTotalFeeParameters['value']
>
export type EstimateContractTotalFeeReturnType = bigint
export type EstimateContractTotalFeeErrorType = GetContractErrorReturnType<
| EncodeFunctionDataErrorType
| EstimateTotalFeeErrorType
| ParseAccountErrorType
>
/**
* Estimates the L1 data fee + L2 fee to execute an L2 contract write.
*
* @param client - Client to use
* @param parameters - {@link EstimateContractTotalFeeParameters}
* @returns The gas estimate (in wei). {@link EstimateContractTotalFeeReturnType}
*
* @example
* import { createPublicClient, http, parseAbi } from 'viem'
* import { optimism } from 'viem/chains'
* import { estimateContractTotalFee } from 'viem/op-stack'
*
* const client = createPublicClient({
* chain: optimism,
* transport: http(),
* })
* const totalFee = await estimateContractTotalFee(client, {
* address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
* abi: parseAbi(['function mint() public']),
* functionName: 'mint',
* account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
* })
*/
export async function estimateContractTotalFee<
const abi extends Abi | readonly unknown[],
functionName extends ContractFunctionName<abi, 'nonpayable' | 'payable'>,
args extends ContractFunctionArgs<abi, 'pure' | 'view', functionName>,
chain extends Chain | undefined,
account extends Account | undefined = undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: EstimateContractTotalFeeParameters<
abi,
functionName,
args,
chain,
account,
chainOverride
>,
): Promise<EstimateContractTotalFeeReturnType> {
const { abi, address, args, functionName, ...request } =
parameters as EstimateContractTotalFeeParameters
const data = encodeFunctionData({
abi,
args,
functionName,
} as EncodeFunctionDataParameters)
try {
const fee = await estimateTotalFee(client, {
data,
to: address,
...request,
} as unknown as EstimateTotalFeeParameters)
return fee
} catch (error) {
const account = request.account ? parseAccount(request.account) : undefined
throw getContractError(error as BaseError, {
abi,
address,
args,
docsPath: '/docs/chains/op-stack/estimateTotalFee',
functionName,
sender: account?.address,
})
}
}

View File

@@ -0,0 +1,144 @@
import type { Abi } 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 { BaseError } from '../../errors/base.js'
import type { Chain } from '../../types/chain.js'
import type {
ContractFunctionArgs,
ContractFunctionName,
ContractFunctionParameters,
GetValue,
} from '../../types/contract.js'
import type { UnionOmit } from '../../types/utils.js'
import {
type EncodeFunctionDataErrorType,
type EncodeFunctionDataParameters,
encodeFunctionData,
} from '../../utils/abi/encodeFunctionData.js'
import {
type GetContractErrorReturnType,
getContractError,
} from '../../utils/errors/getContractError.js'
import {
type EstimateTotalGasErrorType,
type EstimateTotalGasParameters,
estimateTotalGas,
} from './estimateTotalGas.js'
export type EstimateContractTotalGasParameters<
abi extends Abi | readonly unknown[] = Abi,
functionName extends ContractFunctionName<
abi,
'nonpayable' | 'payable'
> = ContractFunctionName<abi, 'nonpayable' | 'payable'>,
args extends ContractFunctionArgs<
abi,
'nonpayable' | 'payable',
functionName
> = ContractFunctionArgs<abi, 'nonpayable' | 'payable', functionName>,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = undefined,
chainOverride extends Chain | undefined = Chain | undefined,
> = ContractFunctionParameters<
abi,
'nonpayable' | 'payable',
functionName,
args
> &
UnionOmit<
EstimateTotalGasParameters<chain, account, chainOverride>,
'data' | 'to' | 'value'
> &
GetValue<
abi,
functionName,
EstimateTotalGasParameters<
chain,
account,
chainOverride
> extends EstimateTotalGasParameters
? EstimateTotalGasParameters<chain, account, chainOverride>['value']
: EstimateTotalGasParameters['value']
>
export type EstimateContractTotalGasReturnType = bigint
export type EstimateContractTotalGasErrorType = GetContractErrorReturnType<
| EncodeFunctionDataErrorType
| EstimateTotalGasErrorType
| ParseAccountErrorType
>
/**
* Estimates the L1 data gas + L2 gas required to successfully execute a contract write function call.
*
* @param client - Client to use
* @param parameters - {@link EstimateContractTotalGasParameters}
* @returns The gas estimate (in wei). {@link EstimateContractTotalGasReturnType}
*
* @example
* import { createPublicClient, http, parseAbi } from 'viem'
* import { optimism } from 'viem/chains'
* import { estimateContractTotalGas } from 'viem/op-stack'
*
* const client = createPublicClient({
* chain: optimism,
* transport: http(),
* })
* const totalGas = await estimateContractTotalGas(client, {
* address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
* abi: parseAbi(['function mint() public']),
* functionName: 'mint',
* account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
* })
*/
export async function estimateContractTotalGas<
const abi extends Abi | readonly unknown[],
functionName extends ContractFunctionName<abi, 'nonpayable' | 'payable'>,
args extends ContractFunctionArgs<abi, 'pure' | 'view', functionName>,
chain extends Chain | undefined,
account extends Account | undefined = undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: EstimateContractTotalGasParameters<
abi,
functionName,
args,
chain,
account,
chainOverride
>,
): Promise<EstimateContractTotalGasReturnType> {
const { abi, address, args, functionName, ...request } =
parameters as EstimateContractTotalGasParameters
const data = encodeFunctionData({
abi,
args,
functionName,
} as EncodeFunctionDataParameters)
try {
const gas = await estimateTotalGas(client, {
data,
to: address,
...request,
} as unknown as EstimateTotalGasParameters)
return gas
} catch (error) {
const account = request.account ? parseAccount(request.account) : undefined
throw getContractError(error as BaseError, {
abi,
address,
args,
docsPath: '/docs/chains/op-stack/estimateTotalGas',
functionName,
sender: account?.address,
})
}
}

View File

@@ -0,0 +1,149 @@
import type { Address } from 'abitype'
import {
type EstimateContractGasErrorType,
type EstimateContractGasParameters,
estimateContractGas,
} from '../../actions/public/estimateContractGas.js'
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import { zeroAddress } from '../../constants/address.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Account, GetAccountParameter } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { UnionEvaluate, UnionOmit } from '../../types/utils.js'
import type { FormattedTransactionRequest } from '../../utils/formatters/transactionRequest.js'
import { portalAbi } from '../abis.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import type { DepositRequest } from '../types/deposit.js'
export type EstimateDepositTransactionGasParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
///
derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = UnionEvaluate<
UnionOmit<
FormattedTransactionRequest<derivedChain>,
| 'accessList'
| 'data'
| 'from'
| 'gas'
| 'gasPrice'
| 'to'
| 'type'
| 'value'
>
> &
GetAccountParameter<account, Account | Address> &
GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<derivedChain, 'portal'> & {
/** L2 transaction request. */
request: DepositRequest
/** Gas limit for transaction execution on the L1. */
gas?: bigint | undefined
}
export type EstimateDepositTransactionGasReturnType = bigint
export type EstimateDepositTransactionGasErrorType =
| EstimateContractGasErrorType
| ErrorType
/**
* Estimates gas required to initiate a [deposit transaction](https://github.com/ethereum-optimism/optimism/blob/develop/specs/deposits.md) on an L1, which executes a transaction on L2.
*
* - Docs: https://viem.sh/op-stack/actions/estimateDepositTransactionGas
*
* @param client - Client to use
* @param parameters - {@link EstimateDepositTransactionGasParameters}
* @returns The L1 transaction hash. {@link EstimateDepositTransactionGasReturnType}
*
* @example
* import { createPublicClient, custom, parseEther } from 'viem'
* import { base, mainnet } from 'viem/chains'
* import { estimateDepositTransactionGas } from 'viem/op-stack'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* })
*
* const gas = await estimateDepositTransactionGas(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* args: {
* gas: 21_000n,
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* targetChain: base,
* })
*/
export async function estimateDepositTransactionGas<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: EstimateDepositTransactionGasParameters<
chain,
account,
chainOverride
>,
) {
const {
account,
chain = client.chain,
gas,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
request: {
data = '0x',
gas: l2Gas,
isCreation = false,
mint,
to = '0x',
value,
},
targetChain,
} = parameters
const portalAddress = (() => {
if (parameters.portalAddress) return parameters.portalAddress
if (chain) return targetChain!.contracts.portal[chain.id].address
return Object.values(targetChain!.contracts.portal)[0].address
})()
const params = {
account,
abi: portalAbi,
address: portalAddress,
functionName: 'depositTransaction',
args: [
isCreation ? zeroAddress : to,
value ?? mint ?? 0n,
l2Gas,
isCreation,
data,
],
gas,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
value: mint,
// TODO: Not sure `chain` is necessary since it's not used downstream
// in `estimateContractGas` or `estimateGas`
// @ts-expect-error
chain,
} satisfies EstimateContractGasParameters<
typeof portalAbi,
'depositTransaction'
>
return estimateContractGas(client, params as any)
}

View File

@@ -0,0 +1,137 @@
import type { Address } from 'abitype'
import {
type EstimateContractGasErrorType,
type EstimateContractGasParameters,
estimateContractGas,
} from '../../actions/public/estimateContractGas.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 { Account, GetAccountParameter } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { UnionEvaluate, UnionOmit } from '../../types/utils.js'
import type { FormattedTransactionRequest } from '../../utils/formatters/transactionRequest.js'
import { portal2Abi, portalAbi } from '../abis.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import type { Withdrawal } from '../types/withdrawal.js'
export type EstimateFinalizeWithdrawalGasParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = UnionEvaluate<
UnionOmit<
FormattedTransactionRequest<_derivedChain>,
| 'accessList'
| 'data'
| 'from'
| 'gas'
| 'gasPrice'
| 'to'
| 'type'
| 'value'
>
> &
GetAccountParameter<account, Account | Address> &
GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<_derivedChain, 'portal'> & {
/** Gas limit for transaction execution on the L2. */
gas?: bigint | undefined
/**
* Finalize against a specific proof submitter.
* If unspecified, the sending account is the default.
*/
proofSubmitter?: Address | null | undefined
withdrawal: Withdrawal
}
export type EstimateFinalizeWithdrawalGasReturnType = bigint
export type EstimateFinalizeWithdrawalGasErrorType =
| EstimateContractGasErrorType
| ErrorType
/**
* Estimates gas required to finalize a withdrawal that occurred on an L2.
*
* - Docs: https://viem.sh/op-stack/actions/estimateFinalizeWithdrawalGas
*
* @param client - Client to use
* @param parameters - {@link EstimateFinalizeWithdrawalGasParameters}
* @returns Estimated gas. {@link EstimateFinalizeWithdrawalGasReturnType}
*
* @example
* import { createPublicClient, http, parseEther } from 'viem'
* import { base, mainnet } from 'viem/chains'
* import { estimateFinalizeWithdrawalGas } from 'viem/op-stack'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
*
* const gas = await estimateFinalizeWithdrawalGas(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* targetChain: optimism,
* withdrawal: { ... },
* })
*/
export async function estimateFinalizeWithdrawalGas<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: EstimateFinalizeWithdrawalGasParameters<
chain,
account,
chainOverride
>,
) {
const {
account,
chain = client.chain,
gas,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
proofSubmitter,
targetChain,
withdrawal,
} = parameters
const portalAddress = (() => {
if (parameters.portalAddress) return parameters.portalAddress
if (chain) return targetChain!.contracts.portal[chain.id].address
return Object.values(targetChain!.contracts.portal)[0].address
})()
const [functionName, args, abi] = proofSubmitter
? [
'finalizeWithdrawalTransactionExternalProof',
[withdrawal, proofSubmitter],
portal2Abi,
]
: ['finalizeWithdrawalTransaction', [withdrawal], portalAbi]
const params = {
account,
abi,
address: portalAddress,
functionName,
args,
gas,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
// TODO: Not sure `chain` is necessary since it's not used downstream
// in `estimateContractGas` or `estimateGas`
// @ts-expect-error
chain,
} satisfies EstimateContractGasParameters
return estimateContractGas(client, params as any)
}

View File

@@ -0,0 +1,125 @@
import type { Address } from 'abitype'
import {
type EstimateContractGasErrorType,
type EstimateContractGasParameters,
estimateContractGas,
} from '../../actions/public/estimateContractGas.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 { Account, GetAccountParameter } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { UnionEvaluate, UnionOmit } from '../../types/utils.js'
import type { FormattedTransactionRequest } from '../../utils/formatters/transactionRequest.js'
import { l2ToL1MessagePasserAbi } from '../abis.js'
import { contracts } from '../contracts.js'
import type { WithdrawalRequest } from '../types/withdrawal.js'
export type EstimateInitiateWithdrawalGasParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = UnionEvaluate<
UnionOmit<
FormattedTransactionRequest<_derivedChain>,
| 'accessList'
| 'data'
| 'from'
| 'gas'
| 'gasPrice'
| 'to'
| 'type'
| 'value'
>
> &
GetAccountParameter<account, Account | Address> &
GetChainParameter<chain, chainOverride> & {
/** Gas limit for transaction execution on the L2. */
gas?: bigint | undefined
/**
* Withdrawal request.
* Supplied to the L2ToL1MessagePasser `initiateWithdrawal` method.
*/
request: WithdrawalRequest
}
export type EstimateInitiateWithdrawalGasReturnType = bigint
export type EstimateInitiateWithdrawalGasErrorType =
| EstimateContractGasErrorType
| ErrorType
/**
* Estimates gas required to initiate a [withdrawal](https://community.optimism.io/docs/protocol/withdrawal-flow/#withdrawal-initiating-transaction) on an L2 to the L1.
*
* - Docs: https://viem.sh/op-stack/actions/estimateInitiateWithdrawalGas
*
* @param client - Client to use
* @param parameters - {@link EstimateInitiateWithdrawalGasParameters}
* @returns Estimated gas. {@link EstimateInitiateWithdrawalGasReturnType}
*
* @example
* import { createPublicClient, http, parseEther } from 'viem'
* import { base, mainnet } from 'viem/chains'
* import { estimateInitiateWithdrawalGas } from 'viem/op-stack'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
*
* const gas = await estimateInitiateWithdrawalGas(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* request: {
* gas: 21_000n,
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* })
*/
export async function estimateInitiateWithdrawalGas<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: EstimateInitiateWithdrawalGasParameters<
chain,
account,
chainOverride
>,
) {
const {
account,
chain = client.chain,
gas,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
request: { data = '0x', gas: l1Gas, to, value },
} = parameters
const params = {
account,
abi: l2ToL1MessagePasserAbi,
address: contracts.l2ToL1MessagePasser.address,
functionName: 'initiateWithdrawal',
args: [to, l1Gas, data],
gas,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
value,
// TODO: Not sure `chain` is necessary since it's not used downstream
// in `estimateContractGas` or `estimateGas`
// @ts-expect-error
chain,
} satisfies EstimateContractGasParameters<
typeof l2ToL1MessagePasserAbi,
'initiateWithdrawal'
>
return estimateContractGas(client, params as any)
}

112
node_modules/viem/op-stack/actions/estimateL1Fee.ts generated vendored Normal file
View File

@@ -0,0 +1,112 @@
import type { Address } from 'abitype'
import {
type ReadContractErrorType,
readContract,
} from '../../actions/public/readContract.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 { Account, GetAccountParameter } from '../../types/account.js'
import type { Chain, GetChainParameter } from '../../types/chain.js'
import type {
TransactionRequestEIP1559,
TransactionSerializable,
} from '../../types/transaction.js'
import type { RequestErrorType } from '../../utils/buildRequest.js'
import { getChainContractAddress } from '../../utils/chain/getChainContractAddress.js'
import type { HexToNumberErrorType } from '../../utils/encoding/fromHex.js'
import {
type SerializeTransactionErrorType,
serializeTransaction,
} from '../../utils/transaction/serializeTransaction.js'
import { parseGwei } from '../../utils/unit/parseGwei.js'
import { gasPriceOracleAbi } from '../abis.js'
import { contracts } from '../contracts.js'
export type EstimateL1FeeParameters<
TChain extends Chain | undefined = Chain | undefined,
TAccount extends Account | undefined = Account | undefined,
TChainOverride extends Chain | undefined = undefined,
> = Omit<TransactionRequestEIP1559, 'from'> &
GetAccountParameter<TAccount> &
GetChainParameter<TChain, TChainOverride> & {
/** Gas price oracle address. */
gasPriceOracleAddress?: Address | undefined
}
export type EstimateL1FeeReturnType = bigint
export type EstimateL1FeeErrorType =
| RequestErrorType
| SerializeTransactionErrorType
| HexToNumberErrorType
| ReadContractErrorType
| ErrorType
/**
* Estimates the L1 data fee required to execute an L2 transaction.
*
* @param client - Client to use
* @param parameters - {@link EstimateL1FeeParameters}
* @returns The fee (in wei). {@link EstimateL1FeeReturnType}
*
* @example
* import { createPublicClient, http, parseEther } from 'viem'
* import { optimism } from 'viem/chains'
* import { estimateL1Fee } from 'viem/chains/optimism'
*
* const client = createPublicClient({
* chain: optimism,
* transport: http(),
* })
* const l1Fee = await estimateL1Fee(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* })
*/
export async function estimateL1Fee<
TChain extends Chain | undefined,
TAccount extends Account | undefined,
TChainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, TChain, TAccount>,
args: EstimateL1FeeParameters<TChain, TAccount, TChainOverride>,
): Promise<EstimateL1FeeReturnType> {
const {
chain = client.chain,
gasPriceOracleAddress: gasPriceOracleAddress_,
} = args
const gasPriceOracleAddress = (() => {
if (gasPriceOracleAddress_) return gasPriceOracleAddress_
if (chain)
return getChainContractAddress({
chain,
contract: 'gasPriceOracle',
})
return contracts.gasPriceOracle.address
})()
const transaction = serializeTransaction({
...args,
chainId: chain?.id ?? 1,
type: 'eip1559',
// Set upper-limit-ish stub values. Shouldn't affect the estimate too much as we are
// tweaking dust bytes here (as opposed to long `data` bytes).
// See: https://github.com/ethereum-optimism/optimism/blob/54d02df55523c9e1b4b38ed082c12a42087323a0/packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L242-L248.
gas: args.data ? 300_000n : 21_000n,
maxFeePerGas: parseGwei('5'),
maxPriorityFeePerGas: parseGwei('1'),
nonce: 1,
} as TransactionSerializable)
return readContract(client, {
abi: gasPriceOracleAbi,
address: gasPriceOracleAddress,
functionName: 'getL1Fee',
args: [transaction as any],
})
}

112
node_modules/viem/op-stack/actions/estimateL1Gas.ts generated vendored Normal file
View File

@@ -0,0 +1,112 @@
import type { Address } from 'abitype'
import {
type ReadContractErrorType,
readContract,
} from '../../actions/public/readContract.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 { Account, GetAccountParameter } from '../../types/account.js'
import type { Chain, GetChainParameter } from '../../types/chain.js'
import type {
TransactionRequestEIP1559,
TransactionSerializable,
} from '../../types/transaction.js'
import type { RequestErrorType } from '../../utils/buildRequest.js'
import { getChainContractAddress } from '../../utils/chain/getChainContractAddress.js'
import type { HexToNumberErrorType } from '../../utils/encoding/fromHex.js'
import {
type SerializeTransactionErrorType,
serializeTransaction,
} from '../../utils/transaction/serializeTransaction.js'
import { parseGwei } from '../../utils/unit/parseGwei.js'
import { gasPriceOracleAbi } from '../abis.js'
import { contracts } from '../contracts.js'
export type EstimateL1GasParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
> = Omit<TransactionRequestEIP1559, 'from'> &
GetAccountParameter<account> &
GetChainParameter<chain, chainOverride> & {
/** Gas price oracle address. */
gasPriceOracleAddress?: Address | undefined
}
export type EstimateL1GasReturnType = bigint
export type EstimateL1GasErrorType =
| RequestErrorType
| SerializeTransactionErrorType
| HexToNumberErrorType
| ReadContractErrorType
| ErrorType
/**
* Estimates the L1 data gas required to execute an L2 transaction.
*
* @param client - Client to use
* @param parameters - {@link EstimateL1GasParameters}
* @returns The gas estimate. {@link EstimateL1GasReturnType}
*
* @example
* import { createPublicClient, http, parseEther } from 'viem'
* import { optimism } from 'viem/chains'
* import { estimateL1Gas } from 'viem/chains/optimism'
*
* const client = createPublicClient({
* chain: optimism,
* transport: http(),
* })
* const l1Gas = await estimateL1Gas(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* })
*/
export async function estimateL1Gas<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
args: EstimateL1GasParameters<chain, account, chainOverride>,
): Promise<EstimateL1GasReturnType> {
const {
chain = client.chain,
gasPriceOracleAddress: gasPriceOracleAddress_,
} = args
const gasPriceOracleAddress = (() => {
if (gasPriceOracleAddress_) return gasPriceOracleAddress_
if (chain)
return getChainContractAddress({
chain,
contract: 'gasPriceOracle',
})
return contracts.gasPriceOracle.address
})()
const transaction = serializeTransaction({
...args,
chainId: chain?.id ?? 1,
type: 'eip1559',
// Set upper-limit-ish stub values. Shouldn't affect the estimate too much as we are
// tweaking dust bytes here (as opposed to long `data` bytes).
// See: https://github.com/ethereum-optimism/optimism/blob/54d02df55523c9e1b4b38ed082c12a42087323a0/packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L242-L248.
gas: args.data ? 300_000n : 21_000n,
maxFeePerGas: parseGwei('5'),
maxPriorityFeePerGas: parseGwei('1'),
nonce: 1,
} as TransactionSerializable)
return readContract(client, {
abi: gasPriceOracleAbi,
address: gasPriceOracleAddress,
functionName: 'getL1GasUsed',
args: [transaction as any],
})
}

View File

@@ -0,0 +1,119 @@
import type { Address } from 'abitype'
import {
type EstimateGasErrorType,
type EstimateGasParameters,
estimateGas,
} from '../../actions/public/estimateGas.js'
import {
type ReadContractErrorType,
readContract,
} from '../../actions/public/readContract.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 { Account, GetAccountParameter } from '../../types/account.js'
import type { Chain, GetChainParameter } from '../../types/chain.js'
import type { TransactionRequestEIP1559 } from '../../types/transaction.js'
import type { RequestErrorType } from '../../utils/buildRequest.js'
import { getChainContractAddress } from '../../utils/chain/getChainContractAddress.js'
import type { HexToNumberErrorType } from '../../utils/encoding/fromHex.js'
import { l1BlockAbi } from '../abis.js'
import { contracts } from '../contracts.js'
export type EstimateOperatorFeeParameters<
TChain extends Chain | undefined = Chain | undefined,
TAccount extends Account | undefined = Account | undefined,
TChainOverride extends Chain | undefined = undefined,
> = Omit<TransactionRequestEIP1559, 'from'> &
GetAccountParameter<TAccount> &
GetChainParameter<TChain, TChainOverride> & {
/** L1 block attributes contract address. */
l1BlockAddress?: Address | undefined
}
export type EstimateOperatorFeeReturnType = bigint
export type EstimateOperatorFeeErrorType =
| RequestErrorType
| EstimateGasErrorType
| HexToNumberErrorType
| ReadContractErrorType
| ErrorType
/**
* Estimates the operator fee required to execute an L2 transaction.
*
* Operator fees are part of the Isthmus upgrade and allow OP Stack operators
* to recover costs related to Alt-DA, ZK proving, or custom gas tokens.
* Returns 0 for pre-Isthmus chains or when operator fee functions don't exist.
*
* @param client - Client to use
* @param parameters - {@link EstimateOperatorFeeParameters}
* @returns The operator fee (in wei). {@link EstimateOperatorFeeReturnType}
*
* @example
* import { createPublicClient, http, parseEther } from 'viem'
* import { optimism } from 'viem/chains'
* import { estimateOperatorFee } from 'viem/chains/optimism'
*
* const client = createPublicClient({
* chain: optimism,
* transport: http(),
* })
* const operatorFee = await estimateOperatorFee(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* })
*/
export async function estimateOperatorFee<
TChain extends Chain | undefined,
TAccount extends Account | undefined,
TChainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, TChain, TAccount>,
args: EstimateOperatorFeeParameters<TChain, TAccount, TChainOverride>,
): Promise<EstimateOperatorFeeReturnType> {
const { chain = client.chain, l1BlockAddress: l1BlockAddress_ } = args
const l1BlockAddress = (() => {
if (l1BlockAddress_) return l1BlockAddress_
if (chain?.contracts?.l1Block)
return getChainContractAddress({
chain,
contract: 'l1Block',
})
return contracts.l1Block.address
})()
// Try to get operator fee parameters. If any of these calls fail,
// it means this is a pre-Isthmus chain and operator fees don't apply
try {
// Get operator fee parameters first to fail fast if not supported
const [operatorFeeScalar, operatorFeeConstant] = await Promise.all([
readContract(client, {
abi: l1BlockAbi,
address: l1BlockAddress,
functionName: 'operatorFeeScalar',
}),
readContract(client, {
abi: l1BlockAbi,
address: l1BlockAddress,
functionName: 'operatorFeeConstant',
}),
])
// Estimate gas for the actual transaction
const gasUsed = await estimateGas(client, args as EstimateGasParameters)
// Calculate operator fee: saturatingAdd(saturatingMul(gasUsed, scalar) / 1e6, constant)
// Using saturating arithmetic to prevent overflow
const scaledFee = (gasUsed * BigInt(operatorFeeScalar)) / 1_000_000n
return scaledFee + BigInt(operatorFeeConstant)
} catch {
// If any call fails, this is likely a pre-Isthmus chain or the contract
// doesn't support these functions. Return 0 for operator fee.
return 0n
}
}

View File

@@ -0,0 +1,146 @@
import type { Address } from 'abitype'
import {
type EstimateContractGasErrorType,
type EstimateContractGasParameters,
estimateContractGas,
} from '../../actions/public/estimateContractGas.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 { Account, GetAccountParameter } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { Hex } from '../../types/misc.js'
import type { UnionEvaluate, UnionOmit } from '../../types/utils.js'
import type { FormattedTransactionRequest } from '../../utils/formatters/transactionRequest.js'
import { portalAbi } from '../abis.js'
import type { GetContractAddressParameter } from '../types/contract.js'
export type EstimateProveWithdrawalGasParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = UnionEvaluate<
UnionOmit<
FormattedTransactionRequest<_derivedChain>,
| 'accessList'
| 'data'
| 'from'
| 'gas'
| 'gasPrice'
| 'to'
| 'type'
| 'value'
>
> &
GetAccountParameter<account, Account | Address> &
GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<_derivedChain, 'portal'> & {
/** Gas limit for transaction execution on the L2. */
gas?: bigint | undefined
l2OutputIndex: bigint
outputRootProof: {
version: Hex
stateRoot: Hex
messagePasserStorageRoot: Hex
latestBlockhash: Hex
}
withdrawalProof: readonly Hex[]
withdrawal: {
data: Hex
gasLimit: bigint
nonce: bigint
sender: Address
target: Address
value: bigint
}
}
export type EstimateProveWithdrawalGasReturnType = bigint
export type EstimateProveWithdrawalGasErrorType =
| EstimateContractGasErrorType
| ErrorType
/**
* Estimates gas required to prove a withdrawal that occurred on an L2.
*
* - Docs: https://viem.sh/op-stack/actions/estimateProveWithdrawalGas
*
* @param client - Client to use
* @param parameters - {@link EstimateProveWithdrawalGasParameters}
* @returns Estimated gas. {@link EstimateProveWithdrawalGasReturnType}
*
* @example
* import { createPublicClient, http, parseEther } from 'viem'
* import { base, mainnet } from 'viem/chains'
* import { estimateProveWithdrawalGas } from 'viem/op-stack'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
*
* const gas = await estimateProveWithdrawalGas(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* l2OutputIndex: 4529n,
* outputRootProof: { ... },
* targetChain: optimism,
* withdrawalProof: [ ... ],
* withdrawal: { ... },
* })
*/
export async function estimateProveWithdrawalGas<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: EstimateProveWithdrawalGasParameters<
chain,
account,
chainOverride
>,
) {
const {
account,
chain = client.chain,
gas,
l2OutputIndex,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
outputRootProof,
targetChain,
withdrawalProof,
withdrawal,
} = parameters
const portalAddress = (() => {
if (parameters.portalAddress) return parameters.portalAddress
if (chain) return targetChain!.contracts.portal[chain.id].address
return Object.values(targetChain!.contracts.portal)[0].address
})()
const params = {
account,
abi: portalAbi,
address: portalAddress,
functionName: 'proveWithdrawalTransaction',
args: [withdrawal, l2OutputIndex, outputRootProof, withdrawalProof],
gas,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
// TODO: Not sure `chain` is necessary since it's not used downstream
// in `estimateContractGas` or `estimateGas`
// @ts-expect-error
chain,
} satisfies EstimateContractGasParameters<
typeof portalAbi,
'proveWithdrawalTransaction'
>
return estimateContractGas(client, params as any)
}

94
node_modules/viem/op-stack/actions/estimateTotalFee.ts generated vendored Normal file
View File

@@ -0,0 +1,94 @@
import {
type EstimateGasErrorType,
type EstimateGasParameters,
estimateGas,
} from '../../actions/public/estimateGas.js'
import {
type GetGasPriceErrorType,
getGasPrice,
} from '../../actions/public/getGasPrice.js'
import {
type PrepareTransactionRequestErrorType,
type PrepareTransactionRequestParameters,
prepareTransactionRequest,
} from '../../actions/wallet/prepareTransactionRequest.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 { Account } from '../../types/account.js'
import type { Chain } from '../../types/chain.js'
import type { RequestErrorType } from '../../utils/buildRequest.js'
import {
type EstimateL1FeeErrorType,
type EstimateL1FeeParameters,
estimateL1Fee,
} from './estimateL1Fee.js'
import {
type EstimateOperatorFeeErrorType,
type EstimateOperatorFeeParameters,
estimateOperatorFee,
} from './estimateOperatorFee.js'
export type EstimateTotalFeeParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
> = EstimateL1FeeParameters<chain, account, chainOverride> &
EstimateOperatorFeeParameters<chain, account, chainOverride>
export type EstimateTotalFeeReturnType = bigint
export type EstimateTotalFeeErrorType =
| RequestErrorType
| PrepareTransactionRequestErrorType
| EstimateL1FeeErrorType
| EstimateOperatorFeeErrorType
| EstimateGasErrorType
| GetGasPriceErrorType
| ErrorType
/**
* Estimates the L1 data fee + L2 fee + operator fee to execute an L2 transaction.
*
* @param client - Client to use
* @param parameters - {@link EstimateTotalFeeParameters}
* @returns The fee (in wei). {@link EstimateTotalFeeReturnType}
*
* @example
* import { createPublicClient, http, parseEther } from 'viem'
* import { optimism } from 'viem/chains'
* import { estimateTotalFee } from 'viem/chains/optimism'
*
* const client = createPublicClient({
* chain: optimism,
* transport: http(),
* })
* const totalGas = await estimateTotalFee(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* })
*/
export async function estimateTotalFee<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
args: EstimateTotalFeeParameters<chain, account, chainOverride>,
): Promise<EstimateTotalFeeReturnType> {
// Populate transaction with required fields to accurately estimate gas.
const request = await prepareTransactionRequest(
client,
args as PrepareTransactionRequestParameters,
)
const [l1Fee, operatorFee, l2Gas, l2GasPrice] = await Promise.all([
estimateL1Fee(client, request as EstimateL1FeeParameters),
estimateOperatorFee(client, request as EstimateOperatorFeeParameters),
estimateGas(client, request as EstimateGasParameters),
getGasPrice(client),
])
return l1Fee + operatorFee + l2Gas * l2GasPrice
}

70
node_modules/viem/op-stack/actions/estimateTotalGas.ts generated vendored Normal file
View File

@@ -0,0 +1,70 @@
import {
type EstimateGasErrorType,
type EstimateGasParameters,
estimateGas,
} from '../../actions/public/estimateGas.js'
import type { PrepareTransactionRequestErrorType } from '../../actions/wallet/prepareTransactionRequest.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 { Account } from '../../types/account.js'
import type { Chain } from '../../types/chain.js'
import type { RequestErrorType } from '../../utils/buildRequest.js'
import {
type EstimateL1GasErrorType,
type EstimateL1GasParameters,
estimateL1Gas,
} from './estimateL1Gas.js'
export type EstimateTotalGasParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
> = EstimateL1GasParameters<chain, account, chainOverride>
export type EstimateTotalGasReturnType = bigint
export type EstimateTotalGasErrorType =
| RequestErrorType
| PrepareTransactionRequestErrorType
| EstimateL1GasErrorType
| EstimateGasErrorType
| ErrorType
/**
* Estimates the amount of L1 data gas + L2 gas required to execute an L2 transaction.
*
* @param client - Client to use
* @param parameters - {@link EstimateTotalGasParameters}
* @returns The gas estimate. {@link EstimateTotalGasReturnType}
*
* @example
* import { createPublicClient, http, parseEther } from 'viem'
* import { optimism } from 'viem/chains'
* import { estimateTotalGas } from 'viem/chains/optimism'
*
* const client = createPublicClient({
* chain: optimism,
* transport: http(),
* })
* const totalGas = await estimateTotalGas(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* })
*/
export async function estimateTotalGas<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
args: EstimateTotalGasParameters<chain, account, chainOverride>,
): Promise<EstimateTotalGasReturnType> {
const [l1Gas, l2Gas] = await Promise.all([
estimateL1Gas(client, args as EstimateL1GasParameters),
estimateGas(client, args as EstimateGasParameters),
])
return l1Gas + l2Gas
}

View File

@@ -0,0 +1,133 @@
import type { Address } from 'abitype'
import {
type WriteContractErrorType,
type WriteContractParameters,
writeContract,
} from '../../actions/wallet/writeContract.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 { Account, GetAccountParameter } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { Hash } from '../../types/misc.js'
import type { UnionEvaluate, UnionOmit } from '../../types/utils.js'
import type { FormattedTransactionRequest } from '../../utils/formatters/transactionRequest.js'
import { portal2Abi, portalAbi } from '../abis.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import type { Withdrawal } from '../types/withdrawal.js'
import type { EstimateFinalizeWithdrawalGasErrorType } from './estimateFinalizeWithdrawalGas.js'
export type FinalizeWithdrawalParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = UnionEvaluate<
UnionOmit<
FormattedTransactionRequest<_derivedChain>,
| 'accessList'
| 'data'
| 'from'
| 'gas'
| 'gasPrice'
| 'to'
| 'type'
| 'value'
>
> &
GetAccountParameter<account, Account | Address> &
GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<_derivedChain, 'portal'> & {
/**
* Gas limit for transaction execution on the L1.
* `null` to skip gas estimation & defer calculation to signer.
*/
gas?: bigint | null | undefined
/**
* Finalize against a provided proof submitter.
* If unspecified, the sending account is the default.
*/
proofSubmitter?: Address | null | undefined
withdrawal: Withdrawal
}
export type FinalizeWithdrawalReturnType = Hash
export type FinalizeWithdrawalErrorType =
| EstimateFinalizeWithdrawalGasErrorType
| WriteContractErrorType
| ErrorType
/**
* Finalizes a withdrawal that occurred on an L2. Used in the Withdrawal flow.
*
* - Docs: https://viem.sh/op-stack/actions/finalizeWithdrawal
*
* @param client - Client to use
* @param parameters - {@link FinalizeWithdrawalParameters}
* @returns The finalize transaction hash. {@link FinalizeWithdrawalReturnType}
*
* @example
* import { createWalletClient, http } from 'viem'
* import { mainnet, optimism } from 'viem/chains'
* import { finalizeWithdrawal } from 'viem/op-stack'
*
* const walletClientL1 = createWalletClient({
* chain: mainnet,
* transport: http(),
* })
*
* const request = await finalizeWithdrawal(walletClientL1, {
* targetChain: optimism,
* withdrawal: { ... },
* })
*/
export async function finalizeWithdrawal<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: FinalizeWithdrawalParameters<chain, account, chainOverride>,
): Promise<FinalizeWithdrawalReturnType> {
const {
account,
chain = client.chain,
gas,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
proofSubmitter,
targetChain,
withdrawal,
} = parameters
const portalAddress = (() => {
if (parameters.portalAddress) return parameters.portalAddress
if (chain) return targetChain!.contracts.portal[chain.id].address
return Object.values(targetChain!.contracts.portal)[0].address
})()
const [functionName, args, abi] = proofSubmitter
? [
'finalizeWithdrawalTransactionExternalProof',
[withdrawal, proofSubmitter],
portal2Abi,
]
: ['finalizeWithdrawalTransaction', [withdrawal], portalAbi]
return writeContract(client, {
account: account!,
abi,
address: portalAddress,
chain,
functionName,
args,
gas: gas ?? undefined,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
} satisfies WriteContractParameters as any)
}

99
node_modules/viem/op-stack/actions/getGame.ts generated vendored Normal file
View File

@@ -0,0 +1,99 @@
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Account } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import {
GameNotFoundError,
type GameNotFoundErrorType,
} from '../errors/withdrawal.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import type { Game } from '../types/withdrawal.js'
import { type GetGamesErrorType, getGames } from './getGames.js'
export type GetGameParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<
_derivedChain,
'portal' | 'disputeGameFactory'
> & {
/**
* The minimum block number of the dispute game.
*/
l2BlockNumber: bigint
/**
* Limit of games to extract.
* @default 100
*/
limit?: number | undefined
/**
* Strategy for extracting a dispute game.
*
* - `latest` - Returns the latest dispute game.
* - `random` - Returns a random dispute game.
*/
strategy?: 'latest' | 'random'
}
export type GetGameReturnType = Game & {
l2BlockNumber: bigint
}
export type GetGameErrorType =
| GetGamesErrorType
| GameNotFoundErrorType
| ErrorType
/**
* Retrieves a valid dispute game on an L2 that occurred after a provided L2 block number.
*
* - Docs: https://viem.sh/op-stack/actions/getGame
*
* @param client - Client to use
* @param parameters - {@link GetGameParameters}
* @returns A valid dispute game. {@link GetGameReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { mainnet, optimism } from 'viem/chains'
* import { getGame } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
*
* const game = await getGame(publicClientL1, {
* l2BlockNumber: 69420n,
* targetChain: optimism
* })
*/
export async function getGame<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: GetGameParameters<chain, chainOverride>,
): Promise<GetGameReturnType> {
const { l2BlockNumber, strategy = 'latest' } = parameters
const latestGames = await getGames(client, parameters)
const games = latestGames.filter((game) => game.l2BlockNumber > l2BlockNumber)
const game = (() => {
if (strategy === 'random')
return games[Math.floor(Math.random() * games.length)]
return games[0]
})()
if (!game) throw new GameNotFoundError()
return game
}

146
node_modules/viem/op-stack/actions/getGames.ts generated vendored Normal file
View File

@@ -0,0 +1,146 @@
import type { Address } from 'abitype'
import {
type MulticallErrorType,
multicall,
} from '../../actions/public/multicall.js'
import {
type ReadContractErrorType,
readContract,
} from '../../actions/public/readContract.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 { Account } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import { disputeGameAbi, disputeGameFactoryAbi, portal2Abi } from '../abis.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import type { Game } from '../types/withdrawal.js'
export type GetGamesParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<
_derivedChain,
'portal' | 'disputeGameFactory'
> & {
/**
* Filter by minimum block number of the dispute games.
*/
l2BlockNumber?: bigint | undefined
/**
* Limit of games to extract.
* @default 100
*/
limit?: number | undefined
}
export type GetGamesReturnType = (Game & {
l2BlockNumber: bigint
})[]
export type GetGamesErrorType =
| ReadContractErrorType
| MulticallErrorType
| ErrorType
/**
* Retrieves dispute games for an L2.
*
* - Docs: https://viem.sh/op-stack/actions/getGame
*
* @param client - Client to use
* @param parameters - {@link GetGameParameters}
* @returns Dispute games. {@link GetGameReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { mainnet, optimism } from 'viem/chains'
* import { getGames } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
*
* const games = await getGames(publicClientL1, {
* targetChain: optimism
* })
*/
export async function getGames<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: GetGamesParameters<chain, chainOverride>,
): Promise<GetGamesReturnType> {
const {
chain = client.chain,
l2BlockNumber,
limit = 100,
targetChain,
} = parameters
const portalAddress = (() => {
if (parameters.portalAddress) return parameters.portalAddress
if (chain) return targetChain!.contracts.portal[chain.id].address
return Object.values(targetChain!.contracts.portal)[0].address
})()
const disputeGameFactoryAddress = (() => {
if (parameters.disputeGameFactoryAddress)
return parameters.disputeGameFactoryAddress
if (chain)
return targetChain!.contracts.disputeGameFactory[chain.id].address
return Object.values(targetChain!.contracts.disputeGameFactory)[0].address
})()
const [gameCount, gameType] = await Promise.all([
readContract(client, {
abi: disputeGameFactoryAbi,
functionName: 'gameCount',
args: [],
address: disputeGameFactoryAddress,
}),
readContract(client, {
abi: portal2Abi,
functionName: 'respectedGameType',
address: portalAddress,
}),
])
const rawGames = (await readContract(client, {
abi: disputeGameFactoryAbi,
functionName: 'findLatestGames',
address: disputeGameFactoryAddress,
args: [
gameType,
BigInt(Math.max(0, Number(gameCount - 1n))),
BigInt(Math.min(limit, Number(gameCount))),
],
})) as Game[]
const l2SequenceNumbers = await multicall(client, {
allowFailure: false,
contracts: rawGames.map((game) => ({
abi: disputeGameAbi,
address: `0x${game.metadata.slice(26)}` as Address,
functionName: 'l2SequenceNumber' as const,
})),
})
const games = rawGames
.map((game, i) => {
const blockNumber = l2SequenceNumbers[i] as bigint
return !l2BlockNumber || blockNumber > l2BlockNumber
? { ...game, l2BlockNumber: blockNumber }
: null
})
.filter(Boolean) as GetGamesReturnType
return games
}

81
node_modules/viem/op-stack/actions/getL1BaseFee.ts generated vendored Normal file
View File

@@ -0,0 +1,81 @@
import type { Address } from 'abitype'
import {
type ReadContractErrorType,
readContract,
} from '../../actions/public/readContract.js'
import type { PrepareTransactionRequestErrorType } from '../../actions/wallet/prepareTransactionRequest.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 { Chain, GetChainParameter } from '../../types/chain.js'
import type { RequestErrorType } from '../../utils/buildRequest.js'
import { getChainContractAddress } from '../../utils/chain/getChainContractAddress.js'
import type { HexToNumberErrorType } from '../../utils/encoding/fromHex.js'
import { gasPriceOracleAbi } from '../abis.js'
import { contracts } from '../contracts.js'
export type GetL1BaseFeeParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = undefined,
> = GetChainParameter<chain, chainOverride> & {
/** Gas price oracle address. */
gasPriceOracleAddress?: Address | undefined
}
export type GetL1BaseFeeReturnType = bigint
export type GetL1BaseFeeErrorType =
| RequestErrorType
| PrepareTransactionRequestErrorType
| HexToNumberErrorType
| ReadContractErrorType
| ErrorType
/**
* get the L1 base fee
*
* @param client - Client to use
* @param parameters - {@link GetL1BaseFeeParameters}
* @returns The basefee (in wei). {@link GetL1BaseFeeReturnType}
*
* @example
* import { createPublicClient, http, parseEther } from 'viem'
* import { optimism } from 'viem/chains'
* import { getL1BaseFee } from 'viem/chains/optimism'
*
* const client = createPublicClient({
* chain: optimism,
* transport: http(),
* })
* const l1BaseFee = await getL1BaseFee(client)
*/
export async function getL1BaseFee<
chain extends Chain | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain>,
args?: GetL1BaseFeeParameters<chain, chainOverride> | undefined,
): Promise<GetL1BaseFeeReturnType> {
const {
chain = client.chain,
gasPriceOracleAddress: gasPriceOracleAddress_,
} = args || {}
const gasPriceOracleAddress = (() => {
if (gasPriceOracleAddress_) return gasPriceOracleAddress_
if (chain)
return getChainContractAddress({
chain,
contract: 'gasPriceOracle',
})
return contracts.gasPriceOracle.address
})()
return readContract(client, {
abi: gasPriceOracleAbi,
address: gasPriceOracleAddress,
functionName: 'l1BaseFee',
})
}

130
node_modules/viem/op-stack/actions/getL2Output.ts generated vendored Normal file
View File

@@ -0,0 +1,130 @@
import {
type ReadContractErrorType,
readContract,
} from '../../actions/public/readContract.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 { Account } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { Hex } from '../../types/misc.js'
import type { OneOf } from '../../types/utils.js'
import { l2OutputOracleAbi } from '../abis.js'
import type { TargetChain } from '../types/chain.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import { type GetGameParameters, getGame } from './getGame.js'
import {
type GetPortalVersionParameters,
getPortalVersion,
} from './getPortalVersion.js'
export type GetL2OutputParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
OneOf<
| GetContractAddressParameter<_derivedChain, 'l2OutputOracle'>
| (GetContractAddressParameter<
_derivedChain,
'portal' | 'disputeGameFactory'
> & {
/**
* Limit of games to extract.
* @default 100
*/
limit?: number | undefined
})
> & {
l2BlockNumber: bigint
}
export type GetL2OutputReturnType = {
outputIndex: bigint
outputRoot: Hex
timestamp: bigint
l2BlockNumber: bigint
}
export type GetL2OutputErrorType = ReadContractErrorType | ErrorType
/**
* Retrieves the first L2 output proposal that occurred after a provided block number.
*
* - Docs: https://viem.sh/op-stack/actions/getL2Output
*
* @param client - Client to use
* @param parameters - {@link GetL2OutputParameters}
* @returns The L2 output. {@link GetL2OutputReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { mainnet, optimism } from 'viem/chains'
* import { getL2Output } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
*
* const output = await getL2Output(publicClientL1, {
* l2BlockNumber: 69420n,
* targetChain: optimism
* })
*/
export async function getL2Output<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: GetL2OutputParameters<chain, chainOverride>,
): Promise<GetL2OutputReturnType> {
const { chain = client.chain, l2BlockNumber, targetChain } = parameters
const version = await getPortalVersion(
client,
parameters as GetPortalVersionParameters,
)
if (version.major >= 3) {
const game = await getGame(client, parameters as GetGameParameters)
return {
l2BlockNumber: game.l2BlockNumber,
outputIndex: game.index,
outputRoot: game.rootClaim,
timestamp: game.timestamp,
}
}
const l2OutputOracleAddress = (() => {
if (parameters.l2OutputOracleAddress)
return parameters.l2OutputOracleAddress
if (chain)
return (targetChain as unknown as TargetChain)!.contracts.l2OutputOracle[
chain.id
].address
return (
Object.values(
(targetChain as unknown as TargetChain)!.contracts.l2OutputOracle,
) as any
)[0].address
})()
const outputIndex = await readContract(client, {
address: l2OutputOracleAddress,
abi: l2OutputOracleAbi,
functionName: 'getL2OutputIndexAfter',
args: [l2BlockNumber],
})
const output = await readContract(client, {
address: l2OutputOracleAddress,
abi: l2OutputOracleAbi,
functionName: 'getL2Output',
args: [outputIndex],
})
return { outputIndex, ...output }
}

87
node_modules/viem/op-stack/actions/getPortalVersion.ts generated vendored Normal file
View File

@@ -0,0 +1,87 @@
import {
type ReadContractErrorType,
readContract,
} from '../../actions/public/readContract.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 {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import { withCache } from '../../utils/promise/withCache.js'
import { portal2Abi } from '../abis.js'
import type { GetContractAddressParameter } from '../types/contract.js'
export type GetPortalVersionParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<_derivedChain, 'portal'>
export type GetPortalVersionReturnType = {
major: number
minor: number
patch: number
}
export type GetPortalVersionErrorType = ReadContractErrorType | ErrorType
/**
* Retrieves the current version of the Portal contract.
*
* - Docs: https://viem.sh/op-stack/actions/getPortalVersion
*
* @param client - Client to use
* @param parameters - {@link GetPortalVersionParameters}
* @returns The version object.
*
* @example
* import { createPublicClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { getPortalVersion } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
*
* const version = await getPortalVersion(publicClientL1, {
* targetChain: optimism,
* })
*
* if (version.major > 3)
* console.log('Fault proofs are enabled on this version of optimism')
* console.log('Fault proofs are not enabled on this version of optimism')
*/
export async function getPortalVersion<
chain extends Chain | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain>,
parameters: GetPortalVersionParameters<chain, chainOverride>,
) {
const { chain = client.chain, targetChain } = parameters
const portalAddress = (() => {
if (parameters.portalAddress) return parameters.portalAddress
if (chain) return targetChain!.contracts.portal[chain.id].address
return Object.values(targetChain!.contracts.portal)[0].address
})()
const version = await withCache(
() =>
readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'version',
}),
{ cacheKey: ['portalVersion', portalAddress].join('.'), cacheTime: 300 },
)
const [major, minor, patch] = version.split('.').map(Number)
return { major, minor, patch }
}

175
node_modules/viem/op-stack/actions/getTimeToFinalize.ts generated vendored Normal file
View File

@@ -0,0 +1,175 @@
import {
type MulticallErrorType,
multicall,
} from '../../actions/public/multicall.js'
import { readContract } from '../../actions/public/readContract.js'
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import { BaseError } from '../../errors/base.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Account } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { Hash } from '../../types/misc.js'
import { l2OutputOracleAbi, portal2Abi, portalAbi } from '../abis.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import { getPortalVersion } from './getPortalVersion.js'
export type GetTimeToFinalizeParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<_derivedChain, 'l2OutputOracle' | 'portal'> & {
withdrawalHash: Hash
}
export type GetTimeToFinalizeReturnType = {
/** The finalization period (in seconds). */
period: number
/** Seconds until the withdrawal can be finalized. */
seconds: number
/** Timestamp of when the withdrawal can be finalized. */
timestamp: number
}
export type GetTimeToFinalizeErrorType = MulticallErrorType | ErrorType
const buffer = 10
/**
* Returns the time until the withdrawal transaction can be finalized. Used for the [Withdrawal](/op-stack/guides/withdrawals) flow.
*
* - Docs: https://viem.sh/op-stack/actions/getTimeToFinalize
*
* @param client - Client to use
* @param parameters - {@link GetTimeToFinalizeParameters}
* @returns Time until finalize. {@link GetTimeToFinalizeReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { getBlockNumber } from 'viem/actions'
* import { mainnet, optimism } from 'viem/chains'
* import { getTimeToFinalize } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const publicClientL2 = createPublicClient({
* chain: optimism,
* transport: http(),
* })
*
* const receipt = await publicClientL2.getTransactionReceipt({
* hash: '0x9a2f4283636ddeb9ac32382961b22c177c9e86dd3b283735c154f897b1a7ff4a',
* })
*
* const [withdrawal] = getWithdrawals(receipt)
*
* const { seconds } = await getTimeToFinalize(publicClientL1, {
* withdrawalHash: withdrawal.withdrawalHash,
* targetChain: optimism
* })
*/
export async function getTimeToFinalize<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: GetTimeToFinalizeParameters<chain, chainOverride>,
): Promise<GetTimeToFinalizeReturnType> {
const { chain = client.chain, withdrawalHash, targetChain } = parameters
const portalAddress = (() => {
if (parameters.portalAddress) return parameters.portalAddress
if (chain) return targetChain!.contracts.portal[chain.id].address
return Object.values(targetChain!.contracts.portal)[0].address
})()
const portalVersion = await getPortalVersion(client, { portalAddress })
// Legacy
if (portalVersion.major < 3) {
const l2OutputOracleAddress = (() => {
if (parameters.l2OutputOracleAddress)
return parameters.l2OutputOracleAddress
if (chain) return targetChain!.contracts.l2OutputOracle[chain.id].address
return Object.values(targetChain!.contracts.l2OutputOracle)[0].address
})()
const [[_outputRoot, proveTimestamp, _l2OutputIndex], period] =
await multicall(client, {
allowFailure: false,
contracts: [
{
abi: portalAbi,
address: portalAddress,
functionName: 'provenWithdrawals',
args: [withdrawalHash],
},
{
abi: l2OutputOracleAbi,
address: l2OutputOracleAddress,
functionName: 'FINALIZATION_PERIOD_SECONDS',
},
],
})
const secondsSinceProven = Date.now() / 1000 - Number(proveTimestamp)
const secondsToFinalize = Number(period) - secondsSinceProven
const seconds = Math.floor(
secondsToFinalize < 0 ? 0 : secondsToFinalize + buffer,
)
const timestamp = Date.now() + seconds * 1000
return { period: Number(period), seconds, timestamp }
}
const numProofSubmitters = await readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'numProofSubmitters',
args: [withdrawalHash],
}).catch(() => 1n)
const proofSubmitter = await readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'proofSubmitters',
args: [withdrawalHash, numProofSubmitters - 1n],
}).catch(() => undefined)
const [[_disputeGameProxy, proveTimestamp], proofMaturityDelaySeconds] =
await Promise.all([
proofSubmitter
? readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'provenWithdrawals',
args: [withdrawalHash, proofSubmitter],
})
: Promise.resolve(['0x', 0n]),
readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'proofMaturityDelaySeconds',
}),
])
if (proveTimestamp === 0n)
throw new BaseError('Withdrawal has not been proven on L1.')
const secondsSinceProven = Date.now() / 1000 - Number(proveTimestamp)
const secondsToFinalize =
Number(proofMaturityDelaySeconds) - secondsSinceProven
const seconds = Math.floor(
secondsToFinalize < 0n ? 0 : secondsToFinalize + buffer,
)
const timestamp = Date.now() + seconds * 1000
return { period: Number(proofMaturityDelaySeconds), seconds, timestamp }
}

157
node_modules/viem/op-stack/actions/getTimeToNextGame.ts generated vendored Normal file
View File

@@ -0,0 +1,157 @@
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Account } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import { type GetGamesErrorType, getGames } from './getGames.js'
export type GetTimeToNextGameParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<
_derivedChain,
'portal' | 'disputeGameFactory'
> & {
/**
* The buffer to account for discrepancies between non-deterministic time intervals.
* @default 1.1
*/
intervalBuffer?: number | undefined
/**
* The minimum L2 block number of the next game.
*/
l2BlockNumber: bigint
}
export type GetTimeToNextGameReturnType = {
/** The estimated interval (in seconds) between L2 dispute games. */
interval: number
/**
* Estimated seconds until the next L2 dispute game.
* `0` if the next L2 dispute game has already been submitted.
*/
seconds: number
/**
* Estimated timestamp of the next L2 dispute game.
* `undefined` if the next L2 dispute game has already been submitted.
*/
timestamp?: number | undefined
}
export type GetTimeToNextGameErrorType = GetGamesErrorType | ErrorType
/**
* Returns the time until the next L2 dispute game (after the provided block number) is submitted.
* Used for the [Withdrawal](/op-stack/guides/withdrawals) flow.
*
* - Docs: https://viem.sh/op-stack/actions/getTimeToNextGame
*
* @param client - Client to use
* @param parameters - {@link GetTimeToNextGameParameters}
* @returns The L2 transaction hash. {@link GetTimeToNextGameReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { getBlockNumber } from 'viem/actions'
* import { mainnet, optimism } from 'viem/chains'
* import { getTimeToNextGame } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
*
* const { seconds } = await getTimeToNextGame(publicClientL1, {
* l2BlockNumber: 113405763n,
* targetChain: optimism
* })
*/
export async function getTimeToNextGame<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: GetTimeToNextGameParameters<chain, chainOverride>,
): Promise<GetTimeToNextGameReturnType> {
const { intervalBuffer = 1.1, l2BlockNumber } = parameters
const games = await getGames(client, {
...parameters,
l2BlockNumber: undefined,
limit: 10,
})
if (games.length === 0) {
return { interval: 0, seconds: 0, timestamp: undefined }
}
const deltas = games
.map(({ l2BlockNumber, timestamp }, index) => {
return index === games.length - 1
? null
: [
games[index + 1].timestamp - timestamp,
games[index + 1].l2BlockNumber - l2BlockNumber,
]
})
.filter(Boolean)
const interval =
deltas.length > 0
? Math.ceil(
(deltas as [bigint, bigint][]).reduce(
(a, [b]) => Number(a) - Number(b),
0,
) / deltas.length,
)
: 0
const blockInterval =
deltas.length > 0
? Math.ceil(
(deltas as [bigint, bigint][]).reduce(
(a, [_, b]) => Number(a) - Number(b),
0,
) / deltas.length,
)
: 0
const latestGame = games[0]
const latestGameTimestamp = Number(latestGame.timestamp) * 1000
const intervalWithBuffer = Math.ceil(interval * intervalBuffer)
const now = Date.now()
const seconds = (() => {
// If the current timestamp is lesser than the latest dispute game timestamp,
// then we assume that the dispute game has already been submitted.
if (now < latestGameTimestamp) return 0
// If the latest dispute game block is newer than the provided dispute game block number,
// then we assume that the dispute game has already been submitted.
if (latestGame.l2BlockNumber > l2BlockNumber) return 0
// If there is only a single game, no interval data
if (intervalWithBuffer === 0) return 0
const elapsedBlocks = Number(l2BlockNumber - latestGame.l2BlockNumber)
const elapsed = Math.ceil((now - latestGameTimestamp) / 1000)
const secondsToNextOutput =
intervalWithBuffer - (elapsed % intervalWithBuffer)
return elapsedBlocks < blockInterval
? secondsToNextOutput
: Math.floor(elapsedBlocks / Number(blockInterval)) * intervalWithBuffer +
secondsToNextOutput
})()
const timestamp = seconds > 0 ? now + seconds * 1000 : undefined
return { interval, seconds, timestamp }
}

View File

@@ -0,0 +1,162 @@
import {
type MulticallErrorType,
multicall,
} from '../../actions/public/multicall.js'
import {
type ReadContractErrorType,
readContract,
} from '../../actions/public/readContract.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 { Account } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import { l2OutputOracleAbi } from '../abis.js'
import type { GetContractAddressParameter } from '../types/contract.js'
export type GetTimeToNextL2OutputParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<_derivedChain, 'l2OutputOracle'> & {
/**
* The buffer to account for discrepancies between non-deterministic time intervals.
* @default 1.1
*/
intervalBuffer?: number | undefined
l2BlockNumber: bigint
}
export type GetTimeToNextL2OutputReturnType = {
/** The interval (in seconds) between L2 outputs. */
interval: number
/**
* Seconds until the next L2 output.
* `0` if the next L2 output has already been submitted.
*/
seconds: number
/**
* Estimated timestamp of the next L2 output.
* `undefined` if the next L2 output has already been submitted.
*/
timestamp?: number | undefined
}
export type GetTimeToNextL2OutputErrorType =
| MulticallErrorType
| ReadContractErrorType
| ErrorType
/**
* Returns the time until the next L2 output (after the provided block number) is submitted. Used for the [Withdrawal](/op-stack/guides/withdrawals) flow.
*
* - Docs: https://viem.sh/op-stack/actions/getTimeToNextL2Output
*
* @param client - Client to use
* @param parameters - {@link GetTimeToNextL2OutputParameters}
* @returns The L2 transaction hash. {@link GetTimeToNextL2OutputReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { getBlockNumber } from 'viem/actions'
* import { mainnet, optimism } from 'viem/chains'
* import { getTimeToNextL2Output } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const publicClientL2 = createPublicClient({
* chain: optimism,
* transport: http(),
* })
*
* const { seconds } = await getTimeToNextL2Output(publicClientL1, {
* targetChain: optimism
* })
*/
export async function getTimeToNextL2Output<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: GetTimeToNextL2OutputParameters<chain, chainOverride>,
): Promise<GetTimeToNextL2OutputReturnType> {
const {
intervalBuffer = 1.1,
chain = client.chain,
l2BlockNumber,
targetChain,
} = parameters
const l2OutputOracleAddress = (() => {
if (parameters.l2OutputOracleAddress)
return parameters.l2OutputOracleAddress
if (chain) return targetChain!.contracts.l2OutputOracle[chain.id].address
return Object.values(targetChain!.contracts.l2OutputOracle)[0].address
})()
const [latestOutputIndex, blockTime, blockInterval] = await multicall(
client,
{
allowFailure: false,
contracts: [
{
abi: l2OutputOracleAbi,
address: l2OutputOracleAddress,
functionName: 'latestOutputIndex',
},
{
abi: l2OutputOracleAbi,
address: l2OutputOracleAddress,
functionName: 'L2_BLOCK_TIME',
},
{
abi: l2OutputOracleAbi,
address: l2OutputOracleAddress,
functionName: 'SUBMISSION_INTERVAL',
},
],
},
)
const latestOutput = await readContract(client, {
abi: l2OutputOracleAbi,
address: l2OutputOracleAddress,
functionName: 'getL2Output',
args: [latestOutputIndex],
})
const latestOutputTimestamp = Number(latestOutput.timestamp) * 1000
const interval = Number(blockInterval * blockTime)
const intervalWithBuffer = Math.ceil(interval * intervalBuffer)
const now = Date.now()
const seconds = (() => {
// If the current timestamp is lesser than the latest L2 output timestamp,
// then we assume that the L2 output has already been submitted.
if (now < latestOutputTimestamp) return 0
// If the latest L2 output block is newer than the provided L2 block number,
// then we assume that the L2 output has already been submitted.
if (latestOutput.l2BlockNumber > l2BlockNumber) return 0
const elapsedBlocks = Number(l2BlockNumber - latestOutput.l2BlockNumber)
const elapsed = Math.ceil((now - latestOutputTimestamp) / 1000)
const secondsToNextOutput =
intervalWithBuffer - (elapsed % intervalWithBuffer)
return elapsedBlocks < blockInterval
? secondsToNextOutput
: Math.floor(elapsedBlocks / Number(blockInterval)) * intervalWithBuffer +
secondsToNextOutput
})()
const timestamp = seconds > 0 ? now + seconds * 1000 : undefined
return { interval, seconds, timestamp }
}

119
node_modules/viem/op-stack/actions/getTimeToProve.ts generated vendored Normal file
View File

@@ -0,0 +1,119 @@
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Account } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { TransactionReceipt } from '../../types/transaction.js'
import type { OneOf } from '../../types/utils.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import {
type GetPortalVersionErrorType,
type GetPortalVersionParameters,
getPortalVersion,
} from './getPortalVersion.js'
import {
type GetTimeToNextGameErrorType,
type GetTimeToNextGameParameters,
type GetTimeToNextGameReturnType,
getTimeToNextGame,
} from './getTimeToNextGame.js'
import {
type GetTimeToNextL2OutputErrorType,
type GetTimeToNextL2OutputParameters,
type GetTimeToNextL2OutputReturnType,
getTimeToNextL2Output,
} from './getTimeToNextL2Output.js'
export type GetTimeToProveParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
OneOf<
| GetContractAddressParameter<_derivedChain, 'l2OutputOracle'>
| GetContractAddressParameter<
_derivedChain,
'disputeGameFactory' | 'portal'
>
> & {
/**
* The buffer to account for discrepancies between non-deterministic time intervals.
* @default 1.1
*/
intervalBuffer?:
| GetTimeToNextL2OutputParameters['intervalBuffer']
| undefined
receipt: TransactionReceipt
}
export type GetTimeToProveReturnType =
| GetTimeToNextGameReturnType
| GetTimeToNextL2OutputReturnType
export type GetTimeToProveErrorType =
| GetPortalVersionErrorType
| GetTimeToNextGameErrorType
| GetTimeToNextL2OutputErrorType
| ErrorType
/**
* Returns the time until the withdrawal transaction is ready to prove. Used for the [Withdrawal](/op-stack/guides/withdrawals) flow.
*
* - Docs: https://viem.sh/op-stack/actions/getTimeToProve
*
* @param client - Client to use
* @param parameters - {@link GetTimeToNextL2OutputParameters}
* @returns Time until prove step is ready. {@link GetTimeToNextL2OutputReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { getBlockNumber } from 'viem/actions'
* import { mainnet, optimism } from 'viem/chains'
* import { getTimeToProve } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const publicClientL2 = createPublicClient({
* chain: optimism,
* transport: http(),
* })
*
* const receipt = await publicClientL2.getTransactionReceipt({ hash: '0x...' })
* const { period, seconds, timestamp } = await getTimeToProve(publicClientL1, {
* receipt,
* targetChain: optimism
* })
*/
export async function getTimeToProve<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: GetTimeToProveParameters<chain, chainOverride>,
): Promise<GetTimeToProveReturnType> {
const { receipt } = parameters
const portalVersion = await getPortalVersion(
client,
parameters as GetPortalVersionParameters,
)
// Legacy
if (portalVersion.major < 3)
return getTimeToNextL2Output(client, {
...parameters,
l2BlockNumber: receipt.blockNumber,
} as GetTimeToNextL2OutputParameters)
return getTimeToNextGame(client, {
...parameters,
l2BlockNumber: receipt.blockNumber,
} as GetTimeToNextGameParameters)
}

View File

@@ -0,0 +1,419 @@
import type { Address } from 'abitype'
import {
type ReadContractErrorType,
readContract,
} from '../../actions/public/readContract.js'
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import { ContractFunctionRevertedError } from '../../errors/contract.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Account } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { Hash } from '../../types/misc.js'
import type { TransactionReceipt } from '../../types/transaction.js'
import type { OneOf } from '../../types/utils.js'
import { anchorStateRegistryAbi, portal2Abi, portalAbi } from '../abis.js'
import {
ReceiptContainsNoWithdrawalsError,
type ReceiptContainsNoWithdrawalsErrorType,
} from '../errors/withdrawal.js'
import type { TargetChain } from '../types/chain.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import {
type GetWithdrawalsErrorType,
getWithdrawals,
} from '../utils/getWithdrawals.js'
import {
type GetGameErrorType,
type GetGameParameters,
getGame,
} from './getGame.js'
import {
type GetL2OutputErrorType,
type GetL2OutputParameters,
getL2Output,
} from './getL2Output.js'
import {
type GetPortalVersionParameters,
getPortalVersion,
} from './getPortalVersion.js'
import {
type GetTimeToFinalizeErrorType,
type GetTimeToFinalizeParameters,
getTimeToFinalize,
} from './getTimeToFinalize.js'
export type GetWithdrawalStatusParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
OneOf<
| GetContractAddressParameter<_derivedChain, 'l2OutputOracle' | 'portal'>
| GetContractAddressParameter<
_derivedChain,
'disputeGameFactory' | 'portal'
>
> & {
/**
* Limit of games to extract to check withdrawal status.
* @default 100
*/
gameLimit?: number
} & OneOf<
| {
/**
* The relative index of the withdrawal in the transaction receipt logs.
* @default 0
*/
logIndex?: number
/**
* The transaction receipt of the withdrawal.
*/
receipt: TransactionReceipt
}
| {
/**
* The L2 block number of the withdrawal.
*/
l2BlockNumber: bigint
/**
* The sender of the withdrawal.
*/
sender: Address
/**
* The hash of the withdrawal.
*/
withdrawalHash: Hash
}
>
export type GetWithdrawalStatusReturnType =
| 'waiting-to-prove'
| 'ready-to-prove'
| 'waiting-to-finalize'
| 'ready-to-finalize'
| 'finalized'
export type GetWithdrawalStatusErrorType =
| GetL2OutputErrorType
| GetTimeToFinalizeErrorType
| GetWithdrawalsErrorType
| ReadContractErrorType
| ReceiptContainsNoWithdrawalsErrorType
| ErrorType
/**
* Returns the current status of a withdrawal. Used for the [Withdrawal](/op-stack/guides/withdrawals) flow.
*
* - Docs: https://viem.sh/op-stack/actions/getWithdrawalStatus
*
* @param client - Client to use
* @param parameters - {@link GetWithdrawalStatusParameters}
* @returns Status of the withdrawal. {@link GetWithdrawalStatusReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { getBlockNumber } from 'viem/actions'
* import { mainnet, optimism } from 'viem/chains'
* import { getWithdrawalStatus } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const publicClientL2 = createPublicClient({
* chain: optimism,
* transport: http(),
* })
*
* const receipt = await publicClientL2.getTransactionReceipt({ hash: '0x...' })
* const status = await getWithdrawalStatus(publicClientL1, {
* receipt,
* targetChain: optimism
* })
*/
export async function getWithdrawalStatus<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: GetWithdrawalStatusParameters<chain, chainOverride>,
): Promise<GetWithdrawalStatusReturnType> {
const {
chain = client.chain,
gameLimit = 100,
receipt,
targetChain: targetChain_,
logIndex = 0,
} = parameters
const targetChain = targetChain_ as unknown as TargetChain
const portalAddress = (() => {
if (parameters.portalAddress) return parameters.portalAddress
if (chain) return targetChain.contracts.portal[chain.id].address
return Object.values(targetChain.contracts.portal)[0].address
})()
const l2BlockNumber = receipt?.blockNumber ?? parameters.l2BlockNumber
const withdrawal = (() => {
if (receipt) {
const withdrawal = getWithdrawals({ logs: receipt.logs })[logIndex]
if (!withdrawal)
throw new ReceiptContainsNoWithdrawalsError({
hash: receipt.transactionHash,
})
return withdrawal
}
return {
sender: parameters.sender,
withdrawalHash: parameters.withdrawalHash,
}
})()
const portalVersion = await getPortalVersion(
client,
parameters as GetPortalVersionParameters,
)
// Legacy (Portal < v3)
if (portalVersion.major < 3) {
const [outputResult, proveResult, finalizedResult, timeToFinalizeResult] =
await Promise.allSettled([
getL2Output(client, {
...parameters,
l2BlockNumber,
} as GetL2OutputParameters),
readContract(client, {
abi: portalAbi,
address: portalAddress,
functionName: 'provenWithdrawals',
args: [withdrawal.withdrawalHash],
}),
readContract(client, {
abi: portalAbi,
address: portalAddress,
functionName: 'finalizedWithdrawals',
args: [withdrawal.withdrawalHash],
}),
getTimeToFinalize(client, {
...parameters,
withdrawalHash: withdrawal.withdrawalHash,
} as GetTimeToFinalizeParameters),
])
// If the L2 Output is not processed yet (ie. the actions throws), this means
// that the withdrawal is not ready to prove.
if (outputResult.status === 'rejected') {
const error = outputResult.reason as GetL2OutputErrorType
if (
error.cause instanceof ContractFunctionRevertedError &&
error.cause.data?.args?.[0] ===
'L2OutputOracle: cannot get output for a block that has not been proposed'
)
return 'waiting-to-prove'
throw error
}
if (proveResult.status === 'rejected') throw proveResult.reason
if (finalizedResult.status === 'rejected') throw finalizedResult.reason
if (timeToFinalizeResult.status === 'rejected')
throw timeToFinalizeResult.reason
const [_, proveTimestamp] = proveResult.value
if (!proveTimestamp) return 'ready-to-prove'
const finalized = finalizedResult.value
if (finalized) return 'finalized'
const { seconds } = timeToFinalizeResult.value
return seconds > 0 ? 'waiting-to-finalize' : 'ready-to-finalize'
}
const numProofSubmitters = await readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'numProofSubmitters',
args: [withdrawal.withdrawalHash],
}).catch(() => 1n)
const proofSubmitter = await readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'proofSubmitters',
args: [withdrawal.withdrawalHash, numProofSubmitters - 1n],
}).catch(() => withdrawal.sender)
const [
disputeGameResult,
provenWithdrawalsResult,
checkWithdrawalResult,
finalizedResult,
] = await Promise.allSettled([
getGame(client, {
...parameters,
l2BlockNumber,
limit: gameLimit,
} as GetGameParameters),
readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'provenWithdrawals',
args: [withdrawal.withdrawalHash, proofSubmitter],
}),
readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'checkWithdrawal',
args: [withdrawal.withdrawalHash, proofSubmitter],
}),
readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'finalizedWithdrawals',
args: [withdrawal.withdrawalHash],
}),
])
if (finalizedResult.status === 'fulfilled' && finalizedResult.value)
return 'finalized'
if (provenWithdrawalsResult.status === 'rejected')
throw provenWithdrawalsResult.reason
if (disputeGameResult.status === 'rejected') {
const error = disputeGameResult.reason as GetGameErrorType
if (error.name === 'GameNotFoundError') return 'waiting-to-prove'
throw disputeGameResult.reason
}
if (checkWithdrawalResult.status === 'rejected') {
const error = checkWithdrawalResult.reason as ReadContractErrorType
if (error.cause instanceof ContractFunctionRevertedError) {
// All potential error causes listed here, can either be the error string or the error name
// if custom error types are returned.
const errorCauses = {
'ready-to-prove': [
'OptimismPortal: invalid game type',
'OptimismPortal: withdrawal has not been proven yet',
'OptimismPortal: withdrawal has not been proven by proof submitter address yet',
'OptimismPortal: dispute game created before respected game type was updated',
'InvalidGameType',
'LegacyGame',
'Unproven',
// After U16
'OptimismPortal_Unproven',
'OptimismPortal_InvalidProofTimestamp',
],
'waiting-to-finalize': [
'OptimismPortal: proven withdrawal has not matured yet',
'OptimismPortal: output proposal has not been finalized yet',
'OptimismPortal: output proposal in air-gap',
],
}
// Pick out the error message and/or error name
// Return the status based on the error
const errors = [
error.cause.data?.errorName,
error.cause.data?.args?.[0] as string,
]
// After U16 we get a generic error message (OptimismPortal_InvalidRootClaim) because the
// OptimismPortal will call AnchorStateRegistry.isGameClaimValid which simply returns
// true/false. If we get this generic error, we need to figure out why the function returned
// false and return a proper status accordingly. We can also check these conditions when we
// get ProofNotOldEnough so users can be notified when their pending proof becomes invalid
// before it can be finalized.
if (
errors.includes('OptimismPortal_InvalidRootClaim') ||
errors.includes('OptimismPortal_ProofNotOldEnough')
) {
// Get the dispute game address from the proven withdrawal.
const disputeGameAddress = provenWithdrawalsResult.value[0] as Address
// Get the AnchorStateRegistry address from the portal.
const anchorStateRegistry = await readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'anchorStateRegistry',
})
// Check if the game is proper, respected, and finalized.
const [
isGameProperResult,
isGameRespectedResult,
isGameFinalizedResult,
] = await Promise.allSettled([
readContract(client, {
abi: anchorStateRegistryAbi,
address: anchorStateRegistry,
functionName: 'isGameProper',
args: [disputeGameAddress],
}),
readContract(client, {
abi: anchorStateRegistryAbi,
address: anchorStateRegistry,
functionName: 'isGameRespected',
args: [disputeGameAddress],
}),
readContract(client, {
abi: anchorStateRegistryAbi,
address: anchorStateRegistry,
functionName: 'isGameFinalized',
args: [disputeGameAddress],
}),
])
// If any of the calls failed, throw the error.
if (isGameProperResult.status === 'rejected')
throw isGameProperResult.reason
if (isGameRespectedResult.status === 'rejected')
throw isGameRespectedResult.reason
if (isGameFinalizedResult.status === 'rejected')
throw isGameFinalizedResult.reason
// If the game isn't proper, the user needs to re-prove.
if (!isGameProperResult.value) {
return 'ready-to-prove'
}
// If the game isn't respected, the user needs to re-prove.
if (!isGameRespectedResult.value) {
return 'ready-to-prove'
}
// If the game isn't finalized, the user needs to wait to finalize.
if (!isGameFinalizedResult.value) {
return 'waiting-to-finalize'
}
// If the actual error was ProofNotOldEnough, then at this point the game is probably
// completely fine but the proof hasn't passed the waiting period. Otherwise, the only
// reason we'd be here is if the game resolved in favor of the challenger, which means the
// user needs to re-prove the withdrawal.
if (errors.includes('OptimismPortal_ProofNotOldEnough')) {
return 'waiting-to-finalize'
}
return 'ready-to-prove'
}
if (errorCauses['ready-to-prove'].some((cause) => errors.includes(cause)))
return 'ready-to-prove'
if (
errorCauses['waiting-to-finalize'].some((cause) =>
errors.includes(cause),
)
)
return 'waiting-to-finalize'
}
throw checkWithdrawalResult.reason
}
if (finalizedResult.status === 'rejected') throw finalizedResult.reason
return 'ready-to-finalize'
}

View File

@@ -0,0 +1,141 @@
import type { Address } from 'abitype'
import {
type WriteContractErrorType,
type WriteContractParameters,
writeContract,
} from '../../actions/wallet/writeContract.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 { Account, GetAccountParameter } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { Hash } from '../../types/misc.js'
import type { UnionEvaluate, UnionOmit } from '../../types/utils.js'
import type { FormattedTransactionRequest } from '../../utils/formatters/transactionRequest.js'
import { l2ToL1MessagePasserAbi } from '../abis.js'
import { contracts } from '../contracts.js'
import type { WithdrawalRequest } from '../types/withdrawal.js'
import type { EstimateInitiateWithdrawalGasErrorType } from './estimateInitiateWithdrawalGas.js'
export type InitiateWithdrawalParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = UnionEvaluate<
UnionOmit<
FormattedTransactionRequest<_derivedChain>,
| 'accessList'
| 'data'
| 'from'
| 'gas'
| 'gasPrice'
| 'to'
| 'type'
| 'value'
>
> &
GetAccountParameter<account, Account | Address> &
GetChainParameter<chain, chainOverride> & {
/**
* Gas limit for transaction execution on the L2.
* `null` to skip gas estimation & defer calculation to signer.
*/
gas?: bigint | null
/** Withdrawal request. Supplied to the L2ToL1MessagePasser `initiateWithdrawal` method. */
request: WithdrawalRequest
}
export type InitiateWithdrawalReturnType = Hash
export type InitiateWithdrawalErrorType =
| EstimateInitiateWithdrawalGasErrorType
| WriteContractErrorType
| ErrorType
/**
* Initiates a [withdrawal](https://community.optimism.io/docs/protocol/withdrawal-flow/#withdrawal-initiating-transaction) on an L2 to the L1.
*
* Internally performs a contract write to the [`initiateWithdrawal` function](https://github.com/ethereum-optimism/optimism/blob/283f0aa2e3358ced30ff7cbd4028c0c0c3faa140/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol#L73)
* on the [Optimism L2ToL1MessagePasser predeploy contract](https://github.com/ethereum-optimism/optimism/blob/283f0aa2e3358ced30ff7cbd4028c0c0c3faa140/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol).
*
* - Docs: https://viem.sh/op-stack/actions/initiateWithdrawal
*
* @param client - Client to use
* @param parameters - {@link InitiateWithdrawalParameters}
* @returns The L2 transaction hash. {@link InitiateWithdrawalReturnType}
*
* @example
* import { createWalletClient, custom, parseEther } from 'viem'
* import { base, mainnet } from 'viem/chains'
* import { initiateWithdrawal } from 'viem/op-stack'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* })
*
* const hash = await initiateWithdrawal(client, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* args: {
* gas: 21_000n,
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* })
*
* @example
* // Account Hoisting
* import { createWalletClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { base, mainnet } from 'viem/chains'
* import { initiateWithdrawal } from 'viem/op-stack'
*
* const client = createWalletClient({
* account: privateKeyToAccount('0x…'),
* chain: mainnet,
* transport: http(),
* })
*
* const hash = await initiateWithdrawal(client, {
* request: {
* gas: 21_000n,
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* })
*/
export async function initiateWithdrawal<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: InitiateWithdrawalParameters<chain, account, chainOverride>,
) {
const {
account,
chain = client.chain,
gas,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
request: { data = '0x', gas: l1Gas, to, value },
} = parameters
return writeContract(client, {
account: account!,
abi: l2ToL1MessagePasserAbi,
address: contracts.l2ToL1MessagePasser.address,
chain,
functionName: 'initiateWithdrawal',
args: [to, l1Gas, data],
gas: gas ?? undefined,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
value,
} satisfies WriteContractParameters as any)
}

142
node_modules/viem/op-stack/actions/proveWithdrawal.ts generated vendored Normal file
View File

@@ -0,0 +1,142 @@
import type { Address } from 'abitype'
import {
type WriteContractErrorType,
type WriteContractParameters,
writeContract,
} from '../../actions/wallet/writeContract.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 { Account, GetAccountParameter } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { Hash, Hex } from '../../types/misc.js'
import type { UnionEvaluate, UnionOmit } from '../../types/utils.js'
import type { FormattedTransactionRequest } from '../../utils/formatters/transactionRequest.js'
import { portal2Abi } from '../abis.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import type { EstimateProveWithdrawalGasErrorType } from './estimateProveWithdrawalGas.js'
export type ProveWithdrawalParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = UnionEvaluate<
UnionOmit<
FormattedTransactionRequest<_derivedChain>,
| 'accessList'
| 'blobs'
| 'data'
| 'from'
| 'gas'
| 'maxFeePerBlobGas'
| 'gasPrice'
| 'to'
| 'type'
| 'value'
>
> &
GetAccountParameter<account, Account | Address> &
GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<_derivedChain, 'portal'> & {
/**
* Gas limit for transaction execution on the L1.
* `null` to skip gas estimation & defer calculation to signer.
*/
gas?: bigint | null | undefined
l2OutputIndex: bigint
outputRootProof: {
version: Hex
stateRoot: Hex
messagePasserStorageRoot: Hex
latestBlockhash: Hex
}
withdrawalProof: readonly Hex[]
withdrawal: {
data: Hex
gasLimit: bigint
nonce: bigint
sender: Address
target: Address
value: bigint
}
}
export type ProveWithdrawalReturnType = Hash
export type ProveWithdrawalErrorType =
| EstimateProveWithdrawalGasErrorType
| WriteContractErrorType
| ErrorType
/**
* Proves a withdrawal that occurred on an L2. Used in the Withdrawal flow.
*
* - Docs: https://viem.sh/op-stack/actions/proveWithdrawal
*
* @param client - Client to use
* @param parameters - {@link ProveWithdrawalParameters}
* @returns The prove transaction hash. {@link ProveWithdrawalReturnType}
*
* @example
* import { createWalletClient, http } from 'viem'
* import { mainnet, optimism } from 'viem/chains'
* import { proveWithdrawal } from 'viem/op-stack'
*
* const walletClientL1 = createWalletClient({
* chain: mainnet,
* transport: http(),
* })
*
* const request = await proveWithdrawal(walletClientL1, {
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* l2OutputIndex: 4529n,
* outputRootProof: { ... },
* targetChain: optimism,
* withdrawalProof: [ ... ],
* withdrawal: { ... },
* })
*/
export async function proveWithdrawal<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: ProveWithdrawalParameters<chain, account, chainOverride>,
): Promise<ProveWithdrawalReturnType> {
const {
account,
chain = client.chain,
gas,
l2OutputIndex,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
outputRootProof,
targetChain,
withdrawalProof,
withdrawal,
} = parameters
const portalAddress = (() => {
if (parameters.portalAddress) return parameters.portalAddress
if (chain) return targetChain!.contracts.portal[chain.id].address
return Object.values(targetChain!.contracts.portal)[0].address
})()
return writeContract(client, {
account: account!,
abi: portal2Abi,
address: portalAddress,
chain,
functionName: 'proveWithdrawalTransaction',
args: [withdrawal, l2OutputIndex, outputRootProof, withdrawalProof],
gas: gas ?? undefined,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
} satisfies WriteContractParameters as any)
}

122
node_modules/viem/op-stack/actions/waitForNextGame.ts generated vendored Normal file
View File

@@ -0,0 +1,122 @@
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Account } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import { poll } from '../../utils/poll.js'
import { GameNotFoundError } from '../errors/withdrawal.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import {
type GetGameErrorType,
type GetGameReturnType,
getGame,
} from './getGame.js'
import {
type GetTimeToNextGameErrorType,
type GetTimeToNextGameParameters,
getTimeToNextGame,
} from './getTimeToNextGame.js'
export type WaitForNextGameParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<
_derivedChain,
'portal' | 'disputeGameFactory'
> & {
/**
* Limit of games to extract.
* @default 100
*/
limit?: number | undefined
/**
* The buffer to account for discrepancies between non-deterministic time intervals.
* @default 1.1
*/
intervalBuffer?: GetTimeToNextGameParameters['intervalBuffer'] | undefined
l2BlockNumber: bigint
/**
* Polling frequency (in ms). Defaults to Client's pollingInterval config.
* @default client.pollingInterval
*/
pollingInterval?: number | undefined
}
export type WaitForNextGameReturnType = GetGameReturnType
export type WaitForNextGameErrorType =
| GetGameErrorType
| GetTimeToNextGameErrorType
| ErrorType
/**
* Waits for the next dispute game (after the provided block number) to be submitted.
*
* - Docs: https://viem.sh/op-stack/actions/waitForNextGame
*
* @param client - Client to use
* @param parameters - {@link WaitForNextGameParameters}
* @returns The L2 transaction hash. {@link WaitForNextGameReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { getBlockNumber } from 'viem/actions'
* import { mainnet, optimism } from 'viem/chains'
* import { waitForNextGame } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const publicClientL2 = createPublicClient({
* chain: optimism,
* transport: http(),
* })
*
* const l2BlockNumber = await getBlockNumber(publicClientL2)
* await waitForNextGame(publicClientL1, {
* l2BlockNumber,
* targetChain: optimism
* })
*/
export async function waitForNextGame<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: WaitForNextGameParameters<chain, chainOverride>,
): Promise<WaitForNextGameReturnType> {
const { pollingInterval = client.pollingInterval } = parameters
const { seconds } = await getTimeToNextGame(client, parameters)
return new Promise((resolve, reject) => {
poll(
async ({ unpoll }) => {
try {
const game = await getGame(client, {
...parameters,
strategy: 'random',
})
unpoll()
resolve(game)
} catch (e) {
const error = e as GetGameErrorType
if (!(error instanceof GameNotFoundError)) {
unpoll()
reject(e)
}
}
},
{
interval: pollingInterval,
initialWaitTime: async () => seconds * 1000,
},
)
})
}

View File

@@ -0,0 +1,117 @@
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import { ContractFunctionRevertedError } from '../../errors/contract.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Account } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import { poll } from '../../utils/poll.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import {
type GetL2OutputErrorType,
type GetL2OutputParameters,
type GetL2OutputReturnType,
getL2Output,
} from './getL2Output.js'
import {
type GetTimeToNextL2OutputErrorType,
type GetTimeToNextL2OutputParameters,
getTimeToNextL2Output,
} from './getTimeToNextL2Output.js'
export type WaitForNextL2OutputParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
GetContractAddressParameter<_derivedChain, 'l2OutputOracle'> & {
/**
* The buffer to account for discrepancies between non-deterministic time intervals.
* @default 1.1
*/
intervalBuffer?:
| GetTimeToNextL2OutputParameters['intervalBuffer']
| undefined
l2BlockNumber: bigint
/**
* Polling frequency (in ms). Defaults to Client's pollingInterval config.
* @default client.pollingInterval
*/
pollingInterval?: number | undefined
}
export type WaitForNextL2OutputReturnType = GetL2OutputReturnType
export type WaitForNextL2OutputErrorType =
| GetL2OutputErrorType
| GetTimeToNextL2OutputErrorType
| ErrorType
/**
* Waits for the next L2 output (after the provided block number) to be submitted.
*
* - Docs: https://viem.sh/op-stack/actions/waitForNextL2Output
*
* @param client - Client to use
* @param parameters - {@link WaitForNextL2OutputParameters}
* @returns The L2 transaction hash. {@link WaitForNextL2OutputReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { getBlockNumber } from 'viem/actions'
* import { mainnet, optimism } from 'viem/chains'
* import { waitForNextL2Output } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const publicClientL2 = createPublicClient({
* chain: optimism,
* transport: http(),
* })
*
* const l2BlockNumber = await getBlockNumber(publicClientL2)
* await waitForNextL2Output(publicClientL1, {
* l2BlockNumber,
* targetChain: optimism
* })
*/
export async function waitForNextL2Output<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: WaitForNextL2OutputParameters<chain, chainOverride>,
): Promise<WaitForNextL2OutputReturnType> {
const { pollingInterval = client.pollingInterval } = parameters
const { seconds } = await getTimeToNextL2Output(client, parameters)
return new Promise((resolve, reject) => {
poll(
async ({ unpoll }) => {
try {
const output = await getL2Output(
client,
parameters as GetL2OutputParameters,
)
unpoll()
resolve(output)
} catch (e) {
const error = e as GetL2OutputErrorType
if (!(error.cause instanceof ContractFunctionRevertedError)) {
unpoll()
reject(e)
}
}
},
{
interval: pollingInterval,
initialWaitTime: async () => seconds * 1000,
},
)
})
}

64
node_modules/viem/op-stack/actions/waitToFinalize.ts generated vendored Normal file
View File

@@ -0,0 +1,64 @@
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Account } from '../../types/account.js'
import type { Chain } from '../../types/chain.js'
import { wait } from '../../utils/wait.js'
import {
type GetTimeToFinalizeErrorType,
type GetTimeToFinalizeParameters,
getTimeToFinalize,
} from './getTimeToFinalize.js'
export type WaitToFinalizeParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
> = GetTimeToFinalizeParameters<chain, chainOverride>
export type WaitToFinalizeReturnType = void
export type WaitToFinalizeErrorType = GetTimeToFinalizeErrorType | ErrorType
/**
* Waits until the withdrawal transaction can be finalized. Used for the [Withdrawal](/op-stack/guides/withdrawals) flow.
*
* - Docs: https://viem.sh/op-stack/actions/waitToFinalize
*
* @param client - Client to use
* @param parameters - {@link WaitToFinalizeParameters}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { getBlockNumber } from 'viem/actions'
* import { mainnet, optimism } from 'viem/chains'
* import { waitToFinalize } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const publicClientL2 = createPublicClient({
* chain: optimism,
* transport: http(),
* })
*
* const receipt = await publicClientL2.getTransactionReceipt({
* hash: '0x9a2f4283636ddeb9ac32382961b22c177c9e86dd3b283735c154f897b1a7ff4a',
* })
*
* const [withdrawal] = getWithdrawals(receipt)
*
* await waitToFinalize(publicClientL1, {
* withdrawalHash: withdrawal.withdrawalHash,
* targetChain: optimism
* })
*/
export async function waitToFinalize<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: WaitToFinalizeParameters<chain, chainOverride>,
): Promise<WaitToFinalizeReturnType> {
const { seconds } = await getTimeToFinalize(client, parameters)
await wait(seconds * 1000)
}

157
node_modules/viem/op-stack/actions/waitToProve.ts generated vendored Normal file
View File

@@ -0,0 +1,157 @@
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Account } from '../../types/account.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../types/chain.js'
import type { TransactionReceipt } from '../../types/transaction.js'
import type { OneOf } from '../../types/utils.js'
import { ReceiptContainsNoWithdrawalsError } from '../errors/withdrawal.js'
import type { GetContractAddressParameter } from '../types/contract.js'
import type { Withdrawal } from '../types/withdrawal.js'
import {
type GetWithdrawalsErrorType,
getWithdrawals,
} from '../utils/getWithdrawals.js'
import {
type GetPortalVersionParameters,
getPortalVersion,
} from './getPortalVersion.js'
import {
type WaitForNextGameParameters,
type WaitForNextGameReturnType,
waitForNextGame,
} from './waitForNextGame.js'
import {
type WaitForNextL2OutputErrorType,
type WaitForNextL2OutputParameters,
type WaitForNextL2OutputReturnType,
waitForNextL2Output,
} from './waitForNextL2Output.js'
export type WaitToProveParameters<
chain extends Chain | undefined = Chain | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = GetChainParameter<chain, chainOverride> &
OneOf<
| GetContractAddressParameter<_derivedChain, 'l2OutputOracle'>
| GetContractAddressParameter<
_derivedChain,
'disputeGameFactory' | 'portal'
>
> & {
/**
* Limit of games to extract.
* @default 100
*/
gameLimit?: number | undefined
receipt: TransactionReceipt
/**
* Polling frequency (in ms). Defaults to Client's pollingInterval config.
* @default client.pollingInterval
*/
pollingInterval?: number | undefined
}
export type WaitToProveReturnType = {
game: WaitForNextGameReturnType
output: WaitForNextL2OutputReturnType
withdrawal: Withdrawal
}
export type WaitToProveErrorType =
| GetWithdrawalsErrorType
| WaitForNextL2OutputErrorType
| ErrorType
/**
* Waits until the L2 withdrawal transaction is ready to be proved. Used for the [Withdrawal](/op-stack/guides/withdrawals) flow.
*
* - Docs: https://viem.sh/op-stack/actions/waitToProve
*
* @param client - Client to use
* @param parameters - {@link WaitToProveParameters}
* @returns The L2 output and withdrawal message. {@link WaitToProveReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { getBlockNumber } from 'viem/actions'
* import { mainnet, optimism } from 'viem/chains'
* import { waitToProve } from 'viem/op-stack'
*
* const publicClientL1 = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const publicClientL2 = createPublicClient({
* chain: optimism,
* transport: http(),
* })
*
* const receipt = await publicClientL2.getTransactionReceipt({ hash: '0x...' })
* await waitToProve(publicClientL1, {
* receipt,
* targetChain: optimism
* })
*/
export async function waitToProve<
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: WaitToProveParameters<chain, chainOverride>,
): Promise<WaitToProveReturnType> {
const { gameLimit, receipt } = parameters
const [withdrawal] = getWithdrawals(receipt)
if (!withdrawal)
throw new ReceiptContainsNoWithdrawalsError({
hash: receipt.transactionHash,
})
const portalVersion = await getPortalVersion(
client,
parameters as GetPortalVersionParameters,
)
// Legacy (Portal < v3)
if (portalVersion.major < 3) {
const output = await waitForNextL2Output(client, {
...parameters,
l2BlockNumber: receipt.blockNumber,
} as WaitForNextL2OutputParameters)
return {
game: {
extraData: '0x',
index: output.outputIndex,
l2BlockNumber: output.l2BlockNumber,
metadata: '0x',
rootClaim: output.outputRoot,
timestamp: output.timestamp,
},
output,
withdrawal,
}
}
const game = await waitForNextGame(client, {
...parameters,
limit: gameLimit,
l2BlockNumber: receipt.blockNumber,
} as WaitForNextGameParameters)
return {
game,
output: {
l2BlockNumber: game.l2BlockNumber,
outputIndex: game.index,
outputRoot: game.rootClaim,
timestamp: game.timestamp,
},
withdrawal,
}
}