DQtech Column | Learn from DEX about PlatON Application Development – Javascript (VIII)

In this chapter, we will learn to realize the multi account switching between test network and main network, as well as the function of entrusting lat.

As there are no pledge nodes in the development and test network of PlatON, we would implement the delegation function in the main network in this chapter

Add NetworkManager

Previously, the connection address of the test network was placed in the transaction manager. In order to support multi network switching, we extracted the node connection into the NetworkManager. Add the code of NetworkManager in the background.js. Some of the codes are as follows:

    static webIns = null;
    static curNetwork = "PlatON开发测试网";

    static networkInfos = {
        PlatON开发测试网: {
            rpcUrl: "http://35.247.155.162:6789",
            chainId: "210309",
            browserUrl: "https://devnetscan.platon.network"
        },
        PlatON网络: {
            rpcUrl: "https://samurai.platon.network",
            chainId: "100",
            browserUrl: "https://scan.platon.network"
        }
    };
    static async InitNetwork()
    /**
     * 加载网络信息
     * @returns
     */
    static loadNetworkInfos() 
    /**
     * 获取所有网络信息
     * @returns
     */
    static GetNetworkNameList() 


    static GetCurNetworkName()
    /**
     * 获取区块链浏览器地址
     * @returns
     */
    static GetBrowserUrl() 
    /**
     * 切换网络
     * @param {}} networkName
     * @returns
     */
    static SwitchNetwork(networkName)
    /**
     * 加载网络信息
     * @returns
     */
    static loadCurNetwork()
    /**
     * 获取网页实例
     * @returns
     */
    static GetWebIns()
    /**
     * 
     * @returns 获取链id
     */
    static GetChainId()

By default, this class has the information of PlatON’s development test network and main network. Then we add the initialization code of NetworkManager in the background.js.

... 省略代码
//  初始化钱包
async function InitWallet() {
    // 加载网络
    await NetworkManager.InitNetwork();
    // 加载交易记录
    TransactionManager.LoadPendingTxRecord();
}
// 调用初始化函数
InitWallet();

This ensures that the plug-in network has been loaded before we opening it.

Add network switching function of Api

When switching the network, you also need to switch the wallet to obtain the address of the transaction record, so you need to add the Api address switching function
Open tx-record.js in the src/api directory. Codes as follows:

import axios from "axios";
let txRecorcdService = null;
export default {
    SwitchNetwork(browserUrl) {
        txRecorcdService = axios.create({
            baseURL: `${browserUrl}/browser-server`,
            timeout: 30000,
            withCredentials: true
        });

        txRecorcdService.interceptors.response.use(
            response => {
                return response.data;
            },
            error => {
                return Promise.reject(error);
            }
        );
    },
    /**
     * 获取交易记录列表
     * @param {*} params
     * @returns
     */
    GetTransactionListByAddress(params) {
        return txRecorcdService.post("/transaction/transactionListByAddress", params);
    }
};

Every time you switch the network, the component directory will call the SwitchNetwork function to switch it

Build a page to switch networks

Open the main-page and modify the layout page as follows:

... 省略代码
<el-dropdown trigger="click">
    <div class="network-component  horzontal-layout flex-center">
        <span style="background:#e91550" class="circle-dot"></span>
        <span class="network-name">{{ curNetworkName }}</span>
        <i class="el-icon-arrow-down"></i>
    </div>
    <el-dropdown-menu slot="dropdown">
        <el-dropdown-item
            @click.native="onSwitchNetwork(item)"
            v-for="(item, index) in networkList"
            :key="index"
            >{{ item }}</el-dropdown-item
        >
    </el-dropdown-menu>
</el-dropdown>
... 省略代码

The interface effect is as follows:

DQtech Column | Learn from DEX about PlatON Application Development - Javascript (VIII)

Write the logic of network page switching

add codes in main-page.vue

async onSwitchNetwork(item) {
    let res = await this.digging.NetworkManager.SwitchNetwork(item);
    if (res.errCode !== 0) {
        this.$message.error(res.errMsg);
        return;
    }
    this.curNetworkName = item;
    // 切换网络
    this.api.SwitchNetwork();
    // 加载表数据
    this.reloadTableData();
   },

 reloadTableData() {
   if (this.tabIndex === 0) {
       this.$refs.txTable.ReloadData();
   } else if (this.tabIndex === 1) {
       this.$refs.pendingTxTable.ReloadData();
   }
}

Through the above code, we can realize the function of switching between the test network and the main network of PlatON

