Skip to main content

VeChain (VET)

Sign VeChain Thor transactions. The app builds a Thor transaction (thor-devkit shape) with one or more clauses; the device signs the transaction hash by its keyPath. Note: only the first clause is used.

FieldValue
FamilyVeChain
coinType818
keyPathm/44'/60'/<accountIdx>'/0/0
amount unitwei
chainIdvechain:b1ac3413d346d43539627e6be7ec1b4a/slip44:818

Supported Methods

MethodSupported
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.

MethodDCENT Biometric WalletDCENT X
getAddress2.23.4 or higher1.0.0 or higher
signTransaction2.23.4 or higher1.0.0 or higher
info

VeChain derives its address on the EVM coinType 60 path (m/44'/60'/0'/0/0) even though its SLIP-44 coinType is 818. The chainId on the wire is always the full vechain:b1ac3413d346d43539627e6be7ec1b4a/slip44:818.

getAddress — account address

Retrieves the VeChain address. Returns a 0x address (same key as the EVM derivation).

FieldTypeRequiredSourceNotes
chainIdstringRequiredAppFull CAIP-19 — vechain:b1ac3413d346d43539627e6be7ec1b4a/slip44:818
keyPathstringRequiredAppBIP-32 path; VeChain uses the EVM coinType 60 path (m/44'/60'/0'/0/0)

Request

await dcent.getAddress({
chainId: 'vechain:b1ac3413d346d43539627e6be7ec1b4a/slip44:818',
keyPath: "m/44'/60'/0'/0/0"
})

Response

FieldTypeNotes
header.statusstring"success" on success
body.commandstring"getAddress"
body.parameter.addresshex addrThe VeChain account address — an EVM-style 0x-prefixed 20-byte hex string (same key as the EVM derivation). VeChain has no address variants, so no addressFormat is needed.
{
"header": {
"version": "1.0",
"status": "success"
},
"body": {
"command": "getAddress",
"parameter": {
"address": "0x6A5C…"
}
}
}

signTransaction — Thor transaction

Signs a VeChain Thor transaction. Returns the signed raw transaction ready to broadcast.

Payload — native VET transfer

payload.transaction:

{
"chainTag": 74,
"blockRef": "0x0000000000000000",
"expiration": 720,
"clauses": [
{
"to": "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
"value": "0x10",
"data": "0x"
}
],
"gasPriceCoef": 0,
"gas": 21000,
"fee": "210000000000000000",
"dependsOn": null,
"nonce": "0x0"
}
FieldTypeRequiredSourceUnitNotes
chainTagnumberRequiredAppNetwork tag (74 = mainnet) — last byte of the genesis block id
blockRefhexRequired-RealAppReal reference block — the app must supply it (non-zero); not auto-filled
expirationnumberRequiredAppblocksBlock count the tx stays valid after blockRef
clauses[].tohex addrRequired-RealAppRecipient (native) or token contract (VIP-180)
clauses[].valuehexRequiredAppweiVET amount; 0x0 for token transfers (amount lives in data)
clauses[].datahexOptionalApp0x for native; calldata for contracts (opaque pass-through)
gasPriceCoefnumberRequiredAppGas price coefficient (0–255)
gasnumberRequired-RealAppGas limit; app estimate
feestringRequired-RealAppTotal VTHO in wei-scale (display only — not part of the signed RLP). Paired with gas by the fee gate; omitting it fails with -32602
dependsOnhex / nullOptionalAppTx id this tx depends on, or null
noncehexRequiredAppTransaction nonce

Request

await dcent.sign({
method: 'signTransaction',
chainId: 'vechain:b1ac3413d346d43539627e6be7ec1b4a/slip44:818',
payload: {
keyPath: "m/44'/60'/0'/0/0",
transaction: {
chainTag: 74,
blockRef: '0x0000000000000000',
expiration: 720,
clauses: [
{
to: '0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd',
value: '0x10',
data: '0x'
}
],
gasPriceCoef: 0,
gas: 21000,
fee: '210000000000000000',
dependsOn: null,
nonce: '0x0'
}
}
})

Response

FieldTypeNotes
header.statusstring"success" on success
body.commandstring"signTransaction"
body.parameter.signaturehexThe broadcast-ready signed raw Thor transaction (RLP-encoded), not a bare signature. Broadcast this value as-is to a Thor node — do not re-encode it.
{
"header": {
"version": "1.0",
"status": "success"
},
"body": {
"command": "signTransaction",
"parameter": {
"signature": "0xf8…"
}
}
} // signed raw tx

Variant — VIP-180 token transfer a9059cbb

info

Token model: form-D descriptor. Always send the token under transaction.token with its symbol and trustworthy decimals (read from the chain yourself) — the bridge signs with no network access and never looks symbol / decimals up, so without the descriptor it fails closed with -32602. See Core Concepts → Token descriptors.

VIP-180 is VeChain's ERC-20-compatible token standard (e.g. VTHO, contract 0x0000000000000000000000000000456E65726779). The token amount lives in data; value stays 0x0.

payload.transaction:

{
"chainTag": 74,
"blockRef": "0x0000000000000000",
"expiration": 720,
"clauses": [
{
"to": "0x0000000000000000000000000000456E65726779",
"value": "0x0",
"data": "0xa9059cbb<recipient 32B><amount 32B>"
}
],
"gasPriceCoef": 0,
"gas": 80000,
"fee": "800000000000000000",
"dependsOn": null,
"nonce": "0x0"
}
  • clauses[0].to = the token contract (not the recipient). Required-Real
  • clauses[0].value = 0x0 (token amount is encoded in data).
  • clauses[0].data = selector 0xa9059cbb + recipient (32B, left-padded) + amount (32B, token base units). Example amount 0x3e8 = 1000 base units.
info

Single-clause; VIP-180 identified. The bridge uses only clauses[0] — multi-clause batches are not supported. VIP-180 token transfers are now identified (m02-05-75): the bridge decodes the transfer(recipient,amount) calldata via extractVechainTokenIdentity and synthesizes a TokenAccount, so the device shows the token symbol/amount — no longer opaque. A token still signs, but only with a form-D descriptor that carries decimals — the bridge signs with no network access and never looks the metadata up for you. If it can resolve neither (no descriptor) or the calldata is malformed it fail-closes with -32602, so a token transfer is never blind-signed as a 0-VET native tx. Non-token clause data stays opaque. (the bridge)

Variant — VIP-180 form-D descriptor

Send a compact token descriptor instead of building clauses yourself. The bridge ABI-encodes a9059cbb transfer(address,uint256) from { contract, to, amount } and emits a single clause { to: contract, value: "0", data }. Your envelope fields (chainTag, blockRef, expiration, gas, gasPriceCoef, fee, nonce, dependsOn) are preserved. amount is a base-unit string (not rescaled by decimals).

payload.transaction:

{
"chainTag": 74,
"blockRef": "0x00b3c1e4a5f6d7e8",
"expiration": 720,
"token": {
"contract": "0x4e17357053da4b473e2daa2c65c2c949545724b8",
"to": "0x0000000000000000000000000000000000000000",
"amount": "1000",
"decimals": 6,
"symbol": "VeUSD"
},
"gasPriceCoef": 0,
"gas": 80000,
"fee": "800000000000000000",
"dependsOn": null,
"nonce": "0x0"
}
FieldTypeRequiredNotes
token.contractstringRequiredVIP-180 contract; must match 0x + 40 hex chars. Unregistered tokens allowed.
token.tostringRequiredRecipient; also validated as 0x + 40 hex so a malformed address is rejected instead of silently padded.
token.amountstringRequiredBase-unit integer string, must fit uint256; not rescaled by decimals. 0 is allowed.
token.decimalsnumberRequiredBase-unit exponent; enables descriptor resolution on the device.
token.symbolstringOptionalDisplay metadata only.
blockRefstringRequired-Real8-byte block reference. The bridge does not refresh it — send a fresh value.
gas / feenumber / stringRequiredMap to the fee gate (gasLimit / fee). fee is the total in wei-scale VTHO.
chainTag / expiration / gasPriceCoef / nonce / dependsOnmixedRequiredThor envelope fields; passed through into the signed RLP.
info

Verified on device (2026-07-20): this descriptor displays as VeUSD 0.001amount "1000" at decimals 6. Note that fee is display-only; it is not part of the signed RLP (VeChain derives the actual cost from gas and gasPriceCoef on-chain).

Common Mistakes

danger

Wrong keyPath — VeChain uses the EVM path m/44'/60'/0'/0/0, not m/44'/818'/…. Stale blockRef — refresh blockRef (and nonce) immediately before signing or the tx expires. Multiple clauses — only the first clause is signed; extra clauses are ignored. VIP-180 to set to the recipient instead of the token contract, with the amount left out of data.