Bitcoin (BTC)
UTXO-based signing. Provide the previous transactions for the inputs you spend; the device signs each input by its keyPath.
| Field | Value |
|---|---|
| Family | Bitcoin (UTXO) |
| coinType | 0 |
| keyPath | m/44'/0'/<accountIdx>'/0/0 |
| addressFormat | legacysegwit-native |
| chainId | bip122:000000000019d6689c085ae165831e93/slip44:0 |
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 | 1.0.0 or higher | 1.0.0 or higher |
signTransaction | 1.0.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. SegWit (bech32) addresses require firmware 1.7.0 or higher.
addressFormat
| Format | Path | Status |
|---|---|---|
legacy (P2PKH) | m/44'/0'/0'/0/0 | Supported |
segwit-native (P2WPKH, bech32) | m/44'/0'/0'/0/0 | Supported |
segwit-wrapped (P2SH-P2WPKH, BIP-49) | — | Not supported |
taproot (P2TR, BIP-86) | m/86'/0'/0'/0/0 | Not supported (no registry entry — param_error) |
Receiving-side Taproot is a different question. This table is about whose key signs — DCENT has no Taproot account, so it cannot spend a bc1p… UTXO. Sending to a Taproot address works: put the bc1p… address in outputs[].addresses with txType: 'p2tr'. So legacy→Taproot and SegWit→Taproot transfers are fine; Taproot→anything is not.
DCENT derives every supported Bitcoin format from m/44'. legacy and segwit-native share the same m/44'/0'/0'/0/0 key — addressFormat only changes how that key is encoded (P2PKH Base58 vs. P2WPKH bech32), not the derivation path. Unlike BIP-84 wallets, a m/84' / m/49' keyPath is rejected.
getAddress — account address
Retrieves the Bitcoin address for a keyPath. The returned format follows addressFormat paired with the derivation purpose: legacy (P2PKH, m/44') returns a Base58Check address starting with 1; segwit-native (P2WPKH, m/44') returns a bech32 address starting with bc1. Both formats derive from the same m/44' key — only the encoding differs.
Parameters
| Field | Type | Required | Source | Notes |
|---|---|---|---|---|
chainId | string | Required | App | Full CAIP-19 — bip122:000000000019d6689c085ae165831e93/slip44:0 |
keyPath | string | Required | App | BIP-44 — always m/44' (both legacy and segwit-native); addressFormat selects the encoding |
addressFormat | enum | Optional | App | legacy / segwit-native only (both m/44', encoding differs).• segwit-wrapped — not supported• taproot — not supported (no registry entry; rejected with param_error) |
Request
await dcent.getAddress({
chainId: 'bip122:000000000019d6689c085ae165831e93/slip44:0',
keyPath: "m/44'/0'/0'/0/0",
addressFormat: 'segwit-native'
})
Response
| Field | Type | Notes |
|---|---|---|
body.parameter.address | string | The Bitcoin address — the encoding is chosen by addressFormat (keyPath is always m/44'). Bitcoin does not return a separate public key.• legacy → Base58Check P2PKH (1…)• segwit-native → bech32 P2WPKH (bc1…) |
{
"header": {
"version": "1.0",
"status": "success"
},
"body": {
"command": "getAddress",
"parameter": {
"address": "bc1q…"
}
}
}
signTransaction
Payload
| Field | Type | Required | Source | Notes |
|---|---|---|---|---|
keyPath | string | Required | App | BIP-44 — always m/44' |
addressFormat | enum | Optional | App | Same enum as getAddress. Selects which Bitcoin variant signs. Omit it and the variant is inferred from inputs[].txType (see below).• legacy / segwit-native — supported• segwit-wrapped / taproot — rejected with param_error (no registry entry yet) |
transaction | object | string | Required | App | The UTXO payload below, or a PSBT base64 string |
Pass the addressFormat you got from getAccountInfo. Bitcoin's legacy and SegWit accounts share one chainId and one m/44' keyPath, so neither of those two fields identifies which account is signing. Without addressFormat the signer falls back to reading inputs[].txType, and that signal is missing from PSBT payloads. getAccountInfo already returns meta.addressFormat for each account — send it straight back on signTransaction.
If you send both and they disagree (for example addressFormat: 'legacy' with txType: 'p2wpkh' inputs), the request is rejected with -32602 rather than one of them silently winning.
payload.transaction:
{
"inputs": [
{
"rawTransaction": "<prev tx hex>",
"index": 0,
"txType": "p2pkh",
"keyPath": "m/44'/0'/0'/0/0",
"sequence": 4294967295
}
],
"outputs": [
{
"txType": "p2pkh",
"amount": "990000",
"addresses": [
"<recipient>"
]
}
]
}
| Field | Type | Required | Source | Unit | Notes |
|---|---|---|---|---|---|
inputs[].rawTransaction | hex | Required-Real | Wallet | — | Full prev-tx hex of the UTXO; must be device-owned |
inputs[].index | number | Required | App | — | UTXO output index |
inputs[].txType | enum | Required | App | — | The script type of the UTXO being spent. When payload.addressFormat is omitted this doubles as the fallback signal for legacy vs SegWit (chainId and keyPath are identical across both variants). Prefer the explicit addressFormat — this fallback cannot express every case: p2sh is ambiguous (legacy multisig vs wrapped SegWit) and PSBT payloads carry no txType at all.• p2pkh = legacy scriptSig → legacy• p2wpkh / p2wsh = native SegWit witness → segwit-native• p2sh = accepted, but it maps to no format — the signer falls back to the legacy account, so send an explicit addressFormat• p2tr = Taproot — not supported: the currency registry has no BIP-86 variant, so this value is rejected with param_error (a firmware update does not change it)All inputs must share one txType; mixed script types are rejected with -32602. |
inputs[].keyPath | string | Required-Real | Wallet | — | Path that signs this input |
inputs[].sequence | number | Optional | App | — | Not defaulted to 0xffffffff — when omitted the bridge uses 0xfffffffd for RBF-enabled coins and leaves it unset otherwise. Also note the first input's value applies to the whole transaction. |
outputs[].txType | enum | Required | App | — | The output script type — a different set from inputs[].txType. p2pkh / p2sh / p2wpkh / p2wsh, plus p2tr for a Taproot recipient and change for the change output.• p2tr — supported: you can send to a bc1p… address. Use it whenever addresses[0] is Taproot; labelling a bc1p… address as anything else is rejected with param_error.• change — must not be a bc1p… address: DCENT cannot spend a Taproot UTXO, so that change would be stranded forever. |
outputs[].amount | string | Required | App | satoshi | Output value |
outputs[].addresses | string[] | Required-Real | App | — | Recipient. Only addresses[0] is used, and exactly one non-change output is allowed — more than one destination is rejected with param_error. Use a second output with txType: 'change' for change. |
option | object | hex | Required | App | — | Zcash only — every other Bitcoin-family currency omits this field. (Horizen also used it for the BIP-115 replay-protection tail, but the native ZEN chain was shut down by the 2025-07-23 migration to a Base ERC-20, and signTransaction for it is no longer supported.) Zcash: a 16-hex-char string (GroupId + consensus branch ID). The signer has no network access to derive either value itself; omitting it (or, for Zcash, supplying a branch ID that a network upgrade has made stale) is rejected with -32602. |
Response
{
"header": {
"status": "success"
},
"body": {
"command": "signTransaction",
"parameter": {
"signature": "<signed raw tx hex>"
}
}
}
| Field | Type | Notes |
|---|---|---|
header.status | string | "success" when the device completes signing |
body.command | string | "signTransaction" |
body.parameter.signature | hex | The broadcast-ready signed raw transaction hex for Bitcoin — not a bare signature. Submit it with sendrawtransaction. |
Variants — multi-step builder
As an alternative to the single-call form, a stateful builder is available:
const tx = dcent.getBitcoinTransactionObject()
dcent.addBitcoinTransactionInput(tx, prevTxHex, utxoIndex, 'p2pkh', "m/44'/0'/0'/0/0")
dcent.addBitcoinTransactionOutput(tx, 'p2pkh', 990000, recipient)
// then sign
Common Mistakes
Assuming BIP-84 m/84' for native segwit — on DCENT both legacy and native segwit derive from m/44'; addressFormat alone selects the encoding. A m/84' keyPath is rejected. Dropping addressFormat on signTransaction — chainId and keyPath are the same for the legacy and SegWit accounts, so a signing request without addressFormat falls back to inputs[].txType; send back the meta.addressFormat from getAccountInfo instead. Missing change output — unspent input value becomes miner fee. Amounts not in satoshi. Missing option for Zcash — the signer cannot fetch the consensus branch ID itself; the app must supply it (a 16-hex-char string). Horizen is discontinued and signTransaction is rejected for it before any of this applies. Omitting it is rejected with -32602.