Skip to content

basantsd/RWA-Tokenisation

Repository files navigation

RWA Tokenisation

Minimal RWA tokenisation flow built for the 86 assignment. Users deposit ETH into a Treasury contract and receive ERC20 tokens representing fractional ownership. Owner can withdraw ETH from the treasury.

Stack

  • Solidity + Hardhat + OpenZeppelin
  • Node.js / TypeScript backend (Express + ethers.js v6)

Setup

# root deps (hardhat, contracts)
npm install

# backend deps
cd backend && npm install && cd ..

# compile contracts
npm run compile

Running locally

Terminal 1 — local chain:

npm run node

Terminal 2 — deploy contracts:

npm run deploy:local

Copy the printed addresses into backend/.env:

RPC_URL=http://127.0.0.1:8545
RWA_TOKEN_ADDRESS=<from deploy output>
TREASURY_ADDRESS=<from deploy output>
PORT=3000

Start the backend:

cd backend && npm run dev

Tests

npm test

18 tests covering deposit flow, withdrawal flow, and edge cases (unauthorized access, zero deposit, etc).


API

GET /health

curl http://localhost:3000/health

GET /api/balance/:address

curl http://localhost:3000/api/balance/0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
{
  "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
  "rwaTokenBalance": "1000.0",
  "rwaTokenBalanceRaw": "1000000000000000000000",
  "ethBalance": "9998.9994",
  "symbol": "RWAT",
  "name": "RWA Asset Token"
}

GET /api/transactions

curl "http://localhost:3000/api/transactions?limit=10"
# filter by address
curl "http://localhost:3000/api/transactions?address=0xf39F...&limit=10"

POST /api/deposit-preview

curl -X POST http://localhost:3000/api/deposit-preview \
  -H "Content-Type: application/json" \
  -d '{"amount": "1.5"}'
{
  "inputEth": "1.5",
  "tokensToMint": "1500.0",
  "exchangeRate": "1000",
  "note": "Rate: 1000 RWAT per 1 ETH"
}

Design decisions

AccessControl on RWAToken instead of Ownable — MINTER_ROLE lets multiple contracts mint without changing ownership. Only Treasury has this role for now.

Fixed exchange rate — 1000 RWAT per ETH keeps the math simple. In production this would come from a price oracle.

previewDeposit is a pure function on-chain — backend delegates the calculation to the contract so the preview is always consistent with what deposit() will actually mint. No duplicated logic.

ReentrancyGuard on Treasury — both withdraw functions send ETH so nonReentrant is the safe default.

Transactions endpoint queries last 1000 blocks — fine for local/testnet. For production you'd use an indexer like The Graph.

About

Minimal RWA tokenisation flow

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors