Tokens

更新 Solidity - 0.5.4 (ERC20 + ERC677)

  • April 2, 2019

我正在嘗試創建一個 ERC20 代幣,它可以將代幣發送到接收方合約,類似於 ERC223 的做法,我決定嘗試 ERC677,但我不明白如何更新此程式碼以匹配 Solidity 0.5>。我在https://github.com/smartcontractkit/LinkToken使用來自 ERC677 倉庫的草稿

當我更新程式碼以匹配 Solidity 0.5.4 時,我收到一個可ERC677Token contract > transferAndCall在 `super.transfer 中找到的應付地址錯誤。我得到的錯誤是:

TypeError: Member "transfer" not found or not visible after argument-dependent lookup in address.
   address(super).transfer(_to, _value);

怎麼了 ?我需要支付 ERC20 餘額嗎?

ERC20 代幣 + ERC677:

pragma solidity ^0.5.4;

library SafeMath {
   function mul(uint256 a, uint256 b) internal pure returns (uint256) {
       if (a == 0) { return 0;}
       uint256 c = a * b;
       require(c / a == b);
       return c;
   }
   function div(uint256 a, uint256 b) internal pure returns (uint256) {
       require(b > 0);
       uint256 c = a / b;
       return c;
   }
   function sub(uint256 a, uint256 b) internal pure returns (uint256) {
       require(b <= a);
       uint256 c = a - b;
       return c;
   }
   function add(uint256 a, uint256 b) internal pure returns (uint256) {
       uint256 c = a + b;
       require(c >= a);
       return c;
   }
   function mod(uint256 a, uint256 b) internal pure returns (uint256) {
       require(b != 0);
       return a % b;
   }
}
contract linkERC20Basic {
 uint256 public totalSupply;
 function balanceOf(address who) public view returns (uint256);
 function transfer(address to, uint256 value) public returns (bool);
 event Transfer(address indexed from, address indexed to, uint256 value);
}
contract linkERC20 is linkERC20Basic {
 function allowance(address owner, address spender) public view returns (uint256);
 function transferFrom(address from, address to, uint256 value) public returns (bool);
 function approve(address spender, uint256 value) public returns (bool);
 event Approval(address indexed owner, address indexed spender, uint256 value);
}
contract ERC677 is linkERC20 {
 function transferAndCall(address payable to, uint value, bytes memory data) public returns (bool success);

 event Transfer(address indexed from, address indexed to, uint value, bytes data);
}
contract ERC677Receiver {
 function onTokenTransfer(address _sender, uint _value, bytes memory _data) public;
}
contract linkBasicToken is linkERC20Basic {
 using SafeMath for uint256;

 mapping(address => uint256) balances;

 /**
 * @dev transfer token for a specified address
 * @param _to The address to transfer to.
 * @param _value The amount to be transferred.
 */
 function transfer(address _to, uint256 _value) public returns (bool) {
   balances[msg.sender] = balances[msg.sender].sub(_value);
   balances[_to] = balances[_to].add(_value);
   emit Transfer(msg.sender, _to, _value);
   return true;
 }

 /**
 * @dev Gets the balance of the specified address.
 * @param _owner The address to query the the balance of. 
 * @return An uint256 representing the amount owned by the passed address.
 */
 function balanceOf(address _owner) public view returns (uint256 balance) {
   return balances[_owner];
 }

}
contract linkStandardToken is linkERC20, linkBasicToken {

 mapping (address => mapping (address => uint256)) allowed;


 /**
  * @dev Transfer tokens from one address to another
  * @param _from address The address which you want to send tokens from
  * @param _to address The address which you want to transfer to
  * @param _value uint256 the amount of tokens to be transferred
  */
 function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
   uint _allowance = allowed[_from][msg.sender];

   // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
   // require (_value <= _allowance);

   balances[_from] = balances[_from].sub(_value);
   balances[_to] = balances[_to].add(_value);
   allowed[_from][msg.sender] = _allowance.sub(_value);
   emit Transfer(_from, _to, _value);
   return true;
 }

 /**
  * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
  * @param _spender The address which will spend the funds.
  * @param _value The amount of tokens to be spent.
  */
 function approve(address _spender, uint256 _value) public returns (bool) {
   allowed[msg.sender][_spender] = _value;
   emit Approval(msg.sender, _spender, _value);
   return true;
 }

 /**
  * @dev Function to check the amount of tokens that an owner allowed to a spender.
  * @param _owner address The address which owns the funds.
  * @param _spender address The address which will spend the funds.
  * @return A uint256 specifying the amount of tokens still available for the spender.
  */
 function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
   return allowed[_owner][_spender];
 }
 
   /*
  * approve should be called when allowed[_spender] == 0. To increment
  * allowed value is better to use this function to avoid 2 calls (and wait until 
  * the first transaction is mined)
  * From MonolithDAO Token.sol
  */
 function increaseApproval (address _spender, uint _addedValue) 
   public returns (bool success) {
   allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
   emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
   return true;
 }

 function decreaseApproval (address _spender, uint _subtractedValue) 
   public returns (bool success) {
   uint oldValue = allowed[msg.sender][_spender];
   if (_subtractedValue > oldValue) {
     allowed[msg.sender][_spender] = 0;
   } else {
     allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
   }
   emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
   return true;
 }

}

contract ERC677Token is ERC677 {

 /**
 * @dev transfer token to a contract address with additional data if the recipient is a contact.
 * @param _to The address to transfer to.
 * @param _value The amount to be transferred.
 * @param _data The extra data to be passed to the receiving contract.
 */
 function transferAndCall(address payable _to, uint _value, bytes memory _data)
   public
   returns (bool success)
 {
   super.transfer(_to, _value);
   Transfer(msg.sender, _to, _value, _data);
   if (isContract(_to)) {
     contractFallback(_to, _value, _data);
   }
   return true;
 }


 // PRIVATE

 function contractFallback(address _to, uint _value, bytes memory _data)
   private
 {
   ERC677Receiver receiver = ERC677Receiver(_to);
   receiver.onTokenTransfer(msg.sender, _value, _data);
 }

 function isContract(address _addr)
   private
   returns (bool hasCode)
 {
   uint length;
   assembly { length := extcodesize(_addr) }
   return length > 0;
 }

}
contract LinkToken is linkStandardToken, ERC677Token {

 uint public constant totalSupply = 10**27;
 string public constant name = 'ChainLink Token';
 uint8 public constant decimals = 18;
 string public constant symbol = 'LINK';

 constructor() public {
   balances[msg.sender] = totalSupply;
 }

 /**
 * @dev transfer token to a specified address with additional data if the recipient is a contract.
 * @param _to The address to transfer to.
 * @param _value The amount to be transferred.
 * @param _data The extra data to be passed to the receiving contract.
 */
 function transferAndCall(address payable _to, uint _value, bytes memory _data)
   public
   validRecipient(_to)
   returns (bool success)
 {
   return super.transferAndCall(_to, _value, _data);
 }

 /**
 * @dev transfer token to a specified address.
 * @param _to The address to transfer to.
 * @param _value The amount to be transferred.
 */
 function transfer(address _to, uint _value)
   public
   validRecipient(_to)
   returns (bool success)
 {
   return super.transfer(_to, _value);
 }

 /**
  * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
  * @param _spender The address which will spend the funds.
  * @param _value The amount of tokens to be spent.
  */
 function approve(address _spender, uint256 _value)
   public
   validRecipient(_spender)
   returns (bool)
 {
   return super.approve(_spender,  _value);
 }

 /**
  * @dev Transfer tokens from one address to another
  * @param _from address The address which you want to send tokens from
  * @param _to address The address which you want to transfer to
  * @param _value uint256 the amount of tokens to be transferred
  */
 function transferFrom(address _from, address _to, uint256 _value)
   public
   validRecipient(_to)
   returns (bool)
 {
   return super.transferFrom(_from, _to, _value);
 }


 // MODIFIERS

 modifier validRecipient(address _recipient) {
   require(_recipient != address(0) && _recipient != address(this));
   _;
 }

}

contract Token677 is linkStandardToken, ERC677Token {
   string public constant name = "Example ERC677 Token";
   string public constant symbol = "ERC677";
   uint8 public constant decimals = 18;
   uint256 public totalSupply;

   constructor(uint _initialBalance) public {
       balances[msg.sender] = _initialBalance;
       totalSupply = _initialBalance;
   }
}

contract Token677ReceiverMock {
   address public tokenSender;
   uint public sentValue;
   bytes public tokenData;
   bool public calledFallback = false;

   function onTokenTransfer(address _sender, uint _value, bytes memory _data)
   public {
     calledFallback = true;

     tokenSender = _sender;
     sentValue = _value;
     tokenData = _data;
   }

}

希望有人知識淵博,知道更安全的方法來解決這個問題。我不想使用標準的 ERC20 + 代理 oracle 在收到事件後進行偵聽和更新。我想使用像 ERC677 或 ERC223 這樣的東西。我願意聽取利弊以及最佳實踐。

先感謝您 !!!

最終實現了 ERC223 :) 謝謝!

pragma solidity ^0.5.2;

/** 
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error.
*/
library SafeMath {
   /**
    * @dev Multiplies two unsigned integers, reverts on overflow.
    */
   function mul(uint256 a, uint256 b) internal pure returns (uint256) {
       // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
       // benefit is lost if 'b' is also tested.
       // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
       if (a == 0) {
           return 0;
       }

       uint256 c = a * b;
       require(c / a == b);

       return c;
   }

   /**
    * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
    */
   function div(uint256 a, uint256 b) internal pure returns (uint256) {
       // Solidity only automatically asserts when dividing by 0
       require(b > 0);
       uint256 c = a / b;
       // assert(a == b * c + a % b); // There is no case in which this doesn't hold

       return c;
   }

   /**
    * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
    */
   function sub(uint256 a, uint256 b) internal pure returns (uint256) {
       require(b <= a);
       uint256 c = a - b;

       return c;
   }

   /**
    * @dev Adds two unsigned integers, reverts on overflow.
    */
   function add(uint256 a, uint256 b) internal pure returns (uint256) {
       uint256 c = a + b;
       require(c >= a);

       return c;
   }

   /**
    * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
    * reverts when dividing by zero.
    */
   function mod(uint256 a, uint256 b) internal pure returns (uint256) {
       require(b != 0);
       return a % b;
   }
}

contract ERC223Interface {
   function totalSupply() external view returns (uint256);
   function balanceOf(address who) external view returns (uint256);
   function transfer(address to, uint256 value) external returns (bool);
   function transfer(address to, uint256 value, bytes calldata data) external returns (bool);
   function approve(address spender, uint256 value) external returns (bool);
   function allowance(address owner, address spender) external view returns (uint256);
   function transferFrom(address from, address to, uint256 value) external returns (bool);
   function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
   function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
   event Transfer(address indexed from, address indexed to, uint256 value);
   event Transfer(address indexed from, address indexed to, uint256 value, bytes data);
   event Approval(address indexed owner, address indexed spender, uint256 value);
}

contract ReentrancyGuard {
   using SafeMath for uint256;
   uint256 private _guardCounter;

   constructor () internal {
       _guardCounter = 1;
   }

   modifier nonReentrant() {
       _guardCounter = _guardCounter.add(1);
       uint256 localCounter = _guardCounter;
       _;
       require(localCounter == _guardCounter);
   }
}

contract ERC223ReceivingContract { 
   /**
    * @dev Standard ERC223 function that will handle incoming token transfers.
    *
    * @param _from  Token sender address.
    * @param _value Amount of tokens.
    * @param _data  Transaction metadata.
    */
   function tokenFallback(address _from, uint256 _value, bytes memory _data) public;
}

contract ERC223Token is ERC223Interface {
   using SafeMath for uint256;

   address private _owner; // variables are not really private unless we are dealing with encrypted data
                           // https://medium.com/swlh/ethereum-aint-hiding-your-secrets-703e89088937

   string  public  constant name = "ERC223";
   string  public  constant symbol = "ERC223";
   uint8   public  constant decimals = 18;
   uint256 private constant _totalSupply = 10000000 * (uint256(10) ** decimals);

   mapping (address => uint256) private _balances;
   mapping (address => mapping (address => uint256)) private _allowed;

   constructor() public {
       _owner = msg.sender;
       _balances[_owner] = _totalSupply;
       emit Transfer(address(0), _owner, _totalSupply);
   }

   function totalSupply() public view returns (uint256) {
       return _totalSupply;
   }

   function balanceOf(address owner) public view returns (uint256 balance) {
       return _balances[owner];
   }

   function transfer(address to, uint256 value) public returns (bool success) {
       require(to != address(0));
       require(value > 0 && balanceOf(msg.sender) >= value);
       require(balanceOf(to).add(value) > balanceOf(to));

       uint256 codeLength;
       bytes memory empty;

       assembly {
           codeLength := extcodesize(to)
       }

       _balances[msg.sender] = _balances[msg.sender].sub(value);
       _balances[to] = _balances[to].add(value);

       if(codeLength>0) {
           ERC223ReceivingContract receiver = ERC223ReceivingContract(to);
           receiver.tokenFallback(msg.sender, value, empty);
       }

       emit Transfer(msg.sender, to, value, empty);
       return true;
   }

   function transfer(address to, uint256 value, bytes memory data) public returns (bool success) {
       require(to != address(0));
       require(value > 0 && balanceOf(msg.sender) >= value);
       require(balanceOf(to).add(value) > balanceOf(to));

       uint256 codeLength;

       assembly {
           codeLength := extcodesize(to)
       }

       _balances[msg.sender] = _balances[msg.sender].sub(value);
       _balances[to] = _balances[to].add(value);

       if(codeLength>0) {
           ERC223ReceivingContract receiver = ERC223ReceivingContract(to);
           receiver.tokenFallback(msg.sender, value, data);
       }

       emit Transfer(msg.sender, to, value, data);
       return true;
   }

   function approve(address spender, uint256 value) public returns (bool success) {
       _allowed[msg.sender][spender] = value;
       emit Approval(msg.sender, spender, value);
       return true;
   }

   function allowance(address owner, address spender) public view returns (uint256) {
       return _allowed[owner][spender];
   }

   function transferFrom(address from, address to, uint256 value) public returns (bool success) {
       require(to != address(0));
       require(value <= _balances[from]);
       require(value <= _allowed[from][msg.sender]);

       _balances[from] = _balances[from].sub(value);
       _balances[to] = _balances[to].add(value);
       _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);
       emit Transfer(from, to, value);
       return true;
   }

   function increaseAllowance(address spender, uint256 addedValue) public returns (bool success) {
       _allowed[msg.sender][spender] = _allowed[msg.sender][spender].add(addedValue);
       emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
       return true;
   }

   function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool success) {
       uint256 oldValue = _allowed[msg.sender][spender];
       if (subtractedValue > oldValue) {
           _allowed[msg.sender][spender] = 0;
       } else {
           _allowed[msg.sender][spender] = oldValue.sub(subtractedValue);
       }
       emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
       return true;
   }

   function unlockERC20Tokens(address tokenAddress, uint256 tokens) public returns (bool success) {
       require(msg.sender == _owner);
       return ERC223Interface(tokenAddress).transfer(_owner, tokens);
   }

   function () external payable {
       revert("This contract does not accept ETH");
   }

}

contract ERC223Contract is ReentrancyGuard {
   using SafeMath for uint256;

   ERC223Interface private token;

   function getBlockNumber() public view returns (uint256) {
       return block.number;
   }

   function getData() public pure returns (bytes memory) {
       return msg.data;
   }

   function getSignature() public pure returns (bytes4) {
       return msg.sig;
   }

   function () external {
     //if ether is sent to this address, send it back.
     revert();
   }

   function tokenFallback(address player, uint tokens, bytes memory data) public nonReentrant {
       emit DepositedERC223Token(player, tokens, data);
   }

   event Created(string, uint);
   event DepositedERC223Token(address from, uint value, bytes data);
}

引用自:https://ethereum.stackexchange.com/questions/68708