SOL Sign
This article explains how to complete Solana address confirmation and transaction signing on UKey Wallet hardware devices. The focus is on three things: using the device-supported derivation path, serializing the transaction into device-parsable rawTx, and completing signature backfilling and verification on the application side.
Table of contents
How It Works
The application sends the transaction to be signed to the device-side Solana App through the SDK entry. The device displays a summary of the account, transfer target, amount, fee, and related details based on the serialized rawTx, then returns the signature only after user confirmation. The application then writes the signature back to Transaction or VersionedTransaction and verifies the signer's public key with @solana/web3.js.
Please confirm the following boundaries before accessing:
- The derived path must be a 4-segment fully hardened structure:
m/44'/501'/account'/0', for instancem/44'/501'/3'/0'. rawTxJust pass in the hexadecimal string, and the0xprefix may be included or omitted.- If you have an
TransactionorVersionedTransactionobject, executeBuffer.from(tx.serialize()).toString('hex')first and then pass it to the SDK. - The application is responsible for constructing the transaction, setting up
recentBlockhash, and serializing the transaction; the signature is only generated after confirmation by the device. - Versioned Transaction requires the firmware version to meet UKey Lite 24 / UKey Lite 25 / UKey Core 26 ≥
1.1.0.
Setup
npm i @ukeyfe/hardware-core @ukeyfe/hardware-common-connect-sdk
Init
import ukeySdk from "@ukeyfe/hardware-common-connect-sdk";
await ukeySdk.init({ env: "webusb", fetchConfig: true, debug: false });
const [{ connectId }] = await ukeySdk.searchDevices();
const deviceId = (await ukeySdk.getFeatures(connectId)).payload?.device_id;
Best For
Scenario 1: Read and confirm address
const path = "m/44'/501'/3'/0'";
const opResult = await ukeySdk.solGetAddress(connectId, deviceId, {
path,
showOnUKey: true,
});
// Example data: opResult.payload.address, publicKey?, path
Input Fields
path: Fully hardened Solana account path in the44'/501'/account'/0'shape.showOnUKey?: Turn this on when the user should verify the address on the device, such as first-time binding or before sensitive actions.
return
Promise<{ success; payload: { address; path; publicKey? } }>;
Scenario 2: Send a prepared transaction to the device for signing
const opResult = await ukeySdk.solSignTransaction(connectId, deviceId, {
path: "m/44'/501'/3'/0'",
rawTx,
keepSession: true,
});
// Example data: opResult.payload.signature
Input Fields
path: Solana account path that matches the address used to build the transaction.rawTx: Serialized transaction as hex. The0xprefix is accepted but not required; convertTransaction.serialize()output to hex before calling.keepSession?: Keep the active session for smoother consecutive signatures.
return
Promise<{ success; payload: { signature } }>;
After getting the signature, attach it back to the transaction object and complete local verification before broadcasting.
Flow & State
- The SDK method returns the final result through Promise; intermediate steps such as unlocking the device, opening the Solana App, confirming the transaction, etc. will notify the application display prompt through the
UI_REQUESTevent. - Please call the interfaces in sequence on the same device and do not initiate multiple signature requests concurrently.
- When signing in batches or continuously, you can combine
keepSessionto make the interaction smoother.
Demo
Reference: transaction signature
import ukeySdk from "@ukeyfe/hardware-common-connect-sdk";
await ukeySdk.init({ env: "webusb", debug: false });
const [{ connectId }] = await ukeySdk.searchDevices();
const deviceId = (await ukeySdk.getFeatures(connectId)).payload?.device_id;
const path = "m/44'/501'/3'/0'";
await ukeySdk.solGetAddress(connectId, deviceId, {
path,
showOnUKey: true,
});
// If you start with a Transaction / VersionedTransaction, convert it to hex first:
const rawTxFromObject = Buffer.from(transaction.serialize()).toString('hex');
// The demoRawTx hex below is already serialized and can be sent to the device as-is.
const demoRawTx =
"5f3b1d7a9c2e4f6081a3b5d7f90c2e4f6081a3b5d7f90c2e4f6081a3b5d7f90c2e4f6081a3b5d7f90c2e4f60";
const { success, payload } = await ukeySdk.solSignTransaction(
connectId,
deviceId,
{
path,
rawTx: demoRawTx,
keepSession: true,
},
);
if (success) {
console.info("Returned signature payload:", payload.signature);
}
Final Checks
| Checkpoint | What to do |
|---|---|
| Path rules | Keep the path in fully hardened form as 44'/501'/account'/0'; other patterns may be rejected. |
| Transaction data | Prepare a valid recentBlockhash, a complete account list, and a stable instruction order first. |
| On-screen match | Make sure the account, destination, amount, and fee summary match the device display. |
| Signature recovery | After reapplying the signature with @solana/web3.js, confirm the signing public key matches the address from solGetAddress. |
| Payload size | Split or simplify the transaction if it is too large or includes instructions the device does not support yet. |
| User exit path | If the user rejects or times out, let them restart the flow instead of replaying the same transaction in the background. |
Continue with the API page: solSignTransaction.