ETH Price: $1,846.09 (+0.66%)

Transaction Decoder

Block:
7019610 at Jan-06-2019 10:28:00 AM +UTC
Transaction Fee:
0.000268551 ETH $0.50
Gas Used:
59,678 Gas / 4.5 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Spark Pool)
9,692.894752629457442101 Eth9,692.895021180457442101 Eth0.000268551
0xc86aaBab...2c176732A
1.856667718984556503 Eth
Nonce: 1517
1.856399167984556503 Eth
Nonce: 1518
0.000268551
0xdd974D5C...6686BD200
0xed4f5326...02Cf34B04

Execution Trace

FeeBurner.sendFeeToWallet( wallet=0xb9E29984Fe50602E7A619662EBED4F90D93824C7, reserve=0x742e8BB8e6bDE9CB2DF5449f8de7510798727fB1 )
  • KyberNetworkCrystal.transferFrom( _from=0xE0E1f00A2537Eccdbb993929A4265658353Affc6, _to=0xb9E29984Fe50602E7A619662EBED4F90D93824C7, _value=690140268407632388 ) => ( True )
    sendFeeToWallet[FeeBurner (ln:367)]
    File 1 of 2: FeeBurner
    pragma solidity 0.4.18;
    
    // File: contracts/FeeBurnerInterface.sol
    
    interface FeeBurnerInterface {
        function handleFees (uint tradeWeiAmount, address reserve, address wallet) public returns(bool);
    }
    
    // File: contracts/ERC20Interface.sol
    
    // https://github.com/ethereum/EIPs/issues/20
    interface ERC20 {
        function totalSupply() public view returns (uint supply);
        function balanceOf(address _owner) public view returns (uint balance);
        function transfer(address _to, uint _value) public returns (bool success);
        function transferFrom(address _from, address _to, uint _value) public returns (bool success);
        function approve(address _spender, uint _value) public returns (bool success);
        function allowance(address _owner, address _spender) public view returns (uint remaining);
        function decimals() public view returns(uint digits);
        event Approval(address indexed _owner, address indexed _spender, uint _value);
    }
    
    // File: contracts/Utils.sol
    
    /// @title Kyber constants contract
    contract Utils {
    
        ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
        uint  constant internal PRECISION = (10**18);
        uint  constant internal MAX_QTY   = (10**28); // 10B tokens
        uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH
        uint  constant internal MAX_DECIMALS = 18;
        uint  constant internal ETH_DECIMALS = 18;
        mapping(address=>uint) internal decimals;
    
        function setDecimals(ERC20 token) internal {
            if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;
            else decimals[token] = token.decimals();
        }
    
        function getDecimals(ERC20 token) internal view returns(uint) {
            if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access
            uint tokenDecimals = decimals[token];
            // technically, there might be token with decimals 0
            // moreover, very possible that old tokens have decimals 0
            // these tokens will just have higher gas fees.
            if(tokenDecimals == 0) return token.decimals();
    
            return tokenDecimals;
        }
    
        function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
            require(srcQty <= MAX_QTY);
            require(rate <= MAX_RATE);
    
            if (dstDecimals >= srcDecimals) {
                require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;
            } else {
                require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));
            }
        }
    
        function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
            require(dstQty <= MAX_QTY);
            require(rate <= MAX_RATE);
            
            //source quantity is rounded up. to avoid dest quantity being too low.
            uint numerator;
            uint denominator;
            if (srcDecimals >= dstDecimals) {
                require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));
                denominator = rate;
            } else {
                require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                numerator = (PRECISION * dstQty);
                denominator = (rate * (10**(dstDecimals - srcDecimals)));
            }
            return (numerator + denominator - 1) / denominator; //avoid rounding down errors
        }
    }
    
    // File: contracts/PermissionGroups.sol
    
    contract PermissionGroups {
    
        address public admin;
        address public pendingAdmin;
        mapping(address=>bool) internal operators;
        mapping(address=>bool) internal alerters;
        address[] internal operatorsGroup;
        address[] internal alertersGroup;
        uint constant internal MAX_GROUP_SIZE = 50;
    
        function PermissionGroups() public {
            admin = msg.sender;
        }
    
        modifier onlyAdmin() {
            require(msg.sender == admin);
            _;
        }
    
        modifier onlyOperator() {
            require(operators[msg.sender]);
            _;
        }
    
        modifier onlyAlerter() {
            require(alerters[msg.sender]);
            _;
        }
    
        function getOperators () external view returns(address[]) {
            return operatorsGroup;
        }
    
        function getAlerters () external view returns(address[]) {
            return alertersGroup;
        }
    
        event TransferAdminPending(address pendingAdmin);
    
        /**
         * @dev Allows the current admin to set the pendingAdmin address.
         * @param newAdmin The address to transfer ownership to.
         */
        function transferAdmin(address newAdmin) public onlyAdmin {
            require(newAdmin != address(0));
            TransferAdminPending(pendingAdmin);
            pendingAdmin = newAdmin;
        }
    
        /**
         * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.
         * @param newAdmin The address to transfer ownership to.
         */
        function transferAdminQuickly(address newAdmin) public onlyAdmin {
            require(newAdmin != address(0));
            TransferAdminPending(newAdmin);
            AdminClaimed(newAdmin, admin);
            admin = newAdmin;
        }
    
        event AdminClaimed( address newAdmin, address previousAdmin);
    
        /**
         * @dev Allows the pendingAdmin address to finalize the change admin process.
         */
        function claimAdmin() public {
            require(pendingAdmin == msg.sender);
            AdminClaimed(pendingAdmin, admin);
            admin = pendingAdmin;
            pendingAdmin = address(0);
        }
    
        event AlerterAdded (address newAlerter, bool isAdd);
    
        function addAlerter(address newAlerter) public onlyAdmin {
            require(!alerters[newAlerter]); // prevent duplicates.
            require(alertersGroup.length < MAX_GROUP_SIZE);
    
            AlerterAdded(newAlerter, true);
            alerters[newAlerter] = true;
            alertersGroup.push(newAlerter);
        }
    
        function removeAlerter (address alerter) public onlyAdmin {
            require(alerters[alerter]);
            alerters[alerter] = false;
    
            for (uint i = 0; i < alertersGroup.length; ++i) {
                if (alertersGroup[i] == alerter) {
                    alertersGroup[i] = alertersGroup[alertersGroup.length - 1];
                    alertersGroup.length--;
                    AlerterAdded(alerter, false);
                    break;
                }
            }
        }
    
        event OperatorAdded(address newOperator, bool isAdd);
    
        function addOperator(address newOperator) public onlyAdmin {
            require(!operators[newOperator]); // prevent duplicates.
            require(operatorsGroup.length < MAX_GROUP_SIZE);
    
            OperatorAdded(newOperator, true);
            operators[newOperator] = true;
            operatorsGroup.push(newOperator);
        }
    
        function removeOperator (address operator) public onlyAdmin {
            require(operators[operator]);
            operators[operator] = false;
    
            for (uint i = 0; i < operatorsGroup.length; ++i) {
                if (operatorsGroup[i] == operator) {
                    operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];
                    operatorsGroup.length -= 1;
                    OperatorAdded(operator, false);
                    break;
                }
            }
        }
    }
    
    // File: contracts/Withdrawable.sol
    
    /**
     * @title Contracts that should be able to recover tokens or ethers
     * @author Ilan Doron
     * @dev This allows to recover any tokens or Ethers received in a contract.
     * This will prevent any accidental loss of tokens.
     */
    contract Withdrawable is PermissionGroups {
    
        event TokenWithdraw(ERC20 token, uint amount, address sendTo);
    
        /**
         * @dev Withdraw all ERC20 compatible tokens
         * @param token ERC20 The address of the token contract
         */
        function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {
            require(token.transfer(sendTo, amount));
            TokenWithdraw(token, amount, sendTo);
        }
    
        event EtherWithdraw(uint amount, address sendTo);
    
        /**
         * @dev Withdraw Ethers
         */
        function withdrawEther(uint amount, address sendTo) external onlyAdmin {
            sendTo.transfer(amount);
            EtherWithdraw(amount, sendTo);
        }
    }
    
    // File: contracts/FeeBurner.sol
    
    interface BurnableToken {
        function transferFrom(address _from, address _to, uint _value) public returns (bool);
        function burnFrom(address _from, uint256 _value) public returns (bool);
    }
    
    
    contract FeeBurner is Withdrawable, FeeBurnerInterface, Utils {
    
        mapping(address=>uint) public reserveFeesInBps;
        mapping(address=>address) public reserveKNCWallet; //wallet holding knc per reserve. from here burn and send fees.
        mapping(address=>uint) public walletFeesInBps; // wallet that is the source of tx is entitled so some fees.
        mapping(address=>uint) public reserveFeeToBurn;
        mapping(address=>uint) public feePayedPerReserve; // track burned fees and sent wallet fees per reserve.
        mapping(address=>mapping(address=>uint)) public reserveFeeToWallet;
        address public taxWallet;
        uint public taxFeeBps = 0; // burned fees are taxed. % out of burned fees.
    
        BurnableToken public knc;
        address public kyberNetwork;
        uint public kncPerETHRate = 300;
    
        function FeeBurner(address _admin, BurnableToken kncToken, address _kyberNetwork) public {
            require(_admin != address(0));
            require(kncToken != address(0));
            require(_kyberNetwork != address(0));
            kyberNetwork = _kyberNetwork;
            admin = _admin;
            knc = kncToken;
        }
    
        event ReserveDataSet(address reserve, uint feeInBps, address kncWallet);
        function setReserveData(address reserve, uint feesInBps, address kncWallet) public onlyAdmin {
            require(feesInBps < 100); // make sure it is always < 1%
            require(kncWallet != address(0));
            reserveFeesInBps[reserve] = feesInBps;
            reserveKNCWallet[reserve] = kncWallet;
            ReserveDataSet(reserve, feesInBps, kncWallet);
        }
    
        event WalletFeesSet(address wallet, uint feesInBps);
        function setWalletFees(address wallet, uint feesInBps) public onlyAdmin {
            require(feesInBps < 10000); // under 100%
            walletFeesInBps[wallet] = feesInBps;
            WalletFeesSet(wallet, feesInBps);
        }
    
        event TaxFeesSet(uint feesInBps);
        function setTaxInBps(uint _taxFeeBps) public onlyAdmin {
            require(_taxFeeBps < 10000); // under 100%
            taxFeeBps = _taxFeeBps;
            TaxFeesSet(_taxFeeBps);
        }
    
        event TaxWalletSet(address taxWallet);
        function setTaxWallet(address _taxWallet) public onlyAdmin {
            require(_taxWallet != address(0));
            taxWallet = _taxWallet;
            TaxWalletSet(_taxWallet);
        }
    
        function setKNCRate(uint rate) public onlyAdmin {
            require(rate <= MAX_RATE);
            kncPerETHRate = rate;
        }
    
        event AssignFeeToWallet(address reserve, address wallet, uint walletFee);
        event AssignBurnFees(address reserve, uint burnFee);
    
        function handleFees(uint tradeWeiAmount, address reserve, address wallet) public returns(bool) {
            require(msg.sender == kyberNetwork);
            require(tradeWeiAmount <= MAX_QTY);
            require(kncPerETHRate <= MAX_RATE);
    
            uint kncAmount = tradeWeiAmount * kncPerETHRate;
            uint fee = kncAmount * reserveFeesInBps[reserve] / 10000;
    
            uint walletFee = fee * walletFeesInBps[wallet] / 10000;
            require(fee >= walletFee);
            uint feeToBurn = fee - walletFee;
    
            if (walletFee > 0) {
                reserveFeeToWallet[reserve][wallet] += walletFee;
                AssignFeeToWallet(reserve, wallet, walletFee);
            }
    
            if (feeToBurn > 0) {
                AssignBurnFees(reserve, feeToBurn);
                reserveFeeToBurn[reserve] += feeToBurn;
            }
    
            return true;
        }
    
        event BurnAssignedFees(address indexed reserve, address sender, uint quantity);
    
        event SendTaxFee(address indexed reserve, address sender, address taxWallet, uint quantity);
    
        // this function is callable by anyone
        function burnReserveFees(address reserve) public {
            uint burnAmount = reserveFeeToBurn[reserve];
            uint taxToSend = 0;
            require(burnAmount > 2);
            reserveFeeToBurn[reserve] = 1; // leave 1 twei to avoid spikes in gas fee
            if (taxWallet != address(0) && taxFeeBps != 0) {
                taxToSend = (burnAmount - 1) * taxFeeBps / 10000;
                require(burnAmount - 1 > taxToSend);
                burnAmount -= taxToSend;
                if (taxToSend > 0) {
                    require(knc.transferFrom(reserveKNCWallet[reserve], taxWallet, taxToSend));
                    SendTaxFee(reserve, msg.sender, taxWallet, taxToSend);
                }
            }
            require(knc.burnFrom(reserveKNCWallet[reserve], burnAmount - 1));
    
            //update reserve "payments" so far
            feePayedPerReserve[reserve] += (taxToSend + burnAmount - 1);
    
            BurnAssignedFees(reserve, msg.sender, (burnAmount - 1));
        }
    
        event SendWalletFees(address indexed wallet, address reserve, address sender);
    
        // this function is callable by anyone
        function sendFeeToWallet(address wallet, address reserve) public {
            uint feeAmount = reserveFeeToWallet[reserve][wallet];
            require(feeAmount > 1);
            reserveFeeToWallet[reserve][wallet] = 1; // leave 1 twei to avoid spikes in gas fee
            require(knc.transferFrom(reserveKNCWallet[reserve], wallet, feeAmount - 1));
    
            feePayedPerReserve[reserve] += (feeAmount - 1);
            SendWalletFees(wallet, reserve, msg.sender);
        }
    }

    File 2 of 2: KyberNetworkCrystal
    pragma solidity ^0.4.13;
    
    library SafeMath {
      function mul(uint256 a, uint256 b) internal constant returns (uint256) {
        uint256 c = a * b;
        assert(a == 0 || c / a == b);
        return c;
      }
    
      function div(uint256 a, uint256 b) internal constant 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 constant returns (uint256) {
        assert(b <= a);
        return a - b;
      }
    
      function add(uint256 a, uint256 b) internal constant returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
      }
    }
    
    contract Ownable {
      address public owner;
    
    
      /**
       * @dev The Ownable constructor sets the original `owner` of the contract to the sender
       * account.
       */
      function Ownable() {
        owner = msg.sender;
      }
    
    
      /**
       * @dev Throws if called by any account other than the owner.
       */
      modifier onlyOwner() {
        require(msg.sender == owner);
        _;
      }
    
    
      /**
       * @dev Allows the current owner to transfer control of the contract to a newOwner.
       * @param newOwner The address to transfer ownership to.
       */
      function transferOwnership(address newOwner) onlyOwner {
        if (newOwner != address(0)) {
          owner = newOwner;
        }
      }
    
    }
    
    
    
    contract ERC20Basic {
      uint256 public totalSupply;
      function balanceOf(address who) constant returns (uint256);
      function transfer(address to, uint256 value) returns (bool);
      
      // KYBER-NOTE! code changed to comply with ERC20 standard
      event Transfer(address indexed _from, address indexed _to, uint _value);
      //event Transfer(address indexed from, address indexed to, uint256 value);
    }
    
    contract BasicToken is ERC20Basic {
      using SafeMath for uint256;
    
      mapping(address => uint256) balances;
    
      /**
      * @dev transfer token for a specified address
      * @param _to The address to transfer to.
      * @param _value The amount to be transferred.
      */
      function transfer(address _to, uint256 _value) returns (bool) {
        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        Transfer(msg.sender, _to, _value);
        return true;
      }
    
      /**
      * @dev Gets the balance of the specified address.
      * @param _owner The address to query the the balance of. 
      * @return An uint256 representing the amount owned by the passed address.
      */
      function balanceOf(address _owner) constant returns (uint256 balance) {
        return balances[_owner];
      }
    
    }
    
    contract ERC20 is ERC20Basic {
      function allowance(address owner, address spender) constant returns (uint256);
      function transferFrom(address from, address to, uint256 value) returns (bool);
      function approve(address spender, uint256 value) returns (bool);
      
      // KYBER-NOTE! code changed to comply with ERC20 standard
      event Approval(address indexed _owner, address indexed _spender, uint _value);
      //event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    contract StandardToken is ERC20, BasicToken {
    
      mapping (address => mapping (address => uint256)) allowed;
    
    
      /**
       * @dev Transfer tokens from one address to another
       * @param _from address The address which you want to send tokens from
       * @param _to address The address which you want to transfer to
       * @param _value uint256 the amout of tokens to be transfered
       */
      function transferFrom(address _from, address _to, uint256 _value) returns (bool) {
        var _allowance = allowed[_from][msg.sender];
    
        // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
        // require (_value <= _allowance);
    
        // KYBER-NOTE! code changed to comply with ERC20 standard
        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        //balances[_from] = balances[_from].sub(_value); // this was removed
        allowed[_from][msg.sender] = _allowance.sub(_value);
        Transfer(_from, _to, _value);
        return true;
      }
    
      /**
       * @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender.
       * @param _spender The address which will spend the funds.
       * @param _value The amount of tokens to be spent.
       */
      function approve(address _spender, uint256 _value) returns (bool) {
    
        // To change the approve amount you first have to reduce the addresses`
        //  allowance to zero by calling `approve(_spender, 0)` if it is not
        //  already 0 to mitigate the race condition described here:
        //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        require((_value == 0) || (allowed[msg.sender][_spender] == 0));
    
        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
      }
    
      /**
       * @dev Function to check the amount of tokens that an owner allowed to a spender.
       * @param _owner address The address which owns the funds.
       * @param _spender address The address which will spend the funds.
       * @return A uint256 specifing the amount of tokens still avaible for the spender.
       */
      function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
        return allowed[_owner][_spender];
      }
    
    }
    
    contract KyberNetworkCrystal is StandardToken, Ownable {
        string  public  constant name = "Kyber Network Crystal";
        string  public  constant symbol = "KNC";
        uint    public  constant decimals = 18;
    
        uint    public  saleStartTime;
        uint    public  saleEndTime;
    
        address public  tokenSaleContract;
    
        modifier onlyWhenTransferEnabled() {
            if( now <= saleEndTime && now >= saleStartTime ) {
                require( msg.sender == tokenSaleContract );
            }
            _;
        }
    
        modifier validDestination( address to ) {
            require(to != address(0x0));
            require(to != address(this) );
            _;
        }
    
        function KyberNetworkCrystal( uint tokenTotalAmount, uint startTime, uint endTime, address admin ) {
            // Mint all tokens. Then disable minting forever.
            balances[msg.sender] = tokenTotalAmount;
            totalSupply = tokenTotalAmount;
            Transfer(address(0x0), msg.sender, tokenTotalAmount);
    
            saleStartTime = startTime;
            saleEndTime = endTime;
    
            tokenSaleContract = msg.sender;
            transferOwnership(admin); // admin could drain tokens that were sent here by mistake
        }
    
        function transfer(address _to, uint _value)
            onlyWhenTransferEnabled
            validDestination(_to)
            returns (bool) {
            return super.transfer(_to, _value);
        }
    
        function transferFrom(address _from, address _to, uint _value)
            onlyWhenTransferEnabled
            validDestination(_to)
            returns (bool) {
            return super.transferFrom(_from, _to, _value);
        }
    
        event Burn(address indexed _burner, uint _value);
    
        function burn(uint _value) onlyWhenTransferEnabled
            returns (bool){
            balances[msg.sender] = balances[msg.sender].sub(_value);
            totalSupply = totalSupply.sub(_value);
            Burn(msg.sender, _value);
            Transfer(msg.sender, address(0x0), _value);
            return true;
        }
    
        // save some gas by making only one contract call
        function burnFrom(address _from, uint256 _value) onlyWhenTransferEnabled
            returns (bool) {
            assert( transferFrom( _from, msg.sender, _value ) );
            return burn(_value);
        }
    
        function emergencyERC20Drain( ERC20 token, uint amount ) onlyOwner {
            token.transfer( owner, amount );
        }
    }