Saltar al contenido principal

Códigos QR a nivel de moneda

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

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.

CadenaExportación predeterminadaAlternativa
Bitcoinur:crypto-account con un xpub de cuenta (elige Native SegWit, Taproot, Nested SegWit o Legacy)Dirección simple; xpub de cofirmante multisig
EVMur:crypto-hdkey, el nodo de cuenta m/44'/60'/0'Dirección simple (índice 0)
Solanaur:crypto-multi-accounts con una clave presetDirección simple
TRONDirecció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ónRuta de la cuentaTags del descriptorScript
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

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).
m/84'/0'/0' · crypto-accountPrimera dirección de recepción: bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu
UR:CRYPTO-ACCOUNT/OEADCYJKSKTNBKAOLYTAADMWTAADDLPDAOWKAXHDCLAOJOKNIDZCPSSAJTPTRPFRCECFKKAMYKJTVTCSBTBDTKCFIYVYOETNEEYKWFNBNYNDAHTAADEHNBAMTAADDYOTADLNCSGHYKAEYKAEYKAOCYJKSKTNBKAXAXAAHDCXGEGUNBPYCLRHUOMDLNNSGLMOOYHSCFGLAXRTWSFHYKADGESWMOWKEOSSKOGHMHZTAYCYKBWFDNUYASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYDPCXPECF
Carga útil decodificada
{
"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"
}
}
}
]
}
Texto del QR
UR:CRYPTO-ACCOUNT/OEADCYJKSKTNBKAOLYTAADMWTAADDLPDAOWKAXHDCLAOJOKNIDZCPSSAJTPTRPFRCECFKKAMYKJTVTCSBTBDTKCFIYVYOETNEEYKWFNBNYNDAHTAADEHNBAMTAADDYOTADLNCSGHYKAEYKAEYKAOCYJKSKTNBKAXAXAAHDCXGEGUNBPYCLRHUOMDLNNSGLMOOYHSCFGLAXRTWSFHYKADGESWMOWKEOSSKOGHMHZTAYCYKBWFDNUYASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYDPCXPECF

Cofirmante multisig de Bitcoin​

Para billeteras multisig, BitTool Signer exporta una clave de cofirmante en crypto-account, en las rutas BIP48:

TipoRuta de la cuentaTags del descriptor
P2WSHm/48'/0'/0'/2'401(303(key)) (wsh)
P2SH-P2WSHm/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
Carga útil decodificada
{
"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"
}
}
}
]
}
Texto del QR
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:

ClaveCampoValor
2is-privatefalse
3key-dataClave pública comprimida de 33 bytes
4chain-code32 bytes
5use-info305({1: 60})
6origin304({1: [44, true, 60, true, 0, true], 2: fingerprint, 3: 3})
7children304({1: [0, false, [], false]}), es decir, 0/*
8parent-fingerprintuint32
9, 10name, note"BitTool Signer", "BitTool Signer public account"

Deriva …/0/i para obtener las direcciones. La misma clave sirve para todas las redes EVM compatibles.

m/44'/60'/0' · crypto-hdkeyPrimera dirección: 0x9858EfFD232B4033E47d90003D41EC34EcaEda94
UR:CRYPTO-HDKEY/PTAOWKAXHDCLAOWDVEROKOPDINHSEEROISYALKSAYKCTJSHEDPRNUYJYFGROVAWEWFTYGHCEGLRPKGAHTAADEHOYADCSFNAMTAADDYOTADLNCSDWYKCSFNYKAEYKAOCYJKSKTNBKAXAXAAHDCXTPLFJSLUKNFWLAISAXWYPALBJYLSWZAMCXHSCYUYLOZTMWFNLDLGSKPYPTGSDECFAYCYTEDMFEAYATTAADDYOYADLRAEWKLAWKASJTFWINJYGHJLJLJZCXGUINIOJTIHJPBKKSCAFWINJYGHJLJLJZCXGUINIOJTIHJPCXJOKPIDJZINIACXHSIAIAJLKPJTJYMUFLSAVD
Carga útil decodificada
{
"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"
}
Texto del QR
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
}
PresetRuta
Standardm/44'/501'/0'/0'
Trustm/44'/501'/0'
Legacym/44'/501'

La dirección es la codificación Base58 de la clave de 32 bytes.

Carga útil decodificada (preset 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"
}
Texto del QR
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ónTexto del QR
Bitcoin (Native SegWit, índice 0)bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu
EVM (índice 0)0x9858EfFD232B4033E47d90003D41EC34EcaEda94
Solana (standard)HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk
TRON índice 0TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH
TRON índice 5TBdYXtwq18cAhi1BA574TrP6tw2G86anu1

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:

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}`);
}
}