10

I have deployed an ERC20 token (contract A) and the tokens are being used for a service in my other contract (Contract B). I can easily transfer tokens from an user owned account(EOA) to Contract B after i call approve on the token Contract A to allow Contract B. However, I am not able to transfer tokens from the Contract B back to the user account. Is there any way I can achieve that?

This works when transferring tokens from EOA to Contract B.

token.transferFrom(msg.sender, this, 100)

However, the other way round doesn't seem to work.

token.transferFrom(this,msg.sender, 100)

Do i need to call any other functions on the original token contract A ?

2 Answers 2

16

Escrowing ERC20 tokens:

Your escrow contract needs to know when it received new tokens, from whom, and how many.

This is achieved by having the "seller" create two transactions:

  1. Approval: First transaction calls token contract
// tracker_0x_address is the address of the ERC20 contract they want to deposit tokens from ( ContractA )
// spender is your deployed escrow contract address

ERC20(tracker_0x_address).approve(address spender, uint tokens)
  1. Deposit: Second transaction calls a method in the escrow contract
mapping ( address => uint256 ) public balances;
deposit(uint tokens) {

  // add the deposited tokens into existing balance 
  balances[msg.sender]+= tokens;

  // transfer the tokens from the sender to this contract
  ERC20(tracker_0x_address).transferFrom(msg.sender, address(this), tokens);
}

This updates the sender's balance, and then transfers the tokens from sender to escrow (contractB).

Releasing escrowed tokens:

All your smart contract has to do is call the

ERC20(tracker_0x_address).transfer(msg.sender, balances[msg.sender]);

on the token tracker address in order to transfer those tokens to an address.

If you're doing multiple tokens and multiple users in one contract you're going to have to implement a second layer mapping for the balances as well as support for a tracker variable in the deposit method.

Code Example

// ----------------------------------------------------------------------------
// ERC Token Standard #20 Interface
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
// ----------------------------------------------------------------------------
pragma solidity ^0.4.17;

contract ERC20 {
    function totalSupply() public constant returns (uint);
    function balanceOf(address tokenOwner) public constant returns (uint balance);
    function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);
    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

contract contractB {
  address tracker_0x_address = 0xd26114cd6EE289AccF82350c8d8487fedB8A0C07; // ContractA Address
  mapping ( address => uint256 ) public balances;
  
  function deposit(uint tokens) public {

    // add the deposited tokens into existing balance 
    balances[msg.sender]+= tokens;

    // transfer the tokens from the sender to this contract
    ERC20(tracker_0x_address).transferFrom(msg.sender, address(this), tokens);
  }
  
  function returnTokens() public {
    uint256 amount = balances[msg.sender];
    balances[msg.sender] = 0;
    ERC20(tracker_0x_address).transfer(msg.sender, amount);
  }

}
3
  • its perfectly working for me(used metamask). But slightly confusing when I call returnTokens from web3. Can you please let me know how to call this programatically? Commented Feb 21, 2019 at 6:33
  • 1
    I think this won't work! ERC20(tracker_0x_address).transfer(msg.sender, balances[msg.sender]); transfers the tokens from msg.sender to msg.sender not from the contract (at least so with DAI: github.com/makerdao/dss/blob/master/src/dai.sol)
    – porton
    Commented Sep 23, 2020 at 18:36
  • @porton tracker.transfer( toAddress, amount ) , since msg.sender is the one doing the transaction, they end up in the toAddress, and the balance is read from the internal balance that you as the msg.sender have in this contract, not the tracker. Commented Sep 24, 2020 at 23:51
1

Use transfer(msg.sender, 100). The transferFrom is when approve is needed only

2
  • But in ERC20 token standard, the transfer method uses only two arguments i.e address 'to' and the amount of tokens. How can i define the sender's address here?
    – tushalien
    Commented Apr 22, 2018 at 1:32
  • 1
    You're transferring from the contract address right? If that's the case then it automatically knows that the msg.sender is the contract (or address 'this')
    – thefett
    Commented Apr 22, 2018 at 18:50

Not the answer you're looking for? Browse other questions tagged or ask your own question.