Skip to content

Wagmi

Wagmi 通过关注开发人员体验、性能、功能覆盖率和稳定性,使应用程序开发人员能够专注于为以太坊构建高质量、高性能的体验,它为开发人员提供了直观的构建块来构建他们的以太坊应用程序。 本篇将介绍如何将Bitget WalletBitget Wallet Lite集成到Wagmi体系中。

快速开始

1.安装依赖

js
pnpm add wagmi
pnpm add wagmi

2.配置 chains 和 provider

js
import { configureChains, allChains } from "wagmi";
import { alchemyProvider } from "wagmi/providers/alchemy";
import { publicProvider } from "wagmi/providers/public";

// Configure chains & providers with the Alchemy provider.
// Two popular providers are Alchemy (alchemy.com) and Infura (infura.io)
const { chains, provider } = configureChains(allChains, [
  alchemyProvider(),
  publicProvider(),
]);
import { configureChains, allChains } from "wagmi";
import { alchemyProvider } from "wagmi/providers/alchemy";
import { publicProvider } from "wagmi/providers/public";

// Configure chains & providers with the Alchemy provider.
// Two popular providers are Alchemy (alchemy.com) and Infura (infura.io)
const { chains, provider } = configureChains(allChains, [
  alchemyProvider(),
  publicProvider(),
]);

3.创建 BitgetWalletConnector

js
import { InjectedConnector } from "wagmi/connectors/injected";

class BitgetWalletConnector extends InjectedConnector {
  constructor({ chains = [], options_ = {} }) {
    const options = {
      name: "BitgetWallet",
      ...options_,
    };
    super({ chains, options });

    this.id = "BitgetWallet";
    this.ready =
      typeof window != "undefined" &&
      !!this.findProvider(window?.bitkeep?.ethereum);
  }
  async getProvider() {
    if (typeof window !== "undefined") {
      // TODO: Fallback to `ethereum#initialized` event for async injection
      // https://github.com/BitKeep/detect-provider#synchronous-and-asynchronous-injection=
      this.provider = window.bitkeep?.ethereum;
    }
    return this.provider;
  }
  getReady(ethereum) {
    if (!ethereum.isBitKeep || !ethereum) return;
    // Brave tries to make itself look like BitKeep
    // Could also try RPC `web3_clientVersion` if following is unreliable
    if (ethereum.isBraveWallet && !ethereum._events && !ethereum._state) return;
    if (ethereum.isTokenPocket) return;
    if (ethereum.isTokenary) return;
    return ethereum;
  }
  findProvider(ethereum) {
    if (ethereum?.providers) return ethereum.providers.find(this.getReady);
    return this.getReady(ethereum);
  }
}
import { InjectedConnector } from "wagmi/connectors/injected";

class BitgetWalletConnector extends InjectedConnector {
  constructor({ chains = [], options_ = {} }) {
    const options = {
      name: "BitgetWallet",
      ...options_,
    };
    super({ chains, options });

    this.id = "BitgetWallet";
    this.ready =
      typeof window != "undefined" &&
      !!this.findProvider(window?.bitkeep?.ethereum);
  }
  async getProvider() {
    if (typeof window !== "undefined") {
      // TODO: Fallback to `ethereum#initialized` event for async injection
      // https://github.com/BitKeep/detect-provider#synchronous-and-asynchronous-injection=
      this.provider = window.bitkeep?.ethereum;
    }
    return this.provider;
  }
  getReady(ethereum) {
    if (!ethereum.isBitKeep || !ethereum) return;
    // Brave tries to make itself look like BitKeep
    // Could also try RPC `web3_clientVersion` if following is unreliable
    if (ethereum.isBraveWallet && !ethereum._events && !ethereum._state) return;
    if (ethereum.isTokenPocket) return;
    if (ethereum.isTokenary) return;
    return ethereum;
  }
  findProvider(ethereum) {
    if (ethereum?.providers) return ethereum.providers.find(this.getReady);
    return this.getReady(ethereum);
  }
}

4.创建 BitgetWalletLiteConnector

