BUILDING AN ESCROW SMART CONTRACT

An escrow agreement is a contract in which a third party receives money from one party and pays it to another if some specific conditions are met. This third party in traditional finance must be a trustworthy human or institution, such as a bank, but with Defi, it can be a smart contract.

In this tutorial, we will go through how to create, deploy and interact with an escrow smart contract on Gather Testnet.

Here we will use MetaMask as our wallet and remix IDE to compile and deploy our smart contract. You can use a different wallet and other frameworks like Hardhat or Truffle as well.

PREREQUISITES

  • Metamask wallet configured with Gather testnet

  • Testnet GTHs. You can use Gather Faucet to get some testnet GTH.

STEP - 1

Create a new file on Remix Escrow.sol

STEP - 2

Now in Escrow.sol Let's create a contract called Escrow

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

contract Escrow {
    // Do stuff
}

STEP - 3

Now let's define some states for the contract.

We will define an enum State with three states - AWAITING_PAYMENT, AWAITING_DELIVERY, COMPLETE

contract Escrow {
    enum State { AWAITING_PAYMENT, AWAITING_DELIVERY, COMPLETE }
    
    State public currState;
}

STEP - 4

Next, we will create variables for seller and buyer. The seller must be payable as they will receive GTH (testnet). Also, we'll create a modifier to ensure that only buyer can call a particular method.

contract Escrow {
  
  	//...
     address public buyer;
     address payable public seller;
     modifier onlyBuyer() {
            require(msg.sender == buyer, "This method is only accessible to buyer!"); 
            _;
        }
  
  	//...
}

STEP - 5

In the constructor, we will initialize our buyer and seller addresses.

contract Escrow {
  
  	//...
  
  constructor(address _buyer, address payable _seller) {
        buyer = _buyer;
        seller = _seller;
    }
  
  	//...
}

STEP - 6

Now let's create a deposit method, as it will receive GTH we should define it as payable.

Only the buyer should be able to call this method. It can only be called when there are no funds in the contract which means the state of the contract should be in AWAITING_PAYMENT

After sending funds into the contract the state should be changed to AWAITING_DELIVERY

contract Escrow {
  
  	//...
  
  function deposit() onlyBuyer external payable {
        require(currState == State.AWAITING_PAYMENT, "Payment is already done!");
        currState = State.AWAITING_DELIVERY;
    }
  
  	//...
}

STEP - 7

Finally, we will create a confirmDelivery method, which will allow the buyer to confirm that they have actually received the service or the product.

This will also be a method that can be only called by the buyer. While calling the method the state must be in AWAITING DELIVEREY, which means the funds are already paid by the buyer and is in the contract.

The state will be changed to COMPLETE at the end of the method.

contract Escrow {
  
  	//...
  
  function confirmDelivery() onlyBuyer external {
        require(currState == State.AWAITING_DELIVERY, "Cannot confirm delivery");
        seller.transfer(address(this).balance);
        currState = State.COMPLETE;
    }
  
  	//...
}

STEP - 8

Now we have our contract code ready to be compiled. You can get the complete code from here as well - https://github.com/GatherNetwork/Escrow-Smart-Contract.git

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Escrow {
    enum State { AWAITING_PAYMENT, AWAITING_DELIVERY, COMPLETE }
    
    State public currState;
    
    address public buyer;
    address payable public seller;
    
    modifier onlyBuyer() {
        require(msg.sender == buyer, "Only buyer can call this method");
        _;
    }
    
    constructor(address _buyer, address payable _seller) {
        buyer = _buyer;
        seller = _seller;
    }
    
    function deposit() onlyBuyer external payable {
        require(currState == State.AWAITING_PAYMENT, "Already paid");
        currState = State.AWAITING_DELIVERY;
    }
    
    function confirmDelivery() onlyBuyer external {
        require(currState == State.AWAITING_DELIVERY, "Cannot confirm delivery");
        seller.transfer(address(this).balance);
        currState = State.COMPLETE;
    }
}

STEP - 9

STEP - 10

Fill in the details for buyer and seller addresses in the deployment section and deploy your contract.

STEP - 11

This will open your MetaMask extension and ask you to confirm the pending transaction. Click the Confirm button on the MetaMask popup.

STEP - 12

Your contract is now deployed on Gather Testnet. You can switch to the buyer's account in the MetaMask and call the Deposit method.

You can also send some testnet GTHs with the transaction to deposit, by entering the desired amount in the value field. Here the amount will be deducted from your account and will be stored inside the contract.

This will trigger a MetaMask transaction, click on approve to proceed.

STEP - 13

After the deposit now you can confirm the delivery from the buyer's account. This will send the amount stored in the contract to the seller.

This will trigger a MetaMask popup for a transaction, click on approve to proceed.

We have successfully created, deployed, and interacted with a simple escrow smart contract. You can deploy this contract to the Gather mainnet as well, you will just need to configure MetaMask with the Gather mainnet.

VIDEO

Last updated