This project implements a secure, production-ready Ethereum escrow smart contract using Solidity and Hardhat.
It uses the Factory Pattern with EIP-1167 Minimal Proxies (Clones) for extreme gas efficiency, and features a robust dispute resolution system with an independent Arbiter and seller-side timeouts.
- Gas Efficient: Uses
@openzeppelin/contracts/proxy/Clones.solto deploy cheap clones instead of full contracts. - Arbiter Dispute Resolution: Funds cannot be unilaterally refunded. A neutral third party (Arbiter) resolves deadlocks.
- Seller Timeout Protection: If the buyer receives the goods but "ghosts" the platform, the seller can claim the funds after a 7-day deadline.
Buyer: Creates the escrow via the Factory, designates the seller and arbiter, and deposits the funds.Seller: Delivers the product/service and receives the funds upon buyer approval or timeout.Arbiter: A neutral third-party address that steps in only if a dispute is raised.
AWAITING_PAYMENT: Contract deployed, waiting for the buyer to send ETH.AWAITING_DELIVERY: Funds locked. Seller must deliver. 7-day timeout starts.COMPLETE: Funds successfully released to the seller.REFUNDED: Funds returned to the buyer (only via Arbiter).IN_DISPUTE: Transaction frozen. Only the Arbiter can resolve it.
- Creation: Buyer calls
createEscrow(sellerAddress, arbiterAddress)on theEscrowFactory. - Deposit: Buyer deposits ETH into their specific Escrow clone. State becomes
AWAITING_DELIVERY. - Resolution (3 Paths):
- Happy Path: Buyer is satisfied and calls
release(). Seller gets paid. - Dispute: Something goes wrong. Buyer or Seller calls
initiateDispute(). The Arbiter reviews off-chain and callsresolveDispute(true/false)to refund the buyer or pay the seller. - Timeout: 7 days pass without the buyer releasing funds or raising a dispute. Seller calls
claimTimeout()to get paid.
- Happy Path: Buyer is satisfied and calls
The project includes 35 comprehensive automated tests written in TypeScript, covering:
- Factory deployment & clone isolation
- State transition constraints
- Access control (preventing unauthorized actions)
- Dispute edge-cases & Arbiter logic
- Time-travel tests for the 7-day
claimTimeoutfallback
To run the tests:
npm install
npx hardhat compile
npx hardhat testThe web/ folder is a Sepolia-oriented UI: scroll hero landing, MetaMask, deploy escrow from the browser, and deposit / release / refund on /escrow/[address].
cd web
cp .env.example .env.local
# Set NEXT_PUBLIC_RPC_URL to Sepolia, e.g. https://sepolia.infura.io/v3/<YOUR_INFURA_KEY>
# (Same Infura project ID as mainnet, but use sepolia. in the URL — not mainnet.)
# Never commit keys; rotate any key that was exposed.
npm install
npm run sync:abi # after changing Escrow.sol — regenerates src/lib/escrow-artifact.ts
npm run devnpm run dev uses webpack (more stable on Windows when the repo root also has a package-lock.json). For Turbopack: npm run dev:turbo.
If dev crashes with out of memory or error 1450, close other Node/IDE processes, delete the web/.next folder, and run npm run dev again.
From the repo root you can run npm run web:dev or npm run web:sync-abi after npx hardhat compile.
Place the hero video at web/public/media/eth-hero.mp4 (bundled from web assets/ in this repo).
- Set Project Root to
web. - In Vercel environment variables, set:
NEXT_PUBLIC_RPC_URL(Sepolia HTTPS RPC)NEXT_PUBLIC_FACTORY_ADDRESS(factory fromdeployments/sepolia.json)NEXT_PUBLIC_EXPLORER_URL=https://sepolia.etherscan.ioGROQ_API_KEY(optional, for assistant route)NEXT_PUBLIC_SENTRY_DSN/SENTRY_DSN(optional)
- From repo root, validate env contract:
npm run validate:web-env
- Deploy contracts on Sepolia and sync env hints:
npx hardhat run scripts/deploy.ts --network sepolia npm run sync:deployment-env -- sepolia