I have a smart contract that holds a token balance, let's call it Token A, users may send this smart contract Token B to receive Token A, they do so by specifying the amount of Token A they would like to receive and the smart contract would automatically calculate the amount of Token B they will have to spend based on a pre-defined ratio.

The problem is, on the front-end, I would like the users to be able to specify the amount of Token B they would like to spend instead of the amount of Token A they would like to receive, so I create a function to convert the user input amount of Token B to Token A, it goes something like this:

function convertBtoA(uint amountB) returns (uint) {
  return totalA.mul(amountB).div(totalB);

The user submits the result of that function, then the smart contract converts the amount back to Token B to determine the payout using another function:

function convertAtoB(uint amountA) returns (uint) {
  return totalB.mul(amountA).div(totalA);

However, the result of this function would sometimes be slightly different from the original user input amount, for example:

# given totalA == 195738239776775570; totalB == 59744193591648150
convertBtoA(50000000000000000) # output == 163813609331349736
convertAtoB(163813609331349736) # output == 49999999999999999

In the example above, the original user input is 50000000000000000 and the result of the conversion done by the smart contract is 49999999999999999. My question is, is there a way to guarantee the conversion of Token A to Token B and back to Token A to result in the same number it starts with every time?

Note: I can't feed the user input amount of Token B directly to the contract without conversion to Token A because this contract is sometimes used to pay out Token B by receiving Token A, in which case, I would like the user input to be specified in Token A.


It is not posible for all cases unless totalA equals totalB in which case you will have amountB = amountA.

The division truncates the result discarding decimals digits, in your example the return value of convertBtoA should be 163813609331349736.27117520077928403.

For most use cases small rounding errors are accepted, for example ERC20 and Ether both have 18 decimals so an error of 1/10^18 is much less than the typical transaction fee.

  • do you think a solution that detects the fraction and just + 1 to the final result would work? – Hieu Jul 21 '19 at 10:06
  • There's no way to detect that situation since the information is missing. Let's say you got 16381 how do you determine if it came from 49999 or 50000? – Ismael Jul 21 '19 at 16:24

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.