跳到主要内容

币种级二维码

ur:crypto-accountur:crypto-hdkeyur:crypto-multi-accountsplain address

币种级二维码使用与其他二维码签名器相同的 UR 类型(遵循 Blockchain Commons 和 Keystone 的注册表),因此已能导入 Keystone 风格账户的钱包通常无需修改即可读取。在 App 中的位置:Connect › Coin Level,然后选择一条链。

链默认导出备选
Bitcoinur:crypto-account,包含一个账户 xpub(可选 Native SegWit、Taproot、Nested SegWit 或 Legacy)纯地址;多签共同签名方 xpub
EVMur:crypto-hdkey,即 m/44'/60'/0' 账户节点纯地址(索引 0)
Solanaur:crypto-multi-accounts,包含一个预设密钥纯地址
TRON任意索引 m/44'/195'/0'/0/i 的纯地址—

App 中的每个二维码都有一个 Payload 区域,显示解码后的内容,与本页的 JSON 视图完全一致。整数 CBOR 键以其在注册表中的字段名显示;指纹为 32 位整数(1942346250 = 0x73C5DA0A)。

Bitcoin:crypto-account​

crypto-account = {
1: master-fingerprint (uint32),
2: [ output-descriptor ] ; exactly one
}
output-descriptor = script tag(s) wrapping 303(crypto-hdkey)
地址类型账户路径描述符标签脚本
Native SegWitm/84'/0'/0'404(303(key))wpkh
Taprootm/86'/0'/0'409(303(key))tr(BIP86)
Nested SegWitm/49'/0'/0'400(404(303(key)))sh(wpkh)
Legacym/44'/0'/0'403(303(key))pkh

其中的 crypto-hdkey 包含账户公钥(33 字节)、链码、带来源指纹和深度的来源路径、父指纹,以及名称 BitTool Signer。收款地址为 …/0/i,找零地址为 …/1/i。

如果你自己解析,有两个细节需要注意:

  • 输出描述符的写法与 Keystone 和 Sparrow 相同,不带 BCR-2020-015 中的 308(crypto-output) 包装。
  • Taproot 使用标签 409(tr),并携带未经 tweak 的内部公钥;派生地址时需应用 BIP86 tweak。较旧的注册表库不认识标签 409,会悄悄将其丢弃(正因如此,下面的示例解码器直接读取标签)。
m/84'/0'/0' · crypto-account第一个收款地址:bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu
UR:CRYPTO-ACCOUNT/OEADCYJKSKTNBKAOLYTAADMWTAADDLPDAOWKAXHDCLAOJOKNIDZCPSSAJTPTRPFRCECFKKAMYKJTVTCSBTBDTKCFIYVYOETNEEYKWFNBNYNDAHTAADEHNBAMTAADDYOTADLNCSGHYKAEYKAEYKAOCYJKSKTNBKAXAXAAHDCXGEGUNBPYCLRHUOMDLNNSGLMOOYHSCFGLAXRTWSFHYKADGESWMOWKEOSSKOGHMHZTAYCYKBWFDNUYASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYDPCXPECF
解码后的载荷
{
"master-fingerprint": 1942346250,
"output-descriptors": [
{
"witness-public-key-hash": {
"crypto-hdkey": {
"is-private": false,
"key-data": "02707a62fdacc26ea9b63b1c197906f56ee0180d0bcf1966e1a2da34f5f3a09a9b",
"use-info": {
"crypto-coin-info": {}
},
"origin": {
"crypto-keypath": {
"components": [
84,
true,
0,
true,
0,
true
],
"source-fingerprint": 1942346250,
"depth": 3
}
},
"chain-code": "4a53a0ab21b9dc95869c4e92a161194e03c0ef3ff5014ac692f433c4765490fc",
"parent-fingerprint": 2129865691,
"name": "BitTool Signer",
"note": "BitTool Signer public account"
}
}
}
]
}
二维码文本
UR:CRYPTO-ACCOUNT/OEADCYJKSKTNBKAOLYTAADMWTAADDLPDAOWKAXHDCLAOJOKNIDZCPSSAJTPTRPFRCECFKKAMYKJTVTCSBTBDTKCFIYVYOETNEEYKWFNBNYNDAHTAADEHNBAMTAADDYOTADLNCSGHYKAEYKAEYKAOCYJKSKTNBKAXAXAAHDCXGEGUNBPYCLRHUOMDLNNSGLMOOYHSCFGLAXRTWSFHYKADGESWMOWKEOSSKOGHMHZTAYCYKBWFDNUYASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYDPCXPECF

