Bitcoin
This section summarizes PSBT data handling for offline signing with animated UR codes.
Data types
Request/response UR type:
crypto-psbtSee also: Basic API (UR primitives and structures)
Flow
Build an unsigned PSBT with your wallet backend.
Encode PSBT → UR (
crypto-psbt) and display as animated QR.Device scans → returns signed PSBT as
crypto-psbt.Finalize and broadcast.
Tip: Keep PSBT size within practical QR limits; split across frames as needed.
Examples
Decode animated QR → signed PSBT
import { URDecoder } from '@ngraveio/bc-ur';
import { CryptoPSBT } from '@keystonehq/bc-ur-registry-btc';
const dec = new URDecoder();
// For each scanned frame string from camera:
// dec.receivePart(frameString)
if (dec.isComplete()) {
const ur = dec.resultUR(); // ur.type should be 'crypto-psbt'
const psbt = CryptoPSBT.fromCBOR(ur.cbor);
const signedPsbt = psbt.getPSBT(); // Buffer of PSBT (signed or partially signed)
// finalize & broadcast using your backend
}Encode unsigned PSBT → animated QR
import { CryptoPSBT } from '@keystonehq/bc-ur-registry-btc';
import { airGapUrUtils } from '@keystonehq/keystone-sdk';
const psbtBytes = Buffer.from(unsignedPsbtBase64, 'base64');
const req = CryptoPSBT.fromPSBT(psbtBytes); // or new CryptoPSBT(psbtBytes)
const ur = req.toUR();
const frames = airGapUrUtils.urToQrcode(ur); // render frames in your UILast updated
Was this helpful?