使用 @wagmi/vue 的 useBytecode:在 Vue 应用中获取链上合约字节码的完整指南
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
useBytecode是@wagmi/vue提供的一个响应式组合式函数(Composable),用于在 Vue 应用中查询指定地址上的合约字节码(Bytecode)。本文以 site/vue/api/composables/useBytecode.md 为核心,结合仓库中@wagmi/vue、@wagmi/core的源码实现与测试用例,系统讲解useBytecode的导入方式、基础用法、全部参数(address、blockNumber、blockTag、chainId、config、scopeKey)、TanStack Query 扩展选项、返回值结构以及底层调用链,帮助你快速判断一个地址是否为已部署合约、校验合约部署状态,或排查链上合约是否存在。
一、useBytecode 是什么
useBytecode是一个 Vue 组合式函数(Composable),封装了对链上地址字节码的查询逻辑。它基于@wagmi/core的getBytecodeaction 与 TanStack Query(@tanstack/vue-query)构建,返回响应式的查询状态(data、error、status等),当链 ID、地址或区块参数发生变化时,查询结果会自动失效并重新获取。
在真实业务中,useBytecode常见的应用场景包括:
- 判断某个地址是否部署了合约(无合约的地址返回空字节码
0x); - 校验合约部署结果,例如在部署交易确认后确认链上已存在代码;
- 结合
blockNumber/blockTag回溯历史区块,检查某区块高度时合约是否存在; - 配合
chainId在多链应用中按指定链查询。
二、导入方式
在 Vue 项目中,从@wagmi/vue包中导入useBytecode:
import { useBytecode } from '@wagmi/vue'该组合式函数的类型定义位于仓库源码 packages/vue/src/composables/useBytecode.ts:
export type UseBytecodeParameters< config extends Config = Config, selectData = GetBytecodeData, > = Compute< DeepMaybeRef<GetBytecodeOptions<config, selectData> & ConfigParameter<config>> > export type UseBytecodeReturnType<selectData = GetBytecodeData> = UseQueryReturnType<selectData, GetBytecodeErrorType>可见它的参数类型UseBytecodeParameters是GetBytecodeOptions与ConfigParameter的组合,并支持DeepMaybeRef——这意味着所有参数都可以是 Vue 的ref/reactive响应式值,这在动态切换查询目标时非常有用。
三、基础用法
useBytecode最基本的使用方式是指定一个合约地址,返回的data即为该地址的字节码(0x开头的十六进制字符串):
<script setup lang="ts"> import { useBytecode } from '@wagmi/vue' const { data: byteCode } = useBytecode({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', }) </script> <template> Byte Code: {{ byteCode }} </template>在调用前需要先创建并注入 Wagmi 配置。仓库中的配置示例(site/snippets/vue/config.ts)如下:
import { createConfig, http } from '@wagmi/vue' import { mainnet, sepolia } from '@wagmi/vue/chains' export const config = createConfig({ chains: [mainnet, sepolia], transports: { [mainnet.id]: http(), [sepolia.id]: http(), }, })然后在应用入口通过WagmiPlugin(Vue 包中对应 React 的WagmiProvider)注入该配置,useBytecode便会从最近的插件上下文中自动获取config。关于配置的详细说明可参考 site/vue/api/createConfig.md 与 site/vue/api/WagmiPlugin.md。
四、参数详解
useBytecode接收一个对象参数,类型为UseBytecodeParameters。以下是文档与源码确认的全部参数。
1.address
- 类型:
Address | undefined - 说明:要查询字节码的合约地址。
<script setup lang="ts"> import { useBytecode } from '@wagmi/vue' const { data: byteCode } = useBytecode({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', }) </script> <template> Byte Code: {{ byteCode }} </template>值得注意的是,address是必填项。从 packages/core/src/query/getBytecode.ts 的源码可以看到,查询的启用条件与校验都依赖于它:
enabled: Boolean(options.address && (options.query?.enabled ?? true)), queryFn: async (context) => { const [, { scopeKey: _, ...parameters }] = context.queryKey if (!parameters.address) throw new Error('address is required') ... }也就是说:未提供address时查询不会执行(enabled为false);即使强制执行,queryFn也会抛出'address is required'错误。
2.blockNumber
- 类型:
bigint | undefined - 说明:指定在哪个区块高度上查询字节码,可用于回溯历史状态。
<script setup lang="ts"> import { useBytecode } from '@wagmi/vue' const { data: byteCode } = useBytecode({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', blockNumber: 16280770n, }) </script> <template> Byte Code: {{ byteCode }} </template>3.blockTag
- 类型:
'latest' | 'earliest' | 'pending' | 'safe' | 'finalized' | undefined - 说明:指定在哪个区块标签上查询字节码。其中
safe与finalized通常用于 Layer 2(如 OP Stack)链,表示已安全确认或已最终确定的区块。
<script setup lang="ts"> import { useBytecode } from '@wagmi/vue' const { data: byteCode } = useBytecode({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', blockTag: 'safe', }) </script> <template> Byte Code: {{ byteCode }} </template>注意:
blockNumber与blockTag是互斥的区块定位参数,二选一使用。
4.chainId
- 类型:
config['chains'][number]['id'] | undefined - 说明:指定在哪个链上查询。未传入时,默认使用当前激活的链 ID(由
useChainId提供)。这使多链应用可以针对不同链分别查询同一地址的字节码。
<script setup lang="ts"> import { useBytecode } from '@wagmi/vue' import { mainnet } from '@wagmi/vue/chains' const { data: byteCode } = useBytecode({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', chainId: mainnet.id, }) </script> <template> Byte Code: {{ byteCode }} </template>5.config
- 类型:
Config | undefined - 说明:显式指定要使用的
[Config](https://link.gitcode.com/i/5ca64b4729193ab1f573ebad3e7e5953)实例,而不是从最近的WagmiPlugin上下文中获取。适合在测试或需要绕过全局上下文注入的场景中使用。
<script setup lang="ts"> import { useBytecode } from '@wagmi/vue' import { config } from './config' const { data: byteCode } = useBytecode({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', config, }) </script> <template> Byte Code: {{ byteCode }} </template>6.scopeKey
- 类型:
string | undefined - 说明:将查询缓存限定到指定上下文。具有相同
scopeKey与相同其他参数的组合式函数会共享同一份缓存,从而避免不同业务场景之间互相污染查询状态。
<script setup lang="ts"> import { useBytecode } from '@wagmi/vue' import { config } from './config' const { data: byteCode } = useBytecode({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', scopeKey: 'foo', }) </script> <template> Byte Code: {{ byteCode }} </template>7.query(TanStack Query 扩展参数)
除上述业务参数外,useBytecode还支持通过query子对象透传 TanStack Query 的选项(详见 site/shared/query-options.md)。常用选项包括:
| 参数 | 类型 | 说明与默认值 |
|---|---|---|
enabled | boolean \| undefined | 设为false可禁用查询自动执行,常用于依赖查询(Dependent Queries) |
gcTime | number \| Infinity \| undefined | 未使用/非活跃缓存数据的保留时间,默认5 * 60 * 1000(5 分钟),SSR 期间为Infinity |
initialData | GetBytecodeData \| (() => GetBytecodeData) \| undefined | 初始缓存数据;初始数据默认视为过期(除非设置了staleTime) |
staleTime | number \| Infinity \| undefined | 数据被视为过期的毫秒数,默认0;设为Infinity则永不过期 |
refetchInterval | number \| false \| function \| undefined | 轮询刷新频率(毫秒),可用于监控合约部署状态 |
retry | boolean \| number \| function \| undefined | 失败重试次数,客户端默认3,服务端默认0 |
networkMode | 'online' \| 'always' \| 'offlineFirst' \| undefined | 网络模式,默认'online' |
select | ((data: GetBytecodeData) => unknown) \| undefined | 对返回数据做变换,仅影响返回的data,不影响缓存内容 |
一个组合示例:仅在地址存在且组件挂载后才发起查询,并对结果做长度判断:
<script setup lang="ts"> import { useBytecode } from '@wagmi/vue' import { ref } from 'vue' const address = ref<`0x${string}` | undefined>() const { data: byteCode, isLoading, isError } = useBytecode({ address, query: { enabled: () => Boolean(address.value), }, }) </script>说明:Wagmi 内部使用
queryFn与queryKey来驱动查询,因此这两个 TanStack Query 参数不支持用户覆盖。
五、返回值
useBytecode的返回值类型为UseBytecodeReturnType(即UseQueryReturnType<GetBytecodeData, GetBytecodeErrorType>),本质上是 TanStack Query 的观察者结果(详见 site/shared/query-result.md),主要包括:
data:GetBytecodeData,最后一次成功解析的数据(字节码,0x开头的十六进制字符串;若地址无合约,data为null——见下文源码说明),默认为undefined;error:null | GetBytecodeErrorType,查询失败时的错误对象,默认为null;status:'error' | 'pending' | 'success',查询状态;fetchStatus:'fetching' | 'idle' | 'paused',是否正在抓取;- 派生布尔值:
isError、isPending、isSuccess、isLoading、isFetching、isRefetching、isStale等; refetch:手动重新执行查询的函数;failureCount/failureReason:失败次数与失败原因;dataUpdatedAt/errorUpdatedAt:数据与错误最近一次更新的时间戳。
在模板中常用的组合方式是:
<script setup lang="ts"> import { useBytecode } from '@wagmi/vue' const { data: byteCode, isLoading, isError } = useBytecode({ address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', }) </script> <template> <div v-if="isLoading">Loading…</div> <div v-else-if="isError">Failed to load bytecode</div> <div v-else> {{ byteCode ? `Contract deployed (${byteCode.length} hex chars)` : 'No contract at address' }} </div> </template>六、源码原理:从 composable 到链上 RPC
useBytecode的调用链清晰且分层明确,理解它有助于排查问题和进行二次封装。
1. Vue 组合式函数层
源码 packages/vue/src/composables/useBytecode.ts 的核心逻辑如下:
export function useBytecode< config extends Config = ResolvedRegister['config'], selectData = GetBytecodeData, >( parameters: UseBytecodeParameters<config, selectData> = {}, ): UseBytecodeReturnType<selectData> { const params = computed(() => deepUnref(parameters)) const config = useConfig(params) const chainId = useChainId({ config }) const options = computed(() => getBytecodeQueryOptions(config as any, { ...params.value, chainId: params.value.chainId ?? chainId.value, }), ) return useQuery(options as any) as any }它依次完成四件事:
- 用
deepUnref将ref/reactive形式的参数深度解包为普通值,并包装为computed,保证参数变化时自动重建查询; - 通过
useConfig获取配置(优先取显式传入的config,否则取插件上下文中的全局配置); - 通过
useChainId获取当前链 ID,并在未显式传入chainId时作为默认值; - 调用
getBytecodeQueryOptions生成查询选项,交由useQuery(对@tanstack/vue-query的封装,见 packages/vue/src/utils/query.ts)执行。
2. 查询选项层(query key / enabled / queryFn)
packages/core/src/query/getBytecode.ts 负责构建查询:
export function getBytecodeQueryOptions< config extends Config, selectData = GetBytecodeData, >(config: config, options: GetBytecodeOptions<config, selectData> = {}) { return { ...options.query, enabled: Boolean(options.address && (options.query?.enabled ?? true)), queryFn: async (context) => { const [, { scopeKey: _, ...parameters }] = context.queryKey if (!parameters.address) throw new Error('address is required') const bytecode = await getBytecode(config, { ...(parameters as any), address: parameters.address, }) return (bytecode ?? null) as any }, queryKey: getBytecodeQueryKey(options), } } export function getBytecodeQueryKey<config extends Config>(options = {}) { return ['getBytecode', filterQueryOptions(options)] as const }要点:
- 缓存键(queryKey)为
['getBytecode', { address, chainId, blockNumber, blockTag, scopeKey }]。只要这些参数变化,就会产生新的缓存条目,并在chainId等变化时自动重新查询; enabled逻辑:address存在且用户未显式禁用时才执行查询;- 空字节码处理:底层若返回
undefined(如 EOA 地址或不存在合约的地址),会统一归一化为null,方便在模板中做v-if判断。
3. 底层 action 层
最终请求由 core 层的getBytecodeaction 发出(packages/core/src/actions/getBytecode.ts):
export async function getBytecode<config extends Config>( config: config, parameters: GetBytecodeParameters<config>, ): Promise<GetBytecodeReturnType> { const { chainId, ...rest } = parameters const client = config.getClient({ chainId }) const action = getAction(client, viem_getBytecode, 'getBytecode') return action(rest) }它从config中按chainId取出对应的 viem Client,并通过getAction调用 viem 的getBytecode方法,最终走 RPC 的eth_getCode。这与 site/core/api/actions/getBytecode.md 中记录的 core action 一一对应。
七、测试验证:查询选项与 action 的行为契约
仓库为getBytecode提供了完整的单元测试,可用于验证上述行为:
- packages/core/src/query/getBytecode.test.ts 验证了查询选项的构建:默认情况下
enabled为true,且queryKey会精确反映address、chainId、blockNumber、blockTag等参数(例如传入chainId: 456时,queryKey 中出现"chainId": 456;传入blockNumber: 1234567890n时以bigint形式出现在 queryKey 中); - packages/core/src/actions/getBytecode.test.ts 则覆盖了 action 层的
default、blockNumber、blockTag、chainId四类调用场景,确认这些参数都会正确透传给底层 viem action。
八、注意事项与最佳实践
address为必填:未提供时查询不会执行,且queryFn会抛错,因此建议配合响应式ref在地址确定后再渲染组件或再启用查询。- 空地址/EOA 的处理:地址上不存在合约时,
data为null,可用v-else分支提示"该地址无合约",而不要用!data简单判断。 - 区块定位参数互斥:
blockNumber与blockTag二选一,同时传入可能导致不符合预期的查询。 - 多链场景显式传
chainId:虽然默认会跟随当前激活链,但在多链 UI 中建议显式传入目标chainId,避免用户切换网络时数据发生跳变。 - 善用
query扩展项:如需轮询合约部署进度,可设置refetchInterval;如需与其他查询联动,可使用enabled实现依赖查询。 - 类型导入:相关的
GetBytecodeData、GetBytecodeOptions、GetBytecodeQueryKey、getBytecodeQueryOptions、getBytecodeQueryKey等类型与工具函数定义于@wagmi/core/query(Vue 侧通过@wagmi/vue暴露),在编写自定义查询或测试时可从@wagmi/vue/query导入。
总结
useBytecode是@wagmi/vue中一个"小而精"的查询组合式函数:参数层覆盖了地址、区块定位、链选择、配置注入与缓存隔离;查询层由 TanStack Query 提供缓存、重试、轮询等能力;底层则由@wagmi/core的getBytecodeaction 与 viem 的eth_getCode支撑。通过本文的参数表、可运行示例与源码调用链分析,你可以直接在 Vue 应用中接入合约字节码查询,用于合约存在性校验、部署状态监控与历史区块回溯等场景。
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考