Accounts

## Accounts

When calling the shim’s `post_message` instruction, you need to pass:

- **`bridge`**: Holds the Wormhole core bridge config.
- **`message`**: Represents the PDA derived from the emitter and is reused by the shim instead of generating new accounts.
- **`emitter`**: Serves as the emitter address (signer).
- **`sequence`**: Tracks the emitter's sequence account.
- **`payer`**: Pays compute and any rent needed on first use (signer).
- **`fee_collector`**: Collects the Wormhole message fee.
- **`clock`**: Provides the current Solana time from the sysvar.
- **`system_program`**: Supplies the standard Solana system program for account creation on first use.
- **`wormhole_program`**: Points to the Wormhole core bridge program.
- **`event_authority`**: Acts as the PDA used by the shim to emit log events (Anchor CPI events).
- **`program`**: Specifies the shim program itself.

The struct below defines the accounts required by your instruction and wires the shim to the core bridge, ensuring the emitter PDA can sign the CPI via seeds.

```rs
#[derive(Accounts)]
pub struct PostMessage<'info> {
    #[account(mut)]
    payer: Signer<'info>,

    wormhole_post_message_shim: Program<'info, WormholePostMessageShim>,

    #[account(mut, address = CORE_BRIDGE_CONFIG)]
    /// CHECK: Wormhole bridge config. [`wormhole::post_message`] requires this account be mutable.
    /// Address constraint added for IDL generation / convenience, it will be enforced by the core bridge.
    pub bridge: UncheckedAccount<'info>,

    #[account(mut, seeds = [&emitter.key.to_bytes()], bump, seeds::program = wormhole_post_message_shim::ID)]
    /// CHECK: Wormhole Message. [`wormhole::post_message`] requires this account be signer and mutable.
    /// Seeds constraint added for IDL generation / convenience, it will be enforced by the shim.
    pub message: UncheckedAccount<'info>,

    #[account(seeds = [b"emitter"], bump)]
    /// CHECK: Our emitter
    /// Seeds constraint added for IDL generation / convenience, it will be enforced to match the signer used in the CPI call.
    pub emitter: UncheckedAccount<'info>,

    #[account(mut)]
    /// CHECK: Emitter's sequence account. [`wormhole::post_message`] requires this account be mutable.
    /// Explicitly do not re-derive this account. The core bridge verifies the derivation anyway and
    /// as of Anchor 0.30.1, auto-derivation for other programs' accounts via IDL doesn't work.
    pub sequence: UncheckedAccount<'info>,

    #[account(mut, address = CORE_BRIDGE_FEE_COLLECTOR)]
    /// CHECK: Wormhole fee collector. [`wormhole::post_message`] requires this account be mutable.
    /// Address constraint added for IDL generation / convenience, it will be enforced by the core bridge.
    pub fee_collector: UncheckedAccount<'info>,

    /// Clock sysvar.
    /// Type added for IDL generation / convenience, it will be enforced by the core bridge.
    pub clock: Sysvar<'info, Clock>,

    /// System program.
    /// Type for IDL generation / convenience, it will be enforced by the core bridge.
    pub system_program: Program<'info, System>,

    #[account(address = CORE_BRIDGE_PROGRAM_ID)]
    /// CHECK: Wormhole program.
    /// Address constraint added for IDL generation / convenience, it will be enforced by the shim.
    pub wormhole_program: UncheckedAccount<'info>,

    /// CHECK: Shim event authority
    /// TODO: An address constraint could be included if this address was published to wormhole_solana_consts
    /// Address will be enforced by the shim.
    pub wormhole_post_message_shim_ea: UncheckedAccount<'info>,
}
```

This instruction reuses a single per-emitter message PDA (no per-message rent). When invoked, the shim emits your payload as an Anchor CPI event and, in the same transaction, calls the core bridge with an empty payload, allowing the core bridge to still assign the sequence and enforce fees/finality. Guardians read the Core call (sequence/finality) and the shim event (payload) from the transaction logs, producing a standard VAA without leaving a persistent message account.


Call post_message

## Call post_message

The `post_message` function builds a `CpiContext` and invokes the shim’s `post_message` instruction, forwarding the nonce, finality, and your payload. The Core Bridge enforces fee requirements and assigns the sequence, while the shim emits the payload as an event in the same transaction.

```rs
pub fn post_message(ctx: Context<PostMessage>) -> Result<()> {
    // wormhole::post_message may require that a fee be sent to the fee_collector account of the core bridge.
    // The following code could be used to handle this via CPI call.
    // However, this example handles this complexity on the client side using a `preInstruction`
    //
    // let fee = ctx.accounts.wormhole_bridge.fee();
    // if fee > 0 {
    //     solana_program::program::invoke(
    //         &solana_program::system_instruction::transfer(
    //             &ctx.accounts.payer.key(),
    //             &ctx.accounts.fee_collector.key(),
    //             fee,
    //         ),
    //         &ctx.accounts.to_account_infos(),
    //     )?;
    // }

    wormhole_post_message_shim::cpi::post_message(
        CpiContext::new_with_signer(
            ctx.accounts.wormhole_post_message_shim.to_account_info(),
            wormhole_post_message_shim::cpi::accounts::PostMessage {
                payer: ctx.accounts.payer.to_account_info(),
                bridge: ctx.accounts.bridge.to_account_info(),
                message: ctx.accounts.message.to_account_info(),
                emitter: ctx.accounts.emitter.to_account_info(),
                sequence: ctx.accounts.sequence.to_account_info(),
                fee_collector: ctx.accounts.fee_collector.to_account_info(),
                clock: ctx.accounts.clock.to_account_info(),
                system_program: ctx.accounts.system_program.to_account_info(),
                wormhole_program: ctx.accounts.wormhole_program.to_account_info(),
                program: ctx.accounts.wormhole_post_message_shim.to_account_info(),
                event_authority: ctx.accounts.wormhole_post_message_shim_ea.to_account_info(),
            },
            &[&[b"emitter", &[ctx.bumps.emitter]]],
        ),
        0,
        Finality::Finalized,
        b"your message goes here!".to_vec(),
    )?;

    Ok(())
}
```


Limitations and Considerations

## Limitations and Considerations

- **Rent**: No persistent account rent is paid for every emission; the cost is now dominated by compute and the emission fee.
- **Logs**: Since all observability is log-based, re-observation is only possible while Solana transaction history is available.
- **Parallelization**: Still limited by the `fee_collector` account being mutable.
- **CPI Depth**: The first shim call for an emitter adds one extra stack depth. This is only relevant if you are near the Solana CPI limit (4).


Conclusion

## Conclusion

By using the emission shim, you can dramatically reduce rent costs when emitting Wormhole messages from Solana, while ensuring compatibility with Guardian observation and core bridge sequencing.

