While trust and security are at the core of blockchain, this smart contract is designed to provide a trustless escrow system for peer-to-peer trades on Rootstock, using Solidity. It ensures that funds or assets are securely held until both the buyer and seller confirm that the terms have been met. If there’s a dispute, a neutral arbiter can step in to resolve the issue and release the funds. This system removes the need for intermediaries, offering a transparent and secure transaction process.
Rootstock (RSK) is a great fit for this application because it combines the security of Bitcoin (through merged mining) with the flexibility of the Ethereum Virtual Machine (EVM), making it accessible for all Ethereum users. With low transaction costs, faster block times, and the ability to leverage Bitcoin liquidity through rBTC, RSK offers a more cost-effective and secure solution for building decentralized applications, especially for Bitcoin-native users.
To successfully build and deploy the escrow smart contract on Rootstock (RSK), you'll need the following:
Rootstock Wallet Setup Requirements
https://public-node.rsk.co
https://public-node.testnet.rsk.co
30
and Testnet: 31
.Audit & Security Guide
You can leverage automated tools like MythX, Slither, or Remix Analyzer.
Variables:
Addresses: buyer
, seller
, and arbiter
define the parties involved.
Amount: Tracks the escrowed funds.
States: An enum
(EscrowState
) defines the current stage of the transaction (e.g., AWAITING_PAYMENT
, COMPLETE
).
Functions:
deposit()
: Buyer deposits funds into the escrow.approveDelivery()
& confirmDelivery()
: Buyer and seller approve the release of funds.dispute()
: Buyer or seller flags a dispute.resolveDispute()
: Arbiter resolves disputes and allocates funds.refundBuyer()
: Refunds the buyer in case of non-delivery.
3. Events:
FundsDeposited(address buyer, uint256 amount)
: Triggered on deposit.FundsReleased(address recipient, uint256 amount)
: Triggered when funds are disbursed.DisputeRaised()
: Triggered when a dispute is flagged.onlyBuyer
, onlySeller
, onlyArbiter
) to ensure only authorized parties can perform specific actions.inState
) to prevent invalid operations.AWAITING_PAYMENT
→ AWAITING_DELIVERY
→ COMPLETE
or DISPUTED
).This code is written with Solidity Programming language
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Escrow {
address public buyer;
address public seller;
address public arbiter; // A third party who resolves disputes
uint256 public amount;
bool public buyerApproval;
bool public sellerApproval;
enum EscrowState { AWAITING_PAYMENT, AWAITING_DELIVERY, COMPLETE, DISPUTED }
EscrowState public state;
modifier onlyBuyer() {
require(msg.sender == buyer, "Only the buyer can call this function.");
_;
}
modifier onlySeller() {
require(msg.sender == seller, "Only the seller can call this function.");
_;
}
modifier onlyArbiter() {
require(msg.sender == arbiter, "Only the arbiter can call this function.");
_;
}
modifier inState(EscrowState _state) {
require(state == _state, "Invalid state for this action.");
_;
}
constructor(address _buyer, address _seller, address _arbiter) {
buyer = _buyer;
seller = _seller;
arbiter = _arbiter;
state = EscrowState.AWAITING_PAYMENT;
}
// Buyer deposits funds into escrow
function deposit() external payable onlyBuyer inState(EscrowState.AWAITING_PAYMENT) {
require(msg.value > 0, "Deposit amount must be greater than 0.");
amount = msg.value;
state = EscrowState.AWAITING_DELIVERY;
}
// Buyer approves the release of funds
function approveDelivery() external onlyBuyer inState(EscrowState.AWAITING_DELIVERY) {
buyerApproval = true;
finalize();
}
// Seller confirms the transaction is complete
function confirmDelivery() external onlySeller inState(EscrowState.AWAITING_DELIVERY) {
sellerApproval = true;
finalize();
}
// Arbiter resolves disputes
function resolveDispute(bool releaseToSeller) external onlyArbiter inState(EscrowState.DISPUTED) {
if (releaseToSeller) {
payable(seller).transfer(amount);
} else {
payable(buyer).transfer(amount);
}
state = EscrowState.COMPLETE;
}
// Mark contract as disputed (only buyer or seller can do this)
function dispute() external {
require(msg.sender == buyer || msg.sender == seller, "Only buyer or seller can dispute.");
state = EscrowState.DISPUTED;
}
// Finalize the transaction if both parties approve
function finalize() internal {
if (buyerApproval && sellerApproval) {
payable(seller).transfer(amount);
state = EscrowState.COMPLETE;
}
}
// Refund the buyer if the seller fails to deliver
function refundBuyer() external onlyBuyer inState(EscrowState.AWAITING_DELIVERY) {
payable(buyer).transfer(amount);
state = EscrowState.COMPLETE;
}
}
Here is the breakdown of how this Smart Contract will work:
AWAITING_PAYMENT
.AWAITING_DELIVERY
.COMPLETE
.To deploy and test the smart contract on Rootstock, I will be using Truffle software to create a development environment, then configure the Rootstock testnet (or mainnet) for the Escrow System.
We will use rBTC faucet on Rootstock testnet to test the smart contract with Truffle testing environments, using the following deploy command:
const Escrow = artifacts.require("Escrow");
module.exports = function (deployer) {
deployer.deploy(Escrow, "buyer_address", "seller_address", "arbiter_address");
};
Let me know if you have any questions in the comments!