How to Deploy ANY Smart Contract on Arbitrum Blockchain Network with thirdweb

How to Deploy ANY Smart Contract on Arbitrum Blockchain Network with thirdweb

This is a comprehensive step-by-step guide on how to build and deploy a smart contract on the Arbitrum Blockchain Network.

·

10 min read

In this article, I will show you how to deploy any type of solidity smart contract to the Arbitrum blockchain network using thirdweb SDK.

The process described in this tutorial can also be reverse-engineered to deploy any smart contract to any EVM-compatible blockchain.

You will learn:

  • How to work with the first chain-agnostic web3 framework - thirdweb;

  • How to build and deploy a smart contract to the Arbitrum Goerli (AGOR) blockchain network; and.

  • How to verify, interact with and build the frontend integration of your smart contract in one button click!

But before we go into the details, let’s clear the elephant in the room:

🤔 Why should you use thirdweb?

  • thirdweb makes the contract deployment process faster, more effective and more secure than ever before.

    Faster in the sense, that you don’t need to stress over getting RPC URLs - thirdweb has saved you all the time. More effective because you may choose out of the ton of well-built secure contracts provided on thirdweb explore. And more secure because you need not worry about exposing your private keys during deployment - thirdweb eliminates that tragedy.

  • thirdweb automatically verifies the contract you deploy with the dashboard. You also get access to a user-friendly interface to interact with your contract on the blockchain.

  • thirdweb React SDK makes it easy to integrate your backend into your project’s frontend. You get access to auto-generated code to connect your contract’s function to your frontend and this is available in Javascript, React, Python, Go, React Native and Unity.

These by far, coupled with dozens of its other features, make thirdweb an indispensable tool for every web3 developer - whether noob, intermediate or expert.

With that sorted, let’s start BUIDLing👷‍♂️!

⚙ Pre-requisites

For this tutorial, you are required to have the following:

  • Nodejs, and VScode installed on your computer;

  • Arbitrum Testnet Faucet Tokens. If you don’t have some yet, visit and follow the steps in this Medium article to claim faucet tokens or bridge your goerli tokens to Arbitrum; and

  • A Basic understanding of the Solidity Programming language.

N.B: You can find the contract deployed in this demonstration here: https://thirdweb.com/goerli/0x6de273F4211dA6038470791Dc643145F80296be3

Let's now build and deploy a smart contract to the Arbitrum Goerli blockchain network in 3 Simple Steps!

👩🏽‍💻 Step 1: Build and compile your contract.

For this tutorial, I have prepared a smart contract (an ETHPriceInsurance) to use for this demonstration - remember you can use any smart contract of your choice!

To set up your development environment, run this code in your terminal.

mkdir thirdweb-demo
cd thirdweb-demo
npx thirdweb create

This code will create a new directory thirdweb-demo and install the thirdweb CLI with the hardhat development framework.

You'd be prompted to install the thirdweb package. Press Enter to proceed.

Then you'll be requested to choose the kind of project you intend to build. Navigate to Contract then click Enter.

The next prompts will require you to name your project and then choose your desired framework. Here select hardhat, then select Empty Project.

Once this installation is completed, change the directory into the package folder with the command:

cd thirdweb-contracts

With your development environment correctly set, the next thing to do now is to add your Solidity smart contract code.

If you're using VSCode, navigate to the left side panel of your IDE, then rename the Contract.sol file to your preferred file name. In this case, I'll name it EthPriceInsurance.

Delete the sample code in the file and add yours. If you don't have a contract to use, copy and paste the code below.

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

interface IInsurancePolicy {
    function mintNFT(
        address recipient,
        uint256 _price,
        uint256 _timeDuration,
        uint256 _portfolioValue
    ) external;

    function mintInsureNFT(
        address recipient,
        uint256 _NFTInsurancePolicyID,
        uint256 _startDate,
        uint256 _endDay,
        uint256 _insureAmount,
        string memory _NFTName,
        string memory _NFTSymbol,
        string memory _NFTTokenURI
    ) external;

    function getTokenURI(
        uint256 _tokenID
    ) external view returns (string memory);

    function burnNFT(uint256 _tokenID) external;
}

pragma solidity ^0.8.0;

// / @title ETHEREUM PRICE DEVALUATION INSURANCE
// / @author Mide Sofek
// / @notice This is an insurance policy for $ETH Price Devaluation
// / @notice Contract owner cannot transfer contract funds
// / @notice Contract leverages parametric insurance model, using chainlink price feeds
// / as a decentralized oracle to automatically handle claim assessment and settlement
// / Users also hold a policy NFT with DYNAMIC METADATA as Proof of Agreement!
// / This NFT is minted upon policy creation and burnt after claim settlements
// / @dev All function calls are currently implemented without side effects
// / @custom:testing stages. This contract has been rigorously tested.

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

