Transaction Hash:
Block:
7353971 at Mar-12-2019 11:18:30 AM +UTC
Transaction Fee:
0.000367962 ETH
$0.92
Gas Used:
52,566 Gas / 7 Gwei
Emitted Events:
52 |
EventsHistory.0x940c4b3549ef0aaff95807dc27f62d88ca15532d1bf535d7d63800f40395d16c( 0x940c4b3549ef0aaff95807dc27f62d88ca15532d1bf535d7d63800f40395d16c, 0x0000000000000000000000009eb87a971beabc2ad91fd67ea2bf8a5347a45531, 0x0000000000000000000000008d12a197cb00d4747a1fe03395095ce2a5cc6819, 0x5441415300000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000000000007e1c1690, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000000002, 0000000000000000000000000000000000000000000000000000000000000000 )
|
53 |
TAAS.Transfer( from=[Sender] 0x9eb87a971beabc2ad91fd67ea2bf8a5347a45531, to=[Receiver] EtherDelta, value=2115770000 )
|
54 |
EtherDelta.Deposit( token=TAAS, user=[Sender] 0x9eb87a971beabc2ad91fd67ea2bf8a5347a45531, amount=2115770000, balance=2219937023 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x331d0775...84c411F84 | |||||
0x8d12A197...2A5CC6819 | (EtherDelta 2) | ||||
0x9eB87a97...347a45531 |
3.504665332548639339 Eth
Nonce: 691
|
3.504297370548639339 Eth
Nonce: 692
| 0.000367962 | ||
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 614.935866630643638061 Eth | 614.936234592643638061 Eth | 0.000367962 |
Execution Trace
EtherDelta.depositToken( token=0xE7775A6e9Bcf904eb39DA2b68c5efb4F9360e08C, amount=2115770000 )
TAAS.transferFrom( _from=0x9eB87a971bEaBC2aD91fd67eA2bF8a5347a45531, _to=0x8d12A197cB00D4747a1fe03395095ce2A5CC6819, _value=2115770000 ) => ( True )
0xd899c145597897bd9f680ba51c91ebf3389c70cc.cca97025( )
TAAS._forwardTransferFromWithReference( _from=0x9eB87a971bEaBC2aD91fd67eA2bF8a5347a45531, _to=0x8d12A197cB00D4747a1fe03395095ce2A5CC6819, _value=2115770000, _reference=, _sender=0x8d12A197cB00D4747a1fe03395095ce2A5CC6819 ) => ( True )
EToken2.proxyTransferFromWithReference( _from=0x9eB87a971bEaBC2aD91fd67eA2bF8a5347a45531, _to=0x8d12A197cB00D4747a1fe03395095ce2A5CC6819, _value=2115770000, _symbol=5441415300000000000000000000000000000000000000000000000000000000, _reference=, _sender=0x8d12A197cB00D4747a1fe03395095ce2A5CC6819 ) => ( True )
EventsHistory.515c1457( )
-
MultiAssetEmitter.emitTransfer( _from=0x9eB87a971bEaBC2aD91fd67eA2bF8a5347a45531, _to=0x8d12A197cB00D4747a1fe03395095ce2A5CC6819, _symbol=5441415300000000000000000000000000000000000000000000000000000000, _value=2115770000, _reference= )
-
-
TAAS.emitTransfer( _from=0x9eB87a971bEaBC2aD91fd67eA2bF8a5347a45531, _to=0x8d12A197cB00D4747a1fe03395095ce2A5CC6819, _value=2115770000 )
depositToken[EtherDelta (ln:226)]
transferFrom[EtherDelta (ln:229)]
safeAdd[EtherDelta (ln:230)]
Deposit[EtherDelta (ln:231)]
File 1 of 5: EtherDelta
File 2 of 5: EventsHistory
File 3 of 5: TAAS
File 4 of 5: EToken2
File 5 of 5: MultiAssetEmitter
pragma solidity ^0.4.9; contract SafeMath { function safeMul(uint a, uint b) internal returns (uint) { uint c = a * b; assert(a == 0 || c / a == b); return c; } function safeSub(uint a, uint b) internal returns (uint) { assert(b <= a); return a - b; } function safeAdd(uint a, uint b) internal returns (uint) { uint c = a + b; assert(c>=a && c>=b); return c; } function assert(bool assertion) internal { if (!assertion) throw; } } contract Token { /// @return total amount of tokens function totalSupply() constant returns (uint256 supply) {} /// @param _owner The address from which the balance will be retrieved /// @return The balance function balanceOf(address _owner) constant returns (uint256 balance) {} /// @notice send `_value` token to `_to` from `msg.sender` /// @param _to The address of the recipient /// @param _value The amount of token to be transferred /// @return Whether the transfer was successful or not function transfer(address _to, uint256 _value) returns (bool success) {} /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` /// @param _from The address of the sender /// @param _to The address of the recipient /// @param _value The amount of token to be transferred /// @return Whether the transfer was successful or not function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {} /// @notice `msg.sender` approves `_addr` to spend `_value` tokens /// @param _spender The address of the account able to transfer the tokens /// @param _value The amount of wei to be approved for transfer /// @return Whether the approval was successful or not function approve(address _spender, uint256 _value) returns (bool success) {} /// @param _owner The address of the account owning tokens /// @param _spender The address of the account able to transfer the tokens /// @return Amount of remaining tokens allowed to spent function allowance(address _owner, address _spender) constant returns (uint256 remaining) {} event Transfer(address indexed _from, address indexed _to, uint256 _value); event Approval(address indexed _owner, address indexed _spender, uint256 _value); uint public decimals; string public name; } contract StandardToken is Token { function transfer(address _to, uint256 _value) returns (bool success) { //Default assumes totalSupply can't be over max (2^256 - 1). //If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap. //Replace the if with this one instead. if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) { //if (balances[msg.sender] >= _value && _value > 0) { balances[msg.sender] -= _value; balances[_to] += _value; Transfer(msg.sender, _to, _value); return true; } else { return false; } } function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { //same as above. Replace this line with the following if you want to protect against wrapping uints. if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) { //if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) { balances[_to] += _value; balances[_from] -= _value; allowed[_from][msg.sender] -= _value; Transfer(_from, _to, _value); return true; } else { return false; } } function balanceOf(address _owner) constant returns (uint256 balance) { return balances[_owner]; } function approve(address _spender, uint256 _value) returns (bool success) { allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } function allowance(address _owner, address _spender) constant returns (uint256 remaining) { return allowed[_owner][_spender]; } mapping(address => uint256) balances; mapping (address => mapping (address => uint256)) allowed; uint256 public totalSupply; } contract ReserveToken is StandardToken, SafeMath { address public minter; function ReserveToken() { minter = msg.sender; } function create(address account, uint amount) { if (msg.sender != minter) throw; balances[account] = safeAdd(balances[account], amount); totalSupply = safeAdd(totalSupply, amount); } function destroy(address account, uint amount) { if (msg.sender != minter) throw; if (balances[account] < amount) throw; balances[account] = safeSub(balances[account], amount); totalSupply = safeSub(totalSupply, amount); } } contract AccountLevels { //given a user, returns an account level //0 = regular user (pays take fee and make fee) //1 = market maker silver (pays take fee, no make fee, gets rebate) //2 = market maker gold (pays take fee, no make fee, gets entire counterparty's take fee as rebate) function accountLevel(address user) constant returns(uint) {} } contract AccountLevelsTest is AccountLevels { mapping (address => uint) public accountLevels; function setAccountLevel(address user, uint level) { accountLevels[user] = level; } function accountLevel(address user) constant returns(uint) { return accountLevels[user]; } } contract EtherDelta is SafeMath { address public admin; //the admin address address public feeAccount; //the account that will receive fees address public accountLevelsAddr; //the address of the AccountLevels contract uint public feeMake; //percentage times (1 ether) uint public feeTake; //percentage times (1 ether) uint public feeRebate; //percentage times (1 ether) mapping (address => mapping (address => uint)) public tokens; //mapping of token addresses to mapping of account balances (token=0 means Ether) mapping (address => mapping (bytes32 => bool)) public orders; //mapping of user accounts to mapping of order hashes to booleans (true = submitted by user, equivalent to offchain signature) mapping (address => mapping (bytes32 => uint)) public orderFills; //mapping of user accounts to mapping of order hashes to uints (amount of order that has been filled) event Order(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user); event Cancel(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s); event Trade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, address get, address give); event Deposit(address token, address user, uint amount, uint balance); event Withdraw(address token, address user, uint amount, uint balance); function EtherDelta(address admin_, address feeAccount_, address accountLevelsAddr_, uint feeMake_, uint feeTake_, uint feeRebate_) { admin = admin_; feeAccount = feeAccount_; accountLevelsAddr = accountLevelsAddr_; feeMake = feeMake_; feeTake = feeTake_; feeRebate = feeRebate_; } function() { throw; } function changeAdmin(address admin_) { if (msg.sender != admin) throw; admin = admin_; } function changeAccountLevelsAddr(address accountLevelsAddr_) { if (msg.sender != admin) throw; accountLevelsAddr = accountLevelsAddr_; } function changeFeeAccount(address feeAccount_) { if (msg.sender != admin) throw; feeAccount = feeAccount_; } function changeFeeMake(uint feeMake_) { if (msg.sender != admin) throw; if (feeMake_ > feeMake) throw; feeMake = feeMake_; } function changeFeeTake(uint feeTake_) { if (msg.sender != admin) throw; if (feeTake_ > feeTake || feeTake_ < feeRebate) throw; feeTake = feeTake_; } function changeFeeRebate(uint feeRebate_) { if (msg.sender != admin) throw; if (feeRebate_ < feeRebate || feeRebate_ > feeTake) throw; feeRebate = feeRebate_; } function deposit() payable { tokens[0][msg.sender] = safeAdd(tokens[0][msg.sender], msg.value); Deposit(0, msg.sender, msg.value, tokens[0][msg.sender]); } function withdraw(uint amount) { if (tokens[0][msg.sender] < amount) throw; tokens[0][msg.sender] = safeSub(tokens[0][msg.sender], amount); if (!msg.sender.call.value(amount)()) throw; Withdraw(0, msg.sender, amount, tokens[0][msg.sender]); } function depositToken(address token, uint amount) { //remember to call Token(address).approve(this, amount) or this contract will not be able to do the transfer on your behalf. if (token==0) throw; if (!Token(token).transferFrom(msg.sender, this, amount)) throw; tokens[token][msg.sender] = safeAdd(tokens[token][msg.sender], amount); Deposit(token, msg.sender, amount, tokens[token][msg.sender]); } function withdrawToken(address token, uint amount) { if (token==0) throw; if (tokens[token][msg.sender] < amount) throw; tokens[token][msg.sender] = safeSub(tokens[token][msg.sender], amount); if (!Token(token).transfer(msg.sender, amount)) throw; Withdraw(token, msg.sender, amount, tokens[token][msg.sender]); } function balanceOf(address token, address user) constant returns (uint) { return tokens[token][user]; } function order(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce) { bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce); orders[msg.sender][hash] = true; Order(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender); } function trade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s, uint amount) { //amount is in amountGet terms bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce); if (!( (orders[user][hash] || ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash),v,r,s) == user) && block.number <= expires && safeAdd(orderFills[user][hash], amount) <= amountGet )) throw; tradeBalances(tokenGet, amountGet, tokenGive, amountGive, user, amount); orderFills[user][hash] = safeAdd(orderFills[user][hash], amount); Trade(tokenGet, amount, tokenGive, amountGive * amount / amountGet, user, msg.sender); } function tradeBalances(address tokenGet, uint amountGet, address tokenGive, uint amountGive, address user, uint amount) private { uint feeMakeXfer = safeMul(amount, feeMake) / (1 ether); uint feeTakeXfer = safeMul(amount, feeTake) / (1 ether); uint feeRebateXfer = 0; if (accountLevelsAddr != 0x0) { uint accountLevel = AccountLevels(accountLevelsAddr).accountLevel(user); if (accountLevel==1) feeRebateXfer = safeMul(amount, feeRebate) / (1 ether); if (accountLevel==2) feeRebateXfer = feeTakeXfer; } tokens[tokenGet][msg.sender] = safeSub(tokens[tokenGet][msg.sender], safeAdd(amount, feeTakeXfer)); tokens[tokenGet][user] = safeAdd(tokens[tokenGet][user], safeSub(safeAdd(amount, feeRebateXfer), feeMakeXfer)); tokens[tokenGet][feeAccount] = safeAdd(tokens[tokenGet][feeAccount], safeSub(safeAdd(feeMakeXfer, feeTakeXfer), feeRebateXfer)); tokens[tokenGive][user] = safeSub(tokens[tokenGive][user], safeMul(amountGive, amount) / amountGet); tokens[tokenGive][msg.sender] = safeAdd(tokens[tokenGive][msg.sender], safeMul(amountGive, amount) / amountGet); } function testTrade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s, uint amount, address sender) constant returns(bool) { if (!( tokens[tokenGet][sender] >= amount && availableVolume(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, user, v, r, s) >= amount )) return false; return true; } function availableVolume(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s) constant returns(uint) { bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce); if (!( (orders[user][hash] || ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash),v,r,s) == user) && block.number <= expires )) return 0; uint available1 = safeSub(amountGet, orderFills[user][hash]); uint available2 = safeMul(tokens[tokenGive][user], amountGet) / amountGive; if (available1<available2) return available1; return available2; } function amountFilled(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s) constant returns(uint) { bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce); return orderFills[user][hash]; } function cancelOrder(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, uint8 v, bytes32 r, bytes32 s) { bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce); if (!(orders[msg.sender][hash] || ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash),v,r,s) == msg.sender)) throw; orderFills[msg.sender][hash] = amountGet; Cancel(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender, v, r, s); } }
File 2 of 5: 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 5: TAAS
pragma solidity 0.4.8; contract EToken2 { function baseUnit(bytes32 _symbol) constant returns(uint8); function name(bytes32 _symbol) constant returns(string); 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 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 Asset { 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 ERC20 { 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 decimals() constant returns(uint8); } 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); } 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 TAAS is ERC20, AssetProxyInterface, Bytes32 { // Assigned EToken2, immutable. EToken2 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(EToken2 _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(Asset) { return Asset(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(EToken2) { return etoken2; } }
File 4 of 5: 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 5 of 5: 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); } }