ETH Price: $2,500.62 (+0.16%)

Transaction Decoder

Block:
8911994 at Nov-11-2019 03:03:53 AM +UTC
Transaction Fee:
0.000605242 ETH $1.51
Gas Used:
55,022 Gas / 11 Gwei

Emitted Events:

71 OysterPearl.Transfer( _from=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, _to=[Receiver] TokenStore, _value=441000000000000000000 )
72 TokenStore.Deposit( token=OysterPearl, user=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, amount=441000000000000000000, balance=441000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x1844b215...aaBA46ab9
0x1cE7AE55...ee6Ee33D8
(Token.Store)
0x343a3C7F...F643678d9
0.009137692281576316 Eth
Nonce: 283
0.008532450281576316 Eth
Nonce: 284
0.000605242
(UUPool)
24.102300667458681475 Eth24.102905909458681475 Eth0.000605242

Execution Trace

TokenStore.depositToken( _token=0x1844b21593262668B7248d0f57a220CaaBA46ab9, _amount=441000000000000000000 )
  • OysterPearl.transferFrom( _from=0x343a3C7F789335C9EA60932D34bE258F643678d9, _to=0x1cE7AE555139c5EF5A57CC8d814a867ee6Ee33D8, _value=441000000000000000000 ) => ( success=True )
    File 1 of 2: TokenStore
    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: 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;
        }
    }