Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Mistake while building an EIP20-compatible token with rational numbers

Member Posts: 16
As a proof-of-concept to learn Solidity better, I tried writing an EIP20-compatible token that internally does not store the balances as a uint256, but as a rational number (with both the numerator and the denominator being a uint256).

However, there is a mistake somewhere in the code, because the `transfer` command gobbles up all available gas.
I haven't been able to find it. Help is greatly appreciated!
`pragma solidity ^0.4.11;// Based on https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/math/SafeMath.sol/** * @title SafeMath * @dev Math operations with safety checks that throw on error */library SafeMath {  function mul(uint256 a, uint256 b) internal returns (uint256) {    uint256 c = a * b;    assert(a == 0 || c / a == b);    return c;  }  function div(uint256 a, uint256 b) internal returns (uint256) {    // assert(b > 0); // Solidity automatically throws when dividing by 0    uint256 c = a / b;    // assert(a == b * c + a % b); // There is no case in which this doesn't hold    return c;  }  function sub(uint256 a, uint256 b) internal returns (uint256) {    assert(b <= a);    return a - b;  }  function add(uint256 a, uint256 b) internal returns (uint256) {    uint256 c = a + b;    assert(c >= a);    return c;  }}`
`// math/Rational.solpragma solidity ^0.4.11;/*Author: Qqwy/Wiebe-Marten WijnjaDate: 2017-07-11*/import './SafeMath.sol';/*  A simple library,  to allow working with the behaviour of and possibilities with  rational numbers on the Ethereum blockchain.*/library Rational{    struct RationalNumber {        uint256 num;        uint256 denom;    }    /* Creates a new number and simplifies it as much as possible */    function num(uint256 num, uint256 denom) internal constant returns (RationalNumber res) {        return simplify(RationalNumber(num, denom));    }    /* Returns the greatest common divisor of two uints. */    function gcd(uint256 a, uint256 b) constant returns (uint256) {        if (a == 0) return b;        if (b == 0) return a;        return gcd(b, a % b);    }    /* Returns a simplified rational number. */    function simplify(RationalNumber a) internal returns (RationalNumber res) {        require (a.denom != 0); // Prevent rational numbers with improper denominator ('division by 0')        uint256 gcdiv = gcd(a.num, a.denom);        uint256 new_denom = a.denom / gcdiv;        return RationalNumber(a.num / gcdiv, new_denom);    }    /* -1 if a < b, 0 if the same, 1 if b > a.*/    function compare(RationalNumber a, RationalNumber b) internal returns (int8) {        uint256 normalized_a = SafeMath.mul(a.num, b.denom);        uint256 normalized_b = SafeMath.mul(b.num, a.denom);        if (normalized_a == normalized_b) return 0;        if (normalized_a < normalized_b) return -1;        return 1;    }    /* Adds two Rational Numbers. Raises on overflow/underflow */    function add(RationalNumber a, RationalNumber b) internal returns (RationalNumber res) {        uint256 new_num = SafeMath.add(SafeMath.mul(a.num, b.denom), SafeMath.mul(b.num, a.denom));        uint256 new_denom = SafeMath.mul(a.denom, b.denom);        return simplify(RationalNumber(new_num, new_denom));    }    /* Subtracts two Rational Numbers. Raises on overflow/underflow */    function sub(RationalNumber a, RationalNumber b) internal returns (RationalNumber res) {        uint256 new_num = SafeMath.sub(SafeMath.mul(a.num, b.denom), SafeMath.mul(b.num, a.denom));        uint256 new_denom = SafeMath.mul(a.denom, b.denom);        return simplify(RationalNumber(uint256(new_num), new_denom));    }    /* This result might be truncated... */    function toUint(RationalNumber number) internal returns (uint256 res) {        return number.num / number.denom;    }}`
`pragma solidity ^0.4.11;/*Author: Qqwy/Wiebe-Marten WijnjaDate: 2017-07-11*/import "./math/Rational.sol";contract RationalToken {    //Part of EIP20    string public standard = "RationalToken v0.1.0";    string public name = "RationalToken";    string public symbol = "//";    Rational.RationalNumber originalSupply = Rational.num(1e9, 1);//Rational.RationalNumber(1e77, 1e68); // million(1e9) tokens, but with 68 decimal places.    uint256 public decimals = 68; // <- Part of EIP20    mapping (address => Rational.RationalNumber) balanceOfRationals;    event Transfer(address indexed from, address indexed to, uint256 value);    function RationalToken() {        balanceOfRationals[msg.sender] = Rational.simplify(originalSupply);    }    // TODO once things can get burned!    function totalSupply() external constant returns (uint256 totalSupply) {        return Rational.toUint(originalSupply);    }    // Part of EIP20    function balanceOf(address to_) external constant returns (uint256) {        var rational = balanceOfRationals[to_];        return Rational.toUint(rational);    }    function balanceOfRational(address to_) external constant returns (uint256, uint256) {        var rational = balanceOfRationals[to_];        return (rational.num, rational.denom);    }    // Part of EIP20    function transfer(address to_, uint256 value_) external returns (bool success) {        do_transferRational(to_, Rational.num(value_, 1));        return true;    }    function transferRational(address to_, uint256 value_num_, uint256 value_denom_) external returns (bool success) {        do_transferRational(to_, Rational.num(value_num_, value_denom_));        return true;    }    function do_transferRational(address to_, Rational.RationalNumber value_) internal {        /* require (to_ != 0x0); */        /* require (Rational.compare(balanceOfRationals[msg.sender], value_) != -1); // Cannot transfer more than stored funds. */        balanceOfRationals[msg.sender] = Rational.sub(balanceOfRationals[msg.sender], value_);        balanceOfRationals[to_] = Rational.add(balanceOfRationals[to_], value_);        Transfer(msg.sender, to_, Rational.toUint(value_));        // TODO TransferRational event.    }}`

` balanceOfRationals[to_] = Rational.add(balanceOfRationals[to_], value_);`
`function simplify(RationalNumber a) internal returns (RationalNumber res) {        require (a.denom != 0); // Prevent rational numbers with improper denominator ('division by 0')`