Create a Page that dDisplays the Authentication Node

We build the validation node page as a component, then let it display in the main-page.vue, and create validator table. Add the docuent validator-table.vue in the component directory Vue file, some codes are as follows:

<template>
    <div class="validator-table">
        <el-table :data="datas" style="width: 100%" height="400">
            <el-table-column prop="ranking" label="排名" width="100" />

            <el-table-column prop="nodeName" label="节点名"  width="120"> </el-table-column>

            <el-table-column label="状态">
                <template slot-scope="scope">
                    <span>{{ getStatus(scope.row) }}</span>
                </template>
            </el-table-column>
            <el-table-column prop="totalValue" label="总质押">
                <template slot-scope="scope">
                    <span>{{ scope.row.totalValue }}LAT</span>
                </template>
            </el-table-column>

            <el-table-column prop="delegateValue" label="委托人数">
                <template slot-scope="scope">
                    <span>{{ scope.row.delegateValue }}LAT</span>
                </template>
            </el-table-column>

            <el-table-column prop="delegatedRewardRatio" label="委托奖励比例" />

            <el-table-column prop="delegateQty" label="委托者" />
            <el-table-column prop="blockQty" label="已产生区块数" />

            <el-table-column prop="expectedIncome" label="预计年收化率" />

            <el-table-column prop="deleAnnualizedRate" label="预计委托年化率" />
            <el-table-column prop="genBlocksRate" label="出块率" />
            <el-table-column prop="version" label="版本号" />
        </el-table>
        <div class="horzontal-layout flex-center validator-table-footer">
            <el-pagination
                class="table-pager"
                background
                layout="total,  prev, pager, next"
                :total="totals"
                :page-size="20"
                :current-page.sync="curPageIndex"
                @current-change="loadData"
            ></el-pagination>
        </div>
    </div>
</template>

Then introduce the component into main-page.vue. Some codes are as follows:

...省略代码
import validatorTable from "@/component/validator-table.vue";
...省略代码

Add a tab for the verification node on the main page

...省略代码
           <div class="transaction-record-container" v-if="tabIndex === 2">
                    <validator-table />
                </div>
...省略代码

The effect is as follows:

DQtech Column | Learn from DEX about PlatON Application Development - Javascript (VIII)

Write the Interface to get the authentication node

We need to write an interface to get the information of the authentication node in the browser. Create a new validator.js file in the api directory, as shown in the following figure:

1639491719(1)

Codes as follows:

import axios from "axios";

const txRecorcdService = axios.create({
    baseURL: "https://devnetscan.platon.network/browser-server",
    timeout: 30000,
    withCredentials: true
});

txRecorcdService.interceptors.response.use(
    response => {
        return response.data;
    },
    error => {
        Message.error(error.response.data.errMsg);
        return Promise.reject(error)
    }
);

export default {
    /**
     * 获取验证节点列表
     * @param {*} params
     * @returns
     */
    GetAliveStakingList(params) {
        return txRecorcdService.post("/staking/aliveStakingList", params);
    }
};

Call this interface and it would return the following fields:

DQtech Column | Learn from DEX about PlatON Application Development - Javascript (VIII)

Some important words:

  • Ranking
  • Node Name
  • Status: The status of the node. 1: candidate 2: active 3: outgoing 6: consensus
  • Total Value
  • Delegate Value
  • Delegated Reward Ratio
  • Delegate Qty
  • Block Qty
  • Expected Income
  • Dele Annualized Rate
  • Gen Blocks Rate 出
  • Version
    在 validator-table.vue中调用该接口, 代码如下:
        async loadData() {
            let res = await this.api.validator
                .GetAliveStakingList({
                    pageNo: this.curPageIndex,
                    pageSize: 20,
                    queryStatus: "all"
                })
                .catch(e => {
                    console.log(e);
                });

            if (res.code !== 0) {
                this.$message.error(res.errMsg);
                return;
            }
            this.datas = res.data;
            this.totals = res.totalCount;
        }

The verification node of test network:

DQtech Column | Learn from DEX about PlatON Application Development - Javascript (VIII)


The authentication node of the main network:

DQtech Column | Learn from DEX about PlatON Application Development - Javascript (VIII)

Since there is no LAT in our test account, you can transfer the lat of the main network to the test account for testing Here, the author adds the function of importing the main network wallet through the secret key.

Add account menu pop-up box

Create a new file account-menu.vue under the component directory. Some codes are as follows:

