Transaction Hash:
Block:
6973817 at Dec-29-2018 12:15:18 PM +UTC
Transaction Fee:
0.000816 ETH
$1.69
Gas Used:
81,600 Gas / 10 Gwei
Emitted Events:
| 18 |
TokenStore.Trade( tokenGet=0x00000000...000000000, amountGet=153742500000000000, tokenGive=0xC16b542f...c3e357cA6, amountGive=75, get=0x5966639a...69E830487, give=[Sender] 0x799532eb9a98b0fa784c4be91cc68009dcfbeadd, nonce=338582788 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x1cE7AE55...ee6Ee33D8 | (Token.Store) | ||||
|
0x52bc44d5...b7d7bE3b5
Miner
| (Nanopool) | 8,637.876097638567100022 Eth | 8,637.876913638567100022 Eth | 0.000816 | |
| 0x799532EB...9dcFBeaDD |
0.1076282848 Eth
Nonce: 221
|
0.1068122848 Eth
Nonce: 222
| 0.000816 |
Execution Trace
TokenStore.trade( _tokenGet=0x0000000000000000000000000000000000000000, _amountGet=2049900000000000000, _tokenGive=0xC16b542ff490e01fcc0DC58a60e1EFdc3e357cA6, _amountGive=1000, _expires=6974672, _nonce=338582788, _user=0x5966639aAD8AD3f97ad6bE15eFBFBeb69E830487, _v=27, _r=AD4F53E409C474816745592C5E8BEC12E2ECA66183819396AE571B52E51A85E9, _s=537697D94343ED06036F521FE4EB1CA10E202BCF8CBF5E0E33B6A6938E35061B, _amount=153742500000000000 )
-
Null: 0x000...002.1ce7ae55( )
-
Null: 0x000...001.9cca1528( ) -
AccountModifiers.tradeModifiers( _maker=0x5966639aAD8AD3f97ad6bE15eFBFBeb69E830487, _taker=0x799532EB9A98B0fa784c4BE91CC68009dcFBeaDD ) => ( 0, 0 )
trade[TokenStore (ln:215)]
sha256[TokenStore (ln:217)]ecrecover[TokenStore (ln:219)]sha3[TokenStore (ln:219)]safeAdd[TokenStore (ln:221)]revert[TokenStore (ln:222)]tradeBalances[TokenStore (ln:224)]safeMul[TokenStore (ln:232)]safeMul[TokenStore (ln:234)]tradeModifiers[TokenStore (ln:238)]safeMul[TokenStore (ln:246)]safeMul[TokenStore (ln:247)]safeAdd[TokenStore (ln:250)]safeAdd[TokenStore (ln:250)]safeSub[TokenStore (ln:251)]safeAdd[TokenStore (ln:251)]safeSub[TokenStore (ln:252)]safeAdd[TokenStore (ln:253)]safeAdd[TokenStore (ln:254)]safeSub[TokenStore (ln:254)]tradeComplete[TokenStore (ln:257)]
safeAdd[TokenStore (ln:225)]Trade[TokenStore (ln:226)]
File 1 of 2: TokenStore
File 2 of 2: AccountModifiers
pragma solidity ^0.4.11;
// ERC20 token protocol, see more details at
// https://theethereum.wiki/w/index.php/ERC20_Token_Standard
// And also https://github.com/ethereum/eips/issues/20
contract Token {
function totalSupply() constant returns (uint256 supply);
function balanceOf(address _owner) constant returns (uint256 balance);
function transfer(address _to, uint256 _value) returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
function approve(address _spender, uint256 _value) returns (bool success);
function allowance(address _owner, address _spender) constant returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
// Safe mathematics to make the code more readable
contract SafeMath {
function safeMul(uint a, uint b) internal returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function safeSub(uint a, uint b) internal returns (uint) {
assert(b <= a);
return a - b;
}
function safeAdd(uint a, uint b) internal returns (uint) {
uint c = a + b;
assert(c>=a && c>=b);
return c;
}
}
// Ownable interface to simplify owner checks
contract Ownable {
address public owner;
function Ownable() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address _newOwner) onlyOwner {
require(_newOwner != address(0));
owner = _newOwner;
}
}
// Interface for trading discounts and rebates for specific accounts
contract AccountModifiersInterface {
function accountModifiers(address _user) constant returns(uint takeFeeDiscount, uint rebatePercentage);
function tradeModifiers(address _maker, address _taker) constant returns(uint takeFeeDiscount, uint rebatePercentage);
}
// Interface for trade tacker
contract TradeTrackerInterface {
function tradeComplete(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, address _get, address _give, uint _takerFee, uint _makerRebate);
}
// Exchange contract
contract TokenStore is SafeMath, Ownable {
// The account that will receive fees
address feeAccount;
// The account that stores fee discounts/rebates
address accountModifiers;
// Trade tracker account
address tradeTracker;
// We charge only the takers and this is the fee, percentage times 1 ether
uint public fee;
// Mapping of token addresses to mapping of account balances (token 0 means Ether)
mapping (address => mapping (address => uint)) public tokens;
// Mapping of user accounts to mapping of order hashes to uints (amount of order that has been filled)
mapping (address => mapping (bytes32 => uint)) public orderFills;
// Address of a next and previous versions of the contract, also status of the contract
// can be used for user-triggered fund migrations
address public successor;
address public predecessor;
bool public deprecated;
uint16 public version;
// Logging events
// Note: Order creation is handled off-chain, see explanation further below
event Cancel(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s);
event Trade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, address get, address give, uint nonce);
event Deposit(address token, address user, uint amount, uint balance);
event Withdraw(address token, address user, uint amount, uint balance);
event FundsMigrated(address user);
function TokenStore(uint _fee, address _predecessor) {
feeAccount = owner;
fee = _fee;
predecessor = _predecessor;
deprecated = false;
if (predecessor != address(0)) {
version = TokenStore(predecessor).version() + 1;
} else {
version = 1;
}
}
// Throw on default handler to prevent direct transactions of Ether
function() {
revert();
}
modifier deprecable() {
require(!deprecated);
_;
}
function deprecate(bool _deprecated, address _successor) onlyOwner {
deprecated = _deprecated;
successor = _successor;
}
function changeFeeAccount(address _feeAccount) onlyOwner {
require(_feeAccount != address(0));
feeAccount = _feeAccount;
}
function changeAccountModifiers(address _accountModifiers) onlyOwner {
accountModifiers = _accountModifiers;
}
function changeTradeTracker(address _tradeTracker) onlyOwner {
tradeTracker = _tradeTracker;
}
// Fee can only be decreased!
function changeFee(uint _fee) onlyOwner {
require(_fee <= fee);
fee = _fee;
}
// Allows a user to get her current discount/rebate
function getAccountModifiers() constant returns(uint takeFeeDiscount, uint rebatePercentage) {
if (accountModifiers != address(0)) {
return AccountModifiersInterface(accountModifiers).accountModifiers(msg.sender);
} else {
return (0, 0);
}
}
////////////////////////////////////////////////////////////////////////////////
// Deposits, withdrawals, balances
////////////////////////////////////////////////////////////////////////////////
function deposit() payable deprecable {
tokens[0][msg.sender] = safeAdd(tokens[0][msg.sender], msg.value);
Deposit(0, msg.sender, msg.value, tokens[0][msg.sender]);
}
function withdraw(uint _amount) {
require(tokens[0][msg.sender] >= _amount);
tokens[0][msg.sender] = safeSub(tokens[0][msg.sender], _amount);
if (!msg.sender.call.value(_amount)()) {
revert();
}
Withdraw(0, msg.sender, _amount, tokens[0][msg.sender]);
}
function depositToken(address _token, uint _amount) deprecable {
// Note that Token(_token).approve(this, _amount) needs to be called
// first or this contract will not be able to do the transfer.
require(_token != 0);
if (!Token(_token).transferFrom(msg.sender, this, _amount)) {
revert();
}
tokens[_token][msg.sender] = safeAdd(tokens[_token][msg.sender], _amount);
Deposit(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
function withdrawToken(address _token, uint _amount) {
require(_token != 0);
require(tokens[_token][msg.sender] >= _amount);
tokens[_token][msg.sender] = safeSub(tokens[_token][msg.sender], _amount);
if (!Token(_token).transfer(msg.sender, _amount)) {
revert();
}
Withdraw(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
function balanceOf(address _token, address _user) constant returns (uint) {
return tokens[_token][_user];
}
////////////////////////////////////////////////////////////////////////////////
// Trading
////////////////////////////////////////////////////////////////////////////////
// Note: Order creation happens off-chain but the orders are signed by creators,
// we validate the contents and the creator address in the logic below
function trade(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive,
uint _expires, uint _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s, uint _amount) {
bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce);
// Check order signatures and expiration, also check if not fulfilled yet
if (ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash), _v, _r, _s) != _user ||
block.number > _expires ||
safeAdd(orderFills[_user][hash], _amount) > _amountGet) {
revert();
}
tradeBalances(_tokenGet, _amountGet, _tokenGive, _amountGive, _user, msg.sender, _amount);
orderFills[_user][hash] = safeAdd(orderFills[_user][hash], _amount);
Trade(_tokenGet, _amount, _tokenGive, _amountGive * _amount / _amountGet, _user, msg.sender, _nonce);
}
function tradeBalances(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive,
address _user, address _caller, uint _amount) private {
uint feeTakeValue = safeMul(_amount, fee) / (1 ether);
uint rebateValue = 0;
uint tokenGiveValue = safeMul(_amountGive, _amount) / _amountGet; // Proportionate to request ratio
// Apply modifiers
if (accountModifiers != address(0)) {
var (feeTakeDiscount, rebatePercentage) = AccountModifiersInterface(accountModifiers).tradeModifiers(_user, _caller);
// Check that the discounts/rebates are never higher then 100%
if (feeTakeDiscount > 100) {
feeTakeDiscount = 0;
}
if (rebatePercentage > 100) {
rebatePercentage = 0;
}
feeTakeValue = safeMul(feeTakeValue, 100 - feeTakeDiscount) / 100; // discounted fee
rebateValue = safeMul(rebatePercentage, feeTakeValue) / 100; // % of actual taker fee
}
tokens[_tokenGet][_user] = safeAdd(tokens[_tokenGet][_user], safeAdd(_amount, rebateValue));
tokens[_tokenGet][_caller] = safeSub(tokens[_tokenGet][_caller], safeAdd(_amount, feeTakeValue));
tokens[_tokenGive][_user] = safeSub(tokens[_tokenGive][_user], tokenGiveValue);
tokens[_tokenGive][_caller] = safeAdd(tokens[_tokenGive][_caller], tokenGiveValue);
tokens[_tokenGet][feeAccount] = safeAdd(tokens[_tokenGet][feeAccount], safeSub(feeTakeValue, rebateValue));
if (tradeTracker != address(0)) {
TradeTrackerInterface(tradeTracker).tradeComplete(_tokenGet, _amount, _tokenGive, tokenGiveValue, _user, _caller, feeTakeValue, rebateValue);
}
}
function testTrade(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires,
uint _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s, uint _amount, address _sender) constant returns(bool) {
if (tokens[_tokenGet][_sender] < _amount ||
availableVolume(_tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce, _user, _v, _r, _s) < _amount) {
return false;
}
return true;
}
function availableVolume(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires,
uint _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s) constant returns(uint) {
bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce);
if (ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash), _v, _r, _s) != _user ||
block.number > _expires) {
return 0;
}
uint available1 = safeSub(_amountGet, orderFills[_user][hash]);
uint available2 = safeMul(tokens[_tokenGive][_user], _amountGet) / _amountGive;
if (available1 < available2) return available1;
return available2;
}
function amountFilled(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires,
uint _nonce, address _user) constant returns(uint) {
bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce);
return orderFills[_user][hash];
}
function cancelOrder(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires,
uint _nonce, uint8 _v, bytes32 _r, bytes32 _s) {
bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce);
if (!(ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash), _v, _r, _s) == msg.sender)) {
revert();
}
orderFills[msg.sender][hash] = _amountGet;
Cancel(_tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce, msg.sender, _v, _r, _s);
}
////////////////////////////////////////////////////////////////////////////////
// Migrations
////////////////////////////////////////////////////////////////////////////////
// User-triggered (!) fund migrations in case contract got updated
// Similar to withdraw but we use a successor account instead
// As we don't store user tokens list on chain, it has to be passed from the outside
function migrateFunds(address[] _tokens) {
// Get the latest successor in the chain
require(successor != address(0));
TokenStore newExchange = TokenStore(successor);
for (uint16 n = 0; n < 20; n++) { // We will look past 20 contracts in the future
address nextSuccessor = newExchange.successor();
if (nextSuccessor == address(this)) { // Circular succession
revert();
}
if (nextSuccessor == address(0)) { // We reached the newest, stop
break;
}
newExchange = TokenStore(nextSuccessor);
}
// Ether
uint etherAmount = tokens[0][msg.sender];
if (etherAmount > 0) {
tokens[0][msg.sender] = 0;
newExchange.depositForUser.value(etherAmount)(msg.sender);
}
// Tokens
for (n = 0; n < _tokens.length; n++) {
address token = _tokens[n];
require(token != address(0)); // 0 = Ether, we handle it above
uint tokenAmount = tokens[token][msg.sender];
if (tokenAmount == 0) {
continue;
}
if (!Token(token).approve(newExchange, tokenAmount)) {
revert();
}
tokens[token][msg.sender] = 0;
newExchange.depositTokenForUser(token, tokenAmount, msg.sender);
}
FundsMigrated(msg.sender);
}
// This is used for migrations only. To be called by previous exchange only,
// user-triggered, on behalf of the user called the migrateFunds method.
// Note that it does exactly the same as depositToken, but as this is called
// by a previous generation of exchange itself, we credit internally not the
// previous exchange, but the user it was called for.
function depositForUser(address _user) payable deprecable {
require(_user != address(0));
require(msg.value > 0);
TokenStore caller = TokenStore(msg.sender);
require(caller.version() > 0); // Make sure it's an exchange account
tokens[0][_user] = safeAdd(tokens[0][_user], msg.value);
}
function depositTokenForUser(address _token, uint _amount, address _user) deprecable {
require(_token != address(0));
require(_user != address(0));
require(_amount > 0);
TokenStore caller = TokenStore(msg.sender);
require(caller.version() > 0); // Make sure it's an exchange account
if (!Token(_token).transferFrom(msg.sender, this, _amount)) {
revert();
}
tokens[_token][_user] = safeAdd(tokens[_token][_user], _amount);
}
}File 2 of 2: AccountModifiers
pragma solidity ^0.4.13;
contract Ownable {
address public owner;
function Ownable() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
contract AccountModifiers is Ownable {
uint defaultTakerFeeDiscount;
uint defaultRebatePercentage;
mapping (address => uint) takerFeeDiscounts; // in % of taker fee (Eg: 100 for 100%)
mapping (address => uint) rebatePercentages; // in % of taker fee charged
function setDefaults(uint _defaultTakerFeeDiscount, uint _defaultRebatePercentage) onlyOwner {
defaultTakerFeeDiscount = _defaultTakerFeeDiscount;
defaultRebatePercentage = _defaultRebatePercentage;
}
function setModifiers(address _user, uint _takeFeeDiscount, uint _rebatePercentage) onlyOwner {
takerFeeDiscounts[_user] = _takeFeeDiscount;
rebatePercentages[_user] = _rebatePercentage;
}
function takerFeeDiscount(address _user) internal constant returns (uint) {
return defaultTakerFeeDiscount > takerFeeDiscounts[_user] ? defaultTakerFeeDiscount : takerFeeDiscounts[_user];
}
function rebatePercentage(address _user) internal constant returns (uint) {
return defaultRebatePercentage > rebatePercentages[_user] ? defaultRebatePercentage : rebatePercentages[_user];
}
function accountModifiers(address _user) constant returns(uint, uint) {
return (takerFeeDiscount(_user), rebatePercentage(_user));
}
function tradeModifiers(address _maker, address _taker) constant returns(uint, uint) {
return (takerFeeDiscount(_taker), rebatePercentage(_maker));
}
}