Chapter 16:  Deploy Solidity smart contract on Layer 2 and multichain wallet connection with Frontend Integration

Chapter 16: Deploy Solidity smart contract on Layer 2 and multichain wallet connection with Frontend Integration

We’ll create a banking smart contract that supports Layer 2 solutions (Arbitrum, Optimism, zkSync, and Polygon zkEVM) and multichain wallet connection.


Steps to Build This System

Write & Deploy a Solidity Smart Contract on Layer 2
Integrate Multichain Wallet (Ethers.js & Web3Modal)
Develop a React Frontend for deposits, withdrawals, and interest calculation


Smart Contract – Solidity (Multi-Token Banking System)

Features:

Supports ETH & ERC-20 deposits
Interest calculation on deposits
Multi-token support
Multichain compatibility with Layer 2 networks

Solidity Code (Layer 2 Banking System)

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract Layer2Bank is Ownable {
    struct Deposit {
        uint256 amount;
        uint256 timestamp;
        address token;
    }

    mapping(address => Deposit[]) public userDeposits;
    mapping(address => bool) public supportedTokens;
    uint256 public interestRate = 5; // 5% annual interest

    event Deposited(address indexed user, uint256 amount, address token);
    event Withdrawn(address indexed user, uint256 amount, address token);

    constructor(address[] memory tokens) {
        for (uint256 i = 0; i < tokens.length; i++) {
            supportedTokens[tokens[i]] = true;
        }
    }

    function depositETH() external payable {
        require(msg.value > 0, "Deposit amount must be greater than zero");
        userDeposits[msg.sender].push(Deposit(msg.value, block.timestamp, address(0)));
        emit Deposited(msg.sender, msg.value, address(0));
    }

    function depositERC20(address token, uint256 amount) external {
        require(supportedTokens[token], "Token not supported");
        require(amount > 0, "Amount must be greater than zero");
        IERC20(token).transferFrom(msg.sender, address(this), amount);
        userDeposits[msg.sender].push(Deposit(amount, block.timestamp, token));
        emit Deposited(msg.sender, amount, token);
    }

    function withdraw(uint256 index) external {
        require(index < userDeposits[msg.sender].length, "Invalid index");
        Deposit memory userDeposit = userDeposits[msg.sender][index];
        uint256 interest = calculateInterest(userDeposit.amount, userDeposit.timestamp);
        uint256 totalAmount = userDeposit.amount + interest;

        if (userDeposit.token == address(0)) {
            payable(msg.sender).transfer(totalAmount);
        } else {
            IERC20(userDeposit.token).transfer(msg.sender, totalAmount);
        }

        delete userDeposits[msg.sender][index];
        emit Withdrawn(msg.sender, totalAmount, userDeposit.token);
    }

    function calculateInterest(uint256 amount, uint256 timestamp) public view returns (uint256) {
        uint256 timeElapsed = block.timestamp - timestamp;
        uint256 interest = (amount * interestRate * timeElapsed) / (365 days * 100);
        return interest;
    }

    function addSupportedToken(address token) external onlyOwner {
        supportedTokens[token] = true;
    }

    function removeSupportedToken(address token) external onlyOwner {
        supportedTokens[token] = false;
    }
}

Deploy the Contract on a Layer 2 Blockchain

We will deploy this contract on a Layer 2 blockchain like Arbitrum, Optimism, or zkSync.

Deployment Steps (Using Hardhat)

Step 1: Install Dependencies

npm install hardhat @openzeppelin/contracts dotenv

Step 2: Configure hardhat.config.js

require("@nomiclabs/hardhat-ethers");

module.exports = {
  networks: {
    arbitrum: {
      url: "https://arb1.arbitrum.io/rpc",
      accounts: [`0x${process.env.PRIVATE_KEY}`],
    },
    optimism: {
      url: "https://mainnet.optimism.io",
      accounts: [`0x${process.env.PRIVATE_KEY}`],
    },
  },
  solidity: "0.8.19",
};

