- 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>
242 lines
7.8 KiB
TypeScript
242 lines
7.8 KiB
TypeScript
import { type Address, parseAbi } 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 { AccountNotFoundError } from '../../errors/account.js'
|
|
import { ClientChainNotConfiguredError } from '../../errors/chain.js'
|
|
import type { GetAccountParameter } from '../../types/account.js'
|
|
import type { GetChainParameter } from '../../types/chain.js'
|
|
import type { UnionOmit } from '../../types/utils.js'
|
|
import type { EncodeFunctionDataReturnType } from '../../utils/abi/encodeFunctionData.js'
|
|
import {
|
|
encodeAbiParameters,
|
|
encodeFunctionData,
|
|
isAddressEqual,
|
|
keccak256,
|
|
parseAccount,
|
|
} from '../../utils/index.js'
|
|
import { ethTokenAbi, l2SharedBridgeAbi } from '../constants/abis.js'
|
|
import {
|
|
ethAddressInContracts,
|
|
l2AssetRouterAddress,
|
|
l2BaseTokenAddress,
|
|
l2NativeTokenVaultAddress,
|
|
legacyEthAddress,
|
|
} from '../constants/address.js'
|
|
import type { ChainEIP712 } from '../types/chain.js'
|
|
import type { ZksyncTransactionRequest } from '../types/transaction.js'
|
|
import { getDefaultBridgeAddresses } from './getDefaultBridgeAddresses.js'
|
|
import { getL1ChainId } from './getL1ChainId.js'
|
|
import { getL2TokenAddress } from './getL2TokenAddress.js'
|
|
import {
|
|
type SendTransactionErrorType,
|
|
type SendTransactionParameters,
|
|
type SendTransactionReturnType,
|
|
sendTransaction,
|
|
} from './sendTransaction.js'
|
|
|
|
export type WithdrawParameters<
|
|
chain extends ChainEIP712 | undefined = ChainEIP712 | undefined,
|
|
account extends Account | undefined = Account | undefined,
|
|
chainOverride extends ChainEIP712 | undefined = ChainEIP712 | undefined,
|
|
> = UnionOmit<
|
|
ZksyncTransactionRequest,
|
|
'from' | 'type' | 'value' | 'data' | 'to' | 'factoryDeps' | 'maxFeePerBlobGas'
|
|
> &
|
|
Partial<GetAccountParameter<account>> &
|
|
Partial<GetChainParameter<chain, chainOverride>> & {
|
|
/** The address of the recipient on L1. Defaults to the sender address. */
|
|
to?: Address | undefined
|
|
/** The address of the token. */
|
|
token: Address
|
|
/** The amount of the token to withdraw. */
|
|
amount: bigint
|
|
/** The address of the bridge contract to be used. */
|
|
bridgeAddress?: Address | undefined
|
|
}
|
|
|
|
export type WithdrawReturnType = SendTransactionReturnType
|
|
|
|
export type WithdrawErrorType = SendTransactionErrorType
|
|
|
|
/**
|
|
* Initiates the withdrawal process which withdraws ETH or any ERC20 token
|
|
* from the associated account on L2 network to the target account on L1 network.
|
|
*
|
|
* @param client - Client to use
|
|
* @param parameters - {@link WithdrawParameters}
|
|
* @returns hash - The [Transaction](https://viem.sh/docs/glossary/terms#transaction) hash. {@link WithdrawReturnType}
|
|
*
|
|
*
|
|
* @example
|
|
* import { createPublicClient, http } from 'viem'
|
|
* import { privateKeyToAccount } from 'viem/accounts'
|
|
* import { zksync } from 'viem/chains'
|
|
* import { withdraw, legacyEthAddress } from 'viem/zksync'
|
|
*
|
|
* const client = createPublicClient({
|
|
* chain: zksync,
|
|
* transport: http(),
|
|
* })
|
|
*
|
|
* const hash = await withdraw(client, {
|
|
* account: privateKeyToAccount('0x…'),
|
|
* amount: 1_000_000_000_000_000_000n,
|
|
* token: legacyEthAddress,
|
|
* })
|
|
*
|
|
* @example Account Hoisting
|
|
* import { createPublicClient, createWalletClient, http } from 'viem'
|
|
* import { privateKeyToAccount } from 'viem/accounts'
|
|
* import { zksync } from 'viem/chains'
|
|
* import { withdraw, legacyEthAddress } from 'viem/zksync'
|
|
*
|
|
* const client = createWalletClient({
|
|
* account: privateKeyToAccount('0x…'),
|
|
* chain: zksync,
|
|
* transport: http(),
|
|
* })
|
|
*
|
|
* const hash = await withdraw(client, {
|
|
* amount: 1_000_000_000_000_000_000n,
|
|
* token: legacyEthAddress,
|
|
* })
|
|
*
|
|
* @example Paymaster
|
|
* import { createPublicClient, http } from 'viem'
|
|
* import { privateKeyToAccount } from 'viem/accounts'
|
|
* import { zksync } from 'viem/chains'
|
|
* import {
|
|
* withdraw,
|
|
* legacyEthAddress,
|
|
* getApprovalBasedPaymasterInput
|
|
* } from 'viem/zksync'
|
|
*
|
|
* const client = createPublicClient({
|
|
* chain: zksync,
|
|
* transport: http(),
|
|
* })
|
|
*
|
|
* const hash = await withdraw(client, {
|
|
* account: privateKeyToAccount('0x…'),
|
|
* amount: 1_000_000_000_000_000_000n,
|
|
* token: legacyEthAddress,
|
|
* paymaster: '0x0EEc6f45108B4b806e27B81d9002e162BD910670',
|
|
* paymasterInput: getApprovalBasedPaymasterInput({
|
|
* minAllowance: 1n,
|
|
* token: '0x2dc3685cA34163952CF4A5395b0039c00DFa851D',
|
|
* innerInput: new Uint8Array(),
|
|
* }),
|
|
* })
|
|
*/
|
|
export async function withdraw<
|
|
chain extends ChainEIP712 | undefined,
|
|
account extends Account | undefined,
|
|
chainOverride extends ChainEIP712 | undefined = undefined,
|
|
>(
|
|
client: Client<Transport, chain, account>,
|
|
parameters: WithdrawParameters<chain, account, chainOverride>,
|
|
): Promise<WithdrawReturnType> {
|
|
let {
|
|
account: account_ = client.account,
|
|
chain: chain_ = client.chain,
|
|
token = l2BaseTokenAddress,
|
|
to,
|
|
amount,
|
|
bridgeAddress,
|
|
...rest
|
|
} = parameters
|
|
const account = account_ ? parseAccount(account_) : client.account
|
|
if (!account)
|
|
throw new AccountNotFoundError({
|
|
docsPath: '/docs/actions/wallet/sendTransaction',
|
|
})
|
|
|
|
if (!to) to = account.address
|
|
let data: EncodeFunctionDataReturnType
|
|
let contract: Address
|
|
let value = 0n
|
|
|
|
if (
|
|
isAddressEqual(token, legacyEthAddress) ||
|
|
isAddressEqual(token, ethAddressInContracts)
|
|
)
|
|
token = await getL2TokenAddress(client, { token: ethAddressInContracts })
|
|
|
|
if (isAddressEqual(token, l2BaseTokenAddress)) {
|
|
data = encodeFunctionData({
|
|
abi: ethTokenAbi,
|
|
functionName: 'withdraw',
|
|
args: [to],
|
|
})
|
|
value = amount
|
|
contract = l2BaseTokenAddress
|
|
} else {
|
|
const assetId = await readContract(client, {
|
|
address: l2NativeTokenVaultAddress,
|
|
abi: parseAbi(['function assetId(address token) view returns (bytes32)']),
|
|
functionName: 'assetId',
|
|
args: [token],
|
|
})
|
|
const originChainId = await readContract(client, {
|
|
address: l2NativeTokenVaultAddress,
|
|
abi: parseAbi([
|
|
'function originChainId(bytes32 assetId) view returns (uint256)',
|
|
]),
|
|
functionName: 'originChainId',
|
|
args: [assetId],
|
|
})
|
|
const l1ChainId = await getL1ChainId(client)
|
|
|
|
const isTokenL1Native =
|
|
originChainId === BigInt(l1ChainId) || token === ethAddressInContracts
|
|
if (!bridgeAddress) {
|
|
// If the legacy L2SharedBridge is deployed we use it for l1 native tokens.
|
|
bridgeAddress = isTokenL1Native
|
|
? (await getDefaultBridgeAddresses(client)).sharedL2
|
|
: l2AssetRouterAddress
|
|
}
|
|
// For non L1 native tokens we need to use the AssetRouter.
|
|
// For L1 native tokens we can use the legacy withdraw method.
|
|
if (!isTokenL1Native) {
|
|
contract = l2AssetRouterAddress
|
|
if (!chain_) throw new ClientChainNotConfiguredError()
|
|
const chainId = chain_.id
|
|
const assetId = keccak256(
|
|
encodeAbiParameters(
|
|
[{ type: 'uint256' }, { type: 'address' }, { type: 'address' }],
|
|
[BigInt(chainId), l2NativeTokenVaultAddress, token],
|
|
),
|
|
)
|
|
const assetData = encodeAbiParameters(
|
|
[{ type: 'uint256' }, { type: 'address' }, { type: 'address' }],
|
|
[BigInt(amount), to, token],
|
|
)
|
|
data = encodeFunctionData({
|
|
abi: parseAbi([
|
|
'function withdraw(bytes32 _assetId, bytes _transferData)',
|
|
]),
|
|
functionName: 'withdraw',
|
|
args: [assetId, assetData],
|
|
})
|
|
} else {
|
|
contract = bridgeAddress
|
|
data = encodeFunctionData({
|
|
abi: l2SharedBridgeAbi,
|
|
functionName: 'withdraw',
|
|
args: [to, token, amount],
|
|
})
|
|
}
|
|
}
|
|
|
|
return await sendTransaction(client, {
|
|
chain: chain_,
|
|
account,
|
|
to: contract,
|
|
data,
|
|
value,
|
|
...rest,
|
|
} as SendTransactionParameters)
|
|
}
|