[Ethereum] Is it possible to do an escrow for ERC20 Token

contract-developmenterc-20metamasksolidityweb3-providers

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 :).

Best Answer

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) {
            require(payment.refundApproved);
        }
        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