On this page
Solana
UR registry types for Solana offline signing.
Data Types
- Request UR:
sol-sign-request(transaction/message bytes + BIP-44 path) - Response UR:
sol-signature
Flow
- Prepare Solana transaction or message bytes and derive path
- Build
sol-sign-requestand show as animated QR - Scan device response → decode
sol-signature→ attach to transaction - Submit to the Solana network
Examples
Install @keystonehq/bc-ur-registry-sol and @ngraveio/bc-ur. The examples require a Buffer implementation. Registry encoding alone does not establish support on a device; verify the UR types accepted by the target firmware.
The wallet supplies the unsigned transaction data and the actual account path and master fingerprint exported by the device. The QR renderer should call nextFrame() on a timer and stop when the signing dialog closes. These snippets encode and decode the transport; they do not submit a transaction.
Build Solana sign request (UR)
import { SolSignRequest, SignType, CryptoKeypath, PathComponent } from '@keystonehq/bc-ur-registry-sol';
// BIP44: m/44'/501'/0'
const keypath = new CryptoKeypath([
new PathComponent({ index: 44, hardened: true }),
new PathComponent({ index: 501, hardened: true }),
new PathComponent({ index: 0, hardened: true }),
], Buffer.from(masterFingerprintHex, 'hex'));
const requestId = globalThis.crypto.randomUUID();
const requestIdBytes = Buffer.from(requestId.replace(/-/g, ''), 'hex');
const req = new SolSignRequest({
requestId: requestIdBytes,
signData: Buffer.from(rawTxBytes), // transaction or message bytes
derivationPath: keypath,
signType: SignType.Transaction, // or SignType.Message
});
const encoder = req.toUREncoder(200);
const nextFrame = () => encoder.nextPart(); // Call from your QR animation timer.Decode Solana signature (UR)
import { URDecoder } from '@ngraveio/bc-ur';
import { SolSignature } from '@keystonehq/bc-ur-registry-sol';
const dec = new URDecoder();
// dec.receivePart(frameString) for each scanned frame
if (dec.isError()) throw new Error('QR decoding failed');
if (dec.isSuccess()) {
const ur = dec.resultUR();
if (ur.type !== 'sol-signature') throw new Error('Expected sol-signature');
const sig = SolSignature.fromCBOR(ur.cbor);
const returnedId = sig.getRequestId();
if (!returnedId || !returnedId.equals(requestIdBytes)) {
throw new Error('Signature request ID does not match');
}
const signature = sig.getSignature();
// Verify the signer and signature against the original payload before use.
// Attach transaction signatures to the original transaction; return message signatures to the caller.
}Last updated on