0

I am building a system that should calculate the 1% of the tokens sent to it. I am thinking that it is ok to deal with large numbers but when it comes to smaller numbers like 1 token or 2 token [not the complete token], how can we calculate 1% of them? The token is 18 decimal places. I am looking for a chunk of code snippet for calculating 1% of the tokens value entered.

  • Tokens and Ether units (such as wei) do not have direct connection unless you give them explicit price in a crowdsale or so. If you only look at a token contract it's impossible to determine the value of one token. So it is a bit unclear to me what you are asking. – Lauri Peltonen Jul 2 at 11:28
  • thanks for a response. so let say a wallet send WHOLE 1 Token to contract and contract has to calculate 1% of it in transfer function, so complete token will be this (1000000000000000000) and then we will calculate 1% of it. Now if someone send only one chunk of the token, that is instead of 1 with 18 decimals, he sent only 1 token then how can we calculate 1% of it? – Fariha Abbasi Jul 2 at 11:32
  • If somebody sends only 1 (aka 1 wei), then you don't quite have a choice, and 1% of it is 0 wei. In fact, even 99% of it would be 0 wei. However, please note that 1 wei is typically considered an extremely small amount (that's why the "wei-resolution" of 1e18 has been introduced to begin with). So it's not likely for anyone to transfer this amount, and even if you give them less than the 1% that they're entitled to get, it's not going to make much of a difference. – goodvibration Jul 2 at 11:41
2

For the sake of this answer let me invent two new terms:

1) Display amount [of tokens]. The amount of tokens you typically see in wallets and exchanges. Can usually be a decimal number.

2) Absolute amount [of tokens]. The amount used in technical context and is always an integer. The amount given for example to ERC20's transfer function.

I think you are a bit confused about the topic of token decimal places. In absolute units 1 token is always the minimum anyone can own, transfer and receive - regardless of how many decimal places the token has. So if you call an ERC20 contract's transfer method the parameter you give it is always an integer and you are transferring full tokens.

However for display purposes 1 "absolute amount of tokens" is not the same thing as 1 "display amount of tokens". When displaying the amount of tokens the decimal places are taken into consideration. So if you have 18 decimal places then 1 display token = 1 000 000 000 000 000 000 absolute tokens and to issue a transaction with the transfer function for 1 display token you give it 1 000 000 000 000 000 000 as a parameter.

Usually people talk about display amounts of tokens but technical people may talk about absolute amount of tokens.

You can read more clarifications for example here: Decimals on ERC20 Tokens and here: What denomination should I issue my ERC20 token by?

1
  • value / 100 if you need to round result down,
  • (value + 99) / 100 to round up,
  • (value + 50) / 100 to round to nearest.

The result will be rounded to the 1/10^18 of the whole token, so precision will be quite good in all three cases.

1

I'll try and give a complete answer to the OP, building upon the very good generic explanations given by other answers.

To make things very clear here, let's talk about the case of a token defined with ZERO decimals. This effectively means that one token cannot be divided further. In Lauri Peltonen's wording it means "display amount = absolute amount".

So if you want to calculate a fraction of ONE indivisible token, well you can't. Solidity's integer division will simply truncate the decimal part of the result, and it will simply return zero if the result is below 1. Even trying to take 1% of 99 tokens will fail, in the sense that it will yield zero fees. Also, all fees will come in 100tokens-steps, so 800 to 899 tokens yield 8 and 900 yields 9, etc.

There are 3 ways to take this:

  1. Ignore it, because for example the token value is very low and a transaction for less than 1000 tokens would cost more gas than the value of the tokens. Also, a loss of a fraction of a token in fees per transfer is negligible. This is often the case when there are 18 decimals.

  2. Disallow it. You detect cases where fees are zero and revert the tx, effectively forcing users to transfer at least 100 tokens.

  3. Work around it in a way that makes sense to your business/Dapp, e.g. by forcing a round up or by keeping track of the remainder.

If you want to keep track of the lost decimal part, you can always use the "precision" trick, and here's an example of how to treat this case:

contract feeBurningToken is ERC20Token {
  using SafeMath for uint;
  uint256 constant PRECISION = 10 ** 3;
  mapping(address => uint256) unpaidPreciseFees; //stored e.g. in milliTokens 

  function transfer(address to, uint256 amount) {
    uint256 fee = feesToBurn(amount);
    super.transfer(bonfire, fee);
    super.transfer(to, amount - fee);
  }

  function transferFrom(address from, address to, uint256 amount) {
    uint256 fee = feesToBurn(amount);
    super.transferFrom(from, bonfire, fee);
    super.transferFrom(from, to, amount - fee);

  }

  function feesToBurn(uint256 amount) public returns (uint256 fee){

    uint256 fee = amount / 100;

    uint256 preciseFee = amount.mul(PRECISION) / 100;

    unpaidPreciseFees[destination] += preciseFee.mod(PRECISION);

//whenever unpaidFees get to 1000 or over, one full token is due,
//so we can now add it to the fees on this transfer.

    if (unpaidPreciseFees[destination] >= PRECISION) {
      unpaidPreciseFees[destination] -= PRECISION;
      fee++;
    }
    return fee;
  }
}

Depending on your context and how you tweak things, you might need more SafeMath in your code.

Obviously there are many ways of treating the remainder, you could for example do the contrary of the code I proposed, and round up on the first tx, then keep track of the userCredit, spending it to cover the millitokens part of the next transfer to the same destination. Btw it is more of a business decision than a developer's responsibility imho.

NOTES:

Notice this code puts an additional limit on the size of transfers, so go easy on the value chosen for precision, esp. if your token has a very large supply. A token with 18 decimals, with a precision of 18 decimals will still be able to transfer up to around 10**(77-18-18) = 10**41 "display tokens" before the multiplication overflows and reverts the tx.

  • thanks for a detailed response, it is helpful. However, I need to burn 1% of the tokens on every transfer, so what you suggest shall I do when the 1% of the transfer tokens results into value less than 1 ? – Fariha Abbasi Jul 5 at 7:38
  • The code I suggested will save and accumulate all unburned token fractions from any transfer, and burn one more token each time those accumulated fractions amount to one full token. This should ensure that your token accounting is consistent over time, and fair to each user. – blackscale Jul 5 at 8:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.