Skip to main content

Cardano (ADA)

Sign Cardano transactions (CIP-30 CBOR or structured inputs/outputs) and CIP-8/CIP-30/CIP-95 data.

FieldValue
FamilyCardano
coinType1815
chainIdcip34:1-764824073
chainId · Testnetcip34:0-2 (Preview)
keyPathm/1852'/1815'/<accountIdx>'/0/0
stakem/1852'/1815'/<accountIdx>'/2/0
DRepm/1852'/1815'/<accountIdx>'/3/0

Derivation purpose — what 1852' and 44' each support​

DCENT resolves two Cardano key trees from the same chainId. The purpose segment of the keyPath you send selects which one you get.

keyPathPurposeWhat it supports
m/1852'/1815'/…CIP-1852Everything — address, plain transfer, reward (stake) address, CIP-30 App connection, staking / withdrawal certificates, CIP-95 governance, signData
m/44'/1815'/…BIP-44 (legacy)Plain transfer only — getAddress (address, without rewardAddress), getPublicKey on m/44'/1815'/<account>'/0/<index> or …/1/<index> (single-key shape), and ordinary signTransaction

Send m/1852' unless you are working with existing legacy accounts. Everything on this page uses 1852'.

Why legacy is transfer-only​

On purpose 44' DCENT X builds every address with a fixed stake credential derived at m/44'/1815'/0'/2/0 — regardless of the account index you asked for. So for account index ≥ 1, a stake key derived at m/44'/1815'/<accountIdx>'/2/0 is not the one embedded in that account's address. Signing a certificate with it would register or delegate a credential that holds none of that account's funds, and the result would look successful.

Rather than hand back a key that does not match the address, these surfaces are refused on 44'.

What legacy returns instead​

