Solana (SOL)
Sign Solana transactions and messages. The most reliable input is a base58-serialized transaction; plain-JSON and several data encodings are also accepted.
| Field | Value |
|---|---|
| Family | Solana |
| coinType | 501 |
| keyPath | m/44'/501'/<accountIdx>' |
| chainId | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501 |
Supported Methods
| Method | Supported |
|---|---|
| getAddress | ✓ |
| signTransaction | ✓ |
| signMessage | ✓ |
| signTypedData | ✗ |
Requirements
Minimum firmware for each method. The two models are on different version lines, so DCENT Biometric Wallet and DCENT X are listed separately (the numbers are not comparable across models). Below this the request returns the firmware-update error (5005) and the bridge prompts to update.
| Method | DCENT Biometric Wallet | DCENT X |
|---|---|---|
getAddress | 2.16.5 or higher | 1.0.0 or higher |
signTransaction | 2.16.5 or higher | 1.0.0 or higher |
signMessage | 2.31.0 or higher | 1.0.0 or higher |
Firmware versions below are for the DCENT Biometric Wallet line — DCENT X supports them from 1.0.0. SPL token support was added in firmware 2.17.1.
getAddress — account address
Retrieves the Solana account address for a chainId + keyPath. A Solana address is the base58-encoded ed25519 public key (32 bytes); there are no address variants, so addressFormat does not apply.
Parameters
| Field | Type | Required | Source | Notes |
|---|---|---|---|---|
chainId | string | Required | App | Full CAIP-19 — solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501 |
keyPath | string | Required | App | BIP-44, default m/44'/501'/0' |
Request
await dcent.getAddress({
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501',
keyPath: "m/44'/501'/0'"
})
Response
| Field | Type | Notes |
|---|---|---|
parameter.address | string | Base58 ed25519 account address — this is the Solana address |
parameter.pubkey | string (hex, optional) | Raw 32-byte ed25519 public key; same key material as the address |
header.status | string | "success" on success |
{
"header": {
"version": "1.0",
"status": "success"
},
"body": {
"command": "getAddress",
"parameter": {
"address": "7Eq…",
"pubkey": "ab12…"
}
}
}
signTransaction
Variant — base58 serialized (recommended)
payload.transaction:
"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6u…" // bs58.encode(tx.serialize({ requireAllSignatures:false }))
Variant — plain JSON
payload.transaction:
{
"version": 0,
"feePayer": "<pubkey>",
"instructions": [
{
"programId": "11111111111111111111111111111111",
"keys": [
{
"pubkey": "<from>",
"isSigner": true,
"isWritable": true
},
{
"pubkey": "<to>",
"isSigner": false,
"isWritable": true
}
],
"data": {
"instruction": 2,
"lamports": 1000000
}
}
],
"recentBlockhash": "<blockhash>"
}
| Field | Type | Required | Source | Notes |
|---|---|---|---|---|
feePayer | pubkey | Required-Real | Wallet | Usually the signer |
instructions[].keys | meta[] | Required-Real | App | Real account metas / ATAs |
recentBlockhash | string | Required-Real | App | Real, current blockhash — the app must supply it (form-E); not auto-filled |
form-E — completed serialized message. The device can only show a friendly label if it recognizes the token locally — use the form-D descriptor below to guarantee a labeled display for any token. SPL token transfer: programId = Tokenkeg…, data = 0x0c + amount (u64 LE) + decimals (u8), keys = [source ATA, mint, dest ATA, owner] — all four are Required-Real, plus a real feePayer + recentBlockhash (form-E).
form-D — structured SPL token transfer. Instead of instructions, send transaction.token = { contract (mint), to (the recipient's owner wallet, not an ATA), amount (base units), decimals, symbol }. This is the only shape in which the device can show an SPL token's symbol and amount — a form-E serialized message is signed as opaque bytes. Because the bridge signs with no network access, a Solana form-D transfer must also carry a sender (the fee-paying wallet — without it the call fails with param_error) and these completed fields: recentBlockhash (base58 32-byte, no surrounding whitespace), preparedFee.fee (positive integer lamports — this is what the device displays as the fee), and extra.isAssociated (boolean — whether the recipient's associated token account already exists; false makes the bridge add an ATA-create instruction). Missing any of the three gives -32602. Do not send the recipient ATA — the bridge derives it offline from owner + mint, so what the device shows cannot be redirected by the app.
Response
| Field | Type | Notes |
|---|---|---|
parameter.signature | string (base58) | The complete, fully-signed Solana transaction, serialized and base58-encoded — broadcast-ready via sendRawTransaction / sendTransaction. This is the whole signed transaction, not a bare 64-byte signature. |
header.status | string | "success" on success |
{
"header": {
"version": "1.0",
"status": "success"
},
"body": {
"command": "signTransaction",
"parameter": {
"signature": "4uQe…"
}
}
} // base58 fully-signed serialized tx
signMessage
await dcent.sign({
method: 'signMessage',
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501',
payload: {
keyPath: "m/44'/501'/0'",
message: '<bytes>',
meta: {
kind: 'raw'
}
}
})
Response
| Field | Type | Notes |
|---|---|---|
parameter.signature | string | ed25519 signature (64 bytes) over the message bytes |
header.status | string | "success" on success |
{
"header": {
"version": "1.0",
"status": "success"
},
"body": {
"command": "signMessage",
"parameter": {
"signature": "…"
}
}
} // ed25519 signature over the message
Common Mistakes
Expired blockhash — fetch recentBlockhash immediately before signing. Unsigned VersionedTransaction — serialize with requireAllSignatures:false. Placeholder ATAs — source/dest ATAs must be the real on-chain accounts.