# 以太坊(EVM)

Bitget Wallet 在其用户访问的网站中注入一个全局 API,地址是window?.bitkeep?.ethereum。这个 API 允许网站请求用户的以太坊账户,从用户连接的区块链上读取数据,并建议用户签署信息和交易。提供者对象的存在表明是一个以太坊用户。

# 检测以太坊提供者

function getProvider() {
  const provider = window.bitkeep && window.bitkeep.ethereum;
  if (!provider) {
    window.open('https://web3.bitget.com/zh-CN/wallet-download?type=2');
    throw "Please go to our official website to download!!"
  }
  return provider;
}
1
2
3
4
5
6
7
8

# 基本用法

对于任何有一定复杂度的以太坊网络应用--又称 dapp、web3 站点等--来说,你必须:

  1. 检测以太坊提供者(window?.bitkeep?.ethereum)
  2. 检测用户连接到哪个以太坊网络
  3. 获取用户的以太坊账户

你可以参考eth-requestaccountsaddress-conflicts-when-switching-network代码片段

提供者 API 是你创建一个全功能的 web3 应用程序所需要的全部内容。

可以参考三方库的 Web3.0 登录以实现快速接入 Bitget 插件钱包。

提示

使用第三方库时,使用 window.bitkeep.ethereum 作为程序的提供者注入。