js
import { InjectedConnector } from "wagmi/connectors/injected";
import {
  OmniConnect
} from "@bitget-wallet/omni-connect";

class BitgetWalletLiteConnector extends InjectedConnector {
  constructor({ chains = [], options_ = {} }) {
    const options = {
      name: "BitgetWallet",
      ...options_,
    };
    super({ chains, options });

    this.id = "BitgetWallet";
    this.ready =
      typeof window != "undefined" &&
      !!this.findProvider(window?.bitkeep?.ethereum);

    this.omni = new OmniConnect({
        metadata: {
          name: options_.dappName,
          iconUrl: options_.dappIconUrl,
          url: options_.dappUrl,
          privacyPolicyUrl: "",
          termsOfUseUrl: "",
        },
        namespace: {
          eip155: {
            chains: chains,
            // methods: ["eth_sign", "personal_sign"],
            // events: ["accountsChanged", "chainChanged"],
          },
        },
      });
  }
  async getProvider() {
    if (typeof window !== "undefined") {
      // TODO: Fallback to `ethereum#initialized` event for async injection
      // https://github.com/BitKeep/detect-provider#synchronous-and-asynchronous-injection=
      
      this.provider = this.omni?.eip155?.provider
    }
    return this.provider;
  }
  getReady(ethereum) {
    if (!ethereum.isBitKeep || !ethereum) return;
    // Brave tries to make itself look like BitKeep
    // Could also try RPC `web3_clientVersion` if following is unreliable
    if (ethereum.isBraveWallet && !ethereum._events && !ethereum._state) return;
    if (ethereum.isTokenPocket) return;
    if (ethereum.isTokenary) return;
    return ethereum;
  }
  findProvider(ethereum) {
    if (ethereum?.providers) return ethereum.providers.find(this.getReady);
    return this.getReady(ethereum);
  }
}
import { InjectedConnector } from "wagmi/connectors/injected";
import {
  OmniConnect
} from "@bitget-wallet/omni-connect";

class BitgetWalletLiteConnector extends InjectedConnector {
  constructor({ chains = [], options_ = {} }) {
    const options = {
      name: "BitgetWallet",
      ...options_,
    };
    super({ chains, options });

    this.id = "BitgetWallet";
    this.ready =
      typeof window != "undefined" &&
      !!this.findProvider(window?.bitkeep?.ethereum);

    this.omni = new OmniConnect({
        metadata: {
          name: options_.dappName,
          iconUrl: options_.dappIconUrl,
          url: options_.dappUrl,
          privacyPolicyUrl: "",
          termsOfUseUrl: "",
        },
        namespace: {
          eip155: {
            chains: chains,
            // methods: ["eth_sign", "personal_sign"],
            // events: ["accountsChanged", "chainChanged"],
          },
        },
      });
  }
  async getProvider() {
    if (typeof window !== "undefined") {
      // TODO: Fallback to `ethereum#initialized` event for async injection
      // https://github.com/BitKeep/detect-provider#synchronous-and-asynchronous-injection=
      
      this.provider = this.omni?.eip155?.provider
    }
    return this.provider;
  }
  getReady(ethereum) {
    if (!ethereum.isBitKeep || !ethereum) return;
    // Brave tries to make itself look like BitKeep
    // Could also try RPC `web3_clientVersion` if following is unreliable
    if (ethereum.isBraveWallet && !ethereum._events && !ethereum._state) return;
    if (ethereum.isTokenPocket) return;
    if (ethereum.isTokenary) return;
    return ethereum;
  }
  findProvider(ethereum) {
    if (ethereum?.providers) return ethereum.providers.find(this.getReady);
    return this.getReady(ethereum);
  }
}

5.设置 client

js
import { createClient } from "wagmi";
import { MetaMaskConnector } from "wagmi/connectors/metaMask";
import BitgetWalletConnector from "./connectors/BitgetWalletConnector";

