Nextjs+wagmi+rainbowkit构建的Dapp开发模板+使用案例

  • Verin
  • 更新于 2022-08-29 17:21
  • 阅读 6265

Nextjs+wagmi+rainbowkit构建的Dapp开发模板+使用案例

技术栈

本次使用技术栈有:Nextjs+wagmi+rainbowkit+ethers,相对于之前我弄过的web3-react模板的优点:配置文件少很多,开箱即用,各种现成的react hooks使用起来较方便,钱包连接的一些交互状态已经被rainbowkit做过了,视觉体验和ui看起来很舒服。

钱包UI: image.png

钱包小交互:

image.png

项目目录介绍

目录总览: image.png

钱包配置

首先看_app.tsx文件:

import { RainbowKitProvider } from "@rainbow-me/rainbowkit";
import { chains, wagmiClient } from "config/wagmi";
import type { AppProps } from "next/app";
import { useEffect, useState } from "react";
import "styles/globals.css";
import "styles/rainbowkit.css";
import { WagmiConfig } from "wagmi";

function MyApp({ Component, pageProps }: AppProps) {
    const [mounted, setMounted] = useState(false);
    useEffect(() => setMounted(true), []);
    if (!mounted) return null;

    return (
        <WagmiConfig client={wagmiClient}>
            <RainbowKitProvider chains={chains}>
                <Component {...pageProps} />
            </RainbowKitProvider>
        </WagmiConfig>
    );
}

export default MyApp;

两个配置:WagmiConfigRainbowKitProvider主要是来自wagmi.ts:

import { connectorsForWallets, wallet } from "@rainbow-me/rainbowkit";
import { chain, configureChains, createClient } from "wagmi";
import { alchemyProvider } from "wagmi/providers/alchemy";
import { jsonRpcProvider } from "wagmi/providers/jsonRpc";
import { publicProvider } from "wagmi/providers/public";

export const { chains, provider, webSocketProvider } = configureChains(
    [chain.mainnet, chain.goerli, chain.rinkeby, chain.kovan, chain.ropsten],
    [
        alchemyProvider({ apiKey: process.env.NEXT_PUBLIC_ALCHEMY_ID }),
        jsonRpcProvider({ rpc: (chain) => ({ http: chain.rpcUrls.default }) }),
        publicProvider(),
    ],
);

const needsInjectedWalletFallback =
    typeof window !== "undefined" &&
    window.ethereum &&
    !window.ethereum.isMetaMask &&
    !window.ethereum.isCoinbaseWallet;

const connectors = connectorsForWallets([
    {
        groupName: "Popular",
        wallets: [
            wallet.metaMask({ chains, shimDisconnect: true }),
            wallet.brave({ chains, shimDisconnect: true }),
            wallet.rainbow({ chains }),
            wallet.walletConnect({ chains }),
            wallet.coinbase({ appName: "Coinbase", chains }),
            ...(needsInjectedWalletFallback ? [wallet.injected({ chains, shimDisconnect: true })] : []),
        ],
    },
    {
        groupName: "Other",
        wallets: [wallet.trust({ chains, shimDisconnect: true }), wallet.steak({ chains }), wallet.imToken({ chains })],
    },
]);

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

这里主要是钱包配置,包括了支持的链和支持的钱包等,这一个文件基本上涵盖了所有钱包的配置,具体配置可以参考rainbowkit的官网

合约的封装

hooks/useContract:

import { Contract, ContractInterface } from "@ethersproject/contracts";
import { AddressMap } from "config/constants/addresses";
import { ChainId, defaultChainId } from "config/constants/chainId";
import { Providers } from "config/providers";
import { useMemo } from "react";
import { useNetwork, useProvider, useSigner } from "wagmi";

export const createStaticContract = <TContract extends Contract = Contract>(ABI: ContractInterface) => {
    return (address: string, chainId: ChainId) => {
        const provider = Providers.getStaticProvider(chainId);
        return useMemo(() => new Contract(address, ABI, provider) as TContract, [address, provider]);
    };
};

const createDynamicContract = <TContract extends Contract = Contract>(ABI: ContractInterface) => {
    return (addressMap: AddressMap, asSigner = false) => {
        const provider = useProvider();
        const { data: signer } = useSigner();
        const { chain = { id: defaultChainId } } = useNetwork();

        return useMemo(() => {
            const address = addressMap[chain.id as keyof typeof addressMap];

            if (!address) return null;

            const providerOrSigner = asSigner && signer ? signer : provider;

            return new Contract(address, ABI, providerOrSigner) as TContract;
        }, [addressMap, chain.id, asSigner, signer, provider]);
    };
};

// export const useStaticExampleContract = createStaticContract<type>(ABI);

