以太坊开发(三十)Bug修复:使用Web3.js转账带小数数量的代币报错

96
yuyangray
2018.12.31 18:38 字数 410

致歉

首先和大家说句抱歉!最近项目太忙,有很多朋友私信我,要我帮忙解决下问题。很多问题我也需要去试一下才能知道问题出在哪里,但是确实忙的连这个时间也没有。再次抱歉!

前言

之前写的文章以太坊开发(二十九)[升级版]使用Web3.js+Node.js封装成接口以供钱包管理/查询/转账,提供了使用Web3.js转账的方法。之前没有测试转账带小数的代币,例如9999.9999。现在项目测试时发现会报错,错误信息大致是web3.utils.toHex(amount)参数不支持小数,也就是在转账的这一步出错:

let tokenTransferData = await contract.methods.transfer(receiverAddress, 
        web3.utils.toHex(amount)).encodeABI()

按理说前面已经获取了代币小数位,然后将传入的要转账的代币数量乘以了10的小数位的次方,本来应该不会再出现小数位:

let decimals = await contract.methods.decimals().call()
let amount = num * Math.pow(10, decimals)

假如此代币的小数位为4,转账数量为9999.9999那么此处amount应该是:

9999.9999 * Math.pow(10, 4)
=
9999.9999 * 10000
=
99999999

但通过观察输出,实际结果为99999999.0001,存在小数位,所以在后面web3.utils.toHex(99999999.0001)时报错。

解决

原因和解决方法可以查看下面这篇文章:Nodejs学习笔记(十七)--- 浮点运算decimal.js

需要引入decimal.js进行处理。安装过程不再赘述,直接看修改后的代码:

Token转账代码修改:

旧:

 let decimals = await contract.methods.decimals().call()
 let amount = num * Math.pow(10, decimals)

新:

let decimals = await contract.methods.decimals().call()
let amount = new decimal(num).mul(new decimal(Math.pow(10, decimals))).toNumber()

Token余额查询也需要修改:

旧:

try {
        let balance = await contract.methods.balanceOf(address).call()
        ctx.body = await Promise.resolve({
            code: 0,
            data: balance / Math.pow(10, decimals),
            message: 'Success',
        })
    } catch (error) {
        ctx.body = await Promise.resolve({
            code: 1,
            data: {},
            message: error.stack,
        })
    }

新:

try {
        let balance = await contract.methods.balanceOf(address).call()
        ctx.body = await Promise.resolve({
            code: 0,
            data: new decimal(balance).div(new decimal(Math.pow(10, decimals))).toNumber(),
            message: 'Success',
        })
    } catch (error) {
        ctx.body = await Promise.resolve({
            code: 1,
            data: {},
            message: error.stack,
        })
    }
以太坊开发