Skip to content

Latest commit

 

History

History
278 lines (221 loc) · 8.28 KB

migration-notes.md

File metadata and controls

278 lines (221 loc) · 8.28 KB

Draft: Migration document from react-celo to rainbowkit

Hello devs 🌱 this is a migration path away from react-celo, formerly known as use-contractkit. With the rise of ethers and viem, came really cool "Modal libraries" to help connect to dapps. This guide will cover rainbowkit and how to start using it instead of react-celo. This will mention a couple key differences between contractkit+web3, powering react-celo and viem, powering rainbowkit, but a more detailled guide can be found here

Requirements

npm install @rainbow-me/rainbowkit@2 wagmi@2 [email protected] @tanstack/react-query

Optional recommended packages

npm install @celo/abis

Initialization

// react-celo
import { CeloProvider } from '@celo/react-celo';
import '@celo/react-celo/lib/styles.css';

function WrappedApp() {
  return (
    <CeloProvider
      dapp={{
        name: 'My awesome dApp',
        description: 'My awesome description',
        url: 'https://example.com',
        // if you plan on supporting WalletConnect compatible wallets, you need to provide a project ID, you can find it here: https://docs.walletconnect.com/2.0/cloud/relay
        walletConnectProjectId: '123',
      }}
    >
      <App />
    </CeloProvider>
  );
}
// rainbowkit
import '@rainbow-me/rainbowkit/styles.css'

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WagmiProvider, http } from 'wagmi'
import { celo, celoAlfajores } from 'wagmi/chains';
import { RainbowKitProvider } from '@rainbow-me/rainbowkit'
import { getDefaultConfig } from '@rainbow-me/rainbowkit'

const config = getDefaultConfig({
   appName: 'Your app',
   projectId: 'YOUR_PROJECT_ID',
   chains: [celo, celoAlfajores],
   transports: {
     [celo.id]: http(),
     [celoAlfajores.id]: http(),
   },
 })

function WrappedApp() {
  return (
    <WagmiProvider config={config}>
       <QueryClientProvider client={queryClient}>
        <RainbowKitProvider>
       {/* Your App */}
       </RainbowKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
}

While this may seem like more boilerplate, and it is, however it's also a lot more flexible since it can handle others chains out of the box, contrary to react-celo.

For example:

import { configureChains } from 'wagmi';
- import { celo, celoAlfajores } from 'wagmi/chains';
+ import { celo, celoAlfajores, avalanche, mainnet } from 'wagmi/chains';

const { chains, publicClient } = configureChains(
-   [celo, celoAlfajores],
+   [celo, celoAlfajores, mainnet, avalanche],
  ...
);

Basic usage

Here we will cover the most basic interactions your dapp will have to implement to be able to interact with its users: connecting to a wallet, sending a transaction, displaying some chain data.

Connecting your dapp to a wallet

// react-celo
import { useCelo } from '@celo/react-celo';

function App() {
  const { connect, address } = useCelo();

  return (
    <>
      {address ? (
        <div>Connected to {address}</div>
      ) : (
        <button onClick={connect}>Connect wallet</button>
      )}
    </>
  );
}
// rainbowkit
import { ConnectButton } from '@rainbow-me/rainbowkit';

function App() {
  const { connect, address } = useCelo();

  return <ConnectButton />;
}

More details regarding the connect button props in the rainbowkit docs

Reading chain data

Firstly, you need to be aware that rainbowkit is a more general library than react-celo ever claimed to be. So it's natural some more specific Celo helpers/boilerplate will need to be implemented on your side. Thankfully, it should be very few, and probably even fewer as time goes.

// ./use-celo-registry-address.ts
// Helper to interact with the celo registry, abstracted in contractkit
// We will probably add it to rainbowkit-celo, but until then, feel free
// to copy the following snippet in your codebase
import { useContractRead } from 'wagmi';
import { registryABI } from '@celo/abis/types/wagmi';

export default function useCeloRegistryAddress(contractName: string) {
  const { data: address } = useContractRead({
    address: '0x000000000000000000000000000000000000ce10', // constant on mainnet and alfajores
    abi: registryABI,
    functionName: 'getAddressForString',
    args: [contractName],
  });

  if (address && parseInt(address, 16) === 0) {
    return undefined;
  }
  return address;
}

You will see in the example below, the paradigm shifts from using imperative async functions to gather data to a more declarative "react-y" style, using hooks.

With rainbowkit using wagmi under the hood, the possiblities are pretty much endless, so here is a straightforward example trying to read data from a contract.

- import { useCelo } from '@celo/react-celo';
+ import { useAccount } from 'wagmi';
+ import { accountsABI } from '@celo/abis/types/wagmi';
+ import useCeloRegistryAddress from './use-celo-registry-address';

+ function useAccountSummary() {
+     const { isConnected, address } = useAccount();
+     const { data: accountSummary } = useContractRead({
+         address: useCeloRegistryAddress('Accounts'),
+         abi: accountsABI,
+         functionName: 'getAccountSummary',
+     });
+
+     return accountSummary
+ }

function App() {
-  const { kit, address } = useCelo();
-
-  async function getAccountSummary() {
-    const accounts = await kit.contracts.getAccounts();
-    await accounts.getAccountSummary(address);
-  }
+  const accountSummary = useAccountSummary();

  return (
    ...
  )
}

Send a transaction

// react-celo
import { useCelo } from '@celo/react-celo';

function App() {
  const { performActions } = useCelo();

  async function transfer() {
    await performActions(async (kit) => {
      const cUSD = await kit.contracts.getStableToken();
      await cUSD.transfer('0x...', 10000).sendAndWaitForReceipt();
    });
  }

  return <button onClick={transfer}>Transfer</button>;
}

Rainbowkit doesn't support a similar function as performActions out of the box, but you may achieve a similar result using react-modal and a simple useState.

// rainbowkit
import { usePrepareContractWrite, useContractWrite } from 'wagmi';
import { StableTokenABI } from '@celo/abis/types/wagmi';
import useCeloRegistryAddress from './use-celo-registry-address';
// optional
import MyCustomModal from './modal';

function App() {
  // optional modal state
  const [isOpen, setIsOpen] = useState(false);

  const { config } = usePrepareContractWrite({
    address: useCeloRegistryAddress('StableToken'),
    abi: StableTokenABI,
    functionName: 'transfer',
    args: ['0x...', 10000],
    onSettle: () => setIsOpen(false),
  });
  const { write } = useContractWrite(config);
  const transfer = useCallback(() => {
    setIsOpen(true);
    write();
  }, [write]);

  return;
  <>
    <button onClick={transfer}>Transfer</button>
    {isOpen && <MyCustomModal />}
  </>;
}

Fee currency

While react-celo provides a feeCurrency variable and an updateFeeCurrency helper method, this isn't the case for rainbowkit. However, [email protected]+ also supports feeCurrency out of the box thanks to its Celo-specific block and transactions formatters.

import { useSendTransaction } from 'wagmi'

function App() {
  const { sendTransaction } = useSendTransaction()

  return (
    <button
      onClick={() =>
        sendTransaction({
          to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
          value: parseEther('0.01'),
          feeCurrency: '<USDC TOKEN ADDRESS HERE>'
        })
      }
    >
      Send transaction
    </button>
  )
}

Further reading

For more in depth examples and documentation about wagmi specifically, I highly recommend checking out the extensive documentations of wagmi. Rainbowkit also does a good job at documenting their library so make sure to check it out!

Another interesting application to help you migrate could be StCelo-v2. You can checkout the changes going from react-celo + contractkit to rainbowkit + wagmi + viem in this pull-request.