Transaction Hash:
Block:
9235570 at Jan-07-2020 08:08:06 PM +UTC
Transaction Fee:
0.000240336 ETH
$0.60
Gas Used:
60,084 Gas / 4 Gwei
Emitted Events:
34 |
OysterPearl.Transfer( _from=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, _to=[Receiver] EtherDelta, _value=1000000000000000000000 )
|
35 |
EtherDelta.Deposit( token=OysterPearl, user=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, amount=1000000000000000000000, balance=1000000000000000000000 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x1844b215...aaBA46ab9 | |||||
0x343a3C7F...F643678d9 |
0.004759477281576316 Eth
Nonce: 295
|
0.004519141281576316 Eth
Nonce: 296
| 0.000240336 | ||
0x8d12A197...2A5CC6819 | (EtherDelta 2) | ||||
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 751.520167357699226259 Eth | 751.520407693699226259 Eth | 0.000240336 |
Execution Trace
EtherDelta.depositToken( token=0x1844b21593262668B7248d0f57a220CaaBA46ab9, amount=1000000000000000000000 )
-
OysterPearl.transferFrom( _from=0x343a3C7F789335C9EA60932D34bE258F643678d9, _to=0x8d12A197cB00D4747a1fe03395095ce2A5CC6819, _value=1000000000000000000000 ) => ( success=True )
depositToken[EtherDelta (ln:226)]
transferFrom[EtherDelta (ln:229)]
safeAdd[EtherDelta (ln:230)]
Deposit[EtherDelta (ln:231)]
File 1 of 2: EtherDelta
File 2 of 2: OysterPearl
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 2: OysterPearl
pragma solidity ^0.4.18; interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; } contract OysterPearl { // Public variables of PRL string public name; string public symbol; uint8 public decimals; uint256 public totalSupply; uint256 public funds; address public director; bool public saleClosed; bool public directorLock; uint256 public claimAmount; uint256 public payAmount; uint256 public feeAmount; uint256 public epoch; uint256 public retentionMax; // Array definitions mapping (address => uint256) public balances; mapping (address => mapping (address => uint256)) public allowance; mapping (address => bool) public buried; mapping (address => uint256) public claimed; // ERC20 event event Transfer(address indexed _from, address indexed _to, uint256 _value); // ERC20 event event Approval(address indexed _owner, address indexed _spender, uint256 _value); // This notifies clients about the amount burnt event Burn(address indexed _from, uint256 _value); // This notifies clients about an address getting buried event Bury(address indexed _target, uint256 _value); // This notifies clients about a claim being made on a buried address event Claim(address indexed _target, address indexed _payout, address indexed _fee); /** * Constructor function * * Initializes contract */ function OysterPearl() public { director = msg.sender; name = "Oyster Pearl"; symbol = "PRL"; decimals = 18; saleClosed = true; directorLock = false; funds = 0; totalSupply = 0; // Marketing share (5%) totalSupply += 25000000 * 10 ** uint256(decimals); // Devfund share (15%) totalSupply += 75000000 * 10 ** uint256(decimals); // Allocation to match PREPRL supply and reservation for discretionary use totalSupply += 8000000 * 10 ** uint256(decimals); // Assign reserved PRL supply to the director balances[director] = totalSupply; // Define default values for Oyster functions claimAmount = 5 * 10 ** (uint256(decimals) - 1); payAmount = 4 * 10 ** (uint256(decimals) - 1); feeAmount = 1 * 10 ** (uint256(decimals) - 1); // Seconds in a year epoch = 31536000; // Maximum time for a sector to remain stored retentionMax = 40 * 10 ** uint256(decimals); } /** * ERC20 balance function */ function balanceOf(address _owner) public constant returns (uint256 balance) { return balances[_owner]; } modifier onlyDirector { // Director can lock themselves out to complete decentralization of Oyster network // An alternative is that another smart contract could become the decentralized director require(!directorLock); // Only the director is permitted require(msg.sender == director); _; } modifier onlyDirectorForce { // Only the director is permitted require(msg.sender == director); _; } /** * Transfers the director to a new address */ function transferDirector(address newDirector) public onlyDirectorForce { director = newDirector; } /** * Withdraw funds from the contract */ function withdrawFunds() public onlyDirectorForce { director.transfer(this.balance); } /** * Permanently lock out the director to decentralize Oyster * Invocation is discretionary because Oyster might be better suited to * transition to an artificially intelligent smart contract director */ function selfLock() public payable onlyDirector { // The sale must be closed before the director gets locked out require(saleClosed); // Prevents accidental lockout require(msg.value == 10 ether); // Permanently lock out the director directorLock = true; } /** * Director can alter the storage-peg and broker fees */ function amendClaim(uint8 claimAmountSet, uint8 payAmountSet, uint8 feeAmountSet, uint8 accuracy) public onlyDirector returns (bool success) { require(claimAmountSet == (payAmountSet + feeAmountSet)); claimAmount = claimAmountSet * 10 ** (uint256(decimals) - accuracy); payAmount = payAmountSet * 10 ** (uint256(decimals) - accuracy); feeAmount = feeAmountSet * 10 ** (uint256(decimals) - accuracy); return true; } /** * Director can alter the epoch time */ function amendEpoch(uint256 epochSet) public onlyDirector returns (bool success) { // Set the epoch epoch = epochSet; return true; } /** * Director can alter the maximum time of storage retention */ function amendRetention(uint8 retentionSet, uint8 accuracy) public onlyDirector returns (bool success) { // Set retentionMax retentionMax = retentionSet * 10 ** (uint256(decimals) - accuracy); return true; } /** * Director can close the crowdsale */ function closeSale() public onlyDirector returns (bool success) { // The sale must be currently open require(!saleClosed); // Lock the crowdsale saleClosed = true; return true; } /** * Director can open the crowdsale */ function openSale() public onlyDirector returns (bool success) { // The sale must be currently closed require(saleClosed); // Unlock the crowdsale saleClosed = false; return true; } /** * Oyster Protocol Function * More information at https://oyster.ws/OysterWhitepaper.pdf * * Bury an address * * When an address is buried; only claimAmount can be withdrawn once per epoch */ function bury() public returns (bool success) { // The address must be previously unburied require(!buried[msg.sender]); // An address must have at least claimAmount to be buried require(balances[msg.sender] >= claimAmount); // Prevent addresses with large balances from getting buried require(balances[msg.sender] <= retentionMax); // Set buried state to true buried[msg.sender] = true; // Set the initial claim clock to 1 claimed[msg.sender] = 1; // Execute an event reflecting the change Bury(msg.sender, balances[msg.sender]); return true; } /** * Oyster Protocol Function * More information at https://oyster.ws/OysterWhitepaper.pdf * * Claim PRL from a buried address * * If a prior claim wasn't made during the current epoch, then claimAmount can be withdrawn * * @param _payout the address of the website owner * @param _fee the address of the broker node */ function claim(address _payout, address _fee) public returns (bool success) { // The claimed address must have already been buried require(buried[msg.sender]); // The payout and fee addresses must be different require(_payout != _fee); // The claimed address cannot pay itself require(msg.sender != _payout); // The claimed address cannot pay itself require(msg.sender != _fee); // It must be either the first time this address is being claimed or atleast epoch in time has passed require(claimed[msg.sender] == 1 || (block.timestamp - claimed[msg.sender]) >= epoch); // Check if the buried address has enough require(balances[msg.sender] >= claimAmount); // Reset the claim clock to the current block time claimed[msg.sender] = block.timestamp; // Save this for an assertion in the future uint256 previousBalances = balances[msg.sender] + balances[_payout] + balances[_fee]; // Remove claimAmount from the buried address balances[msg.sender] -= claimAmount; // Pay the website owner that invoked the web node that found the PRL seed key balances[_payout] += payAmount; // Pay the broker node that unlocked the PRL balances[_fee] += feeAmount; // Execute events to reflect the changes Claim(msg.sender, _payout, _fee); Transfer(msg.sender, _payout, payAmount); Transfer(msg.sender, _fee, feeAmount); // Failsafe logic that should never be false assert(balances[msg.sender] + balances[_payout] + balances[_fee] == previousBalances); return true; } /** * Crowdsale function */ function () public payable { // Check if crowdsale is still active require(!saleClosed); // Minimum amount is 1 finney require(msg.value >= 1 finney); // Price is 1 ETH = 5000 PRL uint256 amount = msg.value * 5000; // totalSupply limit is 500 million PRL require(totalSupply + amount <= (500000000 * 10 ** uint256(decimals))); // Increases the total supply totalSupply += amount; // Adds the amount to the balance balances[msg.sender] += amount; // Track ETH amount raised funds += msg.value; // Execute an event reflecting the change Transfer(this, msg.sender, amount); } /** * Internal transfer, can be called by this contract only */ function _transfer(address _from, address _to, uint _value) internal { // Sending addresses cannot be buried require(!buried[_from]); // If the receiving address is buried, it cannot exceed retentionMax if (buried[_to]) { require(balances[_to] + _value <= retentionMax); } // Prevent transfer to 0x0 address, use burn() instead require(_to != 0x0); // Check if the sender has enough require(balances[_from] >= _value); // Check for overflows require(balances[_to] + _value > balances[_to]); // Save this for an assertion in the future uint256 previousBalances = balances[_from] + balances[_to]; // Subtract from the sender balances[_from] -= _value; // Add the same to the recipient balances[_to] += _value; Transfer(_from, _to, _value); // Failsafe logic that should never be false assert(balances[_from] + balances[_to] == previousBalances); } /** * Transfer tokens * * Send `_value` tokens to `_to` from your account * * @param _to the address of the recipient * @param _value the amount to send */ function transfer(address _to, uint256 _value) public { _transfer(msg.sender, _to, _value); } /** * Transfer tokens from other address * * Send `_value` tokens to `_to` in behalf of `_from` * * @param _from the address of the sender * @param _to the address of the recipient * @param _value the amount to send */ function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { // Check allowance require(_value <= allowance[_from][msg.sender]); allowance[_from][msg.sender] -= _value; _transfer(_from, _to, _value); return true; } /** * Set allowance for other address * * Allows `_spender` to spend no more than `_value` tokens on your behalf * * @param _spender the address authorized to spend * @param _value the max amount they can spend */ function approve(address _spender, uint256 _value) public returns (bool success) { // Buried addresses cannot be approved require(!buried[msg.sender]); allowance[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } /** * Set allowance for other address and notify * * Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it * * @param _spender the address authorized to spend * @param _value the max amount they can spend * @param _extraData some extra information to send to the approved contract */ function approveAndCall(address _spender, uint256 _value, bytes _extraData) public returns (bool success) { tokenRecipient spender = tokenRecipient(_spender); if (approve(_spender, _value)) { spender.receiveApproval(msg.sender, _value, this, _extraData); return true; } } /** * Destroy tokens * * Remove `_value` tokens from the system irreversibly * * @param _value the amount of money to burn */ function burn(uint256 _value) public returns (bool success) { // Buried addresses cannot be burnt require(!buried[msg.sender]); // Check if the sender has enough require(balances[msg.sender] >= _value); // Subtract from the sender balances[msg.sender] -= _value; // Updates totalSupply totalSupply -= _value; Burn(msg.sender, _value); return true; } /** * Destroy tokens from other account * * Remove `_value` tokens from the system irreversibly on behalf of `_from`. * * @param _from the address of the sender * @param _value the amount of money to burn */ function burnFrom(address _from, uint256 _value) public returns (bool success) { // Buried addresses cannot be burnt require(!buried[_from]); // Check if the targeted balance is enough require(balances[_from] >= _value); // Check allowance require(_value <= allowance[_from][msg.sender]); // Subtract from the targeted balance balances[_from] -= _value; // Subtract from the sender's allowance allowance[_from][msg.sender] -= _value; // Update totalSupply totalSupply -= _value; Burn(_from, _value); return true; } }