Skip to content

Latest commit

Β 

History

History
318 lines (256 loc) Β· 11.8 KB

README.md

File metadata and controls

318 lines (256 loc) Β· 11.8 KB

Guide: Port an Ethereum dApp to Rootstock

Project Description

This guide will walk you through the steps needed to port your Ethereum dApp to the Rootstock (RSK) blockchain using Hardhat. Rootstock is a smart contract platform that leverages the security of the Bitcoin network, allowing you to deploy and manage your Ethereum dApps on a Bitcoin sidechain. By following this guide, you’ll be able to migrate your existing Ethereum smart contracts and tests to Rootstock, configure the necessary network settings, and deploy your contracts on the Rootstock mainnet or testnet.

Prerequisites

Before you begin, ensure you have the following prerequisites installed and configured:

  • Node.js (version 14 or higher)
  • npm (version 6 or higher)
  • Hardhat (globally installed)
  • A code editor (e.g., Visual Studio Code)
  • Metamask Wallet (for managing RSK accounts and obtaining private keys)

Additionally, you should have a basic understanding of Smart Contracts and how to use Hardhat for development and deployment.

Expected Result

By the end of this guide, you will have:

1.	Initialized a Hardhat project configured for Rootstock.
2.	Installed and configured necessary dependencies.
3.	Copied and adjusted your Ethereum smart contract code for Rootstock.
4.	Compiled and tested your smart contracts.
5.	Deployed your smart contracts to the Rootstock testnet.
6.	Verified the successful deployment of your contracts on the Rootstock block explorer.

Install hardhat globally

npm i -g hardhat

Initialize hardhat

Create a folder for your project and initialize a hardhat project

mkdir rsk-hardhat-example
cd rsk-hardhat-example
npx hardhat init

Select Create a TypeScript project on the hardhat CLI

888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

πŸ‘· Welcome to Hardhat v2.22.5 πŸ‘·β€

? What do you want to do? …
  Create a JavaScript project
❯ Create a TypeScript project
  Create a TypeScript project (with Viem)
  Create an empty hardhat.config.js
  Quit

Select the project root (press enter)

βœ” What do you want to do? Β· Create a TypeScript project
? Hardhat project root: β€Ί /path/to/your/project/rsk-hardhat-example

Add a .gitignore in case you need it

? Hardhat project root: β€Ί /path/to/your/project/rsk-hardhat-example
? Do you want to add a .gitignore? (Y/n) β€Ί y

Select that you'd like to install dependencies with npm

βœ” Do you want to add a .gitignore? (Y/n) Β· y
? Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox)? (Y/n) β€Ί y

Open your code editor

For this example we'll be using Visual Studio Code but feel free to use the one you prefer

code .

Configure environment and RSK networks (mainnet and testnet)

By now your hardhat project should have 4 main artifacts besides the basic Node configuration:

contracts/
ignition/modules/
test/
hardhat.config.js

Note

The version of Hardhat we'll be using in this example is v2.22.5. For this version, the default tool for managing deployments is Hardhat Ignition.

Install Hardhat Ignition and typescript

npm install --save-dev @nomicfoundation/hardhat-ignition-ethers typescript

Import Hardhat Ignition on the hardhat.config.ts

import "@nomicfoundation/hardhat-ignition-ethers";

Configure the RSK networks

By now your hardhat.config.ts should look something like this

import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import "@nomicfoundation/hardhat-ignition-ethers";

const config: HardhatUserConfig = {
  solidity: "0.8.24",
};

export default config;

In order to configure the RSK networks we'll need: a RPC url for both mainnet and testnet and a Private Key of the account that will deploy the contracts. To get the the RPCs go to the RPC API dashboard from Rootstock Labs, create an account if you don't have one and get an API key for RSK testnet and another for RSK mainnet.

The mainnet RPC url should look similar to this:

https://rpc.mainnet.rootstock.io/<API-KEY>

The testnet RPC url like this

https://rpc.testnet.rootstock.io/<API-KEY>

And if you don't know how to get the Private Key of your wallet, here's a tutorial on how to do it on Metamask. Also, if you haven't added RSK mainnet or testnet to your Metamask Wallet, you can do it by clicking the Add Rootstock or Add Rootstock Testnet buttons in the footer of mainnet explorer or testnet explorer.

Store the RPC urls and the PK

For storing the RPC urls securely you can use a .env file or the hardhat configuration variables. For this example we'll be using the second one. To store the mainnet RPC url in a hardhat configuration variable, type this in the terminal of your project's root:

npx hardhat vars set MAINNET_RPC_URL

And enter the value after pressing enter. Repeat this step with the other two:

npx hardhat vars set TESTNET_RPC_URL
βœ” Enter value: ********************************
npx hardhat vars set PRIVATE_KEY
βœ” Enter value: *************************************

Set the configuration

Now use all the things we've set and stored so your hardhat.config.ts looks like this:

import { HardhatUserConfig, vars } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import "@nomicfoundation/hardhat-ignition-ethers";

