Skip to content

Commit

Permalink
improve scripts and update config and contracts to match new flow
Browse files Browse the repository at this point in the history
  • Loading branch information
tmsdkeys committed Feb 14, 2024
1 parent e36a009 commit 52a1714
Show file tree
Hide file tree
Showing 10 changed files with 336 additions and 77 deletions.
16 changes: 9 additions & 7 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
PRIVATE_KEY_1 = '0x123...'
PRIVATE_KEY_2 = '0x456...'
PRIVATE_KEY_3 = '0x789...'
# Make sure to rename this file to .env before adding your private keys!!!
PRIVATE_KEY_1 = ''
PRIVATE_KEY_2 = ''
PRIVATE_KEY_3 = ''

OP_DISPATCHER = '0xabcd...'
BASE_DISPATCHER = '0xefgh...'
# Contract addresses last updated on 2024-02-13, for Polymer Production Testnet
OP_DISPATCHER = '0x9B056D1a889D98824A2917DaF70cC6890719E9FD'
BASE_DISPATCHER = '0x52646d58B5ce673066764D47cb0C481fdd93ddb2'

OP_UC_MW = '0xijkl...'
BASE_UC_MW = '0mnop...'
OP_UC_MW = '0x742D894d832cD4d1a454A248Ae638F37952c369C'
BASE_UC_MW = '0xEd56F12D5270317cD517b800135B0427a8Ac9C4b'

78 changes: 69 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,73 @@
# Sample Hardhat Project
# Template for IBC enabled Soldity contracts

This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a script that deploys that contract.
This tutorial enables to send an IBC packet from an "Xcounter" contract on either OP or Base. The packet will ensure that a counter variable on either contract remains in sync.

Try running some of the following tasks:
## Install dependencies

