首页
/ Union TypeScript SDK(@unionlabs/client):跨 EVM、Cosmos 与 Aptos 的跨链转账客户端开发指南

Union TypeScript SDK(@unionlabs/client):跨 EVM、Cosmos 与 Aptos 的跨链转账客户端开发指南

2026-09-04 13:42:25作者:董宙帆

本文基于 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-sdk1.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,内置 sepoliaholeskyscrollSepoliaarbitrumSepoliaberachainTestnetbArtio80084 即 README 示例中的 Berachain 测试网 Bartio);
  • Cosmos 链 ID 来自 typescript-sdk/src/cosmos/client.ts,白名单包含 elgafar-1(Stargaze 测试网)、osmo-test-5union-testnet-8/9stride-internal-1bbn-test-5(Babylon 测试网),并附带每条链的默认 RPC 地址(如 union-testnet-9 对应 https://rpc.testnet-9.union.build);
  • 三个白名单都未命中时直接抛出 Invalid chain id

各链客户端参数的差异(见 typescript-sdk/src/types.ts):

  • EVMEvmClientParameters):chainIdtransport(viem 的 Http/Fallback/Custom transport)、可选 accountviemAccount0x 地址字符串);
  • CosmosCosmosClientParameters):chainIdtransport、可选 account(CosmJS 的 OfflineSigner,如 DirectSecp256k1Wallet)、可选 gasPrice: { amount, denom }(Cosmos 侧转账 API 内部会检查 gasPrice 必须提供);
  • AptosAptosClientParameters):chainId、账户(AptosAccount 或浏览器钱包)等。

除了单链客户端,mod.ts 还导出了 createMultiUnionClienttypescript-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)

要点拆解:

  1. 签名账户:Cosmos 侧使用 @cosmjs/proto-signingDirectSecp256k1Wallet,私钥通过 SDK 导出的 hexToBytestypescript-sdk/src/convert.ts,基于 @scure/base)转成 Uint8Array"stride" 是钱包的 bech32 前缀;
  2. transferAsset 返回 neverthrowResult:成功走 transfer.value,失败走 transfer.error,这与全 SDK「不抛异常、显式结果」的风格一致;
  3. autoApprove: true:EVM 侧发起转账前,SDK 会先向 UCS03 合约(或对应中继合约)发起 ERC20 approve(见下文 EVM 流程)。

从源码结构看,旧版(Legacy)转账的完整链路在 EVM 侧是 transferAssetLegacytypescript-sdk/src/evm/client.ts):

  1. denomAddress 作为 baseToken,调用 getHubbleChainDetails({ sourceChainId, destinationChainId }) 获取源/目标通道号(sourceChannel/destinationChannel)与两侧中继合约地址;
  2. 目标链的 UCS03 合约上读取 predictWrappedToken,预测本次转账到账后铸造出的 quote token 地址;
  3. autoApprove 为真,先执行 evmApproveTransferAsset 授权;
  4. 最终调用 transferAssetFromEvmsimulate 默认开启,可先模拟再广播)。

Cosmos 侧对应实现在 typescript-sdk/src/cosmos/client.tssimulateTransaction 中:同源同链走同链转账模拟;跨链时查询 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 合约)的新版 transferAssettypescript-sdk/src/evm/client.tstypescript-sdk/src/cosmos/client.ts),其参数类型为 TransferAssetParameterstypescript-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 源通道号(对应 transferV2channelId
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),其中 saltgenerateSalt() 生成以保证每次交易可区分;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.tscreatePfmMemo({ 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.tsoffchainQuery.chains({ includeAssets, includeEndpoints, includeContracts }),默认 https://graphql.union.build/api/rest/v1,6 秒超时、2 次重试),另有 getChannelInfogetQuoteTokengetRecommendedChannels 等 UCS03 通道查询 API,以及 typescript-sdk/src/query/on-chain.ts 提供的链上状态查询(如 queryCosmosCW20AddressBalancegetCosmosTransactionReceipt)。

地址转换工具

mod.ts 统一导出了 typescript-sdk/src/convert.ts 的一组跨生态地址工具,全部基于 @scure/base

  • bech32AddressToHex({ address }):bech32 → hex(union1…0x…);
  • hexAddressToBech32({ address, bech32Prefix }):hex → 指定前缀的 bech32;
  • bech32ToBech32Address({ address, toPrefix }):同格式换前缀(如 unionstride);
  • bytesToBech32Address / bech32ToBytes:字节数组与 bech32 互转;
  • bytesToHex / hexToBytes:字节与 hex 互转(hexToBytes 自动处理 0x 前缀)。

这些工具正是 Cosmos 账户与 EVM 接收地址之间、以及 PFM memo 中地址编码的基础设施;配套校验函数(isValidBech32AddressisValidEvmAddresstruncateAddress 等)位于 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.tsucs-03.ts 等 ABI 文件),保证 SDK 与合约/接口定义一致。

2. 发布到 npm

npm run build # important!
npm publish --access='public' --no-git-tags

注意必须先 buildtsup --config='tsup.config.ts'),因为 files 字段只发布 distLICENSEREADME.mdpackage.json。发布前可用 npm run check-packagepublint --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/clientexports 指向 ./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 的设计演进。

登录后查看全文
热门项目推荐
相关项目推荐