CallOn m/1852'On m/44'
getAddressaddress + rewardAddressaddress only — no rewardAddress field
getPublicKey{ payment, stake, drep } bundlerole segment 2 or 3, in any notation (2, 2', or the hardened integer) → method_not_found (-32601) — this check comes first, whatever the path length; m/44'/1815'/<account>'/0/<index> or …/1/<index> (account hardened, role and index unhardened) → single { keyPath, publicKey }; any other form → param_error (-32602, Invalid keyPath)
signData (CIP-8 / 30 / 95)✓method_not_found (-32601)
signTransaction — ordinary transfer✓✓
signTransaction — CIP-30 CBOR✓param_error (-32602)
signTransaction — certificates / withdrawal / governance / mint / Plutus, auxiliaryData, requiredSigners, includeNetworkId: true✓param_error (-32602)
info

getPublicKey on 44' returns the single-key shape, not a partial bundle. Branch with 'payment' in response.body.parameter rather than assuming a bundle whenever the chain is Cardano.

Codes above are shown as body.error.code (the v1 string) with the numeric JSON-RPC semantic in parentheses. Branch on the string — the numeric value never appears in body.error.code. See Error Codes.

Supported Methods​

Support below is for m/1852'. See Derivation purpose for what m/44' refuses.

MethodSupported
getAddress✓
signTransaction✓
signMessage✗
signTypedData✗
signData CIP-8 / 30 / 95✓
getPublicKey✓

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 device_fw_incompatible (5005) and the bridge prompts to update.

warning

The numbers below are for m/1852', which is what this page documents.

The device exposes the CIP-1852 tree as a separate coin entry, added in DCENT Biometric Wallet 2.36.2 / DCENT X 1.0.1. Below that version the device does not report it at all, so every call on m/1852' fails with device_fw_incompatible (5005) — the method does not matter. Check the device firmware before you branch on the method table.

MethodDCENT Biometric WalletDCENT X
getAddress2.36.2 or higher1.0.1 or higher
signTransaction2.36.2 or higher1.0.1 or higher
signData2.36.2 or higher1.0.1 or higher
getPublicKey2.36.2 or higher1.0.1 or higher

Legacy m/44' predates the CIP-1852 coin entry, so the floors above apply to m/1852' and not to it — its own floor is older and lower. getAddress and ordinary signTransaction work on firmware below the CIP-1852 floor on both models, as far back as the device reports the CARDANO coin entry. getPublicKey and signData sit behind a different gate, the WalletConnect capability floor (DCENT Biometric Wallet 2.35.0; every DCENT X firmware meets its 1.0.0 floor), and below it they return device_fw_incompatible (5005).

The surfaces that 44' refuses are listed under Derivation purpose, and no upgrade opens them. The param_error (-32602) refusals — CIP-30 CBOR and the extended structured shapes — are decided host-side, without a signing request ever reaching the device. signData and stake / DRep getPublicKey return method_not_found (-32601) once the request clears the capability floor above; on a Biometric wallet below 2.35.0 you see 5005 instead, and on DCENT X you always see -32601. Use m/1852' instead.

getAddress — account address​

Retrieves the Cardano account address for a chainId + keyPath. Returns a Shelley-era bech32 address (addr1… base address; addr_test1… on testnet) plus a rewardAddress (stake address) when the keyPath uses purpose 1852' — on purpose 44' the rewardAddress field is absent. getAddress does not return a public key — call getPublicKey for the payment / stake / DRep key bundle. The keyPath purpose, not addressFormat, picks the account — omit addressFormat.

Parameters​

FieldTypeRequiredSourceNotes
chainIdstringRequiredAppFull CAIP value cip34:1-764824073
keyPathstringRequiredAppCIP-1852, purpose 1852'. purpose 44' also returns an address but without rewardAddress — see Derivation purpose

Request

await dcent.getAddress({
chainId: 'cip34:1-764824073',
keyPath: "m/1852'/1815'/0'/0/0"
})

Response

FieldTypeNotes
body.parameter.addressstringShelley bech32 base address (addr1…, or addr_test1… on testnet) combining payment + stake credentials
body.parameter.rewardAddressstringStake (reward) address (stake1…, or stake_test1… on testnet). Present only on purpose 1852' — absent on purpose 44'. For public keys call getPublicKey.
header.statusstring"success" on completion; "failure" otherwise
{
"header": {
"version": "1.0",
"status": "success",
"response_from": "getAddress"
},
"body": {
"command": "getAddress",
"parameter": {
"address": "addr1…",
"rewardAddress": "stake1…"
}
}
}
info

Cardano uses the Shelley bech32 address format with the addr1 prefix (addr_test1 on testnet). The keyPath purpose (1852' or 44') picks the account, not addressFormat — omit it. On purpose 1852' the same call also returns a rewardAddress (stake address). For public keys — the payment / stake / DRep bundle — call getPublicKey.

getPublicKey — payment / stake / DRep keys​

Returns the Cardano account public keys, each with its derivation path. Split out of getAddress — the pubkey bundle is no longer returned by getAddress.

Parameters​

FieldTypeRequiredSourceNotes
chainIdstringRequiredAppFull CAIP value cip34:1-764824073
keyPathstringRequiredAppAccount path, purpose 1852' (e.g. m/1852'/1815'/0'/0/0). Only the account index is read — the bundle is always derived at …/0/0, …/2/0 and …/3/0, so payment.keyPath may differ from the keyPath you sent. On purpose 44' this verb returns the single-key shape, not the role bundle
addressFormat?enumOptionalAppOmit it — the keyPath purpose picks the account. The Bitcoin encoding values are ignored. ledger is accepted only with m/1852', where it names the same account; standard, or ledger with m/44', → -32602

Request

await dcent.getPublicKey({
chainId: 'cip34:1-764824073',
keyPath: "m/1852'/1815'/0'/0/0"
})

Response

