Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions assignments/Solidity/Escrow/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Node modules
/node_modules

# Compilation output
/dist

# pnpm deploy output
/bundle

# Hardhat Build Artifacts
/artifacts

# Hardhat compilation (v2) support directory
/cache

# Typechain output
/types

# Hardhat coverage reports
/coverage
124 changes: 124 additions & 0 deletions assignments/Solidity/Escrow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Escrow Smart Contract System

A Solidity smart contract system for secure ETH escrow transactions between a buyer and seller, overseen by an agent.

---

## Overview

* **Escrow Contract** – Handles a single escrow transaction:

* Seller deposits ETH
* Buyer confirms delivery
* Agent releases funds or refunds
* **EscrowFactory Contract** – Deploys and manages multiple Escrow contracts:

* Buyers create new escrow agreements with sellers
* Tracks deployed escrows in an array
* Provides helper functions to retrieve escrow count and addresses

---

## Example Usage (TypeScript)

### Deploy EscrowFactory

```ts
import { ethers } from "hardhat";
import { EscrowFactory, Escrow } from "../typechain-types";

async function main() {
const [deployer, buyer, seller] = await ethers.getSigners();

// Deploy Factory
const factoryFactory = await ethers.getContractFactory("EscrowFactory");
const factory = (await factoryFactory.deploy()) as EscrowFactory;
await factory.deployed();

console.log("Factory deployed at:", factory.target);

// Buyer creates a new escrow
const tx = await factory.connect(buyer).createEscrow(seller.address);
await tx.wait();

// Retrieve deployed escrow address
const escrowAddress = await factory.getEscrow(0);
const escrow = (await ethers.getContractAt("Escrow", escrowAddress)) as Escrow;

console.log("Escrow deployed at:", escrowAddress);
}
```

---

### Seller Funds Escrow

```ts
const fundTx = await escrow.connect(seller).fundEscrow({
value: ethers.parseEther("5")
});
await fundTx.wait();
console.log("Escrow funded by seller with 5 ETH");
```

---

### Buyer Confirms Delivery

```ts
const confirmTx = await escrow.connect(buyer).confirmDelivery();
await confirmTx.wait();
console.log("Buyer confirmed delivery");
```

---

### Agent Releases Funds to Seller

```ts
const releaseTx = await escrow.connect(deployer).releaseFunds(); // deployer is agent
await releaseTx.wait();
console.log("Agent released funds to seller");
```

---

### Agent Refunds Buyer

```ts
const refundTx = await escrow.connect(deployer).refundBuyer();
await refundTx.wait();
console.log("Agent refunded buyer");
```

---

### Getting Escrow Details

```ts
const amount = await escrow.amountReceived();
const status = await escrow.status();
const currentBuyer = await escrow.buyer();
const currentSeller = await escrow.seller();

console.log("Amount in escrow:", ethers.formatEther(amount), "ETH");
console.log("Escrow status:", status);
console.log("Buyer:", currentBuyer);
console.log("Seller:", currentSeller);
```

---

### Factory Helper Functions

```ts
// Total escrows
const totalEscrows = await factory.getEscrowCount();
console.log("Total Escrows:", totalEscrows);

// Get escrow address by index
const firstEscrowAddr = await factory.getEscrow(0);
console.log("Escrow[0] address:", firstEscrowAddr);
```

