Transaction Hash:
Block:
5185778 at Mar-02-2018 11:28:13 PM +UTC
Transaction Fee:
0.000222102 ETH
$0.43
Gas Used:
74,034 Gas / 3 Gwei
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
|
0x829BD824...93333A830
Miner
| (F2Pool Old) | 5,527.454562717053460098 Eth | 5,527.454784819053460098 Eth | 0.000222102 | |
| 0xF357De63...8Fc479E6D |
1.047235033513285769 Eth
Nonce: 12
|
1.047012931513285769 Eth
Nonce: 13
| 0.000222102 |
Execution Trace
Exchange.depositToken( token=0xd0929d411954c47438dc1d871dd6081F5C5e149c, amount=350000000 )
Refereum.transferFrom( _from=0xF357De635C61eFA2049076aBB8155eF8Fc479E6D, _to=0x2a0c0DBEcC7E4D658f48E01e3fA353F44050c208, _value=350000000 ) => ( False )
0xdfb0acd77b0a9538d2d1b05399a1f254a541076f.cca97025( )-
0x79f8818d98e6f6eb33aa3468091281c2576e969d.f7ebc39a( )
Refereum._forwardTransferFromWithReference( _from=0xF357De635C61eFA2049076aBB8155eF8Fc479E6D, _to=0x2a0c0DBEcC7E4D658f48E01e3fA353F44050c208, _value=350000000, _reference=, _sender=0x2a0c0DBEcC7E4D658f48E01e3fA353F44050c208 ) => ( False )
EToken2.proxyTransferFromWithReference( _from=0xF357De635C61eFA2049076aBB8155eF8Fc479E6D, _to=0x2a0c0DBEcC7E4D658f48E01e3fA353F44050c208, _value=350000000, _symbol=5246520000000000000000000000000000000000000000000000000000000000, _reference=, _sender=0x2a0c0DBEcC7E4D658f48E01e3fA353F44050c208 ) => ( False )
EventsHistory.e90459f8( )
-
MultiAssetEmitter.emitError( _message=496E73756666696369656E742062616C616E6365000000000000000000000000 )
-
-
depositToken[Exchange (ln:97)]
safeAdd[Exchange (ln:98)]transferFrom[Exchange (ln:100)]Deposit[Exchange (ln:101)]
File 1 of 5: Exchange
File 2 of 5: Refereum
File 3 of 5: EToken2
File 4 of 5: EventsHistory
File 5 of 5: MultiAssetEmitter
pragma solidity ^0.4.16;
contract Token {
bytes32 public standard;
bytes32 public name;
bytes32 public symbol;
uint256 public totalSupply;
uint8 public decimals;
bool public allowTransactions;
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
function transfer(address _to, uint256 _value) returns (bool success);
function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success);
function approve(address _spender, uint256 _value) returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
}
contract Exchange {
function assert(bool assertion) {
if (!assertion) throw;
}
function safeMul(uint a, uint b) returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function safeSub(uint a, uint b) returns (uint) {
assert(b <= a);
return a - b;
}
function safeAdd(uint a, uint b) returns (uint) {
uint c = a + b;
assert(c>=a && c>=b);
return c;
}
address public owner;
mapping (address => uint256) public invalidOrder;
event SetOwner(address indexed previousOwner, address indexed newOwner);
modifier onlyOwner {
assert(msg.sender == owner);
_;
}
function setOwner(address newOwner) onlyOwner {
SetOwner(owner, newOwner);
owner = newOwner;
}
function getOwner() returns (address out) {
return owner;
}
function invalidateOrdersBefore(address user, uint256 nonce) onlyAdmin {
if (nonce < invalidOrder[user]) throw;
invalidOrder[user] = nonce;
}
mapping (address => mapping (address => uint256)) public tokens; //mapping of token addresses to mapping of account balances
mapping (address => bool) public admins;
mapping (address => uint256) public lastActiveTransaction;
mapping (bytes32 => uint256) public orderFills;
address public feeAccount;
uint256 public inactivityReleasePeriod;
mapping (bytes32 => bool) public traded;
mapping (bytes32 => bool) public withdrawn;
event Order(address tokenBuy, uint256 amountBuy, address tokenSell, uint256 amountSell, uint256 expires, uint256 nonce, address user, uint8 v, bytes32 r, bytes32 s);
event Cancel(address tokenBuy, uint256 amountBuy, address tokenSell, uint256 amountSell, uint256 expires, uint256 nonce, address user, uint8 v, bytes32 r, bytes32 s);
event Trade(address tokenBuy, uint256 amountBuy, address tokenSell, uint256 amountSell, address get, address give);
event Deposit(address token, address user, uint256 amount, uint256 balance);
event Withdraw(address token, address user, uint256 amount, uint256 balance);
function setInactivityReleasePeriod(uint256 expiry) onlyAdmin returns (bool success) {
if (expiry > 1000000) throw;
inactivityReleasePeriod = expiry;
return true;
}
function Exchange(address feeAccount_) {
owner = msg.sender;
feeAccount = feeAccount_;
inactivityReleasePeriod = 100000;
}
function setAdmin(address admin, bool isAdmin) onlyOwner {
admins[admin] = isAdmin;
}
modifier onlyAdmin {
if (msg.sender != owner && !admins[msg.sender]) throw;
_;
}
function() external {
throw;
}
function depositToken(address token, uint256 amount) {
tokens[token][msg.sender] = safeAdd(tokens[token][msg.sender], amount);
lastActiveTransaction[msg.sender] = block.number;
if (!Token(token).transferFrom(msg.sender, this, amount)) throw;
Deposit(token, msg.sender, amount, tokens[token][msg.sender]);
}
function deposit() payable {
tokens[address(0)][msg.sender] = safeAdd(tokens[address(0)][msg.sender], msg.value);
lastActiveTransaction[msg.sender] = block.number;
Deposit(address(0), msg.sender, msg.value, tokens[address(0)][msg.sender]);
}
function withdraw(address token, uint256 amount) returns (bool success) {
if (safeSub(block.number, lastActiveTransaction[msg.sender]) < inactivityReleasePeriod) throw;
if (tokens[token][msg.sender] < amount) throw;
tokens[token][msg.sender] = safeSub(tokens[token][msg.sender], amount);
if (token == address(0)) {
if (!msg.sender.send(amount)) throw;
} else {
if (!Token(token).transfer(msg.sender, amount)) throw;
}
Withdraw(token, msg.sender, amount, tokens[token][msg.sender]);
}
function adminWithdraw(address token, uint256 amount, address user, uint256 nonce, uint8 v, bytes32 r, bytes32 s, uint256 feeWithdrawal) onlyAdmin returns (bool success) {
bytes32 hash = keccak256(this, token, amount, user, nonce);
if (withdrawn[hash]) throw;
withdrawn[hash] = true;
if (ecrecover(keccak256("\x19Ethereum Signed Message:\n32", hash), v, r, s) != user) throw;
if (feeWithdrawal > 50 finney) feeWithdrawal = 50 finney;
if (tokens[token][user] < amount) throw;
tokens[token][user] = safeSub(tokens[token][user], amount);
tokens[token][feeAccount] = safeAdd(tokens[token][feeAccount], safeMul(feeWithdrawal, amount) / 1 ether);
amount = safeMul((1 ether - feeWithdrawal), amount) / 1 ether;
if (token == address(0)) {
if (!user.send(amount)) throw;
} else {
if (!Token(token).transfer(user, amount)) throw;
}
lastActiveTransaction[user] = block.number;
Withdraw(token, user, amount, tokens[token][user]);
}
function balanceOf(address token, address user) constant returns (uint256) {
return tokens[token][user];
}
function trade(uint256[8] tradeValues, address[4] tradeAddresses, uint8[2] v, bytes32[4] rs) onlyAdmin returns (bool success) {
/* amount is in amountBuy terms */
/* tradeValues
[0] amountBuy
[1] amountSell
[2] expires
[3] nonce
[4] amount
[5] tradeNonce
[6] feeMake
[7] feeTake
tradeAddressses
[0] tokenBuy
[1] tokenSell
[2] maker
[3] taker
*/
if (invalidOrder[tradeAddresses[2]] > tradeValues[3]) throw;
bytes32 orderHash = keccak256(this, tradeAddresses[0], tradeValues[0], tradeAddresses[1], tradeValues[1], tradeValues[2], tradeValues[3], tradeAddresses[2]);
if (ecrecover(keccak256("\x19Ethereum Signed Message:\n32", orderHash), v[0], rs[0], rs[1]) != tradeAddresses[2]) throw;
bytes32 tradeHash = keccak256(orderHash, tradeValues[4], tradeAddresses[3], tradeValues[5]);
if (ecrecover(keccak256("\x19Ethereum Signed Message:\n32", tradeHash), v[1], rs[2], rs[3]) != tradeAddresses[3]) throw;
if (traded[tradeHash]) throw;
traded[tradeHash] = true;
if (tradeValues[6] > 100 finney) tradeValues[6] = 100 finney;
if (tradeValues[7] > 100 finney) tradeValues[7] = 100 finney;
if (safeAdd(orderFills[orderHash], tradeValues[4]) > tradeValues[0]) throw;
if (tokens[tradeAddresses[0]][tradeAddresses[3]] < tradeValues[4]) throw;
if (tokens[tradeAddresses[1]][tradeAddresses[2]] < (safeMul(tradeValues[1], tradeValues[4]) / tradeValues[0])) throw;
tokens[tradeAddresses[0]][tradeAddresses[3]] = safeSub(tokens[tradeAddresses[0]][tradeAddresses[3]], tradeValues[4]);
tokens[tradeAddresses[0]][tradeAddresses[2]] = safeAdd(tokens[tradeAddresses[0]][tradeAddresses[2]], safeMul(tradeValues[4], ((1 ether) - tradeValues[6])) / (1 ether));
tokens[tradeAddresses[0]][feeAccount] = safeAdd(tokens[tradeAddresses[0]][feeAccount], safeMul(tradeValues[4], tradeValues[6]) / (1 ether));
tokens[tradeAddresses[1]][tradeAddresses[2]] = safeSub(tokens[tradeAddresses[1]][tradeAddresses[2]], safeMul(tradeValues[1], tradeValues[4]) / tradeValues[0]);
tokens[tradeAddresses[1]][tradeAddresses[3]] = safeAdd(tokens[tradeAddresses[1]][tradeAddresses[3]], safeMul(safeMul(((1 ether) - tradeValues[7]), tradeValues[1]), tradeValues[4]) / tradeValues[0] / (1 ether));
tokens[tradeAddresses[1]][feeAccount] = safeAdd(tokens[tradeAddresses[1]][feeAccount], safeMul(safeMul(tradeValues[7], tradeValues[1]), tradeValues[4]) / tradeValues[0] / (1 ether));
orderFills[orderHash] = safeAdd(orderFills[orderHash], tradeValues[4]);
lastActiveTransaction[tradeAddresses[2]] = block.number;
lastActiveTransaction[tradeAddresses[3]] = block.number;
}
}File 2 of 5: Refereum
pragma solidity 0.4.11;
contract RegistryICAPInterface {
function parse(bytes32 _icap) constant returns(address, bytes32, bool);
function institutions(bytes32 _institution) constant returns(address);
}
contract EToken2Interface {
function registryICAP() constant returns(RegistryICAPInterface);
function baseUnit(bytes32 _symbol) constant returns(uint8);
function description(bytes32 _symbol) constant returns(string);
function owner(bytes32 _symbol) constant returns(address);
function isOwner(address _owner, bytes32 _symbol) constant returns(bool);
function totalSupply(bytes32 _symbol) constant returns(uint);
function balanceOf(address _holder, bytes32 _symbol) constant returns(uint);
function isLocked(bytes32 _symbol) constant returns(bool);
function issueAsset(bytes32 _symbol, uint _value, string _name, string _description, uint8 _baseUnit, bool _isReissuable) returns(bool);
function reissueAsset(bytes32 _symbol, uint _value) returns(bool);
function revokeAsset(bytes32 _symbol, uint _value) returns(bool);
function setProxy(address _address, bytes32 _symbol) returns(bool);
function lockAsset(bytes32 _symbol) returns(bool);
function proxyTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
function proxyApprove(address _spender, uint _value, bytes32 _symbol, address _sender) returns(bool);
function allowance(address _from, address _spender, bytes32 _symbol) constant returns(uint);
function proxyTransferFromWithReference(address _from, address _to, uint _value, bytes32 _symbol, string _reference, address _sender) returns(bool);
}
contract AssetInterface {
function _performTransferWithReference(address _to, uint _value, string _reference, address _sender) returns(bool);
function _performTransferToICAPWithReference(bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
function _performApprove(address _spender, uint _value, address _sender) returns(bool);
function _performTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) returns(bool);
function _performTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
function _performGeneric(bytes _data, address _sender) payable returns(bytes32) {
throw;
}
}
contract ERC20Interface {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed from, address indexed spender, uint256 value);
function totalSupply() constant returns(uint256 supply);
function balanceOf(address _owner) constant returns(uint256 balance);
function transfer(address _to, uint256 _value) returns(bool success);
function transferFrom(address _from, address _to, uint256 _value) returns(bool success);
function approve(address _spender, uint256 _value) returns(bool success);
function allowance(address _owner, address _spender) constant returns(uint256 remaining);
// function symbol() constant returns(string);
function decimals() constant returns(uint8);
// function name() constant returns(string);
}
contract AssetProxyInterface {
function _forwardApprove(address _spender, uint _value, address _sender) returns(bool);
function _forwardTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) returns(bool);
function _forwardTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
function balanceOf(address _owner) constant returns(uint);
}
contract Bytes32 {
function _bytes32(string _input) internal constant returns(bytes32 result) {
assembly {
result := mload(add(_input, 32))
}
}
}
/**
* @title EToken2 Asset Proxy.
*
* Proxy implements ERC20 interface and acts as a gateway to a single EToken2 asset.
* Proxy adds etoken2Symbol and caller(sender) when forwarding requests to EToken2.
* Every request that is made by caller first sent to the specific asset implementation
* contract, which then calls back to be forwarded onto EToken2.
*
* Calls flow: Caller ->
* Proxy.func(...) ->
* Asset._performFunc(..., Caller.address) ->
* Proxy._forwardFunc(..., Caller.address) ->
* Platform.proxyFunc(..., symbol, Caller.address)
*
* Generic call flow: Caller ->
* Proxy.unknownFunc(...) ->
* Asset._performGeneric(..., Caller.address) ->
* Asset.unknownFunc(...)
*
* Asset implementation contract is mutable, but each user have an option to stick with
* old implementation, through explicit decision made in timely manner, if he doesn't agree
* with new rules.
* Each user have a possibility to upgrade to latest asset contract implementation, without the
* possibility to rollback.
*
* Note: all the non constant functions return false instead of throwing in case if state change
* didn't happen yet.
*/
contract Refereum is ERC20Interface, AssetProxyInterface, Bytes32 {
// Assigned EToken2, immutable.
EToken2Interface public etoken2;
// Assigned symbol, immutable.
bytes32 public etoken2Symbol;
// Assigned name, immutable. For UI.
string public name;
string public symbol;
/**
* Sets EToken2 address, assigns symbol and name.
*
* Can be set only once.
*
* @param _etoken2 EToken2 contract address.
* @param _symbol assigned symbol.
* @param _name assigned name.
*
* @return success.
*/
function init(EToken2Interface _etoken2, string _symbol, string _name) returns(bool) {
if (address(etoken2) != 0x0) {
return false;
}
etoken2 = _etoken2;
etoken2Symbol = _bytes32(_symbol);
name = _name;
symbol = _symbol;
return true;
}
/**
* Only EToken2 is allowed to call.
*/
modifier onlyEToken2() {
if (msg.sender == address(etoken2)) {
_;
}
}
/**
* Only current asset owner is allowed to call.
*/
modifier onlyAssetOwner() {
if (etoken2.isOwner(msg.sender, etoken2Symbol)) {
_;
}
}
/**
* Returns asset implementation contract for current caller.
*
* @return asset implementation contract.
*/
function _getAsset() internal returns(AssetInterface) {
return AssetInterface(getVersionFor(msg.sender));
}
function recoverTokens(uint _value) onlyAssetOwner() returns(bool) {
return this.transferWithReference(msg.sender, _value, 'Tokens recovery');
}
/**
* Returns asset total supply.
*
* @return asset total supply.
*/
function totalSupply() constant returns(uint) {
return etoken2.totalSupply(etoken2Symbol);
}
/**
* Returns asset balance for a particular holder.
*
* @param _owner holder address.
*
* @return holder balance.
*/
function balanceOf(address _owner) constant returns(uint) {
return etoken2.balanceOf(_owner, etoken2Symbol);
}
/**
* Returns asset allowance from one holder to another.
*
* @param _from holder that allowed spending.
* @param _spender holder that is allowed to spend.
*
* @return holder to spender allowance.
*/
function allowance(address _from, address _spender) constant returns(uint) {
return etoken2.allowance(_from, _spender, etoken2Symbol);
}
/**
* Returns asset decimals.
*
* @return asset decimals.
*/
function decimals() constant returns(uint8) {
return etoken2.baseUnit(etoken2Symbol);
}
/**
* Transfers asset balance from the caller to specified receiver.
*
* @param _to holder address to give to.
* @param _value amount to transfer.
*
* @return success.
*/
function transfer(address _to, uint _value) returns(bool) {
return transferWithReference(_to, _value, '');
}
/**
* Transfers asset balance from the caller to specified receiver adding specified comment.
* Resolves asset implementation contract for the caller and forwards there arguments along with
* the caller address.
*
* @param _to holder address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
*
* @return success.
*/
function transferWithReference(address _to, uint _value, string _reference) returns(bool) {
return _getAsset()._performTransferWithReference(_to, _value, _reference, msg.sender);
}
/**
* Transfers asset balance from the caller to specified ICAP.
*
* @param _icap recipient ICAP to give to.
* @param _value amount to transfer.
*
* @return success.
*/
function transferToICAP(bytes32 _icap, uint _value) returns(bool) {
return transferToICAPWithReference(_icap, _value, '');
}
/**
* Transfers asset balance from the caller to specified ICAP adding specified comment.
* Resolves asset implementation contract for the caller and forwards there arguments along with
* the caller address.
*
* @param _icap recipient ICAP to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
*
* @return success.
*/
function transferToICAPWithReference(bytes32 _icap, uint _value, string _reference) returns(bool) {
return _getAsset()._performTransferToICAPWithReference(_icap, _value, _reference, msg.sender);
}
/**
* Prforms allowance transfer of asset balance between holders.
*
* @param _from holder address to take from.
* @param _to holder address to give to.
* @param _value amount to transfer.
*
* @return success.
*/
function transferFrom(address _from, address _to, uint _value) returns(bool) {
return transferFromWithReference(_from, _to, _value, '');
}
/**
* Prforms allowance transfer of asset balance between holders adding specified comment.
* Resolves asset implementation contract for the caller and forwards there arguments along with
* the caller address.
*
* @param _from holder address to take from.
* @param _to holder address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
*
* @return success.
*/
function transferFromWithReference(address _from, address _to, uint _value, string _reference) returns(bool) {
return _getAsset()._performTransferFromWithReference(_from, _to, _value, _reference, msg.sender);
}
/**
* Performs transfer call on the EToken2 by the name of specified sender.
*
* Can only be called by asset implementation contract assigned to sender.
*
* @param _from holder address to take from.
* @param _to holder address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
* @param _sender initial caller.
*
* @return success.
*/
function _forwardTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) onlyImplementationFor(_sender) returns(bool) {
return etoken2.proxyTransferFromWithReference(_from, _to, _value, etoken2Symbol, _reference, _sender);
}
/**
* Prforms allowance transfer of asset balance between holders.
*
* @param _from holder address to take from.
* @param _icap recipient ICAP address to give to.
* @param _value amount to transfer.
*
* @return success.
*/
function transferFromToICAP(address _from, bytes32 _icap, uint _value) returns(bool) {
return transferFromToICAPWithReference(_from, _icap, _value, '');
}
/**
* Prforms allowance transfer of asset balance between holders adding specified comment.
* Resolves asset implementation contract for the caller and forwards there arguments along with
* the caller address.
*
* @param _from holder address to take from.
* @param _icap recipient ICAP address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
*
* @return success.
*/
function transferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference) returns(bool) {
return _getAsset()._performTransferFromToICAPWithReference(_from, _icap, _value, _reference, msg.sender);
}
/**
* Performs allowance transfer to ICAP call on the EToken2 by the name of specified sender.
*
* Can only be called by asset implementation contract assigned to sender.
*
* @param _from holder address to take from.
* @param _icap recipient ICAP address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
* @param _sender initial caller.
*
* @return success.
*/
function _forwardTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) onlyImplementationFor(_sender) returns(bool) {
return etoken2.proxyTransferFromToICAPWithReference(_from, _icap, _value, _reference, _sender);
}
/**
* Sets asset spending allowance for a specified spender.
* Resolves asset implementation contract for the caller and forwards there arguments along with
* the caller address.
*
* @param _spender holder address to set allowance to.
* @param _value amount to allow.
*
* @return success.
*/
function approve(address _spender, uint _value) returns(bool) {
return _getAsset()._performApprove(_spender, _value, msg.sender);
}
/**
* Performs allowance setting call on the EToken2 by the name of specified sender.
*
* Can only be called by asset implementation contract assigned to sender.
*
* @param _spender holder address to set allowance to.
* @param _value amount to allow.
* @param _sender initial caller.
*
* @return success.
*/
function _forwardApprove(address _spender, uint _value, address _sender) onlyImplementationFor(_sender) returns(bool) {
return etoken2.proxyApprove(_spender, _value, etoken2Symbol, _sender);
}
/**
* Emits ERC20 Transfer event on this contract.
*
* Can only be, and, called by assigned EToken2 when asset transfer happens.
*/
function emitTransfer(address _from, address _to, uint _value) onlyEToken2() {
Transfer(_from, _to, _value);
}
/**
* Emits ERC20 Approval event on this contract.
*
* Can only be, and, called by assigned EToken2 when asset allowance set happens.
*/
function emitApprove(address _from, address _spender, uint _value) onlyEToken2() {
Approval(_from, _spender, _value);
}
/**
* Resolves asset implementation contract for the caller and forwards there transaction data,
* along with the value. This allows for proxy interface growth.
*/
function () payable {
bytes32 result = _getAsset()._performGeneric.value(msg.value)(msg.data, msg.sender);
assembly {
mstore(0, result)
return(0, 32)
}
}
/**
* Indicates an upgrade freeze-time start, and the next asset implementation contract.
*/
event UpgradeProposal(address newVersion);
// Current asset implementation contract address.
address latestVersion;
// Proposed next asset implementation contract address.
address pendingVersion;
// Upgrade freeze-time start.
uint pendingVersionTimestamp;
// Timespan for users to review the new implementation and make decision.
uint constant UPGRADE_FREEZE_TIME = 3 days;
// Asset implementation contract address that user decided to stick with.
// 0x0 means that user uses latest version.
mapping(address => address) userOptOutVersion;
/**
* Only asset implementation contract assigned to sender is allowed to call.
*/
modifier onlyImplementationFor(address _sender) {
if (getVersionFor(_sender) == msg.sender) {
_;
}
}
/**
* Returns asset implementation contract address assigned to sender.
*
* @param _sender sender address.
*
* @return asset implementation contract address.
*/
function getVersionFor(address _sender) constant returns(address) {
return userOptOutVersion[_sender] == 0 ? latestVersion : userOptOutVersion[_sender];
}
/**
* Returns current asset implementation contract address.
*
* @return asset implementation contract address.
*/
function getLatestVersion() constant returns(address) {
return latestVersion;
}
/**
* Returns proposed next asset implementation contract address.
*
* @return asset implementation contract address.
*/
function getPendingVersion() constant returns(address) {
return pendingVersion;
}
/**
* Returns upgrade freeze-time start.
*
* @return freeze-time start.
*/
function getPendingVersionTimestamp() constant returns(uint) {
return pendingVersionTimestamp;
}
/**
* Propose next asset implementation contract address.
*
* Can only be called by current asset owner.
*
* Note: freeze-time should not be applied for the initial setup.
*
* @param _newVersion asset implementation contract address.
*
* @return success.
*/
function proposeUpgrade(address _newVersion) onlyAssetOwner() returns(bool) {
// Should not already be in the upgrading process.
if (pendingVersion != 0x0) {
return false;
}
// New version address should be other than 0x0.
if (_newVersion == 0x0) {
return false;
}
// Don't apply freeze-time for the initial setup.
if (latestVersion == 0x0) {
latestVersion = _newVersion;
return true;
}
pendingVersion = _newVersion;
pendingVersionTimestamp = now;
UpgradeProposal(_newVersion);
return true;
}
/**
* Cancel the pending upgrade process.
*
* Can only be called by current asset owner.
*
* @return success.
*/
function purgeUpgrade() onlyAssetOwner() returns(bool) {
if (pendingVersion == 0x0) {
return false;
}
delete pendingVersion;
delete pendingVersionTimestamp;
return true;
}
/**
* Finalize an upgrade process setting new asset implementation contract address.
*
* Can only be called after an upgrade freeze-time.
*
* @return success.
*/
function commitUpgrade() returns(bool) {
if (pendingVersion == 0x0) {
return false;
}
if (pendingVersionTimestamp + UPGRADE_FREEZE_TIME > now) {
return false;
}
latestVersion = pendingVersion;
delete pendingVersion;
delete pendingVersionTimestamp;
return true;
}
/**
* Disagree with proposed upgrade, and stick with current asset implementation
* until further explicit agreement to upgrade.
*
* @return success.
*/
function optOut() returns(bool) {
if (userOptOutVersion[msg.sender] != 0x0) {
return false;
}
userOptOutVersion[msg.sender] = latestVersion;
return true;
}
/**
* Implicitly agree to upgrade to current and future asset implementation upgrades,
* until further explicit disagreement.
*
* @return success.
*/
function optIn() returns(bool) {
delete userOptOutVersion[msg.sender];
return true;
}
// Backwards compatibility.
function multiAsset() constant returns(EToken2Interface) {
return etoken2;
}
}File 3 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 4 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 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);
}
}