以太坊钱包开发-005 账户转账

Mar 19, 2020

账户转账主要分为两部分:

  • 根据 privateKeykeystore 获取账户私钥及地址
  • 通过私钥签名交易实现转账

通过 privateKey 获取账户私钥及地址

通过调用 web3.eth.accounts.privateKeyToAccount(privateKey) 就可以通过私钥获取相应的用户信息。

async getAccountByPrivatekey(ctx) {

        let returnResult = {
            code: 0,
            msg: '成功!',
            data: {}
        }
        
        const data = ctx.request.body
        const privateKey = data.privateKey

        // 根据私钥获取用户地址
        const account = web3.eth.accounts.privateKeyToAccount(privateKey)

        // 查询账户的余额
        const balance = await web3.eth.getBalance(account.address)
        const ethNum = web3.utils.fromWei(balance, 'ether')

        returnResult.data.address = account.address
        returnResult.data.privateKey = account.privateKey
        returnResult.data.balance = ethNum
        ctx.body = returnResult
    },

通过 keystore 获取账户私钥及地址

通过读取 keystore 里面存储的JSON数据及密码,通过调用 web3.eth.accounts.decrypt(keystoreStr,password) 可以获取用户的私钥及地址。

async getAccountByKeystore(ctx) {

        let returnResult = {
            code: 0,
            msg: '成功!',
            data: {}
        }
        
        const data = ctx.request.body;    // 获取上传文件

        const keystoreFile =  ctx.request.files.file
        const filePath = keystoreFile.path

        // 获取 keystore 里面的json字符串
        const keystoreStr = await fileUtil.readFile(filePath)

        // 获取账户的密码
        const password = data.password

        // 获取账户的信息地址及私钥
        const account = web3.eth.accounts.decrypt(keystoreStr,password)

        const balance = await web3.eth.getBalance(account.address)

        const ethNum = web3.utils.fromWei(balance, 'ether')

        returnResult.data.address = account.address
        returnResult.data.privateKey = account.privateKey
        returnResult.data.balance = ethNum

        ctx.body = returnResult
    }

获取 keystore 里面的json字符串,代码实现:

    readFile(fPath) {
        return new Promise(function (resolve, reject) {
            fs.readFile(fPath, 'utf-8', function(err, data) {
                if (err) reject(err);
                else resolve(data);
            });
        });
    }

上传keystore文件

页面通过 ajax 上传文件,需要构建 FormData,页面上传文件的js 代码如下

function unlockByKeystore() {
        var formData = new FormData();
        formData.append('file', $('#file')[0].files[0]);
        formData.append('password', $('#pwd').val());

        $.ajax({
            url: '/account/keystore',
            type: 'POST',
            cache: false,
            data: formData,
            processData: false,
            contentType: false
        }).done(function(res) {
            console.log(res)
            if (res.code == 0) {
                const result = res.data
                const address = result.address
                const balance = result.balance
                const privateKey = result.privateKey

                $("#balanceDiv").html(balance+' ETH')
                $("#addressDiv").html(address)
                $("#currAccount").val(address)
                $("#currAccountKey").val(privateKey)
            }
        }).fail(function(res) {});
    }

发送交易

发送交易主要分为以下几点:

  • 构建账单数据
  • 用私钥对帐单数据进行签名
  • 通过web3.eth.sendSignedTransaction 发送签名的交易。
 async sendTransaction (ctx) {
        let returnResult = {
            code: 0,
            msg: '成功!',
            data: {}
        }
        
        const data = ctx.request.body

        const currentAccount = data.currAccount
        const privateKey = data.privateKey
        const reciptAccount = data.reciptAccount
        const txValue = data.txValue
        // 获取指定账户地址的交易数
        let nonce = await web3.eth.getTransactionCount(currentAccount);
        
        // 将 ether 转为 wei
        let value = web3.utils.toWei(txValue,'ether');
        
        // 获取当前gasprice
        let gasPrice = await web3.eth.getGasPrice();
        
        // 以太币转账参数    
        let txParms = {
            from: currentAccount,
            to: reciptAccount,
            nonce: nonce,
            gasPrice: gasPrice,
            data: '0x00', // 当使用代币转账或者合约调用时
            value: value // value 是转账金额
        }
        // 获取一下预估gas
        let gas = await web3.eth.estimateGas(txParms);
        txParms.gas = gas;
        // 用密钥对账单进行签名
        let signTx = await web3.eth.accounts.signTransaction(txParms,privateKey)

        // 将签过名的账单进行发送
        try {
            await web3.eth.sendSignedTransaction(signTx.rawTransaction, function(error, hash){
                if (!error) {
                    returnResult.data.hash = hash
                } else {
                    returnResult.code = "101"
                    returnResult.msg = "失败!"
                    returnResult.data.error = error.message

                }
            })
        } catch (error) {
            console.log(error)
        }
        
        ctx.body = returnResult
    }

修改路由文件

修改 routers/index.js 添加如下内容:

const transactionController = require("../controllers/transaction")

...

router.post('/account/privatekey',accountController.getAccountByPrivatekey)
router.post('/account/keystore',accountController.getAccountByKeystore)

router.get('/transaction', transactionController.transaction)
router.post('/transaction/send', transactionController.sendTransaction)

运行项目

启动私链网络

使用 geth 启动私有网络

$ geth --datadir ~/privatechain/data0 --networkid 110  --rpc console

开启本地挖矿

miner.start()

启动项目

$ cd myWallet
$ node index.js

源码下载

https://github.com/didianV5/web3EthWallet/tree/master/005_myWallet

恭喜! 你已经成功订阅了.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.