Contracts holding tokens

OK, I am working on a flashloan contract for myself to execute arbs with.

I have it mostly done, but I have kinda a stupid question I guess. I figure any solidity contract can hold not only Eth but any ERC20 as well. To that degree it works like like any eth based wallet. All tokens share the same address as the eth. (correct me if I am wrong about that).

My question is, when I decide to:

  1. check my contract’s balance, or
  2. send tokens OUT of the contract to me.

How is that done for the tokens?

I mean, if I just check ‘this.balance’ is that just giving me my ETH balance(but not the tokens)? Is it giving me everything (including tokens) denominated in ETH? Do I have to do it differently to check for balance of a specific token?

Do I need to use a differently worded function for sending the tokens out of the contract?

1 Like

Hi @CryptoEatsTheWorld,

An ERC20 is primarily a list of balances for addresses, so you need to interact with each token contract to get the balance and to transfer.

You want to call balanceOf on the token contract.

Depending on what tokens you are interacting with, you may want to use SafeERC20 wrapper.

You want to call IERC20 transfer or SafeERC20 safeTransfer


I suggest creating a simple example to play with this. You can use the following to experiment with.

You would want to have Access Control so that only a privileged address could withdraw.

:warning: The following code has not been tested nor audited, so don't use in production

SimpleToken.sol

// contracts/SimpleToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

/**
 * @title SimpleToken
 * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
 * Note they can later distribute these tokens as they wish using `transfer` and other
 * `ERC20` functions.
 * Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/examples/SimpleToken.sol
 */
contract SimpleToken is ERC20 {
    /**
     * @dev Constructor that gives msg.sender all of existing tokens.
     */
    constructor(
        string memory name,
        string memory symbol,
        uint256 initialSupply
    ) public ERC20(name, symbol) {
        _mint(msg.sender, initialSupply);
    }
}

MyContract.sol

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/GSN/Context.sol";

contract MyContract is Context, AccessControl {

    using SafeERC20 for IERC20;

    bytes32 public constant WITHDRAWER_ROLE = keccak256("WITHDRAWER_ROLE");

    constructor() public  {
        _setupRole(WITHDRAWER_ROLE, _msgSender());
    }

    function withdraw(IERC20 token, address recipient, uint256 amount) public {
        require(hasRole(WITHDRAWER_ROLE, _msgSender()), "MyContract: must have withdrawer role to withdraw");
        token.safeTransfer(recipient, amount);
    }
}

Interact

Create a SimpleToken and MyContract.
Transfer amount of SimpleTokens to MyContract
Check balance of SimpleTokens in MyContract
Withdraw tokens from MyContract
Check balances

$ npx truffle develop
Truffle Develop started at http://127.0.0.1:9545/
...

truffle(develop)> token = await SimpleToken.new("Simple Token", "SIM", "100000000000000000000")
undefined
truffle(develop)> myContract = await MyContract.new()
undefined
truffle(develop)> await token.transfer(myContract.address, "100000000000000000000")
{ tx:
...
truffle(develop)> (await token.balanceOf(myContract.address)).toString()
'100000000000000000000'
truffle(develop)> await myContract.withdraw(token.address, accounts[1], "100000000000000000")
{ tx:
...
truffle(develop)> (await token.balanceOf(accounts[1])).toString()

As an aside, to use an ERC20 in a contract, please see: Example on how to use ERC20 token in another contract

1 Like

Thanks for the quick response! Will look this over and give it a shot.

1 Like

I think all ERC20 token share the same address with the eth.

Do I need to use a differently worded function for sending the tokens out of the contract?

Yeah, I think so.

2 Likes

Hi @CryptoEatsTheWorld,

I have copied the example to: Example of withdrawing an ERC20 from another contract and made it a wikipost so anyone can update/edit.

1 Like

Don’t the tokens have to be approved first?

1 Like

Hi @CryptoEatsTheWorld,

The tokens are held by the contract so the contract can just do a transfer in our withdraw function.

OK, so are you saying that contract addresses do not need to execute the approve function to send tokens? I am confused. My understanding is that ERC20 tokens are actually always held in the contract that created them. And that when any address (external or contract) ‘has’ tokens they basically just have an entry in that ERC20 contract’s mapping with their address and amount.

So really ALL ERC20’s are held by a contract so then shouldn’t you ALWAYS have to execute the approve function before sending them?

1 Like

Hi @CryptoEatsTheWorld,

ERC20 is essentially a contract with a list of addresses and balances.

If you hold tokens (your address has a balance in the token contract), then to use your tokens in another contract you need to approve an allowance for the other contract, and then call that contract to do something and in that function it can call transferFrom to transfer from the holder (you) to anywhere as along as the amount is within its allowance.

If a contract holds tokens (the contract address has a balance in the token contract) and you want to transfer an amount to another address (without the new holder doing anything or being notified), then a function of the contract can include transfer e.g. for a withdraw.

Let me know if you need more information.

OK, let me give you a specific example to see if my understanding is right… int he context of a flash loan.

I initiate a flash loan from AAVE for X amount of Eth. My contract then receives that amount of Eth.

I then proceed to sell that Eth at Uniswap for Dai. After that swap my contract now has a Dai balance it owns.

Next the contract is supposed to sell the Dai on Kyber for Eth. My question is, am I correct in assuming that my flash loan contract has to call the “Approve” function FIRST before proceeding with the swap of that Dia?

1 Like

Hi @CryptoEatsTheWorld,

Yes. If you want to use a token with another contract (so that the other contract is aware) you first need to approve an allowance for that other contract and then you can call a function on that other contract to do stuff and that contract can call transferFrom with an amount up to the allowance that you approved.

1 Like

OK, thanks for helping to clear that up.

1 Like

A post was split to a new topic: Who pays the gas fees when withdrawing tokens from a contract