From 42e2e90d561bf51f79a1e446d35ccd7e4d5cfd06 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Thu, 26 Sep 2024 22:22:38 +0100 Subject: [PATCH 01/31] Update erc-7417.md --- ERCS/erc-7417.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 33b8b711af..19c9fef77c 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -15,7 +15,7 @@ requires: 20, 165, 223 There are multiple token standards on Ethereum chain currently. This EIP introduces a concept of cross-standard interoperability by creating a service that allows [ERC-20](./eip-20.md) tokens to be upgraded to [ERC-223](./eip-223.md) tokens anytime. [ERC-223](./eip-223.md) tokens can be converted back to [ERC-20](./eip-20.md) version without any restrictions to avoid any problems with backwards compatibility and allow different standards to co-exist and become interoperable and interchangeable. -To perform the conversion, the user must send tokens of one standard to the Converter contract and he will automatically receive tokens of another standard. +In order to perform the conversion, a user must deposit tokens of one standard to the Converter contract and it will automatically send tokens of another standard back. ## Motivation From 33d76ddcd9185ad6a49bf6403c0e1bdf5433f735 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Thu, 26 Sep 2024 22:38:37 +0100 Subject: [PATCH 02/31] Update reference implementation of the Token Converter --- ERCS/erc-7417.md | 468 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 423 insertions(+), 45 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 19c9fef77c..ed260d1ae2 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -135,8 +135,382 @@ This service is the first of its kind and therefore does not have any backwards ## Reference Implementation -```solidity - address public ownerMultisig; +```solidity// SPDX-License-Identifier: GPL-3.0 + +pragma solidity =0.8.19; + +library Address { + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + // solhint-disable-next-line no-inline-assembly + assembly { size := extcodesize(account) } + return size > 0; + } +} + +interface IERC20 { + function totalSupply() external view returns (uint256); + function balanceOf(address account) external view returns (uint256); + function transfer(address recipient, uint256 amount) external returns (bool); + function allowance(address owner, address spender) external view returns (uint256); + function approve(address spender, uint256 amount) external returns (bool); + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +interface IERC20Metadata is IERC20 { + /// @return The name of the token + function name() external view returns (string memory); + + /// @return The symbol of the token + function symbol() external view returns (string memory); + + /// @return The number of decimal places the token has + function decimals() external view returns (uint8); +} + +abstract contract IERC223Recipient { + function tokenReceived(address _from, uint _value, bytes memory _data) public virtual returns (bytes4) + { + return 0x8943ec02; + } +} + +abstract contract ERC165 { + /* + * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 + */ + bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; + mapping(bytes4 => bool) private _supportedInterfaces; + + constructor () { + // Derived contracts need only register support for their own interfaces, + // we register support for ERC165 itself here + _registerInterface(_INTERFACE_ID_ERC165); + } + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + return _supportedInterfaces[interfaceId]; + } + function _registerInterface(bytes4 interfaceId) internal virtual { + require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); + _supportedInterfaces[interfaceId] = true; + } +} + +abstract contract IERC223 { + function name() public view virtual returns (string memory); + function symbol() public view virtual returns (string memory); + function decimals() public view virtual returns (uint8); + function totalSupply() public view virtual returns (uint256); + function balanceOf(address who) public virtual view returns (uint); + function transfer(address to, uint value) public virtual returns (bool success); + function transfer(address to, uint value, bytes calldata data) public payable virtual returns (bool success); + event Transfer(address indexed from, address indexed to, uint value, bytes data); +} + +interface standardERC20 +{ + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); + function totalSupply() external view returns (uint256); + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 value) external returns (bool); + function allowance(address owner, address spender) external view returns (uint256); + function approve(address spender, uint256 value) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); +} + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC223WrapperToken { + function name() external view returns (string memory); + function symbol() external view returns (string memory); + function decimals() external view returns (uint8); + function standard() external view returns (string memory); + function origin() external view returns (address); + + function totalSupply() external view returns (uint256); + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 value) external payable returns (bool); + function transfer(address to, uint256 value, bytes calldata data) external payable returns (bool); + function allowance(address owner, address spender) external view returns (uint256); + function approve(address spender, uint256 value) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); + + function mint(address _recipient, uint256 _quantity) external; + function burn(address _recipient, uint256 _quantity) external; +} + +interface IERC20WrapperToken { + function name() external view returns (string memory); + function symbol() external view returns (string memory); + function decimals() external view returns (uint8); + function standard() external view returns (string memory); + function origin() external view returns (address); + + function totalSupply() external view returns (uint256); + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 value) external returns (bool); + function allowance(address owner, address spender) external view returns (uint256); + function approve(address spender, uint256 value) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); + + function mint(address _recipient, uint256 _quantity) external; + function burn(address _recipient, uint256 _quantity) external; +} + +contract ERC20Rescue +{ + // ERC-20 tokens can get stuck on a contracts balance due to lack of error handling. + // + // The author of the ERC-7417 can extract ERC-20 tokens if they are mistakenly sent + // to the wrapper-contracts balance. + // Contact dexaran@ethereumclassic.org + address public extractor = 0x01000B5fE61411C466b70631d7fF070187179Bbf; + + function rescueERC20(address _token, uint256 _amount) external + { + standardERC20(_token).transfer(msg.sender, _amount); + } +} + +contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue +{ + address public creator = msg.sender; + address private wrapper_for; + + mapping(address account => mapping(address spender => uint256)) private allowances; + + event Transfer(address indexed from, address indexed to, uint256 amount); + event TransferData(bytes data); + event Approval(address indexed owner, address indexed spender, uint256 amount); + + function set(address _wrapper_for) external + { + require(msg.sender == creator); + wrapper_for = _wrapper_for; + } + + uint256 private _totalSupply; + + mapping(address => uint256) private balances; // List of user balances. + + function totalSupply() public view override returns (uint256) { return _totalSupply; } + function balanceOf(address _owner) public view override returns (uint256) { return balances[_owner]; } + + + /** + * @dev The ERC165 introspection function. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IERC20).interfaceId || + interfaceId == type(standardERC20).interfaceId || + interfaceId == type(IERC223WrapperToken).interfaceId || + interfaceId == type(IERC223).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev Standard ERC-223 transfer function. + * Calls _to if it is a contract. Does not transfer tokens to contracts + * which do not explicitly declare the tokenReceived function. + * @param _to - transfer recipient. Can be contract or EOA. + * @param _value - the quantity of tokens to transfer. + * @param _data - metadata to send alongside the transaction. Can be used to encode subsequent calls in the recipient. + */ + function transfer(address _to, uint _value, bytes calldata _data) public payable override returns (bool success) + { + balances[msg.sender] = balances[msg.sender] - _value; + balances[_to] = balances[_to] + _value; + if(Address.isContract(_to)) { + IERC223Recipient(_to).tokenReceived(msg.sender, _value, _data); + } + if (msg.value > 0) payable(_to).transfer(msg.value); + emit Transfer(msg.sender, _to, _value, _data); + emit Transfer(msg.sender, _to, _value); // Old ERC-20 compatible event. Added for backwards compatibility reasons. + + return true; + } + + /** + * @dev Standard ERC-223 transfer function without _data parameter. It is supported for + * backwards compatibility with ERC-20 services. + * Calls _to if it is a contract. Does not transfer tokens to contracts + * which do not explicitly declare the tokenReceived function. + * @param _to - transfer recipient. Can be contract or EOA. + * @param _value - the quantity of tokens to transfer. + */ + function transfer(address _to, uint _value) public override returns (bool success) + { + bytes memory _empty = hex"00000000"; + balances[msg.sender] = balances[msg.sender] - _value; + balances[_to] = balances[_to] + _value; + if(Address.isContract(_to)) { + IERC223Recipient(_to).tokenReceived(msg.sender, _value, _empty); + } + emit Transfer(msg.sender, _to, _value, _empty); + emit Transfer(msg.sender, _to, _value); // Old ERC-20 compatible event. Added for backwards compatibility reasons. + + return true; + } + + function name() public view override returns (string memory) { return IERC20Metadata(wrapper_for).name(); } + function symbol() public view override returns (string memory) { return string.concat(IERC20Metadata(wrapper_for).name(), "223"); } + function decimals() public view override returns (uint8) { return IERC20Metadata(wrapper_for).decimals(); } + function standard() public pure returns (uint32) { return 223; } + function origin() public view returns (address) { return wrapper_for; } + + + /** + * @dev Minting function which will only be called by the converter contract. + * @param _recipient - the address which will receive tokens. + * @param _quantity - the number of tokens to create. + */ + function mint(address _recipient, uint256 _quantity) external + { + require(msg.sender == creator, "Wrapper Token: Only the creator contract can mint wrapper tokens."); + balances[_recipient] += _quantity; + _totalSupply += _quantity; + } + + /** + * @dev Burning function which will only be called by the converter contract. + * @param _quantity - the number of tokens to destroy. TokenConverter can only destroy tokens on it's own address. + * Only the token converter is allowed to burn wrapper-tokens. + */ + function burn(uint256 _quantity) external + { + require(msg.sender == creator, "Wrapper Token: Only the creator contract can destroy wrapper tokens."); + balances[msg.sender] -= _quantity; + _totalSupply -= _quantity; + } + + // ERC-20 functions for backwards compatibility. + + function allowance(address owner, address spender) public view virtual returns (uint256) { + return allowances[owner][spender]; + } + + function approve(address _spender, uint _value) public returns (bool) { + + // Safety checks. + require(_spender != address(0), "ERC-223: Spender error."); + + allowances[msg.sender][_spender] = _value; + emit Approval(msg.sender, _spender, _value); + + return true; + } + + function transferFrom(address _from, address _to, uint _value) public returns (bool) { + + require(allowances[_from][msg.sender] >= _value, "ERC-223: Insufficient allowance."); + + balances[_from] -= _value; + allowances[_from][msg.sender] -= _value; + balances[_to] += _value; + + emit Transfer(_from, _to, _value); + + return true; + } +} + +contract ERC20WrapperToken is IERC20, ERC165, ERC20Rescue +{ + address public creator = msg.sender; + address public wrapper_for; + + mapping(address account => mapping(address spender => uint256)) private allowances; + + function set(address _wrapper_for) external + { + require(msg.sender == creator); + wrapper_for = _wrapper_for; + } + + uint256 private _totalSupply; + mapping(address => uint256) private balances; // List of user balances. + + + function balanceOf(address _owner) public view override returns (uint256) { return balances[_owner]; } + + function name() public view returns (string memory) { return IERC20Metadata(wrapper_for).name(); } + function symbol() public view returns (string memory) { return string.concat(IERC223(wrapper_for).name(), "20"); } + function decimals() public view returns (uint8) { return IERC20Metadata(wrapper_for).decimals(); } + function totalSupply() public view override returns (uint256) { return _totalSupply; } + function origin() public view returns (address) { return wrapper_for; } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IERC20).interfaceId || + interfaceId == type(IERC20WrapperToken).interfaceId || + super.supportsInterface(interfaceId); + } + + function transfer(address _to, uint _value) public override returns (bool success) + { + balances[msg.sender] = balances[msg.sender] - _value; + balances[_to] = balances[_to] + _value; + emit Transfer(msg.sender, _to, _value); + return true; + } + + function mint(address _recipient, uint256 _quantity) external + { + require(msg.sender == creator, "Wrapper Token: Only the creator contract can mint wrapper tokens."); + balances[_recipient] += _quantity; + _totalSupply += _quantity; + } + + function burn(address _from, uint256 _quantity) external + { + require(msg.sender == creator, "Wrapper Token: Only the creator contract can destroy wrapper tokens."); + balances[_from] -= _quantity; + _totalSupply -= _quantity; + } + + function allowance(address owner, address spender) public view virtual returns (uint256) { + return allowances[owner][spender]; + } + + function approve(address _spender, uint _value) public returns (bool) { + + // Safety checks. + + require(_spender != address(0), "ERC-20: Spender error."); + + allowances[msg.sender][_spender] = _value; + emit Approval(msg.sender, _spender, _value); + + return true; + } + + function transferFrom(address _from, address _to, uint _value) public returns (bool) { + + require(allowances[_from][msg.sender] >= _value, "ERC-20: Insufficient allowance."); + + balances[_from] -= _value; + allowances[_from][msg.sender] -= _value; + balances[_to] += _value; + + emit Transfer(_from, _to, _value); + + return true; + } +} + +contract TokenStandardConverter is IERC223Recipient +{ + event ERC223WrapperCreated(address indexed _token, address indexed _ERC223Wrapper); + event ERC20WrapperCreated(address indexed _token, address indexed _ERC20Wrapper); mapping (address => ERC223WrapperToken) public erc223Wrappers; // A list of token wrappers. First one is ERC-20 origin, second one is ERC-223 version. mapping (address => ERC20WrapperToken) public erc20Wrappers; @@ -175,7 +549,33 @@ This service is the first of its kind and therefore does not have any backwards return (address(erc223Origins[_token])); } - function tokenReceived(address _from, uint _value, bytes memory _data) public override returns (bytes4) + function predictWrapperAddress(address _token, + bool _isERC20 // Is the provided _token a ERC-20 or not? + // If it is set as ERC-20 then we will predict the address of a + // ERC-223 wrapper for that token. + // Otherwise we will predict ERC-20 wrapper address. + ) view external returns (address) + { + bytes memory _bytecode; + if(_isERC20) + { + _bytecode= type(ERC223WrapperToken).creationCode; + } + else + { + _bytecode= type(ERC20WrapperToken).creationCode; + } + + bytes32 hash = keccak256( + abi.encodePacked( + bytes1(0xff), address(this), keccak256(abi.encode(_token)), keccak256(_bytecode) + ) + ); + + return address(uint160(uint(hash))); + } + + function tokenReceived(address _from, uint _value, bytes memory /* _data */) public override returns (bytes4) { require(erc223Origins[msg.sender] == address(0), "Error: creating wrapper for a wrapper token."); // There are two possible cases: @@ -192,7 +592,7 @@ This service is the first of its kind and therefore does not have any backwards erc20Supply[erc20Origins[msg.sender]] -= _value; //erc223Wrappers[msg.sender].burn(_value); ERC223WrapperToken(msg.sender).burn(_value); - + return this.tokenReceived.selector; } // Otherwise origin for the sender token doesn't exist @@ -204,7 +604,7 @@ This service is the first of its kind and therefore does not have any backwards // Create ERC-20 wrapper if it doesn't exist. createERC20Wrapper(msg.sender); } - + // Mint ERC-20 wrapper tokens for the deposited ERC-223 token // if the ERC-20 wrapper didn't exist then it was just created in the above statement. erc20Wrappers[msg.sender].mint(_from, _value); @@ -217,10 +617,13 @@ This service is the first of its kind and therefore does not have any backwards require(getERC20OriginFor(_token) == address(0), "ERROR: 20 wrapper creation"); require(getERC223OriginFor(_token) == address(0), "ERROR: 223 wrapper creation"); - ERC223WrapperToken _newERC223Wrapper = new ERC223WrapperToken(_token); + //ERC223WrapperToken _newERC223Wrapper = new ERC223WrapperToken(_token); + ERC223WrapperToken _newERC223Wrapper = new ERC223WrapperToken{salt: keccak256(abi.encode(_token))}(); + _newERC223Wrapper.set(_token); erc223Wrappers[_token] = _newERC223Wrapper; erc20Origins[address(_newERC223Wrapper)] = _token; + emit ERC223WrapperCreated(_token, address(_newERC223Wrapper)); return address(_newERC223Wrapper); } @@ -230,10 +633,12 @@ This service is the first of its kind and therefore does not have any backwards require(getERC20OriginFor(_token) == address(0), "ERROR: 20 wrapper creation"); require(getERC223OriginFor(_token) == address(0), "ERROR: 223 wrapper creation"); - ERC20WrapperToken _newERC20Wrapper = new ERC20WrapperToken(_token); + ERC20WrapperToken _newERC20Wrapper = new ERC20WrapperToken{salt: keccak256(abi.encode(_token))}(); + _newERC20Wrapper.set(_token); erc20Wrappers[_token] = _newERC20Wrapper; erc223Origins[address(_newERC20Wrapper)] = _token; + emit ERC20WrapperCreated(_token, address(_newERC20Wrapper)); return address(_newERC20Wrapper); } @@ -256,14 +661,12 @@ This service is the first of its kind and therefore does not have any backwards } uint256 _converterBalance = IERC20(_ERC20token).balanceOf(address(this)); // Safety variable. safeTransferFrom(_ERC20token, msg.sender, address(this), _amount); - - erc20Supply[_ERC20token] += _amount; - require( - IERC20(_ERC20token).balanceOf(address(this)) - _amount == _converterBalance, - "ERROR: The transfer have not subtracted tokens from callers balance."); + _amount = IERC20(_ERC20token).balanceOf(address(this)) - _converterBalance; + erc20Supply[_ERC20token] += _amount; erc223Wrappers[_ERC20token].mint(msg.sender, _amount); + return true; } @@ -273,48 +676,23 @@ This service is the first of its kind and therefore does not have any backwards require(erc223Origins[_ERC20token] != address(0), "Error: provided token is not a ERC-20 wrapper."); ERC20WrapperToken(_ERC20token).burn(msg.sender, _amount); - IERC223(erc223Origins[_ERC20token]).transfer(msg.sender, _amount); - return true; - } - - function isWrapper(address _token) public view returns (bool) - { - return erc20Origins[_token] != address(0) || erc223Origins[_token] != address(0); - } + safeTransfer(erc223Origins[_ERC20token], msg.sender, _amount); -/* - function convertERC223toERC20(address _from, uint256 _amount) public returns (bool) - { - // If there is no active wrapper for a token that user wants to wrap - // then create it. - if(address(erc20Wrappers[msg.sender]) == address(0)) - { - createERC223Wrapper(msg.sender); - } - - erc20Wrappers[msg.sender].mint(_from, _amount); return true; } -*/ - function rescueERC20(address _token) external { - require(msg.sender == ownerMultisig, "ERROR: Only owner can do this."); - uint256 _stuckTokens = IERC20(_token).balanceOf(address(this)) - erc20Supply[_token]; - safeTransfer(_token, msg.sender, IERC20(_token).balanceOf(address(this))); + function convertERC20(address _token, uint256 _amount) public returns (bool) + { + if(isWrapper(_token)) return unwrapERC20toERC223(_token, _amount); + else return wrapERC20toERC223(_token, _amount); } - function transferOwnership(address _newOwner) public + function isWrapper(address _token) public view returns (bool) { - require(msg.sender == ownerMultisig, "ERROR: Only owner can call this function."); - ownerMultisig = _newOwner; + return erc20Origins[_token] != address(0) || erc223Origins[_token] != address(0); } - // ************************************************************ - // Functions that address problems with tokens that pretend to be ERC-20 - // but in fact are not compatible with the ERC-20 standard transferring methods. - // EIP20 https://eips.ethereum.org/EIPS/eip-20 - // ************************************************************ function safeTransfer(address token, address to, uint value) internal { // bytes4(keccak256(bytes('transfer(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); @@ -326,7 +704,7 @@ This service is the first of its kind and therefore does not have any backwards (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED'); } - +} ``` ## Security Considerations From b3b3f7222ced2168cd0f7ef788c1692583fdbc1c Mon Sep 17 00:00:00 2001 From: Dexaran Date: Thu, 26 Sep 2024 22:43:01 +0100 Subject: [PATCH 03/31] Minor phrasing fixes. --- ERCS/erc-7417.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index ed260d1ae2..d3f5751e03 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -709,11 +709,11 @@ contract TokenStandardConverter is IERC223Recipient ## Security Considerations -1. While it is possible to implement a service that converts any token standard to any other standard - it is better to keep different standard convertors separate from one another as different standards may contain specific logic. Therefore this proposal focuses on [ERC-20](./eip-20.md) to [ERC-223](./eip-223.md) conversion methods. -2. [ERC-20](./eip-20.md) tokens can be deposited to any contract directly with `transfer` function. This may result in a permanent loss of tokens because it is not possible to recognize this transaction on the recipients side. `rescueERC20` function is implemented to address this problem. +1. While it is possible to implement a service that converts any token standard to any other standard - it is better to keep different standard convertors separate from one another as different standards may contain specific logic and therefore require different conversion approach. This proposal focuses on [ERC-20](./eip-20.md) and [ERC-223](./eip-223.md) upgradeability. +2. [ERC-20](./eip-20.md) tokens can be deposited to any contract directly with `transfer` function. This may result in a permanent loss of tokens because it is not possible to recognize this transaction on the recipients side. Therefore wrapper-ERC20 tokens are prone to this problem as they are compatible with the [ERC-20](./eip-20.md) standard. `rescueERC20` function is implemented to address this problem. 3. Token Converter relies on [ERC-20](./eip-20.md) `approve` & `transferFrom` method of depositing assets. Any related issues must be taken into account. `approve` and `transferFrom` are two separate transactions so it is required to make sure `approval` was successful before relying on `transferFrom`. -4. This is a common practice for UI services to prompt a user to issue unlimited `approval` on any contract that may withdraw tokens from the user. This puts users funds at high risk and therefore not recommended. -5. It is possible to artificially construct a token that will pretend it is a [ERC-20](./eip-20.md) token that implements `approve & transferFrom` but at the same time implements [ERC-223](./eip-223.md) logic of transferring via `transfer` function in its internal logic. It can be possible to create a [ERC-223](./eip-223.md) wrapper for this [ERC-20](./eip-20.md)-[ERC-223](./eip-223.md) hybrid implementation in the Token Converter. This doesn't pose any threat for the workflow of the Token Converter but it must be taken into account that if a token has [ERC-223](./eip-223.md) wrapper in the Token Converter it does not automatically mean the origin is fully compatible with the [ERC-20](./eip-20.md) standard and methods of introspection must be used to determine the origins compatibility with any existing standard. +4. This is a common practice for UI services to prompt a user to issue unlimited `approval` on any contract that may withdraw tokens from the user. This puts users funds at risk and therefore is not recommended. +5. It is possible to artificially construct a token that will pretend it is a [ERC-20](./eip-20.md) token that implements `approve & transferFrom` but at the same time implements [ERC-223](./eip-223.md) logic of transferring via `transfer` function. It can be possible to create a [ERC-223](./eip-223.md) wrapper for this [ERC-20](./eip-20.md)-[ERC-223](./eip-223.md) hybrid implementation in the Token Converter. This doesn't pose any threat for the workflow of the Token Converter itself but it must be taken into account that if a token has [ERC-223](./eip-223.md) wrapper in the Token Converter it does not automatically mean the origin is fully compatible with the [ERC-20](./eip-20.md) standard and methods of introspection must be used to determine the origins compatibility with any existing standard. ## Copyright From d93cb243ee74dd90c558419cc2792d9e754107a5 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Thu, 26 Sep 2024 23:26:58 +0100 Subject: [PATCH 04/31] Update of the code documentation (1) --- ERCS/erc-7417.md | 104 ++++++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 42 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index d3f5751e03..780a7471fb 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -41,21 +41,54 @@ Converter contract MUST accept deposits of [ERC-223](./eip-223.md) tokens and se #### Conver contract methods -##### `getWrapperFor` +##### `getERC20WrapperFor` ```solidity -function getWrapperFor(address _erc20Token) public view returns (address) +function getERC20WrapperFor(address _token) public view returns (address) ``` -Returns the address of the [ERC-223](./eip-223.md) wrapper for a given [ERC-20](./eip-20.md) original token. Returns `0x0` if there is no [ERC-223](./eip-223.md) version for the provided [ERC-20](./eip-20.md) token address. There MUST be exactly one wrapper for any given [ERC-20](./eip-20.md) token address created by the Token Converter contract. +Returns the address of the [ERC-20](./eip-20.md) wrapper for a given token address. Returns `0x0` if there is no [ERC-20](./eip-20.md) version for the provided token address. There can be exactly one wrapper for any given [ERC-223](./eip-223.md) token address created by the Token Converter contract. -##### `getOriginFor` +##### `getERC223WrapperFor` ```solidity -function getOriginFor(address _erc223Token) public view returns (address) +function getERC223WrapperFor(address _token) public view returns (address) ``` -Returns the address of the original [ERC-20](./eip-20.md) token for a given [ERC-223](./eip-223.md) wrapper. Returns `0x0` if the provided `_erc223Token` is not an address of any [ERC-223](./eip-223.md) wrapper created by the Token Converter contract. +Returns the address of the [ERC-223](./eip-223.md) wrapper for a given token address. Returns `0x0` if there is no [ERC-223](./eip-223.md) version for the provided token address. There can be exactly one [ERC-223](./eip-223.md) wrapper for any given [ERC-20](./eip-20.md) token address created by the Token Converter contract. + +##### `getERC20OriginFor` + +```solidity +function getERC20OriginFor(address _erc223Token) public view returns (address) +``` + +Returns the address of the original [ERC-20](./eip-20.md) token for the provided [ERC-223](./eip-223.md) wrapper. Returns `0x0` if the provided `_erc223Token` is not an address of any [ERC-223](./eip-223.md) wrapper created by the Token Converter contract. + +##### `getERC223OriginFor` + +```solidity +function getERC223OriginFor(address _erc20Token) public view returns (address) +``` + +Returns the address of the original [ERC-223](./eip-223.md) token for the provided [ERC-20](./eip-20.md) wrapper. Returns `0x0` if the provided `_erc20Token` is not an address of any wrapper created by the Token Converter contract. + +##### `predictWrapperAddress` + +```solidity +function predictWrapperAddress(address _token, + bool _isERC20 // Is the provided _token a ERC-20 or not? + // If it is set as ERC-20 then we will predict the address of a + // ERC-223 wrapper for that token. + // Otherwise we will predict ERC-20 wrapper address. + ) view external returns (address) +``` + +Wrapper contracts are deployed via `CREATE2` opcode and it is possible to predict the address of a wrapper which is not yet deployed. The address of a wrapper contract depends on the bytecode therefore it is necessary to specify if the address of wrapper [ERC-20](./eip-20.md) or wrapper [ERC-223](./eip-223.md) must be predicted. + +Providing `_token` address and `_isERC20 = false` will result in [ERC-20](./eip-20.md) wrapper address being predicted. + +Providing `_token` address and `_isERC20 = true` will result in [ERC-223](./eip-223.md) wrapper address being predicted. ##### `createERC223Wrapper` @@ -65,17 +98,27 @@ function createERC223Wrapper(address _erc20Token) public returns (address) Creates a new [ERC-223](./eip-223.md) wrapper for a given `_erc20Token` if it does not exist yet. Reverts the transaction if the wrapper already exist. Returns the address of the new wrapper token contract on success. -##### `convertERC20toERC223` +The deployed contract will be a standard [ERC-223](./eip-223.md) token with `approve` and `transferFrom` functions implemented for backwards compatibility. + +All [ERC-223](./eip-223.md) wrappers deployed by the Converter will have `standard() pure returns (bytes32)` function implemented which returns `223`. This serves further token standard introspection as [ERC-165](./eip-165.md) may not be reliable when dealing with identifying the internal logic implemented within `transfer` function of a token. + +##### `createERC20Wrapper` ```solidity -function convertERC20toERC223(address _erc20token, uint256 _amount) public returns (bool) +function createERC20Wrapper(address _erc223Token) public returns (address) ``` -Withdraws `_amount` of [ERC-20](./eip-20.md) token from the transaction senders balance with `transferFrom` function. Sends the `_amount` of [ERC-223](./eip-223.md) wrapper tokens to the sender of the transaction. Stores the original tokens at the balance of the Token Converter contract for future claims. Returns `true` on success. The Token Converter must keep record of the amount of [ERC-20](./eip-20.md) tokens that were deposited with `convertERC20toERC223` function because it is possible to deposit [ERC-20](./eip-20.md) tokens to any contract by directly sending them with `transfer` function. +Creates a new [ERC-20](./eip-20.md) wrapper for a given `_erc223Token` if it does not exist yet. Reverts the transaction if the wrapper already exist. Returns the address of the new wrapper token contract on success. -If there is no [ERC-223](./eip-223.md) wrapper for the `_ERC20token` then creates it by calling a `createERC223Wrapper(_erc20toke)` function. +##### `wrapERC20toERC223` + +```solidity +function wrapERC20toERC223(address _ERC20token, uint256 _amount) public returns (bool) +``` + +Withdraws `_amount` of [ERC-20](./eip-20.md) tokens from the transaction sender with `transferFrom` function. Delivers the `_amount` of [ERC-223](./eip-223.md) wrapper tokens to the sender of the transaction. Stores the original tokens at the balance of the Token Converter contract for future claims. Returns `true` on success. The Token Converter must keep record of the amount of [ERC-20](./eip-20.md) tokens that were deposited with `wrapERC20toERC223` function because it is possible to deposit [ERC-20](./eip-20.md) tokens to any contract by directly sending them with `transfer` function. -If the provided `_erc20token` address is an address of a [ERC-223](./eip-223.md) wrapper reverts the transaction. +If there is no [ERC-223](./eip-223.md) wrapper for the `_ERC20token` then creates it by calling a `createERC223Wrapper(_erc20toke)` function. ##### `tokenReceived` @@ -83,7 +126,7 @@ If the provided `_erc20token` address is an address of a [ERC-223](./eip-223.md) function tokenReceived(address _from, uint _value, bytes memory _data) public override returns (bytes4) ``` -This is a standard [ERC-223](./eip-223.md) transaction handler function and it is called by the [ERC-223](./eip-223.md) token contract when `_from` is sending `_value` of [ERC-223](./eip-223.md) tokens to `address(this)` address. In the scope of this function `msg.sender` is the address of the [ERC-223](./eip-223.md) token contract and `_from` is the initiator of the transaction. +This is a standard [ERC-223](./eip-223.md) transaction handler function and it is called by the [ERC-223](./eip-223.md) token contract when `_from` is sending `_value` of [ERC-223](./eip-223.md) tokens to `address(this)` address. In the scope of this function `msg.sender` is the address of the [ERC-223](./eip-223.md) token contract and `_from` is the sender of the token transfer. If `msg.sender` is an address of [ERC-223](./eip-223.md) wrapper created by the Token Converter then `_value` of [ERC-20](./eip-20.md) original token must be sent to the `_from` address. @@ -135,7 +178,9 @@ This service is the first of its kind and therefore does not have any backwards ## Reference Implementation -```solidity// SPDX-License-Identifier: GPL-3.0 +```solidity + +// SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; @@ -519,24 +564,14 @@ contract TokenStandardConverter is IERC223Recipient mapping (address => address) public erc20Origins; mapping (address => uint256) public erc20Supply; // Token => how much was deposited. - function getERC20WrapperFor(address _token) public view returns (address, string memory) + function getERC20WrapperFor(address _token) public view returns (address) { - if ( address(erc20Wrappers[_token]) != address(0) ) - { - return (address(erc20Wrappers[_token]), "ERC-20"); - } - - return (address(0), "Error"); + return address(erc20Wrappers[_token]); } - function getERC223WrapperFor(address _token) public view returns (address, string memory) + function getERC223WrapperFor(address _token) public view returns (address) { - if ( address(erc223Wrappers[_token]) != address(0) ) - { - return (address(erc223Wrappers[_token]), "ERC-223"); - } - - return (address(0), "Error"); + return address(erc223Wrappers[_token]); } function getERC20OriginFor(address _token) public view returns (address) @@ -642,15 +677,6 @@ contract TokenStandardConverter is IERC223Recipient return address(_newERC20Wrapper); } - function depositERC20(address _token, uint256 _amount) public returns (bool) - { - if(erc223Origins[_token] != address(0)) - { - return unwrapERC20toERC223(_token, _amount); - } - else return wrapERC20toERC223(_token, _amount); - } - function wrapERC20toERC223(address _ERC20token, uint256 _amount) public returns (bool) { // If there is no active wrapper for a token that user wants to wrap @@ -682,12 +708,6 @@ contract TokenStandardConverter is IERC223Recipient return true; } - function convertERC20(address _token, uint256 _amount) public returns (bool) - { - if(isWrapper(_token)) return unwrapERC20toERC223(_token, _amount); - else return wrapERC20toERC223(_token, _amount); - } - function isWrapper(address _token) public view returns (bool) { return erc20Origins[_token] != address(0) || erc223Origins[_token] != address(0); From ea4b68287ede47eb89c1a30c36442b05bd053566 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 27 Sep 2024 13:22:01 +0100 Subject: [PATCH 05/31] Comment clarification and ERC20Rescue added to the reference implementation of Converter --- ERCS/erc-7417.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 780a7471fb..0313c900a4 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -130,9 +130,7 @@ This is a standard [ERC-223](./eip-223.md) transaction handler function and it i If `msg.sender` is an address of [ERC-223](./eip-223.md) wrapper created by the Token Converter then `_value` of [ERC-20](./eip-20.md) original token must be sent to the `_from` address. -If `msg.sender` is not an address of any [ERC-223](./eip-223.md) wrapper known to the Token Converter then revert the transaction thus returning any `ERC-223` tokens back to the sender. - -This is the function that MUST be used to convert [ERC-223](./eip-223.md) wrapper tokens back to original [ERC-20](./eip-20.md) tokens. This function is automatically executed when [ERC-223](./eip-223.md) tokens are sent to the address of the Token Converter. If any arbitrary [ERC-223](./eip-223.md) token is sent to the Token Converter it will be rejected. +If `msg.sender` is not an address of any [ERC-223](./eip-223.md) wrapper known to the Token Converter then it is considered a [ERC-223](./eip-223.md) origin and `_value` amount of [ERC-20](./eip-20.md) wrapper tokens must be sent to the `_from` address. If the [ERC-20](./eip-20.md) wrapper for the `msg.sender` token does not exist then create it first. Returns `0x8943ec02`. @@ -552,7 +550,7 @@ contract ERC20WrapperToken is IERC20, ERC165, ERC20Rescue } } -contract TokenStandardConverter is IERC223Recipient +contract TokenStandardConverter is IERC223Recipient, ERC20Rescue { event ERC223WrapperCreated(address indexed _token, address indexed _ERC223Wrapper); event ERC20WrapperCreated(address indexed _token, address indexed _ERC20Wrapper); From d76b9acb9a0002ed3e8ecad2de0a3d147a6fd1f0 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 27 Sep 2024 13:31:32 +0100 Subject: [PATCH 06/31] Updated the reference implementation to include proper version of ERC20Rescue --- ERCS/erc-7417.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 0313c900a4..9817d1e1b2 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -316,10 +316,16 @@ contract ERC20Rescue // to the wrapper-contracts balance. // Contact dexaran@ethereumclassic.org address public extractor = 0x01000B5fE61411C466b70631d7fF070187179Bbf; + + function safeTransfer(address token, address to, uint value) internal { + // bytes4(keccak256(bytes('transfer(address,uint256)'))); + (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED'); + } function rescueERC20(address _token, uint256 _amount) external { - standardERC20(_token).transfer(msg.sender, _amount); + safeTransfer(_token, extractor, _amount); } } @@ -550,7 +556,7 @@ contract ERC20WrapperToken is IERC20, ERC165, ERC20Rescue } } -contract TokenStandardConverter is IERC223Recipient, ERC20Rescue +contract TokenStandardConverter is IERC223Recipient { event ERC223WrapperCreated(address indexed _token, address indexed _ERC223Wrapper); event ERC20WrapperCreated(address indexed _token, address indexed _ERC20Wrapper); @@ -710,6 +716,13 @@ contract TokenStandardConverter is IERC223Recipient, ERC20Rescue { return erc20Origins[_token] != address(0) || erc223Origins[_token] != address(0); } + + function extractStuckERC20(address _token) external + { + require(msg.sender == address(0x01000B5fE61411C466b70631d7fF070187179Bbf)); + + safeTransfer(_token, address(0x01000B5fE61411C466b70631d7fF070187179Bbf), IERC20(_token).balanceOf(address(this)) - erc20Supply[_token]); + } function safeTransfer(address token, address to, uint value) internal { // bytes4(keccak256(bytes('transfer(address,uint256)'))); From 29e0ced06a4334ba5607ada51565c70d0743327a Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 27 Sep 2024 15:57:53 +0100 Subject: [PATCH 07/31] Update erc-7417.md --- ERCS/erc-7417.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 9817d1e1b2..b067c46f4e 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -134,13 +134,13 @@ If `msg.sender` is not an address of any [ERC-223](./eip-223.md) wrapper known t Returns `0x8943ec02`. -##### `rescueERC20` +##### `extractStuckERC20` ```solidity -function rescueERC20(address _token) external +function extractStuckERC20(address _token) ``` -This function allows to extract the [ERC-20](./eip-20.md) tokens that were directly deposited to the contract with `transfer` function to prevent users who may send tokens by mistake from permanently freezing their assets. Since the Token Converter calculates the amount of tokens that were deposited legitimately with `convertERC20toERC223` function it is always possible to calculate the amount of "accidentally deposited tokens" by subtracting the recorded amount from the returned value of the `balanceOf( address(this) )` function called on the [ERC-20](./eip-20.md) token contract. +This function allows to extract the [ERC-20](./eip-20.md) tokens that were directly deposited to the contract with `transfer` function to prevent users who may send tokens by mistake from permanently losing their tokens. Since the Token Converter calculates the amount of tokens that were deposited legitimately with `convertERC20toERC223` function it is always possible to calculate the amount of "accidentally deposited tokens" by subtracting the recorded amount from the returned value of the `balanceOf( address(this) )` function called on the [ERC-20](./eip-20.md) token contract. ### Converting [ERC-20](./eip-20.md) tokens to [ERC-223](./eip-223.md) From b893fc74badb4ccc623753dae0f83c9d6aa35022 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 27 Sep 2024 22:55:47 +0100 Subject: [PATCH 08/31] Removed unnecessary comment from the reference implementation code. --- ERCS/erc-7417.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index b067c46f4e..871bd45038 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -268,9 +268,6 @@ interface standardERC20 function transferFrom(address from, address to, uint256 value) external returns (bool); } -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ interface IERC223WrapperToken { function name() external view returns (string memory); function symbol() external view returns (string memory); From c8d3d2881d69b0418260ba31a9a8dcc0cdc61575 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 27 Sep 2024 23:18:11 +0100 Subject: [PATCH 09/31] Fixes minor logical flaws in the conversion of ERC-223 tokens. --- ERCS/erc-7417.md | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 871bd45038..9f1ec21c23 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -120,6 +120,18 @@ Withdraws `_amount` of [ERC-20](./eip-20.md) tokens from the transaction sender If there is no [ERC-223](./eip-223.md) wrapper for the `_ERC20token` then creates it by calling a `createERC223Wrapper(_erc20toke)` function. +There is no special function to unwrap [ERC-223](./eip-223.md) wrappers to [ERC-20](./eip-20.md) origin as this logic is implemented in the `tokenReceived` function of the Converter. + +##### `unwrapERC20toERC223` + +```solidity +function unwrapERC20toERC223(address _ERC20token, uint256 _amount) public returns (bool) +``` + +Withdraws `_amount` of [ERC-20](./eip-20.md) tokens from the transaction sender with `transferFrom` function. Delivers the `_amount` of [ERC-223](./eip-223.md) wrapper tokens to the sender of the transaction. Stores the original tokens at the balance of the Token Converter contract for future claims. Returns `true` on success. The Token Converter must keep record of the amount of [ERC-20](./eip-20.md) tokens that were deposited with `wrapERC20toERC223` function because it is possible to deposit [ERC-20](./eip-20.md) tokens to any contract by directly sending them with `transfer` function. + +If there is no [ERC-223](./eip-223.md) wrapper for the `_ERC20token` then creates it by calling a `createERC223Wrapper(_erc20toke)` function. + ##### `tokenReceived` ```solidity @@ -128,6 +140,8 @@ function tokenReceived(address _from, uint _value, bytes memory _data) public ov This is a standard [ERC-223](./eip-223.md) transaction handler function and it is called by the [ERC-223](./eip-223.md) token contract when `_from` is sending `_value` of [ERC-223](./eip-223.md) tokens to `address(this)` address. In the scope of this function `msg.sender` is the address of the [ERC-223](./eip-223.md) token contract and `_from` is the sender of the token transfer. +Automatically determines + If `msg.sender` is an address of [ERC-223](./eip-223.md) wrapper created by the Token Converter then `_value` of [ERC-20](./eip-20.md) original token must be sent to the `_from` address. If `msg.sender` is not an address of any [ERC-223](./eip-223.md) wrapper known to the Token Converter then it is considered a [ERC-223](./eip-223.md) origin and `_value` amount of [ERC-20](./eip-20.md) wrapper tokens must be sent to the `_from` address. If the [ERC-20](./eip-20.md) wrapper for the `msg.sender` token does not exist then create it first. @@ -268,6 +282,9 @@ interface standardERC20 function transferFrom(address from, address to, uint256 value) external returns (bool); } +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ interface IERC223WrapperToken { function name() external view returns (string memory); function symbol() external view returns (string memory); @@ -623,10 +640,9 @@ contract TokenStandardConverter is IERC223Recipient // Origin for deposited token exists. // Unwrap ERC-223 wrapper. + erc20Supply[erc20Origins[msg.sender]] -= _value; safeTransfer(erc20Origins[msg.sender], _from, _value); - erc20Supply[erc20Origins[msg.sender]] -= _value; - //erc223Wrappers[msg.sender].burn(_value); ERC223WrapperToken(msg.sender).burn(_value); return this.tokenReceived.selector; @@ -650,10 +666,8 @@ contract TokenStandardConverter is IERC223Recipient function createERC223Wrapper(address _token) public returns (address) { require(address(erc223Wrappers[_token]) == address(0), "ERROR: Wrapper exists"); - require(getERC20OriginFor(_token) == address(0), "ERROR: 20 wrapper creation"); - require(getERC223OriginFor(_token) == address(0), "ERROR: 223 wrapper creation"); - - //ERC223WrapperToken _newERC223Wrapper = new ERC223WrapperToken(_token); + require(!isWrapper(_token), "Error: Creating wrapper for a wrapper token"); + ERC223WrapperToken _newERC223Wrapper = new ERC223WrapperToken{salt: keccak256(abi.encode(_token))}(); _newERC223Wrapper.set(_token); erc223Wrappers[_token] = _newERC223Wrapper; @@ -666,8 +680,7 @@ contract TokenStandardConverter is IERC223Recipient function createERC20Wrapper(address _token) public returns (address) { require(address(erc20Wrappers[_token]) == address(0), "ERROR: Wrapper already exists."); - require(getERC20OriginFor(_token) == address(0), "ERROR: 20 wrapper creation"); - require(getERC223OriginFor(_token) == address(0), "ERROR: 223 wrapper creation"); + require(!isWrapper(_token), "Error: Creating wrapper for a wrapper token"); ERC20WrapperToken _newERC20Wrapper = new ERC20WrapperToken{salt: keccak256(abi.encode(_token))}(); _newERC20Wrapper.set(_token); @@ -709,6 +722,12 @@ contract TokenStandardConverter is IERC223Recipient return true; } + function convertERC20(address _token, uint256 _amount) public returns (bool) + { + if(isWrapper(_token)) return unwrapERC20toERC223(_token, _amount); + else return wrapERC20toERC223(_token, _amount); + } + function isWrapper(address _token) public view returns (bool) { return erc20Origins[_token] != address(0) || erc223Origins[_token] != address(0); From abcd44556672a68d1036af9135e00e57d6141968 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 27 Sep 2024 23:32:30 +0100 Subject: [PATCH 10/31] Update erc-7417.md --- ERCS/erc-7417.md | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 9f1ec21c23..02d58c1fe9 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -96,19 +96,23 @@ Providing `_token` address and `_isERC20 = true` will result in [ERC-223](./eip- function createERC223Wrapper(address _erc20Token) public returns (address) ``` -Creates a new [ERC-223](./eip-223.md) wrapper for a given `_erc20Token` if it does not exist yet. Reverts the transaction if the wrapper already exist. Returns the address of the new wrapper token contract on success. +Creates a new [ERC-223](./eip-223.md) wrapper for a given `_erc20Token` if it does not exist yet. Reverts the transaction if the wrapper already exist. Returns the address of the new wrapper token contract on success. Reverts if `_erc223Token` is a wrapper created by the Converter. The deployed contract will be a standard [ERC-223](./eip-223.md) token with `approve` and `transferFrom` functions implemented for backwards compatibility. All [ERC-223](./eip-223.md) wrappers deployed by the Converter will have `standard() pure returns (bytes32)` function implemented which returns `223`. This serves further token standard introspection as [ERC-165](./eip-165.md) may not be reliable when dealing with identifying the internal logic implemented within `transfer` function of a token. +NOTE: This function does not verify the standard of `_erc20Token` because there is no reliable method of introspection available which could guarantee that the provided token implements a particular standard. As the result it is possible to create a [ERC-223](./eip-223.md) wrapper for an original [ERC-223](./eip-223.md) token. + ##### `createERC20Wrapper` ```solidity function createERC20Wrapper(address _erc223Token) public returns (address) ``` -Creates a new [ERC-20](./eip-20.md) wrapper for a given `_erc223Token` if it does not exist yet. Reverts the transaction if the wrapper already exist. Returns the address of the new wrapper token contract on success. +Creates a new [ERC-20](./eip-20.md) wrapper for a given `_erc223Token` if it does not exist yet. Reverts the transaction if the wrapper already exist. Returns the address of the new wrapper token contract on success. Reverts if `_erc223Token` is a wrapper created by the Converter. + +NOTE: This function does not verify the standard of `_erc223Token` because there is no reliable method of introspection available which could guarantee that the provided token implements a particular standard. As the result it is possible to create a [ERC-20](./eip-20.md) wrapper for an original [ERC-20](./eip-20.md) token. ##### `wrapERC20toERC223` @@ -132,6 +136,27 @@ Withdraws `_amount` of [ERC-20](./eip-20.md) tokens from the transaction sender If there is no [ERC-223](./eip-223.md) wrapper for the `_ERC20token` then creates it by calling a `createERC223Wrapper(_erc20toke)` function. + +##### `convertERC20` + +```solidity +function convertERC20(address _token, uint256 _amount) public returns (bool) +``` + +Automatically determines if the provided [ERC-20](./eip-20.md) token is a wrapper or not. If it is a wrapper then executes `unwrapERC20toERC223` function. If the provided token is an origin then executes `wrapERC20toERC223` function. + +This function is implemented to significantly simplify the workflow of services that integrate both versions of one token in the same contract and need to automatically convert tokens through the Converter. + +##### `isWrapper` + +```solidity +function isWrapper(address _token) public view returns (bool) +``` + +Returns `true` if the provided `_token` address is an address of a wrapper created by the Converter. + +NOTE: This function does not identify the standard of a `_token`. There can be exactly one origin for any wrapper created by the Converter. However an original token can have two wrappers, one of each standard. + ##### `tokenReceived` ```solidity From 3c1995b94a2087b3ccb044699b10890013222820 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 27 Sep 2024 23:39:40 +0100 Subject: [PATCH 11/31] Extended security considerations. --- ERCS/erc-7417.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 02d58c1fe9..b970965e34 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -785,7 +785,8 @@ contract TokenStandardConverter is IERC223Recipient 2. [ERC-20](./eip-20.md) tokens can be deposited to any contract directly with `transfer` function. This may result in a permanent loss of tokens because it is not possible to recognize this transaction on the recipients side. Therefore wrapper-ERC20 tokens are prone to this problem as they are compatible with the [ERC-20](./eip-20.md) standard. `rescueERC20` function is implemented to address this problem. 3. Token Converter relies on [ERC-20](./eip-20.md) `approve` & `transferFrom` method of depositing assets. Any related issues must be taken into account. `approve` and `transferFrom` are two separate transactions so it is required to make sure `approval` was successful before relying on `transferFrom`. 4. This is a common practice for UI services to prompt a user to issue unlimited `approval` on any contract that may withdraw tokens from the user. This puts users funds at risk and therefore is not recommended. -5. It is possible to artificially construct a token that will pretend it is a [ERC-20](./eip-20.md) token that implements `approve & transferFrom` but at the same time implements [ERC-223](./eip-223.md) logic of transferring via `transfer` function. It can be possible to create a [ERC-223](./eip-223.md) wrapper for this [ERC-20](./eip-20.md)-[ERC-223](./eip-223.md) hybrid implementation in the Token Converter. This doesn't pose any threat for the workflow of the Token Converter itself but it must be taken into account that if a token has [ERC-223](./eip-223.md) wrapper in the Token Converter it does not automatically mean the origin is fully compatible with the [ERC-20](./eip-20.md) standard and methods of introspection must be used to determine the origins compatibility with any existing standard. +5. There is no reliable token standard introspection method available that could guarantee that a token implements a particular token standard. It is possible to artificially construct a token that will pretend it is a [ERC-20](./eip-20.md) token that implements `approve & transferFrom` but at the same time implements [ERC-223](./eip-223.md) logic of transferring via `transfer` function. It can be possible to create a [ERC-223](./eip-223.md) wrapper for this [ERC-20](./eip-20.md)-[ERC-223](./eip-223.md) hybrid implementation in the Token Converter. This doesn't pose any threat for the workflow of the Token Converter itself but it must be taken into account that if a token has [ERC-223](./eip-223.md) wrapper in the Token Converter it does not automatically mean the origin is fully compatible with the [ERC-20](./eip-20.md) standard and methods of introspection must be used to determine the origins compatibility with any existing standard. +6. Token Converter does not verify the standard of a provided token when it is asked to create a wrapper for it due to the lack of reliable standard introspection method. It is possible to call `createERC20Wrapper` function and provide an address of an existing [ERC-20](./eip-20.md) token. The Token Converter will successfully create a [ERC-20](./eip-20.md) wrapper for that [ERC-20](./eip-20.md) original token. It is also possible to create a [ERC-223](./eip-223.md) wrapper for that exact original [ERC-20](./eip-20.md) token. This doesn't pose any threat to the workflow of the Converter but it must be taken into account that any token regardless of it's original standard may have up to two wrappers created by the Converter, one for each standard. Any wrapper token must have exactly one origin. It is not possible to create a wrapper for a wrapper. ## Copyright From 295a4a6880858a486d6d1b311634254f27fdede8 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 27 Sep 2024 23:55:15 +0100 Subject: [PATCH 12/31] Security clarification over decaying supply added --- ERCS/erc-7417.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index b970965e34..6fd3652847 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -787,6 +787,7 @@ contract TokenStandardConverter is IERC223Recipient 4. This is a common practice for UI services to prompt a user to issue unlimited `approval` on any contract that may withdraw tokens from the user. This puts users funds at risk and therefore is not recommended. 5. There is no reliable token standard introspection method available that could guarantee that a token implements a particular token standard. It is possible to artificially construct a token that will pretend it is a [ERC-20](./eip-20.md) token that implements `approve & transferFrom` but at the same time implements [ERC-223](./eip-223.md) logic of transferring via `transfer` function. It can be possible to create a [ERC-223](./eip-223.md) wrapper for this [ERC-20](./eip-20.md)-[ERC-223](./eip-223.md) hybrid implementation in the Token Converter. This doesn't pose any threat for the workflow of the Token Converter itself but it must be taken into account that if a token has [ERC-223](./eip-223.md) wrapper in the Token Converter it does not automatically mean the origin is fully compatible with the [ERC-20](./eip-20.md) standard and methods of introspection must be used to determine the origins compatibility with any existing standard. 6. Token Converter does not verify the standard of a provided token when it is asked to create a wrapper for it due to the lack of reliable standard introspection method. It is possible to call `createERC20Wrapper` function and provide an address of an existing [ERC-20](./eip-20.md) token. The Token Converter will successfully create a [ERC-20](./eip-20.md) wrapper for that [ERC-20](./eip-20.md) original token. It is also possible to create a [ERC-223](./eip-223.md) wrapper for that exact original [ERC-20](./eip-20.md) token. This doesn't pose any threat to the workflow of the Converter but it must be taken into account that any token regardless of it's original standard may have up to two wrappers created by the Converter, one for each standard. Any wrapper token must have exactly one origin. It is not possible to create a wrapper for a wrapper. +7. The Token Converter only holds the original tokens that were deposited during the conversion process and it assumes that tokens do not decay over time and the token balance of the Converter does not decrease on its own. If some token implements burning logic or decaying supply and it may impact the balance of the Converter then the Converter must not be used to deploy an alternative version of that token as it will not be able to guarantee that there is enough tokens for the conversion at any time. ## Copyright From 8b455f535d9fdf6c9b0dca3cf1fe26aa8a0cb92a Mon Sep 17 00:00:00 2001 From: Dexaran Date: Sat, 28 Sep 2024 00:21:29 +0100 Subject: [PATCH 13/31] Rationale clarification. --- ERCS/erc-7417.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 6fd3652847..d6ad90b238 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -197,15 +197,27 @@ In order to convert [ERC-20](./eip-20.md) tokens to [ERC-223](./eip-223.md) the ## Rationale - +The implementation of this service is an alternative to convincing each token developer to choose an alternative standard at the moment of the token deployment or during the development stage of their project. With this service there will be no need to choose one standard and stick with it as every token can be available in both concurrently. -TBD +The implementation of this Token Converter service is supposed to be a contract deployed on Ethereum mainnet once and forever. It's address will be provided in the text of this proposal as to avoid any potential trust issues and assure the developers that the service they are interacting with is exactly the one which drives the conversion process of existing tokens. + +All the [ERC-223](./eip-223.md) tokens created by the Token Converter will be identical in a way that they all implement the same functions, which return the same values and there is no ambiguity there. This helps to avoid problems where a token deployed during the early stage of a token standard adoption may implement it improperly or there can be an ambiguity in the standard itself that would allow developers to implement tokens of one standard in different ways. + +For example it was a common case with [ERC-20](./eip-20.md) where developers could implement custom logic of the `transfer` function and mess the return values. The [ERC-20](./eip-20.md) specification declares that a `transfer` function MUST return a `bool` value, however in practice we have three different types of [ERC-20](./eip-20.md) tokens which are not compatible with each other: + +1. [ERC-20](./eip-20.md) tokens that return `true` on success and revert on an error. +2. [ERC-20](./eip-20.md) tokens that return `true` on success and `false` on an error without reverting the transaction. +3. [ERC-20](./eip-20.md) tokens that don't have return values and revert on an error. + +Technically the third category of tokens is not compatible with [ERC-20](./eip-20.md) standard. However, USDT token deployed on Ethereum mainnet at `0xdac17f958d2ee523a2206206994597c13d831ec7` address does not implement return values and it is one of the most used tokens and it is not an option to deny supporting USDT due to it's improper implementation of the standard. + +The Token Converter eliminates the issue where different development teams may implement the standard with slight modifications and result in a situation where we would have different versions of the same standard on the mainnet. + +At the same time the Converter enables the concurrent token support in other smart-contracts, such as decentralized exchanges. The Converter can guarantee that a pair of two tokens one of which is a wrapper for another is in fact the same token that can be converted from one standard to another at any time. This enables the creation of liquidity pools where two different tokens are dealt with as if they were one token while in fact these are exactly one token available in different standards. ## Backwards Compatibility From 0ebbb4857cbd640bd1dfdaed172096d8d3faf41e Mon Sep 17 00:00:00 2001 From: Dexaran Date: Sat, 28 Sep 2024 00:33:47 +0100 Subject: [PATCH 14/31] Update erc-7417.md --- ERCS/erc-7417.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index d6ad90b238..90319f30a6 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -19,7 +19,11 @@ In order to perform the conversion, a user must deposit tokens of one standard t ## Motivation -When an ERC-20 contract is upgraded, finding the new address introduces risk. This proposal creates a service that performs token conversion and prevents potentially unsafe implementations from spreading. +This proposal introduces a concept of token standard upgrading procedure driven by a specialized smart-contract which can convert tokens of one standard to another at any time as well as create an alternative version of any existing token of the older standard. + +Currently some tokens are available on different chains in different standards, for example most exchanges support [ERC-20](./eip-20.md) USDT, TRX USDT, BEP-20 USDT and all this tokens are in fact the same USDT token. This proposal is intended to introduce a concept where there can be a [ERC-20](./eip-20.md) USDT and [ERC-223](./eip-223.md) USDT available on Ethereum mainnet at the same time and both can co-exist. + +The address of the deployed Token Converter must be described here as to solve the trust issues for the token developers and help them figure out a proper way of interacting with the Converter. ## Specification From d112bd74ad0482456554865b61f5d3409d6c9c32 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Sat, 28 Sep 2024 00:35:51 +0100 Subject: [PATCH 15/31] Update erc-7417.md --- ERCS/erc-7417.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 90319f30a6..5f2af30f98 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -798,7 +798,7 @@ contract TokenStandardConverter is IERC223Recipient ## Security Considerations 1. While it is possible to implement a service that converts any token standard to any other standard - it is better to keep different standard convertors separate from one another as different standards may contain specific logic and therefore require different conversion approach. This proposal focuses on [ERC-20](./eip-20.md) and [ERC-223](./eip-223.md) upgradeability. -2. [ERC-20](./eip-20.md) tokens can be deposited to any contract directly with `transfer` function. This may result in a permanent loss of tokens because it is not possible to recognize this transaction on the recipients side. Therefore wrapper-ERC20 tokens are prone to this problem as they are compatible with the [ERC-20](./eip-20.md) standard. `rescueERC20` function is implemented to address this problem. +2. [ERC-20](./eip-20.md) tokens can be deposited to any contract directly with `transfer` function. This may result in a permanent loss of tokens because it is not possible to recognize this transaction on the recipients side. Therefore wrapper-ERC-20 tokens are prone to this problem as they are compatible with the [ERC-20](./eip-20.md) standard. `rescueERC20` function is implemented to address this problem. 3. Token Converter relies on [ERC-20](./eip-20.md) `approve` & `transferFrom` method of depositing assets. Any related issues must be taken into account. `approve` and `transferFrom` are two separate transactions so it is required to make sure `approval` was successful before relying on `transferFrom`. 4. This is a common practice for UI services to prompt a user to issue unlimited `approval` on any contract that may withdraw tokens from the user. This puts users funds at risk and therefore is not recommended. 5. There is no reliable token standard introspection method available that could guarantee that a token implements a particular token standard. It is possible to artificially construct a token that will pretend it is a [ERC-20](./eip-20.md) token that implements `approve & transferFrom` but at the same time implements [ERC-223](./eip-223.md) logic of transferring via `transfer` function. It can be possible to create a [ERC-223](./eip-223.md) wrapper for this [ERC-20](./eip-20.md)-[ERC-223](./eip-223.md) hybrid implementation in the Token Converter. This doesn't pose any threat for the workflow of the Token Converter itself but it must be taken into account that if a token has [ERC-223](./eip-223.md) wrapper in the Token Converter it does not automatically mean the origin is fully compatible with the [ERC-20](./eip-20.md) standard and methods of introspection must be used to determine the origins compatibility with any existing standard. From a023de8309a627f68691b49ae1991b19a7de35d4 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Sat, 28 Sep 2024 00:46:39 +0100 Subject: [PATCH 16/31] Update erc-7417.md --- ERCS/erc-7417.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 5f2af30f98..ff35542f35 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -4,7 +4,7 @@ title: Token Converter description: Smart-contract service that converts token of one ERC version to another author: Dexaran (@Dexaran) discussions-to: https://ethereum-magicians.org/t/token-standard-converter/15252 -status: Draft +status: Review type: Standards Track category: ERC created: 2023-07-27 From ea327e982532085ac5301121b2b666b35afcd05b Mon Sep 17 00:00:00 2001 From: Dexaran Date: Tue, 8 Oct 2024 21:43:14 +0100 Subject: [PATCH 17/31] Fixed wrapper token naming bug --- ERCS/erc-7417.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index ff35542f35..fec14e00c8 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -466,7 +466,7 @@ contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue } function name() public view override returns (string memory) { return IERC20Metadata(wrapper_for).name(); } - function symbol() public view override returns (string memory) { return string.concat(IERC20Metadata(wrapper_for).name(), "223"); } + function symbol() public view override returns (string memory) { return string.concat(IERC20Metadata(wrapper_for).symbol(), "223"); } function decimals() public view override returns (uint8) { return IERC20Metadata(wrapper_for).decimals(); } function standard() public pure returns (uint32) { return 223; } function origin() public view returns (address) { return wrapper_for; } From 6bc176eacb24bf048bf1831f3a35b87d36f2a3b3 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Wed, 9 Oct 2024 18:19:31 +0100 Subject: [PATCH 18/31] Moved Rationale section to Motivation as recommended by an EIP editor --- ERCS/erc-7417.md | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index fec14e00c8..70a1baed57 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -19,12 +19,34 @@ In order to perform the conversion, a user must deposit tokens of one standard t ## Motivation -This proposal introduces a concept of token standard upgrading procedure driven by a specialized smart-contract which can convert tokens of one standard to another at any time as well as create an alternative version of any existing token of the older standard. +This proposal introduces a concept of a token standard upgrading procedure driven by a specialized smart-contract which can convert tokens of one standard to another at any time. -Currently some tokens are available on different chains in different standards, for example most exchanges support [ERC-20](./eip-20.md) USDT, TRX USDT, BEP-20 USDT and all this tokens are in fact the same USDT token. This proposal is intended to introduce a concept where there can be a [ERC-20](./eip-20.md) USDT and [ERC-223](./eip-223.md) USDT available on Ethereum mainnet at the same time and both can co-exist. +Currently some tokens are available on different chains in different standards, for example most exchanges support [ERC-20](./eip-20.md) USDT, TRX USDT, BEP-20 USDT and all this tokens are in fact the same USDT token. This proposal is intended to introduce a concept where there can be a [ERC-20](./eip-20.md) USDT and [ERC-223](./eip-223.md) USDT available on Ethereum mainnet at the same time and these would be freely interchangeable. The address of the deployed Token Converter must be described here as to solve the trust issues for the token developers and help them figure out a proper way of interacting with the Converter. +As Ethereum already has an established ecosystem of tokens and [ERC-20](./eip-20.md) is the most adopted standard at the moment the lack of defined migration processes can be a bottleneck for newer standards adoption. This proposal addresses the problem of coordinating the upgrading process and addresses the backwards compatibility problems for [ERC-20](./eip-20.md) and [ERC-223](./eip-223.md) tokens. + +The Token Converter is supposed to allow anyone to create an alternative version of an existing token implemented in a different standard. This proposal focuses on [ERC-20](./eip-20.md) and [ERC-223](./eip-223.md) standards and takes into account the specifics of this particular token standards. It is assumed that the most common case would be creation of [ERC-223](./eip-223.md) version for an existing [ERC-20](./eip-20.md) token. + +The implementation of this service is an alternative to convincing each token developer to choose an alternative standard at the moment of the token deployment or during the development stage of their project. With this service there will be no need to choose one standard and stick with it as every token can be available in both concurrently. + +The implementation of this Token Converter service is supposed to be a contract deployed on Ethereum mainnet once and forever. It's address will be provided in the text of this proposal as to avoid any potential trust issues and assure the developers that the service they are interacting with is exactly the one which drives the conversion process of existing tokens. + +All the [ERC-223](./eip-223.md) tokens created by the Token Converter will be identical in a way that they all implement the same functions, which return the same values and there is no ambiguity there. This helps to avoid problems where a token deployed during the early stage of a token standard adoption may implement it improperly or there can be an ambiguity in the standard itself that would allow developers to implement tokens of one standard in different ways. + +For example it was a common case with [ERC-20](./eip-20.md) where developers could implement custom logic of the `transfer` function and mess the return values. The [ERC-20](./eip-20.md) specification declares that a `transfer` function MUST return a `bool` value, however in practice we have three different types of [ERC-20](./eip-20.md) tokens which are not compatible with each other: + +1. [ERC-20](./eip-20.md) tokens that return `true` on success and revert on an error. +2. [ERC-20](./eip-20.md) tokens that return `true` on success and `false` on an error without reverting the transaction. +3. [ERC-20](./eip-20.md) tokens that don't have return values and revert on an error. + +Technically the third category of tokens is not compatible with [ERC-20](./eip-20.md) standard. However, USDT token deployed on Ethereum mainnet at `0xdac17f958d2ee523a2206206994597c13d831ec7` address does not implement return values and it is one of the most used tokens and it is not an option to deny supporting USDT due to it's improper implementation of the standard. + +The Token Converter eliminates the issue where different development teams may implement the standard with slight modifications and result in a situation where we would have different versions of the same standard on the mainnet. + +At the same time the Converter enables the concurrent token support in other smart-contracts, such as decentralized exchanges. The Converter can guarantee that a pair of two tokens one of which is a wrapper for another is in fact the same token that can be converted from one standard to another at any time. This enables the creation of liquidity pools where two different tokens are dealt with as if they were one token. + ## Specification The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. @@ -201,28 +223,6 @@ In order to convert [ERC-20](./eip-20.md) tokens to [ERC-223](./eip-223.md) the ## Rationale -As Ethereum already has an established ecosystem of tokens and [ERC-20](./eip-20.md) is the most adopted standard at the moment the lack of defined migration processes can be a bottleneck for newer standards adoption. This proposal addresses the problem of coordinating the upgrading process and addresses the backwards compatibility problems for [ERC-20](./eip-20.md) and [ERC-223](./eip-223.md) tokens. - -The Token Converter is supposed to allow anyone to create an alternative version of an existing token implemented in a different standard. This proposal focuses on [ERC-20](./eip-20.md) and [ERC-223](./eip-223.md) standards and takes into account the specifics of this particular token standards. It is assumed that the most common case would be creation of [ERC-223](./eip-223.md) version for an existing [ERC-20](./eip-20.md) token. - -The implementation of this service is an alternative to convincing each token developer to choose an alternative standard at the moment of the token deployment or during the development stage of their project. With this service there will be no need to choose one standard and stick with it as every token can be available in both concurrently. - -The implementation of this Token Converter service is supposed to be a contract deployed on Ethereum mainnet once and forever. It's address will be provided in the text of this proposal as to avoid any potential trust issues and assure the developers that the service they are interacting with is exactly the one which drives the conversion process of existing tokens. - -All the [ERC-223](./eip-223.md) tokens created by the Token Converter will be identical in a way that they all implement the same functions, which return the same values and there is no ambiguity there. This helps to avoid problems where a token deployed during the early stage of a token standard adoption may implement it improperly or there can be an ambiguity in the standard itself that would allow developers to implement tokens of one standard in different ways. - -For example it was a common case with [ERC-20](./eip-20.md) where developers could implement custom logic of the `transfer` function and mess the return values. The [ERC-20](./eip-20.md) specification declares that a `transfer` function MUST return a `bool` value, however in practice we have three different types of [ERC-20](./eip-20.md) tokens which are not compatible with each other: - -1. [ERC-20](./eip-20.md) tokens that return `true` on success and revert on an error. -2. [ERC-20](./eip-20.md) tokens that return `true` on success and `false` on an error without reverting the transaction. -3. [ERC-20](./eip-20.md) tokens that don't have return values and revert on an error. - -Technically the third category of tokens is not compatible with [ERC-20](./eip-20.md) standard. However, USDT token deployed on Ethereum mainnet at `0xdac17f958d2ee523a2206206994597c13d831ec7` address does not implement return values and it is one of the most used tokens and it is not an option to deny supporting USDT due to it's improper implementation of the standard. - -The Token Converter eliminates the issue where different development teams may implement the standard with slight modifications and result in a situation where we would have different versions of the same standard on the mainnet. - -At the same time the Converter enables the concurrent token support in other smart-contracts, such as decentralized exchanges. The Converter can guarantee that a pair of two tokens one of which is a wrapper for another is in fact the same token that can be converted from one standard to another at any time. This enables the creation of liquidity pools where two different tokens are dealt with as if they were one token while in fact these are exactly one token available in different standards. - ## Backwards Compatibility This proposal is supposed to eliminate the backwards compatibility concerns for different token standards making them interchangeable and interoperable. From 53b7c20ba04da2b5271450e2fc415da3eb640b9c Mon Sep 17 00:00:00 2001 From: Dexaran Date: Wed, 9 Oct 2024 18:25:25 +0100 Subject: [PATCH 19/31] Headers of the Rationale design choices added --- ERCS/erc-7417.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 70a1baed57..ae3393f4fc 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -223,6 +223,12 @@ In order to convert [ERC-20](./eip-20.md) tokens to [ERC-223](./eip-223.md) the ## Rationale +### Support of [ERC-223](./eip-223.md) original tokens + +### Modified transfer events of the [ERC-223](./eip-223.md) token + +### `standard()` function for the introspection + ## Backwards Compatibility This proposal is supposed to eliminate the backwards compatibility concerns for different token standards making them interchangeable and interoperable. From e62088b9a3f8544ed6fd76ce83dba75b6b51be29 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Wed, 9 Oct 2024 18:47:06 +0100 Subject: [PATCH 20/31] Clarification of the Converter system structure --- ERCS/erc-7417.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index ae3393f4fc..4477851707 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -51,11 +51,11 @@ At the same time the Converter enables the concurrent token support in other sma The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. -The Token Converter system comprises two main parts: +The Token Converter system comprises two main components: -- Converter contract +- Converter contract. -- [ERC-223](./eip-223.md) wrapper contract for each [ERC-20](./eip-20.md) token +- Wrapper contracts. Each original token can have exactly one wrapper of each standard. Converter contract can deploy new [ERC-223](./eip-223.md) wrapper contracts for any [ERC-20](./eip-20.md) token that does not have a [ERC-223](./eip-223.md) wrapper currently. There MUST be exactly one [ERC-223](./eip-223.md) wrapper for each [ERC-20](./eip-20.md) token. From 74b9e2572b9524b38d085e6109cdec0f9baf3462 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Wed, 9 Oct 2024 19:05:27 +0100 Subject: [PATCH 21/31] Clarification on the usage of the wrappers --- ERCS/erc-7417.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 4477851707..e92d9ada20 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -215,7 +215,7 @@ In order to convert [ERC-20](./eip-20.md) tokens to [ERC-223](./eip-223.md) the 2. Wait for the transaction with `approve` to be submitted to the blockchain. 3. Call the `convertERC20toERC223` function of the Token Converter contract. -### Converting [ERC-223](./eip-223.md) tokens back to [ERC-20](./eip-20.md) +### Converting [ERC-223](./eip-223.md) wrapper tokens back to [ERC-20](./eip-20.md) In order to convert [ERC-20](./eip-20.md) tokens to [ERC-223](./eip-223.md) the token holder should: From 87d7374e43c06cbd04226b79e27af088da0efb22 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 11 Oct 2024 20:30:19 +0100 Subject: [PATCH 22/31] Rationale: support of original ERC-223s --- ERCS/erc-7417.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index e92d9ada20..27273422ca 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -225,6 +225,12 @@ In order to convert [ERC-20](./eip-20.md) tokens to [ERC-223](./eip-223.md) the ### Support of [ERC-223](./eip-223.md) original tokens +Two methods of implementing a Token Converter service were considered: (1) a converter that can only create [ERC-223](./eip-223.md) versions of the existing [ERC-20](./eip-20.md) tokens, and (2) a converter that can create both versions ([ERC-20](./eip-20.md) and [ERC-223](./eip-223.md)) of any original token. + +The first approach would encourage developers to always deploy an original token as [ERC-20](./eip-20.md) and then create it's [ERC-223](./eip-223.md) version in the converter. If it would happen that some developers may consider [ERC-223](./eip-223.md) as their original standard then they would be left with the problem of creating their custom [ERC-20](./eip-20.md) version of the token. In addition, if any third party contracts like liquidity pools are using the proposed Token Converter to ensure that a token can be listed on a DEX with two versions and both can be combined within one pool - then such contract would not be able to recognize any original [ERC-223](./eip-223.md) token and it's [ERC-20](./eip-20.md) version as a valid pair of contracts that represent one token available in two standards. + +For that reason it was decided to go with the second approach where the Converter can create [ERC-20](./eip-20.md) wrappers for original [ERC-223](./eip-223.md) tokens. + ### Modified transfer events of the [ERC-223](./eip-223.md) token ### `standard()` function for the introspection From 56dfc0103fd8e8617cac974482f17631c96b2787 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 11 Oct 2024 21:15:50 +0100 Subject: [PATCH 23/31] Rationale: approve & transferFrom support in ERC-223 wrappers --- ERCS/erc-7417.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 27273422ca..200d53f289 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -231,6 +231,16 @@ The first approach would encourage developers to always deploy an original token For that reason it was decided to go with the second approach where the Converter can create [ERC-20](./eip-20.md) wrappers for original [ERC-223](./eip-223.md) tokens. +### Support of `approve` & `transferFrom` functions in the [ERC-223](./eip-223.md) wrapper tokens + +This functions are superfluous for a [ERC-223](./eip-223.md) token since the `transfer` function can be used to deposit tokens of this standard to contracts. The current ecosystem is built for [ERC-20](./eip-20.md) tokens however and there are plenty of multisig contracts that rely on accepting tokens deposited without any callback with an assumption that it is not necessary for a multisig to count the amount of tokens it stores. + +There can be any other contracts and scenarios where it would be necessary to deposit a token to a contract which is relying on an assumption that tokens are deposited without invoking a callback in the recipient. As the result we can expect that any original deployed [ERC-223](./eip-223.md) tokens will support this functions, as token developers strive for backward compatibility with the existing ecosystem. In order to make tokens deployed by the converter a reference implementation for developers that can be used without any modifications it was decided to support this functions in the [ERC-223](./eip-223.md) wrapper contracts. + +`transferFrom` function does not support error handling and this needs to be taken into account. It is possible to deposit tokens to a contract which is not designed to receive them by approving X tokens to your own address and then calling a `transferFrom(self, contract, X)`. The tokens will be deposited regardless of whether the recipient contract is designed to hold/receive tokens or not. The tokens may get permanently stuck if the recipient contract did not implement the extraction functions. The `approve` & `transferFrom` function is not the default method of token transferring however and it is not directly used by any wallets and any other software that manages tokens. The `transfer` function (which is safe) is used instead. The `transferFrom` function is supposed to be invoked by a contract to pull tokens from the approver. + +As the result, the `approve` & `transferFrom` transferring method must be avoided with [ERC-223](./eip-223.md) tokens whenever possible. + ### Modified transfer events of the [ERC-223](./eip-223.md) token ### `standard()` function for the introspection From 43e8a00b72ac47b8f39a3a85e5c7ae8e6706bc19 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 11 Oct 2024 21:22:50 +0100 Subject: [PATCH 24/31] Rationale: event signatures --- ERCS/erc-7417.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 200d53f289..8dc6657e5a 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -243,8 +243,14 @@ As the result, the `approve` & `transferFrom` transferring method must be avoide ### Modified transfer events of the [ERC-223](./eip-223.md) token +The pure [ERC-223](./eip-223.md) token implementation has the following event emitted on a token transfer: `event Transfer(address indexed _from, address indexed _to, uint256 _value, bytes _data)`. This events are different from ones emitted by [ERC-20](./eip-20.md) tokens and may not be properly recognized by existing blockchain explorers, wallets and other services that browse token transfers history. + +It was considered that events are not an important part of the standard as these do not affect the logic of the token, it's workflow and it's security. When developing the Converter it was decided to prioritize compatibility with existing ecosystem. + ### `standard()` function for the introspection + + ## Backwards Compatibility This proposal is supposed to eliminate the backwards compatibility concerns for different token standards making them interchangeable and interoperable. From 0fbf3190bde3dd79bd4da4ec310464fff27083d0 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 11 Oct 2024 21:37:11 +0100 Subject: [PATCH 25/31] Rationale: introspection --- ERCS/erc-7417.md | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 8dc6657e5a..3fd3a272a8 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -247,9 +247,53 @@ The pure [ERC-223](./eip-223.md) token implementation has the following event em It was considered that events are not an important part of the standard as these do not affect the logic of the token, it's workflow and it's security. When developing the Converter it was decided to prioritize compatibility with existing ecosystem. -### `standard()` function for the introspection +### `standard()` function usage for the introspection +The main existing method of introspection is currently [ERC-165](./eip-165.md) which inspects the signatures of functions implemented in a contract. It is not possible to differentiate an [ERC-20](./eip-20.md) token from an [ERC-223](./eip-223.md) token by just browsing functions that they implement without digging their internal logic. +Here is a token and it is not possible to identify if it should be dealt with as [ERC-20](./eip-20.md) or [ERC-223](./eip-223.md) because it depends on the actual implemenation of it's `transfer` function logic. + +```solidity +abstract contract Token { + function name() external virtual returns (string memory); + function symbol() external virtual returns (string memory); + function decimals() external virtual returns (uint8); + + function transfer(address, uint256) external virtual returns (bool); + function approve(address, uint256) external virtual returns (bool); + function transferFrom(address, address, uint256) external virtual returns (bool); +} +``` + +In case of this implementation the token will behave as [ERC-20](./eip-20.md): + +```solidity + function transfer(address _to, uint256 _amount) external virtual returns (bool) + { + balances[msg.sender] -= _amount; + balances[_to] += _amount; + } +} +``` + +In case of this implementation the token will behave as [ERC-223](./eip-223.md): + +```solidity + function transfer(address _to, uint256 _amount) external virtual returns (bool) + { + balances[msg.sender] -= _amount; + balances[_to] += _amount; + if(_to.isContract()) + { + IERC223Recipient(_to).tokenReceived(msg.sender, _amount, hex"000000"); + } + } +} +``` + +Also, there are plenty of tokens that do not implement [ERC-165](./eip-165.md) introspection at all. As the result it was decided to implement a special `standard() returns (uint32)` function in all [ERC-223](./eip-223.md) wrappers created by the Converter and assume that original [ERC-223](./eip-223.md) tokens may decalre themselves as [ERC-223](./eip-223.md) by implementing the same function as well. It is assumed that if a token does not implement this function then it is [ERC-20](./eip-20.md). + +This method of token standard introspection is more precise than [ERC-165](./eip-165.md). ## Backwards Compatibility From 62bdf2deb479199a2b219b268417fe0e9fabaa46 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Wed, 23 Oct 2024 14:16:28 +0100 Subject: [PATCH 26/31] Gas forwarding fix applied as reported by a security auditor --- ERCS/erc-7417.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 3fd3a272a8..883fbd142a 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -305,8 +305,6 @@ This service is the first of its kind and therefore does not have any backwards ```solidity -// SPDX-License-Identifier: GPL-3.0 - pragma solidity =0.8.19; library Address { @@ -505,10 +503,13 @@ contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue { balances[msg.sender] = balances[msg.sender] - _value; balances[_to] = balances[_to] + _value; + if (msg.value > 0) + { + (bool sent, bytes memory data) = _to.call{value: msg.value}(""); + } if(Address.isContract(_to)) { IERC223Recipient(_to).tokenReceived(msg.sender, _value, _data); } - if (msg.value > 0) payable(_to).transfer(msg.value); emit Transfer(msg.sender, _to, _value, _data); emit Transfer(msg.sender, _to, _value); // Old ERC-20 compatible event. Added for backwards compatibility reasons. @@ -538,7 +539,7 @@ contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue } function name() public view override returns (string memory) { return IERC20Metadata(wrapper_for).name(); } - function symbol() public view override returns (string memory) { return string.concat(IERC20Metadata(wrapper_for).symbol(), "223"); } + function symbol() public view override returns (string memory) { return string.concat(IERC20Metadata(wrapper_for).name(), "223"); } function decimals() public view override returns (uint8) { return IERC20Metadata(wrapper_for).decimals(); } function standard() public pure returns (uint32) { return 223; } function origin() public view returns (address) { return wrapper_for; } From 5158f1b670b1ba9d527cc301e8948f039bd2b63c Mon Sep 17 00:00:00 2001 From: Dexaran Date: Wed, 23 Oct 2024 14:40:35 +0100 Subject: [PATCH 27/31] Adjustments to solve "EIP must not be in backticks" trouble --- ERCS/erc-7417.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 883fbd142a..aa11510b3f 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -394,7 +394,7 @@ interface standardERC20 } /** - * @dev Interface of the ERC20 standard as defined in the EIP. + * @dev Interface of the ERC20 standard. */ interface IERC223WrapperToken { function name() external view returns (string memory); @@ -435,9 +435,9 @@ interface IERC20WrapperToken { contract ERC20Rescue { - // ERC-20 tokens can get stuck on a contracts balance due to lack of error handling. + // ERC20 tokens can get stuck on a contracts balance due to lack of error handling. // - // The author of the ERC-7417 can extract ERC-20 tokens if they are mistakenly sent + // The author of the ERC7417 can extract ERC20 tokens if they are mistakenly sent // to the wrapper-contracts balance. // Contact dexaran@ethereumclassic.org address public extractor = 0x01000B5fE61411C466b70631d7fF070187179Bbf; @@ -492,7 +492,7 @@ contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue } /** - * @dev Standard ERC-223 transfer function. + * @dev Standard ERC223 transfer function. * Calls _to if it is a contract. Does not transfer tokens to contracts * which do not explicitly declare the tokenReceived function. * @param _to - transfer recipient. Can be contract or EOA. @@ -511,14 +511,14 @@ contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue IERC223Recipient(_to).tokenReceived(msg.sender, _value, _data); } emit Transfer(msg.sender, _to, _value, _data); - emit Transfer(msg.sender, _to, _value); // Old ERC-20 compatible event. Added for backwards compatibility reasons. + emit Transfer(msg.sender, _to, _value); // Old ERC20 compatible event. Added for backwards compatibility reasons. return true; } /** - * @dev Standard ERC-223 transfer function without _data parameter. It is supported for - * backwards compatibility with ERC-20 services. + * @dev Standard ERC223 transfer function without _data parameter. It is supported for + * backwards compatibility with ERC20 services. * Calls _to if it is a contract. Does not transfer tokens to contracts * which do not explicitly declare the tokenReceived function. * @param _to - transfer recipient. Can be contract or EOA. @@ -533,13 +533,13 @@ contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue IERC223Recipient(_to).tokenReceived(msg.sender, _value, _empty); } emit Transfer(msg.sender, _to, _value, _empty); - emit Transfer(msg.sender, _to, _value); // Old ERC-20 compatible event. Added for backwards compatibility reasons. + emit Transfer(msg.sender, _to, _value); // Old ERC20 compatible event. Added for backwards compatibility reasons. return true; } function name() public view override returns (string memory) { return IERC20Metadata(wrapper_for).name(); } - function symbol() public view override returns (string memory) { return string.concat(IERC20Metadata(wrapper_for).name(), "223"); } + function symbol() public view override returns (string memory) { return string.concat(IERC20Metadata(wrapper_for).symbol(), "223"); } function decimals() public view override returns (uint8) { return IERC20Metadata(wrapper_for).decimals(); } function standard() public pure returns (uint32) { return 223; } function origin() public view returns (address) { return wrapper_for; } @@ -569,7 +569,7 @@ contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue _totalSupply -= _quantity; } - // ERC-20 functions for backwards compatibility. + // ERC20 functions for backwards compatibility. function allowance(address owner, address spender) public view virtual returns (uint256) { return allowances[owner][spender]; @@ -578,7 +578,7 @@ contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue function approve(address _spender, uint _value) public returns (bool) { // Safety checks. - require(_spender != address(0), "ERC-223: Spender error."); + require(_spender != address(0), "ERC223: Spender error."); allowances[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); @@ -588,7 +588,7 @@ contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue function transferFrom(address _from, address _to, uint _value) public returns (bool) { - require(allowances[_from][msg.sender] >= _value, "ERC-223: Insufficient allowance."); + require(allowances[_from][msg.sender] >= _value, "ERC223: Insufficient allowance."); balances[_from] -= _value; allowances[_from][msg.sender] -= _value; @@ -662,7 +662,7 @@ contract ERC20WrapperToken is IERC20, ERC165, ERC20Rescue // Safety checks. - require(_spender != address(0), "ERC-20: Spender error."); + require(_spender != address(0), "ERC20: Spender error."); allowances[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); @@ -672,7 +672,7 @@ contract ERC20WrapperToken is IERC20, ERC165, ERC20Rescue function transferFrom(address _from, address _to, uint _value) public returns (bool) { - require(allowances[_from][msg.sender] >= _value, "ERC-20: Insufficient allowance."); + require(allowances[_from][msg.sender] >= _value, "ERC20: Insufficient allowance."); balances[_from] -= _value; allowances[_from][msg.sender] -= _value; @@ -689,7 +689,7 @@ contract TokenStandardConverter is IERC223Recipient event ERC223WrapperCreated(address indexed _token, address indexed _ERC223Wrapper); event ERC20WrapperCreated(address indexed _token, address indexed _ERC20Wrapper); - mapping (address => ERC223WrapperToken) public erc223Wrappers; // A list of token wrappers. First one is ERC-20 origin, second one is ERC-223 version. + mapping (address => ERC223WrapperToken) public erc223Wrappers; // A list of token wrappers. First one is ERC20 origin, second one is ERC223 version. mapping (address => ERC20WrapperToken) public erc20Wrappers; mapping (address => address) public erc223Origins; @@ -717,10 +717,10 @@ contract TokenStandardConverter is IERC223Recipient } function predictWrapperAddress(address _token, - bool _isERC20 // Is the provided _token a ERC-20 or not? - // If it is set as ERC-20 then we will predict the address of a - // ERC-223 wrapper for that token. - // Otherwise we will predict ERC-20 wrapper address. + bool _isERC20 // Is the provided _token a ERC20 or not? + // If it is set as ERC20 then we will predict the address of a + // ERC223 wrapper for that token. + // Otherwise we will predict ERC20 wrapper address. ) view external returns (address) { bytes memory _bytecode; @@ -746,8 +746,8 @@ contract TokenStandardConverter is IERC223Recipient { require(erc223Origins[msg.sender] == address(0), "Error: creating wrapper for a wrapper token."); // There are two possible cases: - // 1. A user deposited ERC-223 origin token to convert it to ERC-20 wrapper - // 2. A user deposited ERC-223 wrapper token to unwrap it to ERC-20 origin. + // 1. A user deposited ERC223 origin token to convert it to ERC20 wrapper + // 2. A user deposited ERC223 wrapper token to unwrap it to ERC20 origin. if(erc20Origins[msg.sender] != address(0)) { @@ -763,8 +763,8 @@ contract TokenStandardConverter is IERC223Recipient } // Otherwise origin for the sender token doesn't exist // There are two possible cases: - // 1. ERC-20 wrapper for the deposited token exists - // 2. ERC-20 wrapper for the deposited token doesn't exist and must be created. + // 1. ERC20 wrapper for the deposited token exists + // 2. ERC20 wrapper for the deposited token doesn't exist and must be created. else if(address(erc20Wrappers[msg.sender]) == address(0)) { // Create ERC-20 wrapper if it doesn't exist. From dd9fa3d239d4991443435b893282f94820680001 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Wed, 23 Oct 2024 14:42:14 +0100 Subject: [PATCH 28/31] Update erc-7417.md --- ERCS/erc-7417.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index aa11510b3f..b0b8672c56 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -59,7 +59,7 @@ The Token Converter system comprises two main components: Converter contract can deploy new [ERC-223](./eip-223.md) wrapper contracts for any [ERC-20](./eip-20.md) token that does not have a [ERC-223](./eip-223.md) wrapper currently. There MUST be exactly one [ERC-223](./eip-223.md) wrapper for each [ERC-20](./eip-20.md) token. -Converter contract MUST accept deposits of [ERC-20](./eip-20.md) tokens and send [ERC-223](./eip-223.md) tokens to the depositor at 1:1 ratio. Upon depositing 1234 units of `ERC-20 token_A` the depositor MUST receive exactly 1234 units of `ERC-223 token_A`. This is done by issuing new [ERC-223](./eip-223.md) tokens at the moment of [ERC-20](./eip-20.md) deposit. The original [ERC-20](./eip-20.md) tokens MUST be frozen in the Converter contract and available for claiming back. +Converter contract MUST accept deposits of [ERC-20](./eip-20.md) tokens and send [ERC-223](./eip-223.md) tokens to the depositor at 1:1 ratio. Upon depositing 1234 units of `ERC20 token_A` the depositor MUST receive exactly 1234 units of `ERC223 token_A`. This is done by issuing new [ERC-223](./eip-223.md) tokens at the moment of [ERC-20](./eip-20.md) deposit. The original [ERC-20](./eip-20.md) tokens MUST be frozen in the Converter contract and available for claiming back. Converter contract MUST accept deposits of [ERC-223](./eip-223.md) tokens and send [ERC-20](./eip-20.md) tokens to the depositor at 1:1 ratio. This is done by releasing the original [ERC-20](./eip-20.md) tokens at the moment of [ERC-223](./eip-223.md) deposit. The deposited [ERC-223](./eip-223.md) tokens must be burned. From 229b68fd90815bbc394cd8e0d390041ceed255e5 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Wed, 23 Oct 2024 14:47:33 +0100 Subject: [PATCH 29/31] Grammar gix & clarifications --- ERCS/erc-7417.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index b0b8672c56..1197f6b9ca 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -291,7 +291,7 @@ In case of this implementation the token will behave as [ERC-223](./eip-223.md): } ``` -Also, there are plenty of tokens that do not implement [ERC-165](./eip-165.md) introspection at all. As the result it was decided to implement a special `standard() returns (uint32)` function in all [ERC-223](./eip-223.md) wrappers created by the Converter and assume that original [ERC-223](./eip-223.md) tokens may decalre themselves as [ERC-223](./eip-223.md) by implementing the same function as well. It is assumed that if a token does not implement this function then it is [ERC-20](./eip-20.md). +Also, there are plenty of tokens that do not implement [ERC-165](./eip-165.md) introspection at all. As the result it was decided to implement a special `standard() returns (uint32)` function in all [ERC-223](./eip-223.md) wrappers created by the Converter and assume that original [ERC-223](./eip-223.md) tokens may explicityly declare themselves as [ERC-223](./eip-223.md) by implementing the same function too. It is assumed that if a token does not implement this function then it is [ERC-20](./eip-20.md). This method of token standard introspection is more precise than [ERC-165](./eip-165.md). From 1260687d0f82043ba57d9b1e1f93ac9003890fe8 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Wed, 23 Oct 2024 16:19:40 +0100 Subject: [PATCH 30/31] Removing unnecessary comment --- ERCS/erc-7417.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index 1197f6b9ca..d99a713bfc 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -576,8 +576,6 @@ contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue } function approve(address _spender, uint _value) public returns (bool) { - - // Safety checks. require(_spender != address(0), "ERC223: Spender error."); allowances[msg.sender][_spender] = _value; From b40317e0625d901b93418eaedcb25ed60e933a57 Mon Sep 17 00:00:00 2001 From: Dexaran Date: Fri, 25 Oct 2024 12:08:43 +0100 Subject: [PATCH 31/31] Fixes audit issue ef1387f9da88e168e43533cdf8462d76 --- ERCS/erc-7417.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-7417.md b/ERCS/erc-7417.md index d99a713bfc..46a2292aa1 100644 --- a/ERCS/erc-7417.md +++ b/ERCS/erc-7417.md @@ -305,6 +305,7 @@ This service is the first of its kind and therefore does not have any backwards ```solidity + pragma solidity =0.8.19; library Address { @@ -506,6 +507,7 @@ contract ERC223WrapperToken is IERC223, ERC165, ERC20Rescue if (msg.value > 0) { (bool sent, bytes memory data) = _to.call{value: msg.value}(""); + require(sent); } if(Address.isContract(_to)) { IERC223Recipient(_to).tokenReceived(msg.sender, _value, _data); @@ -618,7 +620,7 @@ contract ERC20WrapperToken is IERC20, ERC165, ERC20Rescue function balanceOf(address _owner) public view override returns (uint256) { return balances[_owner]; } function name() public view returns (string memory) { return IERC20Metadata(wrapper_for).name(); } - function symbol() public view returns (string memory) { return string.concat(IERC223(wrapper_for).name(), "20"); } + function symbol() public view returns (string memory) { return string.concat(IERC223(wrapper_for).symbol(), "20"); } function decimals() public view returns (uint8) { return IERC20Metadata(wrapper_for).decimals(); } function totalSupply() public view override returns (uint256) { return _totalSupply; } function origin() public view returns (address) { return wrapper_for; } @@ -724,11 +726,11 @@ contract TokenStandardConverter is IERC223Recipient bytes memory _bytecode; if(_isERC20) { - _bytecode= type(ERC223WrapperToken).creationCode; + _bytecode = type(ERC223WrapperToken).creationCode; } else { - _bytecode= type(ERC20WrapperToken).creationCode; + _bytecode = type(ERC20WrapperToken).creationCode; } bytes32 hash = keccak256(