FieldTypeNotes
header.statusstring"success" on completion; "failure" otherwise
body.commandstringgetPublicKey
body.parameter.payment{ keyPath, publicKey }Payment (spending) key — role 0 (…/0/0)
body.parameter.stake{ keyPath, publicKey }Stake (rewards / delegation) key — role 2 (…/2/0)
body.parameter.drep{ keyPath, publicKey }DRep governance key — role 3 (…/3/0)
{
"header": {
"version": "1.0",
"status": "success",
"response_from": "getPublicKey"
},
"body": {
"command": "getPublicKey",
"parameter": {
"payment": {
"keyPath": "m/1852'/1815'/0'/0/0",
"publicKey": "…"
},
"stake": {
"keyPath": "m/1852'/1815'/0'/2/0",
"publicKey": "…"
},
"drep": {
"keyPath": "m/1852'/1815'/0'/3/0",
"publicKey": "…"
}
}
}
}
info

The response shape is chain-specific: this { payment, stake, drep } bundle is Cardano's on purpose 1852'. Every other family — and Cardano on purpose 44' — returns a single { keyPath, publicKey }. See the getPublicKey page.

signTransaction​

info

Simple send (senderAddress / receiverAddress / lovelaceToSend) needs UTXO lookup (RPC) and is unsupported over the signTransaction wire (returns -32602); send a completed txCbor or structured (inputs/outputs) transaction instead.

Variant — CIP-30 CBOR​

payload.transaction:

{
"txCbor": "84a3008182582…"
} // built by the app with @emurgo/csl

The device signs the CBOR body hash (it does not validate UTXOs).

Variant — CIP-95 governance (DRep vote)​

An App request: the app builds a CIP-95 transaction whose CBOR carries governance fields (a voting_procedures DRep vote, or a vote-delegation certificate). Governance signing has no floor of its own — it is covered by the CIP-1852 requirement above (DCENT Biometric Wallet 2.36.2 / DCENT X 1.0.1). Below that the device does not report the CIP-1852 coin entry at all and every call on m/1852' returns the firmware-update error device_fw_incompatible (5005).

payload.transaction — DRep vote:

{
"txCbor": "84a40081825820aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00018182581d61f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f01a000f4240021a00030d4013a18202581c33333333333333333333333333333333333333333333333333333333a1825820bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb008201f6a0f5f6"
} // CIP-95 voting_procedures: DRep key voter + one governance action
// source preset: ada-cbor-signtx-cip95-drep-vote
info

The example CBOR uses placeholder inputs (fixture data) — it exercises the signing path only and is not broadcastable. Rebuild it with your own stake key, live UTXOs and DRep id. The bridge detects the voter / certificate and adds the DRep or stake witness path; unsupported voter types (CC hot key or script, DRep script, SPO) and committee certificates (hot-auth / cold-resign) fail closed. A vote-delegation example is preset ada-cbor-signtx-cip95-vote-deleg. Spec: CIP-95.

info

Up to three device signatures. Governance / voting (CIP-95) transactions can require up to three device signatures — the payment witness is always signed, plus a DRep witness (e.g. votes, DRep certificates) and/or a stake witness (e.g. stake certificates, withdrawals) — so they take longer than a normal signature. The request timeout (setTimeOutMs) defaults to 180000 (180 s), which covers this.

Response

FieldTypeNotes
body.parameter.signaturehexThe serialized signed transaction — Cardano transaction CBOR (body + witness set), not a bare signature. On this txCbor path the transaction body bytes you supplied are kept byte-for-byte, and the result is always the 4-element array (84…): a 3-element (83…) input comes back as 84… with is_valid = true added. The structured path frames its result differently depending on the shape you send — see signTransaction — structured.
header.statusstring"success" on completion; "failure" otherwise
{
"header": {
"version": "1.0",
"status": "success",
"response_from": "signTransaction"
},
"body": {
"command": "signTransaction",
"parameter": {
"signature": "84a4…"
}
}
}

signTransaction — structured​

Besides the opaque txCbor passthrough (the app builds the whole CBOR with @emurgo/csl and the device signs its body hash), Cardano also accepts a structured transaction: the app passes individual fields — inputs, outputs, fee, ttl, plus optional certificate / withdrawal / vote / mint / Plutus blocks — and the bridge assembles the CBOR host-side before signing. Unlike txCbor, every field stays editable, which is the point of this path.

