将WalletConnect集成到Vue.js DApps中

开发 前端
在这篇文章中,我们介绍了在你的Vue DApps中实现WalletConnect的逐步细节。从用正确的配置设置我们的项目和构建我们的界面,到编写必要的逻辑以确保我们的应用程序始终与钱包保持同步。

去中心化应用程序(DApps)的主要功能之一是连接钱包的能力,这反过来又允许用户与DApp上的交易互动。它抽象了一些功能,如切换网络,提供签名者,以及其他为用户提供一种认证形式的功能。连接钱包也作为一个网关,允许用户通过DApp在区块链上进行和读取操作,使用他们的钱包地址作为授权身份。

WalletConnect是一个免费的开源协议,使我们的DApp与多个钱包连接成为可能,包括MetaMask、Trust Wallet、Rainbow和其他钱包。该协议通过在DApp和钱包之间建立连接来抽象这个过程,使它们在整个会话中保持同步。

在这篇文章中,我们将使用WalletConnect将我们的钱包应用与我们的DApp连接起来,在前端使用Vue.js。需要注意的是,WalletConnect可以用在任何兼容WalletConnect的DApp、链和钱包(托管和非托管)上。

你可以在这里找到本教程的源代码,以及我们将建立的应用程序的演示。

开始使用Vue.js应用程序

首先,让我们使用Vue CLI来启动这个项目。如果你的系统中已经安装了Vue CLI,你可以直接创建Vue项目。

你可以用这个命令全局安装它。

npm install -g @vue/cli
  • 1.

现在我们可以使用Vue CLI来创建我们的项目。用这个命令创建一个新项目。

vue create vue-wallet-connect
  • 1.

你将需要挑选一个预设。选择手动选择功能,然后选择如下所示的选项。

在项目被创建后,导航到新的项目文件夹。

cd vue-wallet-connect
  • 1.

我们将在我们的Vue应用程序中使用Ethers.js,在连接我们的钱包时直接与区块链互动。

npm i ethers
  • 1.

在这里,我们将WalletConnect库安装到你的项目中。

npm install --save web3 @walletconnect/web3-provider
  • 1.

接下来,为了在Vue 3中直接使用WalletConnect库,我们需要安装node-polyfill-webpack-plugin。

npm i node-polyfill-webpack-plugin
  • 1.

我们安装它是因为我们的项目使用webpack v5,其中polyfill Node核心模块被删除。所以,我们安装它是为了在项目中访问这些模块。

现在,打开vue.config.js文件,用这段代码替换它。

const { defineConfig } = require("@vue/cli-service");
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
module.exports = defineConfig({
  transpileDependencies: true,
  configureWebpack: {
    plugins: [new NodePolyfillPlugin()],
    optimization: {
      splitChunks: {
        chunks: "all",
      },
    },
  },
});
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

一旦完成,你就可以启动服务器了。

npm run serve
  • 1.

构建用户界面

让我们进入组件文件夹,创建一个名为StatusContainer.vue的新文件。这个组件包含我们的主页面。

它有我们的欢迎词,帮助我们连接的连接钱包按钮,以及断开我们与钱包连接的断开按钮。最后,当我们成功连接到一个钱包时,显示Connected按钮。

<template>
  <div class="hello">
    <h1>Welcome to Your Vue.js Dapp</h1>
    <div
       <button class="button">Connected</button>
       <button class="disconnect__button">Disconnect</button>
    </div>

    <button class="button"> Connect Wallet</button>
  </div>
</template>
<script>
export default {
  name: 'StatusContainer'
}
</script>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

一旦完成,打开App.vue文件,像这样导入StatusContainer组件。

<template>
  <status-container/>
</template>
<script>

import StatusContainer from './components/StatusContainer.vue'
export default {
  name: 'App',
  components: {
    StatusContainer
  }
}
</script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Sora:wght@100&display=swap');
#app {
  font-family: 'Sora', sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.button {
      background-color: #1c82ff;
    border: none;
    color: #ffffff;
    font-family: "Sora";
    border-radius: 3rem;
    padding: 2rem 3rem;
    font-weight: 600;
    font-size: 2rem;
    margin: 1rem 1rem 1rem auto;
    width: 40%;
}
.disconnect__button {
     background-color: red;
    border: none;
    color: #ffffff;
    font-family: "Sora";
    border-radius: 3rem;
    padding: 1rem 1.3rem;
    font-weight: 600;
    font-size: 1rem;
    margin: 8rem 1rem 1rem auto;
    width: 20%;
}
</style>
  • 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.

