Introduction
In this article, we’ll detail how to deploy, link, and interact with multi-chain smart contracts across 2 different shards on Quai Network. This method can be extended to deploy trustless cross-chain contracts across all 9 shards. We’ll be using the basic implementation of a QRC721 token, an adapted version of the ERC721 standard, to showcase cross-chain contracts for this tutorial.Prerequisites
To deploy multi chain smart contracts on Quai, we’ll need a few tool-kits and dependencies. Here’s an overview of all of the dependencies we’ll be using:| NodeJS | Javascript runtime environment. Use the LTS version. |
| hardhat-example | A Hardhat project with sample contracts and deploy scripts for Quai Network. |
| Quais.js | A JavaScript library for interacting with Quai Network. |
| quai-hardhat-plugin | A plugin built for Hardhat that provides support for the SolidityX compiler. |
Environment Setup
Dependencies
Start by cloning thehardhat-example repository, navigating to the SolidityX/ directory we’ll be using for this tutorial, and installing the dependencies via npm.
If you’ve already cloned the
hardhat-example repository for the Single-Chain Deployment Tutorial, you
can skip the cloning step. Just navigate to the SolidityX/ directory and run npm install.Smart Contracts
TheSolidityX directory comes with 2 sample contracts: QRC20.sol and QRC721.sol inside of the contracts/ directory. Both of the included contracts are the initial SolidityX/cross-chain implementations of existing token standards. Source code for the contracts can be found in the SolidityX-Contracts Repository
As mentioned above, we’ll be deploying the QRC721 smart contract. Before configuring and deploying the QRC721, we recommend getting familiar with the contract specs.
Environment Variables
We’ve included a sample .env.dist environment file at the root of thehardhat-example repo to hold token details, private keys, and RPC URLs in a secure fashion.
The
.env.dist file is a template file and should not be used as is. You should copy the .env.dist file to a new .env file in the repository root using the following command:This file lives at the root of the hardhat-example repository and serves as the config file for both the Solidity and SolidityX directories..env.dist file in the root to a new .env file in the repository root using the following command:
.env file and add your private keys, RPC URLs, and token args for the contract you’d like to deploy. The .env file should look like this:
.env
The
PK values must all be for unique addresses and correspond to the zone name, i.e. your CYPRUS1PK should be the private key of your
Cyprus1 address.hardhat-example repository uses the Quais SDK to configure network connections using only a single RPC URL. To learn more about how the SDK configures network providers, visit the SDK provider examples section.
After filling in your private keys, RPC URL, we’re now ready to securely consume them inside of hardhat.config.js.
Hardhat Configuration
Hardhat useshardhat.config.js to configure smart contract deployments. The config file allows you to define deployment networks, tasks, compilers, etc. hardhat-example/SolidityX contains a prebuilt hardhat.config.js file with configurations for compiling, deploying, verifying SolidityX smart contracts on Quai.
The below configuration file has two main differences from the hardhat.config.js file use for basic Solidity contract deployment:
- It imports the
quai-hardhat-pluginto handle SolidityX compiler download - Inclusion of the optional
solidityxobject to specify a locally built SolidityX compiler (if you don’t want to use the plugin to download the compiler)
Sample hardhat configuration file
Sample hardhat configuration file
This sample configuration file is provided as part of the Inside the config file you can find deployment and verification definitions for:
hardhat-example repository.hardhat.config.js
The Golden Age devnet currently supports Cyprus 1 and 2. All other shards are not running in the current network configuration.
cyprus1cyprus2cyprus3paxos1paxos2paxos3hydra1hydra2hydra3
hardhat.config.js will pull your private keys and RPC URLs from the .env file and use them to deploy and verify your contracts.
SolidityX Compiler
To be able to properly compile and deploy SolidityX contracts, we’ll need the SolidityX. There are two methods of installing the SolidityX compiler for use with Hardhat:- Install the SolidityX compiler via quai-hardhat-plugin (Recommended)
- Install and build the SolidityX compiler from source
- Install via Plugin
- Install from source
As noted above, the
hardhat.config.js file already includes the quai-hardhat-plugin to handle the SolidityX compiler download. If you’ve followed the above steps, you’re already set up to use the plugin to download the SolidityX compiler.Deploy
1
Compile with SolidityX
SolidityX contract compilation with Hardhat is simple and can be done using Which should output something like:
npx in the CLI.Compile all of the contracts inside the contracts/ directory with:2
Configure deployment scripts
Inside the Your specified network configuration is consumed inside of the We’ll use these ideas to properly modify the token args and network specification to deploy our contracts in the next step.
scripts/ directory, you’ll find a deploy script for both QRC20 and QRC721 contracts: deployQRC20.js and deployQRC721.js. For this tutorial, we’ll be using the QRC721 contract.The deployQRC721.js script works by pulling your specified network/accounts config from hardhat.config.js and the QRC721 arguments specified in the .env file at the root of the repository and uses them to deploy your contract.Token arguments are consumed via the tokenArgs array:provider and wallet variables in tandem with the compiled contract ABI and bytecode to create a new contract instance:3
Deploy contracts
For this tutorial, we’ll be deploying two instances of our QRC721 contract on two different chains. You can extend the methodology used here to deploy and link contracts to any combination of shards within Quai Network.We’ll be deploying the first QRC721 contract on Cyprus-1 chain. To do this, we’ll pass Running this should output:Now, we can deploy an identical QRC721 contract to another shard within Quai, like Cyprus-2. Like before, you’ll pass Which again should output something like this:We’ve now deployed our test QRC721 contract to both the Cyprus-1 and Cyprus-2 chains!
cyprus1 as the network flag in the deployment command like below:cyprus2 as the network flag in the deployment command.When deploying QRC721s, we recommend changing the
baseURI variable for each chain to prevent duplicate mints or additionally modifying
the QRC721 contract to handle minting on different shards. This variable can be changed in the .env file at the root of the repository.Make sure to save these two contract addresses, we’ll need them in the next section.
Link Sister Contracts
To complete our cross-chain NFT deployment, we’ll need to link the two deployed contracts. “Linking” the two QRC721 contracts can be done by adding the deployed contract addresses of our QRC721s to the approved contracts array within each contract. This can be done using theAddApprovedAddresses method. It accepts 2 arrays as arguments: chain indexes and approved addresses.
The AddApprovedAddresses method seen below can be used to add as few as 1 or as many as 8 sister contracts to the approvedAddresses array of a QRC721 or QRC20 contract.
QRC721.sol
ApprovedAddresses of each of the QRC721 contracts, the cross-chain functionality of the transferFrom method becomes available, which allows anyone who owns a token from the collection to trustlessly send their it between shards that the contracts are deployed to.
Contract Linking Script
To link the sister contracts, we’ll utilizequais.js and some of the Hardhat Runtime Environment that we used in the deploy script. Start by creating another file in the scripts directory named addApprovedAddresses.js.
addApprovedAddresses.js:
addApprovedAddresses.js
addApprovedAddresses.js script uses the QRC721.sol ABI to compose and send a transaction that inserts new addresses to the approvedAddresses array in any deployed QRC721 contract.
How the linking script works
How the linking script works
- First, creating a quais
providerwith our specified network configuration from Hardhat 2. Creating a quaiswalletwith ourproviderand key config from Hardhat 3. Defining the contract we’d like to add an approved address to with the importedQRC721.solABI, contract address, andwallet4. Composing theaddApprovedAddressestransaction with the inputs -chainIndexarray: integer chain indices corresponding to the addresses we’d like to add toapprovedAddresses-addressarray: the contract addresses that we’d like to add toapprovedAddresses5. Sending the transaction and waiting for inclusion in a block.
addApprovedAddresses.js file, we’re ready to start linking our sister contracts.
1
Build linking transactions
Now that we’ve set up our script, we’re ready to link our two deployed contracts.Start by grabbing the addresses of the two contracts we deployed in the deploy section.We’ll take these contract addresses and use them to build the transaction data passed to the The transaction data we’ll need to pass to the
addApprovedAddresses method.You can pass the same transaction data to every contract you want to link, as the
addApprovedAddresses method can take in and
handle its own contract address as an argument. This removes the need to alter the transaction data for each contract you want to link.addApprovedAddresses method is (notice the order of the arrays):chainIndexarray:[0, 1]addressarray:['0x00735E9B2c731Fd3eCC8129a3653ACb99dF969cC', '0x0172F38EC31f58B1419E2CcE2B05B095625218ea']
You can extend this transaction data structure to link as many contracts as you’d like by adding additional chain indexes and contract
addresses to the arrays. Always make sure to add the same number of chain indexes and contract addresses to the arrays in matching
order.
2
Send linking transactions
First, we’re going to send the linking transaction to our Cyprus 1 contract. To do this, start by changing the Now, we’re ready to run the script and complete the Cyprus 1 contract linkage. Make sure to pass the The script should output something like this:We’ve now linked our Cyprus 1 contract to our Cyprus 2 contract, but we’re not done yet.To finish linking these two sister contracts, we’ll need to send the exact same transaction data to the Cyprus 2 contract. In the Lastly, send the linkage transaction to our Cyprus 2 token by running the script with the
contractAddress variable to our Cyprus 1 contract address in the addApprovedAddresses.js script:--network cyprus1 flag when sending transactions to the Cyprus 1 contract.addApprovedAddresses.js script, change the contractAddress variable to our Cyprus 2 contract address:--network cyprus2 flag:addApprovedAddresses method. You now have the tools to deploy and link contracts across all zone chains within Quai Network.
The same deploy and link method can be used for any other SolidityX based contract with cross-chain logic, including the QRC-20
Token.