//npm install bitkeep-web3modal
// fork from https://github.com/WalletConnect/web3modal  https://github.com/WalletConnect/web3modal/issues/574
import web3modal from 'bitkeep-web3modal';
const web3Modal = new Web3Modal({
  network: 'mainnet', // optional
  cacheProvider: true, // optional
  providerOptions: {
    bitkeep: {
      package: true,
    },
    walletconnect: {
      display: {
        logo: 'data:image/gif;base64,INSERT_BASE64_STRING',
        name: 'Mobile',
        description: 'Scan qrcode with your mobile wallet',
      },
      package: WalletConnectProvider,
      options: {
        infuraId: 'INFURA_ID', // required
      },
    },
  }, // required
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 链 ID 列表

这些是 Bitget Wallet 默认支持的以太坊链的 chainId。更多信息请参考 chainid.network (opens new window)

Hex Decimal Network Hex Decimal Network
0x1 1 Ethereum 0xa 10 Optimism
0x18 24 KardiaChain 0x19 25 Cronos
0x38 56 BNB Chain 0x39 57 Syscoin
0x3d 61 Ethereum Classic 0x42 66 OKX Chain
0x52 82 Meter Mainnet 0x56 86 GateChain
0x58 88 TomoChain 0x64 100 Gnosis Chain
0x6a 106 Velas 0x73 115 Lucky Chain
0x7a 122 Fuse 0x80 128 Heco
0x89 137 Polygon 0xc7 199 BitTorrent
0xfa 250 Fantom 0x120 288 Boba Network
0x141 321 KCC 0x334 820 Callisto Mainnet
0x3e6 998 Lucky Network 0x500 1280 HALO
0x505 1285 Moonriver 0x71a 1818 CUBE
0x7e3 2019 ClassZZ 0x868 2152 Findora
0x8ae 2222 Kava 0x1251 4689 IoTeX
0x2019 8217 KLAY 0x2710 10000 smartBCH
0x4b82 19330 TRUE 0x4ef4 20212 ZSC
0x7f08 32520 Bitgert 0xa4b1 42161 Arbitrum
0xa4ec 42220 Celo 0xa516 42262 Oasis Emerald
0xa86a 43114 AVAX-C 0x116e2 71394 Nervos CKB EVM
0x335f9 210425 PlatON 0x3e900 256256 Caduceus
0xa3488 668808 ASM 0x4e454152 1313161554 Aurora
0x63564c40 1666600000 Harmony

# isConnected()

提示

请注意,这种方法与用户的账户没有任何关系。你可能经常遇到 "connected "这个词,指的是 web3 站点是否可以访问用户的账户。然而,在提供者界面中,"conented "和 "disconnected "指的是提供者是否能向当前链发出RPC请求。

const Provider = getProvider();
Provider.isConnected();
1
2

# request(args)

  const Provider = getProvider();
  interface RequestArguments {
    method: string;
    params?: unknown[] | object;
  }
  Provider.request(args: RequestArguments): Promise<unknown>;
1
2
3
4
5
6

# eth_requestAccounts

提示

EIP-1102 该方法由EIP-1102 (opens new window)指定。它等同于被废弃的bitkeep.ethereum.enable()提供者 API 方法。

在系统内部,它调用 wallet_requestPermissions 的 eth_accounts 权限。由于 eth_accounts 是目前唯一的权限,这个方法是你目前所需要的全部。

返回值

string[] - 单个十六进制以太坊地址字符串数组。

描述

请求用户提供一个以太坊地址来识别。返回一个解析为单个以太坊地址字符串数组的 Promise。如果用户拒绝该请求,Promise 将拒绝并返回 4001 错误。

这个请求会弹出 Bitget Wallet 钱包窗口,通常用按钮来触发,在该请求未有响应时,应禁止用户点击按钮。

如果您没有获取到用户的账户信息,您应该提示用户点击按钮等操作来发起请求。

  • eth_accounts
    • 获取 user
  • eth_chainId
    • 获取 chainid(十六进制)
const Provider = getProvider();
function connect() {
  Provider.request({ method: 'eth_requestAccounts' })
    .then(handleAccountsChainChanged) // address or chainId changed
    .catch((error) => {
      if (error.code === 4001) {
        // EIP-1193 userRejectedRequest error
        console.log('Please connect to BitKeep.');
      } else {
        console.error(error);
      }
    });
}
//if used injected
const accounts = await Provider.request({ method: 'eth_requestAccounts' });
handleAccountsChainChanged(); // updated address or chainID,refer to accountsChanged/chainChanged(events)
const [address] = await Provider.request({ method: 'eth_accounts' }); // [0x1e805A9aB0FB007B4b9D44d598C6404cE292F20D]
const chainId = await Provider.request({ method: 'eth_chainId' }); // 0x1
//if used web3
import Web3 from 'web3';
const accounts = await Provider.request({ method: 'eth_requestAccounts' });
//  [0x1e805A9aB0FB007B4b9D44d598C6404cE292F20D]
const web3 = new Web3(Provider);
handleAccountsChainChanged(); // updated address or chainID, refer to accountsChanged/chainChanged(events)
const accounts = await web3.eth.getAccounts(); // [0x1e805A9aB0FB007B4b9D44d598C6404cE292F20D]
const chainId = await web3.eth.getChainId(); // 0x1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# wallet_watchAsset

EIP-747

这个方法是由EIP-747 (opens new window)规定的。

参数

  • WatchAssetParams - 要观察的资产的元数据。
  interface WatchAssetParams {
    type: 'ERC20'; // In the future, other standards will be supported
    options: {
      address: string; // The address of the token contract
      'symbol': string; // A ticker symbol or shorthand, up to 11 characters
      decimals: number; // The number of token decimals
      image: string; // A string url of the token logo
    };
}
1
2
3
4
5
6
7
8
9

返回值

boolean - 如果该代币被添加,则为 true,否则为 false

描述

要求用户在 Bitget Wallet 中跟踪代币。返回一个布尔值,表示该代币是否被成功添加。

很多以太坊钱包都支持一组代币,通常来自集中管理的代币注册表。wallet_watchAsset 使 web3 应用程序开发人员能够在运行时要求他们的用户跟踪他们钱包中的代币。添加后,token 与通过传统方法(例如集中式注册表)添加的 token 无法区分。

  const Provider = getProvider();
  Provider
    .request({
      method: 'wallet_watchAsset',
      params: {
        type: 'ERC20',
        options: {
          address: '0xb60e8dd61c5d32be8058bb8eb970870f07233155',
          symbol: 'FOO',
          decimals: 18,
          image: 'https://foo.io/token-image.svg',
        },
      },
    })
    .then((success) => {
      if (success) {
        console.log('FOO successfully added to wallet!');
      } else {
        throw new Error('Something went wrong.');
      }
    })
    .catch(console.error);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# wallet_switchEthereumChain/wallet_addEthereumChain

  • wallet_addEthereumChain

    创建一个确认,要求用户添加指定的链到 Bitget Wallet。用户可以选择在添加后切换到该链。

    参数:

    对于rpcUrlsblockExplorerUrls数组,至少需要一个元素,并且只使用第一个元素。

    interface AddEthereumChainParameter {
      chainId: string; // A 0x-prefixed hexadecimal string
      chainName: string;
      nativeCurrency: {
        name: string,
        symbol: string, // 2-6 characters long
        decimals: 18,
      };
      rpcUrls: string[];
      blockExplorerUrls?: string[];
      iconUrls?: string[]; // Currently ignored.
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    返回值

    null - 如果请求成功,该方法返回 null,否则返回错误。

    与 wallet_switchEthereumChain 一起使用

    我们建议将此方法与 wallet_addEthereumChain 一起使用:

    const Provider = getProvider();
    try {
      await Provider.request({
        method: 'wallet_switchEthereumChain',
        params: [{ chainId: '0xf00' }],
      });
    } catch (switchError) {
      // This error code indicates that the chain has not been added to Bitkeep.
      if (switchError.code === 4902) {
        try {
          await ethereum.request({
            method: 'wallet_addEthereumChain',
            params: [
              {
                chainId: '0xf00',
                chainName: '...',
                rpcUrls: ['https://...'] /* ... */,
              },
            ],
          });
        } catch (addError) {
          // handle "add" error
        }
      }
      // handle other "switch" errors
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
  • wallet_switchEthereumChain

    创建一个确认,要求用户切换到指定链ID的链上。

    参数:

    对于 rpcUrlsblockExplorerUrls 数组,至少需要一个元素,并且只使用第一个元素。

    interface SwitchEthereumChainParameter {
      chainId: string; // A 0x-prefixed hexadecimal string
    }
    
    1
    2
    3

    返回值

    null - 如果请求成功,该方法返回 null,否则返回错误。

    如果错误代码(error.code)是4902,那么 Bitget Wallet 没有添加所请求的链,你必须通过wallet_addEthereumChain请求添加它。

    描述

    与任何导致确认出现的方法一样,wallet_switchEthereumChain 应该只在用户直接操作的情况下被调用,例如点击按钮。

    在以下情况下,Bitget Wallet 将自动拒绝该请求:

    • 如果链 ID 格式错误
    • 如果指定的链 ID 未被添加到 Bitget Wallet

# sendTransaction(Transfer)

const Provider = getProvider();
const transactionParameters = {
  nonce: '0x00', // ignored by Bitkeep
  gasPrice: '0x09184e72a000', // customizable by user during Bitkeep confirmation.
  gas: '0x2710', // customizable by user during Bitkeep confirmation.
  to: '0x0000000000000000000000000000000000000000', // Required except during contract publications.
  from: Provider.selectedAddress, // must match user's active address.
  value: '0x00', // Only required to send ether to the recipient from the initiating external account.
  data:
    '0x7f7465737432000000000000000000000000000000000000000000000000000000600057', // Optional, but used for defining smart contract creation and interaction.
  chainId: '0x3', // Used to prevent transaction reuse across blockchains. Auto-filled by Bitkeep.
};
// txHash is a hex string
// As with any RPC call, it may throw an error
const txHash = await Provider.request({
  method: 'eth_sendTransaction',
  params: [transactionParameters],
});
// if used web3
const accounts = await Provider.request({ method: 'eth_requestAccounts' });
const web3 = new Web3(Provider);
const result = await web3.eth.sendTransaction({
  from: Provider.selectedAddress,
  to: '0x0000000000000000000000000000000000000000',
  value: web3.utils.toWei('1', 'ether'),
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 以太坊 JSON-RPC 方法

关于以太坊 JSON-RPC API,请参见Ethereum wiki (opens new window)。 如何使用参考API Playground (opens new window)


  const Provider = getProvider();
  await Provider.request({method:"eth_accounts", params:[]})
  await Provider.request({method:"eth_getBalance", params:[]})
1
2
3
4
5
6

# 事件监听器

当地址和网络改变时通知。使用了eventemitter3 (opens new window)

const Provider = getProvider();
// reomove all listeners
Provider.removeAllListeners();
function handleAccountsChainChanged() {
  Provider.on('accountsChanged', ([address]) => {
    // Handle the new accounts, or lack thereof.
    // "accounts" will always be an array, but it can be empty.
    alert('address changed');
  });
  Provider.on('chainChanged', async (chainId) => {
    // Handle the new chain.
    // Correctly handling chain changes can be complicated.
    // We recommend reloading the page unless you have good reason not to.
    alert('chainid changed');
  });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

另外,一旦开启监听器,监听完后不要忘记删除它们(例如在 React 中的组件卸载)。使用 removeAllListeners 防止多次监听。

const Provider = getProvider();
function handleAccountsChanged(accounts) {
  // ...
}
Provider.on('accountsChanged', handleAccountsChanged);
//remove
Provider.removeAllListeners(); //remove all
Provider.removeListener('accountsChanged', handleAccountsChanged); // only remove accountsChanged
1
2
3
4
5
6
7
8
9

accountsChanged

每当 eth_accounts RPC 方法的返回值发生变化时,Bitget Wallet 提供程序都会发出此事件。eth_accounts 返回一个空数组或包含单个帐户地址的数组。返回的地址(如果有)是允许调用者访问的最近使用的帐户的地址。调用者由其 URL 来源标识,这意味着具有相同来源的所有站点共享相同的权限。

这意味着只要用户暴露的账户地址发生变化,就会发出 accountsChanged。

  const Provider = getProvider();
  Provider.on('accountsChanged', handler: (accounts: Array<string>) => void);
1
2

chainChanged

当前连接的链发生变化时,BitKeep 提供者会发出这个事件。

所有 RPC 请求都提交给当前连接的链。因此,通过监听这个事件来跟踪当前链的ID是非常重要的。

我们强烈建议在链变化时重新加载页面,除非你有充分的理由不这样做。

  const Provider = getProvider();
  Provider.on('accountsChanged', handler: (accounts: Array<string>) => void);
1
2

# 签署数据

  • eth_sign
  • personal_sign
  • eth_signTypedData
  • eth_signTypedData_v3
  • eth_signTypedData_v4

你可以参考文档

# 错误

所有由 Bitget Wallet 提供者抛出或返回的错误都遵循这个接口:

interface ProviderRpcError extends Error {
  message: string;
  code: number;
  data?: unknown;
}
1
2
3
4
5

ethereum.request(args) 方法会及时的抛出错误。你通常可以使用错误代码属性来确定请求失败的原因。常见的代码及其含义包括:

  • 4001
    • 该请求被用户拒绝
  • -32603
    • 内部错误或参数无效

# npm package

# 使用 onboard

提示:

onboard (opens new window) 默认支持了 Bitget Wallet, 可直接使用, 以下是简单的示例

查看官方demo (opens new window)

# 安装

安装 npm 包

yarn add @web3-onboard/core @web3-onboard/injected-wallets ethers
1

然后在应用中初始化

import Onboard from '@web3-onboard/core'
import injectedModule, { ProviderLabel } from '@web3-onboard/injected-wallets'
import { ethers } from 'ethers'
const MAINNET_RPC_URL = 'https://mainnet.infura.io/v3/<INFURA_KEY>'
const injected = injectedModule({
  // do a manual sort of injected wallets so that BitKeep ordered first
  sort: wallets => {
    const bitKeep = wallets.find(
      ({ label }) => label === ProviderLabel.BitKeep
    )
    return (
      [
      bitKeep,
        ...wallets.filter(
          ({ label }) =>
            label !== ProviderLabel.BitKeep
        )
      ]
        // remove undefined values
        .filter(wallet => wallet)
    )
  },
})
const appMetadata = {
  name: 'BitKeep',
  icon: `<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2029_330)">
<rect width="36" height="36" rx="18" fill="#54FFF5"/>
<g filter="url(#filter0_f_2029_330)">
<path d="M1.8957 27.9279C-4.1242 44.8644 28.0774 40.0809 44.9307 35.5721C62.1794 29.8479 50.2574 4.6153 37.8864 4.05686C25.5154 3.49843 39.4203 15.7243 28.9118 19.2162C18.4033 22.7081 9.42056 6.75722 1.8957 27.9279Z" fill="white"/>
</g>
<g filter="url(#filter1_f_2029_330)">
<path d="M12.0251 -6.44486C8.86728 -15.0727 -2.37922 -3.37541 -7.60774 3.55172C-12.5951 11.0869 0.423192 17.5984 5.52949 14.0667C10.6358 10.5349 1.09329 9.84779 4.13027 5.25529C7.16725 0.662788 15.9724 4.33996 12.0251 -6.44486Z" fill="#00FFF0" fill-opacity="0.67"/>
</g>
<g filter="url(#filter2_f_2029_330)">
<path d="M13.5675 31.6991C9.2602 17.2063 -9.29274 24.8386 -18.0308 30.4663C-26.4361 37.1299 -6.47874 56.2979 1.8102 55.3174C10.0991 54.3369 -4.83915 45.9925 0.279432 41.9292C5.39801 37.8658 18.9515 49.8152 13.5675 31.6991Z" fill="#9D81FF"/>
</g>
<g filter="url(#filter3_f_2029_330)">
<path d="M39.6731 -15.0985C30.3816 -26.1625 17.0806 -17.0133 11.5916 -11.0557C6.78848 -4.31133 31.5386 8.04621 38.4077 5.92842C45.2767 3.81064 29.0407 -0.571508 31.9636 -4.68304C34.8865 -8.79457 51.2875 -1.26834 39.6731 -15.0985Z" fill="#4D94FF"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1047 21.4925H19.2197L12.2637 14.4916L19.3092 7.49057L21.2281 5.625H14.8809L6.79724 13.7499C6.38929 14.1594 6.39139 14.8221 6.80142 15.2295L13.1047 21.4925ZM16.7808 14.508H16.7337L16.7803 14.5074L16.7808 14.508ZM16.7808 14.508L23.7363 21.5084L16.6908 28.5094L14.7719 30.375H21.1191L29.2027 22.2506C29.6107 21.8411 29.6086 21.1784 29.1986 20.771L22.8953 14.508H16.7808Z" fill="black"/>
</g>
<defs>
<filter id="filter0_f_2029_330" x="-12.6901" y="-9.80709" width="80.0941" height="63.4814" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="6.92308" result="effect1_foregroundBlur_2029_330"/>
</filter>
<filter id="filter1_f_2029_330" x="-22.5718" y="-23.3422" width="49.4432" height="52.2431" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="6.92308" result="effect1_foregroundBlur_2029_330"/>
</filter>
<filter id="filter2_f_2029_330" x="-33.9015" y="9.51127" width="62.5572" height="59.6884" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="6.92308" result="effect1_foregroundBlur_2029_330"/>
</filter>
<filter id="filter3_f_2029_330" x="-2.86834" y="-34.1391" width="60.4956" height="54.1552" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="6.92308" result="effect1_foregroundBlur_2029_330"/>
</filter>
<clipPath id="clip0_2029_330">
<rect width="36" height="36" rx="18" fill="white"/>
</clipPath>
</defs>
</svg>
  `,
  description: 'The multi-chain wallet chosen by 10 million users',
  recommendedInjectedWallets: [
    { name: 'BitKeep', url: 'https://web3.bitget.com/zh-CN/wallet-download?type=2' },
  ]
}
const onboard = Onboard({
  wallets: [injected],
  chains: [
    {
      id: '0x1',
      token: 'ETH',
      label: 'Ethereum Mainnet',
      rpcUrl: MAINNET_RPC_URL
    }
  ],
  appMetadata
})
const wallets = await onboard.connectWallet()
console.log(wallets)
if (wallets[0]) {
  // create an ethers provider with the last connected wallet provider
  const ethersProvider = new ethers.providers.Web3Provider(wallets[0].provider, 'any')
    // if using ethers v6 this is:
    // ethersProvider = new ethers.BrowserProvider(wallet.provider, 'any')
  const signer = ethersProvider.getSigner()
  // send a transaction with the ethers provider
  const txn = await signer.sendTransaction({
    to: '0x',
    value: 100000000000000
  })
  const receipt = await txn.wait()
  console.log(receipt)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

# 使用 web3modal v1

提示:

web3modal v1 (opens new window), v1 版本默认支持了 Bitget Wallet, 但是官方不推荐使用V1版本了

查看官方demo (opens new window) 我们提供了一个简单的demo (opens new window)

# 安装

安装 npm 包

yarn add web3modal
1

然后在应用中初始化

import Web3 from "web3";
import Web3Modal from "web3modal";
const providerOptions = {
  /* See Provider Options Section */
};
const web3Modal = new Web3Modal({
  network: "mainnet", // optional
  cacheProvider: true, // optional
  providerOptions // required
});
const provider = await web3Modal.connectTo('bitkeep');
const web3 = new Web3(provider);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
最后更新: 2023/9/7 15:02:36