Transaction Hash:
Block:
12075304 at Mar-20-2021 11:01:56 AM +UTC
Transaction Fee:
0.0099585612 ETH
$18.55
Gas Used:
83,196 Gas / 119.7 Gwei
Emitted Events:
201 |
EventsHistory.0x940c4b3549ef0aaff95807dc27f62d88ca15532d1bf535d7d63800f40395d16c( 0x940c4b3549ef0aaff95807dc27f62d88ca15532d1bf535d7d63800f40395d16c, 0x0000000000000000000000000d0707963952f2fba59dd06f2b425ace40b492fe, 0x00000000000000000000000011a885a7258628f976d484eba9ecb5fff855bbe2, 0x5246520000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000b910c08a, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000000002, 0000000000000000000000000000000000000000000000000000000000000000 )
|
202 |
Refereum.Transfer( from=[Sender] 0x0d0707963952f2fba59dd06f2b425ace40b492fe, to=0x11a885a7258628f976d484eBa9eCb5FFf855BbE2, value=3104882826 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x0D070796...e40b492Fe | (Gate.io 1) |
6,526.044880085518995226 Eth
Nonce: 2389915
|
6,526.034921524318995226 Eth
Nonce: 2389916
| 0.0099585612 | |
0x331d0775...84c411F84 | |||||
0x5A0b54D5...D3E029c4c
Miner
| (Spark Pool) | 5.184534867145152735 Eth | 5.194493428345152735 Eth | 0.0099585612 |
Execution Trace
Refereum.transfer( _to=0x11a885a7258628f976d484eBa9eCb5FFf855BbE2, _value=3104882826 ) => ( True )

0xdfb0acd77b0a9538d2d1b05399a1f254a541076f.1962df71( )
-
0x79f8818d98e6f6eb33aa3468091281c2576e969d.f7ebc39a( )
Refereum._forwardTransferFromWithReference( _from=0x0D0707963952f2fBA59dD06f2b425ace40b492Fe, _to=0x11a885a7258628f976d484eBa9eCb5FFf855BbE2, _value=3104882826, _reference=, _sender=0x0D0707963952f2fBA59dD06f2b425ace40b492Fe ) => ( True )
EToken2.proxyTransferFromWithReference( _from=0x0D0707963952f2fBA59dD06f2b425ace40b492Fe, _to=0x11a885a7258628f976d484eBa9eCb5FFf855BbE2, _value=3104882826, _symbol=5246520000000000000000000000000000000000000000000000000000000000, _reference=, _sender=0x0D0707963952f2fBA59dD06f2b425ace40b492Fe ) => ( True )
EventsHistory.515c1457( )
MultiAssetEmitter.emitTransfer( _from=0x0D0707963952f2fBA59dD06f2b425ace40b492Fe, _to=0x11a885a7258628f976d484eBa9eCb5FFf855BbE2, _symbol=5246520000000000000000000000000000000000000000000000000000000000, _value=3104882826, _reference= )
-
EventsHistory.versions( 0x331d077518216c07C87f4f18bA64cd384c411F84 ) => ( 2 )
-
Null: 0x000...004.CALL( )
-
-
Refereum.emitTransfer( _from=0x0D0707963952f2fBA59dD06f2b425ace40b492Fe, _to=0x11a885a7258628f976d484eBa9eCb5FFf855BbE2, _value=3104882826 )
-
transfer[Refereum (ln:211)]
transferWithReference[Refereum (ln:212)]
_performTransferWithReference[Refereum (ln:227)]
_getAsset[Refereum (ln:227)]
getVersionFor[Refereum (ln:155)]
File 1 of 4: Refereum
File 2 of 4: EventsHistory
File 3 of 4: EToken2
File 4 of 4: MultiAssetEmitter
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 4: EventsHistory
// 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 contract Ambi { function getNodeAddress(bytes32 _nodeName) constant returns(address); function hasRelation(bytes32 _nodeName, bytes32 _relation, address _to) constant returns(bool); function addNode(bytes32 _nodeName, address _nodeAddress) constant returns(bool); } contract AmbiEnabled { Ambi public ambiC; bool public isImmortal; bytes32 public name; modifier checkAccess(bytes32 _role) { if(address(ambiC) != 0x0 && ambiC.hasRelation(name, _role, msg.sender)){ _ } } function getAddress(bytes32 _name) constant returns (address) { return ambiC.getNodeAddress(_name); } function setAmbiAddress(address _ambi, bytes32 _name) returns (bool){ if(address(ambiC) != 0x0){ return false; } Ambi ambiContract = Ambi(_ambi); if(ambiContract.getNodeAddress(_name)!=address(this)) { if (!ambiContract.addNode(_name, address(this))){ return false; } } name = _name; ambiC = ambiContract; return true; } function immortality() checkAccess("owner") returns(bool) { isImmortal = true; return true; } function remove() checkAccess("owner") returns(bool) { if (isImmortal) { return false; } selfdestruct(msg.sender); return true; } } library StackDepthLib { // This will probably work with a value of 390 but no need to cut it // that close in the case that the optimizer changes slightly or // something causing that number to rise slightly. uint constant GAS_PER_DEPTH = 400; function checkDepth(address self, uint n) constant returns(bool) { if (n == 0) return true; return self.call.gas(GAS_PER_DEPTH * n)(0x21835af6, n - 1); } function __dig(uint n) constant { if (n == 0) return; if (!address(this).delegatecall(0x21835af6, n - 1)) throw; } } contract Safe { // Should always be placed as first modifier! modifier noValue { if (msg.value > 0) { // Internal Out Of Gas/Throw: revert this transaction too; // Call Stack Depth Limit reached: revert this transaction too; // Recursive Call: safe, no any changes applied yet, we are inside of modifier. _safeSend(msg.sender, msg.value); } _ } modifier onlyHuman { if (_isHuman()) { _ } } modifier noCallback { if (!isCall) { _ } } modifier immutable(address _address) { if (_address == 0) { _ } } address stackDepthLib; function setupStackDepthLib(address _stackDepthLib) immutable(address(stackDepthLib)) returns(bool) { stackDepthLib = _stackDepthLib; return true; } modifier requireStackDepth(uint16 _depth) { if (stackDepthLib == 0x0) { throw; } if (_depth > 1023) { throw; } if (!stackDepthLib.delegatecall(0x32921690, stackDepthLib, _depth)) { throw; } _ } // Must not be used inside the functions that have noValue() modifier! function _safeFalse() internal noValue() returns(bool) { return false; } function _safeSend(address _to, uint _value) internal { if (!_unsafeSend(_to, _value)) { throw; } } function _unsafeSend(address _to, uint _value) internal returns(bool) { return _to.call.value(_value)(); } function _isContract() constant internal returns(bool) { return msg.sender != tx.origin; } function _isHuman() constant internal returns(bool) { return !_isContract(); } bool private isCall = false; function _setupNoCallback() internal { isCall = true; } function _finishNoCallback() internal { isCall = false; } } /** * @title Events History universal contract. * * Contract serves as an Events storage and version history for a particular contract type. * Events appear on this contract address but their definitions provided by other contracts/libraries. * Version info is provided for historical and informational purposes. * * Note: all the non constant functions return false instead of throwing in case if state change * didn't happen yet. */ contract EventsHistory is AmbiEnabled, Safe { // Event emitter signature to address with Event definiton mapping. mapping(bytes4 => address) public emitters; // Calling contract address to version mapping. mapping(address => uint) public versions; // Version to info mapping. mapping(uint => VersionInfo) public versionInfo; // Latest verion number. uint public latestVersion; struct VersionInfo { uint block; // Block number in which version has been introduced. address by; // Contract owner address who added version. address caller; // Address of this version calling contract. string name; // Version name, informative. string changelog; // Version changelog, informative. } /** * Assign emitter address to a specified emit function signature. * * Can be set only once for each signature, and only by contract owner. * Caller contract should be sure that emitter for a particular signature will never change. * * @param _eventSignature signature of the event emitting function. * @param _emitter address with Event definition. * * @return success. */ function addEmitter(bytes4 _eventSignature, address _emitter) noValue() checkAccess("admin") returns(bool) { if (emitters[_eventSignature] != 0x0) { return false; } emitters[_eventSignature] = _emitter; return true; } /** * Introduce new caller contract version specifing version information. * * Can be set only once for each caller, and only by contract owner. * Name and changelog should not be empty. * * @param _caller address of the new caller. * @param _name version name. * @param _changelog version changelog. * * @return success. */ function addVersion(address _caller, string _name, string _changelog) noValue() checkAccess("admin") returns(bool) { if (versions[_caller] != 0) { return false; } if (bytes(_name).length == 0) { return false; } if (bytes(_changelog).length == 0) { return false; } uint version = ++latestVersion; versions[_caller] = version; versionInfo[version] = VersionInfo(block.number, msg.sender, _caller, _name, _changelog); return true; } /** * Event emitting fallback. * * Can be and only called caller with assigned version. * Resolves msg.sig to an emitter address, and calls it to emit an event. * * Throws if emit function signature is not registered, or call failed. */ function () noValue() { if (versions[msg.sender] == 0) { return; } // Internal Out Of Gas/Throw: revert this transaction too; // Call Stack Depth Limit reached: revert this transaction too; // Recursive Call: safe, all changes already made. if (!emitters[msg.sig].delegatecall(msg.data)) { throw; } } }
File 3 of 4: 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)); } }
File 4 of 4: MultiAssetEmitter
// 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 contract EventsHistory { function versions(address) constant returns(uint); } /** * @title MultiAsset Emitter. * * Contains all the original event emitting function definitions and events. * In case of new events needed later, additional emitters can be developed. * All the functions is meant to be called using delegatecall. */ library MultiAssetEmitter { event Transfer(address indexed from, address indexed to, bytes32 indexed symbol, uint value, string reference, uint version); event Issue(bytes32 indexed symbol, uint value, address by, uint version); event Revoke(bytes32 indexed symbol, uint value, address by, uint version); event OwnershipChange(address indexed from, address indexed to, bytes32 indexed symbol, uint version); event Approve(address indexed from, address indexed spender, bytes32 indexed symbol, uint value, uint version); event Recovery(address indexed from, address indexed to, address by, uint version); event TransferToICAP(address indexed from, address indexed to, bytes32 indexed icap, uint value, string reference, uint version); event Error(bytes32 message, uint version); function emitTransfer(address _from, address _to, bytes32 _symbol, uint _value, string _reference) { Transfer(_from, _to, _symbol, _value, _reference, _getVersion()); } function emitIssue(bytes32 _symbol, uint _value, address _by) { Issue(_symbol, _value, _by, _getVersion()); } function emitRevoke(bytes32 _symbol, uint _value, address _by) { Revoke(_symbol, _value, _by, _getVersion()); } function emitOwnershipChange(address _from, address _to, bytes32 _symbol) { OwnershipChange(_from, _to, _symbol, _getVersion()); } function emitApprove(address _from, address _spender, bytes32 _symbol, uint _value) { Approve(_from, _spender, _symbol, _value, _getVersion()); } function emitRecovery(address _from, address _to, address _by) { Recovery(_from, _to, _by, _getVersion()); } function emitTransferToICAP(address _from, address _to, bytes32 _icap, uint _value, string _reference) { TransferToICAP(_from, _to, _icap, _value, _reference, _getVersion()); } function emitError(bytes32 _message) { Error(_message, _getVersion()); } /** * Get version number of the caller. * * Assuming that the call is made by EventsHistory using delegate call, * context was not changed, so the caller is the address that called * EventsHistory. * * @return current context caller version number. */ function _getVersion() constant internal returns(uint) { return EventsHistory(address(this)).versions(msg.sender); } }