Tokens
更新 Solidity - 0.5.4 (ERC20 + ERC677)
我正在嘗試創建一個 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); }