Tenzro Testnet is live —request testnet TNZO
← Back to Tutorials

Bridge TNZO via Wormhole

BridgeIntermediate25 min

The Tenzro Wormhole adapter uses the Guardian network — a 19-node set that signs cross-chain VAAs (Verifiable Action Approvals)— to move TNZO and arbitrary messages between 30+ chains. This tutorial walks through chain-ID resolution, VAA parsing, and submitting a real bridge transfer.

What You'll Build

  • Resolve human-readable chain names to Wormhole uint16 IDs
  • Parse a VAA identifier into {emitter_chain, emitter_address, sequence}
  • Initiate a TNZO bridge from Tenzro to Solana
  • Call the flow from the CLI, Rust SDK, and TypeScript SDK

Understanding VAAs

A VAA is a signed payload (chain, emitter, sequence, timestamp, nonce, consistency, payload). A message is considered finalized once at least 13 of 19 Guardians have signed. Destination chains reject VAAs whose signatures don't verify against the current Guardian set.

source chain                  Guardians (off-chain)              destination chain
------------                  -----------------------             -----------------
emit message   ─────────►     observe (finality)
                              sign (13/19 needed)
                              publish VAA
                                            ─────────►  submit VAA
                                                        verify signatures
                                                        mint/unlock tokens

Step 1: Resolve Chain IDs

Wormhole uses uint16 chain IDs that differ from the EVM chainId. Tenzro exposes a translator:

tenzro_wormholeChainId({ "chain": "ethereum" })
  -> { chain: "ethereum", wormhole_chain_id: 2 }

tenzro_wormholeChainId({ "chain": "solana" })
  -> { chain: "solana", wormhole_chain_id: 1 }

tenzro_wormholeChainId({ "chain": "base" })
  -> { chain: "base", wormhole_chain_id: 30 }

Step 2: Parse a VAA Identifier

VAA IDs are canonically written as <chain_id>/<emitter_address>/<sequence>:

tenzro_wormholeParseVaaId({ "vaa_id": "2/0x3ee18b2.../42" })
  -> {
    emitter_chain:   2,
    emitter_address: "0x3ee18b2...",
    sequence:        42
  }

This is what you pass to a Wormhole explorer or an off-chain relayer to look up the signed VAA payload once Guardians have published it.

Step 3: Bridge TNZO

The bridge entry point locks TNZO on Tenzro (via the Wormhole Token Bridge integration) and posts a transfer message to the Core Bridge. Guardians observe, sign a VAA, and a relayer redeems it on the destination chain:

tenzro_wormholeBridge({
  "token":       "TNZO",
  "from_chain":  "tenzro",
  "to_chain":    "solana",
  "amount":      "100.0",
  "sender":      "0xSenderOnTenzro...",
  "recipient":   "SolanaPublicKey..."
})
  -> {
    tx_hash:         "0x...",
    wormhole_message_id: "...",
    estimated_finality_sec: 900
  }

Step 4: CLI

# Resolve a chain ID
tenzro wormhole chain-id --chain ethereum

# Parse a VAA identifier
tenzro wormhole parse-vaa --vaa-id 2/0x3ee18b2.../42

# Bridge TNZO from Tenzro to Solana
tenzro wormhole bridge \
  --token TNZO \
  --from-chain tenzro \
  --to-chain solana \
  --amount 100.0 \
  --recipient SolanaPublicKey...

Step 5: Rust SDK

use tenzro_sdk::TenzroClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = TenzroClient::connect(Default::default()).await?;

    // 1. Resolve Wormhole chain IDs
    let from = client.wormhole().chain_id("tenzro").await?;
    let to   = client.wormhole().chain_id("solana").await?;
    println!("src={} dst={}", from.wormhole_chain_id, to.wormhole_chain_id);

    // 2. Initiate bridge
    let res = client.wormhole().bridge(
        "TNZO", "tenzro", "solana",
        "100.0",
        "0xSenderOnTenzro...",
        "SolanaPublicKey...",
    ).await?;

    println!("source tx: {}", res.tx_hash);
    println!("message:   {}", res.wormhole_message_id);
    println!("wait ~{}s for Guardian finality", res.estimated_finality_sec);

    Ok(())
}

Step 6: TypeScript SDK

import { TenzroClient } from "@tenzro/sdk";

const client = new TenzroClient();

const res = await client.wormhole.bridge({
  token:     "TNZO",
  fromChain: "tenzro",
  toChain:   "base",
  amount:    "25.5",
  sender:    "0xSenderOnTenzro...",
  recipient: "0xRecipientOnBase...",
});

console.log("bridge tx", res.tx_hash);

Trust model. Wormhole assumes an honest majority of its 19 Guardians. If you need a distinct trust domain, the CCT / Chainlink CCIP and LayerZero V2 adapters give you different DON/DVN committees — use them in parallel for independent attestations when risk tolerance is low.

What You Learned

Next Steps