Union TypeScript SDK(@unionlabs/client):跨 EVM、Cosmos 与 Aptos 的跨链转账客户端开发指南
本文基于 Union 仓库中 typescript-sdk/README.md 的完整内容,结合 typescript-sdk/ 目录下的源码实现,系统讲解 Union Labs 第一代 TypeScript SDK(npm 包名 @unionlabs/client)的客户端初始化方式、跨链转账 API、地址转换工具、依赖补丁流程与 npm/JSR 双通道发布机制。读完后你可以:在 EVM 与 Cosmos 测试网上用 SDK 发起真实的跨链资产转账;理解 createUnionClient 如何按链 ID 自动分派到 EVM/Cosmos/Aptos 三类底层客户端;并掌握该 SDK 的构建、schema 拉取与发布操作。需要特别注意的是,该 README 开头已明确标注 DEPRECATED,并指向仓库中基于 Effect 的新版 SDK ts-sdk/README.md(包名 @unionlabs/sdk),本文内容适用于维护或研究旧版 @unionlabs/client 的场景。
一、SDK 定位与依赖结构
@unionlabs/client 是 Union 的跨链转账客户端库,用于在 EVM 链、Cosmos 链和 Aptos 之间发起资产转移。从 typescript-sdk/package.json 可以看到其关键依赖:
| 依赖 | 作用 |
|---|---|
viem(peerDependency,固定 2.33.3) |
EVM 链的 RPC 传输、账户与合约读写,http/fallback transport 直接从 viem 透传 |
@cosmjs/proto-signing、@cosmjs/stargate、@cosmjs/amino 等 |
Cosmos 链交易签名(DirectSecp256k1Wallet)与 CosmWasm 交互 |
@aptos-labs/ts-sdk(1.35.0) |
Aptos 链支持 |
neverthrow |
所有转账/查询 API 返回 Result<T, Error>,调用方需显式处理 isErr() 分支 |
ofetch / graphql-request |
访问 Hubble(Union 的链上链下查询服务)做链详情与通道查询 |
@scure/base |
bech32/hex 地址编码转换 |
包本身为 ESM("type": "module"),入口是 dist/index.mjs,由 tsup 构建(npm run build),并通过 tsconfig-paths + gql.tada 做 GraphQL 类型安全查询。
二、初始化客户端:createUnionClient 的三重重载
README「Initiate a client」给出的是 EVM 侧示例:
import { privateKeyToAccount } from "viem/accounts"
import { createUnionClient, http } from "@unionlabs/client"
const client = createUnionClient({
chainId: "80084",
transport: http("https://bartio.rpc.berachain.com"),
account: privateKeyToAccount(`0x${process.env.PRIVATE_KEY}`),
})
在 typescript-sdk/src/mod.ts 中,createUnionClient 声明了三个按参数类型区分的重载(EvmClientParameters / CosmosClientParameters / AptosClientParameters),运行时则通过链 ID 白名单做分派:
- EVM 链 ID 来自 typescript-sdk/src/evm/client.ts,内置
sepolia、holesky、scrollSepolia、arbitrumSepolia、berachainTestnetbArtio(80084即 README 示例中的 Berachain 测试网 Bartio); - Cosmos 链 ID 来自 typescript-sdk/src/cosmos/client.ts,白名单包含
elgafar-1(Stargaze 测试网)、osmo-test-5、union-testnet-8/9、stride-internal-1、bbn-test-5(Babylon 测试网),并附带每条链的默认 RPC 地址(如union-testnet-9对应https://rpc.testnet-9.union.build); - 三个白名单都未命中时直接抛出
Invalid chain id。
各链客户端参数的差异(见 typescript-sdk/src/types.ts):
- EVM(
EvmClientParameters):chainId、transport(viem 的 Http/Fallback/Custom transport)、可选account(viem的Account或0x地址字符串); - Cosmos(
CosmosClientParameters):chainId、transport、可选account(CosmJS 的OfflineSigner,如DirectSecp256k1Wallet)、可选gasPrice: { amount, denom }(Cosmos 侧转账 API 内部会检查 gasPrice 必须提供); - Aptos(
AptosClientParameters):chainId、账户(AptosAccount或浏览器钱包)等。
除了单链客户端,mod.ts 还导出了 createMultiUnionClient(typescript-sdk/src/mod.ts),一次传入多条链的配置数组,返回以 chainId 为键、按链类型映射到 EVM/Cosmos/Aptos 客户端类型映射对象,方便在跨链流程中同时持有源链与目标链客户端。
三、跨链转账实战:从 Stride 测试网转 strd 到 Sepolia
README 给出了一个完整的 Cosmos 侧转账示例——把 Stride 测试网(stride-internal-1)上的 strd 转到 EVM 的 Sepolia(11155111):
import { DirectSecp256k1Wallet } from "@cosmjs/proto-signing"
import { createUnionClient, hexToBytes, http } from "@unionlabs/client"
const PRIVATE_KEY = process.env["PRIVATE_KEY"]
if (!PRIVATE_KEY) throw new Error("Private key not found")
const cosmosAccount = await DirectSecp256k1Wallet.fromKey(
Uint8Array.from(hexToBytes(PRIVATE_KEY)),
"stride"
)
const client = createUnionClient({
account: cosmosAccount,
chainId: "stride-internal-1",
transport: http("stride.testnet-1.stridenet.co")
})
const transfer = await client.transferAsset({
amount: 1n,
autoApprove: true,
denomAddress: "strd",
destinationChainId: "11155111",
receiver: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd"
})
if (transfer.isErr()) {
console.error(transfer.error)
process.exit(1)
}
console.info(transfer.value)
要点拆解:
- 签名账户:Cosmos 侧使用
@cosmjs/proto-signing的DirectSecp256k1Wallet,私钥通过 SDK 导出的hexToBytes(typescript-sdk/src/convert.ts,基于@scure/base)转成Uint8Array,"stride"是钱包的 bech32 前缀; transferAsset返回neverthrow的Result:成功走transfer.value,失败走transfer.error,这与全 SDK「不抛异常、显式结果」的风格一致;autoApprove: true:EVM 侧发起转账前,SDK 会先向 UCS03 合约(或对应中继合约)发起 ERC20 approve(见下文 EVM 流程)。
从源码结构看,旧版(Legacy)转账的完整链路在 EVM 侧是 transferAssetLegacy(typescript-sdk/src/evm/client.ts):
- 以
denomAddress作为baseToken,调用getHubbleChainDetails({ sourceChainId, destinationChainId })获取源/目标通道号(sourceChannel/destinationChannel)与两侧中继合约地址; - 在目标链的 UCS03 合约上读取
predictWrappedToken,预测本次转账到账后铸造出的 quote token 地址; - 若
autoApprove为真,先执行evmApproveTransferAsset授权; - 最终调用
transferAssetFromEvm(simulate默认开启,可先模拟再广播)。
Cosmos 侧对应实现在 typescript-sdk/src/cosmos/client.ts 的 simulateTransaction 中:同源同链走同链转账模拟;跨链时查询 Hubble,若 transferType === "pfm" 则用 createPfmMemo 生成中间转发 memo;发往 Union 测试网(union-testnet-9)时通过 CosmWasm transfer 消息携带 funds,发往 union-testnet-8 时走标准 IBC transfer(sourcePort: "transfer",并附 timeoutHeight)。
新版 transferAsset 与 UCS03 参数表
createEvmClient/createCosmosClient 同时扩展了一个面向 UCS03(Union 的零知识 GM 合约)的新版 transferAsset(typescript-sdk/src/evm/client.ts、typescript-sdk/src/cosmos/client.ts),其参数类型为 TransferAssetParameters(typescript-sdk/src/types.ts):
| 参数 | 类型 | 说明 |
|---|---|---|
baseAmount |
bigint |
源资产数量(最小单位) |
baseToken |
string |
源资产地址(EVM 为 hex 地址,Cosmos 为 denom 或 CW20 合约地址) |
quoteAmount |
bigint |
期望的 quote 侧数量(Legacy 流程中取 amount) |
quoteToken |
string |
quote 侧 token 标识(通常为 predictWrappedToken 的预测结果) |
receiver |
string |
接收者地址,EVM 侧支持 0x 前缀(自动 checksum)或裸 hex(自动补前缀) |
sourceChannelId |
number |
源通道号(对应 transferV2 的 channelId) |
ucs03address |
地址 | UCS03 合约地址(EVM 为 0x…,Cosmos 为 bech32 合约地址) |
wethQuoteToken |
Hex |
EVM 侧 transferV2 额外需要的 quote 侧 WETH 形态 token 地址 |
EVM 侧新版流程直接调用 UCS03 合约的 transferV2(参数顺序为 channelId, receiver, baseToken, baseAmount, quoteToken, quoteAmount, timeoutHeight, timeoutTimestamp, salt),其中 salt 由 generateSalt() 生成以保证每次交易可区分;timeoutHeight 目前硬编码为 0(源码标注了 TODO),Cosmos 侧则通过 cosmwasmTransfer 发送 { transfer: { channel_id, receiver, base_token, ... } } 消息,且当 baseToken 是合法 bech32 合约地址(即 CW20)时不附带原生币 funds。这些细节体现了 SDK 与 evm/contracts/apps 下 UCS03 合约及 cosmwasm/ 合约集之间的调用关系。
四、PFM、Hubble 查询与地址转换工具
PFM 与 Hubble
跨 Cosmos 网络中转(Packet Forward Middleware)依赖一个 JSON memo,typescript-sdk/src/pfm.ts 的 createPfmMemo({ port, channel, receiver }) 生成形如:
{ "forward": { "port": "...", "channel": "...", "receiver": "..." } }
其中 receiver 若带 0x 前缀会被剥掉。同一文件中的 getHubbleChainDetails 目前是一份内置的测试网链详情表(Sepolia/Holesky/union-testnet-9/elgafar-1 的 UCS03 合约地址与通道映射),源码注释表明该逻辑「Will be moved to hubble soon」——真实线上查询走 Hubble 服务。Hubble 的 REST 查询入口在 typescript-sdk/src/query/offchain/hubble.ts(offchainQuery.chains({ includeAssets, includeEndpoints, includeContracts }),默认 https://graphql.union.build/api/rest/v1,6 秒超时、2 次重试),另有 getChannelInfo、getQuoteToken、getRecommendedChannels 等 UCS03 通道查询 API,以及 typescript-sdk/src/query/on-chain.ts 提供的链上状态查询(如 queryCosmosCW20AddressBalance、getCosmosTransactionReceipt)。
地址转换工具
mod.ts 统一导出了 typescript-sdk/src/convert.ts 的一组跨生态地址工具,全部基于 @scure/base:
bech32AddressToHex({ address }):bech32 → hex(union1…→0x…);hexAddressToBech32({ address, bech32Prefix }):hex → 指定前缀的 bech32;bech32ToBech32Address({ address, toPrefix }):同格式换前缀(如union→stride);bytesToBech32Address/bech32ToBytes:字节数组与 bech32 互转;bytesToHex/hexToBytes:字节与 hex 互转(hexToBytes自动处理0x前缀)。
这些工具正是 Cosmos 账户与 EVM 接收地址之间、以及 PFM memo 中地址编码的基础设施;配套校验函数(isValidBech32Address、isValidEvmAddress、truncateAddress 等)位于 typescript-sdk/src/utilities/address.ts,测试见 typescript-sdk/test/address.test.ts。
五、开发、Schema 拉取与发布流程
README 的「Development」章节给出了三个维护流程,均对应仓库中真实存在的脚本:
1. 拉取最新 schema
nix run .#ts-sdk-fetch-schema -L
该 nix 入口定义在 typescript-sdk/typescript-sdk.nix,用于同步 Union 的 GraphQL/链上 schema 到 src/generated/ 与 src/abi/(含 ucs-01.ts、ucs-03.ts 等 ABI 文件),保证 SDK 与合约/接口定义一致。
2. 发布到 npm
npm run build # important!
npm publish --access='public' --no-git-tags
注意必须先 build(tsup --config='tsup.config.ts'),因为 files 字段只发布 dist、LICENSE、README.md、package.json。发布前可用 npm run check-package(publint --strict + @arethetypeswrong/cli)校验包结构。
3. 发布到 JSR
bun ./scripts/publish.ts
typescript-sdk/scripts/publish.ts 支持 bun scripts/publish.ts --period patch 或 --period minor --dry-run:它读取 typescript-sdk/jsr.json(包名同为 @union/client,exports 指向 ./src/mod.ts)与 package.json 的版本号,校验 SDK 内合约与注册表一致后完成 JSR 发布(README 中的 JSR/NPM 徽标即对应这两个发布渠道)。
4. 补丁第三方依赖(patch-package 流程)
README 还给出了为 node_modules 打补丁的标准流程:
npm install
npm install --package-lock-only
# edit node_modules/foo
./node_modules/patch-package/index.js foo
# a patch will be generated for foo in the patches/ dir
仓库根目录的 patches/ 目录中确实存放了本流程的产物,如 @cosmjs__amino@0.33.1.patch、@cosmjs__stargate.patch、@cosmjs__tendermint-rpc@0.33.1.patch——即该 SDK 通过 patch-package 维护了对 CosmJS 系列的本地修改,安装时会被 patch-package 钩子自动应用。
六、小结与迁移提示
@unionlabs/client 展示了 Union 第一代 TS SDK 的完整形态:以 viem + CosmJS + Aptos SDK 为三大底座,用 createUnionClient 按链 ID 统一分派,以 neverthrow Result 贯穿转账与查询 API,围绕 UCS03 transferV2 与 Hubble 通道配置实现跨链资产转移,并配套地址转换、PFM memo 构建、schema 同步与 npm/JSR 双通道发布工具链。由于 README 已声明该目录弃用,新开发建议直接使用基于 Effect 的 ts-sdk(@unionlabs/sdk,构建与发布走 nix build .#ts-sdk -L / nix run .#publish-ts-sdk -L);本文所梳理的旧版 API 与流程则适合用于阅读历史代码、排查线上旧集成或对比两代 SDK 的设计演进。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00