# FF For Future — End-to-End Deploy Runbook

**Audience:** Rob (Stable Pay) + V>> (CodeTonight) + any third-party verifier with a Sepolia RPC endpoint.
**Goal:** From zero → deployed contract → on-chain IDR → replay IDR → fetchable by anyone with the contract address, in ~30 minutes.

---

## Phase A — One-time setup (~5 minutes)

### A1. MetaMask + Sepolia testnet ETH

1. Install [MetaMask](https://metamask.io/) if you haven't (Chrome/Firefox extension).
2. Switch the network selector to **Sepolia Test Network** (unhide via Settings → Advanced → "Show test networks").
3. Get free Sepolia ETH (you need ~0.05 ETH for the whole demo):
   - [sepoliafaucet.com](https://sepoliafaucet.com/) (Alchemy, 0.5 ETH/day with Alchemy account)
   - [faucetlink.to/sepolia](https://faucetlink.to/sepolia) (aggregator)
   - [Google Cloud faucet](https://cloud.google.com/application/web3/faucet/ethereum/sepolia) (0.05 ETH every 24h)

### A2. RPC endpoint

Pick one (free tier is fine):

- **Infura** — sign up at infura.io, create a project, copy the Sepolia RPC URL
- **Alchemy** — sign up at alchemy.com, create app on Sepolia, copy the HTTPS URL

Stash in your shell:

```bash
export INFURA_PROJECT_ID=0123abc...                       # if Infura
# OR
export SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/...  # if Alchemy
```

### A3. Pinata account

- Sign up at [pinata.cloud](https://www.pinata.cloud/) (free tier = 1 GB, way more than we need)
- API Keys → New Key → toggle `pinFileToIPFS` permission → copy the JWT
- Stash:

```bash
# Preferred — macOS Keychain (no plaintext on disk):
security add-generic-password -s pinata-jwt-ff -w 'eyJhbG...'

# Alternative — env var in your shell rc:
export PINATA_JWT='eyJhbG...'
```

### A4. Analyser wallet (NO REAL FUNDS — testnet only)

In MetaMask: Account → Add Account. This is the "analyser" wallet — it writes IDR CIDs back to the contract. Fund it with ~0.01 Sepolia ETH from your main test wallet.

Export the private key (Account details → Show private key) and stash:

```bash
security add-generic-password -s sepolia-analyser-key -w '0xabc...'
# OR
export ANALYSER_PRIVATE_KEY='0xabc...'
```

---

## Phase B — Deploy the contract (~5 minutes, Remix IDE)

### B1. Open Remix

[remix.ethereum.org](https://remix.ethereum.org/) → File Explorer → Create file `FinancialDataRegistry.sol` → paste the contents of `contracts/FinancialDataRegistry.sol` from this repo.

### B2. Compile

- Left rail → Solidity Compiler tab
- Compiler version: `0.8.20+commit...` (or any 0.8.20+)
- Advanced configurations → Enable optimisation → 200 runs
- Click **Compile FinancialDataRegistry.sol**
- Expected: green tick, no errors

### B3. Deploy to Sepolia

- Left rail → Deploy & Run Transactions tab
- Environment: **Injected Provider — MetaMask** (MetaMask popup confirms Sepolia network)
- Contract: `FinancialDataRegistry`
- Click **Deploy** → MetaMask popup → Confirm
- Wait ~15 seconds → look at the bottom log: contract address appears

**Copy the deployed contract address.** Stash:

```bash
export TOKEN_ADDRESS=0xYourDeployedAddress
```

### B4. Verify on Etherscan (optional, recommended)

- Go to `https://sepolia.etherscan.io/address/0xYourDeployedAddress`
- Contract tab → Verify and Publish → enter source code, compiler 0.8.20, 200 runs, no constructor args
- After verification, anyone can read the contract source + call view functions from Etherscan UI

---

## Phase C — Pin the borrower data + publish on-chain (~3 minutes)

### C1. Pin the synthetic borrower JSON to IPFS

```bash
cd ~/CodeTonight/ff-clone  # or wherever you cloned this repo
python3 scripts/pinata_pin.py --file data/thornfields-capital-ag.json --name thornfields-2026-q1
```

Output: a CID like `bafkreigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi`. Save it:

```bash
export DATA_CID=bafkrei...
```

### C2. Verify the pin worked

```bash
python3 scripts/ipfs_fetch.py $DATA_CID --decode-json
```

Should print the borrower JSON.

### C3. Publish the CID to the contract via Remix

In Remix → Deploy & Run Transactions → Deployed Contracts → expand your contract → find `publishRecord`:

| Field      | Value                                                         |
|------------|---------------------------------------------------------------|
| recordId   | `0xee02464820f98c520a6f0d4a39e73ec9dadd625c29e876ad1cf3f8e4371f9458` (this is `keccak256("data:thornfields-2026-q1")`) |
| label      | `data:thornfields-2026-q1`                                    |
| cid        | (paste your `$DATA_CID`)                                      |

> 💡 To compute the recordId for a different deal in your browser console:
> `ethers.keccak256(ethers.toUtf8Bytes("data:your-deal-name"))`

Click **transact** → MetaMask confirms → wait ~15 seconds. Look at the contract events tab — you should see `RecordPublished`.

---

## Phase D — Run the rwa.analyse handler (~2 minutes)

The handler (handler/rwa_analyse.py) reads the contract, fetches the data, runs the council, pins the IDR, and writes the IDR CID back.

For the live POC, wire the real syscalls into the handler via `scripts/dispatch_rwa_analyse.py` (template provided — fill in your env vars and run):

```bash
python3 scripts/dispatch_rwa_analyse.py \
    --token-address $TOKEN_ADDRESS \
    --deal thornfields-2026-q1 \
    --model claude-3.5
```

The handler emits NDJSON events. Expected sequence:

```
started → delta(data_cid_resolved) → delta(borrower_loaded) →
delta(council_*) → delta(council_completed) → delta(idr_pinned) →
completed (with the full IDR + idr_cid + anchor_tx)
```

> Total wall-clock: ~60-90s (council ~30s, Pinata ~5s, Sepolia tx ~15s).

---

## Phase E — Replay for falsifiability (~2 minutes)

Same command, different model:

```bash
python3 scripts/dispatch_rwa_analyse.py \
    --token-address $TOKEN_ADDRESS \
    --deal thornfields-2026-q1 \
    --model gemini-1.5-pro
```

This produces a **second IDR** with the same `data_cid` (so anyone can verify both analysed the same underlying data) but potentially different `risk_rating` / `key_flags` / `confidence`. That's the falsification property: the architecture doesn't ask you to trust an analysis — it gives you the materials to challenge it.

---

## Phase F — Third-party verification

Anyone in the world with a Sepolia RPC endpoint can now run:

```javascript
// From any Node script, Etherscan UI, or browser console:
const ids = await registry.getAllRecordIds();
for (const id of ids) {
    const [cid, label, updatedAt, version] = await registry.getRecord(id);
    console.log(`${label} → ipfs://${cid} (v${version}, updated ${new Date(updatedAt * 1000).toISOString()})`);
}
```

Then fetch the IDRs from IPFS (any gateway) and compare the two model verdicts. The disagreement IS the demo.

---

## Acceptance criteria (from the 2026-05-05 build plan)

| # | Criterion                                              | How to check                                            |
|---|--------------------------------------------------------|---------------------------------------------------------|
| 1 | `data:` record readable from Sepolia via public RPC    | `cast call $TOKEN_ADDRESS "getCID(bytes32)" $DATA_KEY` |
| 2 | HAPPI analysis completes in < 120 s                    | Wall-clock the dispatch script                         |
| 3 | IDR JSON contains all HAPPI/1.1 required fields        | `python -m json.tool < idr.json`                        |
| 4 | `idr:` record readable after analysis                  | Same `getCID` call with `IDR_KEY`                       |
| 5 | IDR fetchable from IPFS via public Pinata gateway      | `curl https://gateway.pinata.cloud/ipfs/$IDR_CID`       |
| 6 | Replay produces a *different* IDR                      | `diff <(python3 fetch claude_cid) <(python3 fetch gemini_cid)` — should differ on risk_rating |
| 7 | Both IDRs reference the same `data_cid`                | Both contain the SAME `data_cid` field                 |

---

## Troubleshooting

### Remix can't find OpenZeppelin imports

Rob's contract is dependency-free (custom `onlyOwner`, no OZ import). Should compile in a vanilla Remix file. If you see an import error, you've pasted from the wrong source.

### `publishRecord` reverts with "record already exists"

You've already published this `recordId`. Either change the deal name (different `recordId`) OR use `updateRecord(recordId, newCid)` instead — same effect, increments version, leaves audit trail in events.

### Pinata pin returns 401

Your JWT is missing the `pinFileToIPFS` permission. Generate a new key with that scope checked.

### `dispatch_rwa_analyse.py` fails with "no module named handler"

Run from the repo root: `PYTHONPATH=. python3 scripts/dispatch_rwa_analyse.py ...`

### Sepolia tx pending forever

You probably need more Sepolia ETH on the analyser account. Top up from a faucet.

---

## Mid-W2 integration checkpoint (acceptance for the demo call)

From the original 2026-05-05 build plan:

| Check                                                | Owner   | Pass criterion                                |
|------------------------------------------------------|---------|-----------------------------------------------|
| Stable Pay contract live on Sepolia with `data:` set | Rob     | Etherscan Sepolia URL works, hash readable    |
| CodeTonight handler reads CID from contract          | V>>     | Returns expected CID, no mock                 |
| Full HAPPI envelope runs end-to-end (no mocks)       | Both    | `completed` event reaches caller within 120 s |

Once these all pass → CryptoMondays demo material is ready.