contract EthPriceInsurance is Ownable, ReentrancyGuard {
    /// @notice A Struct to record each holder of this policy
    /// @dev Struct stores each policyholder's Data
    struct PolicyHolder {
        uint256 insuredPrice;
        uint256 premiumPaid;
        uint256 timeDuration;
        uint256 portfolioValue;
        bool hasPolicy;
    }

    //**************VARIABLE DECLARATION***************//
    uint256 public tokenIds;
    uint256 public noOfHolders;
    uint256 public ethPrice;

    // policy contract instance
    IInsurancePolicy nftPolicy;

    //chainlink price feeds
    AggregatorV3Interface internal priceFeed;

    // a list of all Policy holders
    mapping(address => PolicyHolder) public policyHolders;
    // mapping(uint256 => PolicyHolder) public policyHolders;
    // stores record of insured users
    mapping(address => bool) insured;

    /**
     * Network: Goerli * Aggregator: ETH/USD
     * Address: 0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e
     */
    constructor() {
        priceFeed = AggregatorV3Interface(
            0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e
        );
        nftPolicy = IInsurancePolicy(
            0xb5FfEcac8d239a19836DBEcb5262DA2B7Ca6b78e
        );
    }

    //******************READABLE FUNCTIONS*******************//
    /**
     * Returns the latest price of Ethereum in USD
     */

    /// @notice reads the current price of ETH from chainlink price feeds
    /// @dev returns $Ether as 8 decimals value
    /// @dev function rounds up $Ether price to 18 decimals by ^10
    function getEthPrice() public view returns (uint256) {
        (, int price, , , ) = priceFeed.latestRoundData();
        return uint256(price * 10 ** 10);
    }

    /// @notice function calls getEthPrice() above
    /// @dev enables price feeds data to be reused inside of functions
    /// @dev stores $Ether price into ethPrice
    function checkEthPrice() public returns (uint256) {
        ethPrice = getEthPrice();
        return ethPrice;
    }

    /// @notice function handles the calculation of installmental payments
    /// @dev can only be called by this contract
    function calculateMinimumPremium(
        uint256 _value
    ) private pure returns (uint256) {
        //calculate how much premium user is to pay point
        uint256 premiumInstallments = (_value / 3) * 10 ** 18;
        return premiumInstallments;
    }

    /// @notice function handles the calculation of installmental payments
    /// @dev can only be called by contract owner
    function contractBalance() public view onlyOwner returns (uint256) {
        address insureContract = address(this);
        uint256 insurePool = insureContract.balance;
        return insurePool;
    }

    //******************WRITABLE FUNCTIONS*******************//

    /// @notice Enables DeFi users to create policy agreement
    /// @dev Updates parameters in struct
    /// @dev Records the data of every policyholder && tracks who holds policy
    /// @dev Function mints NFT with a DYNAMIC METADATA to holders wallet
    /// @dev DYNAMIC METADATA stores the "price", "duration" and "portfolio" insured onchain!
    /// @param _price is the ETH price a user wants to insure against
    /// @param _timeDuration the number of days the user wants cover to run(must not be lesser than 30 days)
    /// @param _porfolioValue is the portfolio size of the user. This determines amount to be paid as premium
    function createPolicyAgreement(
        uint256 _price,
        uint256 _timeDuration,
        uint _porfolioValue
    ) public payable {
        //require user hasn't claimed policy already
        require(!insured[msg.sender], "You have the policy already!");
        require(_price > 0, "Invalid Price!");
        uint price = _price * 10 ** 18;
        //require insurance period is more 30 days
        require(_timeDuration >= 30, "Duration must be more than 30Days!");
        //portfolio value is not 0
        require(_porfolioValue > 0, "Portfolio is too small");
        /// check ETH price isn't already less
        require(getEthPrice() > price, "Price isn't valid");

        /// Update User Policy details
        policyHolders[msg.sender].insuredPrice = price;
        policyHolders[msg.sender].timeDuration = _timeDuration;
        policyHolders[msg.sender].portfolioValue = _porfolioValue;
        policyHolders[msg.sender].hasPolicy = true;
        noOfHolders++;

        ////////////////////////////
        // withdraw premium payment
        uint256 premium = calculateMinimumPremium(_porfolioValue);
        require(msg.value >= premium, "Premium Value isn't valid");

        address payable contractAddress = payable(address(this));
        contractAddress.transfer(premium);
        ////////////////////////////

        //Update premiumPaid
        policyHolders[msg.sender].premiumPaid += premium;

        //record user has insured
        insured[msg.sender] = true;
        // mint NFT to wallet
        nftPolicy.mintNFT(msg.sender, price, _timeDuration, _porfolioValue);
        //
    }

    /// @notice Enables DeFi users to withdraw premiums paid (No questions asked)
    /// @dev Function uses Chainlink price feeds to check if $Eth price
    /// @dev is below insured amount, if true, pays holder
    function claimSettlement() public nonReentrant {
        // require sender owns NFT OR he's on the insured list
        require(insured[msg.sender] == true, "You're not entitled to this!");
        // require present ETH price is less than the amount user insured
        require(policyHolders[msg.sender].insuredPrice < ethPrice);
        ///////////////////
        // require agreement is more than 30days
        // require(policyHolders[msg.sender].timeDuration < 30, "");
        ///////////////////
        //
        ///////////////////
        // Withdraw Funds Paid by Users to their Wallet
        uint amountToBePaid = policyHolders[msg.sender].premiumPaid;
        payable(msg.sender).transfer(amountToBePaid);
        ///////////////////
        // @dev later take 1% of claim withdrawn for maintainance
        ///////////////////
        // BurnNFT
        // nftPolicy.burn();
        ///////////////////

        //RETURN DATA TO DEFAULT STATE//
        insured[msg.sender] = false;
        policyHolders[msg.sender].hasPolicy = false;
        policyHolders[msg.sender].insuredPrice = 0;
        policyHolders[msg.sender].timeDuration = 0;
        policyHolders[msg.sender].portfolioValue = 0;
        policyHolders[msg.sender].premiumPaid = 0;
        noOfHolders--;
    }

    receive() external payable {}

    fallback() external payable {}
}

