erc20 token swap contracts

1.2k Views Asked by At

I have a question about ERC20 token swap contracts in this code example. (see below: https://solidity-by-example.org/app/erc20/). My question is that since this contract would require giving allowance to the contract beforehand. Doesn't that create a risk that the contract owner could abscond with the tokens before either party can invoke the swap function? In other words, we still require a trusted party to facilitate the transaction? If so, is there some way of limiting how allowance can be limited (e.g only transfer to certain intended party allowed?)

Thanks

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/token/ERC20/IERC20.sol";

/*
How to swap tokens

1. Alice has 100 tokens from AliceCoin, which is a ERC20 token.
2. Bob has 100 tokens from BobCoin, which is also a ERC20 token.
3. Alice and Bob wants to trade 10 AliceCoin for 20 BobCoin.
4. Alice or Bob deploys TokenSwap
5. Alice approves TokenSwap to withdraw 10 tokens from AliceCoin
6. Bob approves TokenSwap to withdraw 20 tokens from BobCoin
7. Alice or Bob calls TokenSwap.swap()
8. Alice and Bob traded tokens successfully.
*/

contract TokenSwap {
    IERC20 public token1;
    address public owner1;
    uint public amount1;
    IERC20 public token2;
    address public owner2;
    uint public amount2;

    constructor(
        address _token1,
        address _owner1,
        uint _amount1,
        address _token2,
        address _owner2,
        uint _amount2
    ) {
        token1 = IERC20(_token1);
        owner1 = _owner1;
        amount1 = _amount1;
        token2 = IERC20(_token2);
        owner2 = _owner2;
        amount2 = _amount2;
    }

    function swap() public {
        require(msg.sender == owner1 || msg.sender == owner2, "Not authorized");
        require(
            token1.allowance(owner1, address(this)) >= amount1,
            "Token 1 allowance too low"
        );
        require(
            token2.allowance(owner2, address(this)) >= amount2,
            "Token 2 allowance too low"
        );

        _safeTransferFrom(token1, owner1, owner2, amount1);
        _safeTransferFrom(token2, owner2, owner1, amount2);
    }

    function _safeTransferFrom(
        IERC20 token,
        address sender,
        address recipient,
        uint amount
    ) private {
        bool sent = token.transferFrom(sender, recipient, amount);
        require(sent, "Token transfer failed");
    }
}
1

There are 1 best solutions below

2
On

There is no way to misuse the approval in your example above.

However, if the TokenSwap contract included a function that allows an arbitrary token transfer through the contract, then yes - that could be misused.

contract TokenSwap {
    address owner = address(0x123);

    function withdraw(address token, address victim, uint256 amount) public {
        // requires approval from `victim` to `TokenSwap`
        IERC20(token).transferFrom(from, owner, amount);
    }
}

is there some way of limiting how allowance can be limited (e.g only transfer to certain intended party allowed?)

Unfortunately the ERC-20 standard does not limit the approval to how exactly are the tokens going to be spent. There might be some non-standard solutions, but if you want to implement them, they need to be implemented by both the token contract and your swap contract.