Skip to content

Commit

Permalink
Merge pull request #6 from msgport/bear-pingpong
Browse files Browse the repository at this point in the history
Add PingPong Example
  • Loading branch information
boundless-forest authored Jul 4, 2024
2 parents 1aaaeff + 8f323b9 commit fcd21d3
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 6 deletions.
4 changes: 4 additions & 0 deletions script/input/11155111/constructor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"ORMP_PORT": "0x2cd1867Fb8016f93710B6386f7f9F1D540A60812",
"FEE": 10000000000000000
}
3 changes: 0 additions & 3 deletions script/input/11155111/port_address.json

This file was deleted.

4 changes: 4 additions & 0 deletions script/input/701/constructor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"ORMP_PORT": "0x2cd1867Fb8016f93710B6386f7f9F1D540A60812",
"FEE": 5000000000000000000000
}
3 changes: 0 additions & 3 deletions script/input/701/port_address.json

This file was deleted.

3 changes: 3 additions & 0 deletions script/output/11155111/pingpong-latest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"PingPong": "0x0c8541f3A9B5384029BFdbA5e8f8D6E4242FC02a"
}
3 changes: 3 additions & 0 deletions script/output/701/pingpong-latest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"PingPong": "0xC0FFF7abA8C095ce1c074C743c7526aa7432535E"
}
123 changes: 123 additions & 0 deletions src/pingpong/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Cross-chain PingPong

## Preparation

### Install foundry

```bash
curl -L https://foundry.paradigm.xyz | bash
foundryup
forge install
```

### Clone repo and submodules

```bash
git clone --recursive https://github.com/msgport/msgport-examples.git
```

### Account with balance

Before proceeding with the tutorial, you need an account with sufficient balance. This is required to perform contract operations in both the Koi and Sepolia testnets. If you don't have an account with sufficient balance, you may encounter difficulties when interacting with these contracts. Once you have the account, copy and create a `.env` file based on the `.env.example` file in the root of the repository. This will allow you to continue with the tutorial.

## Overview

This examples is designed very simple and straightforward, aims to illustrate how to integrate the Msgport protocol for a dapp.

In this example, we will deploy the `PingPong` contract in both the Koi and Sepolia testnets. We will then call the `ping()` function of the `PingPong` contract in the Koi testnet, which will trigger the cross-chain message delivery and invoke the `pong()` function of the `PingPong` contract in the Sepolia testnet. Once the `pong` is received in the Sepolia testnet, it will call `ping` back and trigger the `pongReceive()` function of the source chain.

### Deploy PingPong in the Koi testnet

```sh
forge script src/pingpong/script/Deploy.s.sol:DPingPongSource --broadcast --rpc-url https://koi-rpc.darwinia.network
```

The output:

```sh
== Logs ==
PingPong has been deployed at chain: 701, contract: 0xC0FFF7abA8C095ce1c074C743c7526aa7432535E

## Setting up 1 EVM.
==========================
Chain 701

Estimated gas price: 150.706512128 gwei
Estimated total gas used for script: 703866
Estimated amount required: 0.106077189865486848 ETH

==========================

##### 701
✅ [Success]Hash: 0xfa05e4ee172254dededb2f8e7364dea987cb768c2541afb989224b97fa429cf1
Contract Address: 0xC0FFF7abA8C095ce1c074C743c7526aa7432535E
Block: 193754
Paid: 0.040809967125653248 ETH (541582 gas * 75.353256064 gwei)

```

### Deploy PingPong in the Sepolia testnet

```sh
forge script src/pingpong/script/Deploy.s.sol:DPingPongTarget --broadcast --rpc-url https://ethereum-sepolia.publicnode.com
```

The output:

