ETH Price: $2,536.04 (-1.74%)

Transaction Decoder

Block:
7538841 at Apr-10-2019 06:59:35 AM +UTC
Transaction Fee:
0.00080273584375 ETH $2.04
Gas Used:
113,161 Gas / 7.09375 Gwei

Account State Difference:

  Address   Before After State Difference Code
0xD26205A5...ec43B3802
0.0209029689363056 Eth
Nonce: 50
0.0201002330925556 Eth
Nonce: 51
0.00080273584375
(Ethermine)
429.156416121208246613 Eth429.157218857051996613 Eth0.00080273584375

Execution Trace

Refereum.transfer( _to=0x85ED9864c77A14f2c737DE60C5f8c91cA3377323, _value=98000000 )
  • 0xdfb0acd77b0a9538d2d1b05399a1f254a541076f.1962df71( )
    • 0x79f8818d98e6f6eb33aa3468091281c2576e969d.f7ebc39a( )
    • Refereum._forwardTransferFromWithReference( _from=0xD26205A544EC45d8611DF4D3f22b1b3ec43B3802, _to=0x85ED9864c77A14f2c737DE60C5f8c91cA3377323, _value=98000000, _reference=, _sender=0xD26205A544EC45d8611DF4D3f22b1b3ec43B3802 )
      • EToken2.proxyTransferFromWithReference( _from=0xD26205A544EC45d8611DF4D3f22b1b3ec43B3802, _to=0x85ED9864c77A14f2c737DE60C5f8c91cA3377323, _value=98000000, _symbol=5246520000000000000000000000000000000000000000000000000000000000, _reference=, _sender=0xD26205A544EC45d8611DF4D3f22b1b3ec43B3802 )
        transfer[Refereum (ln:211)]
        File 1 of 2: Refereum
        pragma solidity 0.4.11;
        
        contract RegistryICAPInterface {
            function parse(bytes32 _icap) constant returns(address, bytes32, bool);
            function institutions(bytes32 _institution) constant returns(address);
        }
        
        contract EToken2Interface {
            function registryICAP() constant returns(RegistryICAPInterface);
            function baseUnit(bytes32 _symbol) constant returns(uint8);
            function description(bytes32 _symbol) constant returns(string);
            function owner(bytes32 _symbol) constant returns(address);
            function isOwner(address _owner, bytes32 _symbol) constant returns(bool);
            function totalSupply(bytes32 _symbol) constant returns(uint);
            function balanceOf(address _holder, bytes32 _symbol) constant returns(uint);
            function isLocked(bytes32 _symbol) constant returns(bool);
            function issueAsset(bytes32 _symbol, uint _value, string _name, string _description, uint8 _baseUnit, bool _isReissuable) returns(bool);
            function reissueAsset(bytes32 _symbol, uint _value) returns(bool);
            function revokeAsset(bytes32 _symbol, uint _value) returns(bool);
            function setProxy(address _address, bytes32 _symbol) returns(bool);
            function lockAsset(bytes32 _symbol) returns(bool);
            function proxyTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
            function proxyApprove(address _spender, uint _value, bytes32 _symbol, address _sender) returns(bool);
            function allowance(address _from, address _spender, bytes32 _symbol) constant returns(uint);
            function proxyTransferFromWithReference(address _from, address _to, uint _value, bytes32 _symbol, string _reference, address _sender) returns(bool);
        }
        
        contract AssetInterface {
            function _performTransferWithReference(address _to, uint _value, string _reference, address _sender) returns(bool);
            function _performTransferToICAPWithReference(bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
            function _performApprove(address _spender, uint _value, address _sender) returns(bool);
            function _performTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) returns(bool);
            function _performTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
            function _performGeneric(bytes _data, address _sender) payable returns(bytes32) {
                throw;
            }
        }
        
        contract ERC20Interface {
            event Transfer(address indexed from, address indexed to, uint256 value);
            event Approval(address indexed from, address indexed spender, uint256 value);
        
            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);
        
            // function symbol() constant returns(string);
            function decimals() constant returns(uint8);
            // function name() constant returns(string);
        }
        
        contract AssetProxyInterface {
            function _forwardApprove(address _spender, uint _value, address _sender) returns(bool);
            function _forwardTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) returns(bool);
            function _forwardTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
            function balanceOf(address _owner) constant returns(uint);
        }
        
        contract Bytes32 {
            function _bytes32(string _input) internal constant returns(bytes32 result) {
                assembly {
                    result := mload(add(_input, 32))
                }
            }
        }
        
        /**
         * @title EToken2 Asset Proxy.
         *
         * Proxy implements ERC20 interface and acts as a gateway to a single EToken2 asset.
         * Proxy adds etoken2Symbol and caller(sender) when forwarding requests to EToken2.
         * Every request that is made by caller first sent to the specific asset implementation
         * contract, which then calls back to be forwarded onto EToken2.
         *
         * Calls flow: Caller ->
         *             Proxy.func(...) ->
         *             Asset._performFunc(..., Caller.address) ->
         *             Proxy._forwardFunc(..., Caller.address) ->
         *             Platform.proxyFunc(..., symbol, Caller.address)
         *
         * Generic call flow: Caller ->
         *             Proxy.unknownFunc(...) ->
         *             Asset._performGeneric(..., Caller.address) ->
         *             Asset.unknownFunc(...)
         *
         * Asset implementation contract is mutable, but each user have an option to stick with
         * old implementation, through explicit decision made in timely manner, if he doesn't agree
         * with new rules.
         * Each user have a possibility to upgrade to latest asset contract implementation, without the
         * possibility to rollback.
         *
         * Note: all the non constant functions return false instead of throwing in case if state change
         * didn't happen yet.
         */
        contract Refereum is ERC20Interface, AssetProxyInterface, Bytes32 {
            // Assigned EToken2, immutable.
            EToken2Interface public etoken2;
        
            // Assigned symbol, immutable.
            bytes32 public etoken2Symbol;
        
            // Assigned name, immutable. For UI.
            string public name;
            string public symbol;
        
            /**
             * Sets EToken2 address, assigns symbol and name.
             *
             * Can be set only once.
             *
             * @param _etoken2 EToken2 contract address.
             * @param _symbol assigned symbol.
             * @param _name assigned name.
             *
             * @return success.
             */
            function init(EToken2Interface _etoken2, string _symbol, string _name) returns(bool) {
                if (address(etoken2) != 0x0) {
                    return false;
                }
                etoken2 = _etoken2;
                etoken2Symbol = _bytes32(_symbol);
                name = _name;
                symbol = _symbol;
                return true;
            }
        
            /**
             * Only EToken2 is allowed to call.
             */
            modifier onlyEToken2() {
                if (msg.sender == address(etoken2)) {
                    _;
                }
            }
        
            /**
             * Only current asset owner is allowed to call.
             */
            modifier onlyAssetOwner() {
                if (etoken2.isOwner(msg.sender, etoken2Symbol)) {
                    _;
                }
            }
        
            /**
             * Returns asset implementation contract for current caller.
             *
             * @return asset implementation contract.
             */
            function _getAsset() internal returns(AssetInterface) {
                return AssetInterface(getVersionFor(msg.sender));
            }
        
            function recoverTokens(uint _value) onlyAssetOwner() returns(bool) {
                return this.transferWithReference(msg.sender, _value, 'Tokens recovery');
            }
        
            /**
             * Returns asset total supply.
             *
             * @return asset total supply.
             */
            function totalSupply() constant returns(uint) {
                return etoken2.totalSupply(etoken2Symbol);
            }
        
            /**
             * Returns asset balance for a particular holder.
             *
             * @param _owner holder address.
             *
             * @return holder balance.
             */
            function balanceOf(address _owner) constant returns(uint) {
                return etoken2.balanceOf(_owner, etoken2Symbol);
            }
        
            /**
             * Returns asset allowance from one holder to another.
             *
             * @param _from holder that allowed spending.
             * @param _spender holder that is allowed to spend.
             *
             * @return holder to spender allowance.
             */
            function allowance(address _from, address _spender) constant returns(uint) {
                return etoken2.allowance(_from, _spender, etoken2Symbol);
            }
        
            /**
             * Returns asset decimals.
             *
             * @return asset decimals.
             */
            function decimals() constant returns(uint8) {
                return etoken2.baseUnit(etoken2Symbol);
            }
        
            /**
             * Transfers asset balance from the caller to specified receiver.
             *
             * @param _to holder address to give to.
             * @param _value amount to transfer.
             *
             * @return success.
             */
            function transfer(address _to, uint _value) returns(bool) {
                return transferWithReference(_to, _value, '');
            }
        
            /**
             * Transfers asset balance from the caller to specified receiver adding specified comment.
             * Resolves asset implementation contract for the caller and forwards there arguments along with
             * the caller address.
             *
             * @param _to holder address to give to.
             * @param _value amount to transfer.
             * @param _reference transfer comment to be included in a EToken2's Transfer event.
             *
             * @return success.
             */
            function transferWithReference(address _to, uint _value, string _reference) returns(bool) {
                return _getAsset()._performTransferWithReference(_to, _value, _reference, msg.sender);
            }
        
            /**
             * Transfers asset balance from the caller to specified ICAP.
             *
             * @param _icap recipient ICAP to give to.
             * @param _value amount to transfer.
             *
             * @return success.
             */
            function transferToICAP(bytes32 _icap, uint _value) returns(bool) {
                return transferToICAPWithReference(_icap, _value, '');
            }
        
            /**
             * Transfers asset balance from the caller to specified ICAP adding specified comment.
             * Resolves asset implementation contract for the caller and forwards there arguments along with
             * the caller address.
             *
             * @param _icap recipient ICAP to give to.
             * @param _value amount to transfer.
             * @param _reference transfer comment to be included in a EToken2's Transfer event.
             *
             * @return success.
             */
            function transferToICAPWithReference(bytes32 _icap, uint _value, string _reference) returns(bool) {
                return _getAsset()._performTransferToICAPWithReference(_icap, _value, _reference, msg.sender);
            }
        
            /**
             * Prforms allowance transfer of asset balance between holders.
             *
             * @param _from holder address to take from.
             * @param _to holder address to give to.
             * @param _value amount to transfer.
             *
             * @return success.
             */
            function transferFrom(address _from, address _to, uint _value) returns(bool) {
                return transferFromWithReference(_from, _to, _value, '');
            }
        
            /**
             * Prforms allowance transfer of asset balance between holders adding specified comment.
             * Resolves asset implementation contract for the caller and forwards there arguments along with
             * the caller address.
             *
             * @param _from holder address to take from.
             * @param _to holder address to give to.
             * @param _value amount to transfer.
             * @param _reference transfer comment to be included in a EToken2's Transfer event.
             *
             * @return success.
             */
            function transferFromWithReference(address _from, address _to, uint _value, string _reference) returns(bool) {
                return _getAsset()._performTransferFromWithReference(_from, _to, _value, _reference, msg.sender);
            }
        
            /**
             * Performs transfer call on the EToken2 by the name of specified sender.
             *
             * Can only be called by asset implementation contract assigned to sender.
             *
             * @param _from holder address to take from.
             * @param _to holder address to give to.
             * @param _value amount to transfer.
             * @param _reference transfer comment to be included in a EToken2's Transfer event.
             * @param _sender initial caller.
             *
             * @return success.
             */
            function _forwardTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) onlyImplementationFor(_sender) returns(bool) {
                return etoken2.proxyTransferFromWithReference(_from, _to, _value, etoken2Symbol, _reference, _sender);
            }
        
            /**
             * Prforms allowance transfer of asset balance between holders.
             *
             * @param _from holder address to take from.
             * @param _icap recipient ICAP address to give to.
             * @param _value amount to transfer.
             *
             * @return success.
             */
            function transferFromToICAP(address _from, bytes32 _icap, uint _value) returns(bool) {
                return transferFromToICAPWithReference(_from, _icap, _value, '');
            }
        
            /**
             * Prforms allowance transfer of asset balance between holders adding specified comment.
             * Resolves asset implementation contract for the caller and forwards there arguments along with
             * the caller address.
             *
             * @param _from holder address to take from.
             * @param _icap recipient ICAP address to give to.
             * @param _value amount to transfer.
             * @param _reference transfer comment to be included in a EToken2's Transfer event.
             *
             * @return success.
             */
            function transferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference) returns(bool) {
                return _getAsset()._performTransferFromToICAPWithReference(_from, _icap, _value, _reference, msg.sender);
            }
        
            /**
             * Performs allowance transfer to ICAP call on the EToken2 by the name of specified sender.
             *
             * Can only be called by asset implementation contract assigned to sender.
             *
             * @param _from holder address to take from.
             * @param _icap recipient ICAP address to give to.
             * @param _value amount to transfer.
             * @param _reference transfer comment to be included in a EToken2's Transfer event.
             * @param _sender initial caller.
             *
             * @return success.
             */
            function _forwardTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) onlyImplementationFor(_sender) returns(bool) {
                return etoken2.proxyTransferFromToICAPWithReference(_from, _icap, _value, _reference, _sender);
            }
        
            /**
             * Sets asset spending allowance for a specified spender.
             * Resolves asset implementation contract for the caller and forwards there arguments along with
             * the caller address.
             *
             * @param _spender holder address to set allowance to.
             * @param _value amount to allow.
             *
             * @return success.
             */
            function approve(address _spender, uint _value) returns(bool) {
                return _getAsset()._performApprove(_spender, _value, msg.sender);
            }
        
            /**
             * Performs allowance setting call on the EToken2 by the name of specified sender.
             *
             * Can only be called by asset implementation contract assigned to sender.
             *
             * @param _spender holder address to set allowance to.
             * @param _value amount to allow.
             * @param _sender initial caller.
             *
             * @return success.
             */
            function _forwardApprove(address _spender, uint _value, address _sender) onlyImplementationFor(_sender) returns(bool) {
                return etoken2.proxyApprove(_spender, _value, etoken2Symbol, _sender);
            }
        
            /**
             * Emits ERC20 Transfer event on this contract.
             *
             * Can only be, and, called by assigned EToken2 when asset transfer happens.
             */
            function emitTransfer(address _from, address _to, uint _value) onlyEToken2() {
                Transfer(_from, _to, _value);
            }
        
            /**
             * Emits ERC20 Approval event on this contract.
             *
             * Can only be, and, called by assigned EToken2 when asset allowance set happens.
             */
            function emitApprove(address _from, address _spender, uint _value) onlyEToken2() {
                Approval(_from, _spender, _value);
            }
        
            /**
             * Resolves asset implementation contract for the caller and forwards there transaction data,
             * along with the value. This allows for proxy interface growth.
             */
            function () payable {
                bytes32 result = _getAsset()._performGeneric.value(msg.value)(msg.data, msg.sender);
                assembly {
                    mstore(0, result)
                    return(0, 32)
                }
            }
        
            /**
             * Indicates an upgrade freeze-time start, and the next asset implementation contract.
             */
            event UpgradeProposal(address newVersion);
        
            // Current asset implementation contract address.
            address latestVersion;
        
            // Proposed next asset implementation contract address.
            address pendingVersion;
        
            // Upgrade freeze-time start.
            uint pendingVersionTimestamp;
        
            // Timespan for users to review the new implementation and make decision.
            uint constant UPGRADE_FREEZE_TIME = 3 days;
        
            // Asset implementation contract address that user decided to stick with.
            // 0x0 means that user uses latest version.
            mapping(address => address) userOptOutVersion;
        
            /**
             * Only asset implementation contract assigned to sender is allowed to call.
             */
            modifier onlyImplementationFor(address _sender) {
                if (getVersionFor(_sender) == msg.sender) {
                    _;
                }
            }
        
            /**
             * Returns asset implementation contract address assigned to sender.
             *
             * @param _sender sender address.
             *
             * @return asset implementation contract address.
             */
            function getVersionFor(address _sender) constant returns(address) {
                return userOptOutVersion[_sender] == 0 ? latestVersion : userOptOutVersion[_sender];
            }
        
            /**
             * Returns current asset implementation contract address.
             *
             * @return asset implementation contract address.
             */
            function getLatestVersion() constant returns(address) {
                return latestVersion;
            }
        
            /**
             * Returns proposed next asset implementation contract address.
             *
             * @return asset implementation contract address.
             */
            function getPendingVersion() constant returns(address) {
                return pendingVersion;
            }
        
            /**
             * Returns upgrade freeze-time start.
             *
             * @return freeze-time start.
             */
            function getPendingVersionTimestamp() constant returns(uint) {
                return pendingVersionTimestamp;
            }
        
            /**
             * Propose next asset implementation contract address.
             *
             * Can only be called by current asset owner.
             *
             * Note: freeze-time should not be applied for the initial setup.
             *
             * @param _newVersion asset implementation contract address.
             *
             * @return success.
             */
            function proposeUpgrade(address _newVersion) onlyAssetOwner() returns(bool) {
                // Should not already be in the upgrading process.
                if (pendingVersion != 0x0) {
                    return false;
                }
                // New version address should be other than 0x0.
                if (_newVersion == 0x0) {
                    return false;
                }
                // Don't apply freeze-time for the initial setup.
                if (latestVersion == 0x0) {
                    latestVersion = _newVersion;
                    return true;
                }
                pendingVersion = _newVersion;
                pendingVersionTimestamp = now;
                UpgradeProposal(_newVersion);
                return true;
            }
        
            /**
             * Cancel the pending upgrade process.
             *
             * Can only be called by current asset owner.
             *
             * @return success.
             */
            function purgeUpgrade() onlyAssetOwner() returns(bool) {
                if (pendingVersion == 0x0) {
                    return false;
                }
                delete pendingVersion;
                delete pendingVersionTimestamp;
                return true;
            }
        
            /**
             * Finalize an upgrade process setting new asset implementation contract address.
             *
             * Can only be called after an upgrade freeze-time.
             *
             * @return success.
             */
            function commitUpgrade() returns(bool) {
                if (pendingVersion == 0x0) {
                    return false;
                }
                if (pendingVersionTimestamp + UPGRADE_FREEZE_TIME > now) {
                    return false;
                }
                latestVersion = pendingVersion;
                delete pendingVersion;
                delete pendingVersionTimestamp;
                return true;
            }
        
            /**
             * Disagree with proposed upgrade, and stick with current asset implementation
             * until further explicit agreement to upgrade.
             *
             * @return success.
             */
            function optOut() returns(bool) {
                if (userOptOutVersion[msg.sender] != 0x0) {
                    return false;
                }
                userOptOutVersion[msg.sender] = latestVersion;
                return true;
            }
        
            /**
             * Implicitly agree to upgrade to current and future asset implementation upgrades,
             * until further explicit disagreement.
             *
             * @return success.
             */
            function optIn() returns(bool) {
                delete userOptOutVersion[msg.sender];
                return true;
            }
        
            // Backwards compatibility.
            function multiAsset() constant returns(EToken2Interface) {
                return etoken2;
            }
        }

        File 2 of 2: EToken2
        // This software is a subject to Ambisafe License Agreement.
        // No use or distribution is allowed without written permission from Ambisafe.
        // https://ambisafe.com/terms.pdf
        
        pragma solidity 0.4.8;
        
        contract Ambi2 {
            function claimFor(address _address, address _owner) returns(bool);
            function hasRole(address _from, bytes32 _role, address _to) constant returns(bool);
            function isOwner(address _node, address _owner) constant returns(bool);
        }
        
        contract Ambi2Enabled {
            Ambi2 ambi2;
        
            modifier onlyRole(bytes32 _role) {
                if (address(ambi2) != 0x0 && ambi2.hasRole(this, _role, msg.sender)) {
                    _;
                }
            }
        
            // Perform only after claiming the node, or claim in the same tx.
            function setupAmbi2(Ambi2 _ambi2) returns(bool) {
                if (address(ambi2) != 0x0) {
                    return false;
                }
        
                ambi2 = _ambi2;
                return true;
            }
        }
        
        contract Ambi2EnabledFull is Ambi2Enabled {
            // Setup and claim atomically.
            function setupAmbi2(Ambi2 _ambi2) returns(bool) {
                if (address(ambi2) != 0x0) {
                    return false;
                }
                if (!_ambi2.claimFor(this, msg.sender) && !_ambi2.isOwner(this, msg.sender)) {
                    return false;
                }
        
                ambi2 = _ambi2;
                return true;
            }
        }
        
        contract RegistryICAPInterface {
            function parse(bytes32 _icap) constant returns(address, bytes32, bool);
            function institutions(bytes32 _institution) constant returns(address);
        }
        
        contract Cosigner {
            function consumeOperation(bytes32 _opHash, uint _required) returns(bool);
        }
        
        contract Emitter {
            function emitTransfer(address _from, address _to, bytes32 _symbol, uint _value, string _reference);
            function emitTransferToICAP(address _from, address _to, bytes32 _icap, uint _value, string _reference);
            function emitIssue(bytes32 _symbol, uint _value, address _by);
            function emitRevoke(bytes32 _symbol, uint _value, address _by);
            function emitOwnershipChange(address _from, address _to, bytes32 _symbol);
            function emitApprove(address _from, address _spender, bytes32 _symbol, uint _value);
            function emitRecovery(address _from, address _to, address _by);
            function emitError(bytes32 _message);
            function emitChange(bytes32 _symbol);
        }
        
        contract Proxy {
            function emitTransfer(address _from, address _to, uint _value);
            function emitApprove(address _from, address _spender, uint _value);
        }
        
        /**
         * @title EToken2.
         *
         * The official Ambisafe assets platform powering all kinds of tokens.
         * EToken2 uses EventsHistory contract to keep events, so that in case it needs to be redeployed
         * at some point, all the events keep appearing at the same place.
         *
         * Every asset is meant to be used through a proxy contract. Only one proxy contract have access
         * rights for a particular asset.
         *
         * Features: assets issuance, transfers, allowances, supply adjustments, lost wallet access recovery.
         *           cosignature check, ICAP.
         *
         * Note: all the non constant functions return false instead of throwing in case if state change
         * didn't happen yet.
         */
        contract EToken2 is Ambi2EnabledFull {
            mapping(bytes32 => bool) switches;
        
            function isEnabled(bytes32 _switch) constant returns(bool) {
                return switches[_switch];
            }
        
            function enableSwitch(bytes32 _switch) onlyRole('issuance') returns(bool) {
                switches[_switch] = true;
                return true;
            }
        
            modifier checkEnabledSwitch(bytes32 _switch) {
                if (!isEnabled(_switch)) {
                    _error('Feature is disabled');
                } else {
                    _;
                }
            }
        
            enum Features { Issue, TransferWithReference, Revoke, ChangeOwnership, Allowances, ICAP }
        
            // Structure of a particular asset.
            struct Asset {
                uint owner;                       // Asset's owner id.
                uint totalSupply;                 // Asset's total supply.
                string name;                      // Asset's name, for information purposes.
                string description;               // Asset's description, for information purposes.
                bool isReissuable;                // Indicates if asset have dynamic of fixed supply.
                uint8 baseUnit;                   // Proposed number of decimals.
                bool isLocked;                    // Are changes still allowed.
                mapping(uint => Wallet) wallets;  // Holders wallets.
            }
        
            // Structure of an asset holder wallet for particular asset.
            struct Wallet {
                uint balance;
                mapping(uint => uint) allowance;
            }
        
            // Structure of an asset holder.
            struct Holder {
                address addr;                    // Current address of the holder.
                Cosigner cosigner;               // Cosigner contract for 2FA and recovery.
                mapping(address => bool) trust;  // Addresses that are trusted with recovery proocedure.
            }
        
            // Iterable mapping pattern is used for holders.
            uint public holdersCount;
            mapping(uint => Holder) public holders;
        
            // This is an access address mapping. Many addresses may have access to a single holder.
            mapping(address => uint) holderIndex;
        
            // Asset symbol to asset mapping.
            mapping(bytes32 => Asset) public assets;
        
            // Asset symbol to asset proxy mapping.
            mapping(bytes32 => address) public proxies;
        
            // ICAP registry contract.
            RegistryICAPInterface public registryICAP;
        
            // Should use interface of the emitter, but address of events history.
            Emitter public eventsHistory;
        
            /**
             * Emits Error event with specified error message.
             *
             * Should only be used if no state changes happened.
             *
             * @param _message error message.
             */
            function _error(bytes32 _message) internal {
                eventsHistory.emitError(_message);
            }
        
            /**
             * Sets EventsHstory contract address.
             *
             * Can be set only once, and only by contract owner.
             *
             * @param _eventsHistory EventsHistory contract address.
             *
             * @return success.
             */
            function setupEventsHistory(Emitter _eventsHistory) onlyRole('setup') returns(bool) {
                if (address(eventsHistory) != 0) {
                    return false;
                }
                eventsHistory = _eventsHistory;
                return true;
            }
        
            /**
             * Sets RegistryICAP contract address.
             *
             * Can be set only once, and only by contract owner.
             *
             * @param _registryICAP RegistryICAP contract address.
             *
             * @return success.
             */
            function setupRegistryICAP(RegistryICAPInterface _registryICAP) onlyRole('setup') returns(bool) {
                if (address(registryICAP) != 0) {
                    return false;
                }
                registryICAP = _registryICAP;
                return true;
            }
        
            /**
             * Emits Error if called not by asset owner.
             */
            modifier onlyOwner(bytes32 _symbol) {
                if (_isSignedOwner(_symbol)) {
                    _;
                } else {
                    _error('Only owner: access denied');
                }
            }
        
            /**
             * Emits Error if called not by asset proxy.
             */
            modifier onlyProxy(bytes32 _symbol) {
                if (_isProxy(_symbol)) {
                    _;
                } else {
                    _error('Only proxy: access denied');
                }
            }
        
            /**
             * Emits Error if _from doesn't trust _to.
             */
            modifier checkTrust(address _from, address _to) {
                if (isTrusted(_from, _to)) {
                    _;
                } else {
                    _error('Only trusted: access denied');
                }
            }
        
            function _isSignedOwner(bytes32 _symbol) internal checkSigned(getHolderId(msg.sender), 1) returns(bool) {
                return isOwner(msg.sender, _symbol);
            }
        
            /**
             * Check asset existance.
             *
             * @param _symbol asset symbol.
             *
             * @return asset existance.
             */
            function isCreated(bytes32 _symbol) constant returns(bool) {
                return assets[_symbol].owner != 0;
            }
        
            function isLocked(bytes32 _symbol) constant returns(bool) {
                return assets[_symbol].isLocked;
            }
        
            /**
             * Returns asset decimals.
             *
             * @param _symbol asset symbol.
             *
             * @return asset decimals.
             */
            function baseUnit(bytes32 _symbol) constant returns(uint8) {
                return assets[_symbol].baseUnit;
            }
        
            /**
             * Returns asset name.
             *
             * @param _symbol asset symbol.
             *
             * @return asset name.
             */
            function name(bytes32 _symbol) constant returns(string) {
                return assets[_symbol].name;
            }
        
            /**
             * Returns asset description.
             *
             * @param _symbol asset symbol.
             *
             * @return asset description.
             */
            function description(bytes32 _symbol) constant returns(string) {
                return assets[_symbol].description;
            }
        
            /**
             * Returns asset reissuability.
             *
             * @param _symbol asset symbol.
             *
             * @return asset reissuability.
             */
            function isReissuable(bytes32 _symbol) constant returns(bool) {
                return assets[_symbol].isReissuable;
            }
        
            /**
             * Returns asset owner address.
             *
             * @param _symbol asset symbol.
             *
             * @return asset owner address.
             */
            function owner(bytes32 _symbol) constant returns(address) {
                return holders[assets[_symbol].owner].addr;
            }
        
            /**
             * Check if specified address has asset owner rights.
             *
             * @param _owner address to check.
             * @param _symbol asset symbol.
             *
             * @return owner rights availability.
             */
            function isOwner(address _owner, bytes32 _symbol) constant returns(bool) {
                return isCreated(_symbol) && (assets[_symbol].owner == getHolderId(_owner));
            }
        
            /**
             * Returns asset total supply.
             *
             * @param _symbol asset symbol.
             *
             * @return asset total supply.
             */
            function totalSupply(bytes32 _symbol) constant returns(uint) {
                return assets[_symbol].totalSupply;
            }
        
            /**
             * Returns asset balance for current address of a particular holder.
             *
             * @param _holder holder address.
             * @param _symbol asset symbol.
             *
             * @return holder balance.
             */
            function balanceOf(address _holder, bytes32 _symbol) constant returns(uint) {
                uint holderId = getHolderId(_holder);
                return holders[holderId].addr == _holder ? _balanceOf(holderId, _symbol) : 0;
            }
        
            /**
             * Returns asset balance for a particular holder id.
             *
             * @param _holderId holder id.
             * @param _symbol asset symbol.
             *
             * @return holder balance.
             */
            function _balanceOf(uint _holderId, bytes32 _symbol) constant internal returns(uint) {
                return assets[_symbol].wallets[_holderId].balance;
            }
        
            /**
             * Returns current address for a particular holder id.
             *
             * @param _holderId holder id.
             *
             * @return holder address.
             */
            function _address(uint _holderId) constant internal returns(address) {
                return holders[_holderId].addr;
            }
        
            function _isProxy(bytes32 _symbol) constant internal returns(bool) {
                return proxies[_symbol] == msg.sender;
            }
        
            /**
             * Sets Proxy contract address for a particular asset.
             *
             * Can be set only once for each asset, and only by contract owner.
             *
             * @param _address Proxy contract address.
             * @param _symbol asset symbol.
             *
             * @return success.
             */
            function setProxy(address _address, bytes32 _symbol) onlyOwner(_symbol) returns(bool) {
                if (proxies[_symbol] != 0x0 && assets[_symbol].isLocked) {
                    return false;
                }
                proxies[_symbol] = _address;
                return true;
            }
        
            /**
             * Transfers asset balance between holders wallets.
             *
             * @param _fromId holder id to take from.
             * @param _toId holder id to give to.
             * @param _value amount to transfer.
             * @param _symbol asset symbol.
             */
            function _transferDirect(uint _fromId, uint _toId, uint _value, bytes32 _symbol) internal {
                assets[_symbol].wallets[_fromId].balance -= _value;
                assets[_symbol].wallets[_toId].balance += _value;
            }
        
            /**
             * Transfers asset balance between holders wallets.
             *
             * Performs sanity checks and takes care of allowances adjustment.
             *
             * @param _fromId holder id to take from.
             * @param _toId holder id to give to.
             * @param _value amount to transfer.
             * @param _symbol asset symbol.
             * @param _reference transfer comment to be included in a Transfer event.
             * @param _senderId transfer initiator holder id.
             *
             * @return success.
             */
            function _transfer(uint _fromId, uint _toId, uint _value, bytes32 _symbol, string _reference, uint _senderId) internal checkSigned(_senderId, 1) returns(bool) {
                // Should not allow to send to oneself.
                if (_fromId == _toId) {
                    _error('Cannot send to oneself');
                    return false;
                }
                // Should have positive value.
                if (_value == 0) {
                    _error('Cannot send 0 value');
                    return false;
                }
                // Should have enough balance.
                if (_balanceOf(_fromId, _symbol) < _value) {
                    _error('Insufficient balance');
                    return false;
                }
                // Should allow references.
                if (bytes(_reference).length > 0 && !isEnabled(sha3(_symbol, Features.TransferWithReference))) {
                    _error('References feature is disabled');
                    return false;
                }
                // Should have enough allowance.
                if (_fromId != _senderId && _allowance(_fromId, _senderId, _symbol) < _value) {
                    _error('Not enough allowance');
                    return false;
                }
                // Adjust allowance.
                if (_fromId != _senderId) {
                    assets[_symbol].wallets[_fromId].allowance[_senderId] -= _value;
                }
                _transferDirect(_fromId, _toId, _value, _symbol);
                // Internal Out Of Gas/Throw: revert this transaction too;
                // Recursive Call: safe, all changes already made.
                eventsHistory.emitTransfer(_address(_fromId), _address(_toId), _symbol, _value, _reference);
                _proxyTransferEvent(_fromId, _toId, _value, _symbol);
                return true;
            }
        
            // Feature and proxy checks done internally due to unknown symbol when the function is called.
            function _transferToICAP(uint _fromId, bytes32 _icap, uint _value, string _reference, uint _senderId) internal returns(bool) {
                var (to, symbol, success) = registryICAP.parse(_icap);
                if (!success) {
                    _error('ICAP is not registered');
                    return false;
                }
                if (!isEnabled(sha3(symbol, Features.ICAP))) {
                    _error('ICAP feature is disabled');
                    return false;
                }
                if (!_isProxy(symbol)) {
                    _error('Only proxy: access denied');
                    return false;
                }
                uint toId = _createHolderId(to);
                if (!_transfer(_fromId, toId, _value, symbol, _reference, _senderId)) {
                    return false;
                }
                // Internal Out Of Gas/Throw: revert this transaction too;
                // Recursive Call: safe, all changes already made.
                eventsHistory.emitTransferToICAP(_address(_fromId), _address(toId), _icap, _value, _reference);
                return true;
            }
        
            function proxyTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool) {
                return _transferToICAP(getHolderId(_from), _icap, _value, _reference, getHolderId(_sender));
            }
        
            /**
             * Ask asset Proxy contract to emit ERC20 compliant Transfer event.
             *
             * @param _fromId holder id to take from.
             * @param _toId holder id to give to.
             * @param _value amount to transfer.
             * @param _symbol asset symbol.
             */
            function _proxyTransferEvent(uint _fromId, uint _toId, uint _value, bytes32 _symbol) internal {
                if (proxies[_symbol] != 0x0) {
                    // Internal Out Of Gas/Throw: revert this transaction too;
                    // Recursive Call: safe, all changes already made.
                    Proxy(proxies[_symbol]).emitTransfer(_address(_fromId), _address(_toId), _value);
                }
            }
        
            /**
             * Returns holder id for the specified address.
             *
             * @param _holder holder address.
             *
             * @return holder id.
             */
            function getHolderId(address _holder) constant returns(uint) {
                return holderIndex[_holder];
            }
        
            /**
             * Returns holder id for the specified address, creates it if needed.
             *
             * @param _holder holder address.
             *
             * @return holder id.
             */
            function _createHolderId(address _holder) internal returns(uint) {
                uint holderId = holderIndex[_holder];
                if (holderId == 0) {
                    holderId = ++holdersCount;
                    holders[holderId].addr = _holder;
                    holderIndex[_holder] = holderId;
                }
                return holderId;
            }
        
            /**
             * Issues new asset token on the platform.
             *
             * Tokens issued with this call go straight to contract owner.
             * Each symbol can be issued only once, and only by contract owner.
             *
             * _isReissuable is included in checkEnabledSwitch because it should be
             * explicitly allowed before issuing new asset.
             *
             * @param _symbol asset symbol.
             * @param _value amount of tokens to issue immediately.
             * @param _name name of the asset.
             * @param _description description for the asset.
             * @param _baseUnit number of decimals.
             * @param _isReissuable dynamic or fixed supply.
             *
             * @return success.
             */
            function issueAsset(bytes32 _symbol, uint _value, string _name, string _description, uint8 _baseUnit, bool _isReissuable) checkEnabledSwitch(sha3(_symbol, _isReissuable, Features.Issue)) returns(bool) {
                // Should have positive value if supply is going to be fixed.
                if (_value == 0 && !_isReissuable) {
                    _error('Cannot issue 0 value fixed asset');
                    return false;
                }
                // Should not be issued yet.
                if (isCreated(_symbol)) {
                    _error('Asset already issued');
                    return false;
                }
                uint holderId = _createHolderId(msg.sender);
        
                assets[_symbol] = Asset(holderId, _value, _name, _description, _isReissuable, _baseUnit, false);
                assets[_symbol].wallets[holderId].balance = _value;
                // Internal Out Of Gas/Throw: revert this transaction too;
                // Recursive Call: safe, all changes already made.
                eventsHistory.emitIssue(_symbol, _value, _address(holderId));
                return true;
            }
        
            function changeAsset(bytes32 _symbol, string _name, string _description, uint8 _baseUnit) onlyOwner(_symbol) returns(bool) {
                if (isLocked(_symbol)) {
                    _error('Asset is locked');
                    return false;
                }
                assets[_symbol].name = _name;
                assets[_symbol].description = _description;
                assets[_symbol].baseUnit = _baseUnit;
                eventsHistory.emitChange(_symbol);
                return true;
            }
        
            function lockAsset(bytes32 _symbol) onlyOwner(_symbol) returns(bool) {
                if (isLocked(_symbol)) {
                    _error('Asset is locked');
                    return false;
                }
                assets[_symbol].isLocked = true;
                return true;
            }
        
            /**
             * Issues additional asset tokens if the asset have dynamic supply.
             *
             * Tokens issued with this call go straight to asset owner.
             * Can only be called by asset owner.
             *
             * @param _symbol asset symbol.
             * @param _value amount of additional tokens to issue.
             *
             * @return success.
             */
            function reissueAsset(bytes32 _symbol, uint _value) onlyOwner(_symbol) returns(bool) {
                // Should have positive value.
                if (_value == 0) {
                    _error('Cannot reissue 0 value');
                    return false;
                }
                Asset asset = assets[_symbol];
                // Should have dynamic supply.
                if (!asset.isReissuable) {
                    _error('Cannot reissue fixed asset');
                    return false;
                }
                // Resulting total supply should not overflow.
                if (asset.totalSupply + _value < asset.totalSupply) {
                    _error('Total supply overflow');
                    return false;
                }
                uint holderId = getHolderId(msg.sender);
                asset.wallets[holderId].balance += _value;
                asset.totalSupply += _value;
                // Internal Out Of Gas/Throw: revert this transaction too;
                // Recursive Call: safe, all changes already made.
                eventsHistory.emitIssue(_symbol, _value, _address(holderId));
                _proxyTransferEvent(0, holderId, _value, _symbol);
                return true;
            }
        
            /**
             * Destroys specified amount of senders asset tokens.
             *
             * @param _symbol asset symbol.
             * @param _value amount of tokens to destroy.
             *
             * @return success.
             */
            function revokeAsset(bytes32 _symbol, uint _value) checkEnabledSwitch(sha3(_symbol, Features.Revoke)) checkSigned(getHolderId(msg.sender), 1) returns(bool) {
                // Should have positive value.
                if (_value == 0) {
                    _error('Cannot revoke 0 value');
                    return false;
                }
                Asset asset = assets[_symbol];
                uint holderId = getHolderId(msg.sender);
                // Should have enough tokens.
                if (asset.wallets[holderId].balance < _value) {
                    _error('Not enough tokens to revoke');
                    return false;
                }
                asset.wallets[holderId].balance -= _value;
                asset.totalSupply -= _value;
                // Internal Out Of Gas/Throw: revert this transaction too;
                // Recursive Call: safe, all changes already made.
                eventsHistory.emitRevoke(_symbol, _value, _address(holderId));
                _proxyTransferEvent(holderId, 0, _value, _symbol);
                return true;
            }
        
            /**
             * Passes asset ownership to specified address.
             *
             * Only ownership is changed, balances are not touched.
             * Can only be called by asset owner.
             *
             * @param _symbol asset symbol.
             * @param _newOwner address to become a new owner.
             *
             * @return success.
             */
            function changeOwnership(bytes32 _symbol, address _newOwner) checkEnabledSwitch(sha3(_symbol, Features.ChangeOwnership)) onlyOwner(_symbol) returns(bool) {
                Asset asset = assets[_symbol];
                uint newOwnerId = _createHolderId(_newOwner);
                // Should pass ownership to another holder.
                if (asset.owner == newOwnerId) {
                    _error('Cannot pass ownership to oneself');
                    return false;
                }
                address oldOwner = _address(asset.owner);
                asset.owner = newOwnerId;
                // Internal Out Of Gas/Throw: revert this transaction too;
                // Recursive Call: safe, all changes already made.
                eventsHistory.emitOwnershipChange(oldOwner, _address(newOwnerId), _symbol);
                return true;
            }
        
            function setCosignerAddress(Cosigner _cosigner) checkSigned(_createHolderId(msg.sender), 1) returns(bool) {
                if (!_checkSigned(_cosigner, getHolderId(msg.sender), 1)) {
                    _error('Invalid cosigner');
                    return false;
                }
                holders[_createHolderId(msg.sender)].cosigner = _cosigner;
                return true;
            }
        
            function isCosignerSet(uint _holderId) constant returns(bool) {
                return address(holders[_holderId].cosigner) != 0x0;
            }
        
            function _checkSigned(Cosigner _cosigner, uint _holderId, uint _required) internal returns(bool) {
                return _cosigner.consumeOperation(sha3(msg.data, _holderId), _required);
            }
        
            modifier checkSigned(uint _holderId, uint _required) {
                if (!isCosignerSet(_holderId) || _checkSigned(holders[_holderId].cosigner, _holderId, _required)) {
                    _;
                } else {
                    _error('Cosigner: access denied');
                }
            }
        
            /**
             * Check if specified holder trusts an address with recovery procedure.
             *
             * @param _from truster.
             * @param _to trustee.
             *
             * @return trust existance.
             */
            function isTrusted(address _from, address _to) constant returns(bool) {
                return holders[getHolderId(_from)].trust[_to];
            }
        
            /**
             * Trust an address to perform recovery procedure for the caller.
             *
             * @param _to trustee.
             *
             * @return success.
             */
            function trust(address _to) returns(bool) {
                uint fromId = _createHolderId(msg.sender);
                // Should trust to another address.
                if (fromId == getHolderId(_to)) {
                    _error('Cannot trust to oneself');
                    return false;
                }
                // Should trust to yet untrusted.
                if (isTrusted(msg.sender, _to)) {
                    _error('Already trusted');
                    return false;
                }
                holders[fromId].trust[_to] = true;
                return true;
            }
        
            /**
             * Revoke trust to perform recovery procedure from an address.
             *
             * @param _to trustee.
             *
             * @return success.
             */
            function distrust(address _to) checkTrust(msg.sender, _to) returns(bool) {
                holders[getHolderId(msg.sender)].trust[_to] = false;
                return true;
            }
        
            /**
             * Perform recovery procedure.
             *
             * This function logic is actually more of an grantAccess(uint _holderId, address _to).
             * It grants another address access to recovery subject wallets.
             * Can only be called by trustee of recovery subject.
             * If cosigning is enabled, should have atleast 2 confirmations.
             *
             * @dev Deprecated. Backward compatibility.
             *
             * @param _from holder address to recover from.
             * @param _to address to grant access to.
             *
             * @return success.
             */
            function recover(address _from, address _to) checkTrust(_from, msg.sender) returns(bool) {
                return _grantAccess(getHolderId(_from), _to);
            }
        
            /**
             * Perform recovery procedure.
             *
             * This function logic is actually more of an grantAccess(uint _holderId, address _to).
             * It grants another address access to subject holder wallets.
             * Can only be called if pre-confirmed by atleast 2 cosign oracles.
             *
             * @param _from holder address to recover from.
             * @param _to address to grant access to.
             *
             * @return success.
             */
            function grantAccess(address _from, address _to) returns(bool) {
                if (!isCosignerSet(getHolderId(_from))) {
                    _error('Cosigner not set');
                    return false;
                }
                return _grantAccess(getHolderId(_from), _to);
            }
        
            function _grantAccess(uint _fromId, address _to) internal checkSigned(_fromId, 2) returns(bool) {
                // Should recover to previously unused address.
                if (getHolderId(_to) != 0) {
                    _error('Should recover to new address');
                    return false;
                }
                // We take current holder address because it might not equal _from.
                // It is possible to recover from any old holder address, but event should have the current one.
                address from = holders[_fromId].addr;
                holders[_fromId].addr = _to;
                holderIndex[_to] = _fromId;
                // Internal Out Of Gas/Throw: revert this transaction too;
                // Recursive Call: safe, all changes already made.
                eventsHistory.emitRecovery(from, _to, msg.sender);
                return true;
            }
        
            /**
             * Sets asset spending allowance for a specified spender.
             *
             * Note: to revoke allowance, one needs to set allowance to 0.
             *
             * @param _spenderId holder id to set allowance for.
             * @param _value amount to allow.
             * @param _symbol asset symbol.
             * @param _senderId approve initiator holder id.
             *
             * @return success.
             */
            function _approve(uint _spenderId, uint _value, bytes32 _symbol, uint _senderId) internal checkEnabledSwitch(sha3(_symbol, Features.Allowances)) checkSigned(_senderId, 1) returns(bool) {
                // Asset should exist.
                if (!isCreated(_symbol)) {
                    _error('Asset is not issued');
                    return false;
                }
                // Should allow to another holder.
                if (_senderId == _spenderId) {
                    _error('Cannot approve to oneself');
                    return false;
                }
                assets[_symbol].wallets[_senderId].allowance[_spenderId] = _value;
                // Internal Out Of Gas/Throw: revert this transaction too;
                // Recursive Call: safe, all changes already made.
                eventsHistory.emitApprove(_address(_senderId), _address(_spenderId), _symbol, _value);
                if (proxies[_symbol] != 0x0) {
                    // Internal Out Of Gas/Throw: revert this transaction too;
                    // Recursive Call: safe, all changes already made.
                    Proxy(proxies[_symbol]).emitApprove(_address(_senderId), _address(_spenderId), _value);
                }
                return true;
            }
        
            /**
             * Sets asset spending allowance for a specified spender.
             *
             * Can only be called by asset proxy.
             *
             * @param _spender holder address to set allowance to.
             * @param _value amount to allow.
             * @param _symbol asset symbol.
             * @param _sender approve initiator address.
             *
             * @return success.
             */
            function proxyApprove(address _spender, uint _value, bytes32 _symbol, address _sender) onlyProxy(_symbol) returns(bool) {
                return _approve(_createHolderId(_spender), _value, _symbol, _createHolderId(_sender));
            }
        
            /**
             * Returns asset allowance from one holder to another.
             *
             * @param _from holder that allowed spending.
             * @param _spender holder that is allowed to spend.
             * @param _symbol asset symbol.
             *
             * @return holder to spender allowance.
             */
            function allowance(address _from, address _spender, bytes32 _symbol) constant returns(uint) {
                return _allowance(getHolderId(_from), getHolderId(_spender), _symbol);
            }
        
            /**
             * Returns asset allowance from one holder to another.
             *
             * @param _fromId holder id that allowed spending.
             * @param _toId holder id that is allowed to spend.
             * @param _symbol asset symbol.
             *
             * @return holder to spender allowance.
             */
            function _allowance(uint _fromId, uint _toId, bytes32 _symbol) constant internal returns(uint) {
                return assets[_symbol].wallets[_fromId].allowance[_toId];
            }
        
            /**
             * Prforms allowance transfer of asset balance between holders wallets.
             *
             * Can only be called by asset proxy.
             *
             * @param _from holder address to take from.
             * @param _to holder address to give to.
             * @param _value amount to transfer.
             * @param _symbol asset symbol.
             * @param _reference transfer comment to be included in a Transfer event.
             * @param _sender allowance transfer initiator address.
             *
             * @return success.
             */
            function proxyTransferFromWithReference(address _from, address _to, uint _value, bytes32 _symbol, string _reference, address _sender) onlyProxy(_symbol) returns(bool) {
                return _transfer(getHolderId(_from), _createHolderId(_to), _value, _symbol, _reference, getHolderId(_sender));
            }
        }