Skip to main content

HAVAH (HVH)

Sign HAVAH transactions. HAVAH is an ICON-family chain, so transactions use the ICON-native wire shape (from/to/value/stepLimit/stepPrice/timestamp/nonce). The bridge converts this wire into a HavahTransaction before the device signs.

FieldValue
coinType858
keyPathm/44'/858'/<accountIdx>'/0/0
amount unitloop
chainIdhavah:mainnet/slip44:858
chainId · Testnethavah:testnet/slip44:858

Supported Methods​

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

MethodDCENT Biometric WalletDCENT X
getAddress2.26.0 or higher1.0.0 or higher
signTransaction2.26.0 or higher1.0.0 or higher

getAddress — account address​

Retrieves the HAVAH account address for a chainId + keyPath. HAVAH uses the ICON-family hx… address format (there are no address variants). Call this before building a transaction so from matches the device account.

FieldTypeRequiredSourceDescription
chainIdstringRequiredAppFull CAIP-19 id — havah:mainnet/slip44:858
keyPathstringRequiredAppBIP-44 derivation path — default m/44'/858'/0'/0/0

Request

await dcent.getAddress({
chainId: 'havah:mainnet/slip44:858',
keyPath: "m/44'/858'/0'/0/0"
})

Response

FieldTypeDescription
header.statusstringsuccess on a completed request
body.commandstringEchoes the method — getAddress
body.parameter.addressstringHAVAH account address in ICON-family hx… format
{
"header": {
"version": "1.0",
"status": "success",
"response_from": "getAddress"
},
"body": {
"command": "getAddress",
"parameter": {
"address": "hx…"
}
}
}

signTransaction — ICON-native wire​

Variant — native HVH transfer​

payload.transaction:

{
"from": "hx0000000000000000000000000000000000000000",
"to": "hx0000000000000000000000000000000000000000",
"value": "0xDE0B6B3A7640001",
"stepLimit": "0x186a0",
"stepPrice": "0x2e90edd000",
"timestamp": "<current time in µs, hex>",
"nid": "0x100",
"nonce": "0x1",
"version": "0x3"
}
FieldTypeRequiredSourceDescription
fromstringRequired-RealWalletDevice HAVAH account (hx…). Not checked against the keyPath account — a different address is signed as sent
tostringRequired-RealAppRecipient (hx… for native send)
valuehexRequiredAppAmount in loop, 0x-prefixed hex (1 HVH = 1e18 loop); a plain decimal string is read as base-10. Send it as a string — values exceed MAX_SAFE_INTEGER
stepLimithexRequiredAppStep (gas) limit, hex. When provided together with stepPrice the bridge builds a self-contained preparedFee
stepPricehexRequired-RealAppReal, current step price, hex. Missing or zero → -32602; a stale value is not detected. Not part of the signed transaction — it sets the fee sent to the device (stepPrice × stepLimit)
timestamphexRequired-RealAppICON microsecond epoch. The wallet never fills this in — the value you send is the one that is signed. Omitting it fails with -32602
nidhexOptionalAppIgnored — the wallet signs 0x100 on mainnet and 0x101 on testnet, from the chainId
noncehexOptionalAppTransaction nonce, hex. Defaults to 0x1 if omitted; 0x0 (or 0) is dropped from the signed transaction
versionhexOptionalAppIgnored — the wallet always signs version 0x3

js — Request:

await dcent.sign({
method: 'signTransaction',
chainId: 'havah:mainnet/slip44:858',
payload: {
keyPath: "m/44'/858'/0'/0/0",
transaction: {
from: "hx0000000000000000000000000000000000000000",
to: "hx0000000000000000000000000000000000000000",
value: "0xDE0B6B3A7640001",
stepLimit: "0x186a0",
stepPrice: "0x2e90edd000",
timestamp: "<current time in µs, hex>",
nid: "0x100",
nonce: "0x1",
version: "0x3"
}
}
}) // → body.parameter.signature

Response

FieldTypeDescription
header.statusstringsuccess on a completed signature
body.commandstringSigning command echo — signTransaction
body.parameter.signaturestringThe device signature (65 bytes, r‖s‖recovery id, on DCENT X) as base64 — not a serialized transaction. To broadcast, set it as the signature field of the ICON transaction exactly as it was signed — nid from the chainId, version 0x3, the nonce you sent (or 0x1; a 0x0 nonce is left out), and numeric fields as lowercase 0x hex — and call icx_sendTransaction
{
"header": {
"version": "1.0",
"status": "success",
"response_from": "signTransaction"
},
"body": {
"command": "signTransaction",
"parameter": {
"signature": "<base64 signature>"
}
}
}

Variant — HSP-20 token transfer​

info

Token model: form-D descriptor. A token on DCENT's supported list is signed with its registered symbol and decimals. For any other token, send a form-D descriptor under transaction.token — contract, to, amount, decimals and optionally symbol (without contract — or its aliases assetId / address — the descriptor is ignored) — the bridge signs with no network access and uses the decimals you supply. See Core Concepts → Token descriptors.

HSP-20 (IRC-2-like) tokens use the ICON call data shape. to is the token contract (cx…), the real recipient is data.params._to, and native value stays 0x0 (no native HVH moves).

payload.transaction:

{
"dataType": "call",
"from": "hx0000000000000000000000000000000000000000",
"to": "cxd7fed2ae1d9b2314087520efe1870c33e5c5baa4",
"value": "0x0",
"stepLimit": "0x186a0",
"stepPrice": "0x2e90edd000",
"timestamp": "<current time in µs, hex>",
"nid": "0x100",
"nonce": "0x1",
"version": "0x3",
"data": {
"method": "transfer",
"params": {
"_to": "hx0000000000000000000000000000000000000000",
"_value": "0xDE0B6B3A7640001"
}
}
}
FieldTypeRequiredSourceDescription
dataTypeenumRequired-RealAppMust be call for a contract call — not checked: without it the request is signed as a native HVH transfer to the cx… address and data is dropped
tostringRequired-RealAppToken contract address (cx…), not the recipient
valuehexRequiredAppMust be present and non-negative; it is not part of the signed call — keep 0x0
data.methodstringRequiredAppContract method — transfer
data.params._tostringRequired-RealAppReal token recipient (hx…)
data.params._valuehexRequiredAppToken amount in base units, 0x-prefixed hex (PER has 18 decimals; 0xF4240 = 1e6 for a 6-decimal token)
timestamphexRequired-RealAppICON microsecond epoch — the wire path never fills it in; omitting it fails with -32602

js — Request:

await dcent.sign({
method: 'signTransaction',
chainId: 'havah:mainnet/slip44:858',
payload: {
keyPath: "m/44'/858'/0'/0/0",
transaction: {
dataType: "call",
from: "hx0000000000000000000000000000000000000000",
to: "cxd7fed2ae1d9b2314087520efe1870c33e5c5baa4",
value: "0x0",
stepLimit: "0x186a0",
stepPrice: "0x2e90edd000",
timestamp: "<current time in µs, hex>",
nid: "0x100",
nonce: "0x1",
version: "0x3",
data: { method: "transfer",
params: { _to: "hx0000000000000000000000000000000000000000",
_value: "0xDE0B6B3A7640001" } }
}
}
}) // → body.parameter.signature

Response

FieldTypeDescription
header.statusstringsuccess on a completed signature
body.commandstringSigning command echo — signTransaction
body.parameter.signaturestringThe device signature (65 bytes, r‖s‖recovery id, on DCENT X) as base64 — not a serialized transaction. To broadcast, set it as the signature field of the ICON transaction exactly as it was signed — nid from the chainId, version 0x3, the nonce you sent (or 0x1; a 0x0 nonce is left out), no value field for the HSP-20 call, and numeric fields (including _value) as lowercase 0x hex — and call icx_sendTransaction
{
"header": {
"version": "1.0",
"status": "success",
"response_from": "signTransaction"
},
"body": {
"command": "signTransaction",
"parameter": {
"signature": "<base64 signature>"
}
}
}
info

Registered HSP-20 tokens are looked up on the selected network — HAVAH mainnet and testnet each have their own list. A transfer call to an unregistered HSP-20 contract is rejected with -32602 — to sign one, use the form-D descriptor below with token.decimals (see Core Concepts → Token descriptors). A dataType:"call" whose to is not a cx… SCORE address → -32602; a SCORE method other than transfer → -32601.

Variant — HSP-20 form-D descriptor​

Send a compact token descriptor instead of a full ICON dataType:"call" envelope. The bridge builds the transfer(_to,_value) SCORE call from { contract, to, amount }, rewrites to to the contract address, and sets value to 0x0. The other envelope fields (from, stepLimit, stepPrice, timestamp, nonce) are handled as in the table below; nid and version are ignored (nid comes from the chainId, version is 0x3). amount is a base-unit string (not rescaled by decimals).

payload.transaction:

{
"token": {
"contract": "cxd8303731bf989a1597a6d38505d7a9483c4d9a38",
"to": "hx0000000000000000000000000000000000000000",
"amount": "1000000",
"decimals": 6,
"symbol": "GODH"
},
"from": "hx0000000000000000000000000000000000000000",
"stepLimit": "0x186a0",
"stepPrice": "0x2e90edd000",
"timestamp": "<current time in µs, hex>",
"nid": "0x100",
"nonce": "0x1",
"version": "0x3"
}
FieldTypeRequiredNotes
token.contractstringRequiredSCORE address; must match cx + 40 hex chars. Unregistered tokens allowed.
token.tostringRequiredRecipient (hx…). Becomes data.params._to.
token.amountstringRequiredBase-unit integer string; not rescaled by decimals. 0 is allowed.
token.decimalsnumberOptional (Required for an unregistered token)Base-unit exponent; lets the bridge resolve an unregistered token (the device receives it for display). An unregistered token without token.decimals is rejected with -32602.
token.symbolstringOptionalDisplay metadata only.
fromstringRequired-RealDevice HAVAH account that signs (hx…). Omitting it fails with -32602.
stepLimit / stepPricestringRequiredMap to the fee gate (gasLimit / fee). Zero or missing fails the gate.
nid / nonce / versionstringOptionalnonce is signed as sent (0x1 if omitted; 0x0 is left out); nid and version are ignored (nid from the chainId, version 0x3).
timestamphexRequired-RealICON microsecond epoch — the wire path never fills it in; omitting it fails with -32602
info

This descriptor is signed as 1.0 GODH — amount "1000000" at decimals 6. Replace from with your own device HAVAH account (hx…) and token.to with the recipient.

Common Mistakes​

danger

Decimal amounts — value / _value are integer loop amounts: 0x-prefixed hex, or a plain decimal string read as base-10. Send them as strings because values exceed MAX_SAFE_INTEGER. Token to vs recipient — for HSP-20, to is the contract (cx…) and the recipient is data.params._to (hx…). Putting the recipient (hx…) in to of a dataType:"call" transaction is rejected with -32602. value on a token transfer — for HSP-20 the native value is ignored (it is not part of the signed call); leave it 0x0. Missing stepLimit — without it the bridge cannot build preparedFee. Missing stepPrice — the app must supply a real, current step price; it is not auto-filled.