Response — structured path

How the result is framed depends on which structured shape you send — the two are assembled by different code paths.

Ordinary transfer only — inputs / outputs / fee / ttl with none of the extended fields below present as keys. A key set to [] or {} still counts as present (except includeNetworkId, which counts only when it is true) and routes to the extended path, which rejects it with -32602, so omit it (or set it to null) rather than sending an empty value. This shape comes back as a 3-element [body, witness_set, auxiliary] array (83…). This differs from the 4-element (84…) result of every other path, so test it on a testnet before relying on it; if you need a known-good result today, build the CBOR yourself and use the opaque txCbor path.

Every other structured operation — anything carrying certificates, withdrawals, voting_procedures, mint, auxiliaryData, collateralInputs / collateral, collateralReturn, totalCollateral, referenceInputs, scriptDataHash, requiredSigners, or includeNetworkId: true — is assembled host-side with CSL and signed by the same signer as txCbor, so it comes back as the 4-element Conway array (84…).

Supported structured operations​

OperationStructured fieldPreset
Ordinary transferinputs / outputsada-structured-signtx-ordinary
Vote delegationcertificates[].VOTE_DELEGATIONada-structured-signtx-vote-deleg
Stake registration + delegationcertificates[].STAKE_REGISTRATION / STAKE_DELEGATIONada-structured-signtx-stake-registration
Reward withdrawalwithdrawals[]ada-structured-signtx-withdrawal
DRep governance votevoting_procedures[]ada-structured-signtx-drep-vote
Native token mint / burnmint[]ada-structured-signtx-mint — 🔴 no script witness derived (see note below)
Plutus scriptcollateralInputs / referenceInputs / scriptDataHashada-structured-signtx-plutus — 🔴 no script witness derived (see note below)

This table lists the operations documented here, not the full set the bridge accepts — certificates[].type also accepts STAKE_DEREGISTRATION, STAKE_AND_VOTE_DELEGATION, STAKE_REGISTRATION_AND_DELEGATION, STAKE_VOTE_REGISTRATION_AND_DELEGATION, DREP_REGISTRATION, DREP_UPDATE and DREP_DEREGISTRATION, and the base shape also accepts auxiliaryData and requiredSigners (body key 14).

warning

Moving an existing multi-asset (native) token is not one of the structured operations above. outputs[].amount in every structured variant, including Ordinary transfer, is lovelace-only. outputs[].tokenBundle is recognized but rejected with -32602 param_error in this shape, so there is no supported way to attach a token balance to an output. mint[] above only mints or burns supply; it does not move an existing balance either. To send an existing native token, build the full CBOR yourself (including the multi-asset value) with @emurgo/csl and use the opaque CIP-30 CBOR path instead — the device signs the body hash without needing the token in any registry.

Example — ordinary transfer​

payload.transaction:

{
"signingMode": "ORDINARY_TRANSACTION",
"inputs": [
{
"prev_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1v8e09uhj7te09uhj7te09uhj7te09uhj7te09uhj7te09us0kxhfc",
"amount": "2000000"
}
],
"fee": "170000",
"ttl": "100000000",
"networkId": 1,
"protocolMagic": 764824073
}
info

