Códigos QR a nivel de moneda
Los códigos QR a nivel de moneda usan los mismos tipos UR que otros firmadores QR (siguen los registros de Blockchain Commons y Keystone), así que una billetera que ya importa cuentas estilo Keystone normalmente puede leerlos sin cambios. En la app: Connect › Coin Level y luego elige una cadena.
| Cadena | Exportación predeterminada | Alternativa |
|---|---|---|
| Bitcoin | ur:crypto-account con un xpub de cuenta (elige Native SegWit, Taproot, Nested SegWit o Legacy) | Dirección simple; xpub de cofirmante multisig |
| EVM | ur:crypto-hdkey, el nodo de cuenta m/44'/60'/0' | Dirección simple (índice 0) |
| Solana | ur:crypto-multi-accounts con una clave preset | Dirección simple |
| TRON | Dirección simple de cualquier índice m/44'/195'/0'/0/i | — |
En la app, cada código QR tiene una sección Payload (carga útil) que muestra el contenido decodificado, exactamente como en las vistas JSON de esta página. Las claves CBOR enteras se muestran con los nombres de campo del registro; las huellas digitales son enteros de 32 bits (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)
| Tipo de dirección | Ruta de la cuenta | Tags del descriptor | Script |
|---|---|---|---|
| Native SegWit | m/84'/0'/0' | 404(303(key)) | wpkh |
| Taproot | m/86'/0'/0' | 409(303(key)) | tr (BIP86) |
| Nested SegWit | m/49'/0'/0' | 400(404(303(key))) | sh(wpkh) |
| Legacy | m/44'/0'/0' | 403(303(key)) | pkh |
El crypto-hdkey interno lleva la clave pública de la cuenta (33 bytes), el chain code, la ruta de origen con la huella digital de origen y la profundidad, la huella digital del padre y el nombre BitTool Signer. Las direcciones de recepción son …/0/i y las de cambio, …/1/i.
Dos detalles si lo analizas por tu cuenta:
- El descriptor de salida se escribe como lo escriben Keystone y Sparrow, sin el envoltorio
308(crypto-output)de BCR-2020-015. - Taproot usa el tag 409 (
tr) con la clave interna sin tweak (untweaked); aplica el tweak BIP86 al derivar direcciones. Las bibliotecas de registro más antiguas no conocen el tag 409 y lo descartan en silencio (por eso el decodificador de ejemplo de abajo lee los tags directamente).
- Native SegWit
- Taproot
- Nested SegWit
- Legacy
{
"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
{
"master-fingerprint": 1942346250,
"output-descriptors": [
{
"taproot": {
"crypto-hdkey": {
"is-private": false,
"key-data": "03418278a2885c8bb98148158d1474634097a179c642f23cf1cc04da629ac6f0fb",
"use-info": {
"crypto-coin-info": {}
},
"origin": {
"crypto-keypath": {
"components": [
86,
true,
0,
true,
0,
true
],
"source-fingerprint": 1942346250,
"depth": 3
}
},
"chain-code": "c61a8f27e98182314d2444da3e600eb5836ec8ad183c86c311f95df8082b18aa",
"parent-fingerprint": 55734490,
"name": "BitTool Signer",
"note": "BitTool Signer public account"
}
}
}
]
}
UR:CRYPTO-ACCOUNT/OEADCYJKSKTNBKAOLYTAADNLTAADDLPDAOWKAXHDCLAXFPLFKSOELOHHLURHLYFDBZLGBBJYIAFZMSOYKKSWFWWZFNWNSFAATNIDNYSWWTZOAHTAADEHNBAMTAADDYOTADLNCSHFYKAEYKAEYKAOCYJKSKTNBKAXAXAAHDCXSWCYMYDIWLLYLFEHGTDKFYTNFMHNBARELSJTSPPMCSFNLNSRBYYTHLYAAYDNCSPKAYCYAXGMJOTNASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYKSBBMKFG
{
"master-fingerprint": 1942346250,
"output-descriptors": [
{
"script-hash": {
"witness-public-key-hash": {
"crypto-hdkey": {
"is-private": false,
"key-data": "02f1f347891b20f7568eae3ec9869fbfb67bcab6f358326f10ecc42356bd55939d",
"use-info": {
"crypto-coin-info": {}
},
"origin": {
"crypto-keypath": {
"components": [
49,
true,
0,
true,
0,
true
],
"source-fingerprint": 1942346250,
"depth": 3
}
},
"chain-code": "6eaae365ae0e0a0aab84325cfe7cd76c3b909035f889e7d3f1b847a9a0797ecb",
"parent-fingerprint": 1023803253,
"name": "BitTool Signer",
"note": "BitTool Signer public account"
}
}
}
}
]
}
UR:CRYPTO-ACCOUNT/OEADCYJKSKTNBKAOLYTAADMHTAADMWTAADDLPDAOWKAXHDCLAOWNWFFLLDCWCXYLHFMNPLFMSOLNNERSRPKGSGRPWFHDEYJLBEWPSSCNHFRYGOMUNTAHTAADEHNBAMTAADDYOTADLNCSEHYKAEYKAEYKAOCYJKSKTNBKAXAXAAHDCXJTPKVLIHPLBABKBKPYLREYHHZEKETSJZFRMHMHECYALDVDTEWNROFLPTNBKKKBSBAYCYFSAHZMKPASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYZCOYAYKE
{
"master-fingerprint": 1942346250,
"output-descriptors": [
{
"public-key-hash": {
"crypto-hdkey": {
"is-private": false,
"key-data": "03774c910fcf07fa96886ea794f0d5caed9afe30b44b83f7e213bb92930e7df4bd",
"use-info": {
"crypto-coin-info": {}
},
"origin": {
"crypto-keypath": {
"components": [
44,
true,
0,
true,
0,
true
],
"source-fingerprint": 1942346250,
"depth": 3
}
},
"chain-code": "3da4bc190a2680111d31fadfdc905f2a7f6ce77c6f109919116f253d43445219",
"parent-fingerprint": 358337113,
"name": "BitTool Signer",
"note": "BitTool Signer public account"
}
}
}
]
}
UR:CRYPTO-ACCOUNT/OEADCYJKSKTNBKAOLYTAADMUTAADDLPDAOWKAXHDCLAXKTGSMEBSTKATZSMTLOJTOSMWWTTLSGWENYZEDYQZGRLSYLVOBWRKMOMUBAKIWKRYAHTAADEHNBAMTAADDYOTADLNCSDWYKAEYKAEYKAOCYJKSKTNBKAXAXAAHDCXFSOXRFCFBKDSLABYCAEHZSURUOMHHEDRLBJZVDKEJLBENLCFBYJLDAFSFXFYGMCFAYCYBZHPSGHKASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYGUTNWNDL
Cofirmante multisig de Bitcoin
Para billeteras multisig, BitTool Signer exporta una clave de cofirmante en crypto-account, en las rutas BIP48:
| Tipo | Ruta de la cuenta | Tags del descriptor |
|---|---|---|
| P2WSH | m/48'/0'/0'/2' | 401(303(key)) (wsh) |
| P2SH-P2WSH | m/48'/0'/0'/1' | 400(401(303(key))) (sh(wsh)) |
Esto corresponde a un solo participante. No incluye umbral, ni otros cofirmantes, ni dirección final. Tu billetera construye la política completa (por ejemplo, un wsh(sortedmulti(…)) 2 de 3 ordenado) a partir de las exportaciones de todos los cofirmantes.
Vector de prueba de cofirmante 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
La exportación EVM es el nodo de cuenta m/44'/60'/0' como un crypto-hdkey de nivel superior (EIP-4527), el formato que importan MetaMask, Rabby, imToken y otras:
| Clave | Campo | Valor |
|---|---|---|
| 2 | is-private | false |
| 3 | key-data | Clave pública comprimida de 33 bytes |
| 4 | chain-code | 32 bytes |
| 5 | use-info | 305({1: 60}) |
| 6 | origin | 304({1: [44, true, 60, true, 0, true], 2: fingerprint, 3: 3}) |
| 7 | children | 304({1: [0, false, [], false]}), es decir, 0/* |
| 8 | parent-fingerprint | uint32 |
| 9, 10 | name, note | "BitTool Signer", "BitTool Signer public account" |
Deriva …/0/i para obtener las direcciones. La misma clave sirve para todas las redes EVM compatibles.
{
"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
Las claves de Solana son totalmente endurecidas (hardened), así que no hay nada a partir de lo cual derivar. La exportación contiene una clave preset (la seleccionada en la app) como un crypto-hdkey con una clave Ed25519 de 32 bytes:
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
}
| Preset | Ruta |
|---|---|
| Standard | m/44'/501'/0'/0' |
| Trust | m/44'/501'/0' |
| Legacy | m/44'/501' |
La dirección es la codificación Base58 de la clave de 32 bytes.
{
"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
Direcciones simples
La opción Address (Dirección) exporta solo la dirección como texto del QR: sin esquema URI, sin monto y sin envoltorio UR. Es para billeteras de solo lectura que no pueden importar claves. TRON siempre se exporta de esta forma, en cualquier índice que el usuario elija (m/44'/195'/0'/0/i, 0 ≤ i < 231).
| Exportación | Texto del QR |
|---|---|
| Bitcoin (Native SegWit, índice 0) | bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu |
| EVM (índice 0) | 0x9858EfFD232B4033E47d90003D41EC34EcaEda94 |
| Solana (standard) | HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk |
| TRON índice 0 | TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH |
| TRON índice 5 | TBdYXtwq18cAhi1BA574TrP6tw2G86anu1 |
Una billetera que se empareja solo con la dirección no puede agregar datos de derivación a sus solicitudes de firma. BitTool Signer aún puede hacer coincidir solicitudes que solo traen dirección para el índice 0 de EVM y los índices 0–99 de TRON, pero es preferible usar las exportaciones de claves si piensas firmar.
Cómo decodificarlos todos
Una sola función para todas las exportaciones a nivel de moneda, usando cborg con los tags del descriptor preservados:
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}`);
}
}