iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 18
0
Blockchain

區塊練起來-智能合約與DApp開發系列 第 18

[區塊練起來-智能合約與DApp開發] DAY 18 - web3.js 取得 Provider

貼心小語

上一篇我們把 Angular 、 MetaMask 、 BootStrap 以及 web3.js 都裝好了,這一篇會寫簡單的 Service 來取得錢包 Provider 。


建立Service

我們先透過 AngularCLI 快速建立一個 Service 來當作往後 Provider 專用的 Service :

ng g s services/provider

因為整個網頁都需要靠 Provider 來運作,所以我們只需要建立一個實例就可以了,讓整個網頁都使用單一 ProviderService 實例,所以我們設定 providedIn: 'root' ,就不會不同模組使用不同的實例:

import { Injectable } from '@angular/core';

@Injectable({
    providedIn: 'root'
})
export class ProviderService {}

使用web3.js

接著我們要把 web3.js 引入,好讓我們可以使用 web3.js 提供的功能:

import * as Web3 from 'web3';

現在的 web3 已經有 type 定義,故 import 方式會有不同,若使用較新版的 web3 ,則改成
import Web3 from 'web3';

constructor 的時候就要取得 Provider ,先檢查 window 下有沒有 web3 屬性,如果有就表示有錢包提供環境,所以去取用 currentProvider ,如果這是美麗的錯誤(?)就直接調用我們的 Ganache :

import { Injectable } from '@angular/core';
import * as Web3 from 'web3';

declare let window: any;

@Injectable({
  providedIn: 'root'
})
export class ProviderService {
    private web3: any = null;
    constructor() {
        this.web3 = typeof window.web3 !== 'undefined'
        ? new Web3(window.web3.currentProvider)
        : new Web3(new Web3.providers.HttpProvider('http://localhost:7545'));
    }
}

編譯報錯?

如果有發生相關錯誤再執行即可

這時候去編譯的話會編譯失敗!得到的相關錯誤是: Module not found: Error: Can’t resolve 'crypto' ,這個問題在 Angular 6 以上會出現,在編譯時會忽略 node.js 的 API ,所以我們必須把部分 node.js 的 API 也編譯進去,那該怎麼做呢?我們這邊寫一個插件去更改 Webpack 的設置,最主要就是要把 node: false 改掉:

const fs = require('fs');
const file = './node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js';

fs.readFile(file, 'utf8', (err, data) => {
    if (err) return console.log(err);
    const result = data.replace(/node: false/g, 'node: {crypto: true, stream: true}');
    fs.writeFile(file, result, 'utf8', (err) => {
        if (err) return console.log(err);
    });
});

可以把這個插件存起來,並設定 package.json 來讓首次使用的開發者透過指令來執行:

"scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "patch-webpack": "node plugins/patch.js"
},

然後輸入下方指令就完成了:

npm run patch-webpack

連接 DApp 與錢包

前面我們取得了 Provider ,不過這樣子並沒有把網頁跟錢包做連接,而是要透過 Provider 去做連接,這邊我們寫一個方法來建立連線,為了方便統一管理,將會盡量用 rxjs 來處理非同步事件,所以我們把 enable() 透過 from() 來將 Promise 轉換成 Observable ,在取得連線同意後會取得帳戶列表:

import { Observable, from } from 'rxjs';

public enableConnect(): Observable<any> {
    return from(this.web3.currentProvider.enable());
}

然後先放在 constructor 中呼叫,這邊順便寫了取得 accounts 的方法:

import { Injectable } from '@angular/core';
import { Observable, from } from 'rxjs';
import { take } from 'rxjs/operators';
import * as Web3 from 'web3';

declare let window: any;

@Injectable({
  providedIn: 'root'
})
export class ProviderService {
    private web3: any = null;
    private accountList: Array<string> = [];

    constructor() {
        this.web3 = typeof window.web3 !== 'undefined'
        ? new Web3(window.web3.currentProvider)
        : new Web3(new Web3.providers.HttpProvider('http://localhost:7545'));
        this.enableConnect().pipe(take(1)).subscribe(
            res => { this.accountList = res; },
            err => { console.error(err); }
        );
    }

    public enableConnect(): Observable<any> {
        return from(this.web3.currentProvider.enable());
    }

}

那要怎麼查看是不是真的有拿到 Provider 呢?可以先在 app.component.ts 中注入 ProviderService ,然後在 Service 用 console.log() 來查看就可以了!


今日小結

成功透過 web3.js 來取得 Provider ,並將網頁與錢包做連線,需要注意的是 Angular 6 以上編譯會報錯,需要去更改 Webpack 設定。


參考資料

Angular 使用 web3 时出现 Can't Resolve 'Crypto' 报错的解决方法


上一篇
[區塊練起來-智能合約與DApp開發] DAY 17 - DApp 環境建置與開發工具
下一篇
[區塊練起來-智能合約與DApp開發] DAY 19 - web3.js 取得 Accounts 及 Block 資訊
系列文
區塊練起來-智能合約與DApp開發31

尚未有邦友留言

立即登入留言