<template>
    <div class="account-menu vertical-layout">
        <div class="account-list">
            <div
                class="horzontal-layout account-item flex-horzontal-center"
                v-for="(item, index) in accountList"
                :key="index"
            >
                <img src="@/assets/little_dog.jpg" />
                <div class="vertical-layout">
                    <div class="account-name">{{ item.account }}</div>
                    <div class="account-lat">
                        <span>{{ item.lat }}</span> LAT
                    </div>
                </div>
            </div>
        </div>

        <div class="account-item-2" @click="onJumpToImportAccount">
            <i class="el-icon-download"></i>
            <span class="item-name">导入账户</span>
        </div>
    </div>
</template>
<script>

The logic of obtaining the account list is as follows:

        async loadAccountList() {
            let res = await this.digging.PrivateKeyManager.GetAccountList();

            this.accountList = res.accountList;

            this.accountMap = {};
            this.accountList.forEach(async ele => {
                ele.lat = 0;
                // 获取钱包余额
                ele.lat = await this.digging.TransactionManager.GetBalanceOf(ele.address);
                this.$forceUpdate();
            });

            this.curAccount = res.curAccount;
        },

we use the interface of PrivateKeyManager to obtain the account list。 The code of using this component in main-page.vue is as follows:

...省略代码
<el-popover placement="bottom" width="320" trigger="click">
    <account-menu />
    <div class="account-select-btn vertical-layout flex-center" slot="reference">
    <img src="@/assets/little_dog.jpg" />
    </div>
</el-popover>
...省略代码

Effect shouws as follow:
2W$%%TN@4){J9P(41_324A

Add Page for Importing Private Key

Create a new iimport-account-page.vue page in the page directory. Some codes are as follows:

<template>
    <div class="import-account-page vertical-only-layout">
        <header-bar />
        <div class="go-back" @click="onGoBack">< Back</div>
        <page-title>导入账户</page-title>
        <div class="desc-text">
            导入的账户将不会与最初创建的 Digging 账户助记词相关联。
        </div>
        <div class="horzontal-layout " style="margin-bottom:6px;">
            <span style="color: black;">请输入账号名:</span>
        </div>
        <div class="horzontal-layout flex-center" style="margin-bottom: 10px;">
            <el-input class="private-key-password-display-textarea" v-model="accountName" />
        </div>

        <div class="horzontal-layout " style="margin-bottom:6px;">
            <span style="color: black;">请粘贴您的私钥:</span>
        </div>
        <div class="horzontal-layout flex-center">
            <el-input
                class="private-key-password-display-textarea"
                type="textarea"
                v-model="privateKey"
                rows="3"
                resize="none"
            />
        </div>

        <div class="horzontal-layout flex-center" style="margin-top:20px;">
            <el-button round class="import-btn" @click="onGoBack">取消</el-button>
            <span class="flex-1"></span>
            <el-button round class="import-btn " type="primary" @click="onImport">创建</el-button>
        </div>
    </div>
</template>
...省略代码
async onImport() {
    if (!this.accountName) {
        this.$message.error("请输入账号名");
        return;
    }

    if (!this.privateKey) {
        this.$message.error("请输入秘钥");
        return;
    }

    let res = await this.digging.PrivateKeyManager.StorePrivateKey(
        this.accountName,
        this.privateKey,
        false
    );

    if (res.errCode !== 0) {
        this.$message.error(res.errMsg);
        return;
    }
    this.$message.success("导入账户成功!");
}
}

Import the private key here and use the storeprivatekey interface of PrivateKeyManager to store the private key. Note that the third parameter must be false, otherwise the situation will be the same as the previous account. The effect is as follows:
1639576849(1)

I imported a test account of the main network, and the effect is as follows:

1639576885(1)

Write the logic of account switching

Add onSwitchAccount function in main-page.vue. The code is as follows:

async onSwitchAccount(accountName) {
    let res = await this.digging.PrivateKeyManager.SwitchAccount(accountName);
    if (res.errCode !== 0) {
        this.$message.error(res.errMsg);
        return;
    }
    this.accountName = accountName;
    this.address = res.data;
    this.makeShowAddress();
    this.getBalanceOf();
},

The effect of account switching is as follows:

DQtech Column | Learn from DEX about PlatON Application Development - Javascript (VIII)

Write Delegation Page

Add delegate-lat-page.vue in the page directory, some codes are as follow:

<template>
    <div class="delegate-lat-page vertical-only-layout">
        <header-bar />
        <div class="go-back" @click="onGoBack">< Back</div>
        <page-title>委托LAT</page-title>
        <el-form ref="delegateForm" :model="delegateInfo">
            <el-form-item prop="nodeName" label="节点名">
                <el-input disabled class="send-input" v-model="delegateInfo.nodeName" />
            </el-form-item>

            <el-form-item prop="nodeId" label="节点id">
                <el-input disabled class="send-input" v-model="delegateInfo.nodeId" />
            </el-form-item>

            <el-form-item
                prop="lat"
                label="委托数量"
                :rules="[
                    {
                        required: true,
                        message: '请输入要委托的LAT数量',
                        validator: ValidateLAT
                    }
                ]"
            >
                <el-input
                    class="send-input"
                    type="number"
                    v-model="delegateInfo.lat"
                    placeholder="请输入要委托的LAT数量(不少于10LAT)"
                />
            </el-form-item>

            <el-form-item label="所需手续费">
                <el-input disabled class="send-input" v-model="delegateInfo.gasfee" />
            </el-form-item>

            <el-form-item>
                <div class="horzontal-layout">
                    <span class="flex-1"></span>
                    <el-button class="create-btn" @click="onGoBack">取消</el-button>
                    <el-button class="create-btn" type="primary" @click="onDelegate"
                        >委托</el-button
                    >
                </div>
            </el-form-item>
        </el-form>
    </div>
