I am developing a website which helps users to perform the escrow of ERC20 tokens. The particular platform only accepts the said ERC20 token. I need my client to deposit the ERC20 token to the escrow contract until he get delivered with the service/product. My doubts are,

  1. Is it possible to do an escrow contract for ERC20 tokens. I can't use Metamask(As web3 provider)since it is not possible to transfer tokens using metamask.

It will be great if someone guide me to find a solution with a sample contract if available, Thanks a lot :).

2 Answers 2


We created a solution for escrow functionality for ERC20 tokens. Hope this helps.

The code we used is as follows.

pragma solidity ^0.4.21;

import "../zeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "../zeppelin-solidity/contracts/ownership/Ownable.sol";
import "../webshop/Webshop.sol";

contract Escrow is Ownable {
    enum PaymentStatus { Pending, Completed, Refunded }

    event PaymentCreation(uint indexed orderId, address indexed customer, uint value);
    event PaymentCompletion(uint indexed orderId, address indexed customer, uint value, PaymentStatus status);

    struct Payment {
        address customer;
        uint value;
        PaymentStatus status;
        bool refundApproved;

    mapping(uint => Payment) public payments;
    ERC20 public currency;
    address public collectionAddress;
    Webshop public webshop;

    function Escrow(ERC20 _currency, address _collectionAddress) public {
        currency = _currency;
        collectionAddress = _collectionAddress;
        webshop = Webshop(msg.sender);

    function createPayment(uint _orderId, address _customer, uint _value) external onlyOwner {
        payments[_orderId] = Payment(_customer, _value, PaymentStatus.Pending, false);
        emit PaymentCreation(_orderId, _customer, _value);

    function release(uint _orderId) external {
        completePayment(_orderId, collectionAddress, PaymentStatus.Completed);

    function refund(uint _orderId) external {
        completePayment(_orderId, msg.sender, PaymentStatus.Refunded);

    function approveRefund(uint _orderId) external {
        require(msg.sender == collectionAddress);
        Payment storage payment = payments[_orderId];
        payment.refundApproved = true;

    function completePayment(uint _orderId, address _receiver, PaymentStatus _status) private {
        Payment storage payment = payments[_orderId];
        require(payment.customer == msg.sender);
        require(payment.status == PaymentStatus.Pending);
        if (_status == PaymentStatus.Refunded) {
        currency.transfer(_receiver, payment.value);
        webshop.changeOrderStatus(_orderId, Webshop.OrderStatus.Completed);
        payment.status = _status;
        emit PaymentCompletion(_orderId, payment.customer, payment.value, _status);

The explanation of the code, including the code can be found on https://medium.com/@s_van_laar/how-to-build-an-escrow-contract-with-an-ethereum-erc20-token-bfc4825b0dd7

  1. You need to know the address of the token, allow the user to store this.
  2. You need to instantiate the token contract at that address so you can call it's functions
  3. You need to use approve() on the client and then call transferFrom() on the contract to send tokens from a user to a contract. transferAndCall() won't be available as you want to work with ERC20.
  4. On completion of conditions you can call .transfer() by the contract to transfer the escrow to the recipient.

Here is a digital trade financing example without tokens, but as I said instead of ether use approve/transferFrom with tokens and it's address instead. https://github.com/kyriediculous/LetterOfCredit/blob/master/LoCWorkflow.sol

Now you have the design desicion to make wheter you want each escrow to be a seperate contract between the two users as I Did in my example :).

Good luck, hopefully this helps.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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