Coin-level QR codes
The coin-level QR codes use the same UR types as other QR signers (they follow the Blockchain Commons and Keystone registries), so a wallet that already imports Keystone-style accounts can usually read them unchanged. In the app: Connect › Coin Level, then pick a chain.
| Chain | Default export | Alternative |
|---|---|---|
| Bitcoin | ur:crypto-account with one account xpub (choose Native SegWit, Taproot, Nested SegWit or Legacy) | Plain address; multisig cosigner xpub |
| EVM | ur:crypto-hdkey, the m/44'/60'/0' account node | Plain address (index 0) |
| Solana | ur:crypto-multi-accounts with one preset key | Plain address |
| TRON | Plain address of any index m/44'/195'/0'/0/i | — |
Each QR code has a Payload section in the app showing the decoded content, exactly as in the JSON views on this page. The integer CBOR keys are shown with their registry field names; fingerprints are 32-bit integers (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)
| Address type | Account path | Descriptor tags | 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 |
The crypto-hdkey inside carries the account public key (33 bytes), chain code, origin path with source fingerprint and depth, parent fingerprint, and the name BitTool Signer. Receive addresses are …/0/i, change addresses …/1/i.
Two details if you parse it yourself:
- The output descriptor is written the way Keystone and Sparrow write it, without the
308(crypto-output)wrapper from BCR-2020-015. - Taproot uses tag 409 (
tr) with the untweaked internal key; apply the BIP86 tweak when deriving addresses. Older registry libraries do not know tag 409 and silently drop it (the sample decoder below reads the tags directly for this reason).
- 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
Bitcoin multisig cosigner
For multisig wallets BitTool Signer exports one cosigner key in crypto-account, at the BIP48 paths:
| Type | Account path | Descriptor tags |
|---|---|---|
| P2WSH | m/48'/0'/0'/2' | 401(303(key)) (wsh) |
| P2SH-P2WSH | m/48'/0'/0'/1' | 400(401(303(key))) (sh(wsh)) |
This is one participant only. It carries no threshold, no other cosigners and no final address. Your wallet builds the full policy (for example a sorted 2-of-3 wsh(sortedmulti(…))) from all cosigners' exports.
P2WSH cosigner test vector
{
"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
The EVM export is the account node m/44'/60'/0' as a top-level crypto-hdkey (EIP-4527), the format MetaMask, Rabby, imToken and others import:
| Key | Field | Value |
|---|---|---|
| 2 | is-private | false |
| 3 | key-data | 33-byte compressed public key |
| 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]}), i.e. 0/* |
| 8 | parent-fingerprint | uint32 |
| 9, 10 | name, note | "BitTool Signer", "BitTool Signer public account" |
Derive …/0/i for addresses. The same key serves every supported EVM network.
{
"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 keys are fully hardened, so there is nothing to derive from. The export holds one preset key (the one selected in the app) as a crypto-hdkey with a 32-byte Ed25519 key:
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 | Path |
|---|---|
| Standard | m/44'/501'/0'/0' |
| Trust | m/44'/501'/0' |
| Legacy | m/44'/501' |
The address is the Base58 encoding of the 32-byte key.
{
"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
Plain addresses
The Address option exports just the address as QR text: no URI scheme, no amount, no UR wrapper. It is for watch-only wallets that cannot import keys. TRON is always exported this way, at any index the user picks (m/44'/195'/0'/0/i, 0 ≤ i < 231).
| Export | QR text |
|---|---|
| Bitcoin (Native SegWit, index 0) | bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu |
| EVM (index 0) | 0x9858EfFD232B4033E47d90003D41EC34EcaEda94 |
| Solana (standard) | HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk |
| TRON index 0 | TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH |
| TRON index 5 | TBdYXtwq18cAhi1BA574TrP6tw2G86anu1 |
A wallet that pairs by address alone cannot add derivation data to its signing requests. BitTool Signer can still match address-only requests for EVM index 0 and TRON indexes 0–99, but prefer the key exports when you plan to sign.
Decoding all of them
One function for every coin-level export, using cborg with the descriptor tags preserved:
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}`);
}
}