Integration Tutorial (Mumbai)

Introduction​

In this tutorial we will show how you can integrate Banana Wallet to your JavaScript or TypeScript-based frontend created using Next. We will demonstrate how to create a new wallet or connect an existing Banana Wallet on any dApp on mumbai chain.

Prerequisites​

  • Basic JavaScript/Typescript knowledge.

  • Previous experience building Next application

  • Enthusiasm to build an amazing dApp on Astar.

Getting started​

Step 1: Create a brand new Next app

Create a new Next project using an existing project template available here with 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 banana sdk package​

Install @rize-labs/banana-wallet-sdk package with

npm install @rize-labs/banana-wallet-sdk
or
yarn add @rize-labs/banana-wallet-sdk

Step 3: Smart Contract deployment​

For this demo, we will be using a very basic smart contract with only two functionalities:

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

  • Fetch value of state variable.

Code for smart contract

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 the contract on Mumbai Testnet using remix or something of your own choice.

For this demo we had already deployed it here:

0x3EEe2FbB0e698531fca47f6d6A9cecF48aDBcB0c

Step 4: Building the front end​

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. Everything is handled by us in the SDK so you can concentrate on building your dApp.

Step 5: Imports​

Next project by default sets up pages/index.js which would be rendered via home route / In this tutorial, we will be writing code in index.js only so that the above frontend would be available over the / (home) route.

import { Banana, Chains } from "@rize-labs/banana-wallet-sdk";
import { useEffect, useState } from "react";
import { ethers } from "ethers";
import { SampleAbi } from "../abi/SampleAbi";

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.

The code in _app.js will look like this

import '../styles/globals.css'

// This default export is required in a new `pages/_app.js` file.
export default function MyApp({ Component, pageProps }) {
    return <Component {...pageProps} />
}

Initializing some states for demo

const [walletAddress, setWalletAddress] = useState("");
const [bananaSdkInstance, setBananSdkInstance] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [walletInstance, setWalletInstance] = useState(null);
const [output, setOutput] = useState("Welcome to Banana Demo");

// contract deployed over polygon mumbaiu testnet
const SampleContractAddress = "0x3EEe2FbB0e698531fca47f6d6A9cecF48aDBcB0c";

Step 6: Initializing Banana SDK instance and creating methods​

// Invoke sdk instance creation
useEffect(() => {
    getBananaInstance();
}, []);

const getBananaInstance = () => {
   // creating sdk instance for mumbai chain
   const bananaInstance = new Banana(Chains.mumbai);
   // setting sdk instance
   setBananSdkInstance(bananaInstance);
};

For simplicity, in this example, we are creating an SDK instance for Polygon Mumbai.

Creating Wallet

const createWallet = async () => {
    // starts loading
    setIsLoading(true);
    
    // creating wallet
    const wallet = await bananaSdkInstance.createWallet();
    setWalletInstance(wallet);
    
    // getting address for wallet created
    const address = await wallet.getAddress();
    setWalletAddress(address);
    setOutput("Wallet Address: " + address);
    setIsLoading(false);
 };

Developers need to call the createWallet method which will inherently ask the user for a wallet name. Once the username is provided, the wallet is initialized for the user, and the method returns an instance of the wallet.

Connecting wallet

const connectWallet = async () => {
    
    // checking does wallet name is cached in cookie 
    const walletName = bananaSdkInstance.getWalletName();
    
    // if cached we will use it 
    if (walletName) {
      setIsLoading(true);

     // connect wallet with cached wallet name
      const wallet = await bananaSdkInstance.connectWallet(walletName);
      setWalletInstance(wallet);

      // extracting wallet address for display purpose
      const address = await wallet.getAddress();
      setWalletAddress(address);
      setOutput("Wallet Address: " + address);
      setIsLoading(false);
    } else {
      setIsLoading(false);
      alert("You don't have wallet created!");
    }
  };
  

When the user wallet is created the wallet's public data is cached in the user's cookie. Once the getWalletName function fetches walletName from the cookie, we pass walletName it into connectWallet which initializes and configures some wallet parameters internally, and returns a wallet instance.

Get ChainId

  const getChainId = async () => {
    setIsLoading(true);
    const signer = walletInstance.getSigner();
    const chainid = await signer.getChainId();
    setOutput(JSON.stringify(chainid));
    setIsLoading(false);
  };

Getting chainId is pretty straightforward. Developers should extract the signer from the wallet and use getChainId it to obtain chainId of the current network.

Get Network

  const getNetwork = async () => {
    setIsLoading(true);
    const provider = walletInstance.getProvider();
    const network = await provider.getNetwork();
    setOutput(JSON.stringify(network));
    setIsLoading(false);
  };

Extracting the network is as easy as it looks. Developers should extract the provider from the wallet and use the getNetwork method to obtain the chain info.

Make transaction

   const makeTransaction = async () => {
    setIsLoading(true);
    
    // getting signer
    const signer = walletInstance.getSigner();
    const amount = "0.00001";
    const tx = {
      gasLimit: "0x55555",
      to: SampleContractAddress,
      value: ethers.utils.parseEther(amount),
      data: new ethers.utils.Interface(SampleAbi).encodeFunctionData(
        "stake",
        []
      ),
    };

    try {
      // sending txn object via signer
      const txn = signer.sendTransaction(tx);
      setOutput(JSON.stringify(txn));
    } catch (err) {
      console.log(err);
    }
    setIsLoading(false);
  };

To initiate a transaction you will create a transaction object. Extract the signer from the wallet instance and initiate a transaction by passing the transaction object to the send transaction method. PS: Make sure your wallet is funded before you initiate transactions.

Signing message

  const signMessage = async () => {
    setIsLoading(true);
    const sampleMsg = "Hello World";
    const signer = walletInstance.getSigner();
    const signMessageResponse = await signer.signBananaMessage(sampleMsg);
    setOutput(JSON.stringify(signMessageResponse));
    setIsLoading(false);
  };

Signing a message is as simple as it looks. Pass a message that needs to be signed, and the method will return an object { messageSigned: "", signature: "" }

messageSigned: message that was signed.

signature: signature for the signed message.

Step 7: Building the frontend​

JSX code for frontend

<div className="App">
  <h1>Banana SDK Demo</h1>
  {walletAddress && <p> Wallet Address: {walletAddress}</p>}
  <button className="btn" onClick={() => createWallet()}>
    Create Wallet
  </button>
  <button className="btn" onClick={() => connectWallet()}>
    Connect Wallet
  </button>
  <button className="btn" onClick={() => getChainId()}>
    ChainId
  </button>
  <button className="btn" onClick={() => getNetwork()}>
    Network
  </button>
  <button className="btn" onClick={() => makeTransaction()}>
    Make transaction
  </button>
  <button className="btn" onClick={() => signMessage()}>
    Sign message
  </button>
  <h1> Output Panel</h1>
  <div className="output-div">
    <p>{isLoading ? "Loading.." : output}</p>
  </div>
</div>

If you are still unable to resolve the issue please post your query to Banana Discord here

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

Last updated