const mainnetIds = [1, 10, 137, 42161]; // BitgetWallet only supports the  mainnet network for the time being
// Set up client
const client = createClient({
  autoConnect: true,
  connectors: [
    new MetaMaskConnector({ chains }),
    new BitgetWalletConnector({
      chains: chains.filter((v) => mainnetIds.includes(v.id)),
      options: {
        name: "BitgetWallet",
        icon: "./assets/bitkeep-icon.png",
      },
    }),
     new BitgetWalletConnector({
      chains: chains.filter((v) => mainnetIds.includes(v.id)),
      options: {
        name: "BitgetWallet",
        iconUrl: "./assets/bitkeep-icon.png",
        dappName:'your dappName',
        dappIconUrl:'your dappIconUrl',
        dappUrl:'your dappUrl',
      },
    }),
  ],
  provider,
});
import { createClient } from "wagmi";
import { MetaMaskConnector } from "wagmi/connectors/metaMask";
import BitgetWalletConnector from "./connectors/BitgetWalletConnector";

const mainnetIds = [1, 10, 137, 42161]; // BitgetWallet only supports the  mainnet network for the time being
// Set up client
const client = createClient({
  autoConnect: true,
  connectors: [
    new MetaMaskConnector({ chains }),
    new BitgetWalletConnector({
      chains: chains.filter((v) => mainnetIds.includes(v.id)),
      options: {
        name: "BitgetWallet",
        icon: "./assets/bitkeep-icon.png",
      },
    }),
     new BitgetWalletConnector({
      chains: chains.filter((v) => mainnetIds.includes(v.id)),
      options: {
        name: "BitgetWallet",
        iconUrl: "./assets/bitkeep-icon.png",
        dappName:'your dappName',
        dappIconUrl:'your dappIconUrl',
        dappUrl:'your dappUrl',
      },
    }),
  ],
  provider,
});

在 React App 内使用

jsx
import {
  WagmiConfig,
  useConnect,
  useAccount,
  useDisconnect,
  useSignMessage,
  useNetwork,
  useSwitchNetwork,
} from "wagmi";
import { verifyMessage } from "ethers/lib/utils";

const { isConnected, connector } = useAccount();
const { connect, connectors, error, isLoading, pendingConnector } =
  useConnect();
const { disconnect } = useDisconnect();

export function NetworkSwitcher() {
  const { chain } = useNetwork();
  const { chains, error, isLoading, pendingChainId, switchNetwork } =
    useSwitchNetwork();

  if (!chain) return null;

  return (
    <div>
      <div>
        Connected to {chain?.name ?? chain?.id}
        {chain?.unsupported && " (unsupported)"}
      </div>

      {switchNetwork && (
        <div>
          {chains.map((x) =>
            x.id === chain?.id ? null : (
              <button key={x.id} onClick={() => switchNetwork(x.id)}>
                {x.name}
                {isLoading && x.id === pendingChainId && " (switching)"}
              </button>
            )
          )}
        </div>
      )}

      <div>{error && error.message}</div>
    </div>
  );
}

function SignMessage() {
  const recoveredAddress = React.useRef();
  const { data, error, isLoading, signMessage } = useSignMessage({
    onSuccess(data, variables) {
      // Verify signature when sign message succeeds
      const address = verifyMessage(variables.message, data);
      recoveredAddress.current = address;
    },
  });

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const formData = new FormData(event.target);
        const message = formData.get("message");
        signMessage({ message });
      }}
    >
      <label htmlFor="message">Enter a message to sign</label>
      <textarea
        id="message"
        name="message"
        placeholder="The quick brown fox…"
      />
      <button disabled={isLoading}>
        {isLoading ? "Check Wallet" : "Sign Message"}
      </button>

      {data && (
        <div>
          <div>Recovered Address: {recoveredAddress.current}</div>
          <div>Signature: {data}</div>
        </div>
      )}

      {error && <div>{error.message}</div>}
    </form>
  );
}

