import { type Account, type Address, encodeAbiParameters, encodeFunctionData, type Hex, zeroHash, } from 'viem' import { readContract as viem_readContract, sendTransaction as viem_sendTransaction, sendTransactionSync as viem_sendTransactionSync, } from 'viem/actions' import { Abis, Actions, Bytes, PublicKey, Secp256k1, TokenId } from 'viem/tempo' import { Abis as ZoneAbis } from 'viem/tempo/zones' import { parseAccount } from 'viem/utils' import { getConnectorClient } from '../../actions/getConnectorClient.js' import type { Config } from '../../createConfig.js' import type { ChainIdParameter, ConnectorParameter, } from '../../types/properties.js' import type { PartialBy, UnionLooseOmit } from '../../types/utils.js' import type { QueryOptions, QueryParameter } from './utils.js' import { filterQueryOptions } from './utils.js' /** * Gets information about the currently stored zone authorization token. * * @example * ```ts * import { createConfig } from '@wagmi/core' * import { Actions, dangerous_secp256k1 } from '@wagmi/core/tempo' * import { Account } from 'viem/tempo' * import { http as zoneHttp, zone } from 'viem/tempo/zones' * * const zoneChain = zone(7) * const account = Account.fromSecp256k1('0x...') * const config = createConfig({ * chains: [zoneChain], * connectors: [dangerous_secp256k1({ account })], * transports: { * [zoneChain.id]: zoneHttp(), * }, * }) * * await Actions.zone.signAuthorizationToken(config, { * chainId: zoneChain.id, * }) * * const info = await Actions.zone.getAuthorizationTokenInfo(config, { * chainId: zoneChain.id, * }) * * console.log(info.expiresAt) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns The authorization token info. */ export function getAuthorizationTokenInfo( config: config, parameters: getAuthorizationTokenInfo.Parameters, ): Promise { const client = config.getClient({ chainId: parameters.chainId }) return Actions.zone.getAuthorizationTokenInfo(client) } export namespace getAuthorizationTokenInfo { export type Parameters = ChainIdParameter export type ReturnValue = Actions.zone.getAuthorizationTokenInfo.ReturnType export type ErrorType = Actions.zone.getAuthorizationTokenInfo.ErrorType export function queryKey( parameters: Parameters, ) { return [ 'getAuthorizationTokenInfo', filterQueryOptions(parameters), ] as const } export type QueryKey = ReturnType< typeof queryKey > export function queryOptions( config: Config, parameters: queryOptions.Parameters, ): queryOptions.ReturnValue { const { query, ...rest } = parameters return { ...query, enabled: Boolean(query?.enabled ?? true), queryKey: queryKey(rest), async queryFn(context) { const [, parameters] = context.queryKey return await getAuthorizationTokenInfo(config, parameters) }, } } export declare namespace queryOptions { export type Parameters< config extends Config, selectData = getAuthorizationTokenInfo.ReturnValue, > = getAuthorizationTokenInfo.Parameters & QueryParameter< getAuthorizationTokenInfo.ReturnValue, getAuthorizationTokenInfo.ErrorType, selectData, getAuthorizationTokenInfo.QueryKey > export type ReturnValue< config extends Config, selectData = getAuthorizationTokenInfo.ReturnValue, > = QueryOptions< getAuthorizationTokenInfo.ReturnValue, getAuthorizationTokenInfo.ErrorType, selectData, getAuthorizationTokenInfo.QueryKey > } } /** * Gets deposit processing status for a Tempo block number. * * @example * ```ts * import { createConfig } from '@wagmi/core' * import { Actions, dangerous_secp256k1 } from '@wagmi/core/tempo' * import { Account } from 'viem/tempo' * import { http as zoneHttp, zone } from 'viem/tempo/zones' * * const zoneChain = zone(7) * const account = Account.fromSecp256k1('0x...') * const config = createConfig({ * chains: [zoneChain], * connectors: [dangerous_secp256k1({ account })], * transports: { * [zoneChain.id]: zoneHttp(), * }, * }) * * await Actions.zone.signAuthorizationToken(config, { * chainId: zoneChain.id, * }) * * const status = await Actions.zone.getDepositStatus(config, { * chainId: zoneChain.id, * tempoBlockNumber: 42n, * }) * * console.log(status.processed) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns The deposit status. */ export function getDepositStatus( config: config, parameters: getDepositStatus.Parameters, ): Promise { const { chainId, ...rest } = parameters const client = config.getClient({ chainId }) return Actions.zone.getDepositStatus(client, rest) } export namespace getDepositStatus { export type Parameters = ChainIdParameter & Actions.zone.getDepositStatus.Parameters export type ReturnValue = Actions.zone.getDepositStatus.ReturnType export type ErrorType = Actions.zone.getDepositStatus.ErrorType export function queryKey( parameters: PartialBy, 'tempoBlockNumber'>, ) { return ['getDepositStatus', filterQueryOptions(parameters)] as const } export type QueryKey = ReturnType< typeof queryKey > export function queryOptions( config: Config, parameters: queryOptions.Parameters, ): queryOptions.ReturnValue { const { query, ...rest } = parameters return { ...query, enabled: Boolean( rest.tempoBlockNumber !== undefined && (query?.enabled ?? true), ), queryKey: queryKey(rest), async queryFn(context) { const [, { tempoBlockNumber, ...parameters }] = context.queryKey if (tempoBlockNumber === undefined) throw new Error('tempoBlockNumber is required.') return await getDepositStatus(config, { ...parameters, tempoBlockNumber, }) }, } } export declare namespace queryOptions { export type Parameters< config extends Config, selectData = getDepositStatus.ReturnValue, > = PartialBy, 'tempoBlockNumber'> & QueryParameter< getDepositStatus.ReturnValue, getDepositStatus.ErrorType, selectData, getDepositStatus.QueryKey > export type ReturnValue< config extends Config, selectData = getDepositStatus.ReturnValue, > = QueryOptions< getDepositStatus.ReturnValue, getDepositStatus.ErrorType, selectData, getDepositStatus.QueryKey > } } /** * Gets the withdrawal fee for a given gas limit. * * @example * ```ts * import { createConfig } from '@wagmi/core' * import { Actions } from '@wagmi/core/tempo' * import { http as zoneHttp, zone } from 'viem/tempo/zones' * * const zoneChain = zone(7) * const config = createConfig({ * chains: [zoneChain], * transports: { * [zoneChain.id]: zoneHttp(), * }, * }) * * const fee = await Actions.zone.getWithdrawalFee(config, { * chainId: zoneChain.id, * gas: 21_000n, * }) * * console.log(fee) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns The withdrawal fee. */ export function getWithdrawalFee( config: config, parameters: getWithdrawalFee.Parameters, ): Promise { const { chainId, ...rest } = parameters const client = config.getClient({ chainId }) return Actions.zone.getWithdrawalFee(client, rest) } export namespace getWithdrawalFee { export type Parameters = ChainIdParameter & Actions.zone.getWithdrawalFee.Parameters export type ReturnValue = Actions.zone.getWithdrawalFee.ReturnType export type ErrorType = Actions.zone.getWithdrawalFee.ErrorType export function queryKey( parameters: Parameters, ) { return ['getWithdrawalFee', filterQueryOptions(parameters)] as const } export type QueryKey = ReturnType< typeof queryKey > export function queryOptions( config: Config, parameters: queryOptions.Parameters, ): queryOptions.ReturnValue { const { query, ...rest } = parameters return { ...query, enabled: Boolean(query?.enabled ?? true), queryKey: queryKey(rest), async queryFn(context) { const [, parameters] = context.queryKey return await getWithdrawalFee(config, parameters) }, } } export declare namespace queryOptions { export type Parameters< config extends Config, selectData = getWithdrawalFee.ReturnValue, > = getWithdrawalFee.Parameters & QueryParameter< getWithdrawalFee.ReturnValue, getWithdrawalFee.ErrorType, selectData, getWithdrawalFee.QueryKey > export type ReturnValue< config extends Config, selectData = getWithdrawalFee.ReturnValue, > = QueryOptions< getWithdrawalFee.ReturnValue, getWithdrawalFee.ErrorType, selectData, getWithdrawalFee.QueryKey > } } /** * Gets the current zone metadata. * * @example * ```ts * import { createConfig } from '@wagmi/core' * import { Actions } from '@wagmi/core/tempo' * import { http as zoneHttp, zone } from 'viem/tempo/zones' * * const zoneChain = zone(7) * const config = createConfig({ * chains: [zoneChain], * transports: { * [zoneChain.id]: zoneHttp(), * }, * }) * * const info = await Actions.zone.getZoneInfo(config, { * chainId: zoneChain.id, * }) * * console.log(info.zoneId) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns The zone metadata. */ export function getZoneInfo( config: config, parameters: getZoneInfo.Parameters, ): Promise { const client = config.getClient({ chainId: parameters.chainId }) return Actions.zone.getZoneInfo(client) } export namespace getZoneInfo { export type Parameters = ChainIdParameter export type ReturnValue = Actions.zone.getZoneInfo.ReturnType export type ErrorType = Actions.zone.getZoneInfo.ErrorType export function queryKey( parameters: Parameters, ) { return ['getZoneInfo', filterQueryOptions(parameters)] as const } export type QueryKey = ReturnType< typeof queryKey > export function queryOptions( config: Config, parameters: queryOptions.Parameters, ): queryOptions.ReturnValue { const { query, ...rest } = parameters return { ...query, enabled: Boolean(query?.enabled ?? true), queryKey: queryKey(rest), async queryFn(context) { const [, parameters] = context.queryKey return await getZoneInfo(config, parameters) }, } } export declare namespace queryOptions { export type Parameters< config extends Config, selectData = getZoneInfo.ReturnValue, > = getZoneInfo.Parameters & QueryParameter< getZoneInfo.ReturnValue, getZoneInfo.ErrorType, selectData, getZoneInfo.QueryKey > export type ReturnValue< config extends Config, selectData = getZoneInfo.ReturnValue, > = QueryOptions< getZoneInfo.ReturnValue, getZoneInfo.ErrorType, selectData, getZoneInfo.QueryKey > } } /** * Signs and stores a zone authorization token for the configured zone transport. * * @example * ```ts * import { createConfig } from '@wagmi/core' * import { Actions, dangerous_secp256k1 } from '@wagmi/core/tempo' * import { Account } from 'viem/tempo' * import { http as zoneHttp, zone } from 'viem/tempo/zones' * * const zoneChain = zone(7) * const account = Account.fromSecp256k1('0x...') * const config = createConfig({ * chains: [zoneChain], * connectors: [dangerous_secp256k1({ account })], * transports: { * [zoneChain.id]: zoneHttp(), * }, * }) * * const result = await Actions.zone.signAuthorizationToken(config, { * chainId: zoneChain.id, * }) * * console.log(result.token) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns The authentication payload and serialized token. */ export async function signAuthorizationToken( config: config, parameters: signAuthorizationToken.Parameters, ): Promise { const { account, chainId, connector, ...rest } = parameters const client = await getZoneWalletClient(config, { account, chainId, connector, }) return Actions.zone.signAuthorizationToken(client, rest as never) } export declare namespace signAuthorizationToken { export type Parameters = ChainIdParameter & ConnectorParameter & { account?: Address | Account | undefined } & UnionLooseOmit< Actions.zone.signAuthorizationToken.Parameters, 'account' | 'chain' > export type ReturnValue = Actions.zone.signAuthorizationToken.ReturnType export type ErrorType = Actions.zone.signAuthorizationToken.ErrorType } /** * Deposits tokens into a zone on the parent Tempo chain. * * @example * ```ts * import { createConfig, http } from '@wagmi/core' * import { tempoModerato } from '@wagmi/core/chains' * import { Actions } from '@wagmi/core/tempo' * * const config = createConfig({ * chains: [tempoModerato], * transports: { * [tempoModerato.id]: http(), * }, * }) * * const hash = await Actions.zone.deposit(config, { * amount: 1_000_000n, * token: '0x20c0000000000000000000000000000000000001', * zoneId: 7, * }) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns Transaction hash. */ export async function deposit( config: config, parameters: deposit.Parameters, ): Promise { const { account, chainId, connector, ...rest } = parameters const client = await getConnectorClient(config, { account, assertChainId: false, chainId, connector, }) const resolvedChainId = chainId ?? client.chain?.id if (!resolvedChainId) throw new Error('`chainId` is required.') const account_ = account ?? client.account if (!account_) throw new Error('`account` is required.') const accountAddress = parseAccount(account_).address const { amount, memo = zeroHash, recipient = accountAddress, token, zoneId, ...tx } = rest const { address: portalAddress } = resolvePortal( config, resolvedChainId, zoneId, ) const tokenAddress = TokenId.toAddress(token) return viem_sendTransaction(client, { ...tx, calls: [ { data: encodeFunctionData({ abi: Abis.tip20, functionName: 'approve', args: [portalAddress, amount], }), to: tokenAddress, }, { data: encodeFunctionData({ abi: ZoneAbis.zonePortal, functionName: 'deposit', args: [tokenAddress, recipient, amount, memo], }), to: portalAddress, }, ], } as never) as never } export declare namespace deposit { export type Parameters = ChainIdParameter & ConnectorParameter & UnionLooseOmit< Actions.zone.deposit.Parameters, 'chain' > export type ReturnValue = Actions.zone.deposit.ReturnValue export type ErrorType = Actions.zone.deposit.ErrorType } /** * Deposits tokens into a zone on the parent Tempo chain. * * Note: This is a synchronous action that waits for the transaction to * be included on a block before returning a response. * * @example * ```ts * import { createConfig, http } from '@wagmi/core' * import { tempoModerato } from '@wagmi/core/chains' * import { Actions } from '@wagmi/core/tempo' * * const config = createConfig({ * chains: [tempoModerato], * transports: { * [tempoModerato.id]: http(), * }, * }) * * const result = await Actions.zone.depositSync(config, { * amount: 1_000_000n, * token: '0x20c0000000000000000000000000000000000001', * zoneId: 7, * }) * * console.log(result.receipt.transactionHash) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns The transaction receipt. */ export async function depositSync( config: config, parameters: depositSync.Parameters, ): Promise { const { account, chainId, connector, throwOnReceiptRevert = true, ...rest } = parameters const client = await getConnectorClient(config, { account, assertChainId: false, chainId, connector, }) const resolvedChainId = chainId ?? client.chain?.id if (!resolvedChainId) throw new Error('`chainId` is required.') const account_ = account ?? client.account if (!account_) throw new Error('`account` is required.') const accountAddress = parseAccount(account_).address const { amount, memo = zeroHash, recipient = accountAddress, token, zoneId, ...tx } = rest const { address: portalAddress } = resolvePortal( config, resolvedChainId, zoneId, ) const tokenAddress = TokenId.toAddress(token) const receipt = await viem_sendTransactionSync(client, { ...tx, calls: [ { data: encodeFunctionData({ abi: Abis.tip20, functionName: 'approve', args: [portalAddress, amount], }), to: tokenAddress, }, { data: encodeFunctionData({ abi: ZoneAbis.zonePortal, functionName: 'deposit', args: [tokenAddress, recipient, amount, memo], }), to: portalAddress, }, ], throwOnReceiptRevert, } as never) return { receipt } } export declare namespace depositSync { export type Parameters = ChainIdParameter & ConnectorParameter & UnionLooseOmit< Actions.zone.depositSync.Parameters, 'chain' > export type ReturnValue = Actions.zone.depositSync.ReturnValue export type ErrorType = Actions.zone.depositSync.ErrorType } /** * Deposits tokens into a zone on the parent Tempo chain with an encrypted * recipient and memo. * * @example * ```ts * import { createConfig, http } from '@wagmi/core' * import { tempoModerato } from '@wagmi/core/chains' * import { Actions } from '@wagmi/core/tempo' * * const config = createConfig({ * chains: [tempoModerato], * transports: { * [tempoModerato.id]: http(), * }, * }) * * const hash = await Actions.zone.encryptedDeposit(config, { * amount: 1_000_000n, * token: '0x20c0000000000000000000000000000000000001', * zoneId: 7, * }) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns Transaction hash. */ export async function encryptedDeposit( config: config, parameters: encryptedDeposit.Parameters, ): Promise { const { account, chainId, connector, ...rest } = parameters const client = await getConnectorClient(config, { account, assertChainId: false, chainId, connector, }) const resolvedChainId = chainId ?? client.chain?.id if (!resolvedChainId) throw new Error('`chainId` is required.') const account_ = account ?? client.account if (!account_) throw new Error('`account` is required.') const accountAddress = parseAccount(account_).address const { amount, memo, recipient = accountAddress, token, zoneId, ...tx } = rest const portal = resolvePortal(config, resolvedChainId, zoneId) const portalAddress = portal.address const tokenAddress = TokenId.toAddress(token) const [publicKey, keyIndex] = portal.sequencerEncryptionKey && portal.encryptionKeyCount !== undefined ? [portal.sequencerEncryptionKey, portal.encryptionKeyCount] : await Promise.all([ viem_readContract(client, { address: portalAddress, abi: ZoneAbis.zonePortal, functionName: 'sequencerEncryptionKey', }).then(([x, yParity]) => ({ x, yParity: Number(yParity) })), viem_readContract(client, { address: portalAddress, abi: ZoneAbis.zonePortal, functionName: 'encryptionKeyCount', }), ]) if (keyIndex === 0n) throw new Error('No sequencer encryption key configured.') const encrypted = await encryptDepositPayload(publicKey, recipient, memo) return viem_sendTransaction(client, { ...tx, calls: [ { data: encodeFunctionData({ abi: Abis.tip20, functionName: 'approve', args: [portalAddress, amount], }), to: tokenAddress, }, { data: encodeFunctionData({ abi: ZoneAbis.zonePortal, functionName: 'depositEncrypted', args: [tokenAddress, amount, keyIndex - 1n, encrypted], }), to: portalAddress, }, ], } as never) as never } export declare namespace encryptedDeposit { export type Parameters = ChainIdParameter & ConnectorParameter & UnionLooseOmit< Actions.zone.encryptedDeposit.Parameters< config['chains'][number], Account >, 'chain' > export type ReturnValue = Actions.zone.encryptedDeposit.ReturnValue export type ErrorType = Actions.zone.encryptedDeposit.ErrorType } /** * Deposits tokens into a zone on the parent Tempo chain with an encrypted * recipient and memo. * * Note: This is a synchronous action that waits for the transaction to * be included on a block before returning a response. * * @example * ```ts * import { createConfig, http } from '@wagmi/core' * import { tempoModerato } from '@wagmi/core/chains' * import { Actions } from '@wagmi/core/tempo' * * const config = createConfig({ * chains: [tempoModerato], * transports: { * [tempoModerato.id]: http(), * }, * }) * * const result = await Actions.zone.encryptedDepositSync(config, { * amount: 1_000_000n, * token: '0x20c0000000000000000000000000000000000001', * zoneId: 7, * }) * * console.log(result.receipt.transactionHash) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns The transaction receipt. */ export async function encryptedDepositSync( config: config, parameters: encryptedDepositSync.Parameters, ): Promise { const { account, chainId, connector, throwOnReceiptRevert = true, ...rest } = parameters const client = await getConnectorClient(config, { account, assertChainId: false, chainId, connector, }) const resolvedChainId = chainId ?? client.chain?.id if (!resolvedChainId) throw new Error('`chainId` is required.') const account_ = account ?? client.account if (!account_) throw new Error('`account` is required.') const accountAddress = parseAccount(account_).address const { amount, memo, recipient = accountAddress, token, zoneId, ...tx } = rest const portal = resolvePortal(config, resolvedChainId, zoneId) const portalAddress = portal.address const tokenAddress = TokenId.toAddress(token) const [publicKey, keyIndex] = portal.sequencerEncryptionKey && portal.encryptionKeyCount !== undefined ? [portal.sequencerEncryptionKey, portal.encryptionKeyCount] : await Promise.all([ viem_readContract(client, { address: portalAddress, abi: ZoneAbis.zonePortal, functionName: 'sequencerEncryptionKey', }).then(([x, yParity]) => ({ x, yParity: Number(yParity) })), viem_readContract(client, { address: portalAddress, abi: ZoneAbis.zonePortal, functionName: 'encryptionKeyCount', }), ]) if (keyIndex === 0n) throw new Error('No sequencer encryption key configured.') const encrypted = await encryptDepositPayload(publicKey, recipient, memo) const receipt = await viem_sendTransactionSync(client, { ...tx, calls: [ { data: encodeFunctionData({ abi: Abis.tip20, functionName: 'approve', args: [portalAddress, amount], }), to: tokenAddress, }, { data: encodeFunctionData({ abi: ZoneAbis.zonePortal, functionName: 'depositEncrypted', args: [tokenAddress, amount, keyIndex - 1n, encrypted], }), to: portalAddress, }, ], throwOnReceiptRevert, } as never) return { receipt } } export declare namespace encryptedDepositSync { export type Parameters = ChainIdParameter & ConnectorParameter & UnionLooseOmit< Actions.zone.encryptedDepositSync.Parameters< config['chains'][number], Account >, 'chain' > export type ReturnValue = Actions.zone.encryptedDepositSync.ReturnValue export type ErrorType = Actions.zone.encryptedDepositSync.ErrorType } /** * Requests a withdrawal from a zone to the parent Tempo chain. * * @example * ```ts * import { createConfig } from '@wagmi/core' * import { Actions, dangerous_secp256k1 } from '@wagmi/core/tempo' * import { Account } from 'viem/tempo' * import { http as zoneHttp, zone } from 'viem/tempo/zones' * * const zoneChain = zone(7) * const account = Account.fromSecp256k1('0x...') * const config = createConfig({ * chains: [zoneChain], * connectors: [dangerous_secp256k1({ account })], * transports: { * [zoneChain.id]: zoneHttp(), * }, * }) * * const hash = await Actions.zone.requestWithdrawal(config, { * amount: 1_000_000n, * chainId: zoneChain.id, * token: '0x20c0000000000000000000000000000000000001', * }) * * console.log(hash) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns Transaction hash. */ export async function requestWithdrawal( config: config, parameters: requestWithdrawal.Parameters, ): Promise { const { account, chainId, connector } = parameters const client = await getZoneWalletClient(config, { account, chainId, connector, }) return Actions.zone.requestWithdrawal(client, parameters as never) } export declare namespace requestWithdrawal { export type Parameters = ChainIdParameter & ConnectorParameter & UnionLooseOmit< Actions.zone.requestWithdrawal.Parameters< config['chains'][number], Account >, 'chain' > export type ReturnValue = Actions.zone.requestWithdrawal.ReturnValue export type ErrorType = Actions.zone.requestWithdrawal.ErrorType } /** * Requests a withdrawal from a zone to the parent Tempo chain. * * Note: This is a synchronous action that waits for the transaction to * be included on a block before returning a response. * * @example * ```ts * import { createConfig } from '@wagmi/core' * import { Actions, dangerous_secp256k1 } from '@wagmi/core/tempo' * import { Account } from 'viem/tempo' * import { http as zoneHttp, zone } from 'viem/tempo/zones' * * const zoneChain = zone(7) * const account = Account.fromSecp256k1('0x...') * const config = createConfig({ * chains: [zoneChain], * connectors: [dangerous_secp256k1({ account })], * transports: { * [zoneChain.id]: zoneHttp(), * }, * }) * * const result = await Actions.zone.requestWithdrawalSync(config, { * amount: 1_000_000n, * chainId: zoneChain.id, * token: '0x20c0000000000000000000000000000000000001', * }) * * console.log(result.receipt.transactionHash) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns The transaction receipt. */ export async function requestWithdrawalSync( config: config, parameters: requestWithdrawalSync.Parameters, ): Promise { const { account, chainId, connector } = parameters const client = await getZoneWalletClient(config, { account, chainId, connector, }) return Actions.zone.requestWithdrawalSync(client, parameters as never) } export declare namespace requestWithdrawalSync { export type Parameters = ChainIdParameter & ConnectorParameter & UnionLooseOmit< Actions.zone.requestWithdrawalSync.Parameters< config['chains'][number], Account >, 'chain' > export type ReturnValue = Actions.zone.requestWithdrawalSync.ReturnValue export type ErrorType = Actions.zone.requestWithdrawalSync.ErrorType } /** * Requests a verifiable withdrawal from a zone to the parent Tempo chain. * * @example * ```ts * import { createConfig } from '@wagmi/core' * import { Actions, dangerous_secp256k1 } from '@wagmi/core/tempo' * import { Account } from 'viem/tempo' * import { http as zoneHttp, zone } from 'viem/tempo/zones' * * const zoneChain = zone(7) * const account = Account.fromSecp256k1('0x...') * const config = createConfig({ * chains: [zoneChain], * connectors: [dangerous_secp256k1({ account })], * transports: { * [zoneChain.id]: zoneHttp(), * }, * }) * * const hash = await Actions.zone.requestVerifiableWithdrawal(config, { * amount: 1_000_000n, * chainId: zoneChain.id, * revealTo: * '0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', * token: '0x20c0000000000000000000000000000000000001', * }) * * console.log(hash) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns Transaction hash. */ export async function requestVerifiableWithdrawal( config: config, parameters: requestVerifiableWithdrawal.Parameters, ): Promise { const { account, chainId, connector } = parameters const client = await getZoneWalletClient(config, { account, chainId, connector, }) return Actions.zone.requestVerifiableWithdrawal(client, parameters as never) } export declare namespace requestVerifiableWithdrawal { export type Parameters = ChainIdParameter & ConnectorParameter & UnionLooseOmit< Actions.zone.requestVerifiableWithdrawal.Parameters< config['chains'][number], Account >, 'chain' > export type ReturnValue = Actions.zone.requestVerifiableWithdrawal.ReturnValue export type ErrorType = Actions.zone.requestVerifiableWithdrawal.ErrorType } /** * Requests a verifiable withdrawal from a zone to the parent Tempo chain. * * Note: This is a synchronous action that waits for the transaction to * be included on a block before returning a response. * * @example * ```ts * import { createConfig } from '@wagmi/core' * import { Actions, dangerous_secp256k1 } from '@wagmi/core/tempo' * import { Account } from 'viem/tempo' * import { http as zoneHttp, zone } from 'viem/tempo/zones' * * const zoneChain = zone(7) * const account = Account.fromSecp256k1('0x...') * const config = createConfig({ * chains: [zoneChain], * connectors: [dangerous_secp256k1({ account })], * transports: { * [zoneChain.id]: zoneHttp(), * }, * }) * * const result = await Actions.zone.requestVerifiableWithdrawalSync(config, { * amount: 1_000_000n, * chainId: zoneChain.id, * revealTo: * '0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', * token: '0x20c0000000000000000000000000000000000001', * }) * * console.log(result.receipt.transactionHash) * ``` * * @param config - Config. * @param parameters - Parameters. * @returns The transaction receipt. */ export async function requestVerifiableWithdrawalSync( config: config, parameters: requestVerifiableWithdrawalSync.Parameters, ): Promise { const { account, chainId, connector } = parameters const client = await getZoneWalletClient(config, { account, chainId, connector, }) return Actions.zone.requestVerifiableWithdrawalSync( client, parameters as never, ) } export declare namespace requestVerifiableWithdrawalSync { export type Parameters = ChainIdParameter & ConnectorParameter & UnionLooseOmit< Actions.zone.requestVerifiableWithdrawalSync.Parameters< config['chains'][number], Account >, 'chain' > export type ReturnValue = Actions.zone.requestVerifiableWithdrawalSync.ReturnValue export type ErrorType = Actions.zone.requestVerifiableWithdrawalSync.ErrorType } const portalAddresses = { 42431: { 6: '0x7069DeC4E64Fd07334A0933eDe836C17259c9B23', 7: '0x3F5296303400B56271b476F5A0B9cBF74350D6Ac', }, } as const satisfies Record> async function getZoneWalletClient( config: config, parameters: { account?: Address | Account | null | undefined chainId?: number | undefined connector?: ConnectorParameter['connector'] }, ) { const client = await getConnectorClient(config, { ...parameters, assertChainId: false, }) const resolvedChainId = parameters.chainId ?? client.chain?.id const account = resolveSignableAccount(parameters.account) ?? (await getSignableConnectorAccount(config, { account: parameters.account ?? client.account, chainId: resolvedChainId, connector: parameters.connector, })) if (!account || resolvedChainId === undefined) return client // Local accounts can sign against the zone transport directly without // depending on the connector provider's currently selected chain. return Object.assign(config.getClient({ chainId: resolvedChainId }), { account, }) as typeof client } async function getSignableConnectorAccount( config: config, parameters: { account?: Address | Account | null | undefined chainId?: number | undefined connector?: ConnectorParameter['connector'] }, ) { const connector = parameters.connector ?? config.state.connections.get(config.state.current!)?.connector const provider = (await connector?.getProvider?.({ chainId: parameters.chainId, })) as | { getAccount?: | ((parameters?: { address?: Address | undefined signable?: boolean | undefined }) => Account | undefined) | undefined } | undefined if (typeof provider?.getAccount !== 'function') return try { return provider.getAccount({ address: parameters.account ? parseAccount(parameters.account).address : undefined, signable: true, }) } catch { return } } function resolveSignableAccount( account?: Address | Account | null | undefined, ) { if (typeof account !== 'object' || account === null) return return 'sign' in account ? account : undefined } function resolvePortal( config: config, chainId: number, zoneId: number, ): { address: Address encryptionKeyCount?: bigint | undefined sequencerEncryptionKey?: { x: Hex; yParity: number } | undefined } { const chain = config.chains.find((chain) => chain.id === chainId) const zonePortal = chain?.contracts?.zonePortal as | Address | { [key: number]: | Address | { address?: Address | undefined encryptionKeyCount?: bigint | undefined sequencerEncryptionKey?: { x: Hex; yParity: number } | undefined } | undefined address?: Address | undefined encryptionKeyCount?: bigint | undefined sequencerEncryptionKey?: { x: Hex; yParity: number } | undefined } | undefined // Allow custom chains to supply portal addresses until viem exposes a // generic resolver for non-hardcoded Tempo networks. if (typeof zonePortal === 'string') return { address: zonePortal } if (zonePortal && typeof zonePortal === 'object') { const portal = 'address' in zonePortal && typeof zonePortal.address === 'string' ? zonePortal : zonePortal[zoneId] if (typeof portal === 'string') return { address: portal as Address } if ( portal && typeof portal === 'object' && typeof portal.address === 'string' ) { return { address: portal.address, encryptionKeyCount: portal.encryptionKeyCount, sequencerEncryptionKey: portal.sequencerEncryptionKey, } } } const address = (portalAddresses as Record>)[ chainId ]?.[zoneId] if (address) return { address } throw new Error( `No portal address configured for zone ${zoneId} on chain ${chainId}.`, ) } async function encryptDepositPayload( publicKey: { x: Hex; yParity: number }, recipient: Address, memo: Hex = zeroHash, ): Promise<{ ciphertext: Hex ephemeralPubkeyX: Hex ephemeralPubkeyYParity: number nonce: Hex tag: Hex }> { const sequencerPublicKey = PublicKey.from({ prefix: publicKey.yParity, x: BigInt(publicKey.x), }) const { privateKey: ephemeralPrivateKey, publicKey: ephemeralPublicKey } = Secp256k1.createKeyPair() const sharedSecret = Secp256k1.getSharedSecret({ privateKey: ephemeralPrivateKey, publicKey: sequencerPublicKey, as: 'Bytes', }) const hkdfKey = await globalThis.crypto.subtle.importKey( 'raw', sharedSecret.buffer as ArrayBuffer, 'HKDF', false, ['deriveKey'], ) const aesKey = await globalThis.crypto.subtle.deriveKey( { name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(12), info: new TextEncoder().encode('ecies-aes-key'), }, hkdfKey, { name: 'AES-GCM', length: 256 }, false, ['encrypt'], ) const nonce = Bytes.random(12) const plaintext = encodeAbiParameters( [{ type: 'address' }, { type: 'bytes32' }], [recipient, memo], ) const ciphertextWithTag = new Uint8Array( await globalThis.crypto.subtle.encrypt( { name: 'AES-GCM', iv: nonce as BufferSource, tagLength: 128 }, aesKey, Bytes.from(plaintext) as BufferSource, ), ) const ciphertext = ciphertextWithTag.slice(0, -16) const tag = ciphertextWithTag.slice(-16) const compressedEphemeral = PublicKey.compress(ephemeralPublicKey) return { ciphertext: bytesToHex(ciphertext), ephemeralPubkeyX: `0x${compressedEphemeral.x.toString(16).padStart(64, '0')}`, ephemeralPubkeyYParity: compressedEphemeral.prefix, nonce: bytesToHex(nonce), tag: bytesToHex(tag), } } function bytesToHex(bytes: Uint8Array): Hex { return `0x${Array.from(bytes, (value) => value.toString(16).padStart(2, '0')).join('')}` as Hex }