```sh
== Logs ==
PingPong has been deployed at chain: 11155111, contract: 0x0c8541f3A9B5384029BFdbA5e8f8D6E4242FC02a

## Setting up 1 EVM.
==========================
Chain 11155111

Estimated gas price: 48.991176468 gwei
Estimated total gas used for script: 736711
Estimated amount required: 0.036092338606916748 ETH
==========================

##### sepolia
✅ [Success]Hash: 0xbf8c63f6cd7fbae37a6b92a51279b41bf0deabafff82f9ae18857c60e37dccf4
Block: 6243115
Paid: 0.00055404038891591 ETH (22490 gas * 24.634966159 gwei)


##### sepolia
✅ [Success]Hash: 0xb1d86cf0d529a8eee8e91462119a13ddcf22d19f41eb7d2f8c85afd8dee297ad
Contract Address: 0x0c8541f3A9B5384029BFdbA5e8f8D6E4242FC02a
Block: 6243115
Paid: 0.013340967383541814 ETH (541546 gas * 24.634966159 gwei)
```

### Send Ping from the Koi testnet

```sh
forge script src/pingpong/script/SendPing.s.sol:SendPing --broadcast --rpc-url https://koi-rpc.darwinia.network --gas-estimate-multiplier 200
```

The output:

```sh
== Logs ==
The message has been sent to chain: 11155111, msgId: 0x2356043973bba1b6881ecf15b326fdc243fd2e7b2498fb12c4149e652421e81d

## Setting up 1 EVM.
==========================
Chain 701

Estimated gas price: 150.706512128 gwei
Estimated total gas used for script: 285696
Estimated amount required: 0.043056247688921088 ETH

==========================

##### 701
✅ [Success]Hash: 0x77d07b664edf1813e41a9284ac895f9335625203efd6b44771e2c3326dbc7f09
Block: 193773
Paid: 0.014324503271254272 ETH (190098 gas * 75.353256064 gwei)
```

