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. Why Roostock? 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. Prerequisites for Building the Escrow Service To successfully build and deploy the escrow smart contract on Rootstock (RSK), you'll need the following: Solidity for smart contract development JavaScript/TypeScript for frontend or integration scripting. Development Environment: A local setup with tools like Truffle, Hardhat, or Remix IDE. Including installed dependencies such as Node.js and npm/yarn. Testing Framework: Mocha/Chai or similar libraries for writing and running test cases. And access to a Rootstock testnet. Wallet Integration: Use Math wallet or any other that supports Rootstock for configuration to interact with the RSK network. Rootstock Wallet Setup Requirements rBTC for Gas: Testnet rBTC for deploying and testing the smart contract. Rootstock Configuration: Add Rootstock testnet or mainnet RPC to your wallet (Math wallet) and development tools: Mainnet RPC: https://public-node.rsk.co Testnet RPC: https://public-node.testnet.rsk.co Chain ID for Mainnet: 30 and Testnet: 31. Contract Deployment Tooling: Setup RSK-specific plugins/modules if you're using Truffle for deployment. Audit & Security Guide You can leverage automated tools like MythX, Slither, or Remix Analyzer. Key Components of the Escrow System Smart Contract 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: Core Operations: 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. Fail-Safes: refundBuyer(): Refunds the buyer in case of non-delivery. 3. Events: Not explicitly included but highly recommended for transaction logging: 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. Modifiers: Role-based access control (onlyBuyer, onlySeller, onlyArbiter) to ensure only authorized parties can perform specific actions. State-based restrictions (inState) to prevent invalid operations. Internal Logic: State Transitions: Smoothly transition through predefined stages (AWAITING_PAYMENT → AWAITING_DELIVERY → COMPLETE or DISPUTED). Conditional Finalization: Release funds only when both buyer and seller approve. The Escrow System Smart Contract 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: Initialization: The contract is deployed with the buyer, seller, and arbiter addresses specified. The initial state is AWAITING_PAYMENT. Deposit: The buyer deposits funds into the contract. The state changes to AWAITING_DELIVERY. Approval and Delivery: The buyer approves delivery, and the seller confirms the receipt of funds. If both parties approve, the funds are transferred to the seller, and the contract's state becomes COMPLETE. Disputes: Either party can mark the contract as disputed. The arbiter resolves the dispute by transferring funds to the appropriate party. Refunds: If the seller fails to deliver, the buyer can request a refund. Deploying & Testing the Smart Contract 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! 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. Why Roostock? 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. Prerequisites for Building the Escrow Service Prerequisites for Building the Escrow Service To successfully build and deploy the escrow smart contract on Rootstock (RSK) , you'll need the following: Rootstock (RSK) Solidity for smart contract development JavaScript/TypeScript for frontend or integration scripting. Development Environment: A local setup with tools like Truffle, Hardhat, or Remix IDE. Including installed dependencies such as Node.js and npm/yarn. Testing Framework: Mocha/Chai or similar libraries for writing and running test cases. And access to a Rootstock testnet. Wallet Integration: Use Math wallet or any other that supports Rootstock for configuration to interact with the RSK network. Solidity for smart contract development JavaScript/TypeScript for frontend or integration scripting. Development Environment: A local setup with tools like Truffle, Hardhat, or Remix IDE. Including installed dependencies such as Node.js and npm/yarn. Testing Framework: Mocha/Chai or similar libraries for writing and running test cases. And access to a Rootstock testnet. Wallet Integration: Use Math wallet or any other that supports Rootstock for configuration to interact with the RSK network. Rootstock Wallet Setup Requirements Rootstock Wallet Setup Requirements rBTC for Gas: Testnet rBTC for deploying and testing the smart contract. Rootstock Configuration: Add Rootstock testnet or mainnet RPC to your wallet (Math wallet) and development tools: Mainnet RPC: https://public-node.rsk.co Testnet RPC: https://public-node.testnet.rsk.co Chain ID for Mainnet: 30 and Testnet: 31. Contract Deployment Tooling: Setup RSK-specific plugins/modules if you're using Truffle for deployment. rBTC for Gas: Testnet rBTC for deploying and testing the smart contract. Testnet rBTC Rootstock Configuration: Add Rootstock testnet or mainnet RPC to your wallet (Math wallet) and development tools: Mainnet RPC: https://public-node.rsk.co Testnet RPC: https://public-node.testnet.rsk.co Chain ID for Mainnet: 30 and Testnet: 31. Mainnet RPC: https://public-node.rsk.co Testnet RPC: https://public-node.testnet.rsk.co Chain ID for Mainnet: 30 and Testnet: 31. Mainnet RPC: https://public-node.rsk.co https://public-node.rsk.co Testnet RPC: https://public-node.testnet.rsk.co https://public-node.testnet.rsk.co Chain ID for Mainnet: 30 and Testnet: 31 . 30 31 Contract Deployment Tooling: Setup RSK-specific plugins/modules if you're using Truffle for deployment. Audit & Security Guide Audit & Security Guide You can leverage automated tools like MythX, Slither, or Remix Analyzer. Key Components of the Escrow System Smart Contract Key Components of the Escrow System Smart Contract 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: 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). Variables : Variables Addresses: buyer , seller , and arbiter define the parties involved. buyer seller arbiter Amount: Tracks the escrowed funds. States: An enum ( EscrowState ) defines the current stage of the transaction (e.g., AWAITING_PAYMENT , COMPLETE ). enum EscrowState AWAITING_PAYMENT COMPLETE Functions: Functions : Functions Core Operations: 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. Fail-Safes: refundBuyer(): Refunds the buyer in case of non-delivery. Core Operations: 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. 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. deposit() : Buyer deposits funds into the escrow. deposit() approveDelivery() & confirmDelivery() : Buyer and seller approve the release of funds. approveDelivery() confirmDelivery() dispute() : Buyer or seller flags a dispute. dispute() resolveDispute() : Arbiter resolves disputes and allocates funds. resolveDispute() Fail-Safes: refundBuyer(): Refunds the buyer in case of non-delivery. refundBuyer(): Refunds the buyer in case of non-delivery. refundBuyer(): Refunds the buyer in case of non-delivery. refundBuyer() : Refunds the buyer in case of non-delivery. refundBuyer() 3. Events : 3. Events Not explicitly included but highly recommended for transaction logging: 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. Modifiers: Role-based access control (onlyBuyer, onlySeller, onlyArbiter) to ensure only authorized parties can perform specific actions. State-based restrictions (inState) to prevent invalid operations. Not explicitly included but highly recommended for transaction logging: 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. 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. FundsDeposited(address buyer, uint256 amount) : Triggered on deposit. FundsDeposited(address buyer, uint256 amount) FundsReleased(address recipient, uint256 amount) : Triggered when funds are disbursed. FundsReleased(address recipient, uint256 amount) DisputeRaised() : Triggered when a dispute is flagged. DisputeRaised() Modifiers: Role-based access control (onlyBuyer, onlySeller, onlyArbiter) to ensure only authorized parties can perform specific actions. State-based restrictions (inState) to prevent invalid operations. Role-based access control (onlyBuyer, onlySeller, onlyArbiter) to ensure only authorized parties can perform specific actions. State-based restrictions (inState) to prevent invalid operations. Role-based access control ( onlyBuyer , onlySeller , onlyArbiter ) to ensure only authorized parties can perform specific actions. onlyBuyer onlySeller onlyArbiter State-based restrictions ( inState ) to prevent invalid operations. inState Internal Logic: Internal Logic : Internal Logic State Transitions: Smoothly transition through predefined stages (AWAITING_PAYMENT → AWAITING_DELIVERY → COMPLETE or DISPUTED). Conditional Finalization: Release funds only when both buyer and seller approve. State Transitions: Smoothly transition through predefined stages ( AWAITING_PAYMENT → AWAITING_DELIVERY → COMPLETE or DISPUTED ). AWAITING_PAYMENT AWAITING_DELIVERY COMPLETE DISPUTED Conditional Finalization: Release funds only when both buyer and seller approve. The Escrow System Smart Contract This code is written with Solidity Programming language 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; } } // 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: Here is the breakdown of how this Smart Contract will work: Initialization: The contract is deployed with the buyer, seller, and arbiter addresses specified. The initial state is AWAITING_PAYMENT. Deposit: The buyer deposits funds into the contract. The state changes to AWAITING_DELIVERY. Approval and Delivery: The buyer approves delivery, and the seller confirms the receipt of funds. If both parties approve, the funds are transferred to the seller, and the contract's state becomes COMPLETE. Disputes: Either party can mark the contract as disputed. The arbiter resolves the dispute by transferring funds to the appropriate party. Refunds: If the seller fails to deliver, the buyer can request a refund. Initialization: The contract is deployed with the buyer, seller, and arbiter addresses specified. The initial state is AWAITING_PAYMENT. The contract is deployed with the buyer, seller, and arbiter addresses specified. The initial state is AWAITING_PAYMENT. The contract is deployed with the buyer, seller, and arbiter addresses specified. The initial state is AWAITING_PAYMENT . AWAITING_PAYMENT Deposit: The buyer deposits funds into the contract. The state changes to AWAITING_DELIVERY. The buyer deposits funds into the contract. The state changes to AWAITING_DELIVERY. The buyer deposits funds into the contract. The state changes to AWAITING_DELIVERY . AWAITING_DELIVERY Approval and Delivery: The buyer approves delivery, and the seller confirms the receipt of funds. If both parties approve, the funds are transferred to the seller, and the contract's state becomes COMPLETE. The buyer approves delivery, and the seller confirms the receipt of funds. If both parties approve, the funds are transferred to the seller, and the contract's state becomes COMPLETE. The buyer approves delivery, and the seller confirms the receipt of funds. If both parties approve, the funds are transferred to the seller, and the contract's state becomes COMPLETE . COMPLETE Disputes: Either party can mark the contract as disputed. The arbiter resolves the dispute by transferring funds to the appropriate party. Either party can mark the contract as disputed. The arbiter resolves the dispute by transferring funds to the appropriate party. Either party can mark the contract as disputed. The arbiter resolves the dispute by transferring funds to the appropriate party. Refunds: If the seller fails to deliver, the buyer can request a refund. If the seller fails to deliver, the buyer can request a refund. If the seller fails to deliver, the buyer can request a refund. Deploying & Testing the Smart Contract 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. 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: use rBTC faucet on Rootstock testnet const Escrow = artifacts.require("Escrow"); module.exports = function (deployer) { deployer.deploy(Escrow, "buyer_address", "seller_address", "arbiter_address"); }; 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!