Tezos (XTZ)
Sign Tezos transaction operations. Provide a Taquito-style operation object together with extra.branch (a recent block hash); the bridge forges the operation locally and signs it with no network access, so counter, fee, gasLimit and storageLimit are used exactly as you supply them. You can instead send pre-forged operation bytes under unsignedTx. FA1.2 and FA2 token transfers are expressed as a smart-contract call to a KT1 contract.
| Field | Value |
|---|---|
| coinType | 1729 |
| keyPath | m/44'/1729'/<accountIdx>'/0' |
| addressFormat | omit or legacy (default account) / standard |
| amount unit | mutez |
| chainId | tezos:NetXdQprcVkpaWU/slip44:1729 |
| chainId · Testnet | tezos:NetXnHfVqm9iesp/slip44:1729 |
Supported Methods
| Method | Supported |
|---|---|
| getAddress | ✓ |
| signTransaction | ✓ |
| signMessage | ✗ |
| signTypedData | ✗ |
| signData | ✗ |
| signAuthEntry | ✗ |
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.24.1 or higher | 1.0.0 or higher |
signTransaction | 2.24.1 or higher | 1.0.0 or higher |
The Standard account (addressFormat: 'standard', below) needs DCENT Biometric Wallet firmware 2.36.2 or higher and DCENT X firmware 1.0.1 or higher.
addressFormat — Standard account
Tezos has two accounts on the same keyPath: DCENT's default account (SLIP-23 Ed25519) and a Standard account derived with standard SLIP-10 Ed25519. The two derive different keys, so they have different tz1… addresses. Because the keyPath is identical, it cannot tell them apart — addressFormat is the only thing that selects the Standard account.
addressFormat | Account |
|---|---|
omitted, or legacy | DCENT default account |
standard | Standard account (SLIP-10 Ed25519) |
Send the same value on getAddress and on signTransaction for that account — native transfers, FA1.2 / FA2 token transfers, the form-D descriptor and unsignedTx all follow it. legacy is the value getAccountInfo reports for the default account, so passing it back is fine; ledger, segwit-wrapped, segwit-native and taproot are rejected with -32602. The testnet chainId has the same two accounts. Every field that names your account — source, from in the form-D descriptor, and the sender in a raw FA1.2 / FA2 transfer (from / from_) — should be the address getAddress returned for the same addressFormat; it is not checked, and the value is signed as sent.
getAddress — account address
Retrieves the Tezos account. Returns a tz1… (ed25519) address derived from the keyPath.
| Field | Type | Required | Description |
|---|---|---|---|
chainId | string | Required | Full CAIP-19 id — tezos:NetXdQprcVkpaWU/slip44:1729 |
keyPath | string | Required | BIP32 derivation path. Default m/44'/1729'/0'/0' |
addressFormat | string | Optional | Omit (or legacy) for the default account; standard for the Standard account — see addressFormat |
Request
await dcent.getAddress({
chainId: 'tezos:NetXdQprcVkpaWU/slip44:1729',
keyPath: "m/44'/1729'/0'/0'"
})
Response
| Field | Type | Description |
|---|---|---|
body.parameter.address | string | Tezos account address in tz1… (ed25519) format, derived from the keyPath |
header.status | string | "success" when the address is returned |
{
"header": {
"version": "1.0",
"status": "success",
"response_from": "getAddress"
},
"body": {
"command": "getAddress",
"parameter": {
"address": "tz1…"
}
}
}
signTransaction
Signs a Tezos operation. Returns the signed operation bytes / signature ready to inject. To sign with the Standard account, add addressFormat: 'standard' to the payload, next to keyPath.
Variant — native XTZ transfer
payload.transaction:
{
"kind": "transaction",
"source": "tz1…",
"fee": "1420",
"counter": "<next counter>",
"gasLimit": "10307",
"storageLimit": "257",
"amount": "1000000",
"destination": "tz1burnburnburnburnburnburnburjAYjjX"
}
| Field | Type | Required | Source | Unit | Description |
|---|---|---|---|---|---|
kind | string | Required | App | — | Always "transaction" |
source | tz address | Optional | Wallet | — | Device Tezos account (tz1…); must be revealed / funded. Defaults to the keyPath's account if omitted. The bridge does not check a supplied value against that account — it is forged as sent |
destination | tz / KT address | Required-Real | App | — | Recipient (tz1… for native; KT1… for token calls) |
amount | string | Required | App | mutez | 1 XTZ = 1,000,000 mutez. "0" for token calls |
fee | string | Required-Real | App | mutez | Operation fee |
counter | string | Required-Real | App | — | Account counter — the next counter for source. It is forged into the signed bytes as sent, so it must be the real value |
gasLimit / storageLimit | string | Required-Real | App | — | Used exactly as supplied — the bridge does not re-estimate them |
extra.branch | base58 block hash | Required-Real | App | — | Recent block hash (B…) so the bridge can forge the operation offline. Required unless you send pre-forged unsignedTx; with neither, the request fails with -32602 |
Request
await dcent.sign({
method: 'signTransaction',
chainId: 'tezos:NetXdQprcVkpaWU/slip44:1729',
payload: {
keyPath: "m/44'/1729'/0'/0'",
transaction: {
kind: 'transaction',
source: 'tz1…',
fee: '1420',
counter: '<next counter>',
gasLimit: '10307',
storageLimit: '257',
amount: '1000000',
destination: 'tz1burnburnburnburnburnburnburjAYjjX',
extra: { branch: 'BLockGenesisGenesisGenesisGenesisGenesisf79b5d1CoW2' }
}
}
})
Response
| Field | Type | Description |
|---|---|---|
body.parameter.signature | string | The signed operation bytes (hex): the forged operation bytes followed by the signature. Submit it as-is through an RPC injection/operation call — it is not a bare edsig… signature. |
body.command | string | "signTransaction" |
header.status | string | "success" when signing completes on the device |
{
"header": {
"version": "1.0",
"status": "success",
"response_from": "signTransaction"
},
"body": {
"command": "signTransaction",
"parameter": {
"signature": "<forged operation hex><signature hex>"
}
}
} // signed operation bytes, ready to inject
Variant — FA1.2 token transfer TZIP-7
Registered tokens only. The bridge identifies the KT1… contract and looks it up among the tokens DCENT already supports (see Supported Coins & Tokens); an unsupported token sent in this raw shape fails with -32602. For any other FA token, use the form-D descriptor variant below and include token.decimals. The bridge never queries a node for you: you build the correct parameters.value yourself.
A fungible token transfer is a smart-contract call to the token's KT1… contract. The outer amount is "0" (no native XTZ moves); the token amount lives in the Michelson parameters.value.
payload.transaction:
{
"kind": "transaction",
"destination": "KT1TwzD6zV3WeJ39ukuqxcfK2fJCnhvrdN1X",
"amount": "0",
"fee": "1420",
"counter": "<next counter>",
"gasLimit": "10307",
"storageLimit": "257",
"extra": {
"branch": "BLockGenesisGenesisGenesisGenesisGenesisf79b5d1CoW2"
},
"parameters": {
"entrypoint": "transfer",
"value": {
"prim": "Pair",
"args": [
{
"string": "tz1…"
},
{
"prim": "Pair",
"args": [
{
"string": "tz1…"
},
{
"int": "1000"
}
]
}
]
}
}
}
| Field | Type | Required | Source | Description |
|---|---|---|---|---|
destination | KT address | Required-Real | App | FA1.2 token contract (e.g. SMAK, 3 decimals) |
amount | string | Required | App | Outer native XTZ = "0" |
parameters.entrypoint | string | Required | App | Always "transfer" |
value → args[0].string | tz address | Required-Real | Wallet | from — your device account (tz1…) |
value → args[1].args[0].string | tz address | Required-Real | App | to — recipient (tz1…) |
value → args[1].args[1].int | int string | Required | App | Token base units (1000 = 1 SMAK at 3 decimals) |
The bridge does not check these fields one by one: if the transfer shape does not match (for example a missing from, to or amount), the payload is not recognised as a token transfer and is signed as a plain contract call to the KT1… address — without the registered-token check.
Variant — FA2 token transfer TZIP-12
Registered tokens only. The bridge identifies the KT1… contract and looks it up among the tokens DCENT already supports (see Supported Coins & Tokens); an unsupported token sent in this raw shape fails with -32602. For any other FA token, use the form-D descriptor variant below and include token.decimals. The bridge never queries a node for you: you build the correct parameters.value yourself.
FA2 (multi-asset) uses a list-shaped transfer batch with an explicit token_id. Outer amount is again "0".
payload.transaction:
{
"kind": "transaction",
"destination": "KT193D4vozYnhGJQVtw7CoxxqphqUEEwK6Vb",
"amount": "0",
"fee": "1420",
"counter": "<next counter>",
"gasLimit": "10307",
"storageLimit": "257",
"extra": {
"branch": "BLockGenesisGenesisGenesisGenesisGenesisf79b5d1CoW2"
},
"parameters": {
"entrypoint": "transfer",
"value": [
{
"prim": "Pair",
"args": [
{
"string": "tz1…"
},
[
{
"prim": "Pair",
"args": [
{
"string": "tz1…"
},
{
"prim": "Pair",
"args": [
{
"int": "0"
},
{
"int": "1000000"
}
]
}
]
}
]
]
}
]
}
}
| Field | Type | Required | Source | Description |
|---|---|---|---|---|
destination | KT address | Required-Real | App | FA2 token contract (e.g. QUIPU, 6 decimals) |
value[0].args[0].string | tz address | Required-Real | Wallet | from_ — your device account (tz1…) |
…args[1][0].args[0].string | tz address | Required-Real | App | to_ — recipient (tz1…) |
inner Pair → first int | int string | Required | App | token_id — must be 0 (the signer supports token_id 0 only); a positive value is rejected with -32602 |
inner Pair → second int | int string | Required | App | amount in base units (1000000 = 1 QUIPU at 6 decimals) |
The bridge does not check these fields one by one: if the transfer shape does not match (for example a missing from, to or amount), the payload is not recognised as a token transfer and is signed as a plain contract call to the KT1… address — without the registered-token check.
Variant — FA1.2 / FA2 form-D descriptor (compact shape)
Instead of hand-building the Michelson Pair/int tree above, send a compact token descriptor and the bridge assembles the same operation for you. Omit tokenId for FA1.2; set tokenId: "0" for FA2 (only token_id 0 is supported this way — a non-zero string is rejected (a numeric tokenId is ignored and the operation is built as FA1.2)).
payload.transaction:
{
"token": {
"contract": "KT1TwzD6zV3WeJ39ukuqxcfK2fJCnhvrdN1X",
"to": "tz1burnburnburnburnburnburnburjAYjjX",
"amount": "1000",
"decimals": 3,
"symbol": "SMAK"
},
"from": "tz1…",
"fee": "1420",
"counter": "<next counter>",
"gasLimit": "10307",
"storageLimit": "257",
"extra": {
"branch": "BLockGenesisGenesisGenesisGenesisGenesisf79b5d1CoW2"
}
} // tokenId omitted = FA1.2. Add "tokenId": "0" for FA2.
| Field | Type | Required | Description |
|---|---|---|---|
token.contract | KT address | Required-Real | FA contract address, must start with KT1 |
token.to | tz address | Required-Real | Recipient tz account — cannot be a KT1 contract (rejected to prevent misdirected funds) |
token.amount | string | Required | Base units, non-negative integer string |
token.tokenId | string | Optional | Omit for FA1.2. Set to the string "0" for FA2 — any other string is rejected with -32602 (signer only supports token_id 0). A non-string value — e.g. the number 0 or 1 — is ignored and the operation is built as FA1.2 |
token.decimals | number | Required for unsupported tokens | Token decimals. A token DCENT does not already support fails with -32602 without it |
token.symbol | string | Optional | Token symbol |
from | tz address | Required-Real | Sender — your device account (sender / source / from_address / fromAddress / owner_address / ownerAddress also accepted). Missing → -32602 |
Both token variants go to a KT1… contract with entrypoint: "transfer" and outer amount: "0". The bridge identifies the token transfer and synthesizes the token account — set the inner from / from_ to your own device account. Every token shape (raw FA1.2 / FA2 and form-D) still needs fee, counter, gasLimit, storageLimit and extra.branch exactly as in the native transfer — without them the request fails with -32602.
Variant — unsignedTx (pre-forged operation)
Send a pre-forged Tezos operation (hex bytes: branch + contents, already built and encoded outside the device) under unsignedTx — the same field name Hedera uses (no new field). The bytes are signed as sent. DCENT X decodes them: it accepts only reveal and transaction operations (any other operation kind, or a second transaction, is rejected) and shows that transaction's recipient and amount. This is the only fully self-contained signTransaction form for Tezos; missing or malformed bytes return -32602.
| Field | Type | Required | Source | Description |
|---|---|---|---|---|
unsignedTx | hex string | Required-Real | App | Pre-forged operation bytes (hex): branch(32B) + contents, built outside the device |
Request
await dcent.sign({
method: 'signTransaction',
chainId: 'tezos:NetXdQprcVkpaWU/slip44:1729',
payload: {
keyPath: "m/44'/1729'/0'/0'",
transaction: {
"unsignedTx": "288f8bb117200c366cade5c8f22e0854cc99429853dcc9b35287cc9cdcc8e3c66c00338cdba49ec68d3e14903e4fd37db865a990ed22900301e80700c0843d000041812f66a66258718eb77f7bdc4f89e2f151af8a00"
}
}
})
Common Mistakes
Non-zero outer amount on a token transfer — for FA1.2 / FA2 the outer amount must be "0", otherwise that native XTZ is moved as well, and DCENT X shows the outer amount instead of the token amount. tz address as token destination — token transfers go to the KT1… contract, not the recipient tz1…. Whole-token amounts — the Michelson int is base units (SMAK 3 decimals, QUIPU 6 decimals), not display units. Unrevealed source — a brand-new source account must be revealed / funded before its operation can be injected.