After sending the ping, you can find the msgId in the output. The msgId can be used to track the status of the ping message in the [Msgport Scan](https://scan.msgport.xyz/). For example, you can check out the status on the Msgport Scan website by entering the msgId and selecting the network. If the status changes from "inflight" to "success", it means that the "pong" function in the target chain has been called successfully. It will then call back and trigger the `PongReceive` event in the Koi testnet.

To check the `PongReceive` event in the Koi testnet, you can wait for about 5 minutes and check the events for the PingPong contract in the Koi testnet.
52 changes: 52 additions & 0 deletions src/pingpong/script/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
pragma solidity ^0.8.17;

import {Script} from "forge-std/Script.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {console2 as console} from "forge-std/console2.sol";

import "../../../script/ScriptTools.sol";
import "../src/PingPong.sol";

contract DPingPongSource is Script {
using stdJson for string;
using ScriptTools for string;

function run() public {
vm.setEnv("FOUNDRY_ROOT_CHAINID", vm.toString(block.chainid));
vm.setEnv("FOUNDRY_EXPORTS_OVERWRITE_LATEST", vm.toString(true));

string memory input = ScriptTools.readInput("constructor");
address port_address = input.readAddress(".ORMP_PORT");
uint256 fee = input.readUint(".FEE");

vm.startBroadcast(vm.envUint("PRIVATE_KEY"));
PingPong pingpong = new PingPong(port_address, fee);
vm.stopBroadcast();

console.log("PingPong has been deployed at chain: %s, contract: %s", block.chainid, address(pingpong));
ScriptTools.exportContract("pingpong", "PingPong", address(pingpong));
}
}

contract DPingPongTarget is Script {
using stdJson for string;
using ScriptTools for string;

function run() public {
vm.setEnv("FOUNDRY_ROOT_CHAINID", vm.toString(block.chainid));
vm.setEnv("FOUNDRY_EXPORTS_OVERWRITE_LATEST", vm.toString(true));

string memory input = ScriptTools.readInput("constructor");
address port_address = input.readAddress(".ORMP_PORT");
uint256 fee = input.readUint(".FEE");

vm.startBroadcast(vm.envUint("PRIVATE_KEY"));
PingPong pingpong = new PingPong(port_address, fee);
// transfer fee to the PingPong contract ahead.
payable(pingpong).transfer(fee);
vm.stopBroadcast();

console.log("PingPong has been deployed at chain: %s, contract: %s", block.chainid, address(pingpong));
ScriptTools.exportContract("pingpong", "PingPong", address(pingpong));
}
}
39 changes: 39 additions & 0 deletions src/pingpong/script/SendPing.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
pragma solidity ^0.8.17;

import {stdJson} from "forge-std/StdJson.sol";
import {Script} from "forge-std/Script.sol";
import {console2 as console} from "forge-std/console2.sol";
import {Surl} from "surl/Surl.sol";

import "../../../script/ScriptTools.sol";
import "../src/PingPong.sol";

contract SendPing is Script {
using Surl for *;
using stdJson for string;
using ScriptTools for string;

function run() public {
// Darwinia Koi network as source chain
uint256 fromChainId = 701;
// Sepolia as target chain
uint256 toChainId = 11155111;

vm.setEnv("FOUNDRY_ROOT_CHAINID", vm.toString(fromChainId));
string memory input = ScriptTools.readInput("constructor");
uint256 fee = input.readUint(".FEE");
string memory souceContract = ScriptTools.readOutput("pingpong");
address pingpongSource = souceContract.readAddress(".PingPong");
vm.setEnv("FOUNDRY_ROOT_CHAINID", vm.toString(toChainId));
string memory targetContract = ScriptTools.readOutput("pingpong");
address pingpongTarget = targetContract.readAddress(".PingPong");
bytes memory message = abi.encodeCall(PingPong.pong, ());

vm.startBroadcast(vm.envUint("PRIVATE_KEY"));
PingPong pingpong = PingPong(payable(pingpongSource));
bytes32 msgId = pingpong.ping{value: fee}(toChainId, pingpongTarget, message);
vm.stopBroadcast();

console.log("The message has been sent to chain: %s, msgId: %s", toChainId, vm.toString(msgId));
}
}
57 changes: 57 additions & 0 deletions src/pingpong/src/PingPong.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "msgport/user/Application.sol";
import "msgport/interfaces/IMessagePort.sol";

contract PingPong is Application {
event ReceivedToken(address indexed from, uint256 value);

event PingSent(uint256 fromChainId, uint256 toChainId, bytes32 msgId);
event PongSent(uint256 fromChainId, uint256 toChainId, bytes32 msgId);
event PongReceive(uint256 fromChainId, uint256 toChainId);

// The port address, which can be any messaging protocols that under the Msgport protocol.
address public immutable PORT;
uint256 public immutable Fee;
bytes public Params =
hex"000000000000000000000000000000000000000000000000000000000001da53000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000";

constructor(address port, uint256 fee) {
PORT = port;
Fee = fee;
}

function ping(uint256 toChainId, address toDapp, bytes memory message) public payable returns (bytes32 msgId) {
msgId = IMessagePort(PORT).send{value: Fee}(toChainId, toDapp, message, Params);
emit PingSent(block.chainid, toChainId, msgId);
return msgId;
}

function pong() public returns (bytes32 msgId) {
uint256 fromChainId = _fromChainId();
address fromDapp = _xmsgSender();
address localPort = _msgPort();
require(localPort == PORT);

// Call back to the pongReceive() function in the source chain.
bytes memory message = abi.encodeWithSignature("pongReceive()");
msgId = ping(fromChainId, fromDapp, message);
emit PongSent(block.chainid, fromChainId, msgId);

return msgId;
}

function pongReceive() public {
uint256 fromChainId = _fromChainId();
address localPort = _msgPort();
require(localPort == PORT);

emit PongReceive(fromChainId, block.chainid);
}

receive() external payable {
emit ReceivedToken(msg.sender, msg.value);
}
}

0 comments on commit fcd21d3

Please sign in to comment.