Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 89 additions & 1 deletion interchain/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ The `@avalanche-sdk/interchain` package provides a developer-friendly TypeScript
- 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

Expand Down Expand Up @@ -163,4 +165,90 @@ async function main() {
}

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

## 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.
45 changes: 45 additions & 0 deletions interchain/examples/warp/buildingMessages/l1ValidatorWeight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { AddressedCall, L1ValidatorWeightMessage, WarpUnsignedMessage } from "../../../src/warp";

/**
* Structure of the message:
* WarpSignedMessage
* - WarpUnsignedMessage
* - NetworkId
* - SourceChainId
* - AddressedCallPayload
* - SourceAddress
* - L1ValidatorWeightMessage
* - BitSet Signatures
*/

// Ref: See tx hash (on Fuji - PChain) zphUhqvXtj8dkYxg2BqCdTPDUPWBJUQkb1XTp38u8zDZ9VjMW
const signedWarpMsgHex = '0000000000056b804f574b890cf9e0cb0f0f68591a394bba1696cf62b4e576e793d8509cc88600000058000000000001000000140feedc0de0000000000000000000000000000000000000360000000000038ccf9ef520784d2fa5d97fbf098b8b4e82ff19408ec423c2970a522ab04b3a0400000000000000040000000000000029000000000000000106a8206d76cf3fa7d65fec8464b0311dce9283d05bcf0ca7987cdf03a3a2f764691e01df4f6aaa3ff6b52e5b92fd3291e519f3fb50bad5d9697a39e34e2c3e99ea585f0332e9d13b4b6db7ecc58eee44c7f96e64371b1eebaa6f7c45bbf0937e68';

// parsing L1 validator weight message from the signed warp message hex
const parsedL1ValidatorWeightMsg = L1ValidatorWeightMessage.fromHex(signedWarpMsgHex)

// re-building the same message using values
const newL1ValidatorWeightMsg = L1ValidatorWeightMessage.fromValues(
'251q44yFiimeVSHaQbBk69TzoeYqKu9VagGtLVqo92LphUxjmR',
4n,
41n,
)
console.log(newL1ValidatorWeightMsg);
console.log('This should be true:', parsedL1ValidatorWeightMsg.toHex() === newL1ValidatorWeightMsg.toHex());

// now, let's build an unsgined message from the above newL1ValidatorWeightMsg
const addressedCallPayload = AddressedCall.fromValues(
'0x35F884853114D298D7aA8607f4e7e0DB52205f07',
newL1ValidatorWeightMsg.toHex()
)
console.log(addressedCallPayload);

// now, let's build a WarpUnsignedMessage from the above addressed call payload
const warpUnsignedMessage = WarpUnsignedMessage.fromValues(
1,
'251q44yFiimeVSHaQbBk69TzoeYqKu9VagGtLVqo92LphUxjmR',
addressedCallPayload.toHex()
)
console.log(warpUnsignedMessage);

// We can sign the above unsgined message by sending it's hex value using .toHex() method
30 changes: 30 additions & 0 deletions interchain/examples/warp/l1ValidatorWeight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { L1ValidatorWeightMessage, WarpMessage } from "../../src/warp";

/**
* Structure of the message:
* WarpSignedMessage
* - WarpUnsignedMessage
* - NetworkId
* - SourceChainId
* - AddressedCallPayload
* - SourceAddress
* - L1ValidatorWeightMessage
* - BitSet Signatures
*/

// Ref: See tx hash (on Fuji - PChain) zphUhqvXtj8dkYxg2BqCdTPDUPWBJUQkb1XTp38u8zDZ9VjMW
const signedWarpMsgHex = '0000000000056b804f574b890cf9e0cb0f0f68591a394bba1696cf62b4e576e793d8509cc88600000058000000000001000000140feedc0de0000000000000000000000000000000000000360000000000038ccf9ef520784d2fa5d97fbf098b8b4e82ff19408ec423c2970a522ab04b3a0400000000000000040000000000000029000000000000000106a8206d76cf3fa7d65fec8464b0311dce9283d05bcf0ca7987cdf03a3a2f764691e01df4f6aaa3ff6b52e5b92fd3291e519f3fb50bad5d9697a39e34e2c3e99ea585f0332e9d13b4b6db7ecc58eee44c7f96e64371b1eebaa6f7c45bbf0937e68';

const signedWarpMsg = WarpMessage.fromHex(signedWarpMsgHex);
console.log(signedWarpMsg);

// Directly extract the L1ValidatorWeightMessage
// `fromHex` static methods accepts:
// - fully signed message
// - addressed call payload
// - L1 validator weight message
const l1ValidatorWeightMsg = L1ValidatorWeightMessage.fromHex(signedWarpMsgHex)
console.log(l1ValidatorWeightMsg);

// Use `.toHex()` method to get back the hex bytes
console.log(l1ValidatorWeightMsg.toHex());
30 changes: 30 additions & 0 deletions interchain/examples/warp/registerL1Validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { L1ValidatorWeightMessage, RegisterL1ValidatorMessage, WarpMessage } from "../../src/warp";

/**
* Structure of the message:
* WarpSignedMessage
* - WarpUnsignedMessage
* - NetworkId
* - SourceChainId
* - AddressedCallPayload
* - SourceAddress
* - RegisterL1ValidatorMessage
* - BitSet Signatures
*/

// Ref: See tx hash (on Fuji - PChain) qKx5qy1zriGsWGhnRfHibE9kuDSUMfQV7gPQVCQ8iBKT5ZriV
const signedWarpMsgHex = '0x0000000000057f78fe8ca06cefa186ef29c15231e45e1056cd8319ceca0695ca61099e610355000000d80000000000010000001433b9785e20ec582d5009965fb3346f1716e8a423000000b60000000000015e8b6e2e8155e93739f2fa6a7f8a32c6bb2e1dce2e471b56dcc60aac49bf34350000001447b37278e32917ffc6d2861b50dd9751b4016dd1b0d305fd70c376b0f5d4e6b9184728dcacb7390f477015690133a5632affab5701e9ebe61038d2e41373de53f4569fd60000000067d1ac310000000100000001380c1fb1db38f176b50e77eca240258e31a5b5e80000000100000001380c1fb1db38f176b50e77eca240258e31a5b5e80000000000004e200000000000000003c4411899be0450aee4dcc1be90a8802bdbd12821a5025a74cb094ff0033982e7f3951d6c4b882a6ce39bd2aa835b31accd09c60f26bc75308af4e05c4237df9b72b04c2697c5a0a7fb0f05f7b09358743a4a2df8cd4eda61f0dea0312a7014baa8a5c1';

const signedWarpMsg = WarpMessage.fromHex(signedWarpMsgHex);
console.log(signedWarpMsg);

// Directly extract the RegisterL1ValidatorMessage
// `fromHex` static methods accepts:
// - fully signed message
// - addressed call payload
// - RegisterL1ValidatorMessage
const registerL1ValidatorMsg = RegisterL1ValidatorMessage.fromHex(signedWarpMsgHex)
console.log(registerL1ValidatorMsg);

// Use `.toHex()` method to get back the hex bytes
console.log(registerL1ValidatorMsg.toHex());
Loading