Multi-Chain Account ID

In this document we define the Multi-chain account id. It is a compactly encoded account identifier that is blockchain agnostic.

mcai ::= <multibase_prefix><mcai_code><chain_namespace><id_size><chain_id><address_size><address><parity_byte>

where,

  • multibase_prefix - the prefix which defines which multibase is used to encode the bytes, z for base58btc
  • mcai_code - a number registered on the multicodec table, makes the multi-chain account id upgradable, encoded as varint
  • chain_namespace - see table below, encoded as varint
  • id_size - the length in bytes of the chain_id, encoded as varint
  • chain_id - the chain id, encoding is defined by the chain namespace
  • address_size - the length of the address, encoded as varint
  • address - the address itself, encoding is defined by the chain namespace
  • parity_byte - a checksum byte, see section below

MCAI multicodec

Should be a number registered on the multicodec table. In the examples below we use 0xCA but this is subject to change.

Chain namespaces

Each blockchain namespace needs to be properly defined.

Registry table

Namespace code
bip122 0x00
eip155 0x01
cosmos 0x02
polkadot 0x03
filecoin 0x04

Bip122

Chain ID: Convert from hex to bytes

Address: Convert from base58btc to bytes

Example

In the exammple below we encode 128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6 on bitcoin mainnet.
This means that we use chain_id = 000000000019d6689c085ae165831e93

zEbYEtEFFMZvVzJK3AWU5R5egEj1ep1yMVEWiWJ2FARDz

Eip155

Chain ID: Convert from integer to bytes

Address: Convert from hex to bytes

Example

In the exammple below we encode 0xde30da39c46104798bb5aa3fe8b9e0e1f348163f on ethereum mainnet.
This means that we use chain_id = 1

zUJWDxUnc8pZCfUtVKcAsRgxijaVqHyuMgeKKF

Cosmos

  • TODO

Polkadot

  • TODO

Filecoin

  • TODO

Parity byte

Using the algorithm described on Wikipedia: checksums. XOR each byte word in the mcai, the resulting byte is the parity byte.

Making MCAI human readable

We could easily build tools and UIs that decompose the encoded mcai similar to this: https://cid.ipfs.io/#bagcqcera6wh5laey5njuo2weun46wv4cn2jlbn6qio6mt3bwian4kbp76tdq

Implementation

Below is a PoC implementation in javascript

const varint = require('varint')
const u8a = require('uint8arrays')

const mcai_code = 0xca

const namespaces = {
  bip122: 0x00,
  eip155: 0x01,
  cosmos: 0x02,
  polkadot: 0x03,
  filecoin: 0x04
}

function checksum(bytes) {
  let result = u8a.xor([bytes[0]], [bytes[1]])
  for (let i = 2; i < bytes.length; i++) {
    result = u8a.xor(result, [bytes[i]])
  }
  return result
}

function encodeMCAI(namespace, chain_id, address) {
  const bytes = u8a.concat([
    varint.encode(mcai_code),
    Uint8Array.from([namespace]),
    varint.encode(1), // chain_id below is just one byte
    Uint8Array.from([chain_id]),
    varint.encode(address.length),
    address
  ])
  const checksummedBytes = u8a.concat([bytes, checksum(bytes)])
  return 'z' + u8a.toString(checksummedBytes, 'base58btc')
}

function encodeBtcMainnet() {
  const chain_id = '000000000019d6689c085ae165831e93'
  const chain_id_bytes = u8a.fromString(chain_id, 'base16')
  const address = '128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6'
  const address_bytes = u8a.fromString(address, 'base58btc')
  return encodeMCAI(namespaces['bip122'], chain_id_bytes, address_bytes)
}

function encodeEthMainnet() {
  const chain_id = 0x01
  const address = '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f'
  const address_bytes = u8a.fromString(address.slice(2), 'base16')
  return encodeMCAI(namespaces['eip155'], chain_id, address_bytes)
}

console.log('btc mainnet:', encodeBtcMainnet())
console.log('eth mainnet:', encodeEthMainnet())
Select a repo