🔗Tokenbound Connectkit

Introduction

ERC-6551 gives NFTs unimaginable capabilities. They empower NFTs to do everything a normal wallet can do. But connecting tokenbound accounts to dApps and executing transactions might be a little bit more complex, as the execute method can only be called by the owner of the NFT bound to the Tokenbound account.

With this connectkit, dApps can now enable tokenbound accounts to seamlessly connect and use their dApps like every normal account on Starknet.

Installation

pnpm install tokenbound-connectkit

Imports

After installing the package, we get access to different methods and types, such as connect, disconnect, TBAStarknetWindowObjectetc which we should import for use in our application.

import {
  connect,
  disconnect,
  TBAStarknetWindowObject,
} from "tokenbound-connectkit";

Now let's take a look at what these methods are:

  • connect: This method is used to establish a connection with the tokenbound account. It provides us with an iFrame that takes in the address of the tokenbound.

  • disconnect: This method is used to disconnect the tokenbound account from your dApp.

  • TBAStarknetWindowObject: A type that specifies the kind of data the wallet connection returns.

Establishing a connection

To establish a wallet connection, we need to call the connect method which we imported earlier. Before that, we need to declare the state variables.

  const [connection, setConnection] = useState<
    TBAStarknetWindowObject | null | undefined
  >(null);
  const [account, setAccount] = useState();

Below is an example function that establishes a connection, and then sets the connection and account states:

  const connectFn = async () => {
    try {
      const { wallet } = await connect({
        tokenboundOptions: {
          chainId: constants.NetworkName.SN_MAIN,
        },
      });
      setConnection(wallet);
      setAccount(wallet?.account);
      setAddress(wallet?.selectedAddress)
    } catch (e) {
      console.error(e);
      alert((e as any).message);
    }
  };

Disconnecting an account

To disconnect an account, the below function simply calls the disconnect method and set the connection to null:

   const disconnectFn = async () => {
    await disconnect();
    setAddress("")
    setAccount(undefined)
    setConnection(null);
  };

Available methods

  1. isConnected - This method available after an attempt to establish a connection, can be used to confirm if an account was truly connected.

  2. selectedAddress - This method can be called to get the wallet address of a connected account.

  3. account - This method gives us access to the account object.

Putting it all together - A simple counter dApp

"use client";
import React, { useState } from "react";
import { constants, Contract, RpcProvider } from "starknet";
import {
  connect,
  disconnect,
  TBAStarknetWindowObject,
} from "tokenbound-connectkit";
import { ABI } from "./utils/abi";

export default function page() {
  const [connection, setConnection] = useState<
    TBAStarknetWindowObject | null | undefined
  >(null);

  const [account, setAccount] = useState();
  const [address, setAddress] = useState("");
  const [retrievedValue, setRetrievedValue] = useState("");

  const contractAddress = "0x077e0925380d1529772ee99caefa8cd7a7017a823ec3db7c003e56ad2e85e300";

  const connectFn = async () => {
    try {
      const { wallet } = await connect({
        tokenboundOptions: {
          chainId: constants.NetworkName.SN_MAIN,
        },
      });
      setConnection(wallet);
      setAccount(wallet?.account);
      setAddress(wallet?.selectedAddress)
    } catch (e) {
      console.error(e);
      alert((e as any).message);
    }
  };

  const disconnectFn = async () => {
    await disconnect();
    setAddress("")
    setAccount(undefined)
    setConnection(null);
  };

  const increaseCounter = async () => {
    try {
      const contract = new Contract(ABI, contractAddress, account).typedv2(ABI);
      await contract.increment();
      alert("you successfully increased the counter");
    } catch (error) {
      console.log(error);
    }
  };

  const decreaseCounter = async () => {
    try {
      const contract = new Contract(ABI, contractAddress, account).typedv2(ABI);
      await contract.decrement();
      alert("you sucessfully decreased the counter");
    } catch (error) {
      console.log(error);
    }
  };

  const getCounter = async () => {
    const provider = new RpcProvider({
      nodeUrl: "https://starknet-mainnet.public.blastapi.io",
    });
    try {
      const contract = new Contract(ABI, contractAddress, provider).typedv2(
        ABI
      );
      const counter = await contract.get_current_count();
      setRetrievedValue(counter.toString());
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <div className="flex flex-col items-center justify-center h-[100vh] space-y-5">
      {!connection ? (
        <button className="button px-5 py-3 bg-[#0C0C4F]" onClick={connectFn}>
          Connect Wallet
        </button>
      ) : (
        <button className="" onClick={disconnectFn}>
          Disconnect
        </button>
      )}

      <header className="">
        {address && (
          <p>
            <b>Address: {address}</b>
          </p>
        )}

        <div className="card py-5">
          <p className="py-5">Increase/Decrease Counter &rarr;</p>
          <div className="cardForm">
            <input
              type="submit"
              value="Increase"
              className="px-5 py-3 bg-[#0C0C4F] cursor-pointer"
              onClick={increaseCounter}
            />
            <input
              type="submit"
              value="Decrease"
              className="bg-red-500 px-5 py-3 cursor-pointer"
              onClick={decreaseCounter}
            />
          </div>
          <hr />
          <p className="pt-10 text-center">Get Counter &rarr;</p>

          <div className="cardForm pt-5 flex items-center justify-center">
            <input
              type="submit"
              className="button cursor-pointer px-5 py-3 bg-[#0C0C4F] rounded-lg"
              value="Get Counter"
              onClick={getCounter}
            />
            <p>{retrievedValue}</p>
          </div>
        </div>
      </header>
    </div>
  );
}

Reference the tokenbound-connectkit-example repository to see full implementation.

The connectkit will be made available for easy use from starknetkit

Last updated