Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup ICM Course #150

Merged
merged 9 commits into from
Sep 11, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ When we send an interchain message, we are just sending a single value of type b

In this section we will recap these topics:

- **Bytes:** What is this data types and why are we using it?
- **Bytes:** What is this data type and why are we using it?
- **Encoding:** How do we turn values into bytes?
- **Decoding:** How can we turn the bytes back into the values?

Expand All @@ -23,7 +23,7 @@ This makes it a pragmatic choice for encapsulating diverse data and data type en

## Encoding & Decoding

In solidity we can use the functions `abi.encode()` and `abi.decode()` for encoding and decoding. These are part of the solidity language, so we do not need to import anything to use them.
In Solidity we can use the functions `abi.encode()` and `abi.decode()` for encoding and decoding. These are part of the solidity language, so we do not need to import anything to use them.

![Encoding and Decoding](/common-images/solidity/encoding-decoding.png)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ interface ITeleporterMessenger {

The `sendCrossChainMessage` function takes `TeleporterMessageInput` struct as an input. In that multiple values are contained: This data will then be included in the payload of the Warp message:

- **destinationChainID:** The chainID where the contract that should receive the message is deployed. This is not the EVM chain ID you may know from adding a network to a wallet, but the blockchain ID on the P-Chain. The P-Chain uses the transaction ID of the transaction that created those blockchain on the P-Chain for the chain ID, e.g.: 0xd7cdc6f08b167595d1577e24838113a88b1005b471a6c430d79c48b4c89cfc53
- **destinationAddress:** The address of the contract that should receive the message
- **feeInfo:** A struct consisting of a contract address of an ERC20 which the fee is paid in as well as the amount of tokens to be paid as an incentive for the relayer. We will look at this later in more detail.
- **requiredGasLimit:** The amount of gas the delivery of the message requires. If the relayer provides the required gas, the message will be considered delivered whether or not its execution succeeds, such that the relayer can claim their fee reward.
- **allowedRelayerAddresses:** An array of addresses of allowed relayers. An empty allowed relayers list means anyone is allowed to deliver the message. We will look at this later in more detail.
- **message:** The message to be sent as bytes. The message can contain multiple encoded values. DApps using Interchain Messaging are responsible for defining the exact format of this payload in a way that can be decoded on the receiving end. The message can hold multiple values that be encoded in a single bytes object. For example, applications may encode multiple method parameters on the sending side, then decode this data in the contract implementing the receiveTeleporterMessage function and call another contract with the parameters from there.
- **`destinationChainID`:** The chainID where the contract that should receive the message is deployed. This is not the EVM chain ID you may know from adding a network to a wallet, but the blockchain ID on the P-Chain. The P-Chain uses the transaction ID of the transaction that created those blockchain on the P-Chain for the chain ID, e.g.: 0xd7cdc6f08b167595d1577e24838113a88b1005b471a6c430d79c48b4c89cfc53
- **`destinationAddress`:** The address of the contract that should receive the message
- **`feeInfo`:** A struct consisting of a contract address of an ERC20 which the fee is paid in as well as the amount of tokens to be paid as an incentive for the relayer. We will look at this later in more detail.
- **`requiredGasLimit`:** The amount of gas the delivery of the message requires. If the relayer provides the required gas, the message will be considered delivered whether or not its execution succeeds, such that the relayer can claim their fee reward.
- **`allowedRelayerAddresses`:** An array of addresses of allowed relayers. An empty allowed relayers list means anyone is allowed to deliver the message. We will look at this later in more detail.
- **`message`:** The message to be sent as bytes. The message can contain multiple encoded values. DApps using Interchain Messaging are responsible for defining the exact format of this payload in a way that can be decoded on the receiving end. The message can hold multiple values that be encoded in a single bytes object. For example, applications may encode multiple method parameters on the sending side, then decode this data in the contract implementing the receiveTeleporterMessage function and call another contract with the parameters from there.
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ contract SenderOnCChain {

The key things to understand:

- **Importing ITeleporterMessenger (Line 8):** We are importing the ITeleporterMessenger Interface we looked at in the previous activity.
- **Defining teleporterMessenger contract (Line 12):** We are defining a the teleporterMessenger contract using the imported interface. It is important to note, that our cross-chain dApp is not implementing the interface itself, but initializes a contract using that interface.
- **Sending the message (Line 21):** We are sending the message by calling the function of our teleporterMessenger. As an input we are defining a TeleporterMessageInput. Make sure to replace the `destinationChainId` with the one of your Blockchain. We will need to provide the address of the receiving contract on the Avalanche L1 as a parameter to the function, since we have not deployed it yet and don't know the address at this time.
- **Importing ITeleporterMessenger (Line 8):** We are importing the `ITeleporterMessenger` Interface we looked at in the previous activity.
- **Defining teleporterMessenger contract (Line 12):** We are defining a `teleporterMessenger` contract using the imported interface. It is important to note, that our cross-chain dApp is not implementing the interface itself, but initializes a contract using that interface.
- **Sending the message (Line 21):** We are sending the message by calling the function of our `teleporterMessenger`. As an input we are defining a `TeleporterMessageInput`. Make sure to replace the `destinationChainId` with the one of your Blockchain. We will need to provide the address of the receiving contract on the Avalanche L1 as a parameter to the function, since we have not deployed it yet and don't know the address at this time.
- **No fees (Line 25):** In this exercise we are not providing any fees to the relayer for relaying the message. This is only possible since the relayer we are running here is configured to pick up any message even if it does not provide any rewards.
- **Encoding the Message (Line 31):** The TeleporterMessageInput defines a message as an array of bytes. For now we will just simply encode the string with abi.encode(). In the future activities, you will see how we can encode multiple values of any type in that message.
- **Encoding the Message (Line 31):** The `TeleporterMessageInput` defines a message as an array of bytes. For now we will just simply encode the string with `abi.encode()`. In the future activities, you will see how we can encode multiple values of any type in that message.

</Step>
<Step>
Expand All @@ -63,56 +63,62 @@ The key things to understand:
To get the blockchainID on our Avalanche L1 let's use the Avalanche-CLI built in feature with the command:

``` bash
avalanche subnet describe myblockchain
avalanche blockchain describe myblockchain
```

In the Details section of the output you will find something like this:
In the output you will find the blockchain details:

```bash
_____ _ _ _
| __ \ | | (_) |
| | | | ___| |_ __ _ _| |___
| | | |/ _ \ __/ _ | | / __|
| |__| | __/ || (_| | | \__ \
|_____/ \___|\__\__,_|_|_|___/
+--------------------------------+-------------------------------------------------------------------------------------+
| PARAMETER | VALUE |
+--------------------------------+-------------------------------------------------------------------------------------+
| Avalanche L1 Name | myblockchain |
+--------------------------------+-------------------------------------------------------------------------------------+
| ChainID | 123498765 |
+--------------------------------+-------------------------------------------------------------------------------------+
| Token Name | test Token |
+--------------------------------+-------------------------------------------------------------------------------------+
| Token Symbol | test |
+--------------------------------+-------------------------------------------------------------------------------------+
| VM Version | v0.6.4 |
+--------------------------------+-------------------------------------------------------------------------------------+
| VM ID | qDNsVQJfGpi2RfCcESbeZauGqPVjtXwoopVMtrGkdoUxmFKov |
+--------------------------------+-------------------------------------------------------------------------------------+
| Local Network Avalanche L1ID | 2CZP2ndbQnZxTzGuZjPrJAm5b4s2K2Bcjh8NqWoymi8NZMLYQk |
+--------------------------------+-------------------------------------------------------------------------------------+
| Local Network RPC URL | http://127.0.0.1:9650/ext/bc/2mJS3tyZ26vVWapQGY9eamBtcx5o4fjGyfn9ZKpL8axnUDy5yx/rpc |
+--------------------------------+-------------------------------------------------------------------------------------+
| Local Network BlockchainID | 2mJS3tyZ26vVWapQGY9eamBtcx5o4fjGyfn9ZKpL8axnUDy5yx |
+ +-------------------------------------------------------------------------------------+
| | 0xe849319dbab299f063eaccca029eb70f3a3337e10106d2c44d09a168385e570a |
+--------------------------------+-------------------------------------------------------------------------------------+
| Local Network Interchain Messaging | 0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf |
| Messenger Address | |
+--------------------------------+-------------------------------------------------------------------------------------+
| Local Network Interchain Messaging | 0x7001E06DF22d22Df4BBCa750CA0969FFf3739A7A |
| Registry Address | |
+--------------------------------+-------------------------------------------------------------------------------------+
```
+---------------------------------------------------------------------------------------------------------------+
| MYBLOCKCHAIN |
+---------------+-----------------------------------------------------------------------------------------------+
| Name | myblockchain |
+---------------+-----------------------------------------------------------------------------------------------+
| VM ID | qDNV9vtxZYYNqm7TN1mYBuaaknLdefDbFK8bFmMLTJQJKaWjV |
+---------------+-----------------------------------------------------------------------------------------------+
| VM Version | v0.6.9 |
+---------------+--------------------------+--------------------------------------------------------------------+
| Local Network | ChainID | 1 |
| +--------------------------+--------------------------------------------------------------------+
| | SubnetID | 2AKbBT8jFUfUsUJ2hhRiDUnAAajJdNhTKeNgEe3q77spMj1N8F |
| +--------------------------+--------------------------------------------------------------------+
| | Owners (Threhold=1) | P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p |
| +--------------------------+--------------------------------------------------------------------+
| | BlockchainID (CB58) | krN11ZE6BNgenbX71Wu3YWv2SS5C3p9pD3WLkkRLVhuETGNJb |
| +--------------------------+--------------------------------------------------------------------+
| | BlockchainID (HEX) | 0x6391b85bafc78f1b10aec35695c2399c2fb7fff9f3b18ea903f77345e9e96e3e | // [!code highlight]
+---------------+--------------------------+--------------------------------------------------------------------+
```

</Step>
<Step>

### Replace `destinationBlockchainID`

Replace the destinationBlockchainID in your sender code.
Replace the destinationBlockchainID in `senderOnCChain.sol` with `BlockchainID (HEX)`

```solidity
contract SenderOnCChain {
ITeleporterMessenger public immutable messenger = ITeleporterMessenger(0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf);

/**
* @dev Sends a message to another chain.
*/
function sendMessage(address destinationAddress, string calldata message) external {
messenger.sendCrossChainMessage(
TeleporterMessageInput({
// Replace with blockchainID of your Subnet (see instructions in Readme)
destinationBlockchainID: 0x6391b85bafc78f1b10aec35695c2399c2fb7fff9f3b18ea903f77345e9e96e3e, // [!code highlight]
destinationAddress: destinationAddress,
feeInfo: TeleporterFeeInfo({feeTokenAddress: address(0), amount: 0}),
requiredGasLimit: 100000,
allowedRelayerAddresses: new address[](0),
message: abi.encode(message)
})
);
}
}
```
</Step>
<Step>

Expand All @@ -123,7 +129,15 @@ To deploy a contract using Foundry use the following command:
```bash
forge create --rpc-url local-c --private-key $PK src/0-send-receive/senderOnCChain.sol:SenderOnCChain
```

```
[⠊] Compiling...
[⠒] Compiling 2 files with Solc 0.8.18
[⠢] Solc 0.8.18 finished in 81.53ms
Compiler run successful!
Deployer: 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC
Deployed to: 0x5DB9A7629912EBF95876228C24A848de0bfB43A9 // [!code highlight]
Transaction hash: 0xcde7873e9e3c68fb00a2ad6644dceb64a01a41941da46de5a0f559d6d70a1638
```
</Step>
<Step>

Expand All @@ -132,7 +146,7 @@ forge create --rpc-url local-c --private-key $PK src/0-send-receive/senderOnCCha
Then save the sender contract address in an environment variable:

```bash
export SENDER_ADDRESS="0x123..."
export SENDER_ADDRESS=0x5DB9A7629912EBF95876228C24A848de0bfB43A9
```

</Step>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ icon: BookOpen

To receive a message we need to enable our cross-Subnet dApps to being called by the Interchain Messaging contract.

<img src="/course-images/teleporter/teleporter-source-destination.png" width="400" class="mx-auto"/>
![](/course-images/teleporter/teleporter-source-destination-with-relayer.png)

The Interchain Messaging does not know our contract and what functions it has. Therefore, our dApp on the destination Subnet has to implement the ITeleporterReceiver interface. It is very straight forward and only requires a single method for receiving the message that then can be called by the Interchain Messaging contract:

Expand Down Expand Up @@ -36,9 +36,9 @@ interface ITeleporterReceiver {

The function receiveTeleporterMessage has three parameters:

- **originChainID**: The chainID where the message originates from, meaning where the user or contract called the sendCrossChainMessage function of the Interchain Messaging contract
- **originSenderAddress**: The address of the user or contract that called the sendCrossChainMessage function of the Interchain Messaging contract on the origin Subnet
- **message**: The message encoded in bytes
- **`originChainID`**: The chainID where the message originates from, meaning where the user or contract called the `sendCrossChainMessage` function of the Interchain Messaging contract
- **`originSenderAddress`**: The address of the user or contract that called the `sendCrossChainMessage` function of the Interchain Messaging contract on the origin Subnet
- **`message`**: The message encoded in bytes

An example for a contract being able to receive Interchain Messaging messages and storing these in a mapping could look like this:

Expand Down Expand Up @@ -88,4 +88,4 @@ contract MessageReceiver is ITeleporterReceiver {
}
```

This contract stores the last message and it's sender of each chain it has received. When it is instantiated, the address of the Interchain Messaging contract is supplied to the constructor. The contract implements the ITelepoterReceiver interface and therefore we also implement the receiveTeleporterMessage function.
This contract stores the last `Message` and it's sender of each chain it has received. When it is instantiated, the address of the Interchain Messaging contract is supplied to the constructor. The contract implements the `ITelepoterReceiver` interface and therefore we also implement the `receiveTeleporterMessage` function.
Loading
Loading