Step 3: Deploy Contract

npx hardhat run scripts/deploy.js --network arbitrum

React Frontend Integration (Multichain Wallet Support)

We will use Ethers.js & Web3Modal to support MetaMask, WalletConnect, and Coinbase Wallet.

Install Dependencies

npm install ethers web3modal

connectWallet.js (Multichain Wallet Connection)

import { ethers } from "ethers";
import Web3Modal from "web3modal";

const supportedNetworks = {
  42161: "Arbitrum",
  10: "Optimism",
  1101: "Polygon zkEVM",
};

export const connectWallet = async () => {
  const web3Modal = new Web3Modal();
  const provider = await web3Modal.connect();
  const ethersProvider = new ethers.providers.Web3Provider(provider);
  const signer = ethersProvider.getSigner();
  const { chainId } = await ethersProvider.getNetwork();

  if (!supportedNetworks[chainId]) {
    alert("Unsupported Network! Please switch to Arbitrum, Optimism, or Polygon zkEVM.");
    return;
  }

  return { signer, chainId };
};

Deposit.js (Deposit ETH & ERC-20)

import { ethers } from "ethers";
import BankContractABI from "./BankContractABI.json";

const contractAddress = {
  42161: "0xArbitrumContractAddress",
  10: "0xOptimismContractAddress",
  1101: "0xPolygonzkEVMContractAddress",
};

export const depositETH = async (signer, chainId, amount) => {
  const contract = new ethers.Contract(contractAddress[chainId], BankContractABI, signer);
  const tx = await contract.depositETH({ value: ethers.utils.parseEther(amount) });
  await tx.wait();
  alert("Deposit Successful!");
};

export const depositERC20 = async (signer, chainId, tokenAddress, amount) => {
  const contract = new ethers.Contract(contractAddress[chainId], BankContractABI, signer);
  const tokenContract = new ethers.Contract(tokenAddress, ["function approve(address,uint256)"], signer);
  
  await tokenContract.approve(contractAddress[chainId], ethers.utils.parseEther(amount));
  const tx = await contract.depositERC20(tokenAddress, ethers.utils.parseEther(amount));
  await tx.wait();
  alert("ERC-20 Deposit Successful!");
};

Withdraw.js (Withdraw Funds with Interest)

export const withdrawFunds = async (signer, chainId, index) => {
  const contract = new ethers.Contract(contractAddress[chainId], BankContractABI, signer);
  const tx = await contract.withdraw(index);
  await tx.wait();
  alert("Withdrawal Successful!");
};

Create a React frontend for multichain banking system

I’ll create a React frontend for your multichain banking system. This will allow users to connect their wallet, deposit ETH/ERC-20 tokens, withdraw funds with interest, and view their transaction history.


Features of the Frontend

Multichain Wallet Connection (MetaMask, WalletConnect, Coinbase Wallet)
Deposit ETH & ERC-20 Tokens
Withdraw Funds with Interest
Transaction History
Automatic Network Detection & Switching


Tech Stack

  • React.js (Frontend framework)
  • Ethers.js (Blockchain interaction)
  • Web3Modal (Multichain wallet support)
  • Tailwind CSS (Styling)

React Code (Complete Frontend)

React project with all functionalities.

Let’s start with the main frontend code:

import React, { useState, useEffect } from "react";
import { ethers } from "ethers";
import Web3Modal from "web3modal";
import BankContractABI from "./BankContractABI.json";

const contractAddress = {
  42161: "0xArbitrumContractAddress",
  10: "0xOptimismContractAddress",
  1101: "0xPolygonzkEVMContractAddress",
};

const supportedNetworks = {
  42161: "Arbitrum",
  10: "Optimism",
  1101: "Polygon zkEVM",
};