</template>

The effect:

DQtech Column | Learn from DEX about PlatON Application Development - Javascript (VIII)

Add Delegated Management Class

Add codes below in the background.js:

/**
 * 委托管理类
 */
class DelegateManager {
    /**
     * 委托LAT
     * @param {}} nodeId 
     * @param {*} lat 
     * @returns 
     */
    static async Deletgate(nodeId, lat) {
        let web3 = NetworkManager.GetWebIns();

        let curAccount = await PrivateKeyManager.GetCurrentAccount();
        let privateketRes = await PrivateKeyManager.ExportPrivateKey(
            curAccount,
            PasswordManager.GetPassPassword()
        );
        if (privateketRes.errCode !== 0) {
            return privateketRes;
        }

        let walletInfo = privateketRes.data;

        let ppos = new web3.PPOS({ provider: NetworkManager.GetCurNetworkRPCUrl() });

        ppos.updateSetting({
            privateKey: walletInfo.privateKey,
            chainId: 100
        });

        let params = {
            funcType: 1004, // 委托LAT的函数
            typ: 0,
            nodeId: ppos.hexStrBuf(nodeId),
            amount: ppos.bigNumBuf(web3.utils.toVon(lat, "lat"))
        };

        let reply = await ppos.send(params);
        return reply;
    }
}

In this chapter, we only implement the function of entrusting lat. Here, we need to fix the SDK bug. Open the web3.js in the public/js directory, find PPOS.prototype.bigNumBuf , and modify the code as follows:

PPOS.prototype.bigNumBuf = function (intStr, radix, byteLen) {
  radix = radix || 10;
  BN.Buffer = Buffer; // 添加这行代码,否则报错
  var num = new BN(intStr, radix);
  byteLen = byteLen || Math.ceil(num.byteLength() / 8) * 8; // 好像宽度没用...
  return num.toTwos(byteLen).toBuffer();
};

Then find the function BN.prototype.toBuffer and modify the code as follows:

  BN.prototype.toBuffer = function toBuffer(endian, length) {
    assert(typeof BN.Buffer !== 'undefined');
    return this.toArrayLike(BN.Buffer, endian, length);
  };

Here I demonstrate how to delegate 10 lats to a node named infinity Platon. The node ID is:

0x9ca01d3332c2e4a7e16fa50c77a30bf7905aee1c0d7c456969b33c2939b3ee410a79a6fb204d826a5997b903964ff0fff62cf8f1c2a0b73b3bbc5f96c265ad49

DQtech Column | Learn from DEX about PlatON Application Development - Javascript (VIII)


Finally, the following transaction records can be seen in the blockchain browser:

DQtech Column | Learn from DEX about PlatON Application Development - Javascript (VIII)

Well, that’s all of this chapter. In the next chapter, we will improve the functions of entrusted lat and multi network docking.

The URL of github : https://github.com/DQTechnology/Platon_DevGuideProject

This article is reproduced from https://forum.latticex.foundation/t/topic/5909

Like (0)
Previous January 27, 2022 18:40
Next February 2, 2022 20:57

相关推荐

Leave a Reply

Please Login to Comment