ETH Price: $2,054.39 (+3.00%)

Transaction Decoder

Block:
11614534 at Jan-08-2021 02:10:00 PM +UTC
Transaction Fee:
0.01100682 ETH $22.61
Gas Used:
53,955 Gas / 204 Gwei

Account State Difference:

  Address   Before After State Difference Code
(zhizhu.top)
2,777.077851300345835651 Eth2,777.088858120345835651 Eth0.01100682
0x41e55600...E20e94E45
0xFBb1b73C...f520fBB98
(Bittrex)
26,420.289130187833192687 Eth
Nonce: 9774094
26,420.278123367833192687 Eth
Nonce: 9774095
0.01100682

Execution Trace

0x5adab998f44331f3416500c55d7cee93f40f1336.6ea056a9( )
  • Controller.sweeperOf( _token=0x41e5560054824eA6B0732E656E3Ad64E20e94E45 ) => ( 0xb2233FCEC42c588Ee71A594d9A25AA695345426c )
  • DefaultSweeper.sweep( _token=0x41e5560054824eA6B0732E656E3Ad64E20e94E45, _amount=116400000000 ) => ( True )
    • Controller.CALL( )
    • Controller.CALL( )
    • Controller.CALL( )
    • Controller.CALL( )
    • CentrallyIssuedToken.balanceOf( _owner=0x5adAB998f44331f3416500C55d7ceE93F40f1336 ) => ( balance=116400000000 )
    • CentrallyIssuedToken.transfer( _to=0xFBb1b73C4f0BDa4f67dcA266ce6Ef42f520fBB98, _value=116400000000 ) => ( success=True )
    • Controller.logSweep( from=0x5adAB998f44331f3416500C55d7ceE93F40f1336, to=0xFBb1b73C4f0BDa4f67dcA266ce6Ef42f520fBB98, token=0x41e5560054824eA6B0732E656E3Ad64E20e94E45, amount=116400000000 )
      File 1 of 3: CentrallyIssuedToken
      /*
       * ERC20 interface
       * see https://github.com/ethereum/EIPs/issues/20
       */
      contract ERC20 {
        uint public totalSupply;
        function balanceOf(address who) constant returns (uint);
        function allowance(address owner, address spender) constant returns (uint);
      
        function transfer(address to, uint value) returns (bool ok);
        function transferFrom(address from, address to, uint value) returns (bool ok);
        function approve(address spender, uint value) returns (bool ok);
        event Transfer(address indexed from, address indexed to, uint value);
        event Approval(address indexed owner, address indexed spender, uint value);
      }
      
      
      
      
      
      
      /**
       * Math operations with safety checks
       */
      contract SafeMath {
        function safeMul(uint a, uint b) internal returns (uint) {
          uint c = a * b;
          assert(a == 0 || c / a == b);
          return c;
        }
      
        function safeDiv(uint a, uint b) internal returns (uint) {
          assert(b > 0);
          uint c = a / b;
          assert(a == b * c + a % b);
          return c;
        }
      
        function safeSub(uint a, uint b) internal returns (uint) {
          assert(b <= a);
          return a - b;
        }
      
        function safeAdd(uint a, uint b) internal returns (uint) {
          uint c = a + b;
          assert(c>=a && c>=b);
          return c;
        }
      
        function max64(uint64 a, uint64 b) internal constant returns (uint64) {
          return a >= b ? a : b;
        }
      
        function min64(uint64 a, uint64 b) internal constant returns (uint64) {
          return a < b ? a : b;
        }
      
        function max256(uint256 a, uint256 b) internal constant returns (uint256) {
          return a >= b ? a : b;
        }
      
        function min256(uint256 a, uint256 b) internal constant returns (uint256) {
          return a < b ? a : b;
        }
      
        function assert(bool assertion) internal {
          if (!assertion) {
            throw;
          }
        }
      }
      
      
      
      /**
       * Standard ERC20 token with Short Hand Attack and approve() race condition mitigation.
       *
       * Based on code by FirstBlood:
       * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
       */
      contract StandardToken is ERC20, SafeMath {
      
        /* Actual balances of token holders */
        mapping(address => uint) balances;
      
        /* approve() allowances */
        mapping (address => mapping (address => uint)) allowed;
      
        /* Interface declaration */
        function isToken() public constant returns (bool weAre) {
          return true;
        }
      
        function transfer(address _to, uint _value) returns (bool success) {
          balances[msg.sender] = safeSub(balances[msg.sender], _value);
          balances[_to] = safeAdd(balances[_to], _value);
          Transfer(msg.sender, _to, _value);
          return true;
        }
      
        function transferFrom(address _from, address _to, uint _value) returns (bool success) {
          uint _allowance = allowed[_from][msg.sender];
      
          balances[_to] = safeAdd(balances[_to], _value);
          balances[_from] = safeSub(balances[_from], _value);
          allowed[_from][msg.sender] = safeSub(_allowance, _value);
          Transfer(_from, _to, _value);
          return true;
        }
      
        function balanceOf(address _owner) constant returns (uint balance) {
          return balances[_owner];
        }
      
        function approve(address _spender, uint _value) returns (bool success) {
      
          // 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
          if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw;
      
          allowed[msg.sender][_spender] = _value;
          Approval(msg.sender, _spender, _value);
          return true;
        }
      
        function allowance(address _owner, address _spender) constant returns (uint remaining) {
          return allowed[_owner][_spender];
        }
      
      }
      
      
      /**
       * Upgrade agent interface inspired by Lunyr.
       *
       * Upgrade agent transfers tokens to a new version of a token contract.
       * Upgrade agent can be set on a token by the upgrade master.
       *
       * Steps are
       * - Upgradeabletoken.upgradeMaster calls UpgradeableToken.setUpgradeAgent()
       * - Individual token holders can now call UpgradeableToken.upgrade()
       *   -> This results to call UpgradeAgent.upgradeFrom() that issues new tokens
       *   -> UpgradeableToken.upgrade() reduces the original total supply based on amount of upgraded tokens
       *
       * Upgrade agent itself can be the token contract, or just a middle man contract doing the heavy lifting.
       */
      contract UpgradeAgent {
      
        uint public originalSupply;
      
        /** Interface marker */
        function isUpgradeAgent() public constant returns (bool) {
          return true;
        }
      
        /**
         * Upgrade amount of tokens to a new version.
         *
         * Only callable by UpgradeableToken.
         *
         * @param _tokenHolder Address that wants to upgrade its tokens
         * @param _amount Number of tokens to upgrade. The address may consider to hold back some amount of tokens in the old version.
         */
        function upgradeFrom(address _tokenHolder, uint256 _amount) external;
      }
      
      
      /**
       * A token upgrade mechanism where users can opt-in amount of tokens to the next smart contract revision.
       *
       * First envisioned by Golem and Lunyr projects.
       */
      contract UpgradeableToken is StandardToken {
      
        /** Contract / person who can set the upgrade path. This can be the same as team multisig wallet, as what it is with its default value. */
        address public upgradeMaster;
      
        /** The next contract where the tokens will be migrated. */
        UpgradeAgent public upgradeAgent;
      
        /** How many tokens we have upgraded by now. */
        uint256 public totalUpgraded;
      
        /**
         * Upgrade states.
         *
         * - NotAllowed: The child contract has not reached a condition where the upgrade can bgun
         * - WaitingForAgent: Token allows upgrade, but we don't have a new agent yet
         * - ReadyToUpgrade: The agent is set, but not a single token has been upgraded yet
         * - Upgrading: Upgrade agent is set and the balance holders can upgrade their tokens
         *
         */
        enum UpgradeState {Unknown, NotAllowed, WaitingForAgent, ReadyToUpgrade, Upgrading}
      
        /**
         * Somebody has upgraded some of his tokens.
         */
        event Upgrade(address indexed _from, address indexed _to, uint256 _value);
      
        /**
         * New upgrade agent available.
         */
        event UpgradeAgentSet(address agent);
      
        /**
         * Upgrade master updated.
         */
        event NewUpgradeMaster(address upgradeMaster);
      
        /**
         * Do not allow construction without upgrade master set.
         */
        function UpgradeableToken(address _upgradeMaster) {
          upgradeMaster = _upgradeMaster;
          NewUpgradeMaster(upgradeMaster);
        }
      
        /**
         * Allow the token holder to upgrade some of their tokens to a new contract.
         */
        function upgrade(uint256 value) public {
      
            UpgradeState state = getUpgradeState();
            if(!(state == UpgradeState.ReadyToUpgrade || state == UpgradeState.Upgrading)) {
              // Called in a bad state
              throw;
            }
      
            // Validate input value.
            if (value == 0) throw;
      
            balances[msg.sender] = safeSub(balances[msg.sender], value);
      
            // Take tokens out from circulation
            totalSupply = safeSub(totalSupply, value);
            totalUpgraded = safeAdd(totalUpgraded, value);
      
            // Upgrade agent reissues the tokens
            upgradeAgent.upgradeFrom(msg.sender, value);
            Upgrade(msg.sender, upgradeAgent, value);
        }
      
        /**
         * Set an upgrade agent that handles
         */
        function setUpgradeAgent(address agent) external {
      
            if(!canUpgrade()) {
              // The token is not yet in a state that we could think upgrading
              throw;
            }
      
            if (agent == 0x0) throw;
            // Only a master can designate the next agent
            if (msg.sender != upgradeMaster) throw;
            // Upgrade has already begun for an agent
            if (getUpgradeState() == UpgradeState.Upgrading) throw;
      
            upgradeAgent = UpgradeAgent(agent);
      
            // Bad interface
            if(!upgradeAgent.isUpgradeAgent()) throw;
            // Make sure that token supplies match in source and target
            if (upgradeAgent.originalSupply() != totalSupply) throw;
      
            UpgradeAgentSet(upgradeAgent);
        }
      
        /**
         * Get the state of the token upgrade.
         */
        function getUpgradeState() public constant returns(UpgradeState) {
          if(!canUpgrade()) return UpgradeState.NotAllowed;
          else if(address(upgradeAgent) == 0x00) return UpgradeState.WaitingForAgent;
          else if(totalUpgraded == 0) return UpgradeState.ReadyToUpgrade;
          else return UpgradeState.Upgrading;
        }
      
        /**
         * Change the upgrade master.
         *
         * This allows us to set a new owner for the upgrade mechanism.
         */
        function setUpgradeMaster(address master) public {
            if (master == 0x0) throw;
            if (msg.sender != upgradeMaster) throw;
            upgradeMaster = master;
            NewUpgradeMaster(upgradeMaster);
        }
      
        /**
         * Child contract can enable to provide the condition when the upgrade can begun.
         */
        function canUpgrade() public constant returns(bool) {
           return true;
        }
      
      }
      
      
      
      /**
       * Centrally issued Ethereum token.
       *
       * We mix in burnable and upgradeable traits.
       *
       * Token supply is created in the token contract creation and allocated to owner.
       * The owner can then transfer from its supply to crowdsale participants.
       * The owner, or anybody, can burn any excessive tokens they are holding.
       *
       */
      contract CentrallyIssuedToken is UpgradeableToken {
      
        string public name;
        string public symbol;
        uint public decimals;
      
        /** Name and symbol were updated. */
        event UpdatedTokenInformation(string newName, string newSymbol);
      
        function CentrallyIssuedToken(address _owner, string _name, string _symbol, uint _totalSupply, uint _decimals)  UpgradeableToken(_owner) {
          name = _name;
          symbol = _symbol;
          totalSupply = _totalSupply;
          decimals = _decimals;
      
          // Allocate initial balance to the owner
          balances[_owner] = _totalSupply;
        }
      
        /**
         * Owner can update token information here.
         *
         * It is often useful to conceal the actual token association, until
         * the token operations, like central issuance or reissuance have been completed.
         * In this case the initial token can be supplied with empty name and symbol information.
         *
         * This function allows the token owner to rename the token after the operations
         * have been completed and then point the audience to use the token contract.
         */
        function setTokenInformation(string _name, string _symbol) {
      
          if(msg.sender != upgradeMaster) {
            throw;
          }
      
          if(bytes(name).length > 0 || bytes(symbol).length > 0) {
            // Information already set
            // Allow owner to set this information only once
            throw;
          }
      
          name = _name;
          symbol = _symbol;
          UpdatedTokenInformation(name, symbol);
        }
      
      }

      File 2 of 3: Controller
      pragma solidity ^0.4.10;
      
      // Copyright 2017 Bittrex
      
      contract AbstractSweeper {
          function sweep(address token, uint amount) returns (bool);
      
          function () { throw; }
      
          Controller controller;
      
          function AbstractSweeper(address _controller) {
              controller = Controller(_controller);
          }
      
          modifier canSweep() {
              if (msg.sender != controller.authorizedCaller() && msg.sender != controller.owner()) throw;
              if (controller.halted()) throw;
              _;
          }
      }
      
      contract Token {
          function balanceOf(address a) returns (uint) {
              (a);
              return 0;
          }
      
          function transfer(address a, uint val) returns (bool) {
              (a);
              (val);
              return false;
          }
      }
      
      contract DefaultSweeper is AbstractSweeper {
          function DefaultSweeper(address controller)
                   AbstractSweeper(controller) {}
      
          function sweep(address _token, uint _amount)
          canSweep
          returns (bool) {
              bool success = false;
              address destination = controller.destination();
      
              if (_token != address(0)) {
                  Token token = Token(_token);
                  uint amount = _amount;
                  if (amount > token.balanceOf(this)) {
                      return false;
                  }
      
                  success = token.transfer(destination, amount);
              }
              else {
                  uint amountInWei = _amount;
                  if (amountInWei > this.balance) {
                      return false;
                  }
      
                  success = destination.send(amountInWei);
              }
      
              if (success) {
                  controller.logSweep(this, destination, _token, _amount);
              }
              return success;
          }
      }
      
      contract UserWallet {
          AbstractSweeperList sweeperList;
          function UserWallet(address _sweeperlist) {
              sweeperList = AbstractSweeperList(_sweeperlist);
          }
      
          function () public payable { }
      
          function tokenFallback(address _from, uint _value, bytes _data) {
              (_from);
              (_value);
              (_data);
           }
      
          function sweep(address _token, uint _amount)
          returns (bool) {
              (_amount);
              return sweeperList.sweeperOf(_token).delegatecall(msg.data);
          }
      }
      
      contract AbstractSweeperList {
          function sweeperOf(address _token) returns (address);
      }
      
      contract Controller is AbstractSweeperList {
          address public owner;
          address public authorizedCaller;
      
          address public destination;
      
          bool public halted;
      
          event LogNewWallet(address receiver);
          event LogSweep(address indexed from, address indexed to, address indexed token, uint amount);
          
          modifier onlyOwner() {
              if (msg.sender != owner) throw; 
              _;
          }
      
          modifier onlyAuthorizedCaller() {
              if (msg.sender != authorizedCaller) throw; 
              _;
          }
      
          modifier onlyAdmins() {
              if (msg.sender != authorizedCaller && msg.sender != owner) throw; 
              _;
          }
      
          function Controller() 
          {
              owner = msg.sender;
              destination = msg.sender;
              authorizedCaller = msg.sender;
          }
      
          function changeAuthorizedCaller(address _newCaller) onlyOwner {
              authorizedCaller = _newCaller;
          }
      
          function changeDestination(address _dest) onlyOwner {
              destination = _dest;
          }
      
          function changeOwner(address _owner) onlyOwner {
              owner = _owner;
          }
      
          function makeWallet() onlyAdmins returns (address wallet)  {
              wallet = address(new UserWallet(this));
              LogNewWallet(wallet);
          }
      
          function halt() onlyAdmins {
              halted = true;
          }
      
          function start() onlyOwner {
              halted = false;
          }
      
          address public defaultSweeper = address(new DefaultSweeper(this));
          mapping (address => address) sweepers;
      
          function addSweeper(address _token, address _sweeper) onlyOwner {
              sweepers[_token] = _sweeper;
          }
      
          function sweeperOf(address _token) returns (address) {
              address sweeper = sweepers[_token];
              if (sweeper == 0) sweeper = defaultSweeper;
              return sweeper;
          }
      
          function logSweep(address from, address to, address token, uint amount) {
              LogSweep(from, to, token, amount);
          }
      }

      File 3 of 3: DefaultSweeper
      pragma solidity ^0.4.10;
      
      // Copyright 2017 Bittrex
      
      contract AbstractSweeper {
          function sweep(address token, uint amount) returns (bool);
      
          function () { throw; }
      
          Controller controller;
      
          function AbstractSweeper(address _controller) {
              controller = Controller(_controller);
          }
      
          modifier canSweep() {
              if (msg.sender != controller.authorizedCaller() && msg.sender != controller.owner()) throw;
              if (controller.halted()) throw;
              _;
          }
      }
      
      contract Token {
          function balanceOf(address a) returns (uint) {
              (a);
              return 0;
          }
      
          function transfer(address a, uint val) returns (bool) {
              (a);
              (val);
              return false;
          }
      }
      
      contract DefaultSweeper is AbstractSweeper {
          function DefaultSweeper(address controller)
                   AbstractSweeper(controller) {}
      
          function sweep(address _token, uint _amount)
          canSweep
          returns (bool) {
              bool success = false;
              address destination = controller.destination();
      
              if (_token != address(0)) {
                  Token token = Token(_token);
                  uint amount = _amount;
                  if (amount > token.balanceOf(this)) {
                      return false;
                  }
      
                  success = token.transfer(destination, amount);
              }
              else {
                  uint amountInWei = _amount;
                  if (amountInWei > this.balance) {
                      return false;
                  }
      
                  success = destination.send(amountInWei);
              }
      
              if (success) {
                  controller.logSweep(this, destination, _token, _amount);
              }
              return success;
          }
      }
      
      contract UserWallet {
          AbstractSweeperList sweeperList;
          function UserWallet(address _sweeperlist) {
              sweeperList = AbstractSweeperList(_sweeperlist);
          }
      
          function () public payable { }
      
          function tokenFallback(address _from, uint _value, bytes _data) {
              (_from);
              (_value);
              (_data);
           }
      
          function sweep(address _token, uint _amount)
          returns (bool) {
              (_amount);
              return sweeperList.sweeperOf(_token).delegatecall(msg.data);
          }
      }
      
      contract AbstractSweeperList {
          function sweeperOf(address _token) returns (address);
      }
      
      contract Controller is AbstractSweeperList {
          address public owner;
          address public authorizedCaller;
      
          address public destination;
      
          bool public halted;
      
          event LogNewWallet(address receiver);
          event LogSweep(address indexed from, address indexed to, address indexed token, uint amount);
          
          modifier onlyOwner() {
              if (msg.sender != owner) throw; 
              _;
          }
      
          modifier onlyAuthorizedCaller() {
              if (msg.sender != authorizedCaller) throw; 
              _;
          }
      
          modifier onlyAdmins() {
              if (msg.sender != authorizedCaller && msg.sender != owner) throw; 
              _;
          }
      
          function Controller() 
          {
              owner = msg.sender;
              destination = msg.sender;
              authorizedCaller = msg.sender;
          }
      
          function changeAuthorizedCaller(address _newCaller) onlyOwner {
              authorizedCaller = _newCaller;
          }
      
          function changeDestination(address _dest) onlyOwner {
              destination = _dest;
          }
      
          function changeOwner(address _owner) onlyOwner {
              owner = _owner;
          }
      
          function makeWallet() onlyAdmins returns (address wallet)  {
              wallet = address(new UserWallet(this));
              LogNewWallet(wallet);
          }
      
          function halt() onlyAdmins {
              halted = true;
          }
      
          function start() onlyOwner {
              halted = false;
          }
      
          address public defaultSweeper = address(new DefaultSweeper(this));
          mapping (address => address) sweepers;
      
          function addSweeper(address _token, address _sweeper) onlyOwner {
              sweepers[_token] = _sweeper;
          }
      
          function sweeperOf(address _token) returns (address) {
              address sweeper = sweepers[_token];
              if (sweeper == 0) sweeper = defaultSweeper;
              return sweeper;
          }
      
          function logSweep(address from, address to, address token, uint amount) {
              LogSweep(from, to, token, amount);
          }
      }