Coder Social home page Coder Social logo

tokenstandardconverter's Introduction

Ethereum Token Converter

This service is created to introduce "cross-standard" interoperability of tokens on Ethereum and EVM-compatible chains.

The most used Ethereum fungible token standard is ERC-20 and this standard is quite old. Therefore it is necessary to introduce a clear procedure of "standard upgrade" without requiring redeploying of existing token contracts. The Token Converter smart-contract can be exactly the tool that ensures smooth transition from the old standard to a newer one.

This particular implementation converts ERC-20 tokens to ERC-223 tokens. ERC-223 tokens can be converted back to the ERC-20 origin anytime.

Deployment

Converter v4 (CLO mainnet): https://explorer.callisto.network/address/0xf0ddb84596C9B52981C2bFf35c8B21d2b8FEd64c/transactions

Ethereum Mainnet v4: https://etherscan.io/address/0x1e9d6cba29e4aa4a9e2587b19d3f0e68de9b6552

Old Version

Converter v3: https://explorer.callisto.network/address/0xc68AD4DDCB3C9cAd52852E6dF7102b77c32865A5/transactions

tokenstandardconverter's People

Contributors

dexaran avatar

Stargazers

 avatar

Watchers

 avatar  avatar

tokenstandardconverter's Issues

Wrapped tokens did not take into account original decimals

If the user wraps tokens with decimals different than 18, the wrapped token will have an incorrect representation.

function createERC223Wrapper(address _erc20Token) public returns (bool)
{
require(address(erc223Wrappers[_erc20Token]) == address(0), "ERROR: Wrapper already exists.");
ERC223WrapperToken _newERC223Wrapper = new ERC223WrapperToken(_erc20Token, "ERC-223-Wrapper", "Wrapped Token", 18);
erc223Wrappers[_erc20Token] = _newERC223Wrapper;
erc20Origins[address(_newERC223Wrapper)] = _erc20Token;
return true;
}

Never use function `transfer` and `transferFrom` for arbitrary third-party tokens.

You are using the function transfer and transferFrom for any arbitrary tokens (unknown tokens). It's high severity issue.
You should use safeTransfer and safeTransferFrom function instead of them

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/f347b410cf6aeeaaf5197e1fece139c793c03b2b/contracts/token/ERC20/utils/SafeERC20.sol#L33-L46

For example, if you deploy this converter on ETH chain and users wrap USDT tokens it will stuck on the contract forever.

