ETH Price: $2,051.61 (-4.59%)

Transaction Decoder

Block:
9780157 at Mar-31-2020 02:50:49 PM +UTC
Transaction Fee:
0.000197012 ETH $0.40
Gas Used:
49,253 Gas / 4 Gwei

Emitted Events:

28 Exchange.OrderCancelled( id=15262, time=1585666249 )

Account State Difference:

  Address   Before After State Difference Code
(K1POOL.COM)
0.788638490612872873 Eth0.788835502612872873 Eth0.000197012
0x8D03635c...f88cAdD3c
0.034993211 Eth
Nonce: 16
0.054796199 Eth
Nonce: 17
0.019802988
0xaA5bBD5A...444Dbd586
(Saturn Network 2)
35.966444195229676859 Eth35.946444195229676859 Eth0.02

Execution Trace

Exchange.cancelOrder( orderId=15262 )
  • ETH 0.02 0x8d03635c0adf3501ccfc69e5cefd8ecf88cadd3c.CALL( )
    // Saturn Protocol
    
    // File: contracts/SafeMath.sol
    
    pragma solidity ^0.4.24;
    
    library SafeMath {
      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a * b;
        assert(a == 0 || c / a == b);
        return c;
      }
    
      function div(uint256 a, uint256 b) internal pure 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 pure returns (uint256) {
        assert(b <= a);
        return a - b;
      }
    
      function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
      }
    }
    
    // File: contracts/BytesLib.sol
    // from
    // https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
    
    
    library BytesLib {
      function toAddress(bytes _bytes, uint _start) internal pure returns (address) {
        require(_bytes.length >= (_start + 20));
        address tempAddress;
    
        assembly {
          tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }
    
        return tempAddress;
      }
    
      function toUint(bytes _bytes, uint _start) internal pure returns (uint256) {
        require(_bytes.length >= (_start + 32));
        uint256 tempUint;
    
        assembly {
          tempUint := mload(add(add(_bytes, 0x20), _start))
        }
    
        return tempUint;
      }
    }
    
    // File: contracts/ERC223.sol
    
    contract ERC223 {
      uint public totalSupply;
      function balanceOf(address who) constant public returns (uint);
    
      function name() constant public returns (string _name);
      function symbol() constant public returns (string _symbol);
      function decimals() constant public returns (uint8 _decimals);
      function totalSupply() constant public returns (uint256 _supply);
    
      function transfer(address to, uint value) public returns (bool ok);
      function transfer(address to, uint value, bytes data) public returns (bool ok);
      event Transfer(address indexed _from, address indexed _to, uint256 _value);
      event ERC223Transfer(address indexed _from, address indexed _to, uint256 _value, bytes _data);
    }
    
    contract ContractReceiver {
      function tokenFallback(address _from, uint _value, bytes _data) public;
    }
    
    contract ERC223I is ERC223 {
      using SafeMath for uint;
    
      mapping(address => uint) balances;
    
      string public name;
      string public symbol;
      uint8 public decimals;
      uint256 public totalSupply;
    
    
      function name() constant public returns (string _name) {
        return name;
      }
      function symbol() constant public returns (string _symbol) {
        return symbol;
      }
      function decimals() constant public returns (uint8 _decimals) {
        return decimals;
      }
      function totalSupply() constant public returns (uint256 _totalSupply) {
        return totalSupply;
      }
    
      function transfer(address _to, uint _value, bytes _data) public returns (bool success) {
        if (isContract(_to)) {
          return transferToContract(_to, _value, _data);
        } else {
          return transferToAddress(_to, _value, _data);
        }
      }
    
      function transfer(address _to, uint _value) public returns (bool success) {
        bytes memory empty;
        if (isContract(_to)) {
          return transferToContract(_to, _value, empty);
        } else {
          return transferToAddress(_to, _value, empty);
        }
      }
    
      function isContract(address _addr) private view returns (bool is_contract) {
        uint length;
        assembly {
          length := extcodesize(_addr)
        }
        return (length > 0);
      }
    
      function transferToAddress(address _to, uint _value, bytes _data) private returns (bool success) {
        if (balanceOf(msg.sender) < _value) revert();
        balances[msg.sender] = balanceOf(msg.sender).sub(_value);
        balances[_to] = balanceOf(_to).add(_value);
        Transfer(msg.sender, _to, _value);
        ERC223Transfer(msg.sender, _to, _value, _data);
        return true;
      }
    
      function transferToContract(address _to, uint _value, bytes _data) private returns (bool success) {
        if (balanceOf(msg.sender) < _value) revert();
        balances[msg.sender] = balanceOf(msg.sender).sub(_value);
        balances[_to] = balanceOf(_to).add(_value);
        ContractReceiver reciever = ContractReceiver(_to);
        reciever.tokenFallback(msg.sender, _value, _data);
        Transfer(msg.sender, _to, _value);
        ERC223Transfer(msg.sender, _to, _value, _data);
        return true;
      }
    
      function balanceOf(address _owner) constant public returns (uint balance) {
        return balances[_owner];
      }
    }
    
    // File: contracts/Exchange.sol
    
    // Saturn Protocol
    
    contract ERC20 {
        function totalSupply() public view returns (uint);
        function balanceOf(address holder) public view returns (uint);
        function allowance(address holder, address other) public view returns (uint);
    
        function approve(address other, uint amount) public returns (bool);
        function transfer(address to, uint amount) public returns (bool);
        function transferFrom(
            address from, address to, uint amount
        ) public returns (bool);
    }
    
    contract Exchange is ContractReceiver {
      using SafeMath for uint256;
      using BytesLib for bytes;
    
      bool private rentrancy_lock = false;
      modifier nonReentrant() {
        require(!rentrancy_lock);
        rentrancy_lock = true;
        _;
        rentrancy_lock = false;
      }
    
      struct Order {
        address owner;
        bool    active;
        address sellToken;
        address buyToken;
        address ring;
        uint256 amount;
        uint256 priceMul;
        uint256 priceDiv;
      }
    
      // person => token => balance
      mapping(address => mapping(address => uint256)) private balances;
      mapping(uint256 => Order) private orderBook;
      uint256 public orderCount;
      address private etherAddress = 0x0;
    
      address private saturnToken;
      address private admin;
      uint256 public tradeMiningBalance;
      address public treasury;
    
      uint256 public feeMul;
      uint256 public feeDiv;
      uint256 public tradeMiningMul;
      uint256 public tradeMiningDiv;
    
      event NewOrder(
        uint256 id,
        address owner,
        address sellToken,
        address buyToken,
        address ring,
        uint256 amount,
        uint256 priceMul,
        uint256 priceDiv,
        uint256 time
      );
    
      event OrderCancelled(
        uint256 id,
        uint256 time
      );
    
      event OrderFulfilled(
        uint256 id,
        uint256 time
      );
    
      event Trade(
        address from,
        address to,
        uint256 orderId,
        uint256 soldTokens,
        uint256 boughtTokens,
        uint256 feePaid,
        uint256 time
      );
    
      event Mined(
        address trader,
        uint256 amount,
        uint256 time
      );
    
      function Exchange(
        address _saturnToken,
        address _treasury,
        uint256 _feeMul,
        uint256 _feeDiv,
        uint256 _tradeMiningMul,
        uint256 _tradeMiningDiv
      ) public {
        saturnToken    = _saturnToken;
        treasury       = _treasury;
        feeMul         = _feeMul;
        feeDiv         = _feeDiv;
        tradeMiningMul = _tradeMiningMul;
        tradeMiningDiv = _tradeMiningDiv;
        admin          = msg.sender;
      }
    
      function() payable public { revert(); }
    
      //////////////////
      // public views //
      //////////////////
      // add views for prices too
      // and for order owner too
    
      function getBalance(address token, address user) view public returns(uint256) {
        return balances[user][token];
      }
    
      function isOrderActive(uint256 orderId) view public returns(bool) {
        return orderBook[orderId].active;
      }
    
      function remainingAmount(uint256 orderId) view public returns(uint256) {
        return orderBook[orderId].amount;
      }
    
      function getBuyTokenAmount(uint256 desiredSellTokenAmount, uint256 orderId) public view returns(uint256 amount) {
        require(desiredSellTokenAmount > 0);
        Order storage order = orderBook[orderId];
    
        if (order.sellToken == etherAddress || order.buyToken == etherAddress) {
          uint256 feediff = feeDiv.sub(feeMul);
          amount = desiredSellTokenAmount.mul(order.priceDiv).mul(feeDiv).div(order.priceMul).div(feediff);
        } else {
          amount = desiredSellTokenAmount.mul(order.priceDiv).div(order.priceMul);
        }
        require(amount > 0);
      }
    
      function calcFees(uint256 amount, uint256 orderId) public view returns(uint256 fees) {
        Order storage order = orderBook[orderId];
    
        if (order.sellToken == etherAddress) {
          uint256 sellTokenAmount = amount.mul(order.priceMul).div(order.priceDiv);
          fees = sellTokenAmount.mul(feeMul).div(feeDiv);
        } else if (order.buyToken == etherAddress) {
          fees = amount.mul(feeMul).div(feeDiv);
        } else {
          fees = 0;
        }
        return fees;
      }
    
      function tradeMiningAmount(uint256 fees, uint256 orderId) public view returns(uint256) {
        if (fees == 0) { return 0; }
        Order storage order = orderBook[orderId];
        if (!order.active) { return 0; }
        uint256 tokenAmount = fees.mul(tradeMiningMul).div(tradeMiningDiv);
    
        if (tradeMiningBalance < tokenAmount) {
          return tradeMiningBalance;
        } else {
          return tokenAmount;
        }
      }
    
      ////////////////////
      // public methods //
      ////////////////////
    
      function withdrawTradeMining() public {
        if (msg.sender != admin) { revert(); }
        require(tradeMiningBalance > 0);
    
        uint toSend = tradeMiningBalance;
        tradeMiningBalance = 0;
        require(sendTokensTo(admin, toSend, saturnToken));
      }
    
      function changeTradeMiningPrice(uint256 newMul, uint256 newDiv) public {
        if (msg.sender != admin) { revert(); }
        require(newDiv != 0);
        tradeMiningMul = newMul;
        tradeMiningDiv = newDiv;
      }
    
      // handle incoming ERC223 tokens
      function tokenFallback(address from, uint value, bytes data) public {
        // depending on length of data
        // this should be either an order creating transaction
        // or an order taking transaction
        // or a transaction allocating tokens for trade mining
        if (data.length == 0 && msg.sender == saturnToken) {
          _topUpTradeMining(value);
        } else if (data.length == 84) {
          _newOrder(from, msg.sender, data.toAddress(64), value, data.toUint(0), data.toUint(32), etherAddress);
        } else if (data.length == 104) {
          _newOrder(from, msg.sender, data.toAddress(64), value, data.toUint(0), data.toUint(32), data.toAddress(84));
        } else if (data.length == 32) {
          _executeOrder(from, data.toUint(0), msg.sender, value);
        } else {
          // unknown payload!
          revert();
        }
      }
    
      function sellEther(
        address buyToken,
        uint256 priceMul,
        uint256 priceDiv
      ) public payable returns(uint256 orderId) {
        require(msg.value > 0);
        return _newOrder(msg.sender, etherAddress, buyToken, msg.value, priceMul, priceDiv, etherAddress);
      }
    
      function sellEtherWithRing(
        address buyToken,
        uint256 priceMul,
        uint256 priceDiv,
        address ring
      ) public payable returns(uint256 orderId) {
        require(msg.value > 0);
        return _newOrder(msg.sender, etherAddress, buyToken, msg.value, priceMul, priceDiv, ring);
      }
    
      function buyOrderWithEth(uint256 orderId) public payable {
        require(msg.value > 0);
        _executeOrder(msg.sender, orderId, etherAddress, msg.value);
      }
    
      function sellERC20Token(
        address sellToken,
        address buyToken,
        uint256 amount,
        uint256 priceMul,
        uint256 priceDiv
      ) public returns(uint256 orderId) {
        require(amount > 0);
        uint256 pulledAmount = pullTokens(sellToken, amount);
        return _newOrder(msg.sender, sellToken, buyToken, pulledAmount, priceMul, priceDiv, etherAddress);
      }
    
      function sellERC20TokenWithRing(
        address sellToken,
        address buyToken,
        uint256 amount,
        uint256 priceMul,
        uint256 priceDiv,
        address ring
      ) public returns(uint256 orderId) {
        require(amount > 0);
        uint256 pulledAmount = pullTokens(sellToken, amount);
        return _newOrder(msg.sender, sellToken, buyToken, pulledAmount, priceMul, priceDiv, ring);
      }
    
      function buyOrderWithERC20Token(
        uint256 orderId,
        address token,
        uint256 amount
      ) public {
        require(amount > 0);
        require(pullTokens(token, amount) > 0);
        _executeOrder(msg.sender, orderId, token, amount);
      }
    
      function cancelOrder(uint256 orderId) public nonReentrant {
        Order storage order = orderBook[orderId];
        require(order.amount > 0);
        require(order.active);
        require(msg.sender == order.owner);
    
        balances[msg.sender][order.sellToken] = balances[msg.sender][order.sellToken].sub(order.amount);
        require(sendTokensTo(order.owner, order.amount, order.sellToken));
    
        // deleting the order refunds the caller some gas
        // this also sets order.active to false
        delete orderBook[orderId];
        emit OrderCancelled(orderId, now);
      }
    
      /////////////////////
      // private methods //
      /////////////////////
    
      function _newOrder(
        address owner,
        address sellToken,
        address buyToken,
        uint256 amount,
        uint256 priceMul,
        uint256 priceDiv,
        address ring
      ) private nonReentrant returns(uint256 orderId) {
        /////////////////////////
        // step 1. validations //
        /////////////////////////
        require(amount > 0);
        require(priceMul > 0);
        require(priceDiv > 0);
        require(sellToken != buyToken);
        ///////////////////////////////
        // step 2. Update order book //
        ///////////////////////////////
        orderId = orderCount++;
        orderBook[orderId] = Order(owner, true, sellToken, buyToken, ring, amount, priceMul, priceDiv);
        balances[owner][sellToken] = balances[owner][sellToken].add(amount);
    
        emit NewOrder(orderId, owner, sellToken, buyToken, ring, amount, priceMul, priceDiv, now);
      }
    
      function _executeBuyOrder(address trader, uint256 orderId, uint256 buyTokenAmount) private returns(uint256) {
        // buytoken: tkn
        // selltoken: ether
        Order storage order = orderBook[orderId];
        uint256 sellTokenAmount = buyTokenAmount.mul(order.priceMul).div(order.priceDiv);
        uint256 fees = sellTokenAmount.mul(feeMul).div(feeDiv);
    
        require(sellTokenAmount > 0);
        require(sellTokenAmount <= order.amount);
        order.amount = order.amount.sub(sellTokenAmount);
        // send tokens to order owner
        require(sendTokensTo(order.owner, buyTokenAmount, order.buyToken));
        // send ether to trader
        require(sendTokensTo(trader, sellTokenAmount.sub(fees), order.sellToken));
    
        emit Trade(trader, order.owner, orderId, sellTokenAmount.sub(fees), buyTokenAmount, fees, now);
        return fees;
      }
    
      function _executeSellOrder(address trader, uint256 orderId, uint256 buyTokenAmount) private returns(uint256) {
        // buytoken: ether
        // selltoken: tkn
        Order storage order = orderBook[orderId];
        uint256 fees = buyTokenAmount.mul(feeMul).div(feeDiv);
        uint256 sellTokenAmount = buyTokenAmount.sub(fees).mul(order.priceMul).div(order.priceDiv);
    
    
        require(sellTokenAmount > 0);
        require(sellTokenAmount <= order.amount);
        order.amount = order.amount.sub(sellTokenAmount);
        // send ether to order owner
        require(sendTokensTo(order.owner, buyTokenAmount.sub(fees), order.buyToken));
        // send token to trader
        require(sendTokensTo(trader, sellTokenAmount, order.sellToken));
    
        emit Trade(trader, order.owner, orderId, sellTokenAmount, buyTokenAmount.sub(fees), fees, now);
        return fees;
      }
    
      function _executeTokenSwap(address trader, uint256 orderId, uint256 buyTokenAmount) private returns(uint256) {
        // no ether was exchanged
        Order storage order = orderBook[orderId];
        uint256 sellTokenAmount = buyTokenAmount.mul(order.priceMul).div(order.priceDiv);
    
        require(sellTokenAmount > 0);
        require(sellTokenAmount <= order.amount);
        order.amount = order.amount.sub(sellTokenAmount);
    
        require(sendTokensTo(order.owner, buyTokenAmount, order.buyToken));
        require(order.active);
        require(sendTokensTo(trader, sellTokenAmount, order.sellToken));
    
        emit Trade(trader, order.owner, orderId, sellTokenAmount, buyTokenAmount, 0, now);
        return 0;
      }
    
      function _executeOrder(address trader, uint256 orderId, address buyToken, uint256 buyTokenAmount) private nonReentrant {
        /////////////////////////
        // step 0. validations //
        /////////////////////////
        require(orderId < orderCount);
        require(buyTokenAmount > 0);
        Order storage order = orderBook[orderId];
        require(order.active);
        require(trader != order.owner);
        require(buyToken == order.buyToken);
    
        // enforce exclusivity
        if (order.ring != etherAddress) { require(order.ring == tx.origin); }
    
        ////////////////////////////
        // step 1. token exchange //
        ////////////////////////////
        uint256 fees;
        if (order.sellToken == etherAddress) {
          // buy order: taker sends ether, gets tokens
          fees = _executeBuyOrder(trader, orderId, buyTokenAmount);
        } else if (order.buyToken == etherAddress) {
          // sell order: taker sends tokens, gets ether
          fees = _executeSellOrder(trader, orderId, buyTokenAmount);
        } else {
          fees = _executeTokenSwap(trader, orderId, buyTokenAmount);
        }
    
        ////////////////////////////
        // step 2. fees & wrap up //
        ////////////////////////////
        // collect fees and issue trade mining
        require(_tradeMiningAndFees(fees, trader));
        // deleting the order refunds the caller some gas
        if (orderBook[orderId].amount == 0) {
          delete orderBook[orderId];
          emit OrderFulfilled(orderId, now);
        }
      }
    
      function _tradeMiningAndFees(uint256 fees, address trader) private returns(bool) {
        if (fees == 0) { return true; }
        // step one: send fees to the treasury
        require(sendTokensTo(treasury, fees, etherAddress));
        if (tradeMiningBalance == 0) { return true; }
    
        // step two: calculate reward
        uint256 tokenAmount = fees.mul(tradeMiningMul).div(tradeMiningDiv);
        if (tokenAmount == 0) { return true; }
        if (tokenAmount > tradeMiningBalance) { tokenAmount = tradeMiningBalance; }
    
        // account for sent tokens
        tradeMiningBalance = tradeMiningBalance.sub(tokenAmount);
        // step three: send the reward to the trader
        require(sendTokensTo(trader, tokenAmount, saturnToken));
        emit Mined(trader, tokenAmount, now);
        return true;
      }
    
      function sendTokensTo(
        address destination,
        uint256 amount,
        address tkn
      ) private returns(bool) {
        if (tkn == etherAddress) {
          destination.transfer(amount);
        } else {
          // works with both ERC223 and ERC20
          ERC20(tkn).transfer(destination, amount);
        }
        return true;
      }
    
      // ERC20 fixture
      function pullTokens(address token, uint256 amount) private nonReentrant returns(uint256) {
        ERC20 tkn = ERC20(token);
        // need to do this balance dance in order to account for deflationary tokens
        uint256 balanceBefore = tkn.balanceOf(address(this));
        tkn.transferFrom(msg.sender, address(this), amount);
        uint256 balanceAfter = tkn.balanceOf(address(this));
        return balanceAfter.sub(balanceBefore);
      }
    
      function _topUpTradeMining(uint256 amount) private returns(bool) {
        tradeMiningBalance = tradeMiningBalance.add(amount);
        return true;
      }
    }