mirror of
https://github.com/arkorty/B.Tech-Project-III.git
synced 2026-04-19 12:41:48 +00:00
1331 lines
52 KiB
Markdown
1331 lines
52 KiB
Markdown
# negoT8 Web3 Integration — Revised Build Guide & Milestone
|
|
|
|
> **One chain. Zero blockchain knowledge needed from users. 10 hours. Maximum judge impact.**
|
|
>
|
|
> Polygon Amoy testnet only. All blockchain is INVISIBLE to end users — they interact via Telegram and dashboard as before. The blockchain proof happens silently in the backend. Users just see a "✅ Sealed on-chain" badge and an explorer link.
|
|
>
|
|
> **Critical Rules:**
|
|
> - Gemini API calls → ALL HARDCODED (rate limits hit)
|
|
> - Users NEVER need a wallet, seed phrase, or crypto knowledge
|
|
> - Backend wallet does everything. One MetaMask account = one backend signer.
|
|
> - If blockchain is down → everything still works. Web3 is additive, not blocking.
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [10-Hour Battle Plan (Read This First)](#1-10-hour-battle-plan)
|
|
2. [What We're Actually Building](#2-what-were-actually-building)
|
|
3. [Architecture — How Blockchain Stays Invisible](#3-architecture)
|
|
4. [Tech Stack Additions (Minimal)](#4-tech-stack-additions)
|
|
5. [Updated Project Structure](#5-updated-project-structure)
|
|
6. [Updated Database Schema](#6-updated-database-schema)
|
|
7. [Mock/Hardcoded Response System (No Gemini Calls)](#7-mock-system)
|
|
8. [Smart Contract — AgreementRegistry (Deploy via Remix)](#8-smart-contract)
|
|
9. [Backend Blockchain Module](#9-backend-blockchain-module)
|
|
10. [Integration into Resolution Flow](#10-integration-into-resolution-flow)
|
|
11. [Telegram Bot Updates](#11-telegram-bot-updates)
|
|
12. [Dashboard Blockchain Components](#12-dashboard-components)
|
|
13. [Milestone Execution (Hour-by-Hour)](#13-milestone-execution)
|
|
14. [Demo Script](#14-demo-script)
|
|
15. [Setup Commands](#15-setup-commands)
|
|
16. [Troubleshooting & Failsafes](#16-troubleshooting)
|
|
|
|
---
|
|
|
|
## 1. 10-Hour Battle Plan
|
|
|
|
```
|
|
HOUR TASK STATUS
|
|
─────────────────────────────────────────────────────────────────────
|
|
0-2 WM1: Mock system + Polygon setup + Deploy contract [ ]
|
|
2-5 WM2: On-chain agreement proof + wire into resolution [ ]
|
|
5-8 WM3: Dashboard web3 cards + Telegram /proof command [ ]
|
|
8-10 WM4: Polish + demo prep + failsafes + video backup [ ]
|
|
─────────────────────────────────────────────────────────────────────
|
|
DEMO TIME 🏆 [ ]
|
|
```
|
|
|
|
### What's IN (4 features, 10 hours)
|
|
1. **Mock negotiation engine** — 3 pre-built scenarios, zero Gemini calls
|
|
2. **On-chain agreement proof** — Every resolution → tx on Polygon Amoy → PolygonScan link
|
|
3. **Dashboard blockchain card** — Shows tx hash, block number, explorer link
|
|
4. **Telegram proof delivery** — Users get "🔗 Verified on blockchain" message with link
|
|
|
|
### What's OUT (not worth the time)
|
|
- ~~Crypto payments~~ → Keep UPI only. Adding ALGO/POL payments is complexity for no judge value.
|
|
- ~~Escrow contracts~~ → Too complex for 10h. Explain it as "future roadmap" in pitch.
|
|
- ~~User wallets~~ → Users don't need wallets. Backend handles everything.
|
|
- ~~NFT minting (ERC-721)~~ → Full NFT is overkill. Storing agreement hash + event on-chain is equally impressive to judges and WAY simpler to implement.
|
|
- ~~Multiple chains~~ → Polygon Amoy only. One chain done well > two chains done badly.
|
|
|
|
---
|
|
|
|
## 2. What We're Actually Building
|
|
|
|
### The User Experience (No Blockchain Knowledge Needed)
|
|
|
|
```
|
|
BEFORE (negoT8 v2):
|
|
User A ↔ Telegram ↔ Agents negotiate ↔ Resolution text + Voice + UPI link
|
|
"Here's what we agreed on!"
|
|
|
|
AFTER (negoT8 v3 — Web3):
|
|
User A ↔ Telegram ↔ Agents negotiate ↔ Resolution text + Voice + UPI link
|
|
+ "🔗 This agreement is permanently recorded on-chain"
|
|
+ [View Proof on PolygonScan] ← clickable link
|
|
+ Dashboard shows blockchain verification badge
|
|
```
|
|
|
|
**The user NEVER:**
|
|
- Creates a wallet
|
|
- Pays gas
|
|
- Signs transactions
|
|
- Understands blockchain
|
|
- Downloads MetaMask
|
|
|
|
**The user DOES see:**
|
|
- "✅ Agreement sealed on blockchain" in their Telegram message
|
|
- A clickable link to PolygonScan showing the proof
|
|
- A blockchain verification card on the dashboard
|
|
- Their reputation score (tracked across negotiations)
|
|
|
|
### Why Judges Love This
|
|
|
|
The pitch to judges is NOT "we built a blockchain app." The pitch is:
|
|
|
|
> "We made blockchain invisible. Every AI agent negotiation produces an immutable, timestamped proof on Polygon — but the user never touches a wallet, never pays gas, never even knows what a blockchain is. They just get a 'verified' badge and a link. THAT is consumer-grade Web3."
|
|
|
|
This is the exact vision that EthIndia and Polygon sponsors want to see: **Web3 that doesn't feel like Web3.**
|
|
|
|
---
|
|
|
|
## 3. Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ USER LAYER (No blockchain) │
|
|
│ │
|
|
│ 📱 Telegram 🖥️ Dashboard │
|
|
│ "Split expenses" Shows negotiation │
|
|
│ "Find restaurant" with blockchain proof │
|
|
│ │
|
|
└──────────────────────────┬──────────────────────────────────────┘
|
|
│
|
|
┌──────────────────────────▼──────────────────────────────────────┐
|
|
│ BACKEND (Python/FastAPI) │
|
|
│ │
|
|
│ Mock Negotiation Engine → Resolution → │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────┐ │
|
|
│ │ blockchain.py │ │
|
|
│ │ │ │
|
|
│ │ 1. Takes resolution data (JSON) │ │
|
|
│ │ 2. Hashes it (SHA-256) │ │
|
|
│ │ 3. Calls AgreementRegistry.registerAgreement() │ │
|
|
│ │ 4. Returns tx_hash + explorer_url │ │
|
|
│ │ │ │
|
|
│ │ Uses: YOUR MetaMask private key (backend signer) │ │
|
|
│ │ Pays: Testnet gas (free from faucet) │ │
|
|
│ └─────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ One wallet. One signer. All agreements go on-chain. │
|
|
│ │
|
|
└──────────────────────────┬──────────────────────────────────────┘
|
|
│
|
|
┌──────────────────────────▼──────────────────────────────────────┐
|
|
│ POLYGON AMOY TESTNET │
|
|
│ │
|
|
│ Chain ID: 80002 │
|
|
│ RPC: https://rpc-amoy.polygon.technology/ │
|
|
│ Explorer: https://amoy.polygonscan.com/ │
|
|
│ │
|
|
│ AgreementRegistry Contract (pre-deployed via Remix) │
|
|
│ - registerAgreement(hash, featureType, parties, summary) │
|
|
│ - Emits AgreementRegistered event (searchable on PolygonScan) │
|
|
│ │
|
|
│ Your MetaMask account pays ~$0.0001 per tx (testnet POL) │
|
|
│ $0.01 POL ≈ 100 agreement registrations. More than enough. │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Tech Stack Additions
|
|
|
|
### Backend (Python) — One new package
|
|
```bash
|
|
pip install web3 --break-system-packages
|
|
# web3.py handles: connecting to Polygon, sending transactions, calling contracts
|
|
# That's it. One package.
|
|
```
|
|
|
|
### Frontend (Next.js) — Nothing new
|
|
```
|
|
No new npm packages needed.
|
|
The blockchain proof is just a card with a link to PolygonScan.
|
|
It's pure HTML/CSS. No ethers.js, no wallet connect, nothing.
|
|
```
|
|
|
|
### Polygon Amoy Testnet (All FREE)
|
|
```
|
|
RPC URL: https://rpc-amoy.polygon.technology/ (free, public, no key)
|
|
Chain ID: 80002
|
|
Explorer: https://amoy.polygonscan.com/
|
|
Token: POL (testnet — no real value)
|
|
Block time: ~2 seconds
|
|
Gas cost: ~$0.0001 per tx (testnet POL is free)
|
|
|
|
Faucets (get more testnet POL):
|
|
- https://faucet.quicknode.com/polygon/amoy
|
|
- https://www.alchemy.com/faucets/polygon-amoy
|
|
- https://faucets.chain.link/polygon-amoy
|
|
```
|
|
|
|
### Environment Variables (add to existing .env)
|
|
```env
|
|
# Add these 3 lines to your existing .env
|
|
POLYGON_RPC_URL=https://rpc-amoy.polygon.technology/
|
|
POLYGON_PRIVATE_KEY=your_metamask_private_key_here
|
|
AGREEMENT_CONTRACT_ADDRESS=to_be_filled_after_remix_deploy
|
|
# MOCK_MODE=true ← add this to disable Gemini calls
|
|
```
|
|
|
|
> **How to export MetaMask private key:** MetaMask → Account → ⋮ menu → Account Details → Show Private Key → Enter password → Copy. NEVER use this key for mainnet funds. This is your testnet-only backend signer.
|
|
|
|
---
|
|
|
|
## 5. Updated Project Structure
|
|
|
|
Only NEW files/folders shown. Everything else stays exactly as-is.
|
|
|
|
```
|
|
negot8/
|
|
├── backend/
|
|
│ ├── ... (ALL existing files — untouched)
|
|
│ │
|
|
│ ├── web3/ # NEW: All blockchain logic
|
|
│ │ ├── __init__.py
|
|
│ │ ├── blockchain.py # Polygon connection + agreement registration
|
|
│ │ └── contract_abi.py # ABI for AgreementRegistry contract
|
|
│ │
|
|
│ └── mock/ # NEW: Hardcoded responses (replaces Gemini)
|
|
│ ├── __init__.py
|
|
│ └── responses.py # All mock negotiation scenarios
|
|
│
|
|
├── contracts/ # NEW: Solidity contract (deploy via Remix)
|
|
│ └── AgreementRegistry.sol
|
|
│
|
|
├── dashboard/
|
|
│ ├── components/
|
|
│ │ ├── ... (existing components)
|
|
│ │ └── BlockchainProof.tsx # NEW: One component — blockchain card
|
|
```
|
|
|
|
That's it. **4 new files + 1 Solidity contract (deployed via Remix UI, not code).**
|
|
|
|
---
|
|
|
|
## 6. Updated Database Schema
|
|
|
|
Add ONE table. Don't touch existing tables.
|
|
|
|
```sql
|
|
-- NEW: On-chain agreement proofs
|
|
CREATE TABLE IF NOT EXISTS blockchain_proofs (
|
|
negotiation_id TEXT PRIMARY KEY,
|
|
tx_hash TEXT NOT NULL,
|
|
block_number INTEGER,
|
|
agreement_hash TEXT NOT NULL, -- SHA-256 of resolution data
|
|
explorer_url TEXT NOT NULL,
|
|
network TEXT DEFAULT 'polygon-amoy',
|
|
gas_used INTEGER,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
```
|
|
|
|
Add this to your existing `init_db()` function in `database.py`:
|
|
|
|
```python
|
|
# Add to the executescript block in init_db():
|
|
CREATE TABLE IF NOT EXISTS blockchain_proofs (
|
|
negotiation_id TEXT PRIMARY KEY,
|
|
tx_hash TEXT NOT NULL,
|
|
block_number INTEGER,
|
|
agreement_hash TEXT NOT NULL,
|
|
explorer_url TEXT NOT NULL,
|
|
network TEXT DEFAULT 'polygon-amoy',
|
|
gas_used INTEGER,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
```
|
|
|
|
And add these helpers:
|
|
|
|
```python
|
|
async def store_blockchain_proof(negotiation_id, tx_hash, block_number,
|
|
agreement_hash, explorer_url, gas_used=0):
|
|
async with aiosqlite.connect(DATABASE_PATH) as db:
|
|
await db.execute(
|
|
"""INSERT OR REPLACE INTO blockchain_proofs
|
|
(negotiation_id, tx_hash, block_number, agreement_hash, explorer_url, gas_used)
|
|
VALUES (?, ?, ?, ?, ?, ?)""",
|
|
(negotiation_id, tx_hash, block_number, agreement_hash, explorer_url, gas_used)
|
|
)
|
|
await db.commit()
|
|
|
|
async def get_blockchain_proof(negotiation_id):
|
|
async with aiosqlite.connect(DATABASE_PATH) as db:
|
|
db.row_factory = aiosqlite.Row
|
|
cursor = await db.execute(
|
|
"SELECT * FROM blockchain_proofs WHERE negotiation_id = ?", (negotiation_id,)
|
|
)
|
|
return await cursor.fetchone()
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Mock System
|
|
|
|
> Since Gemini API limits are hit, ALL agent responses are hardcoded. This section provides 3 complete scenarios with multi-round negotiations.
|
|
|
|
```python
|
|
# backend/mock/__init__.py
|
|
MOCK_MODE = True # Set to True — bypasses ALL Gemini API calls
|
|
```
|
|
|
|
```python
|
|
# backend/mock/responses.py
|
|
"""
|
|
3 complete negotiation scenarios with pre-built rounds.
|
|
Each round has: proposal, satisfaction scores, concessions, reasoning.
|
|
The negotiation engine iterates through these instead of calling Gemini.
|
|
"""
|
|
|
|
MOCK_SCENARIOS = {
|
|
# ─────────────────────────────────────────────────────
|
|
# SCENARIO 1: Expense Splitting (Goa Trip)
|
|
# ─────────────────────────────────────────────────────
|
|
"expenses": {
|
|
"feature_type": "expenses",
|
|
"rounds": [
|
|
{
|
|
"round_number": 1, "proposer": "A", "action": "propose",
|
|
"proposal": {
|
|
"summary": "Hotel 50-50, Fuel 60-40 (I drove), Dinner 50-50. B owes A ₹8,200",
|
|
"details": {
|
|
"hotel": {"amount": 12000, "split": "50-50"},
|
|
"fuel": {"amount": 3000, "split": "60-40"},
|
|
"dinner": {"amount": 2000, "split": "50-50"},
|
|
"settlement": {"from": "B", "to": "A", "amount": 8200,
|
|
"payee_upi": "rahul@paytm", "payee_name": "Rahul"}
|
|
},
|
|
"for_party_a": "Fuel 60-40 reflects driving effort",
|
|
"for_party_b": "Hotel and dinner are fair 50-50"
|
|
},
|
|
"satisfaction_score": 90,
|
|
"reasoning": "Opening with ideal: 60-40 fuel since I drove the entire way.",
|
|
"concessions_made": [],
|
|
"concessions_requested": ["Accept 60-40 fuel split"]
|
|
},
|
|
{
|
|
"round_number": 2, "proposer": "B", "action": "counter",
|
|
"proposal": {
|
|
"summary": "Hotel 50-50, Fuel 50-50 (I navigated + planned route), Dinner 50-50",
|
|
"details": {
|
|
"hotel": {"amount": 12000, "split": "50-50"},
|
|
"fuel": {"amount": 3000, "split": "50-50"},
|
|
"dinner": {"amount": 2000, "split": "50-50"},
|
|
"settlement": {"from": "B", "to": "A", "amount": 7500,
|
|
"payee_upi": "rahul@paytm", "payee_name": "Rahul"}
|
|
},
|
|
"for_party_a": "Equal base split on everything",
|
|
"for_party_b": "Navigation + route planning justifies equal fuel"
|
|
},
|
|
"satisfaction_score": 55,
|
|
"reasoning": "Countering with 50-50 fuel. Navigation effort was significant.",
|
|
"concessions_made": ["Accepted hotel and dinner at 50-50"],
|
|
"concessions_requested": ["Equal fuel split"]
|
|
},
|
|
{
|
|
"round_number": 3, "proposer": "A", "action": "accept",
|
|
"proposal": {
|
|
"summary": "AGREED: Hotel 50-50, Fuel 55-45 (compromise), Dinner 50-50. B owes ₹8,050",
|
|
"details": {
|
|
"hotel": {"amount": 12000, "split": "50-50"},
|
|
"fuel": {"amount": 3000, "split": "55-45"},
|
|
"dinner": {"amount": 2000, "split": "50-50"},
|
|
"settlement": {"from": "B", "to": "A", "amount": 8050,
|
|
"payee_upi": "rahul@paytm", "payee_name": "Rahul"}
|
|
},
|
|
"for_party_a": "55-45 acknowledges driving. Fair middle ground.",
|
|
"for_party_b": "Only ₹150 more than 50-50. Navigation valued."
|
|
},
|
|
"satisfaction_score": 76,
|
|
"reasoning": "55-45 is fair. Both efforts acknowledged. Accepting.",
|
|
"concessions_made": ["Fuel 60-40 → 55-45"],
|
|
"concessions_requested": []
|
|
}
|
|
]
|
|
},
|
|
|
|
# ─────────────────────────────────────────────────────
|
|
# SCENARIO 2: Restaurant Decision
|
|
# ─────────────────────────────────────────────────────
|
|
"collaborative": {
|
|
"feature_type": "collaborative",
|
|
"rounds": [
|
|
{
|
|
"round_number": 1, "proposer": "A", "action": "propose",
|
|
"proposal": {
|
|
"summary": "Thai food at Jaan, Bandra — ₹1,200 for two, great veg options",
|
|
"details": {
|
|
"restaurant": "Jaan Thai Restaurant",
|
|
"cuisine": "Thai", "location": "Hill Road, Bandra West",
|
|
"price_for_two": 1200, "rating": 4.3, "veg_friendly": True
|
|
},
|
|
"for_party_a": "Spicy Thai options, Bandra location, casual vibe",
|
|
"for_party_b": "Vegetarian-friendly menu, within ₹1,200 budget"
|
|
},
|
|
"satisfaction_score": 85,
|
|
"reasoning": "Thai is the overlap. Jaan has spice + veg options.",
|
|
"concessions_made": ["Chose Thai over spicy Indian"],
|
|
"concessions_requested": []
|
|
},
|
|
{
|
|
"round_number": 2, "proposer": "B", "action": "accept",
|
|
"proposal": {
|
|
"summary": "AGREED: Jaan Thai Restaurant, Hill Road Bandra, tonight 8 PM",
|
|
"details": {
|
|
"restaurant": "Jaan Thai Restaurant",
|
|
"cuisine": "Thai", "location": "Hill Road, Bandra West",
|
|
"price_for_two": 1200, "time": "8:00 PM"
|
|
},
|
|
"for_party_a": "Thai in Bandra — perfect match",
|
|
"for_party_b": "Budget-friendly, vegetarian menu, 4.3 rating"
|
|
},
|
|
"satisfaction_score": 88,
|
|
"reasoning": "Perfect overlap. Both sides happy. Accepting.",
|
|
"concessions_made": [], "concessions_requested": []
|
|
}
|
|
]
|
|
},
|
|
|
|
# ─────────────────────────────────────────────────────
|
|
# SCENARIO 3: Marketplace (PS5 Buy/Sell)
|
|
# ─────────────────────────────────────────────────────
|
|
"marketplace": {
|
|
"feature_type": "marketplace",
|
|
"rounds": [
|
|
{
|
|
"round_number": 1, "proposer": "A", "action": "propose",
|
|
"proposal": {
|
|
"summary": "PS5 + 2 controllers + 3 games for ₹35,000. Pickup Andheri.",
|
|
"details": {"item": "PS5 bundle", "price": 35000, "method": "pickup"},
|
|
"for_party_a": "Full asking price", "for_party_b": "Premium bundle"
|
|
},
|
|
"satisfaction_score": 95, "reasoning": "Starting at asking price.",
|
|
"concessions_made": [], "concessions_requested": ["Full price"]
|
|
},
|
|
{
|
|
"round_number": 2, "proposer": "B", "action": "counter",
|
|
"proposal": {
|
|
"summary": "PS5 bundle for ₹27,000. I'll pick up.",
|
|
"details": {"item": "PS5 bundle", "price": 27000, "method": "pickup"},
|
|
"for_party_a": "Quick sale", "for_party_b": "Under budget"
|
|
},
|
|
"satisfaction_score": 60, "reasoning": "Anchoring low.",
|
|
"concessions_made": ["Pickup offered"], "concessions_requested": ["Lower price"]
|
|
},
|
|
{
|
|
"round_number": 3, "proposer": "A", "action": "counter",
|
|
"proposal": {
|
|
"summary": "PS5 bundle + original box for ₹31,000.",
|
|
"details": {"item": "PS5 bundle + box", "price": 31000, "method": "pickup"},
|
|
"for_party_a": "Above minimum", "for_party_b": "Box adds resale value"
|
|
},
|
|
"satisfaction_score": 72, "reasoning": "Dropped ₹4K, sweetened deal with box.",
|
|
"concessions_made": ["₹35K→₹31K", "Added original box"], "concessions_requested": []
|
|
},
|
|
{
|
|
"round_number": 4, "proposer": "B", "action": "accept",
|
|
"proposal": {
|
|
"summary": "AGREED: PS5 + 2 controllers + 3 games + box for ₹29,500. Pickup Andheri.",
|
|
"details": {
|
|
"item": "PS5 + 2 controllers + 3 games + original box",
|
|
"price": 29500, "method": "pickup from Andheri",
|
|
"settlement": {"payee_upi": "seller@upi", "payee_name": "Seller", "amount": 29500}
|
|
},
|
|
"for_party_a": "Above ₹30K minimum", "for_party_b": "Full bundle under ₹30K"
|
|
},
|
|
"satisfaction_score": 78, "reasoning": "Fair split. Bundle worth it.",
|
|
"concessions_made": ["₹27K→₹29.5K"], "concessions_requested": []
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
def get_mock_scenario(feature_type: str) -> dict:
|
|
return MOCK_SCENARIOS.get(feature_type, MOCK_SCENARIOS["expenses"])
|
|
```
|
|
|
|
### Integrate Mock into Existing Code
|
|
|
|
Modify `BaseAgent.call()`:
|
|
```python
|
|
# In backend/agents/base_agent.py — modify the call method
|
|
from mock import MOCK_MODE
|
|
|
|
async def call(self, user_prompt: str) -> dict:
|
|
if MOCK_MODE:
|
|
return {"action": "propose", "proposal": {"summary": "Mock"}, "satisfaction_score": 75,
|
|
"reasoning": "Mock mode", "concessions_made": [], "concessions_requested": []}
|
|
# ... existing Gemini code unchanged ...
|
|
```
|
|
|
|
Modify `run_negotiation()`:
|
|
```python
|
|
# In backend/protocol/negotiation.py — add at the top of the function
|
|
from mock import MOCK_MODE
|
|
from mock.responses import get_mock_scenario
|
|
|
|
async def run_negotiation(negotiation_id, preferences_a, preferences_b,
|
|
user_a_id, user_b_id, feature_type, **kwargs):
|
|
await db.update_negotiation_status(negotiation_id, "active")
|
|
on_round_update = kwargs.get("on_round_update")
|
|
on_resolution = kwargs.get("on_resolution")
|
|
|
|
if MOCK_MODE:
|
|
scenario = get_mock_scenario(feature_type)
|
|
satisfaction_timeline = []
|
|
|
|
for rd in scenario["rounds"]:
|
|
await asyncio.sleep(1.0) # simulate thinking time
|
|
rn = rd["round_number"]
|
|
proposer_id = user_a_id if rd["proposer"] == "A" else user_b_id
|
|
sat = rd["satisfaction_score"]
|
|
sat_a = sat if rd["proposer"] == "A" else max(30, 100 - sat * 0.4)
|
|
sat_b = sat if rd["proposer"] == "B" else max(30, 100 - sat * 0.4)
|
|
satisfaction_timeline.append({"round": rn, "score_a": sat_a, "score_b": sat_b})
|
|
|
|
await db.save_round(negotiation_id, rn, proposer_id, rd,
|
|
rd["action"], reasoning=rd.get("reasoning", ""),
|
|
satisfaction_a=sat_a, satisfaction_b=sat_b,
|
|
concessions_made=rd.get("concessions_made", []))
|
|
|
|
if on_round_update:
|
|
await on_round_update({
|
|
"negotiation_id": negotiation_id, "round_number": rn,
|
|
"action": rd["action"], "proposal": rd["proposal"],
|
|
"satisfaction_score": sat, "reasoning": rd.get("reasoning", ""),
|
|
"proposer_id": proposer_id, "satisfaction_a": sat_a, "satisfaction_b": sat_b
|
|
})
|
|
|
|
if rd["action"] == "accept":
|
|
resolution = {
|
|
"status": "resolved", "final_proposal": rd["proposal"],
|
|
"rounds_taken": rn, "summary": rd["proposal"].get("summary", "Agreed"),
|
|
"satisfaction_timeline": satisfaction_timeline
|
|
}
|
|
await db.update_negotiation_status(negotiation_id, "resolved", resolution)
|
|
if on_resolution:
|
|
await on_resolution(resolution)
|
|
return resolution
|
|
|
|
# Fallback if no accept
|
|
last = scenario["rounds"][-1]
|
|
resolution = {
|
|
"status": "escalated", "final_proposal": last["proposal"],
|
|
"rounds_taken": len(scenario["rounds"]), "summary": "Needs human input",
|
|
"satisfaction_timeline": satisfaction_timeline
|
|
}
|
|
await db.update_negotiation_status(negotiation_id, "escalated", resolution)
|
|
if on_resolution:
|
|
await on_resolution(resolution)
|
|
return resolution
|
|
|
|
# else: original live Gemini-based negotiation (unchanged)
|
|
# ... keep all existing code below ...
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Smart Contract — AgreementRegistry
|
|
|
|
This is a tiny Solidity contract. **Deploy it via Remix IDE in 5 minutes. No Hardhat/Truffle needed.**
|
|
|
|
### The Contract
|
|
|
|
```solidity
|
|
// contracts/AgreementRegistry.sol
|
|
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
contract AgreementRegistry {
|
|
struct Agreement {
|
|
bytes32 agreementHash;
|
|
string featureType;
|
|
string summary;
|
|
uint256 timestamp;
|
|
address registeredBy;
|
|
}
|
|
|
|
mapping(string => Agreement) public agreements; // negotiationId => Agreement
|
|
string[] public agreementIds;
|
|
|
|
event AgreementRegistered(
|
|
string indexed negotiationId,
|
|
bytes32 agreementHash,
|
|
string featureType,
|
|
string summary,
|
|
uint256 timestamp
|
|
);
|
|
|
|
function registerAgreement(
|
|
string calldata negotiationId,
|
|
bytes32 agreementHash,
|
|
string calldata featureType,
|
|
string calldata summary
|
|
) external {
|
|
agreements[negotiationId] = Agreement({
|
|
agreementHash: agreementHash,
|
|
featureType: featureType,
|
|
summary: summary,
|
|
timestamp: block.timestamp,
|
|
registeredBy: msg.sender
|
|
});
|
|
agreementIds.push(negotiationId);
|
|
|
|
emit AgreementRegistered(
|
|
negotiationId, agreementHash, featureType, summary, block.timestamp
|
|
);
|
|
}
|
|
|
|
function getAgreement(string calldata negotiationId)
|
|
external view returns (Agreement memory)
|
|
{
|
|
return agreements[negotiationId];
|
|
}
|
|
|
|
function totalAgreements() external view returns (uint256) {
|
|
return agreementIds.length;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Deploy via Remix (5 minutes, no coding)
|
|
|
|
```
|
|
1. Open https://remix.ethereum.org
|
|
2. Create new file: AgreementRegistry.sol
|
|
3. Paste the contract above
|
|
4. Compile tab → Select 0.8.19 → Compile
|
|
5. Deploy tab:
|
|
- Environment: "Injected Provider - MetaMask"
|
|
- MetaMask: Switch to Polygon Amoy network (chain ID 80002)
|
|
- Click Deploy → Confirm in MetaMask
|
|
6. Copy the deployed contract address
|
|
7. Paste into .env: AGREEMENT_CONTRACT_ADDRESS=0x...
|
|
8. Done. Takes 5 minutes.
|
|
|
|
MetaMask Polygon Amoy setup (if not already):
|
|
Network Name: Polygon Amoy Testnet
|
|
RPC URL: https://rpc-amoy.polygon.technology/
|
|
Chain ID: 80002
|
|
Currency Symbol: POL
|
|
Block Explorer: https://amoy.polygonscan.com/
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Backend Blockchain Module
|
|
|
|
### Contract ABI
|
|
|
|
```python
|
|
# backend/web3/contract_abi.py
|
|
AGREEMENT_REGISTRY_ABI = [
|
|
{
|
|
"inputs": [
|
|
{"internalType": "string", "name": "negotiationId", "type": "string"},
|
|
{"internalType": "bytes32", "name": "agreementHash", "type": "bytes32"},
|
|
{"internalType": "string", "name": "featureType", "type": "string"},
|
|
{"internalType": "string", "name": "summary", "type": "string"}
|
|
],
|
|
"name": "registerAgreement",
|
|
"outputs": [],
|
|
"stateMutability": "nonpayable",
|
|
"type": "function"
|
|
},
|
|
{
|
|
"inputs": [{"internalType": "string", "name": "negotiationId", "type": "string"}],
|
|
"name": "getAgreement",
|
|
"outputs": [
|
|
{"components": [
|
|
{"internalType": "bytes32", "name": "agreementHash", "type": "bytes32"},
|
|
{"internalType": "string", "name": "featureType", "type": "string"},
|
|
{"internalType": "string", "name": "summary", "type": "string"},
|
|
{"internalType": "uint256", "name": "timestamp", "type": "uint256"},
|
|
{"internalType": "address", "name": "registeredBy", "type": "address"}
|
|
], "internalType": "struct AgreementRegistry.Agreement", "name": "", "type": "tuple"}
|
|
],
|
|
"stateMutability": "view",
|
|
"type": "function"
|
|
},
|
|
{
|
|
"inputs": [],
|
|
"name": "totalAgreements",
|
|
"outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
|
|
"stateMutability": "view",
|
|
"type": "function"
|
|
},
|
|
{
|
|
"anonymous": False,
|
|
"inputs": [
|
|
{"indexed": True, "internalType": "string", "name": "negotiationId", "type": "string"},
|
|
{"indexed": False, "internalType": "bytes32", "name": "agreementHash", "type": "bytes32"},
|
|
{"indexed": False, "internalType": "string", "name": "featureType", "type": "string"},
|
|
{"indexed": False, "internalType": "string", "name": "summary", "type": "string"},
|
|
{"indexed": False, "internalType": "uint256", "name": "timestamp", "type": "uint256"}
|
|
],
|
|
"name": "AgreementRegistered",
|
|
"type": "event"
|
|
}
|
|
]
|
|
```
|
|
|
|
### Blockchain Module (The Whole Thing)
|
|
|
|
```python
|
|
# backend/web3/__init__.py
|
|
pass
|
|
```
|
|
|
|
```python
|
|
# backend/web3/blockchain.py
|
|
"""
|
|
The ENTIRE blockchain integration in one file.
|
|
Registers agreement proofs on Polygon Amoy via AgreementRegistry contract.
|
|
Users never interact with this. It's all backend.
|
|
"""
|
|
import hashlib
|
|
import json
|
|
from web3 import Web3
|
|
from web3.contract_abi import AGREEMENT_REGISTRY_ABI
|
|
import os
|
|
|
|
# ─── Configuration ───
|
|
POLYGON_RPC = os.getenv("POLYGON_RPC_URL", "https://rpc-amoy.polygon.technology/")
|
|
PRIVATE_KEY = os.getenv("POLYGON_PRIVATE_KEY", "")
|
|
CONTRACT_ADDRESS = os.getenv("AGREEMENT_CONTRACT_ADDRESS", "")
|
|
EXPLORER_BASE = "https://amoy.polygonscan.com"
|
|
|
|
# ─── Setup ───
|
|
w3 = Web3(Web3.HTTPProvider(POLYGON_RPC))
|
|
account = None
|
|
contract = None
|
|
|
|
if PRIVATE_KEY and CONTRACT_ADDRESS:
|
|
try:
|
|
account = w3.eth.account.from_key(PRIVATE_KEY)
|
|
contract = w3.eth.contract(
|
|
address=Web3.to_checksum_address(CONTRACT_ADDRESS),
|
|
abi=AGREEMENT_REGISTRY_ABI
|
|
)
|
|
print(f"✅ Blockchain connected: Polygon Amoy | Account: {account.address}")
|
|
except Exception as e:
|
|
print(f"⚠️ Blockchain setup failed: {e}. Will use mock mode.")
|
|
else:
|
|
print("⚠️ Blockchain not configured. Agreement proofs will be mocked.")
|
|
|
|
|
|
def hash_agreement(resolution_data: dict) -> bytes:
|
|
"""SHA-256 hash of the agreement data → bytes32 for the contract."""
|
|
canonical = json.dumps(resolution_data, sort_keys=True, default=str)
|
|
return hashlib.sha256(canonical.encode()).digest()
|
|
|
|
|
|
async def register_agreement_on_chain(negotiation_id: str, feature_type: str,
|
|
summary: str, resolution_data: dict) -> dict:
|
|
"""
|
|
Register an agreement proof on Polygon Amoy.
|
|
Called automatically after every resolved negotiation.
|
|
The user NEVER calls this — it's invisible backend magic.
|
|
|
|
Returns: {tx_hash, block_number, explorer_url, agreement_hash, success}
|
|
"""
|
|
agreement_hash = hash_agreement(resolution_data)
|
|
|
|
# ─── If blockchain not configured, return mock proof ───
|
|
if not contract or not account:
|
|
mock_hash = "0x" + agreement_hash.hex()
|
|
return {
|
|
"success": True, "mock": True,
|
|
"tx_hash": f"0xMOCK_{negotiation_id}_{'a' * 40}",
|
|
"block_number": 0,
|
|
"agreement_hash": mock_hash,
|
|
"explorer_url": f"{EXPLORER_BASE}",
|
|
"network": "polygon-amoy (mock)"
|
|
}
|
|
|
|
try:
|
|
# Build the transaction
|
|
nonce = w3.eth.get_transaction_count(account.address)
|
|
|
|
tx = contract.functions.registerAgreement(
|
|
negotiation_id,
|
|
agreement_hash, # bytes32
|
|
feature_type,
|
|
summary[:256] # cap summary length for gas efficiency
|
|
).build_transaction({
|
|
"from": account.address,
|
|
"nonce": nonce,
|
|
"gas": 300000, # generous gas limit for testnet
|
|
"gasPrice": w3.to_wei("30", "gwei"), # Amoy gas price
|
|
"chainId": 80002 # Polygon Amoy
|
|
})
|
|
|
|
# Sign and send
|
|
signed = w3.eth.account.sign_transaction(tx, PRIVATE_KEY)
|
|
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
|
|
tx_hash_hex = tx_hash.hex()
|
|
|
|
# Wait for confirmation (Amoy is fast, ~2 seconds)
|
|
receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=30)
|
|
|
|
result = {
|
|
"success": True, "mock": False,
|
|
"tx_hash": tx_hash_hex,
|
|
"block_number": receipt.blockNumber,
|
|
"agreement_hash": "0x" + agreement_hash.hex(),
|
|
"explorer_url": f"{EXPLORER_BASE}/tx/0x{tx_hash_hex}",
|
|
"gas_used": receipt.gasUsed,
|
|
"network": "polygon-amoy"
|
|
}
|
|
|
|
print(f"✅ Agreement registered on-chain: {result['explorer_url']}")
|
|
return result
|
|
|
|
except Exception as e:
|
|
print(f"❌ Blockchain registration failed: {e}")
|
|
# Graceful fallback — negotiation still works, just no on-chain proof
|
|
return {
|
|
"success": False, "mock": True, "error": str(e),
|
|
"tx_hash": f"0xFAILED_{negotiation_id}",
|
|
"block_number": 0,
|
|
"agreement_hash": "0x" + agreement_hash.hex(),
|
|
"explorer_url": EXPLORER_BASE,
|
|
"network": "polygon-amoy (failed — see error)"
|
|
}
|
|
|
|
|
|
def verify_agreement_on_chain(negotiation_id: str) -> dict:
|
|
"""
|
|
Verify an agreement exists on-chain by reading the contract.
|
|
Used by dashboard to confirm blockchain state.
|
|
"""
|
|
if not contract:
|
|
return {"verified": False, "reason": "Contract not configured"}
|
|
|
|
try:
|
|
result = contract.functions.getAgreement(negotiation_id).call()
|
|
return {
|
|
"verified": True,
|
|
"agreement_hash": "0x" + result[0].hex(),
|
|
"feature_type": result[1],
|
|
"summary": result[2],
|
|
"timestamp": result[3],
|
|
"registered_by": result[4]
|
|
}
|
|
except Exception as e:
|
|
return {"verified": False, "reason": str(e)}
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Integration into Resolution Flow
|
|
|
|
Add blockchain registration to your existing `handle_resolution` function:
|
|
|
|
```python
|
|
# In your run.py or wherever handle_resolution lives:
|
|
|
|
from web3.blockchain import register_agreement_on_chain
|
|
import database as db
|
|
|
|
# Inside handle_resolution, AFTER the existing UPI + voice + analytics logic:
|
|
|
|
async def handle_resolution(negotiation_id, resolution, feature_type,
|
|
user_a_id, user_b_id, bot_a, bot_b,
|
|
preferences_a, preferences_b):
|
|
|
|
# ... existing code: build summary_text, UPI link, voice summary, analytics ...
|
|
|
|
# ─── NEW: Register agreement on Polygon (invisible to user) ───
|
|
blockchain_result = await register_agreement_on_chain(
|
|
negotiation_id=negotiation_id,
|
|
feature_type=feature_type,
|
|
summary=resolution.get("summary", "Agreement reached"),
|
|
resolution_data=resolution
|
|
)
|
|
|
|
# Store proof in DB
|
|
await db.store_blockchain_proof(
|
|
negotiation_id=negotiation_id,
|
|
tx_hash=blockchain_result["tx_hash"],
|
|
block_number=blockchain_result.get("block_number", 0),
|
|
agreement_hash=blockchain_result["agreement_hash"],
|
|
explorer_url=blockchain_result["explorer_url"],
|
|
gas_used=blockchain_result.get("gas_used", 0)
|
|
)
|
|
|
|
# Add blockchain badge to the Telegram message
|
|
if blockchain_result.get("success"):
|
|
blockchain_text = (
|
|
f"\n\n🔗 *Verified on Blockchain*\n"
|
|
f"This agreement is permanently recorded on Polygon.\n"
|
|
f"[View Proof on PolygonScan]({blockchain_result['explorer_url']})"
|
|
)
|
|
else:
|
|
blockchain_text = "\n\n🔗 _Blockchain proof pending..._"
|
|
|
|
summary_text += blockchain_text
|
|
|
|
# ... continue with existing: send to users, voice notes, etc. ...
|
|
```
|
|
|
|
That's the ENTIRE integration. One function call added to your existing flow.
|
|
|
|
---
|
|
|
|
## 11. Telegram Bot Updates
|
|
|
|
### New Command: /proof
|
|
|
|
```python
|
|
# Add to your telegram bot.py
|
|
|
|
async def proof_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|
"""Show blockchain proof for a negotiation."""
|
|
if not context.args:
|
|
await update.message.reply_text(
|
|
"🔗 *View blockchain proof*\n\n"
|
|
"Usage: `/proof <negotiation_id>`\n"
|
|
"You'll find the ID in your resolution messages.",
|
|
parse_mode="Markdown"
|
|
)
|
|
return
|
|
|
|
neg_id = context.args[0]
|
|
proof = await db.get_blockchain_proof(neg_id)
|
|
|
|
if not proof:
|
|
await update.message.reply_text("❌ No blockchain proof found for that negotiation.")
|
|
return
|
|
|
|
await update.message.reply_text(
|
|
f"🔗 *Blockchain Proof*\n\n"
|
|
f"📋 Negotiation: `{neg_id}`\n"
|
|
f"🔐 Agreement Hash: `{proof['agreement_hash'][:20]}...`\n"
|
|
f"⛓️ Block: #{proof['block_number']}\n"
|
|
f"📡 Network: Polygon Amoy\n\n"
|
|
f"[🔍 View on PolygonScan]({proof['explorer_url']})\n\n"
|
|
f"_This agreement is permanently and immutably recorded on the blockchain. "
|
|
f"Neither party can alter or deny it._",
|
|
parse_mode="Markdown",
|
|
disable_web_page_preview=True
|
|
)
|
|
|
|
# Register in create_bot():
|
|
# app.add_handler(CommandHandler("proof", proof_command))
|
|
# Add to COMMANDS: BotCommand("proof", "View blockchain proof for a negotiation"),
|
|
```
|
|
|
|
No `/wallet` command. No `/reputation` command. Users don't need them. Keep it clean.
|
|
|
|
---
|
|
|
|
## 12. Dashboard Components
|
|
|
|
### BlockchainProof Component
|
|
|
|
```typescript
|
|
// dashboard/components/BlockchainProof.tsx
|
|
import { ExternalLink, Shield, CheckCircle2 } from 'lucide-react';
|
|
|
|
interface BlockchainProofProps {
|
|
txHash: string;
|
|
blockNumber: number;
|
|
explorerUrl: string;
|
|
agreementHash: string;
|
|
network: string;
|
|
}
|
|
|
|
export function BlockchainProof({
|
|
txHash, blockNumber, explorerUrl, agreementHash, network
|
|
}: BlockchainProofProps) {
|
|
return (
|
|
<div className="bg-gradient-to-br from-violet-50 to-purple-50 rounded-xl p-5 border border-violet-200 shadow-sm">
|
|
<div className="flex items-center gap-2 mb-4">
|
|
<div className="bg-violet-600 p-1.5 rounded-lg">
|
|
<Shield className="w-4 h-4 text-white" />
|
|
</div>
|
|
<h3 className="text-sm font-bold text-violet-900">On-Chain Proof</h3>
|
|
<div className="ml-auto flex items-center gap-1 bg-green-100 text-green-700 text-xs px-2 py-0.5 rounded-full">
|
|
<CheckCircle2 className="w-3 h-3" />
|
|
Verified
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-3 text-sm">
|
|
<div>
|
|
<span className="text-gray-500 text-xs uppercase tracking-wide">Transaction</span>
|
|
<p className="font-mono text-violet-700 text-xs mt-0.5 break-all">
|
|
{txHash.slice(0, 22)}...{txHash.slice(-8)}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex gap-4">
|
|
<div>
|
|
<span className="text-gray-500 text-xs uppercase tracking-wide">Block</span>
|
|
<p className="font-mono font-bold text-gray-800">#{blockNumber}</p>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-500 text-xs uppercase tracking-wide">Network</span>
|
|
<p className="font-medium text-gray-800">Polygon Amoy</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<span className="text-gray-500 text-xs uppercase tracking-wide">Agreement Hash</span>
|
|
<p className="font-mono text-xs text-gray-600 mt-0.5">
|
|
{agreementHash.slice(0, 18)}...
|
|
</p>
|
|
</div>
|
|
|
|
<a
|
|
href={explorerUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-center justify-center gap-2 bg-violet-600 hover:bg-violet-700
|
|
text-white rounded-lg px-4 py-2.5 text-sm font-medium transition mt-2"
|
|
>
|
|
<ExternalLink className="w-4 h-4" />
|
|
View on PolygonScan
|
|
</a>
|
|
</div>
|
|
|
|
<p className="text-xs text-gray-400 mt-3 text-center">
|
|
This agreement is immutable and publicly verifiable
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Wire into Existing Negotiation Page
|
|
|
|
In your `dashboard/app/negotiation/[id]/page.tsx`, add after the existing resolution display:
|
|
|
|
```typescript
|
|
// Fetch blockchain proof alongside negotiation data
|
|
const proof = negotiationData?.blockchain_proof;
|
|
|
|
// Render after ResolutionCard:
|
|
{proof && proof.tx_hash && (
|
|
<BlockchainProof
|
|
txHash={proof.tx_hash}
|
|
blockNumber={proof.block_number}
|
|
explorerUrl={proof.explorer_url}
|
|
agreementHash={proof.agreement_hash}
|
|
network="polygon-amoy"
|
|
/>
|
|
)}
|
|
```
|
|
|
|
Add to your FastAPI endpoint:
|
|
|
|
```python
|
|
# In your /api/negotiations/{id} endpoint, add:
|
|
proof = await db.get_blockchain_proof(negotiation_id)
|
|
|
|
return {
|
|
"negotiation": neg,
|
|
"rounds": rounds,
|
|
"participants": participants,
|
|
"analytics": analytics,
|
|
"blockchain_proof": dict(proof) if proof else None # NEW
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 13. Milestone Execution
|
|
|
|
### WM1: Mock System + Polygon Setup (Hour 0 → 2)
|
|
|
|
```
|
|
□ Step 1: Create backend/mock/__init__.py (MOCK_MODE = True)
|
|
□ Step 2: Create backend/mock/responses.py (copy from Section 7)
|
|
□ Step 3: Modify BaseAgent to check MOCK_MODE (Section 7)
|
|
□ Step 4: Modify run_negotiation to use mock scenarios (Section 7)
|
|
□ Step 5: Test mock negotiation — run all 3 scenarios with no Gemini calls
|
|
□ Step 6: pip install web3
|
|
□ Step 7: Deploy AgreementRegistry.sol via Remix IDE (Section 8)
|
|
□ Step 8: Copy contract address to .env
|
|
□ Step 9: Export MetaMask private key to .env
|
|
□ Step 10: Get more testnet POL from faucet if needed
|
|
|
|
✅ SUCCESS: Mock negotiations run. Contract deployed. web3.py connects to Polygon.
|
|
```
|
|
|
|
### WM2: On-Chain Agreement Proof (Hour 2 → 5)
|
|
|
|
```
|
|
□ Step 1: Create backend/web3/__init__.py
|
|
□ Step 2: Create backend/web3/contract_abi.py (Section 9)
|
|
□ Step 3: Create backend/web3/blockchain.py (Section 9)
|
|
□ Step 4: Add blockchain_proofs table to database.py (Section 6)
|
|
□ Step 5: Add register_agreement_on_chain call to handle_resolution (Section 10)
|
|
□ Step 6: Test standalone: register one agreement, check PolygonScan
|
|
|
|
Test script:
|
|
import asyncio
|
|
from web3.blockchain import register_agreement_on_chain
|
|
|
|
async def test():
|
|
result = await register_agreement_on_chain(
|
|
"test_001", "expenses",
|
|
"Goa trip settled: Fuel 55-45, Hotel 50-50",
|
|
{"settlement": 8050, "parties": ["Rahul", "Priya"]}
|
|
)
|
|
print(f"TX: {result['tx_hash']}")
|
|
print(f"Explorer: {result['explorer_url']}")
|
|
print(f"Block: {result['block_number']}")
|
|
|
|
asyncio.run(test())
|
|
|
|
□ Step 7: Open the explorer URL — confirm transaction visible with event log
|
|
□ Step 8: Run full flow: mock negotiation → resolution → blockchain proof → Telegram message
|
|
|
|
✅ SUCCESS: PolygonScan shows your agreement. Telegram message includes "🔗 Verified" link.
|
|
```
|
|
|
|
### WM3: Dashboard + Telegram /proof (Hour 5 → 8)
|
|
|
|
```
|
|
□ Step 1: Create dashboard/components/BlockchainProof.tsx (Section 12)
|
|
□ Step 2: Add blockchain_proof to your /api/negotiations/{id} endpoint
|
|
□ Step 3: Render BlockchainProof in negotiation detail page
|
|
□ Step 4: Add /proof command to Telegram bot (Section 11)
|
|
□ Step 5: Test: negotiation → dashboard shows blockchain card → /proof shows proof
|
|
|
|
✅ SUCCESS: Dashboard has pretty blockchain proof card. /proof works in Telegram.
|
|
```
|
|
|
|
### WM4: Polish + Demo Prep (Hour 8 → 10)
|
|
|
|
```
|
|
□ Step 1: Run all 3 demo scenarios end-to-end (mock + blockchain + Telegram + dashboard)
|
|
□ Step 2: Pre-register 3+ agreements on-chain (so dashboard has data even if live demo fails)
|
|
□ Step 3: Screenshot PolygonScan proof pages as backup
|
|
□ Step 4: Record video backup of full flow
|
|
□ Step 5: Test UPI links still work (no regressions)
|
|
□ Step 6: Test voice summaries still work (no regressions)
|
|
□ Step 7: Verify mock mode doesn't break any existing features
|
|
□ Step 8: Prepare pitch script (Section 14)
|
|
□ Step 9: Get testnet POL balance > 0.5 POL (enough for 50+ registrations)
|
|
□ Step 10: Deep breath. You're ready.
|
|
|
|
✅ SUCCESS: Everything works. Video backup saved. Pitch rehearsed.
|
|
```
|
|
|
|
---
|
|
|
|
## 14. Demo Script
|
|
|
|
### Demo 1: Restaurant Decision (60 sec)
|
|
```
|
|
User A: /coordinate @priya → "Dinner tonight, Thai or Indian, Bandra, ₹1500"
|
|
User B: /coordinate @rahul → "Thai or Mediterranean, vegetarian, ₹1200"
|
|
|
|
→ Mock negotiation: 2 rounds → Jaan Thai Restaurant, Bandra
|
|
→ Telegram shows: resolution text + voice note
|
|
→ NEW: "🔗 Verified on Blockchain" + PolygonScan link
|
|
→ Click link → PolygonScan shows the AgreementRegistered event
|
|
|
|
SAY: "This dinner plan is now permanently on the blockchain. Neither
|
|
side can later say 'I never agreed to that.' And the user never
|
|
touched a wallet or paid gas — it just happened."
|
|
```
|
|
|
|
### Demo 2: Expense Splitting (90 sec)
|
|
```
|
|
User A: "Split Goa trip. Hotel 12K, fuel 3K, dinner 2K. UPI: rahul@paytm"
|
|
User B: "Fuel should be 50-50. I navigated."
|
|
|
|
→ Mock: 3 rounds → Fuel 55-45 → B owes ₹8,050
|
|
→ UPI payment button (tap to pay)
|
|
→ Voice note (different voices for A and B)
|
|
→ NEW: "🔗 Verified on Blockchain" + PolygonScan link
|
|
→ Dashboard shows: satisfaction chart + fairness score + blockchain proof card
|
|
|
|
SAY: "Look — the settlement is on UPI for India. But the PROOF that
|
|
they agreed to 55-45 fuel? That's on Polygon. Immutable. If there's
|
|
a dispute next month, the blockchain has the truth."
|
|
```
|
|
|
|
### Demo 3: PS5 Marketplace (90 sec)
|
|
```
|
|
Seller: "PS5 + 2 controllers, ₹35K, minimum ₹30K"
|
|
Buyer: "Max ₹28K"
|
|
|
|
→ Mock: 4 rounds of haggling → Settles at ₹29,500
|
|
→ UPI payment + voice notes
|
|
→ NEW: "🔗 Deal sealed on blockchain"
|
|
→ /proof test_neg_id → shows full proof in Telegram
|
|
→ Dashboard: all 3 negotiations with blockchain cards
|
|
|
|
SAY: "Four rounds of AI haggling. ₹29,500. UPI to pay. Blockchain to
|
|
prove. This is what a trustless marketplace looks like when AI agents
|
|
handle the negotiation and blockchain handles the trust."
|
|
```
|
|
|
|
### The Closing Line
|
|
> "negoT8 makes coordination effortless. AI does the talking. Blockchain does the trusting. And the user? They just send a message on Telegram. That's it. That's consumer Web3."
|
|
|
|
---
|
|
|
|
## 15. Setup Commands
|
|
|
|
```bash
|
|
# 1. Install web3.py
|
|
pip install web3 --break-system-packages
|
|
|
|
# 2. Deploy contract via Remix (see Section 8 — manual, 5 min)
|
|
# → Copy contract address
|
|
|
|
# 3. Export MetaMask private key
|
|
# MetaMask → Account → ⋮ → Account Details → Show Private Key
|
|
|
|
# 4. Add to .env
|
|
echo 'POLYGON_RPC_URL=https://rpc-amoy.polygon.technology/' >> .env
|
|
echo 'POLYGON_PRIVATE_KEY=your_private_key' >> .env
|
|
echo 'AGREEMENT_CONTRACT_ADDRESS=0x_your_contract_address' >> .env
|
|
echo 'MOCK_MODE=true' >> .env
|
|
|
|
# 5. Get more testnet POL (optional but recommended)
|
|
# https://faucet.quicknode.com/polygon/amoy
|
|
# https://www.alchemy.com/faucets/polygon-amoy
|
|
# https://faucets.chain.link/polygon-amoy
|
|
|
|
# 6. Test blockchain connection
|
|
python3 -c "
|
|
from web3 import Web3
|
|
w3 = Web3(Web3.HTTPProvider('https://rpc-amoy.polygon.technology/'))
|
|
print(f'Connected: {w3.is_connected()}')
|
|
print(f'Chain ID: {w3.eth.chain_id}')
|
|
print(f'Latest block: {w3.eth.block_number}')
|
|
"
|
|
```
|
|
|
|
---
|
|
|
|
## 16. Troubleshooting & Failsafes
|
|
|
|
### "Polygon RPC is slow/down"
|
|
```python
|
|
# Fallback RPCs (add to blockchain.py):
|
|
FALLBACK_RPCS = [
|
|
"https://rpc-amoy.polygon.technology/",
|
|
"https://polygon-amoy-bor-rpc.publicnode.com",
|
|
"https://polygon-amoy.drpc.org",
|
|
]
|
|
# Try each until one works
|
|
```
|
|
|
|
### "Contract call fails"
|
|
```python
|
|
# The register_agreement_on_chain function already has try/except with mock fallback.
|
|
# If blockchain fails → negotiation still works → user sees "proof pending"
|
|
# Show pre-registered proofs from earlier test runs
|
|
```
|
|
|
|
### "Out of testnet POL"
|
|
```
|
|
Use any of these faucets:
|
|
- https://faucet.quicknode.com/polygon/amoy (tweet + claim)
|
|
- https://www.alchemy.com/faucets/polygon-amoy (sign up, 0.5/day)
|
|
- https://faucets.chain.link/polygon-amoy (connect wallet)
|
|
Each tx costs ~0.0001 POL. $0.01 POL = 100 transactions.
|
|
```
|
|
|
|
### "I don't want to deploy a contract (too risky / no time)"
|
|
```
|
|
SIMPLEST FALLBACK: Skip the contract entirely.
|
|
Just send 0-value transactions with agreement data in the input field:
|
|
|
|
tx = {
|
|
"to": account.address, # send to self
|
|
"value": 0,
|
|
"data": w3.to_hex(json.dumps(resolution).encode()),
|
|
"gas": 100000,
|
|
"gasPrice": w3.to_wei("30", "gwei"),
|
|
"nonce": w3.eth.get_transaction_count(account.address),
|
|
"chainId": 80002
|
|
}
|
|
|
|
This puts the agreement data on-chain WITHOUT a smart contract.
|
|
PolygonScan will show the data in "Input Data" field.
|
|
Less clean than a contract, but works in 5 minutes.
|
|
```
|
|
|
|
### Demo-Day Failsafes
|
|
1. **Pre-register 3 agreements** before the demo so PolygonScan already has data
|
|
2. **Screenshot** every PolygonScan proof page as image backup
|
|
3. **Video record** a perfect full run the night before
|
|
4. **Mock blockchain fallback** — if blockchain is slow during demo, the mock result still shows a nice "proof pending" badge
|
|
|
|
---
|
|
|
|
## Quick Reference Card
|
|
|
|
```
|
|
⛓️ Chain: Polygon Amoy Testnet (Chain ID: 80002)
|
|
🔗 RPC: https://rpc-amoy.polygon.technology/
|
|
🔍 Explorer: https://amoy.polygonscan.com/
|
|
💰 Gas: ~0.0001 POL per tx (free from faucet)
|
|
📝 Contract: AgreementRegistry.sol (deployed via Remix)
|
|
🐍 Library: web3.py (pip install web3)
|
|
🤖 Mock Mode: MOCK_MODE=true (no Gemini calls)
|
|
|
|
USER SEES:
|
|
"🔗 Verified on Blockchain" + PolygonScan link
|
|
That's it. No wallets. No gas. No crypto knowledge.
|
|
|
|
BACKEND DOES:
|
|
SHA-256(resolution) → registerAgreement() → PolygonScan
|
|
|
|
NEW FILES: 4 total
|
|
backend/web3/blockchain.py
|
|
backend/web3/contract_abi.py
|
|
backend/mock/responses.py
|
|
dashboard/components/BlockchainProof.tsx
|
|
|
|
NEW COMMAND: /proof <negotiation_id>
|
|
NEW TABLE: blockchain_proofs (1 table added)
|
|
NEW .ENV VARS: 3 (RPC URL, private key, contract address)
|
|
|
|
DEMO: 🍕 Restaurant → 💰 Expenses → 🛒 Marketplace
|
|
Each shows "🔗 Verified" + PolygonScan link
|
|
|
|
BUILD TIME: 10 hours across 4 milestones
|
|
```
|
|
|
|
---
|
|
|
|
*Web3 should be invisible. If the user has to understand blockchain to use your product, you've failed. negoT8 puts agreements on-chain silently — the user just gets a link that proves it. That's the future.*
|
|
|
|
**Go build it. Go win it. 🏆**
|