Milton
Milton

Reputation: 171

How to convert RSK token balance to a Javascript number?

I want to get the balance (number of tokens) from the token smart contract.

I'm using web3.js to interact with the contract, and am able to get a return value. However, with this value, if I do .toString(), I see that it has the correct value. However, if I do .toNumber(), it gives me an error: Error: Number can only safely store up to 53 bits

Why does this happen? And how can I get the balance of a particular account from a smart contract, as a number (not a string)?

Upvotes: 4

Views: 1622

Answers (2)

Kseniya Zhytomyrska
Kseniya Zhytomyrska

Reputation: 21

or use BN from - it extends bytes length and actually is better (xmr / vet also needs more digits) - http://silentmatt.com/biginteger/

Upvotes: 2

bguiz
bguiz

Reputation: 28587

Smart contracts can support extremely large numbers (up to uint256 in Solidity). However the built in Number type of Javascript cannot represent numbers that large, and thus in web3.js, any numeric values are wrapped in BN (Big Number). You can find this class in web3.utils.BN.

This is why when you get the error that you are getting for your balance query, because balances are uint256, and typically used to represent 18 decimal places. We can reproduce this using only web3.js, without

const web3 = require('web3');

// the balance is a `1` with 21 `0`-s after it
// typical token would return this value for an account with 1000 tokens
const balanceBN = new web3.utils.BN('1000000000000000000000');
const balance = balanceBN.toNumber();

This throws the following error:

Uncaught Error: Number can only safely store up to 53 bits
    at assert (/some/path/node_modules/bn.js/lib/bn.js:6:21)
    at BN.toNumber (/some/path/node_modules/bn.js/lib/bn.js:519:7)

Therefore, your options are:

  • You can use .toNumber() if the BN is small enough.
  • If the BN is too large, use .div() to size it down before calling .toNumber().

Applying the above to your specific question, about getting token balances, we can do the following:

const balanceBN = contract.methods.balanceOf(myAddress).call();
const decimalsBN = contract.methods.decimals().call();

// when we know that the BN is small engouh to be represented in JS number
const decimals = decimalsBN.toNumber();

// when we know that the BN is too alrge to be represented in JS number

const balance = balanceBN.div(new  web3.utils.BN(10).pow(decimalsBN)).toNumber();
  • Query the token contract to get the balance and decimals values, both as BN
  • Convert decimals to a number directly, using .toNumber(), since we are expecting this to be small enough
  • Divide the balance BN by 10 raised to the power of the decimals BN, and then call .toNumber on it

NOTE: The resulting value of balance will match the number of tokens that is typically show in user interfaces... not the value stored in the smart contract itself.

Upvotes: 3

Related Questions