ETH Price: $2,531.13 (+1.07%)
Gas: 5.65 Gwei

Transaction Decoder

Block:
7720182 at May-08-2019 01:02:07 PM +UTC
Transaction Fee:
0.000263148 ETH $0.67
Gas Used:
65,787 Gas / 4 Gwei

Emitted Events:

270 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000001ce7ae555139c5ef5a57cc8d814a867ee6ee33d8, 0x000000000000000000000000343a3c7f789335c9ea60932d34be258f643678d9, 00000000000000000000000000000000000000000000000000000000000f4240 )
271 TokenStore.Withdraw( token=FiatTokenProxy, user=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, amount=1000000, balance=9000021 )

Account State Difference:

  Address   Before After State Difference Code
0x1cE7AE55...ee6Ee33D8
(Token.Store)
0x343a3C7F...F643678d9
0.10535342004692148 Eth
Nonce: 166
0.10509027204692148 Eth
Nonce: 167
0.000263148
(firepool)
209.668535098178390927 Eth209.668798246178390927 Eth0.000263148
0xA0b86991...E3606eB48

Execution Trace

TokenStore.withdrawToken( _token=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, _amount=1000000 )
  • FiatTokenProxy.a9059cbb( )
    • FiatTokenV1.transfer( _to=0x343a3C7F789335C9EA60932D34bE258F643678d9, _value=1000000 ) => ( True )
      File 1 of 3: TokenStore
      pragma solidity ^0.4.11;
      
      // ERC20 token protocol, see more details at
      // https://theethereum.wiki/w/index.php/ERC20_Token_Standard
      // And also https://github.com/ethereum/eips/issues/20
      
      contract Token {
        function totalSupply() constant returns (uint256 supply);
        function balanceOf(address _owner) constant returns (uint256 balance);
        function transfer(address _to, uint256 _value) returns (bool success);
        function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
        function approve(address _spender, uint256 _value) returns (bool success);
        function allowance(address _owner, address _spender) constant returns (uint256 remaining);
      
        event Transfer(address indexed _from, address indexed _to, uint256 _value);
        event Approval(address indexed _owner, address indexed _spender, uint256 _value);
      }
      
      // Safe mathematics to make the code more readable
      
      contract SafeMath {
        function safeMul(uint a, uint b) internal returns (uint) {
          uint c = a * b;
          assert(a == 0 || 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;
        }
      }
      
      // Ownable interface to simplify owner checks
      
      contract Ownable {
        address public owner;
      
        function Ownable() {
          owner = msg.sender;
        }
      
        modifier onlyOwner() {
          require(msg.sender == owner);
          _;
        }
      
        function transferOwnership(address _newOwner) onlyOwner {
          require(_newOwner != address(0));
          owner = _newOwner;
        }
      }
      
      // Interface for trading discounts and rebates for specific accounts
      
      contract AccountModifiersInterface {
        function accountModifiers(address _user) constant returns(uint takeFeeDiscount, uint rebatePercentage);
        function tradeModifiers(address _maker, address _taker) constant returns(uint takeFeeDiscount, uint rebatePercentage);
      }
      
      // Interface for trade tacker
      
      contract TradeTrackerInterface {
        function tradeComplete(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, address _get, address _give, uint _takerFee, uint _makerRebate);
      }
      
      // Exchange contract
      
      contract TokenStore is SafeMath, Ownable {
      
        // The account that will receive fees
        address feeAccount;
      
        // The account that stores fee discounts/rebates
        address accountModifiers;
        
        // Trade tracker account
        address tradeTracker;
      
        // We charge only the takers and this is the fee, percentage times 1 ether
        uint public fee;
      
        // Mapping of token addresses to mapping of account balances (token 0 means Ether)
        mapping (address => mapping (address => uint)) public tokens;
      
        // Mapping of user accounts to mapping of order hashes to uints (amount of order that has been filled)
        mapping (address => mapping (bytes32 => uint)) public orderFills;
        
        // Address of a next and previous versions of the contract, also status of the contract
        // can be used for user-triggered fund migrations
        address public successor;
        address public predecessor;
        bool public deprecated;
        uint16 public version;
      
        // Logging events
        // Note: Order creation is handled off-chain, see explanation further below
        event Cancel(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s);
        event Trade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, address get, address give, uint nonce);
        event Deposit(address token, address user, uint amount, uint balance);
        event Withdraw(address token, address user, uint amount, uint balance);
        event FundsMigrated(address user);
      
        function TokenStore(uint _fee, address _predecessor) {
          feeAccount = owner;
          fee = _fee;
          predecessor = _predecessor;
          deprecated = false;
          if (predecessor != address(0)) {
            version = TokenStore(predecessor).version() + 1;
          } else {
            version = 1;
          }
        }
      
        // Throw on default handler to prevent direct transactions of Ether
        function() {
          revert();
        }
        
        modifier deprecable() {
          require(!deprecated);
          _;
        }
      
        function deprecate(bool _deprecated, address _successor) onlyOwner {
          deprecated = _deprecated;
          successor = _successor;
        }
      
        function changeFeeAccount(address _feeAccount) onlyOwner {
          require(_feeAccount != address(0));
          feeAccount = _feeAccount;
        }
      
        function changeAccountModifiers(address _accountModifiers) onlyOwner {
          accountModifiers = _accountModifiers;
        }
        
        function changeTradeTracker(address _tradeTracker) onlyOwner {
          tradeTracker = _tradeTracker;
        }
      
        // Fee can only be decreased!
        function changeFee(uint _fee) onlyOwner {
          require(_fee <= fee);
          fee = _fee;
        }
        
        // Allows a user to get her current discount/rebate
        function getAccountModifiers() constant returns(uint takeFeeDiscount, uint rebatePercentage) {
          if (accountModifiers != address(0)) {
            return AccountModifiersInterface(accountModifiers).accountModifiers(msg.sender);
          } else {
            return (0, 0);
          }
        }
        
        ////////////////////////////////////////////////////////////////////////////////
        // Deposits, withdrawals, balances
        ////////////////////////////////////////////////////////////////////////////////
      
        function deposit() payable deprecable {
          tokens[0][msg.sender] = safeAdd(tokens[0][msg.sender], msg.value);
          Deposit(0, msg.sender, msg.value, tokens[0][msg.sender]);
        }
      
        function withdraw(uint _amount) {
          require(tokens[0][msg.sender] >= _amount);
          tokens[0][msg.sender] = safeSub(tokens[0][msg.sender], _amount);
          if (!msg.sender.call.value(_amount)()) {
            revert();
          }
          Withdraw(0, msg.sender, _amount, tokens[0][msg.sender]);
        }
      
        function depositToken(address _token, uint _amount) deprecable {
          // Note that Token(_token).approve(this, _amount) needs to be called
          // first or this contract will not be able to do the transfer.
          require(_token != 0);
          if (!Token(_token).transferFrom(msg.sender, this, _amount)) {
            revert();
          }
          tokens[_token][msg.sender] = safeAdd(tokens[_token][msg.sender], _amount);
          Deposit(_token, msg.sender, _amount, tokens[_token][msg.sender]);
        }
      
        function withdrawToken(address _token, uint _amount) {
          require(_token != 0);
          require(tokens[_token][msg.sender] >= _amount);
          tokens[_token][msg.sender] = safeSub(tokens[_token][msg.sender], _amount);
          if (!Token(_token).transfer(msg.sender, _amount)) {
            revert();
          }
          Withdraw(_token, msg.sender, _amount, tokens[_token][msg.sender]);
        }
      
        function balanceOf(address _token, address _user) constant returns (uint) {
          return tokens[_token][_user];
        }
        
        ////////////////////////////////////////////////////////////////////////////////
        // Trading
        ////////////////////////////////////////////////////////////////////////////////
      
        // Note: Order creation happens off-chain but the orders are signed by creators,
        // we validate the contents and the creator address in the logic below
      
        function trade(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive,
            uint _expires, uint _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s, uint _amount) {
          bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce);
          // Check order signatures and expiration, also check if not fulfilled yet
      		if (ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash), _v, _r, _s) != _user ||
            block.number > _expires ||
            safeAdd(orderFills[_user][hash], _amount) > _amountGet) {
            revert();
          }
          tradeBalances(_tokenGet, _amountGet, _tokenGive, _amountGive, _user, msg.sender, _amount);
          orderFills[_user][hash] = safeAdd(orderFills[_user][hash], _amount);
          Trade(_tokenGet, _amount, _tokenGive, _amountGive * _amount / _amountGet, _user, msg.sender, _nonce);
        }
        
        function tradeBalances(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive,
            address _user, address _caller, uint _amount) private {
      
          uint feeTakeValue = safeMul(_amount, fee) / (1 ether);
          uint rebateValue = 0;
          uint tokenGiveValue = safeMul(_amountGive, _amount) / _amountGet; // Proportionate to request ratio
      
          // Apply modifiers
          if (accountModifiers != address(0)) {
            var (feeTakeDiscount, rebatePercentage) = AccountModifiersInterface(accountModifiers).tradeModifiers(_user, _caller);
            // Check that the discounts/rebates are never higher then 100%
            if (feeTakeDiscount > 100) {
              feeTakeDiscount = 0;
            }
            if (rebatePercentage > 100) {
              rebatePercentage = 0;
            }
            feeTakeValue = safeMul(feeTakeValue, 100 - feeTakeDiscount) / 100;  // discounted fee
            rebateValue = safeMul(rebatePercentage, feeTakeValue) / 100;        // % of actual taker fee
          }
          
          tokens[_tokenGet][_user] = safeAdd(tokens[_tokenGet][_user], safeAdd(_amount, rebateValue));
          tokens[_tokenGet][_caller] = safeSub(tokens[_tokenGet][_caller], safeAdd(_amount, feeTakeValue));
          tokens[_tokenGive][_user] = safeSub(tokens[_tokenGive][_user], tokenGiveValue);
          tokens[_tokenGive][_caller] = safeAdd(tokens[_tokenGive][_caller], tokenGiveValue);
          tokens[_tokenGet][feeAccount] = safeAdd(tokens[_tokenGet][feeAccount], safeSub(feeTakeValue, rebateValue));
          
          if (tradeTracker != address(0)) {
            TradeTrackerInterface(tradeTracker).tradeComplete(_tokenGet, _amount, _tokenGive, tokenGiveValue, _user, _caller, feeTakeValue, rebateValue);
          }
        }
      
        function testTrade(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires,
            uint _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s, uint _amount, address _sender) constant returns(bool) {
          if (tokens[_tokenGet][_sender] < _amount ||
            availableVolume(_tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce, _user, _v, _r, _s) < _amount) {
            return false;
          }
          return true;
        }
      
        function availableVolume(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires,
            uint _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s) constant returns(uint) {
          bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce);
          if (ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash), _v, _r, _s) != _user ||
            block.number > _expires) {
            return 0;
          }
          uint available1 = safeSub(_amountGet, orderFills[_user][hash]);
          uint available2 = safeMul(tokens[_tokenGive][_user], _amountGet) / _amountGive;
          if (available1 < available2) return available1;
          return available2;
        }
      
        function amountFilled(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires,
            uint _nonce, address _user) constant returns(uint) {
          bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce);
          return orderFills[_user][hash];
        }
      
        function cancelOrder(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires,
            uint _nonce, uint8 _v, bytes32 _r, bytes32 _s) {
          bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce);
          if (!(ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash), _v, _r, _s) == msg.sender)) {
            revert();
          }
          orderFills[msg.sender][hash] = _amountGet;
          Cancel(_tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce, msg.sender, _v, _r, _s);
        }
        
        ////////////////////////////////////////////////////////////////////////////////
        // Migrations
        ////////////////////////////////////////////////////////////////////////////////
      
        // User-triggered (!) fund migrations in case contract got updated
        // Similar to withdraw but we use a successor account instead
        // As we don't store user tokens list on chain, it has to be passed from the outside
        function migrateFunds(address[] _tokens) {
        
          // Get the latest successor in the chain
          require(successor != address(0));
          TokenStore newExchange = TokenStore(successor);
          for (uint16 n = 0; n < 20; n++) {  // We will look past 20 contracts in the future
            address nextSuccessor = newExchange.successor();
            if (nextSuccessor == address(this)) {  // Circular succession
              revert();
            }
            if (nextSuccessor == address(0)) { // We reached the newest, stop
              break;
            }
            newExchange = TokenStore(nextSuccessor);
          }
      
          // Ether
          uint etherAmount = tokens[0][msg.sender];
          if (etherAmount > 0) {
            tokens[0][msg.sender] = 0;
            newExchange.depositForUser.value(etherAmount)(msg.sender);
          }
      
          // Tokens
          for (n = 0; n < _tokens.length; n++) {
            address token = _tokens[n];
            require(token != address(0)); // 0 = Ether, we handle it above
            uint tokenAmount = tokens[token][msg.sender];
            if (tokenAmount == 0) {
              continue;
            }
            if (!Token(token).approve(newExchange, tokenAmount)) {
              revert();
            }
            tokens[token][msg.sender] = 0;
            newExchange.depositTokenForUser(token, tokenAmount, msg.sender);
          }
      
          FundsMigrated(msg.sender);
        }
      
        // This is used for migrations only. To be called by previous exchange only,
        // user-triggered, on behalf of the user called the migrateFunds method.
        // Note that it does exactly the same as depositToken, but as this is called
        // by a previous generation of exchange itself, we credit internally not the
        // previous exchange, but the user it was called for.
        function depositForUser(address _user) payable deprecable {
          require(_user != address(0));
          require(msg.value > 0);
          TokenStore caller = TokenStore(msg.sender);
          require(caller.version() > 0); // Make sure it's an exchange account
          tokens[0][_user] = safeAdd(tokens[0][_user], msg.value);
        }
      
        function depositTokenForUser(address _token, uint _amount, address _user) deprecable {
          require(_token != address(0));
          require(_user != address(0));
          require(_amount > 0);
          TokenStore caller = TokenStore(msg.sender);
          require(caller.version() > 0); // Make sure it's an exchange account
          if (!Token(_token).transferFrom(msg.sender, this, _amount)) {
            revert();
          }
          tokens[_token][_user] = safeAdd(tokens[_token][_user], _amount);
        }
      }

      File 2 of 3: FiatTokenProxy
      pragma solidity ^0.4.24;
      
      // File: zos-lib/contracts/upgradeability/Proxy.sol
      
      /**
       * @title Proxy
       * @dev Implements delegation of calls to other contracts, with proper
       * forwarding of return values and bubbling of failures.
       * It defines a fallback function that delegates all calls to the address
       * returned by the abstract _implementation() internal function.
       */
      contract Proxy {
        /**
         * @dev Fallback function.
         * Implemented entirely in `_fallback`.
         */
        function () payable external {
          _fallback();
        }
      
        /**
         * @return The Address of the implementation.
         */
        function _implementation() internal view returns (address);
      
        /**
         * @dev Delegates execution to an implementation contract.
         * This is a low level function that doesn't return to its internal call site.
         * It will return to the external caller whatever the implementation returns.
         * @param implementation Address to delegate.
         */
        function _delegate(address implementation) internal {
          assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize)
      
            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
      
            // Copy the returned data.
            returndatacopy(0, 0, returndatasize)
      
            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize) }
            default { return(0, returndatasize) }
          }
        }
      
        /**
         * @dev Function that is run as the first thing in the fallback function.
         * Can be redefined in derived contracts to add functionality.
         * Redefinitions must call super._willFallback().
         */
        function _willFallback() internal {
        }
      
        /**
         * @dev fallback implementation.
         * Extracted to enable manual triggering.
         */
        function _fallback() internal {
          _willFallback();
          _delegate(_implementation());
        }
      }
      
      // File: openzeppelin-solidity/contracts/AddressUtils.sol
      
      /**
       * Utility library of inline functions on addresses
       */
      library AddressUtils {
      
        /**
         * Returns whether the target address is a contract
         * @dev This function will return false if invoked during the constructor of a contract,
         * as the code is not actually created until after the constructor finishes.
         * @param addr address to check
         * @return whether the target address is a contract
         */
        function isContract(address addr) internal view returns (bool) {
          uint256 size;
          // XXX Currently there is no better way to check if there is a contract in an address
          // than to check the size of the code at that address.
          // See https://ethereum.stackexchange.com/a/14016/36603
          // for more details about how this works.
          // TODO Check this again before the Serenity release, because all addresses will be
          // contracts then.
          // solium-disable-next-line security/no-inline-assembly
          assembly { size := extcodesize(addr) }
          return size > 0;
        }
      
      }
      
      // File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol
      
      /**
       * @title UpgradeabilityProxy
       * @dev This contract implements a proxy that allows to change the
       * implementation address to which it will delegate.
       * Such a change is called an implementation upgrade.
       */
      contract UpgradeabilityProxy is Proxy {
        /**
         * @dev Emitted when the implementation is upgraded.
         * @param implementation Address of the new implementation.
         */
        event Upgraded(address implementation);
      
        /**
         * @dev Storage slot with the address of the current implementation.
         * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
         * validated in the constructor.
         */
        bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
      
        /**
         * @dev Contract constructor.
         * @param _implementation Address of the initial implementation.
         */
        constructor(address _implementation) public {
          assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
      
          _setImplementation(_implementation);
        }
      
        /**
         * @dev Returns the current implementation.
         * @return Address of the current implementation
         */
        function _implementation() internal view returns (address impl) {
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            impl := sload(slot)
          }
        }
      
        /**
         * @dev Upgrades the proxy to a new implementation.
         * @param newImplementation Address of the new implementation.
         */
        function _upgradeTo(address newImplementation) internal {
          _setImplementation(newImplementation);
          emit Upgraded(newImplementation);
        }
      
        /**
         * @dev Sets the implementation address of the proxy.
         * @param newImplementation Address of the new implementation.
         */
        function _setImplementation(address newImplementation) private {
          require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
      
          bytes32 slot = IMPLEMENTATION_SLOT;
      
          assembly {
            sstore(slot, newImplementation)
          }
        }
      }
      
      // File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol
      
      /**
       * @title AdminUpgradeabilityProxy
       * @dev This contract combines an upgradeability proxy with an authorization
       * mechanism for administrative tasks.
       * All external functions in this contract must be guarded by the
       * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
       * feature proposal that would enable this to be done automatically.
       */
      contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
        /**
         * @dev Emitted when the administration has been transferred.
         * @param previousAdmin Address of the previous admin.
         * @param newAdmin Address of the new admin.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
      
        /**
         * @dev Storage slot with the admin of the contract.
         * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is
         * validated in the constructor.
         */
        bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
      
        /**
         * @dev Modifier to check whether the `msg.sender` is the admin.
         * If it is, it will run the function. Otherwise, it will delegate the call
         * to the implementation.
         */
        modifier ifAdmin() {
          if (msg.sender == _admin()) {
            _;
          } else {
            _fallback();
          }
        }
      
        /**
         * Contract constructor.
         * It sets the `msg.sender` as the proxy administrator.
         * @param _implementation address of the initial implementation.
         */
        constructor(address _implementation) UpgradeabilityProxy(_implementation) public {
          assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
      
          _setAdmin(msg.sender);
        }
      
        /**
         * @return The address of the proxy admin.
         */
        function admin() external view ifAdmin returns (address) {
          return _admin();
        }
      
        /**
         * @return The address of the implementation.
         */
        function implementation() external view ifAdmin returns (address) {
          return _implementation();
        }
      
        /**
         * @dev Changes the admin of the proxy.
         * Only the current admin can call this function.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeAdmin(address newAdmin) external ifAdmin {
          require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
          emit AdminChanged(_admin(), newAdmin);
          _setAdmin(newAdmin);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy.
         * Only the admin can call this function.
         * @param newImplementation Address of the new implementation.
         */
        function upgradeTo(address newImplementation) external ifAdmin {
          _upgradeTo(newImplementation);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy and call a function
         * on the new implementation.
         * This is useful to initialize the proxied contract.
         * @param newImplementation Address of the new implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be
         * called, as described in
         * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
          _upgradeTo(newImplementation);
          require(address(this).call.value(msg.value)(data));
        }
      
        /**
         * @return The admin slot.
         */
        function _admin() internal view returns (address adm) {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            adm := sload(slot)
          }
        }
      
        /**
         * @dev Sets the address of the proxy admin.
         * @param newAdmin Address of the new proxy admin.
         */
        function _setAdmin(address newAdmin) internal {
          bytes32 slot = ADMIN_SLOT;
      
          assembly {
            sstore(slot, newAdmin)
          }
        }
      
        /**
         * @dev Only fall back when the sender is not the admin.
         */
        function _willFallback() internal {
          require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
          super._willFallback();
        }
      }
      
      // File: contracts/FiatTokenProxy.sol
      
      /**
      * Copyright CENTRE SECZ 2018
      *
      * Permission is hereby granted, free of charge, to any person obtaining a copy 
      * of this software and associated documentation files (the "Software"), to deal 
      * in the Software without restriction, including without limitation the rights 
      * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
      * copies of the Software, and to permit persons to whom the Software is furnished to 
      * do so, subject to the following conditions:
      *
      * The above copyright notice and this permission notice shall be included in all 
      * copies or substantial portions of the Software.
      *
      * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
      * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
      * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
      * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
      * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      */
      
      pragma solidity ^0.4.24;
      
      
      /**
       * @title FiatTokenProxy
       * @dev This contract proxies FiatToken calls and enables FiatToken upgrades
      */ 
      contract FiatTokenProxy is AdminUpgradeabilityProxy {
          constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) {
          }
      }

      File 3 of 3: FiatTokenV1
      pragma solidity ^0.4.24;
      
      // File: contracts/Ownable.sol
      
      /**
      * Copyright CENTRE SECZ 2018
      *
      * Permission is hereby granted, free of charge, to any person obtaining a copy 
      * of this software and associated documentation files (the "Software"), to deal 
      * in the Software without restriction, including without limitation the rights 
      * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
      * copies of the Software, and to permit persons to whom the Software is furnished to 
      * do so, subject to the following conditions:
      *
      * The above copyright notice and this permission notice shall be included in all 
      * copies or substantial portions of the Software.
      *
      * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
      * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
      * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
      * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
      * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      */
      
      pragma solidity ^0.4.24;
      
      /**
       * @title Ownable
       * @dev The Ownable contract from https://github.com/zeppelinos/labs/blob/master/upgradeability_ownership/contracts/ownership/Ownable.sol 
       * branch: master commit: 3887ab77b8adafba4a26ace002f3a684c1a3388b modified to:
       * 1) Add emit prefix to OwnershipTransferred event (7/13/18)
       * 2) Replace constructor with constructor syntax (7/13/18)
       * 3) consolidate OwnableStorage into this contract
       */
      contract Ownable {
      
        // Owner of the contract
        address private _owner;
      
        /**
        * @dev Event to show ownership has been transferred
        * @param previousOwner representing the address of the previous owner
        * @param newOwner representing the address of the new owner
        */
        event OwnershipTransferred(address previousOwner, address newOwner);
      
        /**
        * @dev The constructor sets the original owner of the contract to the sender account.
        */
        constructor() public {
          setOwner(msg.sender);
        }
      
        /**
       * @dev Tells the address of the owner
       * @return the address of the owner
       */
        function owner() public view returns (address) {
          return _owner;
        }
      
        /**
         * @dev Sets a new owner address
         */
        function setOwner(address newOwner) internal {
          _owner = newOwner;
        }
      
        /**
        * @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) public onlyOwner {
          require(newOwner != address(0));
          emit OwnershipTransferred(owner(), newOwner);
          setOwner(newOwner);
        }
      }
      
      // File: contracts/Blacklistable.sol
      
      /**
      * Copyright CENTRE SECZ 2018
      *
      * Permission is hereby granted, free of charge, to any person obtaining a copy 
      * of this software and associated documentation files (the "Software"), to deal 
      * in the Software without restriction, including without limitation the rights 
      * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
      * copies of the Software, and to permit persons to whom the Software is furnished to 
      * do so, subject to the following conditions:
      *
      * The above copyright notice and this permission notice shall be included in all 
      * copies or substantial portions of the Software.
      *
      * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
      * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
      * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
      * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
      * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      */
      
      pragma solidity ^0.4.24;
      
      
      /**
       * @title Blacklistable Token
       * @dev Allows accounts to be blacklisted by a "blacklister" role
      */
      contract Blacklistable is Ownable {
      
          address public blacklister;
          mapping(address => bool) internal blacklisted;
      
          event Blacklisted(address indexed _account);
          event UnBlacklisted(address indexed _account);
          event BlacklisterChanged(address indexed newBlacklister);
      
          /**
           * @dev Throws if called by any account other than the blacklister
          */
          modifier onlyBlacklister() {
              require(msg.sender == blacklister);
              _;
          }
      
          /**
           * @dev Throws if argument account is blacklisted
           * @param _account The address to check
          */
          modifier notBlacklisted(address _account) {
              require(blacklisted[_account] == false);
              _;
          }
      
          /**
           * @dev Checks if account is blacklisted
           * @param _account The address to check    
          */
          function isBlacklisted(address _account) public view returns (bool) {
              return blacklisted[_account];
          }
      
          /**
           * @dev Adds account to blacklist
           * @param _account The address to blacklist
          */
          function blacklist(address _account) public onlyBlacklister {
              blacklisted[_account] = true;
              emit Blacklisted(_account);
          }
      
          /**
           * @dev Removes account from blacklist
           * @param _account The address to remove from the blacklist
          */
          function unBlacklist(address _account) public onlyBlacklister {
              blacklisted[_account] = false;
              emit UnBlacklisted(_account);
          }
      
          function updateBlacklister(address _newBlacklister) public onlyOwner {
              require(_newBlacklister != address(0));
              blacklister = _newBlacklister;
              emit BlacklisterChanged(blacklister);
          }
      }
      
      // File: contracts/Pausable.sol
      
      /**
      * Copyright CENTRE SECZ 2018
      *
      * Permission is hereby granted, free of charge, to any person obtaining a copy 
      * of this software and associated documentation files (the "Software"), to deal 
      * in the Software without restriction, including without limitation the rights 
      * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
      * copies of the Software, and to permit persons to whom the Software is furnished to 
      * do so, subject to the following conditions:
      *
      * The above copyright notice and this permission notice shall be included in all 
      * copies or substantial portions of the Software.
      *
      * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
      * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
      * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
      * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
      * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      */
      
      pragma solidity ^0.4.24;
      
      
      /**
       * @title Pausable
       * @dev Base contract which allows children to implement an emergency stop mechanism.
       * Based on openzeppelin tag v1.10.0 commit: feb665136c0dae9912e08397c1a21c4af3651ef3
       * Modifications:
       * 1) Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018)
       * 2) Removed whenNotPause/whenPaused from pause/unpause (6/14/2018)
       * 3) Removed whenPaused (6/14/2018)
       * 4) Switches ownable library to use zeppelinos (7/12/18)
       * 5) Remove constructor (7/13/18)
       */
      contract Pausable is Ownable {
        event Pause();
        event Unpause();
        event PauserChanged(address indexed newAddress);
      
      
        address public pauser;
        bool public paused = false;
      
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         */
        modifier whenNotPaused() {
          require(!paused);
          _;
        }
      
        /**
         * @dev throws if called by any account other than the pauser
         */
        modifier onlyPauser() {
          require(msg.sender == pauser);
          _;
        }
      
        /**
         * @dev called by the owner to pause, triggers stopped state
         */
        function pause() onlyPauser public {
          paused = true;
          emit Pause();
        }
      
        /**
         * @dev called by the owner to unpause, returns to normal state
         */
        function unpause() onlyPauser public {
          paused = false;
          emit Unpause();
        }
      
        /**
         * @dev update the pauser role
         */
        function updatePauser(address _newPauser) onlyOwner public {
          require(_newPauser != address(0));
          pauser = _newPauser;
          emit PauserChanged(pauser);
        }
      
      }
      
      // File: openzeppelin-solidity/contracts/math/SafeMath.sol
      
      /**
       * @title SafeMath
       * @dev Math operations with safety checks that throw on error
       */
      library SafeMath {
      
        /**
        * @dev Multiplies two numbers, throws on overflow.
        */
        function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
          // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
          // benefit is lost if 'b' is also tested.
          // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
          if (a == 0) {
            return 0;
          }
      
          c = a * b;
          assert(c / a == b);
          return c;
        }
      
        /**
        * @dev Integer division of two numbers, truncating the quotient.
        */
        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 a / b;
        }
      
        /**
        * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
        */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
          assert(b <= a);
          return a - b;
        }
      
        /**
        * @dev Adds two numbers, throws on overflow.
        */
        function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
          c = a + b;
          assert(c >= a);
          return c;
        }
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
      
      /**
       * @title ERC20Basic
       * @dev Simpler version of ERC20 interface
       * See https://github.com/ethereum/EIPs/issues/179
       */
      contract ERC20Basic {
        function totalSupply() public view returns (uint256);
        function balanceOf(address who) public view returns (uint256);
        function transfer(address to, uint256 value) public returns (bool);
        event Transfer(address indexed from, address indexed to, uint256 value);
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
      
      /**
       * @title ERC20 interface
       * @dev see https://github.com/ethereum/EIPs/issues/20
       */
      contract ERC20 is ERC20Basic {
        function allowance(address owner, address spender)
          public view returns (uint256);
      
        function transferFrom(address from, address to, uint256 value)
          public returns (bool);
      
        function approve(address spender, uint256 value) public returns (bool);
        event Approval(
          address indexed owner,
          address indexed spender,
          uint256 value
        );
      }
      
      // File: contracts/FiatTokenV1.sol
      
      /**
      * Copyright CENTRE SECZ 2018
      *
      * Permission is hereby granted, free of charge, to any person obtaining a copy 
      * of this software and associated documentation files (the "Software"), to deal 
      * in the Software without restriction, including without limitation the rights 
      * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
      * copies of the Software, and to permit persons to whom the Software is furnished to 
      * do so, subject to the following conditions:
      *
      * The above copyright notice and this permission notice shall be included in all 
      * copies or substantial portions of the Software.
      *
      * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
      * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
      * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
      * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
      * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      */
      
      pragma solidity ^0.4.24;
      
      
      
      
      
      
      /**
       * @title FiatToken
       * @dev ERC20 Token backed by fiat reserves
       */
      contract FiatTokenV1 is Ownable, ERC20, Pausable, Blacklistable {
          using SafeMath for uint256;
      
          string public name;
          string public symbol;
          uint8 public decimals;
          string public currency;
          address public masterMinter;
          bool internal initialized;
      
          mapping(address => uint256) internal balances;
          mapping(address => mapping(address => uint256)) internal allowed;
          uint256 internal totalSupply_ = 0;
          mapping(address => bool) internal minters;
          mapping(address => uint256) internal minterAllowed;
      
          event Mint(address indexed minter, address indexed to, uint256 amount);
          event Burn(address indexed burner, uint256 amount);
          event MinterConfigured(address indexed minter, uint256 minterAllowedAmount);
          event MinterRemoved(address indexed oldMinter);
          event MasterMinterChanged(address indexed newMasterMinter);
      
          function initialize(
              string _name,
              string _symbol,
              string _currency,
              uint8 _decimals,
              address _masterMinter,
              address _pauser,
              address _blacklister,
              address _owner
          ) public {
              require(!initialized);
              require(_masterMinter != address(0));
              require(_pauser != address(0));
              require(_blacklister != address(0));
              require(_owner != address(0));
      
              name = _name;
              symbol = _symbol;
              currency = _currency;
              decimals = _decimals;
              masterMinter = _masterMinter;
              pauser = _pauser;
              blacklister = _blacklister;
              setOwner(_owner);
              initialized = true;
          }
      
          /**
           * @dev Throws if called by any account other than a minter
          */
          modifier onlyMinters() {
              require(minters[msg.sender] == true);
              _;
          }
      
          /**
           * @dev Function to mint tokens
           * @param _to The address that will receive the minted tokens.
           * @param _amount The amount of tokens to mint. Must be less than or equal to the minterAllowance of the caller.
           * @return A boolean that indicates if the operation was successful.
          */
          function mint(address _to, uint256 _amount) whenNotPaused onlyMinters notBlacklisted(msg.sender) notBlacklisted(_to) public returns (bool) {
              require(_to != address(0));
              require(_amount > 0);
      
              uint256 mintingAllowedAmount = minterAllowed[msg.sender];
              require(_amount <= mintingAllowedAmount);
      
              totalSupply_ = totalSupply_.add(_amount);
              balances[_to] = balances[_to].add(_amount);
              minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount);
              emit Mint(msg.sender, _to, _amount);
              emit Transfer(0x0, _to, _amount);
              return true;
          }
      
          /**
           * @dev Throws if called by any account other than the masterMinter
          */
          modifier onlyMasterMinter() {
              require(msg.sender == masterMinter);
              _;
          }
      
          /**
           * @dev Get minter allowance for an account
           * @param minter The address of the minter
          */
          function minterAllowance(address minter) public view returns (uint256) {
              return minterAllowed[minter];
          }
      
          /**
           * @dev Checks if account is a minter
           * @param account The address to check    
          */
          function isMinter(address account) public view returns (bool) {
              return minters[account];
          }
      
          /**
           * @dev Get allowed amount for an account
           * @param owner address The account owner
           * @param spender address The account spender
          */
          function allowance(address owner, address spender) public view returns (uint256) {
              return allowed[owner][spender];
          }
      
          /**
           * @dev Get totalSupply of token
          */
          function totalSupply() public view returns (uint256) {
              return totalSupply_;
          }
      
          /**
           * @dev Get token balance of an account
           * @param account address The account
          */
          function balanceOf(address account) public view returns (uint256) {
              return balances[account];
          }
      
          /**
           * @dev Adds blacklisted check to approve
           * @return True if the operation was successful.
          */
          function approve(address _spender, uint256 _value) whenNotPaused notBlacklisted(msg.sender) notBlacklisted(_spender) public returns (bool) {
              allowed[msg.sender][_spender] = _value;
              emit Approval(msg.sender, _spender, _value);
              return true;
          }
      
          /**
           * @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 amount of tokens to be transferred
           * @return bool success
          */
          function transferFrom(address _from, address _to, uint256 _value) whenNotPaused notBlacklisted(_to) notBlacklisted(msg.sender) notBlacklisted(_from) public returns (bool) {
              require(_to != address(0));
              require(_value <= balances[_from]);
              require(_value <= allowed[_from][msg.sender]);
      
              balances[_from] = balances[_from].sub(_value);
              balances[_to] = balances[_to].add(_value);
              allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
              emit Transfer(_from, _to, _value);
              return true;
          }
      
          /**
           * @dev transfer token for a specified address
           * @param _to The address to transfer to.
           * @param _value The amount to be transferred.
           * @return bool success
          */
          function transfer(address _to, uint256 _value) whenNotPaused notBlacklisted(msg.sender) notBlacklisted(_to) public returns (bool) {
              require(_to != address(0));
              require(_value <= balances[msg.sender]);
      
              balances[msg.sender] = balances[msg.sender].sub(_value);
              balances[_to] = balances[_to].add(_value);
              emit Transfer(msg.sender, _to, _value);
              return true;
          }
      
          /**
           * @dev Function to add/update a new minter
           * @param minter The address of the minter
           * @param minterAllowedAmount The minting amount allowed for the minter
           * @return True if the operation was successful.
          */
          function configureMinter(address minter, uint256 minterAllowedAmount) whenNotPaused onlyMasterMinter public returns (bool) {
              minters[minter] = true;
              minterAllowed[minter] = minterAllowedAmount;
              emit MinterConfigured(minter, minterAllowedAmount);
              return true;
          }
      
          /**
           * @dev Function to remove a minter
           * @param minter The address of the minter to remove
           * @return True if the operation was successful.
          */
          function removeMinter(address minter) onlyMasterMinter public returns (bool) {
              minters[minter] = false;
              minterAllowed[minter] = 0;
              emit MinterRemoved(minter);
              return true;
          }
      
          /**
           * @dev allows a minter to burn some of its own tokens
           * Validates that caller is a minter and that sender is not blacklisted
           * amount is less than or equal to the minter's account balance
           * @param _amount uint256 the amount of tokens to be burned
          */
          function burn(uint256 _amount) whenNotPaused onlyMinters notBlacklisted(msg.sender) public {
              uint256 balance = balances[msg.sender];
              require(_amount > 0);
              require(balance >= _amount);
      
              totalSupply_ = totalSupply_.sub(_amount);
              balances[msg.sender] = balance.sub(_amount);
              emit Burn(msg.sender, _amount);
              emit Transfer(msg.sender, address(0), _amount);
          }
      
          function updateMasterMinter(address _newMasterMinter) onlyOwner public {
              require(_newMasterMinter != address(0));
              masterMinter = _newMasterMinter;
              emit MasterMinterChanged(masterMinter);
          }
      }