Cardano
UKey Wallet Cardano Provider 用于在 Web 应用中连接 Cardano 钱包、读取地址和 UTxO、签名交易、确认提交交易以及签名链下数据。页面中可通过 window.cardano.ukey 访问,也提供 window.cardano.nami 兼容入口。
ℹ️
UKey Wallet 实现 CIP-30 Cardano dApp Connector,并提供 Nami 兼容性,方便现有 Cardano DApp 迁移。
快捷链接
找 Provider
// UKey Wallet 同时暴露 ukey 与 nami 两套接口
const ukey = window.cardano?.ukey;
const nami = window.cardano?.nami;
// 确认是否可用
if (!ukey) {
throw new Error("未检测到 UKey Wallet Cardano provider");
}
// 读取钱包信息
console.log("钱包名称:", ukey.name); // 'UKey Wallet'
console.log("API 版本号:", ukey.apiVersion); // '0.1.0'
console.log("图标地址:", ukey.icon);
快上手
请求启用钱包
// 向钱包申请访问授权
const api = await window.cardano.ukey.enable();
// 此时即可调用完整 API
const networkId = await api.getNetworkId();
console.log("当前网络:", networkId === 1 ? "主网" : "测试网");
检查是否已启用
const isEnabled = await window.cardano.ukey.isEnabled();
if (isEnabled) {
const api = await window.cardano.ukey.enable();
// 继续使用 API...
}
账户信息
读取网络 ID
const api = await window.cardano.ukey.enable();
const networkId = await api.getNetworkId();
// 0 表示测试网,1 表示主网
console.log("当前网络:", networkId);
读取余额
const balance = await api.getBalance();
// 返回值为 CBOR 编码内容(十六进制字符串)
console.log("CBOR 余额:", balance);
// 借助 cardano-serialization-lib 解码
import { Value } from "@emurgo/cardano-serialization-lib-browser";
const value = Value.from_bytes(Buffer.from(balance, "hex"));
const lovelace = value.coin().to_str();
console.log("当前余额:", parseInt(lovelace) // 1000000, "ADA");
读取地址
// 读取已使用地址(已有交易历史)
const usedAddresses = await api.getUsedAddresses();
console.log("已有使用记录的地址:", usedAddresses);
// 读取未使用地址(新地址)
const unusedAddresses = await api.getUnusedAddresses();
console.log("尚未使用的地址:", unusedAddresses);
// 读取找零地址
const changeAddress = await api.getChangeAddress();
console.log("找零用途地址:", changeAddress);
// 读取奖励/质押地址
const rewardAddresses = await api.getRewardAddresses();
console.log("奖励/质押地址:", rewardAddresses);
读取 UTxO
// 读取所有 UTxOs
const utxos = await api.getUtxos();
console.log("读取到的 UTxOs:", utxos);
// 读取满足最小金额的 UTxOs
const utxosWithAmount = await api.getUtxos(
"1000000", // 最低参考值为 1 ADA(CBOR 编码)
);
// 用分页方式读取
const paginatedUtxos = await api.getUtxos(undefined, {
page: 0,
limit: 10,
});
交易项
签交易
// transaction 这里要传 CBOR 编码后的十六进制字符串
const signedTx = await api.signTx(transactionCbor, partialSign);
// partialSign flag: boolean
// - false: 会把钱包持有的所有输入都签上
// - true: 只签那些明确归当前钱包所有的输入(适合多签)
console.log("签名后的交易数据:", signedTx);
确认提交交易
// 确认提交签名后的交易
const txHash = await api.submitTx(signedTransactionCbor);
console.log("交易哈希:", txHash);
完整交易流程
import {
TransactionBuilder,
TransactionBuilderConfigBuilder,
LinearFee,
BigNum,
Address,
TransactionOutput,
Value,
} from "@emurgo/cardano-serialization-lib-browser";
async function submitAdaTransfer(recipientAddress, amountLovelace) {
const api = await window.cardano.ukey.enable();
// 读取 UTxOs
const utxos = await api.getUtxos();
const changeAddress = await api.getChangeAddress();
// 组装交易
const txBuilder = TransactionBuilder.new(
TransactionBuilderConfigBuilder.new()
.fee_algo(LinearFee.new(BigNum.from_str("44"), BigNum.from_str("155381")))
.pool_deposit(BigNum.from_str("500000000"))
.key_deposit(BigNum.from_str("2000000"))
.max_value_size(5000)
.max_tx_size(16384)
.coins_per_utxo_byte(BigNum.from_str("4310"))
.build(),
);
// 从 UTxO 列表里补充输入项
utxos.forEach((utxo) => {
// 把 UTxO 添加为输入...
});
// 加入输出
txBuilder.add_output(
TransactionOutput.new(
Address.from_bech32(recipientAddress),
Value.new(BigNum.from_str(amountLovelace)),
),
);
// 配置找零地址
txBuilder.add_change_if_needed(Address.from_bech32(changeAddress));
// 组装交易
const tx = txBuilder.build_tx();
const txCbor = Buffer.from(tx.to_bytes()).toString("hex");
// 执行签名
const signedTxCbor = await api.signTx(txCbor, false);
// 执行确认提交
const txHash = await api.submitTx(signedTxCbor);
console.log("交易哈希:", txHash);
return txHash;
}
数据签名
签名数据 (CIP-8)
数据签名可用于登录认证、地址所有权证明和链下授权。建议在消息中加入 nonce 和应用来源:
const api = await window.cardano.ukey.enable();
const addresses = await api.getUsedAddresses();
const address = addresses[0];
// 这里准备待签名的消息内容
const payload = Buffer.from("Cardano demo message").toString("hex");
const signature = await api.signData(address, payload);
console.log({
signature: signature.signature, // COSE_Sign1 格式签名
key: signature.key, // COSE_Key 公钥数据
});
验证签名
import { COSESign1, COSEKey } from "@emurgo/cardano-message-signing-browser";
const coseSign1 = COSESign1.from_bytes(Buffer.from(signature.signature, "hex"));
const coseKey = COSEKey.from_bytes(Buffer.from(signature.key, "hex"));
// 读取签名后的 payload
const signedPayload = coseSign1.payload();
// 校验签名
const publicKey = coseKey.header(0); // 读取 Ed25519 密钥
// 校验逻辑...
事件流
看账户变化
const api = await window.cardano.ukey.enable();
// 兼容 Nami 的事件 API
api.experimental.on("accountChange", (addresses) => {
console.log("当前账户已切换为:", addresses);
});
API说明
钱包接口 (CIP-30)
| 属性/方法 | 说明 |
|---|---|
name | 钱包名称 ('UKey Wallet') |
icon | 钱包图标 URL |
apiVersion | API 版本要求 |
isEnabled() | 核验是否已启用 |
enable() | 请求钱包访问权限 |
dApp API(启用后)
| 方法 | 说明 |
|---|---|
getNetworkId() | 获取网络 (0=测试网, 1=主网) |
getBalance() | 获取总余额 (CBOR) |
getUtxos(amount?, paginate?) | 获取 UTxOs |
getUsedAddresses() | 获取已使用地址 |
getUnusedAddresses() | 获取未使用地址 |
getChangeAddress() | 获取找零地址 |
getRewardAddresses() | 获取质押地址 |
signTx(tx, partialSign?) | 签名交易 |
signData(addr, payload) | 签名数据 (CIP-8) |
submitTx(tx) | 确认提交交易 |
实验性 API
| 方法 | 说明 |
|---|---|
experimental.getCollateral() | 获取抵押 UTxOs |
experimental.on(event, cb) | 订阅事件 |
experimental.off() | 取消订阅 |
类型集
type Cbor = string; // 以十六进制表示的 CBOR
interface Paginate {
page: number;
limit: number;
}
interface Cip30DataSignature {
signature: string; // COSE_Sign1 的十六进制结果
key: string; // COSE_Key 的十六进制结果
}
type NetworkId = 0 | 1; // 0 表示测试网,1 表示主网
事件流
| 事件 | 说明 |
|---|---|
accountChange | 当前账户已切换 |
处理异常
try {
const api = await window.cardano.ukey.enable();
} catch (error) {
if (error.code === -1) {
console.log("用户拒绝了本次连接");
} else if (error.code === -2) {
console.log("没有返回可用账户");
} else {
console.error("执行报错:", error.message);
}
}
常见异常
| 错误码 | 说明 |
|---|---|
| -1 | 用户取消了操作 |
| -2 | 未匹配到账户 |
| -3 | 网络参数不正确 |
使用 Lucid
Lucid 可以简化交易构造和确认提交流程:
npm install lucid-cardano
import { Lucid } from "lucid-cardano";
// 通过 UKey Wallet 初始化 Lucid
const lucid = await Lucid.new(
new Blockfrost("https://cardano-mainnet.blockfrost.io/api", "demo-api-key"),
"Mainnet",
);
// 连接 UKey Wallet 设备
const api = await window.cardano.ukey.enable();
lucid.selectWallet(api);
// 发起 ADA 转账
const tx = await lucid
.newTx()
.payToAddress("addr1demoexample...", { lovelace: 5000000n })
.complete();
const signedTx = await tx.sign().complete();
const txHash = await signedTx.submit();
console.log("交易哈希:", txHash);
使用 Mesh
Mesh 也可以直接使用浏览器钱包能力:
npm install @meshsdk/core
import { BrowserWallet, Transaction } from "@meshsdk/core";
// 建立到 UKey Wallet 的连接
const wallet = await BrowserWallet.enable("ukey");
// 读取钱包信息
const balance = await wallet.getBalance();
const addresses = await wallet.getUsedAddresses();
// 组装并发送交易
const tx = new Transaction({ initiator: wallet }).sendLovelace(
"addr1demoexample...",
"5000000",
);
const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
Nami 兼容性
如需兼容已有 Nami 接入,也可以通过 window.cardano.nami 使用同一套能力:
// 通过 Nami 接口调用
const nami = window.cardano.nami;
const api = await nami.enable();
// 与 UKey Wallet 保持一致的 API
const balance = await api.getBalance();