```shell
npx hardhat help
npx hardhat test
REPORT_GAS=true npx hardhat test
npx hardhat node
npx hardhat run scripts/deploy.js
To use the quickstart tutorial, make sure that you have all dependencies installed.

From the root directory run:
```bash
forge install
```
to install the [vIBC core smart contracts](https://github.com/open-ibc/vibc-core-smart-contracts) as a dependency.

Again from root, run:
```bash
npm install
```
to install some node modules as dependencies, specifically when you want to use Hardhat.

## Set up your environment variables

In the `.env` file (you'll find it as `.env.example`), add your private key(s) and rename to drop the "example" in the filename. The dispatcher addresses should be correct but you could use custom ones if required.

Next, check the `config.json` file. This is where a lot of the parameters you need to deploy, create channels and send packets are stored.

When using the default scripts, those fields will be mostly auto-populated. Only the contract types in the `deploy` field must be updated when you write your own contracts.

## Run the scripts

There's three types of scripts in the project:

- `deploy.js` and `deploy-config.js` allow you to deploy your application contract
- `create-channel.js` and `create-channel-config.js` creates a channel
- `send-packet.js` sends packets over an existing channel

For every script you'll find a field in the config.json!!

Make sure to update the config with the intended files before running one of the scripts like so:
```bash
npx hardhat run scripts/send-packet.js --network optimism
```

**NOTE** Make sure to align the `--network` flag value to be compatible with your config values either on optimism or base.

## Deploy

Run:
```bash
# format node scripts/deploy-config.js [source] [destination]
node scripts/deploy-config.js optimism base
```

To deploy instances of the contracts on optimism as the source and base as the destination chains. (You can also switch the order)

Also this script will take the output of the deployment and update the config file with all the relevant information.

Then run:
```bash
node scripts/create-channel-config.js
```

To create a channel between base and optimism. Note that the **ORDER MATTERS**; if you picked optimism as the source chain (first argument) above, by default it will create the channel from optimism and vice versa.

Also this script will take the output of the channel creation and update the config file with all the relevant information.

Check out the [channel tab in the explorer](https://explorer.prod.testnet.polymer.zone/channels) to find out if the correct channel-id's related to your contracts were updated in the config.

Finally run:
```bash
npx hardhat run scripts/send-packet.js --network optimism
```
to send a packet. You can pick either optimism or base to send the packet from.
44 changes: 30 additions & 14 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
{
"deploy": {
"optimism": "CustomChanIbcContract",
"base": "CustomChanIbcContract"
},
"createChannel": {
"srcContractType": "IbcApp",
"srcChain": "base",
"srcAddr": "0x123...",
"dstChain": "optimism",
"dstAddr": "0xabc...",
"version": "1.0",
"ordering": 1,
"fees": false
"srcChain": "optimism",
"srcAddr": "0x123",
"dstChain": "base",
"dstAddr": "0x456",
"version": "1.0",
"ordering": 1,
"fees": false
},
"sendPacket": {
"srcAddr": "0x123...",
"srcChannelId": "channel-0",
"optimism": {
"portAddr": "0x123",
"channelId": "channel-n",
"timeout": 36000
},
"base": {
"portAddr": "0x456",
"channelId": "channel-n",
"timeout": 36000
}
},
"sendUniversalPacket": {
"srcAddr": "0x456...",
"dstAddr": "0xdef...",
"srcChannelId": "channel-1",
"optimism": {
"portAddr": "0xabc",
"channelId": "channel-x",
"timeout": 36000
},
"base": {
"portAddr": "0xdef",
"channelId": "channel-y",
"timeout": 36000
}
}
}
}
66 changes: 51 additions & 15 deletions contracts/CustomChanIbcContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ contract CustomChanIbcContract is IbcReceiverBase, IbcReceiver {
// received timeout packet as chain A
IbcPacket[] public timeoutPackets;

// bytes array with the channel IDs of the connected channels
bytes32[] public connectedChannels;
struct ChannelMapping {
bytes32 channelId;
bytes32 cpChannelId;
}

// ChannelMapping array with the channel IDs of the connected channels
ChannelMapping[] public connectedChannels;

// add supported versions (format to be negotiated between apps)
string[] supportedVersions = ['1.0'];
Expand All @@ -25,6 +30,17 @@ contract CustomChanIbcContract is IbcReceiverBase, IbcReceiver {
function updateDispatcher(IbcDispatcher _dispatcher) external onlyOwner {
dispatcher = _dispatcher;
}

/**
* @dev Sends a packet with a greeting message over a specified channel.
* @param message The greeting message to be sent.
* @param channelId The ID of the channel to send the packet to.
* @param timeoutTimestamp The timestamp at which the packet will expire if not received.
*/

function sendGreet(string calldata message, bytes32 channelId, uint64 timeoutTimestamp) external {
dispatcher.sendPacket(channelId, bytes(message), timeoutTimestamp);
}

function onRecvPacket(IbcPacket memory packet) external onlyIbcDispatcher returns (AckPacket memory ackPacket) {
recvedPackets.push(packet);
Expand All @@ -42,6 +58,33 @@ contract CustomChanIbcContract is IbcReceiverBase, IbcReceiver {
// do logic
}

/**
*
* @param feeEnabled in production, you'll want to enable this to avoid spamming create channel calls (costly for relayers)
* @param connectionHops 2 connection hops to connect to the destination via Polymer
* @param counterparty the address of the destination chain contract you want to connect to
* @param proof not implemented for now
*/
function createChannel(
string calldata version,
uint8 ordering,
bool feeEnabled,
string[] calldata connectionHops,
CounterParty calldata counterparty,
Proof calldata proof
) external {

dispatcher.openIbcChannel(
IbcChannelReceiver(address(this)),
version,
ChannelOrder(ordering),
feeEnabled,
connectionHops,
counterparty,
proof
);
}

function onOpenIbcChannel(
string calldata version,
ChannelOrder ordering,
Expand Down Expand Up @@ -101,7 +144,11 @@ contract CustomChanIbcContract is IbcReceiverBase, IbcReceiver {

// do logic

connectedChannels.push(channelId);
ChannelMapping memory channelMapping = ChannelMapping({
channelId: channelId,
cpChannelId: counterpartyChannelId
});
connectedChannels.push(channelMapping);
}

function onCloseIbcChannel(
Expand All @@ -112,7 +159,7 @@ contract CustomChanIbcContract is IbcReceiverBase, IbcReceiver {
// logic to determin if the channel should be closed
bool channelFound = false;
for (uint256 i = 0; i < connectedChannels.length; i++) {
if (connectedChannels[i] == channelId) {
if (connectedChannels[i].channelId == channelId) {
delete connectedChannels[i];
channelFound = true;
break;
Expand All @@ -130,15 +177,4 @@ contract CustomChanIbcContract is IbcReceiverBase, IbcReceiver {
function triggerChannelClose(bytes32 channelId) external onlyOwner {
dispatcher.closeIbcChannel(channelId);
}

/**
* @dev Sends a packet with a greeting message over a specified channel.
* @param message The greeting message to be sent.
* @param channelId The ID of the channel to send the packet to.
* @param timeoutTimestamp The timestamp at which the packet will expire if not received.
*/

function sendGreet(string calldata message, bytes32 channelId, uint64 timeoutTimestamp) external {
dispatcher.sendPacket(channelId, bytes(message), timeoutTimestamp);
}
}
7 changes: 4 additions & 3 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module.exports = {
},
networks: {
// for Base testnet
'base-sepolia': {
'base': {
url: 'https://sepolia.base.org',
accounts: [
process.env.PRIVATE_KEY_1,
Expand All @@ -24,7 +24,7 @@ module.exports = {
],
},
// for OP testnet
'op-sepolia': {
'optimism': {
url: 'https://sepolia.optimism.io',
accounts: [
process.env.PRIVATE_KEY_1,
Expand All @@ -33,5 +33,6 @@ module.exports = {
],
},
},
defaultNetwork: 'op-sepolia',
defaultNetwork: 'optimism',
};

49 changes: 49 additions & 0 deletions scripts/create-channel-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const { exec } = require("child_process");
const fs = require("fs");
const path = require("path");
const config = require("../config.json");

// Function to update config.json
function updateConfig(network, channel, cpNetwork, cpChannel) {
const configPath = path.join(__dirname, '..', 'config.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));

// Update the config object
config["sendPacket"][`${network}`]["channelId"] = channel;
config["sendPacket"][`${cpNetwork}`]["channelId"] = cpChannel;

// Write the updated config back to the file
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
}

// Function to run the deploy script and capture output
function createChannelAndCapture() {
exec(`npx hardhat run scripts/create-channel.js --network ${config.createChannel.srcChain}`, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
return;
}

// Process stdout to find the contract address and network
const output = stdout.trim();
const match = output.match(/Channel created: (\S+) with portID (\S+) on network (\S+), Counterparty: (\S+) on network (\S+)/);

if (match) {
const channel = match[1];
const portId = match[2];
const network = match[3];
const cpChannel = match[4];
const cpNetwork = match[5];

console.log(`Created channel: ${channel} with portID ${portId} on network ${network}, Counterparty: ${cpChannel} on network ${cpNetwork}`);

// Update the config.json file
updateConfig(network, channel, cpNetwork, cpChannel);
console.log(`Updated config.json with ${channel} on network ${network} and ${cpChannel} on network ${cpNetwork}`);
} else {
console.error("Could not find required parameters in output");
}
});
}

createChannelAndCapture();
Loading

0 comments on commit 52a1714

Please sign in to comment.