Banana <> Rainbow (Next)

Set up the repo and install packages

Step 1: Create a brand-new Next app

Create a new Next project using an existing project template available here with the name banana-sdk-demo and now let's move into it.

npx create-next-app@latest banana-sdk-demo --use-npm --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter"
cd banana-sdk-demo

Step 2: Installing rainbowkit, wagmi and Banana rainbow plugin package

npm install @rainbow-me/rainbowkit@0.8.1 wagmi@0.9.0 @rize-labs/banana-rainbowkit-plugin

Smart Contracts

We will have a fundamental smart contract for this demo's purpose. There will be two functionalities:

  • Make a transaction to the blockchain by making a state variable change its value.

  • Fetch value of state variable

Here is the smart contract code:

pragma solidity ^0.8.12;

contract Sample {

    uint public stakedAmount = 0;
    
    function stake() external payable {
        stakedAmount = stakedAmount + msg.value;
    }

    function returnStake() external {
        payable(0x48701dF467Ba0efC8D8f34B2686Dc3b0A0b1cab5).transfer(stakedAmount);
    }
}

You can deploy it using remix.ethereum.org and save the smart contract address.

For your reference, a sample contract has been deployed on polygon Mumbai here it's address:

0x3EEe2FbB0e698531fca47f6d6A9cecF48aDBcB0c

Building the frontend

We will have a simple front end with some buttons to interact with the blockchain. Although Banana SDK provides you with a smart contract wallet you don't need to worry about its deployment. We handle everything in the SDK so you can just concentrate on building your Dapp.

In _app.js

let's import the required for rainbow Next project by default sets up pages/index.js which would be rendered via the home route / In this tutorial, We will create pages/_app.js from where we would drill down the context of Rainbow and wagmi. Inside _app.js let's import packages from wagmi and Rainbow

Importing packages from Rainbowkit

import "@rainbow-me/rainbowkit/styles.css";
import '../styles/globals.css'
import {
  RainbowKitProvider,
  connectorsForWallets,
} from "@rainbow-me/rainbowkit";
import {
  metaMaskWallet,
  injectedWallet,
  rainbowWallet,
  walletConnectWallet,
} from "@rainbow-me/rainbowkit/wallets";

let's import modules required from wagmi

import { polygonMumbai, optimismGoerli, goerli, gnosis, gnosisChiado  } from "@wagmi/chains";
import { jsonRpcProvider } from 'wagmi/providers/jsonRpc';
import { configureChains, createClient, WagmiConfig } from "wagmi";

Importing BananaWallet plugin and few chains which are not by default avalaible in wagmi or Rainbow from banana-rainbowkit-plugin package

import { BananaWallet, astarChain, shibuyaChain } from '@rize-labs/banana-rainbowkit-plugin'

Configure chains and providers

const { chains, provider } = configureChains( 
  // currently on these three chains are supported by BananaWallet
  [polygonMumbai, optimismGoerli, goerli, gnosis, gnosisChiado, astarChain, shibuyaChain ],
   [
      jsonRpcProvider({
        rpc: chain => ({ http: chain.rpcUrls.default.http[0] }),
      }),
    ]

);

Download SampleAbi.js from here SampleAbi.js and put it in abi/SampleAbi.js and for css you can download it from here and put it into styles/globals.css and import it in pages/_app.js after creating _app.js.

Creating connectors for rainbow

  const connectors = connectorsForWallets([
    {
      groupName: "Recommended",
      wallets: [
        BananaWallet({ chains, connect: { networkId: 80001 } }), // networkId: 592 for astar
        metaMaskWallet({ chains, shimDisconnect: true }),
        rainbowWallet({ chains }),
        walletConnectWallet({ chains }),
        injectedWallet({ chains, shimDisconnect: true }),
      ],
    },
  ]);

Creating wagmiClient

  const wagmiClient = createClient({
    autoConnect: true,
    connectors,
    provider,
  });

Providing context of Rainbowkit and wagmi

  return (
    <div className="App">
      <WagmiConfig client={wagmiClient}>
        <RainbowKitProvider chains={chains}>
          <Demo />
        </RainbowKitProvider>
      </WagmiConfig>
    </div>
  );

Putting all together you can find the final App.js file here

Let's start modifying pages/index.js which will contain logic for doing multiple things with wallet instance

Importing Hooks from wagmi

import { useSigner, useProvider, useAccount } from "wagmi";

Extracting signers and providers from wagmi hooks

const { isConnected } = useAccount();
const provider = useProvider();
const { data: signer } = useSigner();

Implementing function to getChainid of current chain

  const getChainID = async () => {
    try {
      //@ts-ignore
      const chainId = await signer.getChainId();
      setOutput(JSON.stringify(chainId.toString()));
    } catch (e) {
      console.error(e);
    }
  };

Implementing function to getBalance of an address

  const getBalance = async () => {
    try {
      //@ts-ignore
      const account = await signer.getAddress();
      const balanceChk1 = await provider!.getBalance(account);
      setOutput(JSON.stringify(balanceChk1));
    } catch (e) {
      console.error(e);
    }
  };

Implementing function to get network info

  const getNetworks = async () => {
    try {
      const network = await provider!.getNetwork();
      setOutput(JSON.stringify(network));
    } catch (e) {
      console.error(e);
    }
  };

Implementing function to sign Message using Banana Wallet

  const signMessage = async () => {
    try {
      setIsLoading(true);
      const message = "Welcome to Banana Wallet";

      //@ts-ignore
      const sig = await signer.signBananaMessage(message);
      setOutput(JSON.stringify(sig));
      setIsLoading(false);
    } catch (e) {
      setIsLoading(false);
      console.error(e);
    }
  };

implementing function to sendEth

  const sendETH = async () => {
    try {
      setIsLoading(true);
      //@ts-ignore
      const toAddress = ethers.Wallet.createRandom().address;

      const tx1 = {
        gasLimit: "0x55555",
        to: toAddress,
        value: ethers.utils.parseEther("0.000001"),
        data: "0x",
      };

      //@ts-ignore
      const txnResp = await signer.sendTransaction(tx1);
      setOutput(JSON.stringify(txnResp));
      setIsLoading(false);
    } catch (e) {
      setIsLoading(false);
      console.error(e);
    }
  };

Implementing function to make call to smart contract method

  const stakeTxn = async () => {
    try {
      setIsLoading(true);
      const amount = "0.00001";

      const tx = {
        gasLimit: "0x55555",
        to: SampleContractAddress,
        value: ethers.utils.parseEther(amount),
        data: new ethers.utils.Interface(SampleAbi).encodeFunctionData(
          "stake",
          []
        ),
      };

      //@ts-ignore
      const txn = await signer.sendTransaction(tx);
      setOutput(JSON.stringify(txn));
      setIsLoading(false)
    } catch (err) {
      setIsLoading(false);
      console.log(err);
    }
  };

Putting all together

Full code can be found here

PS: if you face unexpected export token issue you can fix it by creating next config file in the root of your project and mentioning banana-rainbowkit-plugin package inside transpilePackages array like this.

// next.config.js
const nextConfig = {
    transpilePackages: ['@rize-labs/banana-rainbowkit-plugin'],
};
  
module.exports = nextConfig;

If you have any questions please post them Banana SDK Discord forum.

Last updated