// export const useDynamicExampleContract = createDynamicContract<type>(ABI);

引入的配置直接看文件即可,这里分为两种 1.createStaticContract可以用在无需连接钱包的地方,比如一些查询功能; 2.createDynamicContract则是用在需要钱包连接的功能,一般我用在写合约方法里面;

使用案例

1.在useContract.ts中初始化:

import { Contract, ContractInterface } from "@ethersproject/contracts";
import ExampleABI from "config/abis/example.json";
import { Example } from "config/abis/types";
import { AddressMap } from "config/constants/addresses";
import { ChainId, defaultChainId } from "config/constants/chainId";
import { Providers } from "config/providers";
import { useMemo } from "react";
import { useNetwork, useProvider, useSigner } from "wagmi";
export const createStaticContract = <TContract extends Contract = Contract>(ABI: ContractInterface) => {
    return (address: string, chainId: ChainId) => {
        const provider = Providers.getStaticProvider(chainId);
        return useMemo(() => new Contract(address, ABI, provider) as TContract, [address, provider]);
    };
};

const createDynamicContract = <TContract extends Contract = Contract>(ABI: ContractInterface) => {
    return (addressMap: AddressMap, asSigner = false) => {
        const provider = useProvider();
        const { data: signer } = useSigner();
        const { chain = { id: defaultChainId } } = useNetwork();

        return useMemo(() => {
            const address = addressMap[chain.id as keyof typeof addressMap];

            if (!address) return null;

            const providerOrSigner = asSigner && signer ? signer : provider;

            return new Contract(address, ABI, providerOrSigner) as TContract;
        }, [addressMap, chain.id, asSigner, signer, provider]);
    };
};

// export const useStaticExampleContract = createStaticContract<type>(ABI);

export const useStaticExampleContract = createStaticContract<Example>(ExampleABI);

// export const useDynamicExampleContract = createDynamicContract<type>(ABI);

export const useDynamicExampleContract = createDynamicContract<Example>(ExampleABI);

2.在组件中使用:

import { BasciConnect } from "components/ConnectWallet";
import { EXAMPLE_ADDRESSES } from "config/constants/addresses";
import { ChainId } from "config/constants/chainId";
import { useDynamicExampleContract, useStaticExampleContract } from "hooks/useContract";
import { useEffect, useState } from "react";
import { useAccount } from "wagmi";

const Example = () => {
    const StaticExampleInstance = useStaticExampleContract(EXAMPLE_ADDRESSES[ChainId.RINKEBY], ChainId.RINKEBY);
    const DynamicExampleInstance = useDynamicExampleContract(EXAMPLE_ADDRESSES, true);
    const [count, setCount] = useState("");
    const { isConnected } = useAccount();

    useEffect(() => {
        init();
    }, []);
    const init = async () => {
        const count = await StaticExampleInstance.getCount();
        setCount(count.toString());
    };
    return (
        <div>
            <div>
                <BasciConnect></BasciConnect>

                <div
                    style={{
                        width: "100%",
                        textAlign: "center",
                        fontSize: "24px",
                    }}
                >
                    count (
                    <span
                        style={{
                            color: "gray",
                        }}
                    >
                        read Contract
                    </span>
                    ) :{count ? count : 0}
                </div>
                <>
                    {isConnected ? (
                        <div>
                            <div
                                style={{
                                    width: "200px",
                                    height: "50px",
                                    backgroundColor: "#0076F7",
                                    color: "white",
                                    borderRadius: "20px",
                                    textAlign: "center",
                                    lineHeight: "50px",
                                    fontSize: "14px",
                                    margin: "0 auto",
                                    cursor: "pointer",
                                }}
                                onClick={async () => {
                                    try {
                                        console.log(DynamicExampleInstance);

                                        const tx = await DynamicExampleInstance.setCount();
                                        await tx.wait();
                                        init();
                                    } catch (error) {
                                        console.log(error);
                                    }
                                }}
                            >
                                setCount(write Contract)
                            </div>
                        </div>
                    ) : (
                        <div
                            style={{
                                display: "flex",
                                justifyContent: "center",
                            }}
                        >
                            <BasciConnect></BasciConnect>
                        </div>
                    )}
                </>
            </div>
        </div>
    );
};

export default Example;

合约已经部署过,想体验的可以用rinkeby调用一下案例网站:https://nextjs-wagmi-template.vercel.app/

github:https://github.com/Verin1005/Nextjs-wagmi-template 后续会更新一下Vite+React版本

如果好用,欢迎大家来我git点个star,谢谢。

点赞 3
收藏 5
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

4 条评论

请先 登录 后评论
Verin
Verin
discord:Verin#2256 v: daqingchong-pro 备注来意