export function Profile() {
  const { isConnected, connector } = useAccount();
  const { connect, connectors, error, isLoading, pendingConnector } =
    useConnect();
  const { disconnect } = useDisconnect();

  return (
    <div>
      <div className="block">
        {isConnected && (
          <button onClick={() => disconnect()}>
            Disconnect from {connector?.name}
          </button>
        )}
        {connectors.map((connector) => (
          <button
            disabled={!connector.ready}
            key={connector.id}
            onClick={() => connect({ connector })}
          >
            <img src={connector.icon}></img>
            {connector.name}
            {!connector.ready && " (unsupported)"}
            {isLoading &&
              connector.id === pendingConnector?.id &&
              " (connecting)"}
          </button>
        ))}
        {error && <div>{error.message}</div>}
      </div>

      <div className="block">
        <Account></Account>
      </div>
      <div className="block">
        <NetworkSwitcher></NetworkSwitcher>
      </div>

      {isConnected && (
        <div className="block">
          <SignMessage />
        </div>
      )}
    </div>
  );
}

function App() {
  return (
    <div className="App">
      <WagmiConfig client={client}>
        <Profile />
      </WagmiConfig>
    </div>
  );
}
import {
  WagmiConfig,
  useConnect,
  useAccount,
  useDisconnect,
  useSignMessage,
  useNetwork,
  useSwitchNetwork,
} from "wagmi";
import { verifyMessage } from "ethers/lib/utils";

const { isConnected, connector } = useAccount();
const { connect, connectors, error, isLoading, pendingConnector } =
  useConnect();
const { disconnect } = useDisconnect();

export function NetworkSwitcher() {
  const { chain } = useNetwork();
  const { chains, error, isLoading, pendingChainId, switchNetwork } =
    useSwitchNetwork();

  if (!chain) return null;

  return (
    <div>
      <div>
        Connected to {chain?.name ?? chain?.id}
        {chain?.unsupported && " (unsupported)"}
      </div>

      {switchNetwork && (
        <div>
          {chains.map((x) =>
            x.id === chain?.id ? null : (
              <button key={x.id} onClick={() => switchNetwork(x.id)}>
                {x.name}
                {isLoading && x.id === pendingChainId && " (switching)"}
              </button>
            )
          )}
        </div>
      )}

      <div>{error && error.message}</div>
    </div>
  );
}

function SignMessage() {
  const recoveredAddress = React.useRef();
  const { data, error, isLoading, signMessage } = useSignMessage({
    onSuccess(data, variables) {
      // Verify signature when sign message succeeds
      const address = verifyMessage(variables.message, data);
      recoveredAddress.current = address;
    },
  });

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const formData = new FormData(event.target);
        const message = formData.get("message");
        signMessage({ message });
      }}
    >
      <label htmlFor="message">Enter a message to sign</label>
      <textarea
        id="message"
        name="message"
        placeholder="The quick brown fox…"
      />
      <button disabled={isLoading}>
        {isLoading ? "Check Wallet" : "Sign Message"}
      </button>

      {data && (
        <div>
          <div>Recovered Address: {recoveredAddress.current}</div>
          <div>Signature: {data}</div>
        </div>
      )}

      {error && <div>{error.message}</div>}
    </form>
  );
}

export function Profile() {
  const { isConnected, connector } = useAccount();
  const { connect, connectors, error, isLoading, pendingConnector } =
    useConnect();
  const { disconnect } = useDisconnect();

  return (
    <div>
      <div className="block">
        {isConnected && (
          <button onClick={() => disconnect()}>
            Disconnect from {connector?.name}
          </button>
        )}
        {connectors.map((connector) => (
          <button
            disabled={!connector.ready}
            key={connector.id}
            onClick={() => connect({ connector })}
          >
            <img src={connector.icon}></img>
            {connector.name}
            {!connector.ready && " (unsupported)"}
            {isLoading &&
              connector.id === pendingConnector?.id &&
              " (connecting)"}
          </button>
        ))}
        {error && <div>{error.message}</div>}
      </div>

      <div className="block">
        <Account></Account>
      </div>
      <div className="block">
        <NetworkSwitcher></NetworkSwitcher>
      </div>

      {isConnected && (
        <div className="block">
          <SignMessage />
        </div>
      )}
    </div>
  );
}

function App() {
  return (
    <div className="App">
      <WagmiConfig client={client}>
        <Profile />
      </WagmiConfig>
    </div>
  );
}

API 参考

wagmi