const MAINNET_RPC_URL = vars.get("MAINNET_RPC_URL");
const TESTNET_RPC_URL = vars.get("TESTNET_RPC_URL");
const PRIVATE_KEY = vars.get("PRIVATE_KEY");

const config: HardhatUserConfig = {
  solidity: "0.8.24",
  networks: {
    rskMainnet: {
      url: MAINNET_RPC_URL,
      chainId: 30,
      gasPrice: 60000000,
      accounts: [PRIVATE_KEY],
    },
    rskTestnet: {
      url: TESTNET_RPC_URL,
      chainId: 31,
      gasPrice: 60000000,
      accounts: [PRIVATE_KEY],
    },
  },
};

export default config;

Congratulations! πŸŽ‰ You’ve successfully completed all the steps and finished the configuration. Now let's bring the contracts from Ethereum.

Copy Ethereum Contract Code and Tests

We'll copy the contracts from Ethereum and it's tests to our RSK hardhat project. The contract is the following and will be inside contracts folder so the route would be contracts/SimpleStorage.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract SimpleStorage {
    uint256 public favoriteNumber;

    function store(uint256 _favoriteNumber) public  {
        favoriteNumber = _favoriteNumber;
    }
}

The same with the test. The file will be named SimpleStorage.ts inside the test folder so the result is test/SimpleStorage.ts

import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers";
import { expect } from "chai";
import hre from "hardhat";

describe("SimpleStorage", function () {
  async function deploySimpleStorageFixture() {
    const [owner] = await hre.ethers.getSigners();

    const SimpleStorage = await hre.ethers.getContractFactory("SimpleStorage");
    const simpleStorage = await SimpleStorage.deploy();

    return { simpleStorage, owner };
  }

  describe("Deployment", function () {
    it("Should deploy and initialize favoriteNumber to 0", async function () {
      const { simpleStorage } = await loadFixture(deploySimpleStorageFixture);

      expect(await simpleStorage.favoriteNumber()).to.equal(0);
    });
  });

  describe("Store", function () {
    it("Should store the value 42 and retrieve it", async function () {
      const { simpleStorage } = await loadFixture(deploySimpleStorageFixture);

      const storeTx = await simpleStorage.store(42);
      await storeTx.wait();

      expect(await simpleStorage.favoriteNumber()).to.equal(42);
    });

    it("Should store a different value and retrieve it", async function () {
      const { simpleStorage } = await loadFixture(deploySimpleStorageFixture);

      const storeTx = await simpleStorage.store(123);
      await storeTx.wait();

      expect(await simpleStorage.favoriteNumber()).to.equal(123);
    });
  });
});

Compile, Test and Deploy your contract

Compile

In order to compile your contract, type this in your terminal:

npx hardhat compile

And you should get something like this:

Generating typings for: 1 artifacts in dir: typechain-types for target: ethers-v6
Successfully generated 6 typings!
Compiled 1 Solidity file successfully (evm target: paris).

Test

For testing out your contract(s) write this command in the console:

npx hardhat test

And you should get something similar to this:

SimpleStorage
    Deployment
      βœ” Should deploy and initialize favoriteNumber to 0
    Store
      βœ” Should store the value 42 and retrieve it
      βœ” Should store a different value and retrieve it


  3 passing (286ms)

Deployment

To deploy on mainnet or testnet, you need to have enough balance of the native token. In this example we'll be deploying the contract on testnet.

Note

To get testnet tokens for deploying your contract go to the faucet.

Create the deployment script

Create a file named SimpleStorage.ts inside the folder ignition/modules. Then paste this in that file:

import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";

const SimpleStorageModule = buildModule("SimpleStorageModule", (m) => {
  const simpleStorage = m.contract("SimpleStorage");

  return { simpleStorage };
});

export default SimpleStorageModule;

This Typescript script uses hardhat ignition to deploy the SimpleStorage contract in a declarative way. After you paste that code and save the changes, type this in your terminal to deploy the contract:

npx hardhat ignition deploy ignition/modules/SimpleStorage.ts --network rskTestnet

And you should get something like this:

βœ” Confirm deploy to network rskTestnet (31)? … yes
Hardhat Ignition πŸš€

Resuming existing deployment from ./ignition/deployments/chain-31

Deploying [ SimpleStorageModule ]

Batch #1
  Executed SimpleStorageModule#SimpleStorage

[ SimpleStorageModule ] successfully deployed πŸš€

Deployed Addresses

SimpleStorageModule#SimpleStorage - 0x3570c42943697702bA582B1ae3093A15D8bc2115

Tip

If you get an error like IgnitionError: IGN401 try running the command again.

If you want to deploy your contract on mainnet change rskTestnet with rskMainnet in the last command we ran and make sure you have RBTC available on your wallet.

Congrats! You deployed your contract on Rootstock!

Now go to https://explorer.testnet.rootstock.io/ and paste your contract address in the search bar to verify the deployment was successful. If you deployed your contract on mainnet look in https://explorer.rootstock.io/