diff --git a/.env.example b/.env.example index 02e06259..d37944e5 100644 --- a/.env.example +++ b/.env.example @@ -8,20 +8,3 @@ BASE_ALCHEMY_API_KEY='' OP_BLOCKSCOUT_API_KEY='' BASE_BLOCKSCOUT_API_KEY='' # TENDERLY_TOKEN='' - -# Contract addresses last updated on 2024-03-05, for public testnet launch -OP_DISPATCHER='0x58f1863f75c9db1c7266dc3d7b43832b58f35e83' -BASE_DISPATCHER='0xfc1d3e02e00e0077628e8cc9edb6812f95db05dc' - -OP_UC_MW='0x34a0e37cCCEdaC70EC1807e5a1f6A4a91D4AE0Ce' -BASE_UC_MW='0x50E32e236bfE4d514f786C9bC80061637dd5AF98' - -# Contract addresses for the sim-client -OP_DISPATCHER_SIM="0x6C9427E8d770Ad9e5a493D201280Cc178125CEc0" -BASE_DISPATCHER_SIM="0x0dE926fE2001B2c96e9cA6b79089CEB276325E9F" - -OP_UC_MW_SIM='0xC3318ce027C560B559b09b1aA9cA4FEBDDF252F5' -BASE_UC_MW_SIM='0x5031fb609569b67608Ffb9e224754bb317f174cD' - -# Configuration file the scripts will use, defaulting to config.json when not set -CONFIG_PATH='config.json' \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 565cd0ab..7c312a16 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/polymer-registry-poc"] + path = lib/polymer-registry-poc + url = https://github.com/tmsdkeys/polymer-registry-poc diff --git a/Justfile b/Justfile index 63ddc354..c3d55c4e 100644 --- a/Justfile +++ b/Justfile @@ -4,6 +4,12 @@ install: npm install forge install --shallow +# Build config file at location specified in the .env file +# Usage: just build-config +build-config SOURCE DESTINATION: + echo "Building config file..." + node utils/buildConfig.js {{SOURCE}} {{DESTINATION}} + # Compile contracts using the specified compiler or default to Hardhat # The compiler argument is optional; if not provided, it defaults to "hardhat". # Usage: just compile [compiler] @@ -62,13 +68,14 @@ send-packet SOURCE: echo "Sending a packet with the values from the config..." node scripts/private/_send-packet-config.js {{SOURCE}} +# DEPRECATED: Use single config file per client type # Switch between the sim client and the client with proofs # Usage: just switch-client -switch-client: - echo "Switching between sim client and client with proofs..." - npx hardhat run scripts/private/_update-vibc-address.js --network optimism - npx hardhat run scripts/private/_update-vibc-address.js --network base - node scripts/private/_switch-clients.js +# switch-client: +# echo "Switching between sim client and client with proofs..." +# npx hardhat run scripts/private/_update-vibc-address.js --network optimism +# npx hardhat run scripts/private/_update-vibc-address.js --network base +# node scripts/private/_switch-clients.js # Run the full E2E flow by setting the contracts, deploying them, creating a channel, and sending a packet # Usage: just do-it diff --git a/README.md b/README.md index ba5cd4f1..5fb9a606 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,26 @@ Additionally Hardhat will be installed as a dev dependency with some useful plug > Note: In case you're experiencing issues with dependencies using the `just install` recipe, check that all prerequisites are correctly installed. If issues persist with forge, try to do the individual dependency installations... -## ⚙️ Set up your environment variables +## ⚙️ Set up your environment and configuration + +The idea is to ensure that most configuration to add Polymer's vIBC is added as custom data in the configuration file of your development environment, e.g. Hardhat or Foundry. (Note that at the time of writing, only Hardhat is fully supported). + +Make sure to add network information to the Hardhat configuration for all supported networks you're interested in building on, following this schema: +```javascript +networks: { + // for OP testnet + optimism: { + url: 'https://sepolia.optimism.io', + alchemyRPC: `https://opt-sepolia.g.alchemy.com/v2/${process.env.OP_ALCHEMY_API_KEY}`, + accounts: [process.env.PRIVATE_KEY_1], + chainId: 11155420, + } +} +``` + +Especially make sure the chain ID is added as it will be used to fetch the correct data from the Polymer registry by ID, while you can locally refer to the chain as the name you've specified in the Hardhat config. + +### Environment variables Convert the `.env.example` file into an `.env` file. This will ignore the file for future git commits as well as expose the environment variables. Add your private keys and update the other values if you want to customize (advanced usage feature). @@ -55,17 +74,23 @@ This will enable you to sign transactions with your private key(s). If not added The configuration file is where all important data is stored for the just commands and automation. We strive to make direct interaction with the config file as little as possible. -By default the configuration file is stored at root as `config.json`. +By default the configuration file is stored in the config folder as `config.json`. -However, it is recommended to split up different contracts/projects in the same repo into different config file in case you want to switch between them. +> 💡 However, it is recommended to split up different contracts/projects in the same repo into different config files in case you want to switch between them. -Store alternate config files in the /config directory and set -```sh -# .env file -CONFIG_PATH='config/alt-config.json' +Store alternate config files in the /config directory and set the path in the Hardhat coniguration file: +```javascript + // path to configuration file the scripts will use for Polymer's vibc, defaulting to config/config.json when not set + vibcConfigPath: 'config/alt-config.json', ``` to use a different config file. +Contrary to previous version, you have to build the default configuration file by specifying the networks (from the Hardhat config) you want it to include: +```sh +# Usage: just build-config SOURCE DESTINATION +just build-config optimism base +``` + ### Obtaining testnet ETH The account associated with your private key must have both Base Sepolia and Optimism Sepolia ETH. To obtain the testnet ETH visit: @@ -240,56 +265,19 @@ to send a packet over a channel (script looks at the config's isUniversal flag t ## Verify, don't trust -As a starter value, the sim-client is used to improve latency. **The sim-client is useful for iterative development and testing BUT also insecure as it involves no proofs**. Make sure to move to the client **with proofs** by running another just command... +As a starter value, the sim-client is used to improve latency. **The sim-client is useful for iterative development and testing BUT also insecure as it involves no proofs**. Make sure to move to the client **with proofs** by setting the `proofsEnabled` flag in the config file to true: -```bash -# Usage: just switch-client -just switch-client -``` - -This will use the op-stack client with proofs, making sure that the relayer is proving what is being submitted every step along the way, ensuring there's no trust assumption on the relayer. - -An overview of the different clients can be found in `ibc.json`: ```json +// In config/proof-config.json { - "optimism": { - "sim-client": { - "canonConnFrom": "connection-0", - "canonConnTo": "connection-1", - "universalChannel": "channel-10" - }, - "op-client": { - "canonConnFrom": "connection-8", - "canonConnTo": "connection-9", - "universalChannel": "channel-16" - } - }, - "base": { - "sim-client" : { - "canonConnFrom": "connection-4", - "canonConnTo": "connection-5", - "universalChannel": "channel-11" - }, - "op-client": { - "canonConnFrom": "connection-10", - "canonConnTo": "connection-11", - "universalChannel": "channel-17" - } - } + ..., + "proofsEnabled": true, + ... } ``` -## 🦾 Advanced usage - -For advanced users, there's multiple custimizations to follow. These includes configuring the config.json manually and/or running the scripts without using just. -For example, the last action to send a packet on a universal channel could be executed with this command: - -```bash -npx hardhat run scripts/send-universal-packet.js --network base -``` - -To send a universal packet from the contract specified in the config.sendUniversalPacket field in the config. +This will use the op-stack client with proofs, making sure that the relayer is proving what is being submitted every step along the way, ensuring there's no trust assumption on the relayer. ## 🤝 Contributing diff --git a/config/alt-config.json b/config/alt-config.json deleted file mode 100644 index 9249c32b..00000000 --- a/config/alt-config.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "proofsEnabled": false, - "deploy": { - "optimism": "XCounterUC", - "base": "XCounterUC" - }, - "isUniversal": true, - "createChannel": { - "srcChain": "optimism", - "srcAddr": "0x1234567890AbCdEf1234567890aBcDeF12345678", - "dstChain": "base", - "dstAddr": "0x1234567890AbCdEf1234567890aBcDeF12345678", - "version": "1.0", - "ordering": 0, - "fees": false - }, - "sendPacket": { - "optimism": { - "portAddr": "0x1234567890abcdef1234567890abcdef12345678", - "channelId": "channel-n", - "timeout": 36000 - }, - "base": { - "portAddr": "0x1234567890abcdef1234567890abcdef12345678", - "channelId": "channel-n", - "timeout": 36000 - } - }, - "sendUniversalPacket": { - "optimism": { - "portAddr": "0x1234567890abcdef1234567890abcdef12345678", - "channelId": "channel-x", - "timeout": 36000 - }, - "base": { - "portAddr": "0x1234567890abcdef1234567890abcdef12345678", - "channelId": "channel-y", - "timeout": 36000 - } - }, - "backup": {} -} \ No newline at end of file diff --git a/config.json b/config/config.json similarity index 78% rename from config.json rename to config/config.json index 9249c32b..1713f32e 100644 --- a/config.json +++ b/config/config.json @@ -1,14 +1,14 @@ { "proofsEnabled": false, + "isUniversal": true, "deploy": { - "optimism": "XCounterUC", - "base": "XCounterUC" + "optimism": "", + "molten": "" }, - "isUniversal": true, "createChannel": { - "srcChain": "optimism", + "srcChain": "", "srcAddr": "0x1234567890AbCdEf1234567890aBcDeF12345678", - "dstChain": "base", + "dstChain": "", "dstAddr": "0x1234567890AbCdEf1234567890aBcDeF12345678", "version": "1.0", "ordering": 0, @@ -20,7 +20,7 @@ "channelId": "channel-n", "timeout": 36000 }, - "base": { + "molten": { "portAddr": "0x1234567890abcdef1234567890abcdef12345678", "channelId": "channel-n", "timeout": 36000 @@ -29,14 +29,13 @@ "sendUniversalPacket": { "optimism": { "portAddr": "0x1234567890abcdef1234567890abcdef12345678", - "channelId": "channel-x", + "channelId": "channel-n", "timeout": 36000 }, - "base": { + "molten": { "portAddr": "0x1234567890abcdef1234567890abcdef12345678", - "channelId": "channel-y", + "channelId": "channel-n", "timeout": 36000 } - }, - "backup": {} + } } \ No newline at end of file diff --git a/contracts/XCounter.sol b/contracts/XCounter.sol index 33ff2c1e..6b81ceb9 100644 --- a/contracts/XCounter.sol +++ b/contracts/XCounter.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.9; -import './base/CustomChanIbcApp.sol'; +import "./base/CustomChanIbcApp.sol"; contract XCounter is CustomChanIbcApp { // app specific state @@ -28,7 +28,6 @@ contract XCounter is CustomChanIbcApp { * @param channelId The ID of the channel (locally) to send the packet to. * @param timeoutSeconds The timeout in seconds (relative). */ - function sendPacket( bytes32 channelId, uint64 timeoutSeconds) external { // incrementing counter on source chain increment(); @@ -46,14 +45,14 @@ contract XCounter is CustomChanIbcApp { /** * @dev Packet lifecycle callback that implements packet receipt logic and returns and acknowledgement packet. * MUST be overriden by the inheriting contract. - * * @param packet the IBC packet encoded by the source and relayed by the relayer. */ function onRecvPacket(IbcPacket memory packet) external override onlyIbcDispatcher returns (AckPacket memory ackPacket) { recvedPackets.push(packet); + // decoding the caller address from the packet data address _caller = abi.decode(packet.data, (address)); + // updating the counterMap with the caller address and incrementing the counter counterMap[packet.sequence] = _caller; - increment(); return AckPacket(true, abi.encode(counter)); @@ -62,24 +61,22 @@ contract XCounter is CustomChanIbcApp { /** * @dev Packet lifecycle callback that implements packet acknowledgment logic. * MUST be overriden by the inheriting contract. - * * @param ack the acknowledgment packet encoded by the destination and relayed by the relayer. */ function onAcknowledgementPacket(IbcPacket calldata, AckPacket calldata ack) external override onlyIbcDispatcher { ackPackets.push(ack); - + // decoding the counter from the acknowledgment packet (uint64 _counter) = abi.decode(ack.data, (uint64)); - - if (_counter != counter) { - resetCounter(); - } + // resetting the counter if the counter in the acknowledgment packet is different from the local counter + if (_counter != counter) { + resetCounter(); + } } /** * @dev Packet lifecycle callback that implements packet receipt logic and return and acknowledgement packet. * MUST be overriden by the inheriting contract. * NOT SUPPORTED YET - * * @param packet the IBC packet encoded by the counterparty and relayed by the relayer */ function onTimeoutPacket(IbcPacket calldata packet) external override onlyIbcDispatcher { diff --git a/contracts/XCounterUC.sol b/contracts/XCounterUC.sol index fa8b52ce..2d970ca9 100644 --- a/contracts/XCounterUC.sol +++ b/contracts/XCounterUC.sol @@ -42,7 +42,6 @@ contract XCounterUC is UniversalChanIbcApp { /** * @dev Packet lifecycle callback that implements packet receipt logic and returns and acknowledgement packet. * MUST be overriden by the inheriting contract. - * * @param channelId the ID of the channel (locally) the packet was received on. * @param packet the Universal packet encoded by the source and relayed by the relayer. */ @@ -53,10 +52,10 @@ contract XCounterUC is UniversalChanIbcApp { returns (AckPacket memory ackPacket) { recvedPackets.push(UcPacketWithChannel(channelId, packet)); - + // decode the packet data (address payload, uint64 c) = abi.decode(packet.appData, (address, uint64)); + // update the counterMap with the caller address and increment the counter counterMap[c] = payload; - increment(); return AckPacket(true, abi.encode(counter)); @@ -65,7 +64,6 @@ contract XCounterUC is UniversalChanIbcApp { /** * @dev Packet lifecycle callback that implements packet acknowledgment logic. * MUST be overriden by the inheriting contract. - * * @param channelId the ID of the channel (locally) the ack was received on. * @param packet the Universal packet encoded by the source and relayed by the relayer. * @param ack the acknowledgment packet encoded by the destination and relayed by the relayer. @@ -76,10 +74,9 @@ contract XCounterUC is UniversalChanIbcApp { onlyIbcMw { ackPackets.push(UcAckWithChannel(channelId, packet, ack)); - // decode the counter from the ack packet (uint64 _counter) = abi.decode(ack.data, (uint64)); - + // reset the counter if the counter in the ack packet is different from the local counter if (_counter != counter) { resetCounter(); } @@ -89,7 +86,6 @@ contract XCounterUC is UniversalChanIbcApp { * @dev Packet lifecycle callback that implements packet receipt logic and return and acknowledgement packet. * MUST be overriden by the inheriting contract. * NOT SUPPORTED YET - * * @param channelId the ID of the channel (locally) the timeout was submitted on. * @param packet the Universal packet encoded by the counterparty and relayed by the relayer */ diff --git a/contracts/base/CustomChanIbcApp.sol b/contracts/base/CustomChanIbcApp.sol index 4eeac1c3..873e29c4 100644 --- a/contracts/base/CustomChanIbcApp.sol +++ b/contracts/base/CustomChanIbcApp.sol @@ -2,14 +2,19 @@ pragma solidity ^0.8.9; -import '@open-ibc/vibc-core-smart-contracts/contracts/libs/Ibc.sol'; -import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcReceiver.sol'; -import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcDispatcher.sol'; -import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/ProofVerifier.sol'; +import { IbcPacket, AckPacket, ChannelOrder, CounterParty, invalidCounterPartyPortId } from "@open-ibc/vibc-core-smart-contracts/contracts/libs/Ibc.sol"; +import { IbcReceiverBase, IbcReceiver, IbcChannelReceiver } from "@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcReceiver.sol"; +import { IbcDispatcher } from "@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcDispatcher.sol"; +import { Ics23Proof } from "@open-ibc/vibc-core-smart-contracts/contracts/interfaces/ProofVerifier.sol"; // CustomChanIbcApp is a contract that can be used as a base contract // for IBC-enabled contracts that send packets over a custom IBC channel. contract CustomChanIbcApp is IbcReceiverBase, IbcReceiver { + struct ChannelMapping { + bytes32 channelId; + bytes32 cpChannelId; + } + // received packet as chain B IbcPacket[] public recvedPackets; // received ack packet as chain A @@ -17,16 +22,11 @@ contract CustomChanIbcApp is IbcReceiverBase, IbcReceiver { // received timeout packet as chain A IbcPacket[] public timeoutPackets; - 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']; + string[] public supportedVersions = ["1.0"]; constructor(IbcDispatcher _dispatcher) IbcReceiverBase(_dispatcher) {} @@ -34,14 +34,14 @@ contract CustomChanIbcApp is IbcReceiverBase, IbcReceiver { dispatcher = _dispatcher; } - function getConnectedChannels() external view returns (ChannelMapping[] memory) { - return connectedChannels; - } - function updateSupportedVersions(string[] memory _supportedVersions) external onlyOwner { supportedVersions = _supportedVersions; } + function getConnectedChannels() external view returns (ChannelMapping[] memory) { + return connectedChannels; + } + /** * @dev Implement a function to send a packet that calls the dispatcher.sendPacket function * It has the following function handle: @@ -57,7 +57,7 @@ contract CustomChanIbcApp is IbcReceiverBase, IbcReceiver { function onRecvPacket(IbcPacket memory packet) external virtual onlyIbcDispatcher returns (AckPacket memory ackPacket) { recvedPackets.push(packet); // do logic - return AckPacket(true, abi.encodePacked('{ "account": "account", "reply": "got the message" }')); + return AckPacket(true, abi.encodePacked("{ 'account': 'account', 'reply': 'got the message' }")); } /** @@ -130,7 +130,7 @@ contract CustomChanIbcApp is IbcReceiverBase, IbcReceiver { * In both cases, the selected version should be in the supported versions list. */ bool foundVersion = false; - selectedVersion = keccak256(abi.encodePacked(version)) == keccak256(abi.encodePacked('')) + selectedVersion = keccak256(abi.encodePacked(version)) == keccak256(abi.encodePacked("")) ? counterparty.version : version; for (uint256 i = 0; i < supportedVersions.length; i++) { @@ -139,12 +139,12 @@ contract CustomChanIbcApp is IbcReceiverBase, IbcReceiver { break; } } - require(foundVersion, 'Unsupported version'); + require(foundVersion, "Unsupported version"); // if counterpartyVersion is not empty, then it must be the same foundVersion - if (keccak256(abi.encodePacked(counterparty.version)) != keccak256(abi.encodePacked(''))) { + if (keccak256(abi.encodePacked(counterparty.version)) != keccak256(abi.encodePacked(""))) { require( keccak256(abi.encodePacked(counterparty.version)) == keccak256(abi.encodePacked(selectedVersion)), - 'Version mismatch' + "Version mismatch" ); } @@ -166,7 +166,7 @@ contract CustomChanIbcApp is IbcReceiverBase, IbcReceiver { break; } } - require(foundVersion, 'Unsupported version'); + require(foundVersion, "Unsupported version"); // do logic @@ -190,7 +190,7 @@ contract CustomChanIbcApp is IbcReceiverBase, IbcReceiver { break; } } - require(channelFound, 'Channel not found'); + require(channelFound, "Channel not found"); // do logic } diff --git a/contracts/base/GeneralMiddleware.sol b/contracts/base/GeneralMiddleware.sol index 82e3852f..6fe11d41 100644 --- a/contracts/base/GeneralMiddleware.sol +++ b/contracts/base/GeneralMiddleware.sol @@ -2,10 +2,15 @@ pragma solidity ^0.8.9; -import '@open-ibc/vibc-core-smart-contracts/contracts/libs/Ibc.sol'; -import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcReceiver.sol'; -import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcDispatcher.sol'; -import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcMiddleware.sol'; +import { IbcUtils, UniversalPacket, AckPacket } from "@open-ibc/vibc-core-smart-contracts/contracts/libs/Ibc.sol"; +import { + IbcUniversalPacketReceiver, + IbcMwPacketSender, + IbcMwPacketReceiver, + IbcMwUser, + IbcMwEventsEmitter, + IbcMiddleware +} from "@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcMiddleware.sol"; contract GeneralMiddleware is IbcMwUser, IbcMiddleware, IbcMwEventsEmitter { /** @@ -21,7 +26,6 @@ contract GeneralMiddleware is IbcMwUser, IbcMiddleware, IbcMwEventsEmitter { /** * @param _middleware The middleware contract address this contract sends packets to and receives packets from. */ - constructor(uint256 mwId, address _middleware) IbcMwUser(_middleware) { MW_ID = mwId; } diff --git a/contracts/base/UniversalChanIbcApp.sol b/contracts/base/UniversalChanIbcApp.sol index 73ab3220..b6df3369 100644 --- a/contracts/base/UniversalChanIbcApp.sol +++ b/contracts/base/UniversalChanIbcApp.sol @@ -2,10 +2,8 @@ pragma solidity ^0.8.9; -import '@open-ibc/vibc-core-smart-contracts/contracts/libs/Ibc.sol'; -import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcReceiver.sol'; -import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcDispatcher.sol'; -import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcMiddleware.sol'; +import { UniversalPacket, AckPacket, IbcUtils } from "@open-ibc/vibc-core-smart-contracts/contracts/libs/Ibc.sol"; +import { IbcMwUser, IbcUniversalPacketReceiver, IbcUniversalPacketSender } from "@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcMiddleware.sol"; // UniversalChanIbcApp is a contract that can be used as a base contract // for IBC-enabled contracts that send packets over the universal channel. @@ -57,7 +55,7 @@ contract UniversalChanIbcApp is IbcMwUser, IbcUniversalPacketReceiver { // 2. do logic // 3. encode the ack packet (encoding format should be agreed between the two applications) // below is an example, the actual ackpacket data should be implemented by the contract developer - return AckPacket(true, abi.encodePacked(address(this), IbcUtils.toAddress(packet.srcPortAddr), 'ack-', packet.appData)); + return AckPacket(true, abi.encodePacked(address(this), IbcUtils.toAddress(packet.srcPortAddr), "ack-", packet.appData)); } /** diff --git a/hardhat.config.js b/hardhat.config.js index c95c11d0..065096fb 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -3,6 +3,8 @@ require('@nomicfoundation/hardhat-foundry'); require('dotenv').config(); +const polyConfig = require('./lib/polymer-registry-poc/dist/output.json'); + /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: { @@ -18,12 +20,21 @@ module.exports = { // for Base testnet base: { url: 'https://sepolia.base.org', + alchemyRPC: `https://base-sepolia.g.alchemy.com/v2/${process.env.BASE_ALCHEMY_API_KEY}`, accounts: [process.env.PRIVATE_KEY_1], + chainId: 84532, }, // for OP testnet optimism: { url: 'https://sepolia.optimism.io', + alchemyRPC: `https://opt-sepolia.g.alchemy.com/v2/${process.env.OP_ALCHEMY_API_KEY}`, + accounts: [process.env.PRIVATE_KEY_1], + chainId: 11155420, + }, + molten: { + url: 'https://sepolia.molten.io', accounts: [process.env.PRIVATE_KEY_1], + chainId: 49483, }, }, defaultNetwork: 'optimism', @@ -35,16 +46,13 @@ module.exports = { libraries: './lib', }, etherscan: { - apiKey: { - optimism: process.env.OP_BLOCKSCOUT_API_KEY, - base: process.env.BASE_BLOCKSCOUT_API_KEY, - }, customChains: [ { network: 'base', chainId: 84532, urls: { apiURL: 'https://base-sepolia.blockscout.com/api', + apiKey: process.env.BASE_BLOCKSCOUT_API_KEY, browserURL: 'https://base-sepolia.blockscout.com', }, }, @@ -53,9 +61,12 @@ module.exports = { chainId: 11155420, urls: { apiURL: 'https://optimism-sepolia.blockscout.com/api', + apiKey: process.env.OP_BLOCKSCOUT_API_KEY, browserURL: 'https://optimism-sepolia.blockscout.com', }, }, ], }, + polymer: polyConfig, + vibcConfigPath: 'config/config.json', // path to configuration file the scripts will use for Polymer's vibc, defaulting to config/config.json when not set }; diff --git a/ibc.json b/ibc.json deleted file mode 100644 index 030e8d42..00000000 --- a/ibc.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "optimism": { - "sim-client": { - "canonConnFrom": "connection-0", - "canonConnTo": "connection-1", - "universalChannel": "channel-10" - }, - "op-client": { - "canonConnFrom": "connection-8", - "canonConnTo": "connection-9", - "universalChannel": "channel-16" - } - }, - "base": { - "sim-client" : { - "canonConnFrom": "connection-4", - "canonConnTo": "connection-5", - "universalChannel": "channel-11" - }, - "op-client": { - "canonConnFrom": "connection-10", - "canonConnTo": "connection-11", - "universalChannel": "channel-17" - } - } -} \ No newline at end of file diff --git a/lib/polymer-registry-poc b/lib/polymer-registry-poc new file mode 160000 index 00000000..7df273b4 --- /dev/null +++ b/lib/polymer-registry-poc @@ -0,0 +1 @@ +Subproject commit 7df273b40bdf67037dca22d7ffa3976931cd614e diff --git a/scripts/private/_create-channel-config.js b/scripts/private/_create-channel-config.js index 953122ea..72577921 100644 --- a/scripts/private/_create-channel-config.js +++ b/scripts/private/_create-channel-config.js @@ -1,16 +1,20 @@ const { exec } = require('child_process'); -const { getConfigPath, updateConfigCreateChannel, getWhitelistedNetworks } = require('./_helpers.js'); +const { getConfigPath, updateConfigCreateChannel, getWhitelistedNetworks, convertNetworkToChainId } = require('./_helpers.js'); const { setupIbcChannelEventListener } = require('./_events.js'); // Function to run the deploy script and capture output -function createChannelAndCapture() { - const config = require(getConfigPath()); - const srcChain = config.createChannel.srcChain; - +function createChannelAndCapture(config, srcChain, dstChain) { // Check if the source chain from user input is whitelisted const allowedNetworks = getWhitelistedNetworks(); - if (!allowedNetworks.includes(srcChain)) { - console.error('❌ Invalid network name'); + const srcChainId = convertNetworkToChainId(srcChain); + const dstChainId = convertNetworkToChainId(dstChain); + + if (!allowedNetworks.includes(`${srcChainId}`)) { + console.error('❌ Invalid network name: Please provide a valid source chain'); + return; + } + if (!allowedNetworks.includes(`${dstChainId}`)) { + console.error('❌ Invalid network name: Please provide a valid destination chain'); return; } exec(`npx hardhat run scripts/private/_create-channel.js --network ${srcChain}`, (error, stdout) => { @@ -51,8 +55,11 @@ function createChannelAndCapture() { } async function main() { - await setupIbcChannelEventListener(); - createChannelAndCapture(); + const config = require(getConfigPath()); + const srcChain = config.createChannel.srcChain; + const dstChain = config.createChannel.dstChain; + await setupIbcChannelEventListener(srcChain, dstChain); + createChannelAndCapture(config, srcChain, dstChain); } main().catch((error) => { diff --git a/scripts/private/_create-channel.js b/scripts/private/_create-channel.js index 01d25f29..deb478e2 100644 --- a/scripts/private/_create-channel.js +++ b/scripts/private/_create-channel.js @@ -7,7 +7,7 @@ const hre = require('hardhat'); const { getConfigPath, addressToPortId } = require('./_helpers'); const { getIbcApp } = require('./_vibc-helpers.js'); -const ibcConfig = require('../../ibc.json'); +const polyConfig = hre.config.polymer; function createDummyProof() { return { @@ -34,14 +34,17 @@ async function main() { const config = require(getConfigPath()); const chanConfig = config.createChannel; const networkName = hre.network.name; + const chainId = hre.config.networks[`${networkName}`].chainId; // Get the contract type from the config and get the contract const ibcApp = await getIbcApp(networkName); const connectedChannelsBefore = await ibcApp.getConnectedChannels(); // Prepare the arguments to create the channel - const connHop1 = ibcConfig[chanConfig.srcChain][`${config.proofsEnabled ? 'op-client' : 'sim-client'}`].canonConnFrom; - const connHop2 = ibcConfig[chanConfig.dstChain][`${config.proofsEnabled ? 'op-client' : 'sim-client'}`].canonConnTo; + // TODO: Update to allow dynamic choice of client type + const connHop1 = polyConfig[`${chainId}`]['clients'][`${config.proofsEnabled ? 'op-client' : 'sim-client'}`].canonConnFrom; + const connHop2 = polyConfig[`${chainId}`]['clients'][`${config.proofsEnabled ? 'op-client' : 'sim-client'}`].canonConnTo; + const srcPortId = addressToPortId(`polyibc.${chanConfig.srcChain}`, chanConfig.srcAddr); const dstPortId = addressToPortId(`polyibc.${chanConfig.dstChain}`, chanConfig.dstAddr); diff --git a/scripts/private/_deploy-config.js b/scripts/private/_deploy-config.js index f2c49a53..8d4bba93 100644 --- a/scripts/private/_deploy-config.js +++ b/scripts/private/_deploy-config.js @@ -1,5 +1,5 @@ const { exec } = require('child_process'); -const { updateConfigDeploy, getWhitelistedNetworks } = require('./_helpers'); +const { updateConfigDeploy, getWhitelistedNetworks, convertNetworkToChainId } = require('./_helpers'); // Run script with source and destination networks as arguments // Example: @@ -15,7 +15,9 @@ if (!source || !destination) { // Function to run the deploy script and capture output function deployAndCapture(network, isSource) { const allowedNetworks = getWhitelistedNetworks(); - if (!allowedNetworks.includes(network)) { + const chainId = convertNetworkToChainId(network); + + if (!allowedNetworks.includes(`${chainId}`)) { console.error('Invalid network. Please provide a valid network as an argument.'); return; } diff --git a/scripts/private/_events.js b/scripts/private/_events.js index 1357a39f..e4016b02 100644 --- a/scripts/private/_events.js +++ b/scripts/private/_events.js @@ -1,22 +1,20 @@ const hre = require('hardhat'); -const { areAddressesEqual, getConfigPath } = require('./_helpers.js'); +const { areAddressesEqual, getConfigPath, getExplorerDataFromConfig } = require('./_helpers.js'); const { getDispatcher, getUcHandlerAddress } = require('./_vibc-helpers.js'); -const explorerOpUrl = 'https://optimism-sepolia.blockscout.com/'; -const explorerBaseUrl = 'https://base-sepolia.blockscout.com/'; - function filterChannelEvents(portAddress) { const config = require(getConfigPath()); return areAddressesEqual(portAddress, config.createChannel['srcAddr']) || areAddressesEqual(portAddress, config.createChannel['dstAddr']); } function listenForIbcChannelEvents(network, source, dispatcher) { - const explorerUrl = network === 'optimism' ? explorerOpUrl : explorerBaseUrl; + const explorerUrl = getExplorerDataFromConfig(network).browserURL; console.log(`👂 Listening for IBC channel events on ${network}...`); + dispatcher.on('OpenIbcChannel', (portAddress, version, ordering, feeEnabled, connectionHops, counterparytPortId, counterpartyChannelId, event) => { const txHash = event.log.transactionHash; const counterpartyChannelIdString = hre.ethers.decodeBytes32String(counterpartyChannelId); - const url = `${explorerUrl}tx/${txHash}`; + const url = `${explorerUrl}/tx/${txHash}`; if (filterChannelEvents(portAddress)) { console.log(` @@ -56,7 +54,7 @@ function listenForIbcChannelEvents(network, source, dispatcher) { dispatcher.on('ConnectIbcChannel', (portAddress, channelId, event) => { const txHash = event.log.transactionHash; const channelIdString = hre.ethers.decodeBytes32String(channelId); - const url = `${explorerUrl}tx/${txHash}`; + const url = `${explorerUrl}/tx/${txHash}`; if (filterChannelEvents(portAddress)) { console.log(` -------------------------------------------`); @@ -89,7 +87,7 @@ function listenForIbcChannelEvents(network, source, dispatcher) { dispatcher.on('CloseIbcChannel', (portAddress, channelId, event) => { const txHash = event.log.transactionHash; const channelIdString = hre.ethers.decodeBytes32String(channelId); - const url = `${explorerUrl}tx/${txHash}`; + const url = `${explorerUrl}/tx/${txHash}`; if (filterChannelEvents(portAddress)) { console.log(` ------------------------------------------- @@ -117,13 +115,13 @@ function filterPacketEvents(portAddress, network) { } function listenForIbcPacketEvents(network, dispatcher) { - const explorerUrl = network === 'optimism' ? explorerOpUrl : explorerBaseUrl; + const explorerUrl = getExplorerDataFromConfig(network).browserURL; console.log(`👂 Listening for IBC packet events on ${network}...`); dispatcher.on('SendPacket', (sourcePortAddress, sourceChannelId, packet, sequence, timeoutTimestamp, event) => { const txHash = event.log.transactionHash; const sourceChannelIdString = hre.ethers.decodeBytes32String(sourceChannelId); - const url = `${explorerUrl}tx/${txHash}`; + const url = `${explorerUrl}/tx/${txHash}`; if (filterPacketEvents(sourcePortAddress, network)) { console.log(` @@ -147,7 +145,7 @@ function listenForIbcPacketEvents(network, dispatcher) { dispatcher.on('RecvPacket', (destPortAddress, destChannelId, sequence, event) => { const txHash = event.log.transactionHash; const destChannelIdString = hre.ethers.decodeBytes32String(destChannelId); - const url = `${explorerUrl}tx/${txHash}`; + const url = `${explorerUrl}/tx/${txHash}`; if (filterPacketEvents(destPortAddress, network)) { console.log(` @@ -170,7 +168,7 @@ function listenForIbcPacketEvents(network, dispatcher) { dispatcher.on('WriteAckPacket', (writerPortAddress, writerChannelId, sequence, ackPacket, event) => { const txHash = event.log.transactionHash; const writerChannelIdString = hre.ethers.decodeBytes32String(writerChannelId); - const url = `${explorerUrl}tx/${txHash}`; + const url = `${explorerUrl}/tx/${txHash}`; if (filterPacketEvents(writerPortAddress, network)) { console.log(` ------------------------------------------- @@ -193,7 +191,7 @@ function listenForIbcPacketEvents(network, dispatcher) { dispatcher.on('Acknowledgement', (sourcePortAddress, sourceChannelId, sequence, event) => { const txHash = event.log.transactionHash; const sourceChannelIdString = hre.ethers.decodeBytes32String(sourceChannelId); - const url = `${explorerUrl}tx/${txHash}`; + const url = `${explorerUrl}/tx/${txHash}`; if (filterPacketEvents(sourcePortAddress, network)) { console.log(` ------------------------------------------- @@ -213,26 +211,22 @@ function listenForIbcPacketEvents(network, dispatcher) { }); } -async function setupIbcPacketEventListener() { +async function setupIbcPacketEventListener(src, dst) { console.log('🔊 Setting up IBC packet event listener...'); // Get the dispatchers for both source and destination to listen for IBC packet events - const opDispatcher = await getDispatcher('optimism'); - const baseDispatcher = await getDispatcher('base'); - listenForIbcPacketEvents('optimism', opDispatcher); - listenForIbcPacketEvents('base', baseDispatcher); + const srcDispatcher = await getDispatcher(src); + const dstDispatcher = await getDispatcher(dst); + listenForIbcPacketEvents(src, srcDispatcher); + listenForIbcPacketEvents(dst, dstDispatcher); } -async function setupIbcChannelEventListener() { +async function setupIbcChannelEventListener(src, dst) { console.log('🔊 Setting up IBC channel event listener...'); - const config = require(getConfigPath()); - const opIsSource = config.createChannel.srcChain === 'optimism'; - const baseIsSource = config.createChannel.srcChain === 'base'; - // Get the dispatchers for both source and destination to listen for IBC packet events - const opDispatcher = await getDispatcher('optimism'); - const baseDispatcher = await getDispatcher('base'); - listenForIbcChannelEvents('optimism', opIsSource, opDispatcher); - listenForIbcChannelEvents('base', baseIsSource, baseDispatcher); + const srcDispatcher = await getDispatcher(src); + const dstDispatcher = await getDispatcher(dst); + listenForIbcChannelEvents(src, true, srcDispatcher); + listenForIbcChannelEvents(dst, false, dstDispatcher); } module.exports = { diff --git a/scripts/private/_helpers.js b/scripts/private/_helpers.js index 56521941..1afa97f7 100644 --- a/scripts/private/_helpers.js +++ b/scripts/private/_helpers.js @@ -1,19 +1,43 @@ const fs = require('fs'); const axios = require('axios'); const hre = require('hardhat'); -const ibcConfig = require('../../ibc.json'); + +const polyConfig = hre.config.polymer; // Function to get the path to the configuration file function getConfigPath() { const path = require('path'); - const configRelativePath = process.env.CONFIG_PATH ? process.env.CONFIG_PATH : 'config.json'; + const configRelativePath = hre.config.vibcConfigPath ? hre.config.vibcConfigPath : 'config/config.json'; // console.log(`📔 Using config file at ${configRelativePath}`); const configPath = path.join(__dirname, '../..', configRelativePath); return configPath; } +// Function to convert network name to chain ID, as specified in hardhat config file +function convertNetworkToChainId(network) { + const chainId = hre.config.networks[`${network}`].chainId; + if (!chainId) { + throw new Error('❌ Chain ID not found for network:', network); + } + return chainId; +} + +// Function that gets the explorer url and api url from HH config +function getNetworkDataFromConfig(network) { + const networks = hre.config.networks; + return networks[`${network}`]; +} + +// Function that gets the explorer url and api url from HH config +function getExplorerDataFromConfig(network) { + const customChains = hre.config.etherscan.customChains; + const chainInfo = customChains.find((chain) => chain.network === network); + return chainInfo.urls; +} + // Function to update config.json function updateConfigDeploy(network, address, isSource) { + const chainId = hre.config.networks[`${network}`].chainId; try { const configPath = getConfigPath(); const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); @@ -30,9 +54,10 @@ function updateConfigDeploy(network, address, isSource) { config['sendPacket'][`${network}`]['portAddr'] = address; } else if (config.isUniversal) { // When using the universal channel, we can skip channel creation and instead update the sendUniversalPacket field in the config + //TODO: update for multi-client selection const client = config.proofsEnabled ? 'op-client' : 'sim-client'; config['sendUniversalPacket'][`${network}`]['portAddr'] = address; - config['sendUniversalPacket'][`${network}`]['channelId'] = ibcConfig[`${network}`][`${client}`]['universalChannel']; + config['sendUniversalPacket'][`${network}`]['channelId'] = polyConfig[`${chainId}`]['clients'][`${client}`]['universalChannelId']; } // Write the updated config back to the file @@ -59,9 +84,9 @@ function updateConfigCreateChannel(network, channel, cpNetwork, cpChannel) { } } -async function fetchABI(explorerUrl, contractAddress) { +async function fetchABI(explorerApiUrl, contractAddress) { try { - const response = await axios.get(`${explorerUrl}api/v2/smart-contracts/${contractAddress}`); + const response = await axios.get(`${explorerApiUrl}/v2/smart-contracts/${contractAddress}`); if (response.status === 200) { const abi = response.data.abi; return abi; @@ -93,17 +118,21 @@ function areAddressesEqual(address1, address2) { // Helper function to convert an address to a port ID function addressToPortId(portPrefix, address) { const config = require(getConfigPath()); + // TODO: implement dynamic suffix for selected client const simAddOn = config.proofsEnabled ? '-proofs-1' : '-sim'; const suffix = address.slice(2); return `${portPrefix}${simAddOn}.${suffix}`; } function getWhitelistedNetworks() { - return Object.keys(ibcConfig); + return Object.keys(polyConfig); } module.exports = { getConfigPath, + convertNetworkToChainId, + getNetworkDataFromConfig, + getExplorerDataFromConfig, updateConfigDeploy, updateConfigCreateChannel, fetchABI, diff --git a/scripts/private/_sanity-check-custom.js b/scripts/private/_sanity-check-custom.js index bc852b06..51f0aedb 100644 --- a/scripts/private/_sanity-check-custom.js +++ b/scripts/private/_sanity-check-custom.js @@ -9,10 +9,13 @@ const { getConfigPath } = require('./_helpers'); const { areAddressesEqual } = require('./_helpers'); const { getIbcApp } = require('./_vibc-helpers'); +const polyConfig = hre.config.polymer; + async function main() { const configPath = getConfigPath(); const config = require(configPath); const networkName = hre.network.name; + const chainId = hre.config.networks[`${networkName}`].chainId; // Get the Dispatcher from your IBC enabled contract and compare it with the stored value in the .env file @@ -32,13 +35,11 @@ async function main() { let sanityCheck = false; let envDispatcherAddr; try { - if (networkName === 'optimism') { - envDispatcherAddr = config.proofsEnabled === true ? process.env.OP_DISPATCHER : process.env.OP_DISPATCHER_SIM; - sanityCheck = areAddressesEqual(dispatcherAddr, envDispatcherAddr); - } else if (networkName === 'base') { - envDispatcherAddr = config.proofsEnabled === true ? process.env.BASE_DISPATCHER : process.env.BASE_DISPATCHER_SIM; - sanityCheck = areAddressesEqual(dispatcherAddr, envDispatcherAddr); - } + envDispatcherAddr = + config.proofsEnabled === true + ? polyConfig[`${chainId}`]['clients']['op-client'].dispatcherAddr + : polyConfig[`${chainId}`]['clients']['sim-client'].dispatcherAddr; + sanityCheck = areAddressesEqual(dispatcherAddr, envDispatcherAddr); } catch (error) { console.log(`❌ Error comparing dispatcher addresses in .env file and IBC app: ${error}`); return; diff --git a/scripts/private/_sanity-check-universal.js b/scripts/private/_sanity-check-universal.js index 247687ad..5ad59342 100644 --- a/scripts/private/_sanity-check-universal.js +++ b/scripts/private/_sanity-check-universal.js @@ -8,9 +8,12 @@ const hre = require('hardhat'); const { getConfigPath, areAddressesEqual } = require('./_helpers.js'); const { getIbcApp, getUcHandler } = require('./_vibc-helpers.js'); +const polyConfig = hre.config.polymer; + async function main() { const config = require(getConfigPath()); const networkName = hre.network.name; + const chainId = hre.config.networks[`${networkName}`].chainId; // Get the Universal Channel Mw from your IBC enabled contract and comare it with the values in the .env file @@ -32,13 +35,12 @@ async function main() { let sanityCheck = false; let envUcHandlerAddr; try { - if (networkName === 'optimism') { - envUcHandlerAddr = config.proofsEnabled === true ? process.env.OP_UC_MW : process.env.OP_UC_MW_SIM; - sanityCheck = areAddressesEqual(ucHandlerAddr, envUcHandlerAddr); - } else if (networkName === 'base') { - envUcHandlerAddr = config.proofsEnabled === true ? process.env.BASE_UC_MW : process.env.BASE_UC_MW_SIM; - sanityCheck = areAddressesEqual(ucHandlerAddr, envUcHandlerAddr); - } + // TODO: update for multi-client selection + envUcHandlerAddr = + config.proofsEnabled === true + ? polyConfig[`${chainId}`]['clients']['op-client'].universalChannelAddr + : polyConfig[`${chainId}`]['clients']['sim-client'].universalChannelAddr; + sanityCheck = areAddressesEqual(ucHandlerAddr, envUcHandlerAddr); } catch (error) { console.log(`❌ Error comparing Universal Channel Mw addresses in .env file and IBC app: ${error}`); return; @@ -53,15 +55,11 @@ async function main() { try { ucHandler = await getUcHandler(networkName); dispatcherAddr = await ucHandler.dispatcher(); - if (networkName === 'optimism') { - envDispatcherAddr = config.proofsEnabled === true ? process.env.OP_DISPATCHER : process.env.OP_DISPATCHER_SIM; - sanityCheck = areAddressesEqual(dispatcherAddr, envDispatcherAddr); - } else if (networkName === 'base') { - envDispatcherAddr = config.proofsEnabled === true ? process.env.BASE_DISPATCHER : process.env.BASE_DISPATCHER_SIM; - sanityCheck = areAddressesEqual(dispatcherAddr, envDispatcherAddr); - } else { - sanityCheck = false; - } + envDispatcherAddr = + config.proofsEnabled === true + ? polyConfig[`${chainId}`]['clients']['op-client'].dispatcherAddr + : polyConfig[`${chainId}`]['clients']['sim-client'].dispatcherAddr; + sanityCheck = areAddressesEqual(dispatcherAddr, envDispatcherAddr); } catch (error) { console.log(`❌ Error getting dispatcher address from Universal Channel Mw or from config: ${error}`); return; diff --git a/scripts/private/_sanity-check.js b/scripts/private/_sanity-check.js index 16cea492..a6dbf519 100644 --- a/scripts/private/_sanity-check.js +++ b/scripts/private/_sanity-check.js @@ -3,8 +3,7 @@ const { exec } = require('child_process'); const { getConfigPath } = require('./_helpers.js'); -function runSanityCheck(network) { - const config = require(getConfigPath()); +function runSanityCheck(config, network) { const scriptSuffix = config.isUniversal ? 'universal' : 'custom'; exec(`npx hardhat run scripts/private/_sanity-check-${scriptSuffix}.js --network ${network}`, (error, stdout) => { @@ -16,6 +15,13 @@ function runSanityCheck(network) { }); } -// TODO: EXTEND THIS TO SUPPORT MULTIPLE NETWORKS -runSanityCheck('optimism'); -runSanityCheck('base'); +function main() { + const config = require(getConfigPath()); + const configChains = config.isUniversal ? Object.keys(config.sendUniversalPacket) : Object.keys(config.sendPacket); + + configChains.forEach((network) => { + runSanityCheck(config, network); + }); +} + +main(); diff --git a/scripts/private/_send-packet-config.js b/scripts/private/_send-packet-config.js index 22026091..6ecff8ca 100644 --- a/scripts/private/_send-packet-config.js +++ b/scripts/private/_send-packet-config.js @@ -1,5 +1,5 @@ const { exec } = require('child_process'); -const { getConfigPath, getWhitelistedNetworks } = require('./_helpers.js'); +const { getConfigPath, getWhitelistedNetworks, convertNetworkToChainId } = require('./_helpers.js'); const { setupIbcPacketEventListener } = require('./_events.js'); const source = process.argv[2]; @@ -25,16 +25,21 @@ function runSendPacketCommand(command) { async function runSendPacket(config) { // Check if the source chain from user input is whitelisted const allowedNetworks = getWhitelistedNetworks(); - if (!allowedNetworks.includes(source)) { + const srcChainId = convertNetworkToChainId(source); + if (!allowedNetworks.includes(`${srcChainId}`)) { console.error('❌ Please provide a valid source chain'); process.exit(1); } + const destination = config.isUniversal + ? Object.keys(config.sendUniversalPacket).find((chain) => chain !== source) + : Object.keys(config.sendPacket).find((chain) => chain !== source); + const script = config.isUniversal ? 'send-universal-packet.js' : 'send-packet.js'; const command = `npx hardhat run scripts/${script} --network ${source}`; try { - await setupIbcPacketEventListener(); + await setupIbcPacketEventListener(source, destination); await runSendPacketCommand(command); } catch (error) { console.error('❌ Error sending packet: ', error); diff --git a/scripts/private/_set-contracts-config.js b/scripts/private/_set-contracts-config.js index ad37544d..830c2ce8 100644 --- a/scripts/private/_set-contracts-config.js +++ b/scripts/private/_set-contracts-config.js @@ -2,7 +2,7 @@ // Example: // $ node set-contracts-config.js optimism XCounterUC true const fs = require('fs'); -const { getConfigPath } = require('./_helpers.js'); +const { getConfigPath, convertNetworkToChainId, getWhitelistedNetworks } = require('./_helpers.js'); if (process.argv.length < 5) { console.error('❌ Incorrect number of args. Usage: node set-contracts-config.js '); @@ -11,8 +11,13 @@ if (process.argv.length < 5) { const chain = process.argv[2]; const contractType = process.argv[3]; const universalBoolean = process.argv[4].trim().toLowerCase(); - -if (chain !== 'optimism' && chain !== 'base') { +try { + const allowedNetworks = getWhitelistedNetworks(); + const chainId = convertNetworkToChainId(chain); + if (!allowedNetworks.includes(`${chainId}`)) { + throw new Error('Invalid network. Please provide a valid network as an argument.'); + } +} catch (error) { console.error('❌ Incorrect chain value. Usage: node set-contracts-config.js '); process.exit(1); } diff --git a/scripts/private/_switch-clients.js b/scripts/private/_switch-clients.js index 4d5eee21..df6c1b8f 100644 --- a/scripts/private/_switch-clients.js +++ b/scripts/private/_switch-clients.js @@ -1,6 +1,9 @@ +// DEPRECATED: This script is no longer used in the current version of the SDK const fs = require('fs'); const { getConfigPath } = require('./_helpers.js'); -const ibcConfig = require('../../ibc.json'); + +const hhConfig = require('../../hardhat.config.js'); +const polyConfig = hhConfig.polymer; // Function to update config.json function flipConfig() { @@ -12,6 +15,8 @@ function flipConfig() { const tempConfig = { ...config }; const source = tempConfig['createChannel']['srcChain']; const destination = tempConfig['createChannel']['dstChain']; + const srcChainId = hhConfig.networks[`${source}`].chainId; + const dstChainId = hhConfig.networks[`${destination}`].chainId; // Below, we'll update the config object if (config.backup !== undefined && typeof config.backup === 'object' && Object.keys(config.backup).length > 0) { @@ -60,12 +65,12 @@ function flipConfig() { } // Update the universal channel values for new client - config['sendUniversalPacket']['optimism']['channelId'] = tempConfig.proofsEnabled - ? ibcConfig['optimism']['sim-client']['universalChannel'] - : ibcConfig['optimism']['op-client']['universalChannel']; - config['sendUniversalPacket']['base']['channelId'] = tempConfig.proofsEnabled - ? ibcConfig['base']['sim-client']['universalChannel'] - : ibcConfig['base']['op-client']['universalChannel']; + config['sendUniversalPacket'][`${source}`]['channelId'] = tempConfig.proofsEnabled + ? polyConfig[`${srcChainId}`]['clients']['op-client'].universalChannelId + : polyConfig[`${srcChainId}`]['clients']['sim-client'].universalChannelId; + config['sendUniversalPacket'][`${destination}`]['channelId'] = tempConfig.proofsEnabled + ? polyConfig[`${dstChainId}`]['clients']['op-client'].universalChannelId + : polyConfig[`${dstChainId}`]['clients']['sim-client'].universalChannelId; // Write a new backup object to the config config['backup'] = { diff --git a/scripts/private/_vibc-helpers.js b/scripts/private/_vibc-helpers.js index 173d9a2a..619b973c 100644 --- a/scripts/private/_vibc-helpers.js +++ b/scripts/private/_vibc-helpers.js @@ -1,11 +1,8 @@ const { ethers } = require('hardhat'); -const { getConfigPath, fetchABI } = require('./_helpers.js'); +const { getConfigPath, fetchABI, convertNetworkToChainId, getExplorerDataFromConfig, getNetworkDataFromConfig } = require('./_helpers.js'); -const explorerOpUrl = 'https://optimism-sepolia.blockscout.com/'; -const explorerBaseUrl = 'https://base-sepolia.blockscout.com/'; - -const rpcOptimism = `https://opt-sepolia.g.alchemy.com/v2/${process.env.OP_ALCHEMY_API_KEY}`; -const rpcBase = `https://base-sepolia.g.alchemy.com/v2/${process.env.BASE_ALCHEMY_API_KEY}`; +const hhConfig = require('../../hardhat.config.js'); +const polyConfig = hhConfig.polymer; async function getIbcApp(network) { try { @@ -23,45 +20,22 @@ async function getIbcApp(network) { function getDispatcherAddress(network) { const config = require(getConfigPath()); - let dispatcherAddr; - if (network === 'optimism') { - dispatcherAddr = config.proofsEnabled ? process.env.OP_DISPATCHER : process.env.OP_DISPATCHER_SIM; - } else if (network === 'base') { - dispatcherAddr = config.proofsEnabled ? process.env.BASE_DISPATCHER : process.env.BASE_DISPATCHER_SIM; - } else { - throw new Error('❌ Invalid network'); - } + const chainId = convertNetworkToChainId(network); + + const dispatcherAddr = config.proofsEnabled + ? polyConfig[`${chainId}`]['clients']['op-client'].dispatcherAddr + : polyConfig[`${chainId}`]['clients']['sim-client'].dispatcherAddr; return dispatcherAddr; } async function getDispatcher(network) { - const config = require(getConfigPath()); - const providerOptimism = new ethers.JsonRpcProvider(rpcOptimism); - const providerBase = new ethers.JsonRpcProvider(rpcBase); - - let explorerUrl; - let dispatcher; - let dispatcherAddress; + const explorerApiUrl = getExplorerDataFromConfig(network).apiURL; + const rpc = getNetworkDataFromConfig(network).alchemyRPC; + const provider = new ethers.JsonRpcProvider(rpc); try { - if (network === 'optimism') { - explorerUrl = explorerOpUrl; - dispatcherAddress = config.proofsEnabled - ? (dispatcherAddress = process.env.OP_DISPATCHER) - : (dispatcherAddress = process.env.OP_DISPATCHER_SIM); - - const opDispatcherAbi = await fetchABI(explorerUrl, dispatcherAddress); - dispatcher = new ethers.Contract(dispatcherAddress, opDispatcherAbi, providerOptimism); - } else if (network === 'base') { - explorerUrl = explorerBaseUrl; - dispatcherAddress = config.proofsEnabled - ? (dispatcherAddress = process.env.BASE_DISPATCHER) - : (dispatcherAddress = process.env.BASE_DISPATCHER_SIM); - - const baseDispatcherAbi = await fetchABI(explorerUrl, dispatcherAddress); - dispatcher = new ethers.Contract(dispatcherAddress, baseDispatcherAbi, providerBase); - } else { - throw new Error(`❌ Invalid network: ${network}`); - } + const dispatcherAddress = getDispatcherAddress(network); + const dispatcherAbi = await fetchABI(explorerApiUrl, dispatcherAddress); + const dispatcher = new ethers.Contract(dispatcherAddress, dispatcherAbi, provider); return dispatcher; } catch (error) { console.log(`❌ Error getting dispatcher: ${error}`); @@ -71,43 +45,21 @@ async function getDispatcher(network) { function getUcHandlerAddress(network) { const config = require(getConfigPath()); - let ucHandlerAddr; - if (network === 'optimism') { - ucHandlerAddr = config.proofsEnabled ? process.env.OP_UC_MW : process.env.OP_UC_MW_SIM; - } else if (network === 'base') { - ucHandlerAddr = config.proofsEnabled ? process.env.BASE_UC_MW : process.env.BASE_UC_MW_SIM; - } else { - throw new Error('❌ Invalid network'); - } + const chainId = convertNetworkToChainId(network); + const ucHandlerAddr = config.proofsEnabled + ? polyConfig[`${chainId}`]['clients']['op-client'].universalChannelAddr + : polyConfig[`${chainId}`]['clients']['sim-client'].universalChannelAddr; return ucHandlerAddr; } async function getUcHandler(network) { - const config = require(getConfigPath()); - const providerOptimism = new ethers.JsonRpcProvider(rpcOptimism); - const providerBase = new ethers.JsonRpcProvider(rpcBase); - - let explorerUrl; - let ucHandler; - let ucHandlerAddress; - + const explorerApiUrl = getExplorerDataFromConfig(network).apiURL; + const rpc = getNetworkDataFromConfig(network).alchemyRPC; + const provider = new ethers.JsonRpcProvider(rpc); try { - if (network === 'optimism') { - explorerUrl = explorerOpUrl; - ucHandlerAddress = config.proofsEnabled ? process.env.OP_UC_MW : process.env.OP_UC_MW_SIM; - - const opUcHandlerAbi = await fetchABI(explorerUrl, ucHandlerAddress); - ucHandler = new ethers.Contract(ucHandlerAddress, opUcHandlerAbi, providerOptimism); - } else if (network === 'base') { - explorerUrl = explorerBaseUrl; - ucHandlerAddress = config.proofsEnabled ? process.env.BASE_UC_MW : process.env.BASE_UC_MW_SIM; - - const baseUcHandlerAbi = await fetchABI(explorerUrl, ucHandlerAddress); - ucHandler = new ethers.Contract(ucHandlerAddress, baseUcHandlerAbi, providerBase); - } else { - throw new Error(`❌ Invalid network: ${network}`); - } - + const ucHandlerAddress = getUcHandlerAddress(network); + const ucHandlerAbi = await fetchABI(explorerApiUrl, ucHandlerAddress); + const ucHandler = new ethers.Contract(ucHandlerAddress, ucHandlerAbi, provider); return ucHandler; } catch (error) { console.log(`❌ Error getting ucHandler: ${error}`); diff --git a/scripts/send-universal-packet.js b/scripts/send-universal-packet.js index 3cfe5a0f..20b0d559 100644 --- a/scripts/send-universal-packet.js +++ b/scripts/send-universal-packet.js @@ -20,8 +20,8 @@ async function main() { // Do logic to prepare the packet // If the network we are sending on is optimism, we need to use the base port address and vice versa - const destPortAddr = - networkName === 'optimism' ? config['sendUniversalPacket']['base']['portAddr'] : config['sendUniversalPacket']['optimism']['portAddr']; + const destChain = Object.keys(sendConfig).find((chain) => chain !== networkName); + const destPortAddr = sendConfig[`${destChain}`]['portAddr']; const channelId = sendConfig[`${networkName}`]['channelId']; const channelIdBytes = hre.ethers.encodeBytes32String(channelId); const timeoutSeconds = sendConfig[`${networkName}`]['timeout']; diff --git a/utils/buildConfig.js b/utils/buildConfig.js new file mode 100644 index 00000000..a0454b36 --- /dev/null +++ b/utils/buildConfig.js @@ -0,0 +1,61 @@ +const fs = require('fs'); +const { getConfigPath, getWhitelistedNetworks, convertNetworkToChainId } = require('../scripts/private/_helpers'); + +const sendPacketInfo = { + portAddr: '0x1234567890abcdef1234567890abcdef12345678', + channelId: 'channel-n', + timeout: 36000, +}; + +const createChannelInfo = { + srcChain: '', + srcAddr: '0x1234567890AbCdEf1234567890aBcDeF12345678', + dstChain: '', + dstAddr: '0x1234567890AbCdEf1234567890aBcDeF12345678', + version: '1.0', + ordering: 0, + fees: false, +}; + +const chainName1 = process.argv[2]; +const chainName2 = process.argv[3]; +if (!chainName1 || !chainName2) { + console.error('Usage: node buildConfig.js '); + process.exit(1); +} +const allowedNetworks = getWhitelistedNetworks(); +const chainId1 = convertNetworkToChainId(chainName1); +const chainId2 = convertNetworkToChainId(chainName2); +console.log(`Chain ID 1: ${chainId1}`); +console.log(`Chain ID 2: ${chainId2}`); +if (!allowedNetworks.includes(`${chainId1}`) || !allowedNetworks.includes(`${chainId2}`)) { + console.error('Invalid network name. Please use one of the following:', allowedNetworks); + process.exit(1); +} + +function main() { + try { + const configPath = getConfigPath(); + const config = { + proofsEnabled: false, + isUniversal: true, + deploy: {}, + createChannel: createChannelInfo, + sendPacket: {}, + sendUniversalPacket: {}, + }; + + config.sendUniversalPacket[`${chainName1}`] = sendPacketInfo; + config.sendUniversalPacket[`${chainName2}`] = sendPacketInfo; + config.sendPacket[`${chainName1}`] = sendPacketInfo; + config.sendPacket[`${chainName2}`] = sendPacketInfo; + config.deploy[`${chainName1}`] = ''; + config.deploy[`${chainName2}`] = ''; + + fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); + } catch (error) { + console.error('❌ Error building config:', error); + } +} + +main();