Open-source format and libraries · v1.1.0

MHDA MultiChain Hierarchical Deterministic Address

A URN-based descriptor for blockchain HD addresses, compatible with RFC 8141. A single string captures everything needed to identify a derived address: network, derivation scheme, path, signature curve, encoding format, prefix and suffix conventions, and an optional wallet context.

MIT licence Go reference · C++17 port 14 networks Zero dependencies

A Taproot address on Bitcoin, BIP-86 path, bech32m:

urn:mhda:nt:bitcoin:ci:bitcoin:dt:bip86:dp:m/86'/0'/0'/0/0:af:bech32m:ap:bc1p

An EVM address bound to a wallet context:

urn:mhda:nt:evm:ci:1:dt:bip44:dp:m/44'/60'/0'/0/0:wt:web3:wi:5f2a8c31
The format

One string for a derived address

MHDA unifies the encoding of derivation paths, signature curves and address formats across EVM, Bitcoin, Cosmos, Solana, XRP Ledger, Stellar, NEAR, Aptos, Sui, Cardano, Algorand and TON. The chain identity is the (nt, ci) pair; the SLIP-44 coin type is an optional ct annotation, because for HD addresses the coin already lives in the derivation path.

MHDA URN components
KeyComponentRequiredDefault
ntNetwork typeyes–
ciChain IDyes–
ctCoin type (SLIP-44)nonone (metadata)
dtDerivation typenoroot
dpDerivation pathnoempty when dt=root
aaAddress algorithmnoper-network default
afAddress formatnoper-network default
apAddress prefixnonone
asAddress suffixnonone
wtWallet typenonone
wiWallet IDnonone

Canonical order, any input order

The chain identity comes first, so a chain key is always a strict prefix of the URN. Parsers accept any order on input; optional components are emitted only when set.

Exact round-trip

For canonical input Parse(s).String() == s. A short form round-trips to the same short form, a long form to the same long form. Hardening markers ', H and h are all accepted.

Wallet domain

wt names the client or protocol (web3, tonconnect), wi the wallet instance, e.g. a UUID or an HD root key fingerprint. Setters reject :, ?, # and whitespace.

Coverage

Supported networks

Every network registers its algorithms, formats and derivation types. Strict validation requires the resolved combination to be in this matrix; root, the non-HD form, is accepted on every network.

MHDA network compatibility matrix
nt SLIP-44 Algorithms Formats Derivations Defaults
bitcoin0secp256k1p2pkh, p2sh, p2wpkh, p2wsh, p2tr, bech32, bech32mbip32, bip44, bip49, bip84, bip86secp256k1
evm60secp256k1hexbip32, bip44secp256k1, hex
avalanche9000secp256k1hex, bech32bip44secp256k1
tron195secp256k1base58bip44secp256k1, base58
cosmos118secp256k1, ed25519bech32bip44, cip11secp256k1, bech32
solana501ed25519base58slip10ed25519, base58
xrpl144secp256k1, ed25519base58bip44secp256k1, base58
stellar148ed25519strkeyslip10ed25519, strkey
near397ed25519, secp256k1hexslip10, bip44ed25519, hex
aptos637ed25519, secp256k1hexslip10, bip44ed25519, hex
sui784ed25519, secp256k1, secp256r1hexslip10, bip54, bip74ed25519, hex
cardano1815ed25519bech32, base58cip1852ed25519, bech32
algorand283ed25519base32slip10ed25519, base32
ton607ed25519base64url, hexslip10ed25519, base64url

Derivation types: root, bip32, bip44, bip49, bip54, bip74, bip84, bip86, slip10 (variable length), cip11, cip1852, zip32. Each path is checked against the template of its type. Per-network notes are in SPEC §2.

Examples

MHDA URNs by network

Example MHDA URNs
NetworkExample URN
Ethereumurn:mhda:nt:evm:ci:1:dt:bip44:dp:m/44'/60'/0'/0/0
Bitcoin (BIP-86)urn:mhda:nt:bitcoin:ci:bitcoin:dt:bip86:dp:m/86'/0'/0'/0/0:af:bech32m:ap:bc1p
Solanaurn:mhda:nt:solana:ci:mainnet:dt:slip10:dp:m/44'/501'/0'/0'
Stellarurn:mhda:nt:stellar:ci:mainnet:dt:slip10:dp:m/44'/148'/0'
Sui (ed25519)urn:mhda:nt:sui:ci:mainnet:dt:slip10:dp:m/44'/784'/0'/0'/0'
Cardanourn:mhda:nt:cardano:ci:mainnet:dt:cip1852:dp:m/1852'/1815'/0'/0/0
Cosmosurn:mhda:nt:cosmos:ci:cosmoshub:dt:cip11:dp:m/44'/118'/0'/0/0
Algorandurn:mhda:nt:algorand:ci:mainnet non-HD
TONurn:mhda:nt:ton:ci:mainnet non-HD, friendly base64url by default
EVM short formurn:mhda:nt:evm:ci:1 defaults: secp256k1, hex
With metadataurn:mhda:nt:evm:ci:1:ct:60 optional SLIP-44 annotation
Wallet-boundurn:mhda:nt:ton:ci:mainnet:wt:tonconnect:wi:c0a8f2d4-3b6e-4a51-9c7d-2f8e1a0b5c93
Libraries

