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:
- 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)
- 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);
}
}
ERC20(tracker_0x_address).transfer(msg.sender, balances[msg.sender]);transfers the tokens frommsg.sendertomsg.sendernot from the contract (at least so with DAI: https://github.com/makerdao/dss/blob/master/src/dai.sol) – porton Sep 23 '20 at 18:36tracker.transfer( toAddress, amount ), sincemsg.senderis the one doing the transaction, they end up in thetoAddress, and the balance is read from the internal balance that you as the msg.sender have in this contract, not the tracker. – Micky Socaci Sep 24 '20 at 23:51