Wagmi
Wagmi 通过关注开发人员体验、性能、功能覆盖率和稳定性,使应用程序开发人员能够专注于为以太坊构建高质量、高性能的体验,它为开发人员提供了直观的构建块来构建他们的以太坊应用程序。 本篇将介绍如何将Bitget Wallet
和 Bitget 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>
);
}