What the libraries give you

Lenient and strict parsing

Lenient parsing checks structure: prefix, known keys and values, no duplicates, paths that match their type. Strict parsing also checks the (algorithm, format, derivation) combination against the network matrix.

Chain keys

nt:evm:ci:1 is itself a parseable key for maps, caches and hashes. Chain keys are canonical-only: a pre-1.1 key that carries ct fails loudly instead of being reinterpreted.

Content addressing

Hash256 and NSSHash256 give a SHA-256 of the canonical form for deduplication and content addressing. SHA-1 variants are kept only for stable identifiers.

Codecs out of the box

In Go, Address implements TextMarshaler, so JSON, XML and YAML encoding work unchanged; MarshalText writes the canonical URN.

Typed errors

Every failure wraps an exported sentinel: errors.Is(err, mhda.ErrIncompatible) in Go, parse_error::code() in C++. Messages are for people, codes are the contract.

Fuzzed and sanitised

Go fuzz targets for URNs, NSS and paths. The C++ port runs 139 test cases and about 11,000 randomised iterations under AddressSanitizer, UBSan and LeakSanitizer.

Get started

Install and quick start

Go

go get github.com/censync/go-mhda
import (
    "errors"
    "fmt"

    mhda "github.com/censync/go-mhda"
)

func main() {
    // Lenient parsing: structural validation only.
    addr, err := mhda.ParseURN("urn:mhda:nt:evm:ci:1")
    if err != nil {
        panic(err)
    }
    fmt.Println(addr.Chain().NetworkType()) // evm
    fmt.Println(addr.Algorithm())           // secp256k1 (default for evm)
    fmt.Println(addr.Format())              // hex (default for evm)

    // Strict parsing: also checks the (network, algorithm, format,
    // derivation) combination is in the known-good compatibility matrix.
    if _, err := mhda.ParseURNStrict("urn:mhda:nt:evm:ci:1:aa:ed25519"); err != nil {
        if errors.Is(err, mhda.ErrIncompatible) {
            fmt.Println("evm + ed25519 rejected, as expected")
        }
    }

    // The chain identity (nt, ci) is itself a parseable key.
    key := addr.Chain().Key() // "nt:evm:ci:1"
    chain, _ := mhda.ChainFromKey(key)
    _ = chain

    // Optional wallet context: client type + wallet instance id.
    wallet, _ := mhda.ParseURN("urn:mhda:nt:evm:ci:1:wt:web3:wi:5f2a8c31")
    fmt.Println(wallet.WalletType(), wallet.WalletId()) // web3 5f2a8c31
}

Go 1.18+. API reference on pkg.go.dev.

C++17

include(FetchContent)
FetchContent_Declare(mhda
    GIT_REPOSITORY https://github.com/censync/mhda.git
    GIT_TAG        v1.1.0
)
FetchContent_MakeAvailable(mhda)
target_link_libraries(my_app PRIVATE mhda::mhda)
#include <iostream>
#include "mhda/mhda.hpp"

int main() {
    using namespace mhda;

    // Lenient parsing: structural validation only.
    auto addr = parse_urn("urn:mhda:nt:evm:ci:1");
    std::cout << addr.get_chain().network().str() << "\n";  // evm
    std::cout << addr.resolved_algorithm().str() << "\n";   // secp256k1
    std::cout << addr.resolved_format().str() << "\n";      // hex

    // Strict parsing also checks the (network, algorithm, format, derivation)
    // combination is in the known-good compatibility matrix.
    try {
        parse_urn_strict("urn:mhda:nt:evm:ci:1:aa:ed25519");
    } catch (const parse_error& e) {
        if (e.code() == error_code::incompatible) {
            std::cout << "evm + ed25519 rejected, as expected\n";
        }
    }

    // Hashing for content-addressing or deduplication.
    auto bip = parse_urn("urn:mhda:nt:evm:ci:1:dt:bip44:dp:m/44'/60'/0'/0/0");
    std::cout << bip.hash256() << "\n";   // SHA-256 hex
}

C++17, CMake 3.14+, no external runtime dependencies. Also installable with cmake --install and find_package(mhda).

Source code

GitHub repositories and packages

go-mhda is the normative reference: the specification lives there and changes land there first. mhda is a faithful C++17 port that keeps the URN format and validation rules bit-identical, so a URN produced by either implementation parses and round-trips through the other. Both are MIT-licensed; the current release is 1.1.0.

MHDA repositories, packages and install commands
Language GitHub repository Package Install Release
Goreference implementation and specification censync/go-mhda pkg.go.dev github.com/censync/go-mhda go get github.com/censync/go-mhda v1.1.0
C++17port of go-mhda censync/mhda CMake mhda::mhda
GitHub Releases
CMake FetchContent or find_package(mhda) v1.1.0

Documentation

Version 1.1

URN grammar 1.1 (4 July 2026) makes (nt, ci) the chain identity, turns the SLIP-44 coin type into optional metadata, adds the wallet domain and renames network types to the commonly accepted names (bitcoin, tron, solana…). It is a format-changing release: strings produced by 1.0.0 do not round-trip unchanged, and the release notes describe the migration.