Moreover, functions transfer and transferFrom may return false if the sender has insufficient balance (like DAO token. You did not check the return value of transferFrom here:

IERC20(_ERC20token).transferFrom(msg.sender, address(this), _amount);

So users can mint wrapped tokens even with 0 original tokens on balance.

returned value never checked.

The function tokenReceived returns magic number:

function tokenReceived(address _from, uint _value, bytes memory _data) public override returns (bytes4)
{
require(erc20Origins[msg.sender] != address(0), "ERROR: The received token is not a ERC-223 Wrapper for any ERC-20 token.");
IERC20(erc20Origins[msg.sender]).transfer(_from, _value);
erc223Wrappers[msg.sender].burn(_value);
return 0x8943ec02;
}

But in the only place where this return value can be checked ignore it.

if(Address.isContract(_to)) {
bytes4 ret = IERC223Recipient(_to).tokenReceived(msg.sender, _value, _empty);
}

Standard event should be emitted

According to ERC223 standard on each transfer this event should be emitted:

https://github.com/Dexaran/ERC223-token-standard/blob/7c35af76f0175f1260ef1cf01e62ece787c1d086/token/ERC223/IERC223.sol#L31-L34

So uncomment this row
//emit Transfer(msg.sender, _to, _value, _data);
and here.

Developers are free to add more events if needed, so I recommend to emit
emit Transfer(msg.sender, _to, _value); (as it is, to be compatible with explorers)

but this event is unnecessary and can be removed
https://github.com/Dexaran/TokenStandardConverter/blob/5690be8dfb80903d0ea5d553f20b266baf503549/TokenConverter.sol#L112C34-L112C34

Owner can withdraw all erc20 and erc223 tokens from wrapper contract

  1. When rescuing ERC20 tokens, the _stuckTokens is calculated but is not used.
    safeTransfer(_token, msg.sender, IERC20(_token).balanceOf(address(this)));

must be:

        safeTransfer(_token, msg.sender, _stuckTokens);
  1. This function allows the owner to withdraw ERC223 tokens which were deposited to wrap to ERC20. Therefore, to protect users you should add:
        require(address(erc20Wrappers[_token]) == address(0), "ERC223 origin is not allowed");

wasting of gas in function convertERC20toERC223

You don't need to check balanceOf(msg.sender) before and after the token transfer. Removing these calls will save about 5K gas.

uint256 _callerBalance = IERC20(_ERC20token).balanceOf(msg.sender); // Safety variable.
uint256 _converterBalance = IERC20(_ERC20token).balanceOf(address(this)); // Safety variable.
IERC20(_ERC20token).transferFrom(msg.sender, address(this), _amount);
erc20Supply[_ERC20token] += _amount;
require(
IERC20(_ERC20token).balanceOf(msg.sender) + _amount == _callerBalance &&

The contract should check only own balance balanceOf(address(this)). The contract should mint the amount which it receives. Does not matter what msg.sender balance is.

Wrapper is not compatible with tokens with transfer fee

Some tokens (like reflecting tokens) take a fee on transfer. So receiver will receive fewer tokens than the sender sends.

This requirement does not allow to use such tokens

require(
IERC20(_ERC20token).balanceOf(address(this)) - _amount == _converterBalance,
"ERROR: The transfer have not subtracted tokens from callers balance.");

I would recommend replacing this part:

erc20Supply[_ERC20token] += _amount;
require(
IERC20(_ERC20token).balanceOf(address(this)) - _amount == _converterBalance,
"ERROR: The transfer have not subtracted tokens from callers balance.");

with:

        _amount = IERC20(_ERC20token).balanceOf(address(this)) - _converterBalance;
        erc20Supply[_ERC20token] += _amount;

Follow good coding practice

If you want to use this contract to publish EIP, then better to follow good code practice.

  1. Follow Style Guide.

https://docs.soliditylang.org/en/v0.8.23/style-guide.html

  1. Missing docstrings.

The contracts in the code base lack documentation. This hinders reviewers’ understanding of the code’s intention, which is fundamental to correctly assess not only security but also correctness. Additionally, docstrings improve readability and ease maintenance. They should explicitly explain the purpose or intention of the functions, the scenarios under which they can fail, the roles allowed to call them, the values returned, and the events emitted.

Consider thoroughly documenting all functions (and their parameters) that are part of the contracts’ public API. Functions implementing sensitive functionality, even if not public, should be documented as well. When writing docstrings, consider following the Ethereum Natural Specification Format (NatSpec).

https://docs.soliditylang.org/en/v0.8.23/natspec-format.html

  1. Functions not used internally could be marked as external.

It's a good coding practice to mark a function external when it's not called within the contract but only from outside the contract.

supportsInterface

In the function supportsInterface() you should use standard IERC20 interface to be recognizable by others, instead of using yours IERC20

/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
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);
}

The interface mustn't contain OPTIONAL functions (name, symbol, decimals).
https://eips.ethereum.org/EIPS/eip-20

Unnecessary code

  1. The wrapper token contract can't call transferFrom in itself, so this can be removed:
  1. The variable wrapper_for should be private (to avoid generating a useless getter function), since the getter function origin() public view returns (address) { return wrapper_for; } already exists.
  1. The variable balances should be private (to avoid generating a useless getter function), since the getter function balanceOf(address _owner) public view override returns (uint256) { return balances[_owner]; } already exists.
  1. Variable empty is not used
  1. The variables
    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;
    mapping (address => address) public erc223Origins;
    mapping (address => address) public erc20Origins;

should be private (to avoid generating useless getter functions), since the getters already exist:

function getERC20WrapperFor(address _token) public view returns (address, string memory)
{
if ( address(erc20Wrappers[_token]) != address(0) )
{
return (address(erc20Wrappers[_token]), "ERC-20");
}
return (address(0), "Error");
}
function getERC223WrapperFor(address _token) public view returns (address, string memory)
{
if ( address(erc223Wrappers[_token]) != address(0) )
{
return (address(erc223Wrappers[_token]), "ERC-223");
}
return (address(0), "Error");
}
function getERC20OriginFor(address _token) public view returns (address)
{
return (address(erc20Origins[_token]));
}
function getERC223OriginFor(address _token) public view returns (address)
{
return (address(erc223Origins[_token]));
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.