# Interchain

Interchain Messaging (ICM) made simple for Avalanche Subnet ecosystems.

The `@avalanche-sdk/interchain` package provides a developer-friendly TypeScript SDK to send and manage cross-chain messages between Avalanche and its subnets using the Interchain Messaging (ICM) protocol.

## Features

- Type-safe ICM client for sending cross-chain messages
- Works seamlessly with [`viem`](https://viem.sh/) wallet clients
- Built-in support for Avalanche C-Chain and custom subnets
- Cross-Chain ERC20 tokens transfer (ICTT)
- Warp Message Building

## Requirements

- Node.js >= 20.0.0
- TypeScript >= 5.0.0

## Installation

```bash
npm install @avalanche-sdk/interchain viem
```

## Sending Messages Cross-Chain

Here’s a minimal example demonstrating how to send a cross-chain message using `createICMClient`:

```ts
import { createWalletClient, http } from "viem";
import { createICMClient } from "@avalanche-sdk/interchain";
import { privateKeyToAccount } from "viem/accounts";

// these will be made available in a separate SDK soon
import { avalancheFuji, dispatch } from "@avalanche-sdk/interchain/chains";

// Load your signer/account
const account = privateKeyToAccount('0x63e0730edea86f6e9e95db48dbcab18406e60bebae45ad33e099f09d21450ebf');

// Create a viem wallet client connected to Avalanche Fuji
const wallet = createWalletClient({
  transport: http('https://api.avax-test.network/ext/bc/C/rpc'),
  account,
});

// Initialize the ICM client
const icmClient = createICMClient(wallet);

// Send a message across chains
async function main() {
  const hash = await icmClient.sendMsg({
    sourceChain: avalancheFuji,
    destinationChain: dispatch,
    message: 'Hello from Avalanche Fuji to Dispatch Fuji!',
  });
  console.log('Message sent with hash:', hash);
}

main();
```

## Sending Tokens Cross-Chain

Sending ERC20 tokens from one Avalanche L1 to another is possible using Teleporter.
Before sending tokens, we need to setup some contracts on both source and destination chains. Here's a quick summary of steps we'd need to follow:

- Deploy ERC20 Token.
- Deploy `TokenHome` contract on the source chain.
- Deploy `TokenRemote` contract on the destination chain.
- Register `TokenRemote` on `TokenHome` by issuing a transaction on `TokenRemote` contract, which in turn will emit an event on the source chain.
- Approve `TokenHome` contract to spend (and hence lock) ERC20 tokens.
- Send ERC20 Token from source to destination chain.

Here's a demo to do that with the Interchain SDK.

```typescript
import { http, createWalletClient } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { createICTTClient } from "@avalanche-sdk/interchain";

// These will be made available in separate SDK
import { avalancheFuji, dispatch } from "@avalanche-sdk/interchain/chains";

const account = privateKeyToAccount('0x63e0730edea86f6e9e95db48dbcab18406e60bebae45ad33e099f09d21450ebf');

// We need separate wallet clients for each to do certain operations
const fujiWallet = createWalletClient({
    chain: avalancheFuji,
    transport: http(),
    account,
})
const dispatchWallet = createWalletClient({
    chain: dispatch,
    transport: http(),
    account,
})

// We can create ICTT client with or without default source/destination chains
const ictt = createICTTClient();

async function main() {
    // Deploy ERC20 token on Avalanche Fuji
    const { contractAddress: tokenAddress } = await ictt.deployERC20Token({
        walletClient: fujiWallet,
        sourceChain: avalancheFuji,
        name: 'Test Token',
        symbol: 'TEST',
        initialSupply: 1000000,
    });
    console.log(`Token deployed on Avalanche Fuji: ${tokenAddress}`);

    // Deploy Token Home Contract on Avalanche Fuji. This is one-time process for each token we
    // want to send to another chain.
    const { contractAddress: tokenHomeContract } = await ictt.deployTokenHomeContract({
        walletClient: fujiWallet,
        sourceChain: avalancheFuji,
        erc20TokenAddress: tokenAddress,
        minimumTeleporterVersion: 1,
    });
    console.log(`Token home contract deployed on Avalanche Fuji: ${tokenHomeContract}`);

    // Deploy Token Remote Contract on Dispatch. This is one-time process for each token we
    // want to send to another chain.
    const { contractAddress: tokenRemoteContract } = await ictt.deployTokenRemoteContract({
        walletClient: dispatchWallet,
        sourceChain: avalancheFuji,
        destinationChain: dispatch,
        tokenHomeContract,
    });
    console.log(`Token remote contract deployed on Dispatch: ${tokenRemoteContract}`);

    // Register Token Remote Contract with Token Home Contract on Dispatch. This is one-time process for each token we
    // want to send to another chain.
    const { txHash: registerRemoteWithHomeTxHash } = await ictt.registerRemoteWithHome({
        walletClient: dispatchWallet,
        sourceChain: avalancheFuji,
        destinationChain: dispatch,
        tokenRemoteContract,
    })
    console.log(`Token remote contract registered with home on Dispatch: ${registerRemoteWithHomeTxHash}`);

    // Approve token on Avalanche Fuji. This operation approves the HomeContract to
    // lock token on Fuji as collateral for the interchain transfer.
    const { txHash: approveTokenTxHash } = await ictt.approveToken({
        walletClient: fujiWallet,
        sourceChain: avalancheFuji,
        tokenHomeContract,
        tokenAddress,
        amountInBaseUnit: 2,
    })
    console.log(`Token approved on Avalanche Fuji: ${approveTokenTxHash}`);

    // Send token from Avalanche Fuji to Dispatch.
    const { txHash: sendTokenTxHash } = await ictt.sendToken({
        walletClient: fujiWallet,
        sourceChain: avalancheFuji,
        destinationChain: dispatch,
        tokenHomeContract,
        tokenRemoteContract,
        amountInBaseUnit: 1,
        recipient: '0x909d71Ed4090ac6e57E3645dcF2042f8c6548664',
    })
    console.log(`Token sent from Avalanche Fuji to Dispatch: ${sendTokenTxHash}`);
}

main().catch(console.error);
```

## ReactJS Examples

We have also included few examples to integrate the `interchain` SDK with frontend libraries like ReactJS.
Run the following commands to setup the React example repository. See examples [here](./examples/react-examples/)

```bash
cd examples/react-examples
npm install
npm run dev
```

Now visit the local website to interact with the ICM and ICTT examples. 

## Warp Message Parsing

The SDK provides utilities for parsing and working with Warp messages, which are used for cross-chain communication in the Avalanche network. Warp messages are signed messages that can be verified across different chains.

### Message Structure

A Warp message consists of several components:
- Network ID
- Source Chain ID
- Addressed Call Payload
  - Source Address
  - Message Payload (specific to the message type)
- BitSet Signatures

For more details on the format, refer to original [AvalancheGo docs](https://github.com/ava-labs/avalanchego/blob/ae36212/vms/platformvm/warp/README.md)

### Supported Message Types

The SDK currently supports parsing the following types of AddressedCall payload messages:

1. **RegisterL1ValidatorMessage**
2. **L1ValidatorWeightMessage**
3. **L1ValidatorRegistrationMessage**
4. **SubnetToL1ConversionMessage**

For more details on the format of message types, refer to original [ACP 77](https://github.com/avalanche-foundation/ACPs/blob/58c78c/ACPs/77-reinventing-subnets/README.md#p-chain-warp-message-payloads)

### Working with Warp Messages

You can parse Warp messages from their hex representation and access their components. The SDK provides type-safe methods to work with different message types.

For detailed examples of working with Warp messages, see the [warp examples directory](https://github.com/ava-labs/avalanche-sdk-typescript/tree/main/interchain/examples/warp).

### Example Usage

Message types and related builder functions can imported from `/warp` sub-path as shown below.

```typescript
import { WarpMessage, RegisterL1ValidatorMessage } from "@avalanche-sdk/interchain/warp";

const signedWarpMsgHex = '<SIGNED_MSG_HEX>'

// Parse a signed Warp message
const signedWarpMsg = WarpMessage.fromHex(signedWarpMsgHex);

// Parse message from signed message, or AddressedCall payload, or the actual message
const registerL1ValidatorMsg = RegisterL1ValidatorMessage.fromHex(signedWarpMsgHex);

// Convert back to hex
const hexBytes = registerL1ValidatorMsg.toHex();
```

### Building Unsigned Messages

You can also build the unsigned messages like `RegisterL1ValidatorMessage` or `L1ValidatorWeightMessage`.

```typescript
import {
    AddressedCall,
    L1ValidatorWeightMessage,
    WarpUnsignedMessage
} from "@avalanche-sdk/interchain/warp";

// building the L1ValidatorWeight message using values
const newL1ValidatorWeightMsg = L1ValidatorWeightMessage.fromValues(
    '251q44yFiimeVSHaQbBk69TzoeYqKu9VagGtLVqo92LphUxjmR',
    4n,
    41n,
)

// building AddressedCall payload from the above message
const addressedCallPayload = AddressedCall.fromValues(
    '0x35F884853114D298D7aA8607f4e7e0DB52205f07',
    newL1ValidatorWeightMsg.toHex()
)

// building WarpUnsignedMessage from the above addressed call payload
const warpUnsignedMessage = WarpUnsignedMessage.fromValues(
    1,
    '251q44yFiimeVSHaQbBk69TzoeYqKu9VagGtLVqo92LphUxjmR',
    addressedCallPayload.toHex()
)
```

For more detailed examples and use cases, refer to the [warp examples](https://github.com/ava-labs/avalanche-sdk-typescript/tree/main/interchain/examples/warp) in the repository.