Bitcoin 多签共同签名方​

对于多签钱包,BitTool Signer 以 crypto-account 格式导出一个共同签名方(cosigner)密钥,使用 BIP48 路径:

类型账户路径描述符标签
P2WSHm/48'/0'/0'/2'401(303(key))(wsh)
P2SH-P2WSHm/48'/0'/0'/1'400(401(303(key)))(sh(wsh))

这只是其中一个参与方。它不包含门限、其他共同签名方,也不包含最终地址。你的钱包需要根据所有共同签名方的导出数据构建完整的策略(例如排序的 2-of-3 wsh(sortedmulti(…)))。

P2WSH 共同签名方测试向量
解码后的载荷
{
"master-fingerprint": 1942346250,
"output-descriptors": [
{
"witness-script-hash": {
"crypto-hdkey": {
"is-private": false,
"key-data": "021a3bf5fbf737d0f36993fd46dc4913093beb532d654fe0dfd98bd27585dc9f29",
"use-info": {
"crypto-coin-info": {}
},
"origin": {
"crypto-keypath": {
"components": [
48,
true,
0,
true,
0,
true,
2,
true
],
"source-fingerprint": 1942346250,
"depth": 4
}
},
"chain-code": "bba0c7ca160a870efeb940ab90d0f4284fea1b5e0d2117677e823fc37e2d5763",
"parent-fingerprint": 485660438,
"name": "BitTool Signer",
"note": "BitTool Signer public account"
}
}
}
]
}
二维码文本
UR:CRYPTO-ACCOUNT/OEADCYJKSKTNBKAOLYTAADMETAADDLPDAOWKAXHDCLAOCYFRYKZOYLEMTIWFINMUZCFGUOGABWASFRWMGUDPIHGWVTURTALUTDKPLPUONEDTAHTAADEHNBAMTAADDYOTADLOCSDYYKAEYKAEYKAOYKAOCYJKSKTNBKAXAAAAHDCXRKNBSTSGCMBKLTBAZERHFZPYMHTIWKDEGWWDCWHYBTCLCHIOKBLFFHSRKBDPHGIAAYCYCEWZMSCMASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYVEGWJLEO

EVM:crypto-hdkey​

EVM 导出的是账户节点 m/44'/60'/0',形式为顶层 crypto-hdkey(EIP-4527),也就是 MetaMask、Rabby、imToken 等钱包所导入的格式:

键字段值
2is-privatefalse
3key-data33 字节压缩公钥
4chain-code32 字节
5use-info305({1: 60})
6origin304({1: [44, true, 60, true, 0, true], 2: fingerprint, 3: 3})
7children304({1: [0, false, [], false]}),即 0/*
8parent-fingerprintuint32
9, 10name, note"BitTool Signer"、"BitTool Signer public account"

派生 …/0/i 即可得到地址。所有支持的 EVM 网络共用同一个密钥。

m/44'/60'/0' · crypto-hdkey第一个地址:0x9858EfFD232B4033E47d90003D41EC34EcaEda94
UR:CRYPTO-HDKEY/PTAOWKAXHDCLAOWDVEROKOPDINHSEEROISYALKSAYKCTJSHEDPRNUYJYFGROVAWEWFTYGHCEGLRPKGAHTAADEHOYADCSFNAMTAADDYOTADLNCSDWYKCSFNYKAEYKAOCYJKSKTNBKAXAXAAHDCXTPLFJSLUKNFWLAISAXWYPALBJYLSWZAMCXHSCYUYLOZTMWFNLDLGSKPYPTGSDECFAYCYTEDMFEAYATTAADDYOYADLRAEWKLAWKASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYMUFLSAVD
解码后的载荷
{
"is-private": false,
"key-data": "02eae4b876a8696134b868f88cc2f51f715f2dbedb7446b8e6edf3d4541c4eb67b",
"use-info": {
"crypto-coin-info": {
"type": 60
}
},
"origin": {
"crypto-keypath": {
"components": [
44,
true,
60,
true,
0,
true
],
"source-fingerprint": 1942346250,
"depth": 3
}
},
"chain-code": "d882718b7a42806803eeb17f7483f20620611adb88fc943c898dc5aba94c2819",
"parent-fingerprint": 3543024904,
"children": {
"crypto-keypath": {
"components": [
0,
false,
[],
false
]
}
},
"name": "BitTool Signer",
"note": "BitTool Signer public account"
}
二维码文本
UR:CRYPTO-HDKEY/PTAOWKAXHDCLAOWDVEROKOPDINHSEEROISYALKSAYKCTJSHEDPRNUYJYFGROVAWEWFTYGHCEGLRPKGAHTAADEHOYADCSFNAMTAADDYOTADLNCSDWYKCSFNYKAEYKAOCYJKSKTNBKAXAXAAHDCXTPLFJSLUKNFWLAISAXWYPALBJYLSWZAMCXHSCYUYLOZTMWFNLDLGSKPYPTGSDECFAYCYTEDMFEAYATTAADDYOYADLRAEWKLAWKASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYMUFLSAVD

Solana:crypto-multi-accounts​

Solana 密钥全部经过硬化派生,因此无法从中继续派生。导出内容为一个预设密钥(即 App 中选中的那个),以 crypto-hdkey 的形式承载 32 字节的 Ed25519 公钥:

crypto-multi-accounts = {
1: master-fingerprint,
2: [ 303({ 2: false, 3: <32-byte public key>, 5: 305({1: 501}), 6: 304(<path>), 9: name, 10: note }) ],
3: "BitTool Signer", ; device
5: "1.0.0" ; version
}
预设路径
Standardm/44'/501'/0'/0'
Trustm/44'/501'/0'
Legacym/44'/501'

地址就是这 32 字节公钥的 Base58 编码。

解码后的载荷(standard 预设)
{
"master-fingerprint": 1942346250,
"keys": [
{
"crypto-hdkey": {
"is-private": false,
"key-data": "f036276246a75b9de3349ed42b15e232f6518fc20f5fcd4f1d64e81f9bd258f7",
"use-info": {
"crypto-coin-info": {
"type": 501
}
},
"origin": {
"crypto-keypath": {
"components": [
44,
true,
501,
true,
0,
true,
0,
true
],
"source-fingerprint": 1942346250,
"depth": 4
}
},
"name": "BitTool Signer",
"note": "BitTool Signer public account"
}
}
],
"device": "BitTool Signer",
"version": "1.0.0"
}
二维码文本
UR:CRYPTO-MULTI-ACCOUNTS/OXADCYJKSKTNBKAOLYTAADDLOLAOWKAXHDCXWTENDIIDFGOSHPNTVLEENNTYDNBZVOEYYNGYMYSABSHESNGWCAIEVSCTNDTDHDYLAHTAADEHOYADCFADYKAMTAADDYOTADLOCSDWYKCFADYKYKAEYKAEYKAOCYJKSKTNBKAXAAASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYAXJTFWINJYGHJLJLJZCXGUINIOJTIHJPAHIHEHDMDYDMDYMSFNHFVA

纯地址​

Address 选项只把地址本身作为二维码文本导出:不带 URI scheme、不带金额,也没有 UR 封装。它适用于无法导入密钥的观察钱包。TRON 始终以这种方式导出,索引由用户任选(m/44'/195'/0'/0/i,0 ≤ i < 231)。

导出二维码文本
Bitcoin(Native SegWit,索引 0)bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu
EVM(索引 0)0x9858EfFD232B4033E47d90003D41EC34EcaEda94
Solana(standard)HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk
TRON 索引 0TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH
TRON 索引 5TBdYXtwq18cAhi1BA574TrP6tw2G86anu1

仅通过地址配对的钱包无法在签名请求中添加派生数据。BitTool Signer 仍然可以匹配 EVM 索引 0 和 TRON 索引 0–99 的纯地址请求,但如果你打算签名,请优先使用密钥导出。

全部解码​

一个函数即可处理所有币种级导出,它使用 cborg 并保留描述符标签:

examples/coin-level.mjs
import { URDecoder } from '@ngraveio/bc-ur';
import { decode, Tagged } from 'cborg';
import { HDKey } from '@scure/bip32';
import { base58 } from '@scure/base';

// Output-descriptor tags (BCR-2020-010) → script names.
const SCRIPT = { 400: 'sh', 401: 'wsh', 403: 'pkh', 404: 'wpkh', 409: 'tr' };
const options = { useMaps: true, strict: true, tags: Tagged.preserve(303, 304, 305, 400, 401, 403, 404, 409) };

const fingerprint = (n) => n.toString(16).padStart(8, '0');
const pathOf = (keypath) => { // Tagged(304, Map{1: [index, hardened, …]})
const c = keypath.value.get(1), parts = [];
for (let i = 0; i < c.length; i += 2) parts.push(`${c[i]}${c[i + 1] ? "'" : ''}`);
return `m/${parts.join('/')}`;
};
const accountOf = (hdkey) => { // crypto-hdkey map: 3 key, 4 chain code, 6 origin, 8 parent fp
const origin = hdkey.get(6).value.get(1);
return {
path: pathOf(hdkey.get(6)),
masterFingerprint: fingerprint(hdkey.get(6).value.get(2)),
xpub: new HDKey({ publicKey: hdkey.get(3), chainCode: hdkey.get(4), depth: origin.length / 2,
index: origin.at(-2) + (origin.at(-1) ? 0x80000000 : 0), parentFingerprint: hdkey.get(8) }).publicExtendedKey,
};
};

/** Reads any coin-level export from BitTool Signer. */
export function readCoinLevelQR(frames) {
if (frames.length === 1 && !/^ur:/i.test(frames[0])) return { kind: 'address', address: frames[0].trim() };
const decoder = new URDecoder();
for (const frame of frames) decoder.receivePart(frame);
if (!decoder.isSuccess()) throw new Error('Incomplete or invalid QR');
const ur = decoder.resultUR();
const cbor = decode(ur.cbor, options);

switch (ur.type) {
case 'crypto-account': { // Bitcoin: {1: master fp, 2: [output descriptor]}
let node = cbor.get(2)[0];
const script = [];
while (node.tag !== 303) { script.push(SCRIPT[node.tag]); node = node.value; } // e.g. sh → wpkh
return { kind: 'bitcoin', script: script.join('-'), ...accountOf(node.value) };
}
case 'crypto-hdkey': // EVM account node
return { kind: 'evm', ...accountOf(cbor) };
case 'crypto-multi-accounts': { // Solana: {1: master fp, 2: [crypto-hdkey with a 32-byte key]}
const key = cbor.get(2)[0].value;
return { kind: 'solana', masterFingerprint: fingerprint(cbor.get(1)), path: pathOf(key.get(6)), address: base58.encode(key.get(3)) };
}
default:
throw new Error(`Not a pairing QR: ${ur.type}`);
}
}