Before broadcasting, replace inputs[].prev_hash / prev_index with your own UTXO and outputs[].address with a valid bech32 recipient — the placeholder input (aaaa…) signs but cannot be broadcast. amount / fee are in lovelace (1 ADA = 1e6). The signing key is the top-level Key Path (m/1852'/1815'/0'/0/0); the per-input path is ignored. 🔴 Per-input amount changes the validation mode. Supply it on every input and the bridge enforces Σinput == Σoutput + fee exactly, rejecting anything else with -32602 param_error — so you must include your own change output. Omit it on any input and the check is skipped entirely — input amounts are never encoded into the transaction body, so the body is byte-identical either way and an unbalanced transaction signs without complaint, only to be rejected at submission. For certificate / withdrawal / vote operations the bridge derives the witness path automatically from the structured fields (stake or DRep role). 🔴 mint and Plutus are not covered. These paths do return a complete transaction, but its witness set carries vkey witnesses only — the policy script (mint) and the Plutus script, datum and redeemer are neither derived nor accepted as input (there is no field for them), so a structured mint / Plutus transaction signs but cannot be submitted. Build the complete CBOR yourself — body and the full witness set — and send it through the opaque txCbor path instead.

Structured base fields​

Every structured variant shares this base transaction shape; each variant below adds its own fields.

FieldTypeRequiredNotes
signingModestringOptionalDefaults to ORDINARY_TRANSACTION when omitted or null. Only the ordinary-transfer path validates it (any other value → -32602); on the extended variants it is not read at all, so the value shown in those examples is inert.
inputs[].prev_hashstring (hex)Required-Real32-byte source-tx hash of your UTXO (txHash alias accepted). Placeholder aaaa… signs but cannot broadcast. Per-input path/amount are optional; path is always ignored, and amount is read only on the ordinary-transfer path (where it selects the balance-check mode — see that example). The extended variants ignore it, so they perform no balance check.
inputs[].prev_indexnumberRequiredOutput index within the source tx (index alias accepted).
outputs[].addressstring (bech32)Required-RealRecipient address; CSL checksum-validated.
outputs[].amountstringRequiredLovelace (1 ADA = 1e6).
feestringRequiredTransaction fee in lovelace.
ttlstringOptionalTime-to-live — absolute slot upper bound. Encoded only when present.
networkIdnumberOptional1 = mainnet, 0 = testnet. Advisory on the ordinary path — not encoded there; the chainId decides the network. 🔴 On the extended path it is encoded (body key 15) when includeNetworkId is true, and then it must be exactly 0 or 1 or the request is -32602.
protocolMagicnumberOptionalMainnet 764824073; present in the ordinary example.

Variant — CIP-95 vote delegation​

Variant-specific fields (in addition to the base fields above):

FieldTypeRequiredNotes
certificates[].typestring (enum)Required"VOTE_DELEGATION".
certificates[].keyHashstring (hex)Required-Real28-byte stake credential key-hash.
certificates[].drepobjectRequired-RealTarget DRep — {type:"KEY_HASH",keyHash} | {type:"ALWAYS_ABSTAIN"} | {type:"ALWAYS_NO_CONFIDENCE"}.

Delegate your stake credential's voting power to a DRep via a certificates[] entry of type VOTE_DELEGATION. The bridge detects the certificate and signs with the stake witness path automatically.

payload.transaction:

{
"signingMode": "ORDINARY_TRANSACTION",
"inputs": [
{
"prev_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1qyenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxv6yg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zqjeq96c",
"amount": "1000000"
}
],
"fee": "200000",
"ttl": "21084865",
"networkId": 1,
"certificates": [
{
"type": "VOTE_DELEGATION",
"keyHash": "a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0",
"drep": {
"type": "KEY_HASH",
"keyHash": "c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0"
}
}
]
}
info

Edit certificates[].keyHash (your 28-byte stake credential) and certificates[].drep — {type:'KEY_HASH',keyHash}, or {type:'ALWAYS_ABSTAIN'} / {type:'ALWAYS_NO_CONFIDENCE'}. The placeholder hashes sign but cannot be broadcast.

Variant — Stake registration + delegation​

Variant-specific fields (in addition to the base fields above):

FieldTypeRequiredNotes
certificates[].typestring (enum)RequiredSTAKE_REGISTRATION or STAKE_DELEGATION.
certificates[].keyHashstring (hex)Required-Real28-byte stake credential (same on both certificates).
certificates[].depositstringOptionalOn STAKE_REGISTRATION — protocol key deposit in lovelace (e.g. 2000000). Omit it to emit the implicit-deposit (pre-Conway) certificate form.
certificates[].poolKeyHashstring (hex)Required-RealOn STAKE_DELEGATION — 28-byte target pool key-hash.

Register a stake key (STAKE_REGISTRATION with deposit) and delegate it to a pool (STAKE_DELEGATION with poolKeyHash) in one transaction. The bridge signs both certificates with the stake witness path.

payload.transaction:

{
"signingMode": "ORDINARY_TRANSACTION",
"inputs": [
{
"prev_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1qyenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxv6yg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zqjeq96c",
"amount": "1000000"
}
],
"fee": "200000",
"ttl": "21084865",
"networkId": 1,
"certificates": [
{
"type": "STAKE_REGISTRATION",
"keyHash": "a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0",
"deposit": "2000000"
},
{
"type": "STAKE_DELEGATION",
"keyHash": "a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0",
"poolKeyHash": "b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0"
}
]
}
info

Set both certificates[].keyHash to your stake credential, poolKeyHash to the target pool's key-hash, and deposit to the protocol key deposit (e.g. 2 ADA = 2000000 lovelace).

Variant — Reward withdrawal​

Variant-specific fields (in addition to the base fields above):

FieldTypeRequiredNotes
withdrawals[].rewardAddressstring (bech32)Required-RealReward/stake address (stake1… / stake_test1…); key-credential only — a script credential is rejected at signing time with -32601 method_not_found, not at CBOR assembly. -32601 is the shared code for nearly every witness-analysis rejection, not a marker unique to this one (see Common Mistakes).
withdrawals[].amountstringRequiredReward lovelace to withdraw.

Withdraw staking rewards via withdrawals[]. The bridge recognises a key-credential reward address and signs with the stake witness path (script-credential reward addresses are rejected).

payload.transaction:

{
"signingMode": "ORDINARY_TRANSACTION",
"inputs": [
{
"prev_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1qyenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxv6yg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zqjeq96c",
"amount": "1000000"
}
],
"fee": "200000",
"ttl": "21084865",
"networkId": 1,
"withdrawals": [
{
"rewardAddress": "stake1u8gdp5xs6rgdp5xs6rgdp5xs6rgdp5xs6rgdp5xs6rgdp5qsy63p4",
"amount": "1500000"
}
]
}
info

Replace withdrawals[].rewardAddress with your bech32 reward address (stake1… / stake_test1…) and amount with the reward lovelace to claim.

Variant — CIP-95 DRep governance vote​

Variant-specific fields (in addition to the base fields above):

FieldTypeRequiredNotes
voting_procedures[].voterobjectRequired-Real{type:"DREP_KEY",keyHash} — 28-byte DRep key-hash. Any other voter type is rejected with -32602 (this shape cannot express it).
voting_procedures[].govActionIdobjectRequired-Real{txHash,index} — 32-byte governance-action tx hash + index.
voting_procedures[].votestring (enum)RequiredYES / NO / ABSTAIN.

A registered DRep casts a vote on a governance action via voting_procedures[]. The bridge detects the DRep-key voter and signs with the DRep witness path (CC-Hot / DRep-script / SPO voters are rejected).

payload.transaction:

{
"signingMode": "ORDINARY_TRANSACTION",
"inputs": [
{
"prev_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1qyenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxv6yg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zqjeq96c",
"amount": "1000000"
}
],
"fee": "200000",
"ttl": "21084865",
"networkId": 1,
"voting_procedures": [
{
"voter": {
"type": "DREP_KEY",
"keyHash": "c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0"
},
"govActionId": {
"txHash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"index": 0
},
"vote": "YES"
}
]
}
info

Set voter.keyHash to your 28-byte DRep key-hash, govActionId (txHash + index) to the target action, and vote to YES / NO / ABSTAIN.

Variant — Native token mint / burn​

Variant-specific fields (in addition to the base fields above):

FieldTypeRequiredNotes
mint[].policyIdstring (hex)Required-Real28-byte minting policy id.
mint[].tokens[].assetNameHexstring (hex)Required0–32-byte asset name (744443454e54 = "tDCENT"; default asset = ""). assetNameBytes alias accepted.
mint[].tokens[].amountstringRequiredSigned 64-bit integer: positive = mint, negative = burn, 0 rejected.

Mint or burn native tokens via mint[] (transaction body key 9). amount is a signed 64-bit integer string — positive mints, negative burns, zero is rejected.

payload.transaction:

{
"signingMode": "ORDINARY_TRANSACTION",
"inputs": [
{
"prev_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1qyenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxv6yg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zqjeq96c",
"amount": "1000000"
}
],
"fee": "200000",
"ttl": "21084865",
"networkId": 1,
"mint": [
{
"policyId": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"tokens": [
{
"assetNameHex": "744443454e54",
"amount": "1000000"
}
]
}
]
}
info

🔴 The returned transaction's witness set carries vkey witnesses only — the policy-script witness is neither derived nor accepted as input, so this signs but cannot be submitted. Build the complete CBOR (body and witness set) and use the opaque txCbor path instead. Set mint[].policyId (28-byte policy), tokens[].assetNameHex (0–32-byte asset name; 744443454e54 = 'tDCENT', default asset = ''), and the signed amount.

Variant — Plutus script (collateral / reference inputs)​

Variant-specific fields (in addition to the base fields above):

FieldTypeRequiredNotes
collateralInputs[]arrayRequired-RealCollateral UTXOs {prev_hash,prev_index} (collateral alias accepted).
scriptDataHashstring (hex)Required-Real32-byte hash of redeemer + datum + cost-models. The bridge does not require it — a transaction built without it still reaches the device — but when present it must be 32-byte hex and needs collateralInputs, otherwise -32602
collateralReturnobjectOptionalChange for unused collateral {address,amount}.
totalCollateralstringOptionalTotal collateral in lovelace.
referenceInputs[]arrayOptionalReference-script / inline-datum UTXOs {prev_hash,prev_index}.

Assemble a Plutus-script transaction with scriptDataHash (body key 11), collateralInputs (13), collateralReturn (16), totalCollateral (17), and referenceInputs (18).

payload.transaction:

{
"signingMode": "ORDINARY_TRANSACTION",
"inputs": [
{
"prev_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1qyenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxv6yg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zqjeq96c",
"amount": "1000000"
}
],
"fee": "200000",
"ttl": "21084865",
"networkId": 1,
"collateralInputs": [
{
"prev_hash": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"prev_index": 1
}
],
"collateralReturn": {
"address": "addr1qyenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxv6yg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zqjeq96c",
"amount": "4500000"
},
"totalCollateral": "500000",
"referenceInputs": [
{
"prev_hash": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"prev_index": 2
}
],
"scriptDataHash": "abababababababababababababababababababababababababababababababab"
}
info

🔴 The returned transaction's witness set carries vkey witnesses only — the Plutus witness (script, datum, redeemer) is neither derived nor accepted as input, so this signs but cannot be submitted. Build the complete CBOR (body and witness set) and use the opaque txCbor path instead. Replace the collateral / reference UTXOs (prev_hash / prev_index) with your own and scriptDataHash with the redeemer + datum + cost-model hash for your script.

signData CIP-8 / CIP-30 / CIP-95​

await dcent.sign({
method: 'signData',
chainId: 'cip34:1-764824073',
payload: {
keyPath: "m/1852'/1815'/0'/0/0",
address: '<hex credential>',
payload: '<hex bytes>'
}
})
// → body.parameter = { signature, key } // CIP-30 DataSignature (key = COSE_Key)
FieldTypeRequiredSourceDescription
keyPathstringRequiredAppBIP-44 path. Account path on purpose 1852'. 🔴 The signing role is chosen by address, not by the role segment you send. A 29-byte stake address (0xe0/0xe1) → role 2 and a 28-byte DRep credential → role 3 — for those two only the account index of your keyPath is used. A 29-byte enterprise address (0x60/0x61) — the ordinary CIP-30 message case — signs with the payment key at exactly the keyPath you sent (e.g. …/0/0), as does any other payment address. Script credentials (0xf0/0xf1), any other unrecognized 29-byte header, a non-empty address shorter than 28 bytes, a stake address whose network header does not match the wallet (0xe0 testnet against a mainnet wallet or the reverse), and a payload whose decoded bytes exceed 65,536 (64 KiB) (reachable only through the UTF-8 fall-through — a hex payload is capped earlier by the connector, see the payload row) or the degenerate "0x" are all refused — these surface as internal_error (-32603), not the method_not_found / param_error used elsewhere on this page.
addressstring (hex)RequiredAppThe address bytes as hex (the CIP-30 Address hex you were handed), not a bech32 addr1… / stake1… string. Length selects the role, so do not strip it: 29 bytes 0xe0/0xe1 = stake address, 29 bytes 0x60/0x61 = enterprise payment address, and 28 bytes is read as a DRep credential — trimming a base address down to its payment key hash silently lands in the DRep branch and fails the ownership check with internal_error (-32603).
payloadstring (hex)RequiredAppThe message / data bytes to sign, hex-encoded (bare or 0x-prefixed). The connector caps the whole serialized payload object at 65,536 characters — thrown by sign() before the popup opens, not returned as an error envelope — so a hex payload tops out near 32 KB of data bytes; a decoded payload over 65,536 bytes (64 KiB) is rejected as well. 🔴 Only even-length hex is read as bytes — anything else, including an odd-length hex string, is silently signed as its own UTF-8 text rather than rejected.

Response

FieldTypeNotes
body.parameter.signaturehexCIP-30 DataSignature — the COSE_Sign1 structure over the payload (not a broadcastable transaction)
body.parameter.keyhexCOSE_Key — the public key for verifying the signature
header.statusstring"success" on completion; "failure" otherwise

Common Mistakes​

danger

Invalid txCbor — must be valid CIP-30 CBOR. bech32 instead of hex address bytes in signData.address, or trimming an address to a bare 28-byte key hash (read as a DRep credential). Unfunded input UTXO in the structured inputs[] (the simple-send form is not accepted over this wire — see the note at the top of signTransaction).

danger

Sending a m/44' keyPath to a stake or governance call. signData and stake / DRep getPublicKey return -32601 method_not_found, and CBOR / certificate / withdrawal / governance / mint / Plutus transactions return -32602 param_error — legacy Cardano is plain-transfer only. If you are reading a rewardAddress from getAddress and finding it missing, that is the same cause. Switch the keyPath to m/1852'/1815'/… — see Derivation purpose.

🔴 A legacy keyPath is not the only source of -32601. On m/1852' the same code is returned for every witness-analysis rejection except the one noted below: an unsupported certificate kind, a committee certificate (hot-auth / cold-resign, which needs the committee cold key), an unsupported voter (CC hot key or script, DRep script, SPO), a script or unknown reward credential, a sparse voter/proposal entry, an unrecognized voter kind, a malformed certificate kind, and a pool registration requiring the operator cold key. Read body.error.message, which names the specific cause, before concluding the keyPath is wrong. (An over-long CIP-100 anchor URL is the one witness-analysis rejection that returns param_error (-32602) instead.) On the structured path (certificates / voting_procedures fields), an unsupported certificate type or a voter other than DREP_KEY is rejected earlier, while the transaction is built, with param_error (-32602).

warning

Assuming getPublicKey always returns the { payment, stake, drep } bundle on Cardano. It does on m/1852'; on a m/44' path whose role segment is 2 or 3 it fails with method_not_found (-32601); on m/44'/1815'/<account>'/0/<index> or …/1/<index> (account hardened, role and index unhardened) it returns a single { keyPath, publicKey }; any other m/44' form is rejected with param_error (-32602). Reading body.parameter.stake without checking gives undefined.