---
82 changes: 82 additions & 0 deletions assignments/Solidity/Escrow/contracts/Escrow.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract Escrow {
address public agent;
address payable public buyer;
address payable public seller;
uint public amountReceived;

enum Status {
PENDING,
PAID,
AWAITING_CONFIRM,
COMPLETE
}

Status public status;

event EscrowFunded(address indexed seller, uint amount);
event DeliveryConfirmed(address indexed buyer);
event FundsReleased(address indexed seller, uint amount);
event FundsRefunded(address indexed buyer, uint amount);

modifier onlyBuyer() {
require(msg.sender == buyer, "Only buyer allowed");
_;
}

modifier onlySeller() {
require(msg.sender == seller, "Only seller allowed");
_;
}

modifier onlyAgent() {
require(msg.sender == agent, "Only agent allowed");
_;
}

constructor(address payable _buyer, address payable _seller) {
agent = msg.sender; // factory deployer is agent
buyer = _buyer;
seller = _seller;
status = Status.PENDING;
}

/// Seller deposits ETH into escrow
function fundEscrow() external payable onlySeller {
require(status == Status.PENDING, "Already funded");
require(msg.value > 0, "Must send ETH");

amountReceived = msg.value;
status = Status.PAID;

emit EscrowFunded(msg.sender, msg.value);
}

/// Buyer confirms delivery of goods
function confirmDelivery() external onlyBuyer {
require(status == Status.PAID, "Funds not deposited yet");
status = Status.AWAITING_CONFIRM;

emit DeliveryConfirmed(msg.sender);
}

/// Agent releases funds to seller
function releaseFunds() external onlyAgent {
require(status == Status.AWAITING_CONFIRM, "Not ready to release");
seller.transfer(amountReceived);
status = Status.COMPLETE;

emit FundsReleased(seller, amountReceived);
}

/// Agent refunds buyer
function refundBuyer() external onlyAgent {
require(status == Status.AWAITING_CONFIRM, "Not ready to refund");
buyer.transfer(amountReceived);
status = Status.COMPLETE;

emit FundsRefunded(buyer, amountReceived);
}
}
35 changes: 35 additions & 0 deletions assignments/Solidity/Escrow/contracts/EscrowFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.28;

import "./Escrow.sol";

contract EscrowFactory {
Escrow [] public escrows;

event EscrowCreated(
address escrowAddress,
address buyer,
address seller
);
// buyer creates new escrow order with the seller and stores in escorws array

function createEscrow (address payable seller) external {
Escrow escrow = new Escrow (payable(msg.sender), seller);
escrows.push (escrow);

emit EscrowCreated (address(escrow), msg.sender, seller);
}

/// i added it so it will return the total num of escrows
function getEscrowCount () external view returns (uint256){
return escrows.length;
}
// retrieve the address by index and check if d index is valid then returns the escrows contract address

function getEscrow (uint index) external view returns(address) {
require (index < escrows.length, "Index is out of range");
return address (escrows[index]);
}
}

38 changes: 38 additions & 0 deletions assignments/Solidity/Escrow/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import hardhatToolboxMochaEthersPlugin from "@nomicfoundation/hardhat-toolbox-mocha-ethers";
import { configVariable, defineConfig } from "hardhat/config";

export default defineConfig({
plugins: [hardhatToolboxMochaEthersPlugin],
solidity: {
profiles: {
default: {
version: "0.8.28",
},
production: {
version: "0.8.28",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
},
},
networks: {
hardhatMainnet: {
type: "edr-simulated",
chainType: "l1",
},
hardhatOp: {
type: "edr-simulated",
chainType: "op",
},
sepolia: {
type: "http",
chainType: "l1",
url: configVariable("SEPOLIA_RPC_URL"),
accounts: [configVariable("SEPOLIA_PRIVATE_KEY")],
},
},
});
9 changes: 9 additions & 0 deletions assignments/Solidity/Escrow/ignition/modules/Counter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";

export default buildModule("CounterModule", (m) => {
const counter = m.contract("Counter");

m.call(counter, "incBy", [5n]);

return { counter };
});
20 changes: 20 additions & 0 deletions assignments/Solidity/Escrow/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "Test",
"version": "1.0.0",
"type": "module",
"devDependencies": {
"@nomicfoundation/hardhat-ethers": "^4.0.4",
"@nomicfoundation/hardhat-ignition": "^3.0.7",
"@nomicfoundation/hardhat-toolbox-mocha-ethers": "^3.0.2",
"@types/chai": "^4.3.20",
"@types/chai-as-promised": "^8.0.2",
"@types/mocha": "^10.0.10",
"@types/node": "^22.19.10",
"chai": "^5.3.3",
"ethers": "^6.16.0",
"forge-std": "github:foundry-rs/forge-std#v1.9.4",
"hardhat": "^3.1.7",
"mocha": "^11.7.5",
"typescript": "~5.8.0"
}
}
22 changes: 22 additions & 0 deletions assignments/Solidity/Escrow/scripts/send-op-tx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { network } from "hardhat";

const { ethers } = await network.connect({
network: "hardhatOp",
chainType: "op",
});

console.log("Sending transaction using the OP chain type");

const [sender] = await ethers.getSigners();

console.log("Sending 1 wei from", sender.address, "to itself");

console.log("Sending L2 transaction");
const tx = await sender.sendTransaction({
to: sender.address,
value: 1n,
});

await tx.wait();

console.log("Transaction sent successfully");
Loading