Web3 dealing with decimals in ERC20

I am using web3 v1.0.27 and trying to send erc20 tokens. These tokens have a decimal of 18 but might have a different decimal. Trying to keep it generic. I have tried the following.

const amountToSendinDecimal = amountToSend * Math.pow(10, decimal);
const amountToSendinDecimalBN = new BigNumber(amountToSendinDecimal);

With a decimal of 18 and value of 3178.44. I keep on getting the following error.

[BigNumber Error] Number primitive has more than 15 significant digits: 3.1784400000000003e+21

Cannot seems to find a work around for it.

Any suggestions.

When you have web3 js, you can do it with either of the following ways.

var amountToSend = 2;
var weiAmout = amountToSend * 1e18;
var weiAmount2 = web3.toWei(amountToSend);
• Thanks. I used a mixture or your solution and BigNumber utils. Mar 3 '18 at 12:14

The following code does this conversion carefully. It takes a BN constructor because I/you don't want to pull in a separate BN library or build web3, so you should pass it (for example) toBaseUnit("0.01", 18, web3.utils.BN)

function isString(s) {
return (typeof s === 'string' || s instanceof String)
}

export function toBaseUnit(value, decimals, BN) {
if (!isString(value)) {
throw new Error('Pass strings to prevent floating point precision issues.')
}
const ten = new BN(10);
const base = ten.pow(new BN(decimals));

// Is it negative?
let negative = (value.substring(0, 1) === '-');
if (negative) {
value = value.substring(1);
}

if (value === '.') {
throw new Error(
`Invalid value \${value} cannot be converted to`
+ ` base unit with \${decimals} decimals.`);
}

// Split it into a whole and fractional part
let comps = value.split('.');
if (comps.length > 2) { throw new Error('Too many decimal points'); }

let whole = comps, fraction = comps;

if (!whole) { whole = '0'; }
if (!fraction) { fraction = '0'; }
if (fraction.length > decimals) {
throw new Error('Too many decimal places');
}

while (fraction.length < decimals) {
fraction += '0';
}

whole = new BN(whole);
fraction = new BN(fraction);

if (negative) {
wei = wei.neg();
}

return new BN(wei.toString(10), 10);
}
• noice. This handles big values nicely Nov 22 '18 at 6:36

Full working web3.js solution with no loss of precision, reading decimals from contract first:

//create contract object using abi and address

//get decimals to multiply by token amount
contract.methods.decimals().call(function(error,d){
console.log("decimals:",error,d);

//calculate actual tokens amounts based on decimals in token
let tokens=web3.utils.toBN("0x"+(amount*10**d).toString(16));

//call mint function
//show result
console.log(error,transactionHash);
callback(transactionHash);
});
});

Javascript's Number type should not be used to handle Ethereum amounts, which generally are uint256 (256 bits unsigned integers), because for really large values Number would overflow. For this reason, numbers are usually passed as decimal (or hexadecimal) strings.

Web3 has some utility functions for doing these unit conversions in the case of ETH which has 18 decimals, same as your coin. So for example, the solution to your problem would be:

const Web3 = require('web3');
const BN = Web3.utils.BN;
let amountToSend = 3178.44;
let weiAmount = Web3.utils.toWei(amountToSend.toString());

Be aware that there are many tokens with different number of decimals, and this solution only works for 18 decimals.

const amount = 0.0001
web3.utils.toWei(amount.toString())

I used the following code to come around the issue

private _fromDecimal = (value) => { const decimal = 18; const amountToSendinDecimal = value * ( 10 ** decimal) ; return this.toBigNumber(amountToSendinDecimal); };

• In this example value must be divided, not multiplied. Also, it will fail on some values as was specified in original question. Aug 2 '19 at 13:49