在我们的样式标签中,我们现在为我们先前创建的按钮添加样式:.button和.disconnect__button。另外,我们从Google Fonts导入Sora自定义字体,并将其作为我们的字体家族。

实例化WalletConnect

我们将需要一个RPC提供者来实例化我们的WalletConnect库。在这个例子中,我们将使用Infura。打开Infura,创建一个新的项目,并获取项目ID。

现在,在src文件夹下创建一个新的walletConnect文件夹:src/walletConnect。在这个文件夹中,让我们创建一个provider.js文件。在这里,我们导入我们的WalletConnect库,使用我们的Infura ID将其实例化,并导出它以便在其他文件中使用。

src/walletConnect/provider.js将看起来像这样。

import WalletConnectProvider from "@walletconnect/web3-provider";
export const provider = new WalletConnectProvider({
  infuraId: process.env.VUE_APP_INFURA_ID,
});
  • 1.
  • 2.
  • 3.
  • 4.

Infura的ID应该作为环境变量使用。所以在你的.env文件中添加以下内容。

VUE_APP_INFURA_ID={{INFURA__ID}}
  • 1.

使用可合成物添加功能

在创建我们的接口并成功实例化我们的库之后,下一步是实现我们的功能。为了做到这一点,我们将使用Vue composables,因为它允许我们在应用中的任何组件中使用我们的状态和动作,类似于我们在Pinea和Vuex中的情况。

创建一个可组合的

在src文件夹内,添加src/composables/connect。在connect文件夹内,让我们创建一个index.js文件。

在这里,我们导入reactive和watch,我们将在这个文件中使用它们。让我们创建一个叫做defaultState的状态对象。

import { reactive, watch } from "vue";

const defaultState = {
  address: "",
  chainId: "",
  status: false,
};

const state = defaultState
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

为了保持状态的一致性,我们将状态与本地存储中的一个项目进行同步。让我们把这个项目命名为 "userState",并把它分配给一个叫做STATE_NAME的变量。这样做是为了避免在多个地方重复 "userState "时犯错误。

const STATE_NAME = "userState";
  • 1.

现在我们使用watch来更新我们的本地存储,一旦我们的状态有任何变化。

watch(
  () state,
  () {
    localStorage.setItem(STATE_NAME, JSON.stringify(state));
  },
  { deep: true }
);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

接下来,我们创建一个getDefaultState函数,检查我们本地存储中的STATE_NAME项是否存在,并将本地存储项分配给状态。如果我们的本地存储项不存在,它就将默认状态分配给state。

现在,我们可以删除 const state = defaultState 并使用 reactive 来分配 const state = reactive(getDefaultState());。

const getDefaultState = () {
  if (localStorage.getItem(STATE_NAME) !== null) {
    return JSON.parse(localStorage.getItem(STATE_NAME));
  }
  return defaultState;
};
const state = reactive(getDefaultState());
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

