viem
Version:
123 lines (114 loc) • 4.22 kB
text/typescript
import type { Address } from 'abitype'
import type { Account } from '../../accounts/types.js'
import { readContract } from '../../actions/public/readContract.js'
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import {
ChainNotFoundError,
type ChainNotFoundErrorType,
} from '../../errors/chain.js'
import type { Chain } from '../../types/chain.js'
import type { Hash } from '../../types/misc.js'
import { isAddressEqual, slice } from '../../utils/index.js'
import { l1SharedBridgeAbi, l2SharedBridgeAbi } from '../constants/abis.js'
import { l2BaseTokenAddress } from '../constants/address.js'
import {
WithdrawalLogNotFoundError,
type WithdrawalLogNotFoundErrorType,
} from '../errors/bridge.js'
import type { ChainEIP712 } from '../types/chain.js'
import { getWithdrawalL2ToL1Log } from '../utils/bridge/getWithdrawalL2ToL1Log.js'
import { getWithdrawalLog } from '../utils/bridge/getWithdrawalLog.js'
import { getBaseTokenL1Address } from './getBaseTokenL1Address.js'
import { getDefaultBridgeAddresses } from './getDefaultBridgeAddresses.js'
import { getLogProof } from './getLogProof.js'
export type IsWithdrawalFinalizedParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
> = {
/** L2 client */
client: Client<Transport, chain, account>
/** Hash of the L2 transaction where the withdrawal was initiated. */
hash: Hash
/** In case there were multiple withdrawals in one transaction, you may pass an index of the
withdrawal you want to finalize. */
index?: number | undefined
}
export type IsWithdrawalFinalizedReturnType = boolean
export type IsWithdrawalFinalizedErrorType =
| WithdrawalLogNotFoundErrorType
| ChainNotFoundErrorType
/**
* Returns whether the withdrawal transaction is finalized on the L1 network.
*
* @param client - Client to use
* @param parameters - {@link IsWithdrawalFinalizedParameters}
* @returns bool - Whether the withdrawal transaction is finalized on the L1 network. {@link IsWithdrawalFinalizedReturnType}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { mainnet, zksync } from 'viem/chains'
* import { isWithdrawalFinalized } from 'viem/zksync'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
*
* const clientL2 = createPublicClient({
* chain: zksync,
* transport: http(),
* })
*
* const hash = await isWithdrawalFinalized(client, {
* client: clientL2,
* hash: '0x...',
* })
*/
export async function isWithdrawalFinalized<
chain extends Chain | undefined,
account extends Account | undefined,
chainL2 extends ChainEIP712 | undefined,
accountL2 extends Account | undefined,
>(
client: Client<Transport, chain, account>,
parameters: IsWithdrawalFinalizedParameters<chainL2, accountL2>,
): Promise<IsWithdrawalFinalizedReturnType> {
const { client: l2Client, hash, index = 0 } = parameters
if (!l2Client.chain) throw new ChainNotFoundError()
const { log } = await getWithdrawalLog(l2Client, { hash, index })
const { l2ToL1LogIndex } = await getWithdrawalL2ToL1Log(l2Client, {
hash,
index,
})
const sender = slice(log.topics[1]!, 12) as Address
// `getLogProof` is called not to get proof but
// to get the index of the corresponding L2->L1 log,
// which is returned as `proof.id`.
const proof = await getLogProof(l2Client, {
txHash: hash,
index: l2ToL1LogIndex!,
})
if (!proof) {
throw new WithdrawalLogNotFoundError({ hash })
}
let l1Bridge: Address
if (
isAddressEqual(sender, await getBaseTokenL1Address(l2Client)) ||
isAddressEqual(sender, l2BaseTokenAddress)
)
l1Bridge = (await getDefaultBridgeAddresses(l2Client)).sharedL1
else
l1Bridge = await readContract(l2Client, {
address: sender,
abi: l2SharedBridgeAbi,
functionName: 'l1SharedBridge',
args: [],
})
return await readContract(client, {
address: l1Bridge,
abi: l1SharedBridgeAbi,
functionName: 'isWithdrawalFinalized',
args: [BigInt(l2Client.chain.id), log.l1BatchNumber!, BigInt(proof.id)],
})
}