Project: Project 1 β Decentralised Book Rental (CS218)
- Mohd Hassan Raza Ansari - 240008019
- Dhyan Chandra C - 240041014
- Manish Garasiya - 240041026
- Manjeet Kumar - 240041027
- Harsha Varshan Bonu - 240001020
BookChain is a dApp built on Ethereum for renting books. Book owners (lenders) can list their books, and readers can borrow them by putting down a security deposit and a daily fee. The whole thing (listing, renting, returning, and disputes) is managed by our smart contract so it's completely trustless.
- Listing: Lenders can add books with a daily fee and deposit. The actual book details are stored on IPFS.
- Borrowing: Readers pay the deposit + 1 day fee upfront to get a book. (They also have to accept our T&Cs)
- Returning: When a reader returns a book, the status goes to
AwaitingConfirm. - Confirming: The lender checks the book. The contract calculates the final cost and refunds whatever is left of the deposit.
- Auto-Confirm: If the lender forgets to confirm within 48 hours, anyone can trigger the confirmation to get the renter their refund.
- Max 5 active rentals per user (don't hoard the books!).
- Late penalty: 2x the daily rate if you keep it more than 7 days.
- Zero-fee lending is supported if you just want to lend a book for free.
- Raise Dispute: If a book comes back ruined, either party can raise a dispute.
- Random Arbitrator: We use
block.prevrandaoto pick a random arbitrator from the pool (excluding the lender and renter so it's fair). - Fallback: The contract deployer is automatically an arbitrator just in case the pool is empty.
- Resolution: The assigned arbitrator picks a winner, who gets all the locked funds.
We have 3 main views:
- Lender Dashboard - list books, confirm returns.
- Library Catalog - for readers to browse and borrow.
- Arbitrator Panel - for resolving disputes.
- Smart Contract: Solidity ^0.8.20, OpenZeppelin (Ownable, ReentrancyGuard)
- Development: Hardhat
- Frontend: Vanilla HTML/CSS/JS with ethers.js v5.7.2
- Wallet: MetaMask
- Styling: Custom warm library theme with Playfair Display serif typography
- Node.js (v18+)
- npm
- MetaMask browser extension
-
Install dependencies:
npm install
-
Compile the smart contract:
npx hardhat compile
-
Start a local Hardhat node (Terminal 1):
npx hardhat node
-
Deploy the contract (Terminal 2):
npx hardhat run scripts/deploy.js --network localhost
Copy the deployed contract address from the output.
-
Update the frontend config: Open
frontend/config.jsand replace thecontractAddressvalue with the new deployed address. -
Start the frontend server (Terminal 3):
cd frontend python3 -m http.server 8000Open
http://localhost:8000in your browser. -
Connect MetaMask:
- Add the Hardhat network to MetaMask (RPC:
http://127.0.0.1:8545, Chain ID:31337). - Import Hardhat test accounts using the private keys printed when the node started.
- Add the Hardhat network to MetaMask (RPC:
npx hardhat test
npx hardhat coverageDAppBook/
βββ contracts/
β βββ BookRental.sol # Main smart contract
βββ scripts/
β βββ deploy.js # Deployment script
βββ test/
β βββ BookRental.test.js # Test suite
βββ frontend/
β βββ index.html # Landing page
β βββ owner.html # Lender dashboard
β βββ renter.html # Reader / library catalog
β βββ arbitrator.html # Arbitrator panel
β βββ config.js # Shared contract ABI, address, wallet logic
β βββ owner.js # Lender page logic
β βββ renter.js # Reader page logic
β βββ arbitrator.js # Arbitrator page logic
β βββ style.css # Library-themed UI styles
βββ hardhat.config.js
βββ package.json
Optimisations Applied:
- Replaced string-based
require()statements with Custom Errors (e.g.,if (...) revert BookRental__InvalidPrice()) - Switched postfix increments (
counter++) to prefix increments (++counter)
| Metric | Before | After |
|---|---|---|
| Deployment Cost | ~1,100,000 gas | 1,064,310 gas |
listItem Execution |
~226,500 gas | 225,911 gas |
| Deployment Cost | ~1,716,566 gas | 1,744,141 gas |
listItem Execution |
~166,177 gas | 137,585 gas |
rentItem Execution |
~121,957 gas | 95,388 gas |
returnItem Execution |
~52,543 gas | 30,466 gas |
Reasoning: Custom errors eliminate on-chain string storage and encode to a compact 4-byte selector, reducing both deployment and runtime gas costs. Prefix increments avoid caching the previous variable state.
We have strictly adhered to privacy requirements:
| Location | Data Stored |
|---|---|
| On-Chain | Prices, statuses, timestamps, IPFS CID reference, wallet addresses |
| Off-Chain (IPFS) | Book titles, descriptions, cover images, and any personal information |
| Function | Who | Description |
|---|---|---|
listItem() |
Lender | Add a book with daily fee + deposit |
rentItem() |
Reader | Borrow a book (pays deposit + 1 day fee) |
returnItem() |
Reader | Mark a book as returned |
confirmReturn() |
Lender / Anyone after 48h | Confirm return, trigger refund |
raiseDispute() |
Lender or Reader | Raise dispute on a returned book |
resolveDispute() |
Assigned Arbitrator | Resolve dispute, send funds to winner |
registerAsArbitrator() |
Anyone | Join the arbitrator pool |
unregisterAsArbitrator() |
Arbitrator | Leave the pool |
- ReentrancyGuard: All functions involving ETH transfers are protected against reentrancy attacks.
- Custom Errors: Gas-efficient error handling with descriptive error names.
- Access Control: Only the book owner, assigned renter, or assigned arbitrator can perform their respective actions.
- Pseudo-Random Arbitrator Selection: Uses
keccak256(block.prevrandao, block.timestamp, itemId)for randomized dispute assignment.
- The IPFS CID requires pinning. If we forget to pin it or the node goes down, the book cover might not load.
- Random arbitrator selection uses
block.prevrandao. It works fine for our project demo, but we know miners could theoretically manipulate it on mainnet. - Hardhat localhost only for now.
- MetaMask is weird about internal transactions (contract -> wallet refunds), so you have to actually check your balance to see the refund. It doesn't show in the activity tab.
- If someone interrupts the
confirmReturnflow, theactiveRentalscounter might get slightly out of sync.
MIT (feel free to use it)