Skip to content

Commit

Permalink
Merge pull request WTFAcademy#12 from WTFAcademy/feat/walletconnect
Browse files Browse the repository at this point in the history
feat: add WalletConnect
  • Loading branch information
yutingzhao1991 authored Mar 13, 2024
2 parents e9cef5a + 1f9c782 commit 906272f
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 3 deletions.
Binary file added 10_WalletConnect/img/walletconnect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 10_WalletConnect/img/walletnetwork.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 10_WalletConnect/img/walletqrcode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
133 changes: 131 additions & 2 deletions 10_WalletConnect/readme.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,132 @@
这一讲将会介绍如何使用`WalletConnect`来连接移动端 APP 钱包。
这一讲将会介绍如何使用 [WalletConnect](https://walletconnect.com/) 来连接移动端 APP 钱包,这对提升 DApp 的用户体验至关重要

> 文档编写中,敬请期待。
## 什么是 WalletConnect

我们在之前的课程中实现了在 DApp 连接浏览器插件形式的钱包,但是很多时候用户希望使用手机上的钱包应用来连接电脑上打开的 DApp,又或者希望在手机浏览器访问 DApp 的时候也可以连接上手机上的钱包。

这种场景因为不在 PC 的浏览器中,所以无法通过浏览器插件的方式来连接钱包,而 WalletConnect 协议则是一种通过服务端中转的方式来实现钱包和 DApp 建立连接。

WalletConnect 是由同名非盈利组织 WalletConnect 基金会支持开发的一套开源协议,通过这个协议,用户可以用手机钱包应用连接 DApp,不需要安装浏览器插件。

## WalletConnect 的原理是什么

我们先来看看 WalletConnect 的协议是什么样的,它其实背后也对应了一个 EIP 的协议 [EIP-1328](https://eips.ethereum.org/EIPS/eip-1328)

```
request = "wc" ":" topic [ "@" version ][ "?" parameters ]
topic = STRING
version = 1*DIGIT
parameters = parameter *( "&" parameter )
parameter = key "=" value
key = STRING
value = STRING
```

这是一个 URI 的规范,具体内容上,1.0 和 2.0 的参数不同。

2.0 的参数如下:

- symKey:用于通过中继加密消息的对称密钥
- methods:支持的 jsonrpc 方法
- relay-protocol:用于中继消息服务的传输协议
- relay-data(可选):用于中继消息协议的数据,可能是一些协议需要的配置信息
- expiryTimestamp(可选):配对过期时的时间,通常就是中继服务对称密钥的过期时间

一个具体的样例如下:

```
wc:02c2d94b12d9fde35a149a3620544892b98ea14d45832c9bbd903af9d57d3ea9@2?expiryTimestamp=1710298160&relay-protocol=irn&symKey=8327616fa992557f5d125fe5397116c73ace7f368ac6183724052b1bcb917414
```

其中 `relay-protocol` 代表使用什么协议,通常就是 `irn`。它代表 WalletConnect 定义的一个[协议](https://specs.walletconnect.com/2.0/specs/servers/relay/relay-server-rpc)。我们来试试基于这个协议连接钱包会发生什么。

如果你观察通过 WalletConnect 连接时的 DApp 网络请求,你会发现浏览器会像 `wss://relay.walletconnect.com` 地址发送请求。

![wallet](./img/walletnetwork.png)

这个请求是通过 WebSocket 协议发送的,它是一个双向通信协议,所以可以实现双向通信。这个请求是通过 WalletConnect 的中继服务器来转发的,这样就实现了钱包和 DApp 之间的连接。`wss://relay.walletconnect.com` 地址是 `irn` 协议默认的地址。你也可以参考 WalletConnect 的官方文档构建自己的中继服务器。

[EIP-1328](https://eips.ethereum.org/EIPS/eip-1328) 定义的角度来看,WalletConnect 2.0 是一个很灵活的协议,它只是简单定义了一个 URI 的规范。但是具体的 `irn` 协议就包含了更多的细节,比如消息的格式,消息的加密方式,消息的传输协议等等。目前大部分的钱包也都是支持 `irn` 协议,所以通常来说我们也是使用该协议建立连接。但是当我们提到 WalletConnect 协议的时候,可能往往包含了 `irn` 协议的内容。协议本身比较复杂,但是对于 DApp 来说,通常不需要关心,接下来让我们看看在 DApp 中如何支持 WalletConnect。

## 如何使用 WalletConnect

接下来我们引导你在本课程的 DApp 中使用 WalletConnect 来连接钱包。

在 wagmi 中通过[集成 WalletConnect 的 SDK](https://wagmi.sh/core/api/connectors/walletConnect) 内置支持了 WalletConnect,我们结合 Ant Design Web3 提供的 [ConnectModal](https://web3.ant.design/components/connect-modal-cn) 组件可以简单的接入 WalletConnect。

首先我们需要在 wagmi 的配置中添加 `walletConnect`

```diff
- import { injected } from "wagmi/connectors";
+ import { injected, walletConnect } from "wagmi/connectors";

const config = createConfig({
chains: [mainnet],
transports: {
[mainnet.id]: http(),
},
connectors: [
injected({
target: "metaMask",
}),
+ walletConnect({
+ projectId: 'c07c0051c2055890eade3556618e38a6'
+ showQrModal: false,
+ }),
],
});
```

上面代码中的 `projectId` 是 Ant Design Web3 提供的测试项目 ID,实际项目中你需要在 [https://cloud.walletconnect.com/](https://cloud.walletconnect.com/) 申请自己的 ID。`showQrModal` 配置是为了关闭 ConnectModal 的默认弹窗,避免出现重复的弹窗。

添加了 `walletConnect` 之后其实就可以直接使用了,Ant Design Web3 会自动检测到钱包是否支持 WalletConnect,如果支持的话就会在未安装插件钱包的情况下再 ConnectModal 中显示二维码出来,供用户扫码连接。

![walletqrcode](./img/walletqrcode.png)

你也可以继续添加一个单独的 WalletConnect 的钱包选择:

```diff
import {
Address,
ConnectButton,
Connector,
NFTCard,
useAccount,
} from "@ant-design/web3";
import {
MetaMask,
WagmiWeb3ConfigProvider,
+ WalletConnect,
} from "@ant-design/web3-wagmi";
import { Button, message } from "antd";
import { parseEther } from "viem";
import { createConfig, http, useReadContract, useWriteContract } from "wagmi";
import { mainnet } from "wagmi/chains";
import { injected, walletConnect } from "wagmi/connectors";

//...

export default function Web3() {
return (
<WagmiWeb3ConfigProvider
config={config}
+ wallets={[MetaMask()]}
+ wallets={[MetaMask(), WalletConnect()]}
>
<Address format address="0xEcd0D12E21805803f70de03B72B1C162dB0898d9" />
<NFTCard
address="0xEcd0D12E21805803f70de03B72B1C162dB0898d9"
tokenId={641}
/>
<Connector>
<ConnectButton />
</Connector>
<CallTest />
</WagmiWeb3ConfigProvider>
);
}
```

完整的代码你可以在 [web3.tsx](./web3.tsx) 中找到。

![walletconnect](./img/walletconnect.png)
115 changes: 115 additions & 0 deletions 10_WalletConnect/web3.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {
Address,
ConnectButton,
Connector,
NFTCard,
useAccount,
} from "@ant-design/web3";
import {
MetaMask,
WagmiWeb3ConfigProvider,
WalletConnect,
} from "@ant-design/web3-wagmi";
import { Button, message } from "antd";
import { parseEther } from "viem";
import { createConfig, http, useReadContract, useWriteContract } from "wagmi";
import { mainnet } from "wagmi/chains";
import { injected, walletConnect } from "wagmi/connectors";

const config = createConfig({
chains: [mainnet],
transports: {
[mainnet.id]: http(),
},
connectors: [
injected({
target: "metaMask",
}),
walletConnect({
projectId: "c07c0051c2055890eade3556618e38a6",
showQrModal: false,
}),
],
});

const CallTest = () => {
const { account } = useAccount();
const result = useReadContract({
abi: [
{
type: "function",
name: "balanceOf",
stateMutability: "view",
inputs: [{ name: "account", type: "address" }],
outputs: [{ type: "uint256" }],
},
],
// Goerli test contract 0x418325c3979b7f8a17678ec2463a74355bdbe72c
address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
functionName: "balanceOf",
args: [account?.address as `0x${string}`],
});
const { writeContract } = useWriteContract();

return (
<div>
{result.data?.toString()}
<Button
onClick={() => {
writeContract(
{
abi: [
{
type: "function",
name: "mint",
stateMutability: "payable",
inputs: [
{
internalType: "uint256",
name: "quantity",
type: "uint256",
},
],
outputs: [],
},
],
address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
functionName: "mint",
args: [1],
value: parseEther("0.01"),
},
{
onSuccess: () => {
message.success("Mint Success");
},
onError: (err) => {
message.error(err.message);
},
}
);
}}
>
mint
</Button>
</div>
);
};

export default function Web3() {
return (
<WagmiWeb3ConfigProvider
config={config}
wallets={[MetaMask(), WalletConnect()]}
>
<Address format address="0xEcd0D12E21805803f70de03B72B1C162dB0898d9" />
<NFTCard
address="0xEcd0D12E21805803f70de03B72B1C162dB0898d9"
tokenId={641}
/>
<Connector>
<ConnectButton />
</Connector>
<CallTest />
</WagmiWeb3ConfigProvider>
);
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ DApp 开发极简入门

**第 9 讲:EIP1193 和 EIP6963**[教程](./09_EIP1193/readme.md) | [代码](./09_EIP1193/demo.js)

**第 10 讲:通过 WalletConnect 连接移动端钱包**[教程](./10_WalletConnect/readme.md)
**第 10 讲:通过 WalletConnect 连接移动端钱包**[教程](./10_WalletConnect/readme.md) | [代码](./10_WalletConnect/web3.tsx)

**第 11 讲:支持多链**[教程](./11_MultipleChain/readme.md)

Expand Down

0 comments on commit 906272f

Please sign in to comment.