export default function App() {
  const [account, setAccount] = useState("");
  const [signer, setSigner] = useState(null);
  const [chainId, setChainId] = useState(null);
  const [amount, setAmount] = useState("");
  const [tokenAddress, setTokenAddress] = useState("");
  const [transactionHistory, setTransactionHistory] = useState([]);

  const connectWallet = async () => {
    const web3Modal = new Web3Modal();
    const provider = await web3Modal.connect();
    const ethersProvider = new ethers.providers.Web3Provider(provider);
    const signer = ethersProvider.getSigner();
    const { chainId } = await ethersProvider.getNetwork();
    
    if (!supportedNetworks[chainId]) {
      alert("Unsupported Network! Please switch to Arbitrum, Optimism, or Polygon zkEVM.");
      return;
    }
    
    setSigner(signer);
    setAccount(await signer.getAddress());
    setChainId(chainId);
  };

  const depositETH = async () => {
    if (!signer || !chainId) return alert("Connect your wallet first!");
    const contract = new ethers.Contract(contractAddress[chainId], BankContractABI, signer);
    const tx = await contract.depositETH({ value: ethers.utils.parseEther(amount) });
    await tx.wait();
    alert("Deposit Successful!");
  };

  const depositERC20 = async () => {
    if (!signer || !chainId) return alert("Connect your wallet first!");
    const contract = new ethers.Contract(contractAddress[chainId], BankContractABI, signer);
    const tokenContract = new ethers.Contract(tokenAddress, ["function approve(address,uint256)"], signer);
    
    await tokenContract.approve(contractAddress[chainId], ethers.utils.parseEther(amount));
    const tx = await contract.depositERC20(tokenAddress, ethers.utils.parseEther(amount));
    await tx.wait();
    alert("ERC-20 Deposit Successful!");
  };

  const withdrawFunds = async (index) => {
    if (!signer || !chainId) return alert("Connect your wallet first!");
    const contract = new ethers.Contract(contractAddress[chainId], BankContractABI, signer);
    const tx = await contract.withdraw(index);
    await tx.wait();
    alert("Withdrawal Successful!");
  };

  return (
    <div className="flex flex-col items-center p-10">
      <h1 className="text-3xl font-bold">Multichain Banking DApp</h1>
      <button onClick={connectWallet} className="mt-5 px-4 py-2 bg-blue-500 text-white rounded">
        {account ? `Connected: ${account.substring(0, 6)}...` : "Connect Wallet"}
      </button>
      <div className="mt-5">
        <input
          type="text"
          placeholder="Amount"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          className="p-2 border rounded mr-2"
        />
        <button onClick={depositETH} className="px-4 py-2 bg-green-500 text-white rounded">
          Deposit ETH
        </button>
      </div>
      <div className="mt-5">
        <input
          type="text"
          placeholder="ERC-20 Token Address"
          value={tokenAddress}
          onChange={(e) => setTokenAddress(e.target.value)}
          className="p-2 border rounded mr-2"
        />
        <button onClick={depositERC20} className="px-4 py-2 bg-yellow-500 text-white rounded">
          Deposit ERC-20
        </button>
      </div>
      <div className="mt-5">
        <h2 className="text-xl">Transaction History</h2>
        {transactionHistory.length > 0 ? (
          transactionHistory.map((tx, index) => (
            <div key={index} className="border p-2 mt-2 rounded">
              {tx}
            </div>
          ))
        ) : (
          <p>No transactions yet</p>
        )}
      </div>
    </div>
  );
}

This React frontend enables users to:

  • Connect their wallet
  • Deposit ETH and ERC-20 tokens
  • Withdraw funds
  • View transaction history
  • Automatically detect supported networks (Arbitrum, Optimism, Polygon zkEVM)

Conclusion

This Layer 2 multichain banking DApp enables users to deposit, withdraw, and earn interest across Arbitrum, Optimism, and Polygon zkEVM. Future enhancements will include cross-chain transactions and staking rewards.

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *