From 770b0d64f2a71b2b00fca095e7a66c27fc5c7793 Mon Sep 17 00:00:00 2001 From: Jonas Daniels Date: Mon, 26 Feb 2024 09:33:20 -0800 Subject: [PATCH] feat(adapters): Add toViemPublicClient and toViemWalletClient functions --- packages/thirdweb/src/adapters/viem.ts | 153 ++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 1 deletion(-) diff --git a/packages/thirdweb/src/adapters/viem.ts b/packages/thirdweb/src/adapters/viem.ts index 7be3a56b309..48cb3a717b8 100644 --- a/packages/thirdweb/src/adapters/viem.ts +++ b/packages/thirdweb/src/adapters/viem.ts @@ -1,9 +1,22 @@ -import type { GetContractReturnType } from "viem"; +import { + createPublicClient, + http, + type GetContractReturnType, + type PublicClient, + type Chain as ViemChain, + type WalletClient, + createWalletClient, + custom, +} from "viem"; import { getContract, type ThirdwebContract } from "../contract/contract.js"; import type { Abi } from "abitype"; import type { Chain } from "../chains/types.js"; import type { ThirdwebClient } from "../client/client.js"; import { resolveContractAbi } from "../contract/actions/resolve-abi.js"; +import { getRpcUrlForChain } from "../chains/utils.js"; +import type { Account, Wallet } from "../wallets/interfaces/wallet.js"; +import type { Prettify } from "../utils/type-utils.js"; +import { toAccount } from "viem/accounts"; export const viemAdapter = { contract: { @@ -35,6 +48,43 @@ export const viemAdapter = { */ toViem: toViemContract, }, + + /** + * Converts options to a Viem public client. + * @param options - The options for creating the Viem public client. + * @returns The Viem public client. + * @example + * ```ts + * import { viemAdapter } from "thirdweb/adapters"; + * + * const publicClient = viemAdapter.publicClient.toViem({ + * chain: ethereum, + * client, + * }); + * ``` + */ + publicClient: { + toViem: toViemPublicClient, + }, + + walletClient: { + /** + * Converts options to a Viem Wallet client. + * @param options - The options for creating the Viem Wallet client. + * @returns The Viem Wallet client. + * @example + * ```ts + * import { viemAdapter } from "thirdweb/adapters"; + * + * const walletClient = viemAdapter.walletClient.toViem({ + * wallet: wallet, + * client, + * chain: ethereum, + * }); + * ``` + */ + toViem: toViemWalletClient, + }, }; type FromViemContractOptions = { @@ -62,3 +112,104 @@ async function toViemContract( abi: await resolveContractAbi(contract), }; } + +type ToViemPublicClientOptions = { + client: ThirdwebClient; + chain: Chain; +}; + +function toViemPublicClient(options: ToViemPublicClientOptions): PublicClient { + const { chain, client } = options; + const rpcUrl = getRpcUrlForChain({ chain, client }); + const viemChain: ViemChain = { + id: chain.id, + name: chain.name || "", + rpcUrls: { + default: { http: [rpcUrl] }, + }, + nativeCurrency: { + name: chain.nativeCurrency?.name || "Ether", + symbol: chain.nativeCurrency?.symbol || "ETH", + decimals: chain.nativeCurrency?.decimals || 18, + }, + }; + return createPublicClient({ + transport: http(rpcUrl, { + fetchOptions: client.secretKey + ? { + headers: { + "x-secret-key": client.secretKey, + }, + } + : undefined, + }), + chain: viemChain, + }); +} + +type ToViemWalletClientOptions = Prettify< + ( + | { + wallet: Wallet; + account?: never; + } + | { + wallet?: never; + account: Account; + } + ) & { + client: ThirdwebClient; + chain: Chain; + } +>; + +function toViemWalletClient(options: ToViemWalletClientOptions): WalletClient { + const account = options.account || options.wallet.getAccount(); + // this *may* exist as a private function on the wallet + const provider = (options.wallet as any)?.provider; + if (!account) { + throw new Error("Wallet not connected."); + } + + const { chain, client } = options; + const rpcUrl = getRpcUrlForChain({ chain, client }); + const viemChain: ViemChain = { + id: chain.id, + name: chain.name || "", + rpcUrls: { + default: { http: [rpcUrl] }, + }, + nativeCurrency: { + name: chain.nativeCurrency?.name || "Ether", + symbol: chain.nativeCurrency?.symbol || "ETH", + decimals: chain.nativeCurrency?.decimals || 18, + }, + }; + + const transport = provider + ? custom(provider) + : http(rpcUrl, { + fetchOptions: options.client.secretKey + ? { headers: { "x-secret-key": options.client.secretKey } } + : undefined, + }); + + return createWalletClient({ + transport, + account: account.signTransaction + ? toAccount({ + address: account.address, + signMessage: account.signMessage, + signTransaction: (tx) => { + if (!account.signTransaction) { + throw new Error("signTransaction not supported"); + } + return account.signTransaction(tx); + }, + signTypedData: account.signTypedData, + }) + : account.address, + chain: viemChain, + key: "thirdweb-wallet", + }); +}