最后,我们导出我们的状态。我们还添加了一个if语句,检查我们的本地存储项是否不存在。如果不存在,它就创建这个项目并将状态分配给本地存储。

 export default () => {
  if (localStorage.getItem(STATE_NAME) === null) {
    localStorage.setItem(STATE_NAME, JSON.stringify(state));
  }
  return {
    state,
  };
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

现在,我们的状态总是与本地存储同步,确保一致性。

我们来看看 src/composables/connect/index.js。

import { reactive, watch } from "vue";

const defaultState = {
  address: "",
  chainId: "",
  status: false,
};

const STATE_NAME = "userState";
const getDefaultState = () {
  if (localStorage.getItem(STATE_NAME) !== null) {
    return JSON.parse(localStorage.getItem(STATE_NAME));
  }
  return defaultState;
};
const state = reactive(getDefaultState());

watch(
  () state,
  () {
    localStorage.setItem(STATE_NAME, JSON.stringify(state));
  },
  { deep: true }
);
export default () => {
  if (localStorage.getItem(STATE_NAME) === null) {
    localStorage.setItem(STATE_NAME, JSON.stringify(state));
  }
  return {
    state,
  };
};
  • 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.

创建动作

我们的动作由将在我们的应用程序中使用的函数组成。我们将创建三个函数。

  • • connectWalletConnect,用于触发WalletConnect模式以连接钱包
  • • autoConnect,在DApp连接后处理WalletConnect会话的一致性,所以当DApp连接后,你刷新页面时,用户的会话仍然是活动的。
  • • disconnectWallet,断开DApp与钱包的连接并结束用户的会话。

让我们直接跳到代码中

ConnectWalletConnect

还是在我们的connect文件夹(src/composables/connect)中,创建connectWalletConnect文件。首先,我们导入我们的索引文件,来自以太坊的提供者,以及我们之前在 src/walletConnect/provider.js 文件中创建的提供者。

import { providers } from "ethers";
import connect from "./index";
import { provider } from "../../walletConnect/provider";

const connectWalletConnect = async () => {
  try {
    const { state } = connect();
    //  Enable session (triggers QR Code modal)
    await provider.enable();
    const web3Provider = new providers.Web3Provider(provider);
    const signer = await web3Provider.getSigner();
    const address = await signer.getAddress();
    state.status = true;
    state.address = address;
    state.chainId = await provider.request({ method: "eth_chainId" });

    provider.on("disconnect", (code, reason) => {
      console.log(code, reason);
      console.log("disconnected");
      state.status = false;
      state.address = "";
      localStorage.removeItem("userState");
    });

    provider.on("accountsChanged", (accounts) => {
       if (accounts.length > 0) {
        state.address = accounts[0];
      }
    });

    provider.on("chainChanged", (chainId) => {
      state.chainId = chainId
    });
  } catch (error) {
    console.log(error);
  }
};
export default connectWalletConnect;
  • 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.

接下来,我们有一个try-catch语句。在我们的尝试语句中,我们从connect()中获得我们的状态,并弹出我们的QR模式进行连接。一旦连接,我们就将我们的地址和chainId分配给状态属性,并使我们的state.status读作true。

然后,我们用提供者观察三个事件:断开连接、账户变更和链式连接。

  • • 一旦用户直接从他们的钱包断开连接,就会触发断开连接。
  • • accountsChanged是在用户切换他们钱包中的账户时触发的。如果账户数组的长度大于0,我们将state.address分配给数组中的第一个地址(accounts[0]),也就是当前地址。
  • • 如果用户切换了他们的链/网络,就会触发 chainChainged。例如,如果他们将他们的链从Ethereum mainnet切换到rinkeby testnet,我们的应用程序将state.chainId从1变为4。

然后,我们的catch语句只是将任何错误记录到控制台。

回到connect文件夹中的index.js文件,导入connectWalletConnect动作。在这里,我们创建一个动作对象,并将其与我们的状态一起导出。

import { reactive, watch } from "vue";
import connectWalletConnect from "./connectWalletConnect";

const STATE_NAME = "userState";
const defaultState = {
  address: "",
  chainId: "",
  status: false,
};
const getDefaultState = () {
  if (localStorage.getItem(STATE_NAME) !== null) {
    return JSON.parse(localStorage.getItem(STATE_NAME));
  }
  return defaultState;
};
const state = reactive(getDefaultState());
const actions = {
  connectWalletConnect,
};
watch(
  () state,
  () {
    localStorage.setItem(STATE_NAME, JSON.stringify(state));
  },
  { deep: true }
);
export default () => {
  if (localStorage.getItem(STATE_NAME) === null) {
    localStorage.setItem(STATE_NAME, JSON.stringify(state));
  }
  return {
    state,
    ...actions,
  };
};
  • 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.

autoConnect

让我们继续看autoConnect.js,以及我们的动作。与 connectWalletConnect 类似,创建一个 autoConnect.js 文件。我们导入索引文件并对其进行解构,以获得我们的状态,并使用connect()进行connectWalletConnect。

import connect from "./index";

const autoConnect = () {
  const { state, connectWalletConnect } = connect();
  if (state.status) {
    if (localStorage.getItem("walletconnect") == null) {
      console.log("disconnected");
      console.log("disconnected");
      state.status = false;
      state.address = "";
      localStorage.removeItem("userState");
    }
    if (localStorage.getItem("walletconnect")) {
      (async () => {
        console.log("start");
        connectWalletConnect();
      })();
    }
  }
};
export default autoConnect;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

你应该知道的一件事是,一旦WalletConnect成功连接到一个DApp,所有关于该钱包的信息(包括地址和链ID)都在本地存储中,在一个名为walletconnect的项目下。一旦会话被断开,它就会被自动删除。

autoConnect检查我们的state.status是否为true。如果是,我们就检查本地存储中是否有一个walletConnect项目。如果它不在本地存储中,我们就删除我们的状态中的所有现有数据和本地存储中的userState项。

然而,如果walletconnect存在于你的本地存储中,我们有一个异步函数,通过启动connectWalletConnect();为我们的DApp "重新激活 "现有会话。因此,如果我们刷新页面,连接仍然是活跃的,并且可以监听我们的提供者事件。

断开钱包

让我们来看看最后一个动作:disconnectWallet。这个动作允许我们从DApp本身结束会话。

首先,我们导入我们的提供者和状态。然后,我们使用provider.disconnect();来断开会话,之后我们将状态重置为默认值,并删除本地存储中的 "userState "项目。

import { provider } from "../../walletConnect/provider";
import connect from "./index";
const disconnectWallet = async () => {
    const { state } = connect();
    await provider.disconnect();
    state.status = false;
    state.address = "";
    localStorage.removeItem("userState");
  }
export default disconnectWallet;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

现在我们可以回到我们的src/composables/connect/index.js中,像这样更新动作对象。

const actions = {
  connectWalletConnect,
  autoConnect,
  disconnectWallet
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

在我们的组件中实现逻辑

让我们打开我们的StatusContainer组件,将我们的可组合文件中的逻辑连接到接口上。像往常一样,导入你的可组合文件并对其进行解构,以获得动作(连接和断开)和我们的状态。

<script>
import connect from '../composables/connect/index';
export default {
  name: 'StatusContainer',
  setup: () {
    const { connectWalletConnect, disconnectWallet, state } = connect();
    const connectUserWallet = async () => {
      await connectWalletConnect();
    };

    const disconnectUser = async() => {
      await disconnectWallet()
    }
    return {
      connectUserWallet,
      disconnectUser,
      state
    }
  }
}
</script>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

然后我们返回函数(disconnectUser,connectUserWallet)和状态,以便在模板中使用。

 <template>
  <div class="hello">
    <h1>Welcome to Your Vue.js Dapp</h1>
    <div v-if="state.status">
       <button  @click="connectUserWallet" class="button">Connected</button>
       <h3>Address: {{state.address}}</h3>
       <h3>ChainId: {{state.chainId}}</h3>
       <button  @click="disconnectUser" class="disconnect__button">Disconnect</button>
    </div>

    <button v-else @click="connectUserWallet" class="button"> Connect Wallet</button>
  </div>
</template>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

首先,我们使用v-if来有条件地显示东西,使用state.status。如果我们连接了并且state.status为真,我们就会显示Connected按钮、用户地址和chainId。同时,我们会显示一个断开连接的按钮,触发我们的disconnectUser函数。

如果用户没有连接,且state.status为false,我们只显示连接钱包按钮,点击后会触发我们的connectUserWallet函数。

添加自动连接

让我们进入App.vue组件,将自动连接逻辑添加到该组件中。与我们之前所做的类似,我们导入我们的composable,并对其进行解构以获得autoConnect动作。使用Vue的onMounted,我们将启动autoConnect()函数。如前所述,这使我们能够监听来自钱包的实时事件,即使我们刷新页面。

<script>
import StatusContainer from './components/StatusContainer.vue'
import connect from './composables/connect/index';
import {onMounted} from "vue";
export default {
  name: 'App',
  components: {
    StatusContainer
  },
  setup: () {
    const { autoConnect} = connect();
     onMounted(async () => {
      await autoConnect()
    })
  }
}
</script>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

结论

如果你一路走到这里,那就恭喜你了!

在这篇文章中,我们介绍了在你的Vue DApps中实现WalletConnect的逐步细节。从用正确的配置设置我们的项目和构建我们的界面,到编写必要的逻辑以确保我们的应用程序始终与钱包保持同步。

原文链接:https://blog.logrocket.com/integrating-walletconnect-vue-js-dapps/

责任编辑:武晓燕 来源: 今日头条
相关推荐

2023-03-16 14:29:48

Vue.js测试

2021-05-08 06:14:28

Vue.js片段开发

2018-04-04 10:32:13

前端JavascriptVue.js

2016-11-04 19:58:39

vue.js

2017-07-04 17:55:37

Vue.js插件开发

2022-01-19 22:18:56

Vue.jsVue SPA开发

2021-01-22 11:47:27

Vue.js响应式代码

2016-11-01 19:10:33

vue.js前端前端框架

2017-07-14 10:10:08

Vue.jsMixin

2020-09-07 14:40:20

Vue.js构建工具前端

2024-05-13 08:04:26

Vue.jsWeb应用程序

2017-07-11 18:00:21

vue.js数据组件

2017-07-20 11:18:22

Vue.jsMVVMMVC

2020-04-01 08:40:44

Vue.jsweb开发

2023-03-29 14:25:08

Vue.js前端框架

2017-08-30 17:10:43

前端JavascriptVue.js

2024-01-18 11:50:28

2019-07-26 14:40:58

Vue.jsSocket.IO前端

2016-09-21 13:32:13

JavascriptWeb前端

2020-09-16 06:12:30

Vue.js 3.0Suspense组件前端
点赞
收藏

51CTO技术栈公众号