Chain Links (Public)
A chain link is a verified association between an Orbinum alias and a wallet address on a foreign blockchain. When you attach a chain link, the runtime verifies on-chain that you control the external wallet — you are not just claiming ownership.
Chain links are public: the address is written to chain storage and visible to anyone.
For private cross-chain wallet registration, see Private links.
Supported signature schemes
The runtime validates external wallet ownership using a configurable signature scheme per chain. Governance registers each chain via add_supported_chain.
| Scheme | Used by | Signature length | Verification |
|---|---|---|---|
Eip191 | Ethereum and EVM-compatible chains | 65 bytes (ECDSA + recovery bit) | EIP-191 prefix + Keccak-256 of AccountId32 |
Ed25519 | Solana and other Ed25519 chains | 64 bytes | Raw Ed25519 signature of AccountId32 |
Note: the Ed25519 variant is raw Ed25519, not the Substrate Ed25519 wrapper. They use the same curve but different message framing.
The message signed by the external wallet is always the SCALE-encoded AccountId32 of the Orbinum account. This proves the external wallet explicitly authorized the link to this specific Orbinum identity.
Support for additional schemes — such as Schnorr/BIP340 for Bitcoin Taproot or raw Secp256k1 for Cosmos — will be added via governance as new SignatureScheme variants are introduced in the runtime.
Why Substrate accounts are excluded
Chain links are exclusively for wallets from external, non-Substrate ecosystems. Adding a chain link to another Substrate parachain account is not supported and is rejected at the pallet level (SubstrateNativeSchemeNotAllowed).
The reason is fundamental: in the Substrate key model, an AccountId32 is a public key. If you control the keypair on Orbinum, you already control the same address on any other Substrate chain — there is nothing cryptographically meaningful to prove. Allowing such links would create a false sense of "cross-chain verification" on something that is simply key identity reuse. Any future SignatureScheme variant for a Substrate-native scheme must implement is_for_external_chain() → false, which causes add_supported_chain to reject it automatically.
Chain ID convention
The chain_id: u32 in the pallet is an internal Orbinum registry identifier assigned by governance — not an automatic standard. However, the following conventions are recommended to avoid conflicts:
| Ecosystem | Convention | Examples |
|---|---|---|
| EVM chains | EIP-155 chain ID | Ethereum = 1, Polygon = 137, BSC = 56, Arbitrum = 42161 |
| Non-EVM chains | SLIP-0044 coin type | Bitcoin = 0, Litecoin = 2, Solana = 501 |
Using EIP-155 IDs for EVM chains is strongly recommended since they are widely known and already used by tooling. For non-EVM chains such as Bitcoin or Litecoin, there is no universal standard — SLIP-0044 (the HD wallet derivation path standard) provides a reasonable canonical source.
Chain ID convention
The chain_id: u32 is split into two namespaces using bit 31 as a discriminant, so EIP-155 and SLIP-0044 IDs never collide:
| Bit 31 | Range | Standard | Purpose |
|---|---|---|---|
0 | 0x00000000 – 0x7FFFFFFF | EIP-155 | EVM-compatible chains |
1 | 0x80000000 – 0xFFFFFFFF | SLIP-0044 | Non-EVM chains (Bitcoin, Solana, etc.) |
Common registered IDs:
| Chain | Standard | chain_id |
|---|---|---|
| Ethereum | EIP-155 | 1 |
| BNB Smart Chain | EIP-155 | 56 |
| Polygon | EIP-155 | 137 |
| Arbitrum One | EIP-155 | 42161 |
| Bitcoin | SLIP-0044 | 0x80000000 |
| Litecoin | SLIP-0044 | 0x80000002 |
| Solana | SLIP-0044 | 0x800001F5 |
| Cosmos | SLIP-0044 | 0x80000076 |
Governance MUST follow this convention when registering new chains. The canonical constants are defined in types::chain in the pallet source.
Adding a chain link
pallet: AccountMapping (index 14)
call: add_chain_link (index 8)
args:
chain_id: u32 — the registered chain identifier
address: Vec<u8> — the raw address bytes on the external chain
signature: Vec<u8> — the signature from the external wallet
origin: Signed (alias owner)
Steps executed on-chain:
- Verifies the caller has an alias (
NoAlias). - Checks the chain is supported (
UnsupportedChain). - Checks no link for
chain_idalready exists (ChainLinkAlreadyExists). - Verifies the signature from
addressoverencode(AccountId32). - Stores the
ChainLink { chain_id, address }in theIdentityRecord. - Stores the reverse index
(chain_id, address) → AccountId32inReverseChainLinks.
On success, emits:
ChainLinkAdded { account, chain_id, address }
Limits
- Maximum 16 chain links per alias (shared with private links).
- Maximum address length: 128 bytes (covers Bitcoin bech32, Solana base58, EVM hex).
Removing a chain link
pallet: AccountMapping (index 14)
call: remove_chain_link (index 9)
args: chain_id: u32
origin: Signed (alias owner)
Removes the link for chain_id from the IdentityRecord and deletes the reverse index entry. No signature required for removal — owning the alias is sufficient.
Emits: ChainLinkRemoved { account, chain_id }.
Governance: managing supported chains
Supported chains are registered by root (governance). This ensures that signature verification logic exists for a chain before any user can add a link to it.
pallet: AccountMapping (index 14)
call: add_supported_chain (index 11)
args: chain_id: u32, scheme: SignatureScheme
origin: Root
call: remove_supported_chain (index 12)
args: chain_id: u32
origin: Root
Removing a supported chain does not retroactively invalidate existing links.
Proxy call dispatch via linked wallet
A verified chain link enables universal proxy signing: an external wallet can authorize and dispatch any Substrate call on behalf of the Orbinum account, without holding ORB for gas.
pallet: AccountMapping (index 14)
call: dispatch_as_linked_account (index 13)
args:
owner: AccountId32 — the Orbinum account that owns the link
chain_id: u32
address: Vec<u8> — the external wallet address
signature: Vec<u8> — signature over the SCALE-encoded inner call
call: RuntimeCall — the call to execute as `owner`
origin: Signed (any relayer)
Execution flow:
- The runtime looks up
(chain_id, address)inReverseChainLinksand verifies it belongs toowner. - Verifies the external wallet's signature over
encode(call). - Dispatches
callwithorigin = Signed(owner). - The relayer pays the transaction fee;
ownerauthorizes the action.
Emits: ProxyCallExecuted { owner, chain_id, address }.
This feature enables Solana wallets (via Phantom) or hardware Ethereum wallets to control an Orbinum identity without ever interacting with the chain directly.
Chain link vs. private link
| Chain link (public) | Private link | |
|---|---|---|
| Address on-chain | Yes, visible | No, only a Poseidon commitment |
| Signature required | Yes, at registration | No, at registration — only at reveal |
| Reversible | Yes, via remove_chain_link | Yes, while not revealed |
| Promoted to public | N/A | Yes, via reveal_private_link |
| Dispatch support | dispatch_as_linked_account | dispatch_as_private_link (ZK proof) |
See Private links for the confidential alternative.