Then run:

npm install @openzeppelin/contracts @chainlink/contracts

🎉 And that's it, you are now ready to deploy your contract with thirdweb.

🌐 Step 2: Upload your Contract

To deploy your smart contract simply run in your terminal:

yarn deploy

If you have more than one file, you will be prompted to select the solidity contract you intend to deploy.

Mide Sofek Thirdweb

But with just one, thirdweb will directly upload your contract to the thirdweb dashboard. Follow the link displayed in your terminal to proceed to the deployment phase.

If you haven’t worked with the thirdweb dashboard before now, check out my article on thirdweb Explore and How to work with the thirdweb dashboard

👩🏽‍🚀 Step 3: Deploy Your Contract

After following the link displayed in your terminal, you will be directed to a page that looks like this.

Here is where all the magic (deployment, interaction and connecting to web3) happens.

To get started, connect your wallet. (No sign-ins/emails/credit card😅 required)

Then select Arbitrum Goerli (AGOR) as your desired chain.

P.S: thirdweb also works well with mainnet projects

Then click Deploy Now.

If you haven’t added the Arbitrum Goerli testnet network to your Metamask, you’ll be prompted to do so by clicking just one button.

Bye-bye to copying and pasting 👋

Your Metamask will pop. Click “Approve” then “Switch Network”

But, if you still want to add the Arbitrum Goerli testnet manually, here are the network details for the AGOR Testnet:

Network Name: Arbitrum Goerli Testnet

New RPC URL: https://goerli-rollup.arbitrum.io/rpc

Chain ID: 421613

Currency Symbol: ETH

Block Explorer URL: https://goerli.arbiscan.io/

N.B: If you still don’t have faucet tokens, follow this medium guide to claim faucet tokens or bridge your goerli tokens to Arbitrum.

Once that is done, it is time to deploy. Click “Deploy Now”. Then confirm the transaction.

thirdweb will then request you to sign a transaction - this transaction doesn’t require gas. It is to enable you to access the contract directly on your thirdweb dashboard.

🥳🎉 And Voila! You have successfully deployed, verified and can now interact with your smart contract on the blockchain.

There is more…..

As explained earlier, with your contract deployed with thirdweb, you can easily integrate your contract into your frontend with thirdweb’s React SDK.

Learn more about thirdweb’s React SDK here

Also, you can interact directly with your smart contract on your dashboard Explorer.

My Personal Experience

Working with the thirdweb Solidity SDK and Dashboard was a very smooth and user-friendly experience.

The contract shared above is the backend code for a DeFi Insurance application I built with a team of builders.

I built the smart contract from the ground-up with thirdweb contractkit, deployed it with thirdweb dashboard and added web3 functionality into the project's frontend with thirdweb's React SDK.

P.S: You can check out the dApp here. And the GitHub project repo here.

The result of this exercise demonstrated the possibility of executing a full web3 project with thirdweb - and this is why I describe thirdweb as the indispensable starter kit for every web3 developer.

Finally...

With thirdweb, you can deploy any contract to any EVM network (both mainnet and testnet) in a seamless, safe and most efficient way possible without having to worry about running after RPC URLs or exposing your private keys.

To boost your confidence, I challenge you to try deploying to other EVM chains asides from the Arbitrum Goerli testnet - it's the same process as the one shared in this article.

Additional Resources

  1. How to Easily Deploy Smart Contracts to any EVM Chain by thirdweb

  2. thirdweb's Official Website

  3. thirdweb's Official Documentation

  4. Explore thirdweb Dashboard

  5. Learn Everything you need to know about thirdweb React SDK

  6. How to build and Deploy Smart Contracts with thirdweb Explore and Dashboard

  7. How to claim Testnet faucet tokens on Arbitrum Goerli

I'd love to connect with you on Twitter | Linkedin | GitHub

Thank you for reading :)