For a complete, working reference, see the full example implementation in the Wormhole repo: [`post_message.rs`](https://github.com/wormhole-foundation/wormhole/blob/main/svm/wormhole-core-shims/anchor/programs/wormhole-integrator-example/src/instructions/post_message.rs){target=\_blank}.


Prerequisites

## Prerequisites

To interact with the verification shim, you'll need the following:

- [Rust and Solana CLI installed](https://solana.com/docs/intro/installation){target=\_blank}.
- [Anchor installed](https://www.anchor-lang.com/docs/installation){target=\_blank}.
- The canonical verification shim program already deployed at [`EFaNWErqAtVWufdNb7yofSHHfWFos843DFpu4JBw24at`](https://explorer.solana.com/address/EFaNWErqAtVWufdNb7yofSHHfWFos843DFpu4JBw24at){target=\_blank}.
- The shim’s [IDL](https://github.com/wormhole-foundation/wormhole/blob/main/svm/wormhole-core-shims/anchor/idls/wormhole_verify_vaa_shim.json){target=\_blank} for wiring accounts.
- A payer (signer) funded for compute and temporary account rent (you’ll close and reclaim).


Setup

## Setup

To start, add the verification shim CPI and declare the external program so your instruction can CPI into it. This lets your program verify a VAA digest against the active Guardian set without creating persistent core bridge accounts.

```rs
declare_program!(wormhole_verify_vaa_shim);

use anchor_lang::{
    prelude::*,
    solana_program::{self, keccak},
};
use wormhole_verify_vaa_shim::cpi::accounts::VerifyHash;
use wormhole_verify_vaa_shim::program::WormholeVerifyVaaShim;
```


Accounts

## Accounts

You’ll wire three accounts for verification:

- `guardian_set`: Core bridge `GuardianSet` PDA for the VAA’s `guardianSetIndex` (shim checks derivation).
- `guardian_signatures`: Temporary account created by `post_signatures` (shim checks ownership & discriminator).
- `wormhole_verify_vaa_shim`: The verification shim program.

```rs
#[derive(Accounts)]
pub struct ConsumeVaa<'info> {
    /// CHECK: Guardian set used for signature verification by shim.
    /// Derivation is checked by the shim.
    guardian_set: UncheckedAccount<'info>,

    /// CHECK: Stored guardian signatures to be verified by shim.
    /// Ownership ownership and discriminator is checked by the shim.
    guardian_signatures: UncheckedAccount<'info>,

    wormhole_verify_vaa_shim: Program<'info, WormholeVerifyVaaShim>,
}
```

Here, `guardian_set` is a core bridge PDA, and `guardian_signatures` is created and owned by the verification shim. Derive `guardian_set = PDA(["GuardianSet", index_be_bytes], CORE_BRIDGE_PROGRAM_ID)` using the `guardianSetIndex` from the VAA header (big-endian), compute its bump, and pass that bump into your instruction.


Verify the VAA

## Verify the VAA

The `consume_vaa` function computes the digest, calls the shim’s `verify_hash`, and then proceeds with your logic. This step validates Guardian signatures and quorum against the active Guardian set for the VAA’s `guardianSetIndex`, then lets your program proceed without persisting a `PostedVAA`.

```rs
pub fn consume_vaa(
    ctx: Context<ConsumeVaa>,
    guardian_set_bump: u8,
    vaa_body: Vec<u8>,
) -> Result<()> {
    // Compute the message hash.
    let message_hash = &solana_program::keccak::hashv(&[&vaa_body]).to_bytes();
    let digest = keccak::hash(message_hash.as_slice()).to_bytes();
    // Verify the hash against the signatures.
    wormhole_verify_vaa_shim::cpi::verify_hash(
        CpiContext::new(
            ctx.accounts.wormhole_verify_vaa_shim.to_account_info(),
            VerifyHash {
                guardian_set: ctx.accounts.guardian_set.to_account_info(),
                guardian_signatures: ctx.accounts.guardian_signatures.to_account_info(),
            },
        ),
        guardian_set_bump,
        digest,
    )?;
    // Decode vaa_body, perform security checks, and do your thing.
    Ok(())
}
```


Limitations and Security Considerations

## Limitations and Security Considerations

- You must be the payer and/or account owner to reclaim lamports from the `GuardianSignatures` account.
- The verification proof is not a permanent on-chain record unless you keep the account.
- Compute usage (CU) is higher for the rent-efficient pattern, but the total cost is dramatically lower than keeping permanent accounts.
- All validation guarantees remain as strong as with the legacy method.
- If you do not close accounts you create, rent will be lost as before.
- This approach assumes you do not need to later re-validate the VAA from an on-chain artifact.


Conclusion

## Conclusion

By following this flow, you can efficiently verify VAAs on Solana with minimal rent overhead, leaving no unnecessary state behind on-chain. For a complete, working reference, see the full example implementation in the Wormhole repo: [`consume_vaa.rs`](https://github.com/wormhole-foundation/wormhole/blob/main/svm/wormhole-core-shims/anchor/programs/wormhole-integrator-example/src/instructions/consume_vaa.rs){target=\_blank}.


Query NTT Data and Transfers with Wormholescan

Prerequisites

## Prerequisites

Before you begin, ensure you have the following:

 - [Node.js and npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm){target=\_blank} installed on your machine.
 - [TypeScript](https://www.typescriptlang.org/download/){target=\_blank} installed globally.


Project Setup

## Project Setup

In this section, you will create the directory, initialize a Node.js project, install dependencies, and configure TypeScript.

1. **Create the project**: set up the directory and navigate into it.

    ```bash
    mkdir wormholescan-api-demo
    cd wormholescan-api-demo
    ```

2. **Initialize a Node.js project**: generate a `package.json` file.

    ```bash
    npm init -y
    ```

3. **Set up TypeScript**: create a `tsconfig.json` file.

    ```bash
    touch tsconfig.json
    ```

    Then, add the following configuration:

    ```json title="tsconfig.json"
    {
        "compilerOptions": {
            "target": "ES2022",
            "module": "ESNext",
            "moduleResolution": "node",
            "strict": true,
            "esModuleInterop": true,
            "allowSyntheticDefaultImports": true,
            "skipLibCheck": true,
            "outDir": "./dist",
            "declaration": true,
            "declarationMap": true,
            "sourceMap": true
        },
        "include": ["src/**/*"],
        "exclude": ["node_modules", "dist"]
    }
    ```

4. **Install dependencies**: add the required packages. This guide uses the SDK version `3.x`.

    ```bash
    npm install @wormhole-foundation/sdk axios
    npm install -D tsx typescript @types/node
    ```

     - `@wormhole-foundation/sdk`: utility methods (e.g., chain ID helpers).
     - `axios`: HTTP client for calling the Wormholescan API.
     - `tsx`: runs TypeScript files without compiling them.
     - `typescript`: adds TypeScript support.
     - `@types/node`: provides Node.js type definitions.

5. **Create the project structure**: set up the required directories and files.

    ```bash
    mkdir -p src/helpers src/scripts

    touch \
        src/helpers/api-client.ts \
        src/helpers/utils.ts \
        src/helpers/types.ts \
        src/scripts/fetch-ntt-tokens.ts \
        src/scripts/fetch-operations.ts
    ```

     - `src/helpers/`: contains shared API logic, utilities, and type definitions.
     - `src/scripts/`: contains runnable scripts for fetching token and transfer data.


Create Helper Functions

## Create Helper Functions

Before writing scripts that interact with Wormholescan, we will define a few reusable helper modules. These will handle API calls, extract token and chain data, and provide consistent TypeScript types for working with NTT tokens and transfers.

These helpers will make it easier to write clean, focused scripts later on.


Create a Wormholescan API Client

### Create a Wormholescan API Client

In this step, you will create a lightweight API client to interact with Wormholescan. This helper will enable you to easily fetch NTT tokens and perform token transfer operations using a reusable `get()` method with built-in error handling. The client supports both mainnet and testnet endpoints.

It exposes two core methods:

 - `getNTTTokens()`: fetches the complete list of NTT-enabled tokens.
 - `getTokenTransfers()`: fetches token transfer operations with optional filters (chain, address, pagination, etc.).

Under the hood, both methods use a generic `get(endpoint, params)` wrapper that handles URL construction and error reporting.

Add the following code to `src/helpers/api-client.ts`:

```typescript title="src/helpers/api-client.ts"
import axios, { AxiosResponse } from 'axios';

// WormholeScan API Client -  simple wrapper for making requests to the WormholeScan API
export class WormholeScanAPI {
  private baseURL: string;

  constructor(isTestnet: boolean = false) {
    this.baseURL = isTestnet
      ? 'https://api.testnet.wormholescan.io/api/v1'
      : 'https://api.wormholescan.io/api/v1';
  }

  async get<T>(endpoint: string, params?: Record<string, any>): Promise<T> {
    try {
      const response: AxiosResponse<T> = await axios.get(
        `${this.baseURL}${endpoint}`,
        {
          params,
          headers: {
            'Content-Type': 'application/json',
          },
        }
      );

      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        throw new Error(
          `API request failed: ${error.response?.status} - ${error.response?.statusText}`
        );
      }
      throw new Error(`Unexpected error: ${error}`);
    }
  }

  async getNTTTokens(withLinks: boolean = false) {
    return this.get('/native-token-transfer/token-list', { withLinks });
  }

  async getTokenTransfers(
    params: {
      tokenAddress?: string;
      fromChain?: number;
      toChain?: number;
      page?: number;
      pageSize?: number;
      sortBy?: string;
      sortOrder?: 'asc' | 'desc';
    } = {}
  ) {
    return this.get('/native-token-transfer', params);
  }
}

export const wormholeScanAPI = new WormholeScanAPI();
export const wormholeScanTestnetAPI = new WormholeScanAPI(true);
```


Add Utility Functions

### Add Utility Functions

Next, you will define two utility functions that help interpret NTT tokens and operations from Wormholescan:

 - `getRandomPlatform(token)`: selects a random platform for a given NTT token and returns its address and chain ID.
 - `getOperationStatus(operation)`: interprets the status of a token transfer operation (e.g., In Progress, Emitted, Completed).

These utilities will be used in later scripts to randomly select a chain/token combo and display the transfer status more clearly.

Add the following code to `src/helpers/utils.ts`:

```typescript title="src/helpers/utils.ts"
import { toChainId } from '@wormhole-foundation/sdk';
import { NTTToken, Operation } from './types';

export function getRandomPlatform(
  token: NTTToken
): { platform: string; address: string; chainId: number } | null {
  const platforms = Object.entries(token.platforms);

  if (platforms.length === 0) {
    return null;
  }

  const randomIndex = Math.floor(Math.random() * platforms.length);
  const [platform, address] = platforms[randomIndex];

  try {
    // SDK expects "Ethereum" not "ethereum"
    const capitalizedPlatform =
      platform.charAt(0).toUpperCase() + platform.slice(1);
    const chainId = toChainId(capitalizedPlatform);
    return { platform, address, chainId };
  } catch (error) {
    const platformMapping: Record<string, string> = {
      'arbitrum-one': 'Arbitrum',
      'binance-smart-chain': 'Bsc',
      'polygon-pos': 'Polygon',
      'optimistic-ethereum': 'Optimism',
    };

    const mappedPlatform = platformMapping[platform.toLowerCase()];
    if (mappedPlatform) {
      try {
        const chainId = toChainId(mappedPlatform);
        return { platform, address, chainId };
      } catch (mappedError) {
        console.warn(
          `Could not convert mapped platform ${mappedPlatform} to chain ID`
        );
        return null;
      }
    }

    console.warn(`Could not convert platform ${platform} to chain ID`);
    return null;
  }
}

export function getOperationStatus(operation: Operation): string {
  if (operation.targetChain) {
    return 'Completed';
  } else if (operation.vaa) {
    return 'Emitted';
  } else if (operation.sourceChain) {
    return 'In Progress';
  } else {
    return 'Unknown';
  }
}
```


### Define Types for NTT Tokens and Transfers

Before proceeding, let's define the TypeScript interfaces required for type-safe API responses from Wormholescan. These types will be used throughout the project to validate and work with token metadata and transfer operations.

Add the following content inside `src/helpers/types.ts`:

```typescript title="src/helpers/types.ts"
export interface NTTToken {
  symbol: string;
  coingecko_id: string;
  fully_diluted_valuation: string;
  price: string;
  price_change_percentage_24h: string;
  volume_24h: string;
  total_value_transferred: string;
  total_value_locked: string | null;
  market_cap: string;
  circulating_supply: string;
  image: {
    thumb: string;
    small: string;
    large: string;
  };
  platforms: Record<string, string>;
}

export interface NTTTokenDetail {
  home: {
    blockchain: string;
    externalSalt: string | null;
    isCanonical: boolean;
    lastIndexed: number;
    manager: {
      address: string;
      limits: Array<{
        amount: string;
        baseAmount: string;
        blockchain: string;
        type: string;
        wormholeChainId: number;
      }>;
      owner: {
        address: string | null;
        nttOwner: string | null;
      };
      transceivers: Array<{
        address: string;
        index: number;
        type: string;
      }>;
      version: string;
    };
    mode: string;
    token: {
      address: string;
      decimals: number;
      maxSupply: string;
      minter: string;
      name: string;
      owner: string;
      symbol: string;
      totalSupply: string;
    };
    wormholeChainId: number;
  };
  peers: any[];
}

export interface Operation {
  id: string;
  emitterChain: number;
  emitterAddress: {
    hex: string;
    native: string;
  };
  sequence: string;
  vaa?: {
    raw: string;
    guardianSetIndex: number;
    isDuplicated: boolean;
  };
  content: {
    payload: {
      nttManagerMessage: {
        id: string;
        sender: string;
      };
      nttMessage: {
        additionalPayload: string;
        sourceToken: string;
        to: string;
        toChain: number;
        trimmedAmount: {
          amount: string;
          decimals: number;
        };
      };
      transceiverMessage: {
        prefix: string;
        recipientNttManager: string;
        sourceNttManager: string;
        transceiverPayload: string;
      };
    };
    standarizedProperties: {
      appIds: string[];
      fromChain: number;
      fromAddress: string;
      toChain: number;
      toAddress: string;
      tokenChain: number;
      tokenAddress: string;
      amount: string;
      feeAddress: string;
      feeChain: number;
      fee: string;
      normalizedDecimals: number;
    };
  };
  sourceChain?: {
    chainId: number;
    timestamp: string;
    transaction: {
      txHash: string;
    };
    from: string;
    status: string;
    fee: string;
    gasTokenNotional: string;
    feeUSD: string;
  };
  targetChain?: {
    chainId: number;
    timestamp: string;
    transaction: {
      txHash: string;
    };
    status: string;
    from: string;
    to: string;
    fee: string;
    gasTokenNotional: string;
    feeUSD: string;
  };
}

export interface OperationsResponse {
  operations: Operation[];
}
```


Fetch and Inspect NTT Tokens

## Fetch and Inspect NTT Tokens

In this step, you will create a script that fetches a list of NTT tokens from Wormholescan and inspects their detailed metadata.

The script does the following:

 - Retrieves all available NTT tokens via the API client.
 - Picks the first 5 with platform data.
 - Selects a random platform for each token (e.g., Ethereum, Arbitrum).
 - Fetches and logs metadata for that token on that platform.

You can control how many tokens are processed by modifying the `TOKENS_TO_PROCESS` constant near the top of the script.

Add the following code to `src/scripts/fetch-ntt-tokens.ts`:

```typescript title="src/scripts/fetch-ntt-tokens.ts"
#!/usr/bin/env tsx

import { wormholeScanAPI } from '../helpers/api-client';
import { NTTToken, NTTTokenDetail } from '../helpers/types';
import { getRandomPlatform } from '../helpers/utils';

// Configurable variable - easily adjust the number of tokens to process
const TOKENS_TO_PROCESS = INSERT_NUMBER_OF_TOKENS;

async function fetchNTTTokens() {
  console.log('🔍 Fetching NTT tokens from WormholeScan API...\n');

  try {
    const tokens = (await wormholeScanAPI.getNTTTokens(false)) as NTTToken[];

    // Get detailed information for tokens
    const tokensWithPlatforms = tokens.filter(
      (token) => Object.keys(token.platforms).length > 0
    );
    const tokensToProcess = tokensWithPlatforms.slice(0, TOKENS_TO_PROCESS);

    console.log(
      `🔍 Fetching detailed NTT information for first ${TOKENS_TO_PROCESS} tokens...\n`
    );

    for (const token of tokensToProcess) {
      console.log(`📋 ${token.symbol} (${token.coingecko_id})`);

      // Get a random platform from the token's available platforms
      const platformInfo = getRandomPlatform(token);

      if (!platformInfo) {
        console.error(
          `❌ Could not determine platform info for ${token.symbol}`
        );
        continue;
      }

      const { platform, address, chainId } = platformInfo;

      console.log(`Selected Platform: ${platform} (Chain ID: ${chainId})`);

      try {
        // Fetch detailed NTT information using the correct chain ID
        const detail = (await wormholeScanAPI.get(
          `/ntt/token/${chainId}/${address}`
        )) as NTTTokenDetail;

        console.log(`Token Name: ${detail.home.token.name}`);
        console.log(`Token Symbol: ${detail.home.token.symbol}`);
        console.log(`Mode: ${detail.home.mode}`);
        console.log(`Manager Address: ${detail.home.manager.address}`);
        console.log(`Version: ${detail.home.manager.version}`);
        console.log(
          `Transceiver Address: ${
            detail.home.manager.transceivers[0]?.address || 'N/A'
          }`
        );
      } catch (error) {
        console.log(
          `❌ No NTT configuration found for ${token.symbol} on ${platform}`
        );
      }

      console.log('\n' + '='.repeat(50) + '\n');
    }
  } catch (error) {
    console.error('❌ Error fetching NTT tokens:', error);
    process.exit(1);
  }
}

fetchNTTTokens();
```

Run it with:

```bash
npx tsx src/scripts/fetch-ntt-tokens.ts
```

If successful, the output will be:

	npm run ntt-tokens

	> demo-wormholescan-api@1.0.0 ntt-tokens
	> npx tsx src/scripts/fetch-ntt-tokens.ts

	🔍 Fetching NTT tokens from WormholeScan API...

	🔍 Fetching detailed NTT information for first 5 tokens...

	📋 LINGO (lingo)
	Selected Platform: solana (Chain ID: 1)
	Token Name: Lingo
    Token Symbol: Lingo
    Mode: burning
    Manager Address: nTTQspEC1JoEUJVFTcgZSatgcv4PNT8UYtCtyaUSKcX
    Version: 3.0.0
    Transceiver Address: CnDQ53A3j2EcniJAm7UtuYKmQtovFAmumcuzC648moSE
    ==================================================
    ...


Fetch Transfer Operations

## Fetch Transfer Operations

Now, you will create a script to fetch NTT transfer operations using the Wormholescan API. These operations contain key details such as:

 - Source and target chains.
 - Wallet addresses.
 - Token amount and metadata.
 - VAA and execution status.

You will log the first few operations in a readable format to better understand how transfers are structured.

The script will use a specific emitter address to query transfers. You can easily change which token or manager you are tracking by modifying the `EMITTER_ADDRESS` constant near the top of the file.

Add the following code to `src/scripts/fetch-operations.ts`:

```typescript title="src/scripts/fetch-operations.ts"
#!/usr/bin/env tsx

import { wormholeScanTestnetAPI } from '../helpers/api-client';
import { OperationsResponse } from '../helpers/types';
import { getOperationStatus } from '../helpers/utils';

const EMITTER_ADDRESS = 'INSERT_EMITTER_ADDRESS';
const PAGE_SIZE = 'INSERT_NUMBER_OF_TRANSFERS_TO_FETCH'; // e.g., 10, 20, etc.

async function fetchTokenTransfers() {
  console.log(
    '🔍 Fetching token transfer operations from WormholeScan API...\n'
  );

  try {
    const response = (await wormholeScanTestnetAPI.get(
      `/operations?address=${EMITTER_ADDRESS}&pageSize=${PAGE_SIZE}`
    )) as OperationsResponse;

    console.log(
      `✅ Found ${response.operations.length} operations for emitter ${EMITTER_ADDRESS}\n`
    );

    for (const operation of response.operations) {
      const overallStatus = getOperationStatus(operation);
      const { standarizedProperties } = operation.content;

      console.log(`📋 Status: ${overallStatus}`);
      console.log(
        `🔗 Transfer: Chain ${standarizedProperties.fromChain} → Chain ${standarizedProperties.toChain}`
      );
      console.log(`📍 From: ${standarizedProperties.fromAddress}`);
      console.log(`📍 To: ${standarizedProperties.toAddress}`);

      if (operation.sourceChain) {
        console.log(
          `🟢 Source: ${operation.sourceChain.transaction.txHash} (${operation.sourceChain.status})`
        );
      }

      if (operation.targetChain) {
        console.log(
          `🟢 Target: ${operation.targetChain.transaction.txHash} (${operation.targetChain.status})`
        );
      }

      if (operation.vaa && !operation.targetChain) {
        console.log(`⏳ VAA emitted, awaiting completion`);
      }

      console.log(''); // Empty line between operations
    }
  } catch (error) {
    console.error('❌ Error fetching token transfers:', error);
    process.exit(1);
  }
}

fetchTokenTransfers();
```

Run it with:

```bash
npx tsx src/scripts/fetch-operations.ts
```

If successful, the output will look like this:

	npm run operations

	> demo-wormholescan-api@1.0.0 operations
	> npx tsx src/scripts/fetch-operations.ts

	🔍 Fetching token transfer operations from WormholeScan API...

	✅ Found 5 operations for emitter 0xdF77F921a560F6882e4EC4bbDc2fF37a7A26D4Db

	📋 Status: Completed
	🔗 Transfer: Chain 1 → Chain 10002
	📍 From: wA8eCo4AR7pAgFsAPpU64wYouBY6CUVPLGuMMBu2eaB
    📍 To: 0xdF77F921a560F6882e4EC4bbDc2fF37a7A26D4Db
    🟢 Source: 5axKXqHHq8C4vWqKnrZ3vnmDRTu4D9XPMaJBDrMrxuEawhRS7xnwcAJ5UZ1k9eYgakzz4LXopJdJjyCEZDbH9CEH (confirmed)
    🟢 Target: 0x619da24b77289c20fbbd2564833b49522f2624db5e92194ddea65f18ebb116fc (completed)

    ...


Resources

## Resources

You can explore the complete project and find all necessary scripts and configurations in Wormhole's [demo GitHub repository](https://github.com/wormhole-foundation/demo-wormholescan-api){target=\_blank}.

The repository includes everything covered in this guide, which is helpful for dashboards, bots, or alerting systems built on top of Wormholescan.


Key Features

## Key Features

- **Multichain messaging**: Send arbitrary data between blockchains, enabling xDapps, governance actions, or coordination across ecosystems.
- **Decentralized validation**: A network of independent [Guardians](/docs/protocol/infrastructure/guardians/){target=\_blank} observes and signs multichain messages, producing [Verifiable Action Approvals (VAAs)](/docs/protocol/infrastructure/vaas/){target=\_blank} that ensure integrity.
- **Composable architecture**: Works with smart contracts, token bridges, or decentralized applications, providing a flexible foundation for multichain use cases.


How It Works

## How It Works

The messaging flow consists of several core components:

1. **Source chain (emitter contract)**: A contract emits a message by calling the Wormhole [Core Contract](/docs/protocol/infrastructure/core-contracts/){target=\_blank} on the source chain.
2. **Guardian Network**: [Guardians](/docs/protocol/infrastructure/guardians/){target=\_blank} observe the message, validate it, and generate a signed [VAA](/docs/protocol/infrastructure/vaas/){target=\_blank}.
3. **Relayers (Executor)**: Off-chain or on-chain relayers transport the VAA to the destination chain. In Wormhole’s architecture, this role is fulfilled by the [**Executor**](/docs/products/messaging/concepts/executor-overview/){target=\_blank}, a permissionless, shared framework that standardizes message delivery across all supported chains.
4. **Target chain (recipient contract)**: The [Core Contract](/docs/protocol/infrastructure/core-contracts/){target=\_blank} on the destination chain verifies the VAA and triggers the specified application logic.

![Wormhole architecture detailed diagram: source to target chain communication.](/docs/images/protocol/architecture/architecture-1.webp)


Use Cases

## Use Cases

Wormhole Messaging enables a wide range of multichain applications. Below are common use cases and the Wormhole stack components you can use to build them.

- **Borrowing and Lending Across Chains (e.g., [Folks Finance](https://wormhole.com/case-studies/folks-finance){target=\_blank})**

    - **[Messaging](/docs/products/messaging/get-started/){target=\_blank}**: Coordinate actions across chains.
    - **[Native Token Transfers](/docs/products/token-transfers/native-token-transfers/overview/){target=\_blank}**: Transfer collateral as native assets.

- **Oracle Networks (e.g., [Pyth](https://wormhole.com/case-studies/pyth){target=\_blank})**

    - **[Messaging](/docs/products/messaging/get-started/){target=\_blank}**: Relay verified data.

- **Gas Abstraction**

    - **[Messaging](/docs/products/messaging/get-started/){target=\_blank}**: Coordinate gas logic.
    - **[Native Token Transfers](/docs/products/token-transfers/native-token-transfers/overview/){target=\_blank}**: Handle native token swaps.

- **Bridging Intent Library**

    - **[Messaging](/docs/products/messaging/get-started/){target=\_blank}**: Dispatch and execute intents.
    - **[Settlement](/docs/products/settlement/overview/){target=\_blank}**: Execute user-defined bridging intents.

- **Decentralized Social Platforms (e.g., [Chingari](https://chingari.io/){target=\_blank})**

    - **[Messaging](/docs/products/messaging/get-started/){target=\_blank}**: Facilitate decentralized interactions.
    - **[Wrapped Token Transfers](/docs/products/token-transfers/wrapped-token-transfers/overview/){target=\_blank}**: Enable tokenized rewards.


Next Steps

## Next Steps

Follow these steps to work with Wormhole Messaging:

<div class="grid cards" markdown>

-   :octicons-tools-16:{ .lg .middle } **Get Started with Messaging**

    ---

    Use the core protocol to publish a multichain message and return transaction info with VAA identifiers.

    [:custom-arrow: Get Started](/docs/products/messaging/get-started/)

-   :octicons-book-16:{ .lg .middle } **Executor Overview**

    ---

    Learn how to use Executors to automate message handling and application logic across chains.

    [:custom-arrow: Learn about Executors](/docs/protocol/infrastructure/relayers/executor-framework/)

-   :octicons-tools-16:{ .lg .middle } **Solana Shim Programs**

    ---

    For lower-cost, efficient integration with Core Bridge on Solana, consider using shim programs:

    [:custom-arrow: Learn about Solana Shims](/docs/products/messaging/concepts/solana-shim/)

    [:custom-arrow: Emit Messages with the Emission Shim](/docs/products/messaging/guides/solana-shims/sol-emission/)

    [:custom-arrow: Verify VAAs with the Verification Shim](/docs/products/messaging/guides/solana-shims/sol-verification/)

</div>


Structure Overview

## Structure Overview

The Wormhole Core system consists of a proxy contract and a modular implementation constructed through layered inheritance.

```text
Wormhole.sol (Proxy)
└── Implementation.sol
    └── Governance.sol
        ├── Getters.sol
        ├── GovernanceStructs.sol
        ├── Messages.sol
        ├── Setters.sol
        └── Structs.sol
```

**Key Components:**

 - **Wormhole.sol**: The upgradeable proxy contract that delegates all logic to `Implementation.sol`.
 - **Implementation.sol**: The main logic contract, which handles message publication and initialization. Inherits from Governance.sol.
 - **Governance.sol**: Core governance logic for processing upgrades, setting fees, and updating the Guardian set. Also responsible for verifying governance VAAs and performing privileged actions.
 - **Getters.sol**: Exposes view functions to access internal contract state, such as current Guardian sets, fees, and contract configuration.
 - **GovernanceStructs.sol**: Provides structures and helpers for processing governance-related VAAs.
 - **Messages.sol**: Handles VAA parsing and verification.
 - **Setters.sol**: Contains internal functions for mutating contract state.
 - **Structs.sol**: Defines core data structures like GuardianSet and VM (VAA Message) used across multiple modules.


State Variables

## State Variables

 - **`provider` ++"Structs.Provider"++**: Holds metadata like `chainId`, `governanceChainId`, and `governanceContract`. This is a nested struct.
 - **`guardianSets` ++"mapping(uint32 => GuardianSet)"++**: Mapping of all Guardian sets by index.
 - **`guardianSetIndex` ++"uint32"++**: Index of the currently active Guardian set.
 - **`guardianSetExpiry` ++"uint32"++**: How long a Guardian set remains valid after it's replaced (in seconds).
 - **`sequences` ++"mapping(address => uint64)"++**: Tracks message sequences per emitter (used to enforce message ordering).
 - **`consumedGovernanceActions` ++"mapping(bytes32 => bool)"++**: Used to prevent governance VAAs from being reused (replay protection).
 - **`initializedImplementations` ++"mapping(address => bool)"++**: Tracks which implementation addresses have been initialized (for upgrade safety).
 - **`messageFee` ++"uint256"++**: The amount (in native gas token) required to post a message. Set via governance.
 - **`evmChainId` ++"uint256"++**: The actual EVM chain ID (e.g., 1 for Ethereum, 10 for Optimism). Used in fork recovery.


Events

## Events


LogMessagePublished

### LogMessagePublished

Emitted when a message is published via `publishMessage`. *(Defined in [Implementation.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Implementation.sol){target=\_blank})*

```solidity
event LogMessagePublished(
    address indexed sender,
    uint64 sequence,
    uint32 nonce,
    bytes payload,
    uint8 consistencyLevel
)
```

??? interface "Parameters"

    `sender` ++"address"++

    Address that called `publishMessage`.

    ---

    `sequence` ++"uint64"++

    The sequence number of the message.

    ---

    `nonce` ++"uint32"++

    The provided nonce.

    ---

    `payload` ++"bytes"++

    The payload that was published.

    ---

    `consistencyLevel` ++"uint8"++

    Finality level requested.


ContractUpgraded

### ContractUpgraded

Emitted when the Core Contract is upgraded to a new implementation via governance. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*

```solidity
event ContractUpgraded(
    address indexed oldContract,
    address indexed newContract
)
```

??? interface "Parameters"

    `oldContract` ++"address"++

    The address of the previous implementation.

    ---

    `newContract` ++"address"++

    The address of the new implementation.


GuardianSetAdded

### GuardianSetAdded

Emitted when a new Guardian set is registered via governance. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*

```solidity
event GuardianSetAdded(
    uint32 indexed index
)
```

??? interface "Parameters"

    `index` ++"uint32"++

    Index of the newly added Guardian set.


LogGuardianSetChanged

### LogGuardianSetChanged

Emitted when the active Guardian set is changed. *(Defined in [State.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/State.sol){target=\_blank})*

```solidity
event LogGuardianSetChanged(
    uint32 oldGuardianIndex,
    uint32 newGuardianIndex
)
```

??? interface "Parameters"

    `oldGuardianIndex` ++"uint32"++

    The previous active Guardian set index.

    ---

    `newGuardianIndex` ++"uint32"++

    The new active Guardian set index.


Functions

## Functions


publishMessage

### publishMessage

Publishes a message to Wormhole's Guardian Network. *(Defined in [Implementation.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Implementation.sol){target=\_blank})*

```solidity
function publishMessage(
    uint32 nonce,
    bytes memory payload,
    uint8 consistencyLevel
) public payable returns (uint64 sequence)
```

??? interface "Parameters"

    `nonce` ++"uint32"++

    Custom sequence identifier for the emitter.

    ---

    `payload` ++"bytes"++

    Arbitrary user data to be included in the message.

    ---

    `consistencyLevel` ++"uint8"++

    Finality requirement for Guardian attestation (e.g., safe or finalized).

??? interface "Returns"

    `sequence` ++"uint64"++

    Unique sequence number assigned to this message.


getCurrentGuardianSetIndex

### getCurrentGuardianSetIndex

Returns the index of the currently active Guardian set. *(Defined in [Getters.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Getters.sol){target=\_blank})*

Each VAA includes the index of the Guardian set that signed it. This function allows contracts to retrieve the current index, ensuring the VAA is verified against the correct set.

```solidity
function getCurrentGuardianSetIndex() external view returns (uint32)
```

??? interface "Returns"

    `index` ++"uint32"++

    The index of the active Guardian set used to verify signatures.


getGuardianSet

### getGuardianSet

Retrieves metadata for a given Guardian set index. *(Defined in [Getters.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Getters.sol){target=\_blank})*

```solidity
function getGuardianSet(uint32 index) external view returns (address[] memory keys, uint32 expirationTime)
```

??? interface "Parameters"

    `index` ++"uint32"++

    Guardian set index to query.

??? interface "Returns"

    `keys` ++"address[]"++

    Public keys of the guardians in this set.

    ---

    `expirationTime` ++"uint32"++

    Timestamp after which the Guardian set is considered expired.


getGuardianSetExpiry

### getGuardianSetExpiry

Returns the expiration time of a specific Guardian set index. *(Defined in [Getters.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Getters.sol){target=\_blank})*

```solidity
function getGuardianSetExpiry(uint32 index) external view returns (uint32)
```

??? interface "Parameters"

    `index` ++"uint32"++

    The index of the Guardian set to query.

??? interface "Returns"

    `expiry` ++"uint32"++

    UNIX timestamp after which the set is no longer valid.


messageFee

### messageFee

Returns the current fee (in native tokens) required to publish a message. *(Defined in [Getters.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Getters.sol){target=\_blank})*

```solidity
function messageFee() public view returns (uint256)
```

??? interface "Returns"

    `fee` ++"uint256"++

    Fee in Wei required to publish a message successfully. Must be sent as `msg.value`.


nextSequence

### nextSequence

Retrieves the next sequence number for a given emitter address. *(Defined in [Getters.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Getters.sol){target=\_blank})*

```solidity
function nextSequence(address emitter) external view returns (uint64)
```

??? interface "Parameters"

    `emitter` ++"address"++

    The address for which the next sequence will be issued.

??? interface "Returns"

    `sequence` ++"uint64"++

    The next sequence number for the specified emitter.


parseAndVerifyVM

### parseAndVerifyVM

Verifies signatures and parses a signed VAA. *(Defined in [Messages.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Messages.sol){target=\_blank})*

```solidity
function parseAndVerifyVM(bytes memory encodedVM)
    external
    view
    returns (
        VM memory vm,
        bool valid,
        string memory reason
    )
```

??? interface "Parameters"

    `encodedVM` ++"bytes"++

    Serialized signed VAA from Guardians.

??? interface "Returns"

    `vm` ++"VM memory"++

    Full parsed VAA contents

    ---

    `valid` ++"bool"++

    Whether the VAA is valid according to the current Guardian set.

    ---

    `reason` ++"string"++

    Reason for invalidity if `valid` is false (invalid).


verifyVM

### verifyVM

Performs low-level VAA signature verification. *(Defined in [Messages.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Messages.sol){target=\_blank})*

```solidity
function verifyVM(bytes memory encodedVM)
    public view returns (bool isValid, string memory reason)
```

??? interface "Parameters"

    `encodedVM` ++"bytes"++

    Serialized signed VAA to verify.

??? interface "Returns"

    `isValid` ++"bool"++

    `true` if the signatures are valid and meet the quorum.

    ---

    `reason` ++"string"++

    Explanation for failure if `isValid` is `false`.


verifySignatures

### verifySignatures

Used to verify individual Guardian signatures against a VAA digest. *(Defined in [Messages.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Messages.sol){target=\_blank})*

```solidity
function verifySignatures(
    bytes32 hash,
    Structs.Signature[] memory signatures,
    GuardianSet memory guardianSet
) public view returns (bool)
```

??? interface "Parameters"

    `hash` ++"bytes32"++

    The message digest to verify.

    ---

    `signatures` ++"Structs.Signature[]"++

    An array of Guardian signatures.

    ---

    `guardianSet` ++"GuardianSet memory"++

    Guardian set to validate against.

??? interface "Returns"

    `isValid` ++"bool"++

    `true` if the required number of valid signatures is present.


quorum

### quorum

Returns the number of Guardian signatures required to reach quorum. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*

```solidity
function quorum() public view returns (uint8)
```

??? interface "Returns"

    `quorum` ++"uint8"++

    Number of valid Guardian signatures required to reach consensus for VAA verification.


chainId

### chainId

Returns Wormhole chain ID used internally by the protocol. *(Defined in [Getters.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Getters.sol){target=\_blank})*

```solidity
function chainId() public view returns (uint16)
```

??? interface "Returns"

    `id` ++"uint16"++

    Wormhole-specific chain identifier.


evmChainId

### evmChainId

Returns the EVM chain ID (i.e., value from `block.chainid`). *(Defined in [Getters.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Getters.sol){target=\_blank})*

```solidity
function evmChainId() public view returns (uint256)
```

??? interface "Returns"

    `id` ++"uint256"++

    Native EVM chain ID for the current network.


Errors

## Errors


Invalid Fee

### Invalid Fee

Reverts when the message fee (`msg.value`) sent is not equal to the required fee returned by `messageFee()`. *(Defined in [Implementation.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Implementation.sol){target=\_blank})*


Unsupported

### Unsupported

Reverts on any call to the fallback function. The contract does not support arbitrary calls. *(Defined in [Implementation.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Implementation.sol){target=\_blank})*


### The Wormhole Contract Does Not Accept Assets

Reverts when native tokens (ETH) are sent directly to the contract via the `receive()` function. *(Defined in [Implementation.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Implementation.sol){target=\_blank})*


Already Initialized

### Already Initialized

Reverts when trying to call `initialize()` on an implementation that has already been initialized. *(Defined in [Implementation.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Implementation.sol){target=\_blank}, via `initializer` modifier)*


Unknown Chain ID

### Unknown Chain ID

Reverts inside the `initialize()` function if the chain ID stored by the contract does not match any known Wormhole chain. *(Defined in [Implementation.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Implementation.sol){target=\_blank})*


Invalid Fork

### Invalid Fork

Reverts when attempting to perform a governance action intended only for forked chains on a non-forked chain. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*


Invalid Module

### Invalid Module

Reverts if the VAA’s module field doesn’t match the expected "Core" module. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*


Invalid Chain

### Invalid Chain

Reverts if the VAA’s target chain doesn’t match the chain on which this contract is deployed. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*


New Guardian Set is Empty

### New Guardian Set is Empty

Reverts when trying to register a new Guardian set that has no keys. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*


Index Must Increase in Steps of 1

### Index Must Increase in Steps of 1

Reverts when the new Guardian set index is not exactly one greater than the current. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*


Not a Fork

### Not a Fork

Reverts when trying to recover chain ID on a non-forked chain. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*


Invalid EVM Chain

### Invalid EVM Chain

Reverts if the recovered chain ID doesn't match the current `block.chainid`. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*


Governance Action Already Consumed

### Governance Action Already Consumed

Reverts when the same governance VAA is submitted more than once. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*


Wrong Governance Contract

### Wrong Governance Contract

Reverts when the governance VAA’s emitter address doesn't match the expected governance contract address. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*


Wrong Governance Chain

### Wrong Governance Chain

Reverts when the governance VAA’s emitter chain doesn't match the expected governance chain (Solana). *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*


Not Signed by Current Guardian Set

### Not Signed by Current Guardian Set

Reverts if the Guardian set index in the VAA doesn’t match the current Guardian set. *(Defined in [Governance.sol](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/Governance.sol){target=\_blank})*


Structure Overview

## Structure Overview

The Wormhole Core program on Solana is implemented using modular Rust files. Logic is separated across instruction dispatch, account definitions, core types, and signature verification.

```text
lib.rs
├── instructions.rs
├── accounts.rs
├── api.rs
│   ├── post_message
│   ├── verify_signatures
│   ├── post_vaa
│   ├── upgrade_contract
│   └── upgrade_guardian_set
├── types.rs
└── vaa.rs
```

**Key Components:**

 - **lib.rs**: Program entry point and instruction dispatcher. Registers all handlers and exposes the on-chain processor.
 - **instructions.rs**: Defines the `WormholeInstruction` enum and maps it to individual instruction handlers.
 - **accounts.rs**: Specifies the account constraints and validation logic for each instruction.
 - **api.rs**: Contains the main logic for processing instructions such as message posting, VAA verification, upgrades, and governance actions.
 - **types.rs**: Defines shared structs and enums used throughout the program, including configuration and `GuardianSet` formats.
 - **vaa.rs**: Implements VAA parsing, hashing, and signature-related logic used to verify Wormhole messages.
 - **error.rs** (not listed above): Defines custom error types used across the program for precise failure handling.
 - **wasm.rs** (not listed above): Provides WebAssembly bindings for testing and external tooling; not used on-chain.


State Accounts

## State Accounts

Below are on-chain PDAs used to store persistent state for the core contract. All are derived using deterministic seeds with the program ID.

 - **`bridge` ++"BridgeData"++**: Stores global config like the active Guardian set index, message fee, and Guardian set expiration time. (Derived at PDA seed `["Bridge"]`)
 - **`guardianSets` ++"GuardianSetData"++**: Mapping of Guardian sets by index. Each Guardian set includes public key hashes and creation/expiration times. (Derived at PDA seed `["GuardianSet", index]`)
 - **`sequences` ++"SequenceTracker"++**: Tracks the last sequence number used by each emitter, enforcing strict message ordering. (Derived at PDA seed `["Sequence", emitter]`)
 - **`postedVAAs` ++"PostedVAAData"++**: Stores verified and finalized VAAs, preventing replay. (Derived at PDA seed `["PostedVAA", hash]`)
 - **`claims` ++"ClaimData"++**: Tracks consumed governance VAAs to ensure replay protection. (Derived at PDA seed `["Claim", emitter, sequence]`)
 - **`feeCollector` ++"FeeCollector"++**: Holds lamports collected via message fees, and can be drained via governance. (Derived at PDA seed `["fee_collector"]`)


Instructions

## Instructions


initialize

### initialize

Initializes the Wormhole Core contract on Solana with a Guardian set and fee configuration. This should be called only once at deployment time. *(Defined in [api/initialize.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/api/initialize.rs){target=\_blank})*

```rust
initialize(
    payer: Pubkey,
    fee: u64,
    guardian_set_expiration_time: u32,
    initial_guardians: &[[u8; 20]]
)
```

??? interface "Accounts"

    - **`Bridge`**: PDA to store global configuration.
    - **`GuardianSet`**: PDA for Guardian set at index 0.
    - **`FeeCollector`**: PDA to collect message posting fees.
    - **`Payer`**: Funds account creation.
    - **`Clock`, `Rent`, `SystemProgram`**: Solana system accounts.

??? interface "Parameters"

    `fee` ++"u64"++

    Fee in lamports required to post messages.

    ---

    `guardian_set_expiration_time` ++"u32"++

    Time in seconds after which the Guardian set expires.

    ---

    `initial_guardians` ++"[[u8; 20]]"++

    List of Guardian public key hashes (Ethereum-style addresses).


post_message

### post_message

Posts a Wormhole message to the Solana Core contract. *(Defined in [api/post_message.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/api/post_message.rs){target=\_blank})*

```rust
PostMessage {
    nonce: u32,
    payload: Vec<u8>,
    consistency_level: u8
}
```

??? interface "Accounts"

    - **`Bridge`**: PDA for global config.
    - **`Message`**: PDA where the posted message will be stored.
    - **`Emitter`**: The emitting account (must sign).
    - **`Sequence`**: PDA tracking the emitter’s message sequence.
    - **`Payer`**: Pays for account creation and fees.
    - **`FeeCollector`**: PDA that collects message fees.
    - **`Clock`, `Rent`, `SystemProgram`**: Solana system accounts.

??? interface "Parameters"

    `nonce` ++"u32"++

    Unique nonce to disambiguate messages with the same payload.

    ---

    `payload` ++"Vec<u8>"++

    The arbitrary message payload to be posted.

    ---

    `consistency_level` ++"u8"++

    Level of finality required before the message is processed.

    `1` = Confirmed, `32` = Finalized.


post_message_unreliable

### post_message_unreliable

Posts a Wormhole message without requiring reliable delivery. Used for lightweight publishing when finality isn't critical. *(Defined in [api/post_message.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/api/post_message.rs){target=\_blank})*

```rust
PostMessageUnreliable {
    nonce: u32,
    payload: Vec<u8>,
    consistency_level: u8
}
```

??? interface "Accounts"

    - **`Bridge`**: PDA for global config.
    - **`Message`**: PDA where the posted message will be stored.
    - **`Emitter`**: The emitting account (must sign).
    - **`Sequence`**: PDA tracking the emitter’s message sequence.
    - **`Payer`**: Pays for account creation and fees.
    - **`FeeCollector`**: PDA that collects message fees.
    - **`Clock`, `Rent`, `SystemProgram`**: Solana system accounts.

??? interface "Parameters"

    `nonce` ++"u32"++

    Unique nonce to disambiguate messages with the same payload.

    ---

    `payload` ++"Vec<u8>"++

    The arbitrary message payload to be posted.

    ---

    `consistency_level` ++"u8"++

    Level of finality required before the message is processed. `1` = Confirmed, `32` = Finalized.


verify_signatures

### verify_signatures

Verifies Guardian signatures over a VAA body hash. This is the first step in VAA processing and is required before posting the VAA. *(Defined in [api/verify_signature.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/api/verify_signature.rs){target=\_blank})*

```rust
VerifySignatures {
    signers: [i8; 19]
}
```

??? interface "Accounts"

    - **`Payer`**: Pays for account creation and fees.
    - **`GuardianSet`**: PDA holding the current Guardian set.
    - **`SignatureSet`**: PDA that will store the verified signature data.
    - **`InstructionsSysvar`**: Required to access prior instructions (e.g., secp256k1 sigverify).
    - **`Rent`, `SystemProgram`**: Solana system accounts.

??? interface "Parameters"

    `signers` ++"[i8; 19]"++

    A mapping from Guardian index to its position in the instruction payload (or -1 if not present).

    Used to correlate secp256k1 verify instructions with Guardian set entries.


post_vaa

### post_vaa

Finalizes a VAA after signature verification. This stores the message on-chain and marks it as consumed. *(Defined in [api/post_vaa.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/api/post_vaa.rs){target=\_blank})*

```rust
PostVAA {
    version: u8,
    guardian_set_index: u32,
    timestamp: u32,
    nonce: u32,
    emitter_chain: u16,
    emitter_address: [u8; 32],
    sequence: u64,
    consistency_level: u8,
    payload: Vec<u8>
}
```

??? interface "Accounts"

    - **`GuardianSet`**: PDA of the Guardian set used to verify the VAA.
    - **`Bridge`**: Global Wormhole state.
    - **`SignatureSet`**: Verified signature PDA (from verify_signatures).
    - **`PostedVAA`**: PDA where the VAA will be stored.
    - **`Payer`**: Funds the account creation.
    - **`Clock`, `Rent`, `SystemProgram`**: Solana system accounts.

??? interface "Parameters"

    `version` ++"u8"++

    VAA protocol version.

    ---

    `guardian_set_index` ++"u32"++

    Index of the Guardian Set that signed this VAA.

    ---

    `timestamp` ++"u32"++

    The time the emitter submitted the message.

    ---

    `nonce` ++"u32"++

    Unique identifier for the message.

    ---

    `emitter_chain` ++"u16"++

    ID of the chain where the message originated.

    ---

    `emitter_address` ++"[u8; 32]"++

    Address of the contract or account that emitted the message.

    ---

    `sequence` ++"u64"++

    Monotonically increasing sequence number for the emitter.

    ---

    `consistency_level` ++"u8"++

    Required confirmation level before the message is accepted.

    `1` = Confirmed, `32` = Finalized.

    ---

    `payload` ++"Vec<u8>"++

    Arbitrary data being transferred in the message.


set_fees

### set_fees

Updates the message posting fee for the core bridge contract. *(Defined in [api/governance.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/api/governance.rs){target=\_blank})*

```rust
SetFees {}
```

This function is called via governance and requires a valid governance VAA. The VAA payload must contain the new fee value.

??? interface "Accounts"

    - **`Payer`**: Funds transaction execution.
    - **`Bridge`**: PDA storing global Wormhole state.
    - **`Message`**: The PostedVAA account containing the governance message.
    - **`Claim`**: PDA that ensures this governance message hasn't been processed already.
    - **`SystemProgram`**: Required by Solana for creating/initializing accounts.


transfer_fees

### transfer_fees

Transfers the accumulated message posting fees from the contract to a specified recipient. *(Defined in [api/governance.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/api/governance.rs){target=\_blank})*

```rust
TransferFees {}
```

This function is triggered via a governance VAA and transfers the fee balance from the `FeeCollector` to the recipient address specified in the VAA payload.

??? interface "Accounts"

    - **`Payer`**: Funds transaction execution.
    - **`Bridge`**: PDA storing global Wormhole state.
    - **`Message`**: PostedVAA account containing the governance message.
    - **`FeeCollector`**: PDA holding the accumulated fees.
    - **`Recipient`**: The account that will receive the fees.
    - **`Claim`**: PDA that ensures this governance message hasn't been processed already.
    - **`Rent`, `SystemProgram`**: Standard Solana system accounts.


upgrade_contract

### upgrade_contract

Upgrades the deployed Wormhole program using a governance VAA. *(Defined in [api/governance.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/api/governance.rs){target=\_blank})*

```rust
UpgradeContract {}
```

This instruction allows authorized governance messages to trigger an upgrade of the on-chain Wormhole program logic to a new address.

??? interface "Accounts"

    - **`Payer`**: Funds transaction execution.
    - **`Bridge`**: PDA storing global Wormhole state.
    - **`Message`**: PostedVAA account containing the governance message.
    - **`Claim`**: PDA that ensures this governance message hasn't been processed already.
    - **`UpgradeAuthority`**: PDA with authority to perform the upgrade (seeded with "upgrade").
    - **`Spill`**: Account that receives remaining funds from the upgrade buffer.
    - **`NewContract`**: Account holding the new program data.
    - **`ProgramData`**: Metadata account for the upgradable program.
    - **`Program`**: Current program to be upgraded.
    - **`Rent`, `Clock`**: System accounts used during the upgrade process.
    - **`BPFLoaderUpgradeable`**: Solana system program for upgrades.
    - **`SystemProgram`**: Required by Solana for creating/initializing accounts.


upgrade_guardian_set

### upgrade_guardian_set

Upgrades the current Guardian set using a governance VAA. *(Defined in [api/governance.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/api/governance.rs){target=\_blank})*

```rust
UpgradeGuardianSet {}
```

This instruction replaces the active Guardian set with a new one, allowing the Wormhole network to rotate its validator keys securely through governance.

??? interface "Accounts"

    - **`Payer`**: Funds transaction execution.
    - **`Bridge`**: PDA storing global Wormhole state.
    - **`Message`**: PostedVAA account containing the governance message.
    - **`Claim`**: PDA that ensures this governance message hasn't been processed already.
    - **`GuardianSetOld`**: Current (active) Guardian set PDA.
    - **`GuardianSetNew`**: PDA for the newly proposed Guardian set.
    - **`SystemProgram`**: Standard Solana system accounts.


Errors

## Errors


GuardianSetMismatch

### GuardianSetMismatch

The Guardian set index does not match the expected value. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InstructionAtWrongIndex

### InstructionAtWrongIndex

The instruction was found at the wrong index. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InsufficientFees

### InsufficientFees

Insufficient fees were provided to post the message. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InvalidFeeRecipient

### InvalidFeeRecipient

The recipient address does not match the one specified in the governance VAA. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InvalidGovernanceAction

### InvalidGovernanceAction

The action specified in the governance payload is invalid. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InvalidGovernanceChain

### InvalidGovernanceChain

The governance VAA was not emitted by a valid governance chain. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InvalidGovernanceKey

### InvalidGovernanceKey

The emitter address in the governance VAA is not the expected governance key. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InvalidGovernanceModule

### InvalidGovernanceModule

The module string in the governance VAA header is invalid. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InvalidGovernanceWithdrawal

### InvalidGovernanceWithdrawal

Fee withdrawal would cause the fee collector account to drop below rent-exempt balance. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InvalidGuardianSetUpgrade

### InvalidGuardianSetUpgrade

The Guardian set upgrade VAA is invalid (e.g., skipped index or mismatched current index). *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InvalidHash

### InvalidHash

The hash computed from the VAA does not match the expected result. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InvalidSecpInstruction

### InvalidSecpInstruction

The SECP256k1 instruction used for signature verification is malformed. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


MathOverflow

### MathOverflow

An arithmetic overflow occurred during computation. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


PostVAAConsensusFailed

### PostVAAConsensusFailed

Not enough valid signatures were collected to achieve quorum. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


PostVAAGuardianSetExpired

### PostVAAGuardianSetExpired

The Guardian set used to verify the VAA has already expired. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


TooManyGuardians

### TooManyGuardians

The Guardian set exceeds the maximum allowed number of guardians. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


VAAAlreadyExecuted

### VAAAlreadyExecuted

The VAA has already been executed and cannot be processed again. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


VAAInvalid

### VAAInvalid

The VAA is structurally invalid or fails to decode. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


InvalidPayloadLength

### InvalidPayloadLength

The payload length is incorrect or malformed. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


EmitterChanged

### EmitterChanged

The emitter address changed unexpectedly. *(Defined in [error.rs](https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/error.rs){target=\_blank})*


Executor

## Executor

=== "Mainnet"

Chain Name	Contract Address

Ethereum	0x84EEe8dBa37C36947397E1E11251cA9A06Fc6F8a

Solana	execXUrAsMnqMmTHj5m7N1YQgsDz3cwGLYCYyuDRciV

0G (Zero Gravity)	0xcD2BffD0C0289e8C0cb89c458dB1B74f1892Fa6c

Aptos	0x11aa75c059e1a7855be66b931bf340a2e0973274ac16b5f519c02ceafaf08a18

Arbitrum	0x3980f8318fc03d79033Bbb421A622CDF8d2Eeab4

Avalanche	0x4661F0E629E4ba8D04Ee90080Aee079740B00381

Base	0x9E1936E91A4a5AE5A5F75fFc472D6cb8e93597ea

Berachain	0x0Dd7a5a32311b8D87A615Cc7f079B632D3d5e2D3

BNB Smart Chain	0xeC8cCCD058DbF28e5D002869Aa9aFa3992bf4ee0

Celo	0xe6Ea5087c6860B94Cf098a403506262D8F28cF05

CreditCoin	0xd2e420188f17607Aa6344ee19c3e76Cf86CA7BDe

Fogo	execXUrAsMnqMmTHj5m7N1YQgsDz3cwGLYCYyuDRciV

HyperEVM :material-alert:{ title='⚠ The HyperEVM integration is experimental, as its node software is not open source. Use Wormhole messaging on HyperEVM with caution.' }	0xd7717899cc4381033Bc200431286D0AC14265F78

Ink	0x3e44a5F45cbD400acBEF534F51e616043B211Ddd

Linea	0x23aF2B5296122544A9A7861da43405D5B15a9bD3

MegaETH	0xD405E0A1f3f9edc25Ea32d0B079d6118328b2EcB

Mezo	0x0f9b8E144Cc5C5e7C0073829Afd30F26A50c5606

Moca	0x7b8097af5459846c5A72fCc960D94F31C05915aD

Monad	0xC04dE634982cAdF2A677310b73630B7Ac56A3f65

Moonbeam	0x85D06449C78064c2E02d787e9DC71716786F8D19

Optimism	0x85B704501f6AE718205C0636260768C4e72ac3e7

Polygon	0x0B23efA164aB3eD08e9a39AC7aD930Ff4F5A5e81

Scroll	0xcFAdDE24640e395F5A71456A825D0D7C3741F075

SeiEVM	0x25f1c923fb7a5aefa5f0a2b419fc70f2368e66e5

Sonic	0x3Fdc36b4260Da38fBDba1125cCBD33DD0AC74812

Sui	0xdb0fe8bb1e2b5be628adbea0636063325073e1070ee11e4281457dfd7f158235

Unichain	0x764dD868eAdD27ce57BCB801E4ca4a193d231Aed

World Chain	0x8689b4E6226AdC8fa8FF80aCc3a60AcE31e8804B

XRPL-EVM	0x8345E90Dcd92f5Cf2FAb0C8E2A56A5bc2c30d896

=== "Testnet"

Chain Name	Contract Address

Ethereum Sepolia	0xD0fb39f5a3361F21457653cB70F9D0C9bD86B66B

Solana	execXUrAsMnqMmTHj5m7N1YQgsDz3cwGLYCYyuDRciV

0G (Zero Gravity)	0x7C43825EeB76DF7aAf3e1D2e8f684d4876F0CC05

Aptos	0x139717c339f08af674be77143507a905aa28cbc67a0e53e7095c07b630d73815

Arbitrum Sepolia	0xBF161de6B819c8af8f2230Bcd99a9B3592f6F87b

Avalanche	0x4661F0E629E4ba8D04Ee90080Aee079740B00381

Base Sepolia	0x51B47D493CBA7aB97e3F8F163D6Ce07592CE4482

BNB Smart Chain	0xeC8cCCD058DbF28e5D002869Aa9aFa3992bf4ee0

Converge	0xAab9935349B9c08e0e970720F6D640d5B91C293E

Fogo	execXUrAsMnqMmTHj5m7N1YQgsDz3cwGLYCYyuDRciV

Linea	0x4f6c3a93a80DdC691312974DAAbf9B6e4Bb44111

Mezo	0x0f9b8E144Cc5C5e7C0073829Afd30F26A50c5606

Moca	0xc4a03f2c47caA4b961101bAD6338DEf37376F052

Monad Testnet	0xe37D3E162B4B1F17131E4e0e6122DbA31243382f

Optimism Sepolia	0x5856651eB82aeb6979B4954317194d48e1891b3c

Plume	0x8fc2FbA8F962fbE89a9B02f03557a011c335A455

Polygon Amoy	0x7056721C33De437f0997F67BC87521cA86b721d3

SeiEVM	0x25f1c923Fb7A5aEFA5F0A2b419fC70f2368e66e5

Sui	0x4000cfe2955d8355b3d3cf186f854fea9f787a457257056926fde1ec977670eb

Unichain	0x764dD868eAdD27ce57BCB801E4ca4a193d231Aed

XRPL-EVM	0x4d9525D94D275dEB495b7C8840b154Ae04cfaC2A


CCTP with Executor

## CCTP with Executor

=== "Mainnet v1"

Chain Name	Contract Address

Ethereum	0x6DDE92942DbB24F7c9B75765b74a33446980C1e3

Solana	CXGRA5SCc8jxDbaQPZrmmZNu2JV34DP7gFW4m31uC1zs

Aptos	0xc89bf3746dfc70bb3f2de7c35a98f327a40b9d55a443743a5935c5e3de90b7ac

Arbitrum	0x772373214238F09a494828A5323574E3d7e27558

Avalanche	0x58aC806cd205083E7E048E196f36Ff6C4Ae17bE5

Base	0x4D1Cc8921e297155044C01761f581fa52a24C33d

Optimism	0x6826c075973a4393CEf0e131c4B16869426563a7

Polygon	0x7e6Ae241101B355447A4B471D0C6968b132eC4Ab

Sui	N/A*

Unichain	0xa997Ef229E4D2a1fEca249eB41fBf5D4b2217d6E

=== "Testnet v1"

Chain Name	Contract Address

Ethereum Sepolia	0x2fcc7b2332d924764f17f1cf5eda1cd4b36751a2

Solana	CXGRA5SCc8jxDbaQPZrmmZNu2JV34DP7gFW4m31uC1zs

Aptos	0x14a12d1fd6ef371b70c2113155534ec152ec7f779e281b54866c796c9a4a58d3

Arbitrum Sepolia	0x8158305d331594f3e8d18c33ca4e6d3cdc109b75

Avalanche	0x62819ab61cc7fcc864af7bcfc92e6c1965eb69a6

Base Sepolia	0x96846c31e4f87c0f186a322926c61d4183439f0a

Optimism Sepolia	0xe17de8e29f1f0941b541b053829af74ac81c89a6

Polygon	0xdce63172e9ad15243c97acafd01cc4fdda98bead

Unichain Sepolia	0x2c1354296a11029056e0d7d7abbdd58743dbaf59

=== "Mainnet v2"

Chain Name	Contract Address

Ethereum	0xDD68aBa3E04CB1a05082402B9325753314803005

Solana	Supported

Arbitrum	0x760feC4425B46E3D8FEf8E2CE49786e5a6f74446

Avalanche	0xE42aE9e352157fcEf74E971F2C5c74A5963a71D7

Base	0x52892976559fB2fc8b7f850440eD9AA5Dc26f7D9

Codex	0x0684286448140F75d4C3D2F2c02BF66f98CdEA51

HyperEVM :material-alert:{ title='⚠ The HyperEVM integration is experimental, as its node software is not open source. Use Wormhole messaging on HyperEVM with caution.' }	0x001319beBA062d918d7007E4D2D76a0A9cc439Db

Ink	0xef0B43b49315A4aDF11bA2617Be81a304c5D6ecc

Linea	0x257dBB6AD7C7AC19360bEe1A107ebE631D568776

Monad	0x1FdCCf65318b34CFd3F5903fFb747C17e76330ac

Optimism	0x9b51579e67D4ab18D79609105509ad37B2a0D342

Plume	0x9be9C6B420eAfaaC1162D680fd7E61446b38Cf29

Polygon	0x5116F1358ae2445f571AA702dA1feB5e13094E59

SeiEVM	0xe067C0D378C50CDc34bCd973F202736D5A19e5D2

Sonic	0x8A850b2077F1eFccA89eAa9c35b45C4dC9227cdb

Sui	N/A*

Unichain	0xaf7f4FbB6C220baf57ABC7babF81D47Fd628bdb4

World Chain	0xAA4841e5d9652593852403E3ce9e8003f8D579D0

=== "Testnet v2"

Chain Name	Contract Address

Ethereum Sepolia	0xc58475c97ebde9cf4fefa0d4fb2774df81905d43

Solana	Supported

Arbitrum Sepolia	0xf601f9988d62943cb842baae1e46be9b17d0b2a4

Avalanche	0x10018394905f70daa1d740040d64cbed5a82301e

Base Sepolia	0x1effdcfedc6d45e44b3133257debfb522adb1cae

Ink	0x63993ee08bda32ecb0ba5cdc751b404f5c5c0458

Linea Sepolia	0xe8Ad216e23fc9425E65aB315F0EC13737e75afEF

Monad Testnet	0x358bAd031d7A217ebA2d471cad5fD611AeC2aF17

Optimism Sepolia	0x49f386393c26439b74e62f5794062925dfb7c1db

Polygon	0x05da7c69db265b37b4d3530d476ec4b33bd9dd45

SeiEVM	0xDC735908C3eCF29f40D8CA5f6407F2d94d316a9F

Unichain Sepolia	0xf082af7668f000f60bc519b378f6363708fc302b


NTT with Executor

## NTT with Executor

=== "Mainnet"

Chain Name	Contract Address

Ethereum	0xD2D9c936165a85F27a5a7e07aFb974D022B89463
Multi Ntt: 0x03dB430D830601DB368991eE55DAa9A708df7912

Solana	nex1gkSWtRBheEJuQZMqHhbMG5A45qPU76KqnCZNVHR

0G	0xe175a8b838f3cdb2e1aaf4ff74c05cf2f3aea9a8

Arbitrum	0x0Af42A597b0C201D4dcf450DcD0c06d55ddC1C77

Avalanche	0x4e9Af03fbf1aa2b79A2D4babD3e22e09f18Bb8EE

Base	0x83216747fC21b86173D800E2960c0D5395de0F30

Berachain	0x0a2AF374Cc9CCCbB0Acc4E34B20b9d02a0f08c30

BNB Smart Chain	0x39B57Dd9908F8be02CfeE283b67eA1303Bc29fe1

Celo	0x3d69869fcB9e1CD1F4020b637fb8256030BAc8fC

CreditCoin	0x5454b995719626256C96fb57454b044ffb3Da2F9

Fogo	nex1gkSWtRBheEJuQZMqHhbMG5A45qPU76KqnCZNVHR

HyperEVM :material-alert:{ title='⚠ The HyperEVM integration is experimental, as its node software is not open source. Use Wormhole messaging on HyperEVM with caution.' }	0x431017B1718b86898C7590fFcCC380DEf0456393

Ink	0x420370DC2ECC4D44b47514B7859fd11809BbeFF5

Linea	0xEAa5AddB5b8939Eb73F7faF46e193EefECaF13E9

MegaETH	0x3EFEc0c7Ee79135330DD03e995872f84b1AD49b6

Mezo	0x484b5593BbB90383f94FB299470F09427cf6cfE2

Moca	0xE612837749a0690BA2BCe490D6eFb5F8Fc347df3

Monad	0xc3F3dDa544815a440633176c7598f5B97500793e
Multi Ntt: 0xFEA937F7124E19124671f1685671d3f04a9Af4E4

Moonbeam	0x1365593C8bae71a55e48E105a2Bb76d5928c7DE3

Optimism	0x85C0129bE5226C9F0Cf4e419D2fefc1c3FCa25cF

Plume	0x6Eb53371f646788De6B4D0225a4Ed1d9267188AD

Polygon	0x6762157b73941e36cEd0AEf54614DdE545d0F990

Scroll	0x055625d48968f99409244E8c3e03FbE73B235a62

SeiEVM	0x3F2D6441C7a59Dfe80f8e14142F9E28F6D440445

Sonic	0xaCa00703bb87F31D6F9fCcc963548b48FA46DfeB

Sui	N/A*

Unichain	0x607723D6353Dae3ef62B7B277Cfabd0F4bc6CB4C

World Chain	0x66b1644400D51e104272337226De3EF1A820eC79

XRPL-EVM	0x6bBd1ff3bB303F88835A714EE3241bF45DE26d29

=== "Testnet"

Chain Name	Contract Address

Ethereum Sepolia	0x54DD7080aE169DD923fE56d0C4f814a0a17B8f41

Solana	nex1gkSWtRBheEJuQZMqHhbMG5A45qPU76KqnCZNVHR

0G Galileo	0xA8CA118f4C8d44Ab651Dad52B5E1a212e5d5c55b

Arbitrum Sepolia	0xd048170F1ECB8D47E499D3459aC379DA023E2C1B

Avalanche	0x4e9Af03fbf1aa2b79A2D4babD3e22e09f18Bb8EE

Base Sepolia	0x5845E08d890E21687F7Ebf7CbAbD360cD91c6245

BNB Smart Chain	0x39B57Dd9908F8be02CfeE283b67eA1303Bc29fe1

Celo	0x3d69869fcB9e1CD1F4020b637fb8256030BAc8fC

Converge	0x3d8c26b67BDf630FBB44F09266aFA735F1129197

Fogo	nex1gkSWtRBheEJuQZMqHhbMG5A45qPU76KqnCZNVHR

Ink	0xF420BFFf922D11c2bBF587C9dF71b83651fAf8Bc

Linea Sepolia	0xaA469cb84C91D5a63bf4B370dE35f0831F2CE4FF

Mezo	0x484b5593BbB90383f94FB299470F09427cf6cfE2

Moca	0x47f26bF9253Eb398fBAf825D7565FE975D839a71

Monad Testnet	0xc8F014FE6a8521259D9ADDc2170bA9e59305D306

Optimism Sepolia	0xaDB1C56D363FF5A75260c3bd27dd7C1fC8421EF5

Plume	0x6Eb53371f646788De6B4D0225a4Ed1d9267188AD

Polygon	0x2982B9566E912458fE711FB1Fd78158264596937

SeiEVM	0x3F2D6441C7a59Dfe80f8e14142F9E28F6D440445

Unichain Sepolia	0x607723D6353Dae3ef62B7B277Cfabd0F4bc6CB4C

XRPL EVM Testnet	0xcDD9d7C759b29680f7a516d0058de8293b2AC7b1


WTT Executor

## WTT Executor

=== "Mainnet"

Chain Name	Contract Address

Ethereum	0xa8969F3f8D97b3Ed89D4e2EC19B6B0CfD504b212

Solana	tbr7Qje6qBzPwfM52csL5KFi8ps5c5vDyiVVBLYVdRf

0G (Zero Gravity)	0x3dBb4dCf25dBA8F0C2417E1c34e527De0E465ecc

Arbitrum	0x04C98824a64d75CD1E9Bc418088b4c9A99048153

Avalanche	0x8849F05675E034b54506caB84450c8C82694a786

Base	0xD8B736EF27Fc997b1d00F22FE37A58145D3BDA07

Berachain	0xFAeFa20CB3759AEd2310E25015F05d62D8567A3F

BNB Smart Chain	0x2513515340fF71DD5AF02fC1BdB9615704d91524

Celo	0xe478DEe705BEae591395B08934FA19F54df316BE

Fantom	0xcafd2f0a35a4459fa40c0517e17e6fa2939441ca

Fogo	tbr7Qje6qBzPwfM52csL5KFi8ps5c5vDyiVVBLYVdRf

Ink	0x4bFB47F4c8A904d2C24e73601D175FE3a38aAb5B

MegaETH	0x4eEC1c908aD6e778664Efb03386C429fE5710D77

Monad	0xf7E051f93948415952a2239582823028DacA948e

Moonbeam	0xF6b9616C63Fa48D07D82c93CE02B5d9111c51a3d

Optimism	0x37aC29617AE74c750a1e4d55990296BAF9b8De73

Polygon	0x1d98CA4221516B9ac4869F5CeA7E6bb9C41609D6

Scroll	0x05129e142e7d5A518D81f19Db342fBF5f7E26A18

SeiEVM	0x7C129bc8F6188d12c0d1BBDE247F134148B97618

Sui	0x9b68b36399a3cd87680878d72253b3e8fdf82edb8ed74f7ec440b8bddd51f85d

Unichain	0x9Bca817F67f01557aeD615130825A28F4C5f3b87

World Chain	0xc0565Bd29b34603C0383598E16843d95Ae9c4f65

XRPL-EVM	0x37bCc9d175124F77Bfce68589d2a8090eF846B85

=== "Testnet"

Chain Name	Contract Address

Ethereum Sepolia	0xb0b2119067cF04fa959f654250BD49fE1BD6F53c

Solana	tbr7Qje6qBzPwfM52csL5KFi8ps5c5vDyiVVBLYVdRf

0G (Zero Gravity)	0x57188fC61ce92c8E941504562811660Ab883E895

Arbitrum Sepolia	0xaE8dc4a7438801Ec4edC0B035EcCCcF3807F4CC1

Avalanche	0x10Ce9a35883C44640e8B12fea4Cc1e77F77D8c52

Base Sepolia	0x523d25D33B975ad72283f73B1103354352dBCBb8

BNB Smart Chain	0x26e7e3869b781f360A108728EE8391Cee6051E17

Celo	0x9563a59c15842a6f322b10f69d1dd88b41f2e97b

Fantom	0x9563a59c15842a6f322b10f69d1dd88b41f2e97b

Fogo	tbr7Qje6qBzPwfM52csL5KFi8ps5c5vDyiVVBLYVdRf

Linea	0x1C5CC8522b5eE1e528159989A163167bC9264D07

Mezo	0x2002a44b1106DF83671Fb419A2079a75e2a34808

Moca	0x36b91D24BAba19Af3aD1b5D5E2493A571044f14F

Monad Testnet	0x03D9739c91a26d30f4B35f7e55B9FF995ef13dDb

Moonbeam	0x9563a59c15842a6f322b10f69d1dd88b41f2e97b

Optimism Sepolia	0xD9AA4f8Ac271B3149b8C3d1D0f999Ef7cb9af9EC

Polygon Amoy	0xC5c0bF6A8419b3d47150B2a6146b7Ed598C9d736

SeiEVM	0x595712bA7e4882af338d60ae37058082a5d0331A

Sui	0xb4b86c12d4ee0a813d976fb452b7afb325a2b381d00ccb2e54c5342f5ef2e684

Unichain	0x74D37B2bcD2f8CaB6409c5a5f81C8cF5b4156963

XRPL-EVM	0xb00224c60fe6ab134c8544dc29350286545f8dcc


WTT Executor with Referrer

## WTT Executor with Referrer

=== "Mainnet"

Chain Name	Contract Address

Ethereum	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

0G (Zero Gravity)	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Arbitrum	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Avalanche	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Base	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Berachain	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

BNB Smart Chain	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Celo	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Ink	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

MegaETH	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Monad	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Moonbeam	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Optimism	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Polygon	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Scroll	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

SeiEVM	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Unichain	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

World Chain	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

XRPL-EVM	0x13a35c075D6Acc1Fb9BddFE5FE38e7672789e4db

=== "Testnet"

Chain Name	Contract Address

Ethereum Sepolia	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

0G (Zero Gravity)	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Avalanche	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Base Sepolia	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

BNB Smart Chain	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Linea	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Mezo	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Moca	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Monad Testnet	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Optimism Sepolia	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Polygon Amoy	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

SeiEVM	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

Unichain	0x412f30e9f8B4a1e99eaE90209A6b00f5C3cc8739

XRPL-EVM	0x17CFAAf9e8a5ABb1eee758dB9040F945c9EAC907


Wormhole Overview

## Wormhole Overview

We'll interact with two key Wormhole components: the [relayer](/docs/protocol/infrastructure/relayer/){target=\_blank} and the [Wormhole Core Contracts](/docs/protocol/infrastructure/core-contracts/){target=\_blank}. The relayer handles cross-chain message delivery and ensures the message is accurately received on the target chain. This allows smart contracts to communicate across blockchains without developers worrying about the underlying complexity.

Additionally, we'll rely on the relayer to automatically determine cross-chain transaction costs and facilitate payments. This feature simplifies cross-chain development by allowing you to specify only the target chain and the message. The relayer handles the rest, ensuring that the message is transmitted with the appropriate fee.

![Wormhole architecture detailed diagram: source to target chain communication.](/docs/images/protocol/architecture/architecture-1.webp)


Prerequisites

## Prerequisites

Before starting this tutorial, ensure you have the following:

- [Node.js and npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm){target=\_blank} installed on your machine.
- [Foundry](https://getfoundry.sh/introduction/installation/){target=\_blank} for deploying contracts.
- Testnet tokens for [Avalanche-Fuji](https://core.app/tools/testnet-faucet/?token=C){target=\_blank} and [Base-Sepolia](https://docs.base.org/docs/tools/network-faucets/){target=\_blank} to cover gas fees.
- Wallet private key.


Build Cross-Chain Messaging Contracts

## Build Cross-Chain Messaging Contracts

In this section, we'll deploy two smart contracts: one to send a message from Avalanche Fuji and another to receive it on Base Sepolia. The contracts interact with the relayer to transmit messages across chains.

At a high level, our contracts will:

1. Send a message from Avalanche to Base Sepolia using the relayer.
2. Receive and process the message on Base Sepolia, logging the content of the message.

Before diving into the deployment steps, let's first break down key parts of the contracts.


Sender Contract: MessageSender

### Sender Contract: MessageSender

The `MessageSender` contract is responsible for quoting the cost of sending a message cross-chain and then sending that message.

Key functions include:

 - **`quoteCrossChainCost`**: Calculates the cost of delivering a message to the target chain using the relayer.
 - **`sendMessage`**: Encodes the message and sends it to the target chain and contract address using the relayer.

Here's the core of the contract:

```solidity
    function sendMessage(
        uint16 targetChain,
        address targetAddress,
        string memory message
    ) external payable {
        uint256 cost = quoteCrossChainCost(targetChain);

        require(
            msg.value >= cost,
            "Insufficient funds for cross-chain delivery"
        );

        wormholeRelayer.sendPayloadToEvm{value: cost}(
            targetChain,
            targetAddress,
            abi.encode(message, msg.sender),
            0,
            GAS_LIMIT
        );
    }
```

You can find the full code for the `MessageSender.sol` below.

??? code "MessageSender.sol"

    ```solidity
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.18;

    import "lib/wormhole-solidity-sdk/src/interfaces/IWormholeRelayer.sol";

    contract MessageSender {
        IWormholeRelayer public wormholeRelayer;
        uint256 constant GAS_LIMIT = 50000;

        constructor(address _wormholeRelayer) {
            wormholeRelayer = IWormholeRelayer(_wormholeRelayer);
        }

        function quoteCrossChainCost(
            uint16 targetChain
        ) public view returns (uint256 cost) {
            (cost, ) = wormholeRelayer.quoteEVMDeliveryPrice(
                targetChain,
                0,
                GAS_LIMIT
            );
        }

        function sendMessage(
            uint16 targetChain,
            address targetAddress,
            string memory message
        ) external payable {
            uint256 cost = quoteCrossChainCost(targetChain);

            require(
                msg.value >= cost,
                "Insufficient funds for cross-chain delivery"
            );

            wormholeRelayer.sendPayloadToEvm{value: cost}(
                targetChain,
                targetAddress,
                abi.encode(message, msg.sender),
                0,
                GAS_LIMIT
            );
        }
    }
    ```


Receiver Contract: MessageReceiver

### Receiver Contract: MessageReceiver

The `MessageReceiver` contract handles incoming cross-chain messages. When a message arrives, it decodes the payload and logs the message content. It ensures that only authorized contracts can send and process messages, adding an extra layer of security in cross-chain communication.

#### Emitter Validation and Registration

In cross-chain messaging, validating the sender is essential to prevent unauthorized contracts from sending messages. The `isRegisteredSender` modifier ensures that messages can only be processed if they come from the registered contract on the source chain. This guards against malicious messages and enhances security.

Key implementation details include:

 - **`registeredSender`**: Stores the address of the registered sender contract.
 - **`setRegisteredSender`**: Registers the sender's contract address on the source chain. It ensures that only registered contracts can send messages, preventing unauthorized senders.
 - **`isRegisteredSender`**: Restricts the processing of messages to only those from registered senders, preventing unauthorized cross-chain communication.

```solidity
    mapping(uint16 => bytes32) public registeredSenders;
    modifier isRegisteredSender(uint16 sourceChain, bytes32 sourceAddress) {
        require(
            registeredSenders[sourceChain] == sourceAddress,
            "Not registered sender"
        );
        _;
    }

    function setRegisteredSender(
        uint16 sourceChain,
        bytes32 sourceAddress
    ) public {
        require(
            msg.sender == registrationOwner,
            "Not allowed to set registered sender"
        );
        registeredSenders[sourceChain] = sourceAddress;
    }
```

#### Message Processing

The `receiveWormholeMessages` is the core function that processes the received message. It checks that the relayer sent the message, decodes the payload, and emits an event with the message content. It is essential to verify the message sender to prevent unauthorized messages.

```solidity
    function receiveWormholeMessages(
        bytes memory payload,
        bytes[] memory,
        bytes32 sourceAddress,
        uint16 sourceChain,
        bytes32
    ) public payable override isRegisteredSender(sourceChain, sourceAddress) {
        require(
            msg.sender == address(wormholeRelayer),
            "Only the Wormhole relayer can call this function"
        );

        // Decode the payload to extract the message
        string memory message = abi.decode(payload, (string));

        // Example use of sourceChain for logging
        if (sourceChain != 0) {
            emit SourceChainLogged(sourceChain);
        }

        // Emit an event with the received message
        emit MessageReceived(message);
    }
```

You can find the full code for the `MessageReceiver.sol` below.

??? code "MessageReceiver.sol"

    ```solidity
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.18;

    import "lib/wormhole-solidity-sdk/src/interfaces/IWormholeRelayer.sol";
    import "lib/wormhole-solidity-sdk/src/interfaces/IWormholeReceiver.sol";

    contract MessageReceiver is IWormholeReceiver {
        IWormholeRelayer public wormholeRelayer;
        address public registrationOwner;

        // Mapping to store registered senders for each chain
        mapping(uint16 => bytes32) public registeredSenders;

        event MessageReceived(string message);
        event SourceChainLogged(uint16 sourceChain);

        constructor(address _wormholeRelayer) {
            wormholeRelayer = IWormholeRelayer(_wormholeRelayer);
            registrationOwner = msg.sender; // Set contract deployer as the owner
        }

        modifier isRegisteredSender(uint16 sourceChain, bytes32 sourceAddress) {
            require(
                registeredSenders[sourceChain] == sourceAddress,
                "Not registered sender"
            );
            _;
        }

        function setRegisteredSender(
            uint16 sourceChain,
            bytes32 sourceAddress
        ) public {
            require(
                msg.sender == registrationOwner,
                "Not allowed to set registered sender"
            );
            registeredSenders[sourceChain] = sourceAddress;
        }

        // Update receiveWormholeMessages to include the source address check
        function receiveWormholeMessages(
            bytes memory payload,
            bytes[] memory,
            bytes32 sourceAddress,
            uint16 sourceChain,
            bytes32
        ) public payable override isRegisteredSender(sourceChain, sourceAddress) {
            require(
                msg.sender == address(wormholeRelayer),
                "Only the Wormhole relayer can call this function"
            );

            // Decode the payload to extract the message
            string memory message = abi.decode(payload, (string));

            // Example use of sourceChain for logging
            if (sourceChain != 0) {
                emit SourceChainLogged(sourceChain);
            }

            // Emit an event with the received message
            emit MessageReceived(message);
        }
    }
    ```


Deploy Contracts

## Deploy Contracts

This section will guide you through deploying the cross-chain messaging contracts on the Avalanche Fuji and Base Sepolia Testnets. Follow these steps to get your contracts up and running.


Deployment Tools

### Deployment Tools
We use _Foundry_ to deploy our smart contracts. However, you can use any tool you're comfortable with, such as:

 - [Remix](https://remix.ethereum.org/){target=\_blank} for a browser-based IDE.
 - [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#installation){target=\_blank} for a more extensive JavaScript/TypeScript workflow.
 - [Foundry](https://getfoundry.sh/introduction/installation/){target=\_blank} for a CLI-focused experience with built-in scripting and testing features.

The contracts and deployment steps remain the same regardless of your preferred tool. The key is to ensure you have the necessary Testnet funds and are deploying to the right networks.


Repository Setup

### Repository Setup

To get started with cross-chain messaging using Wormhole, first clone the [GitHub repository](https://github.com/wormhole-foundation/demo-wormhole-messaging){target=\_blank}. This repository includes everything you need to deploy, interact, and test the message flow between chains.

This demo focuses on using the scripts, so it's best to take a look at them, starting with `deploySender.ts`, `deployReceiver.ts`, and `sendMessage.ts`.

To configure the dependencies properly, run the following command:

```bash
npm install
```

The repository includes:

- Two Solidity contracts:

    - **`MessageSender.sol`**: Contract that sends the cross-chain message from Avalanche.
    - **`MessageReceiver.sol`**: Contract that receives the cross-chain message on Base Sepolia.

- Deployment scripts located in the `script` directory:

    - **`deploySender.ts`**: Deploys the `MessageSender` contract to Avalanche.
    - **`deployReceiver.ts`**: Deploys the `MessageReceiver` contract to Base Sepolia.
    - **`sendMessage.ts`**: Sends a message from Avalanche to Base Sepolia.

- Configuration files and ABI JSON files for easy deployment and interaction:

    - **`chains.json`**: Configuration file that stores key information for the supported Testnets, including the relayer addresses, RPC URLs, and chain IDs. You likely won't need to modify this file unless you're working with different networks.

 - A dedicated `interfaces` directory inside the `src` folder for TypeScript type definitions:

    - **`ChainsConfig.ts`**: Defines the types for the `chains.json` configuration file.
    - **`DeployedContracts.ts`**: Contains types for deployed contract addresses and related information.
    - **`MessageJsons.ts`**: Includes types for ABI and bytecode JSONs used by the deployment scripts.
    - **`index.ts`**: Serves as an export aggregator for the interfaces, simplifying imports in other files.


Important Setup Steps

### Important Setup Steps

1. **Add your private key**: Create a `.env` file in the root of the project and add your private key.

    ```env
    touch .env
    ```

    Inside `.env`, add your private key in the following format:

    ```env
    PRIVATE_KEY=INSERT_PRIVATE_KEY
    ```

2. **Compile the contracts**: Ensure everything is set up correctly by compiling the contracts.

    ```bash
    forge build
    ```

The expected output should be similar to this:

	forge build
	 > [⠒] Compiling...
	 > [⠰] Compiling 30 files with 0.8.23
	 [⠔] Solc 0.8.23 finished in 2.29s
	Compiler run successful!