Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Offchain db connection string for mongodb
DATABASE_URL="mongodb://tap:tap@localhost:27017/mongo?authSource=admin&retryWrites=true&w=majority"
# TAP publishes Mongo on host 27027 (container still uses 27017) so it does not steal
# the default Mongo port. Override with MONGO_PORT in docker-compose if needed.
DATABASE_URL="mongodb://tap:tap@localhost:27027/mongo?authSource=admin&retryWrites=true&w=majority"
DATABASE_REPLSET="0" # set to "1" if using --replSet option in mongo. this allows transactions
# MONGO_PORT=27027

# RPC URL for blockchain connection
# Anvil (local): http://127.0.0.1:8545
Expand Down
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pnpm app:dev # http://localhost:3000/app (reads app/.en
| Piece | Process |
| --- | --- |
| Mongo + API + poller | Docker via `pnpm bootstrap` / `docker compose up` |
| Mongo only | `pnpm docker:mongo` — **host port 27027** (not 27017). `restart: unless-stopped`. `pnpm docker:down` frees the port. |
| Product UI / wallet | Host `pnpm app:dev` + `app/.env.local` + browser extension wallet (EIP-6963, no cloud key) |
| Contract artifacts | `pnpm setup` → `chain/out` (required before server image build) |
| Factory in Mongo | `REUSE_TAP_FACTORY=1` (demo) or `pnpm deploy-factory` (you own it) |
Expand All @@ -46,6 +47,7 @@ pnpm app:dev # http://localhost:3000/app (reads app/.en
| Stale factory impl in docs | Hardcoded old address | Always read impl onchain (`factory:register` does); live beacon ≠ landing screenshot |
| Mint OK, register **500** | Docker app rewrites to `localhost:8293` | Docker: `NEXT_PUBLIC_API_URL=http://server:8293`; host app:dev: `localhost:8293` |
| Poller `0xUPDATE_ME` / invalid BytesLike | Placeholder PRIVATE_KEY | Real hex for server-signed; placeholder OK for read-only poller |
| TAP Mongo on 27017 / other app blocked | Old compose published default Mongo port with `restart: always` | Host port is **27027**. Host `DATABASE_URL` uses 27027. Inside compose, Mongo is still `mongodb:27017`. `pnpm docker:down` then `pnpm docker:mongo`. |

**Plume defaults:** `CHAIN_ID=98866`, `RPC_URL=https://rpc.plume.org`. Prefer mainnet for product work (not Anvil mint).

Expand Down
64 changes: 0 additions & 64 deletions CLAUDE.md

This file was deleted.

9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pnpm app:dev # Product UI — http://localhost:3000/app
|---------|-----|
| **Server** | http://localhost:8293 |
| **App (host)** | http://localhost:3000 — use `pnpm app:dev` for wallet work (`app/.env.local`) |
| **MongoDB** | localhost:27017 |
| **MongoDB** | localhost:27027 (not 27017) |

`pnpm bootstrap` is idempotent. Prefer host `pnpm app:dev` for the product UI; Docker app is optional.

Expand All @@ -65,9 +65,10 @@ Then go read official [docs](https://docs.transferagentprotocol.xyz)
### Scripts

```bash
# Docker (runs all services: MongoDB, server, app)
# Docker (MongoDB, server, app)
pnpm docker:up # Start all services
pnpm docker:down # Stop all services
pnpm docker:mongo # Mongo only (host 27027)
pnpm docker:down # Stop and remove containers (frees 27027)
pnpm docker:logs # Stream logs
pnpm docker:build # Rebuild and start

Expand All @@ -84,7 +85,7 @@ For AI-assisted / agent development, see:

- [`WARP.md`](./WARP.md) — monorepo architecture, commands, patterns, pitfalls
- [`app/WARP.md`](./app/WARP.md) — frontend conventions (routes, write path, styled-components)
- [`AGENTS.md`](./AGENTS.md) / [`CLAUDE.md`](./CLAUDE.md) — short pointers to those files
- [`AGENTS.md`](./AGENTS.md) — short pointer to those files
- [`CONTRIBUTING.md`](./CONTRIBUTING.md) — branch and PR conventions

## Contributing
Expand Down
20 changes: 7 additions & 13 deletions WARP.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,7 @@ REUSE_TAP_FACTORY=1 pnpm bootstrap # Mongo + API (+ app image), demo factory i
pnpm app:dev # product UI — do not rely on Docker app alone
```

`pnpm bootstrap` (`scripts/bootstrap-plume.sh`) is idempotent: ensures `.env` + `app/.env.local`, installs deps if needed, builds contracts and **asserts** non-empty `chain/out`, creates `offchain-db`, `docker compose up -d --build`, waits for API health, optionally registers the shared demo factory when `REUSE_TAP_FACTORY=1`. Use `SKIP_APP=1` to start only mongodb+server.

**Factory model (required reading):**
- **Protocol builder** — owns shared demo factory on Plume (`0xcd6…`, owner TAP Admin `0x366a…`).
- **Transfer agent** — deploys **own** factory via `pnpm deploy-factory` (auto Mongo register); beacon upgrades are theirs.
- **Issuer ADMIN** — `createCapTable` (permissionless) on a factory; wallet manage UI. Using shared factory ≠ owning it.
- **Mongo `factories`** — local mirror only.

**Failure matrix:** `@tap/units` missing on `:3000` → Docker app without `packages/` (fixed in `Dockerfile.app`) or stop Docker app and use `pnpm app:dev`. Empty connect modal → install a browser extension wallet (Rabby, MetaMask — EIP-6963; no cloud key required). `COPY chain/out` fail → run `pnpm setup`. Fresh Mongo has no historical issuers until mint/register/load-from-wallet. Mint OK but register **500** → Docker app `NEXT_PUBLIC_API_URL` must be `http://server:8293`. Poller `invalid BytesLike 0xUPDATE_ME` → placeholder `PRIVATE_KEY`; poller now falls back to read-only when key is missing.
`pnpm bootstrap` is idempotent. Use `SKIP_APP=1` for mongodb+server only, or `pnpm docker:mongo` for Mongo alone (host **27027**, not 27017). Factory model + failure matrix: [`AGENTS.md`](./AGENTS.md).

Manual steps (if not using bootstrap):

Expand All @@ -154,7 +146,7 @@ pnpm install
pnpm setup
cp .env.example .env
# also create app/.env.local with the same NEXT_PUBLIC_* values
pnpm docker:up # or: docker compose up -d mongodb server
pnpm docker:mongo # host 27027; or SKIP_APP=1 pnpm bootstrap for API too
pnpm app:dev
```

Expand Down Expand Up @@ -428,7 +420,8 @@ The system supports multiple environments via `.env` files:

**Key Variables**:

- `DATABASE_URL`: MongoDB connection string
- `DATABASE_URL`: MongoDB connection string (host TAP Mongo is **27027**; compose-internal stays `mongodb:27017`)
- `MONGO_PORT`: optional host publish port (default 27027)
- `DATABASE_REPLSET`: Set to "1" for replica set (enables transactions)
- `RPC_URL`: Ethereum RPC endpoint
- `CHAIN_ID`: Network chain ID (31337 for Anvil, 98866 for Plume Mainnet, 98867 for Plume Testnet)
Expand Down Expand Up @@ -550,12 +543,13 @@ Libraries:
13. **Issuing a stakeholder's first stock**: the Issue Stock dropdown needs the issuer's stakeholders, so `GET /cap-table/holdings/stock` returns `stakeholders` (and `stockClasses`) — the manage UI can populate the dropdown before any issuance exists. Don't source the stakeholder list only from `holdings[]`; it's empty until stock is issued, which would make a fresh cap table unable to issue its first shares after a page reload.
14. **Nav issuer id**: company section links must use the real UUID from `router.query.issuerId` (or path), never a pattern string from `pathname` — otherwise users land on `/app/companies/%5BissuerId%5D`.
15. **Ghost stock classes**: registering metadata with `is_onchain_synced: false` after a failed wallet path, or jumping the poller past unprocessed events, leaves classes in Mongo that never landed onchain. Prefer receipt-gated `/register-onchain` (synced + tx_hash) and reconcile over head-jumps for routine refresh.
15. **Transfer already exists onchain/server**: UI transfer is a thin direct-wallet wrapper around `CapTable.transferStock` / TransferStock poller handling — do not invent a parallel transfer protocol or reimplement scaling outside `@tap/units`.
16. **Transfer already exists onchain/server**: UI transfer is a thin direct-wallet wrapper around `CapTable.transferStock` / TransferStock poller handling — do not invent a parallel transfer protocol or reimplement scaling outside `@tap/units`.
17. **TAP Mongo on 27017 / comes back after Docker Desktop restart**: compose used to publish 27017 with `restart: always`. Host port is **27027**, policy is `unless-stopped`. Update host `DATABASE_URL`. `pnpm docker:down` removes the container; `pnpm docker:mongo` starts only Mongo.

## Debugging

- **Logs**: The server logs extensively. Look for emoji prefixes (✅, ❌, ⏳, 💾)
- **Database**: Connect to MongoDB on port 27017 (credentials in `.env`)
- **Database**: Connect to MongoDB on host port **27027** (credentials in `.env`)
- **Blockchain**: Use RPC_URL to query contract state with ethers.js or cast
- **Event poller**: Runs in-process by default; check console for event processing logs
- **Poller block number stalled or far behind head**: fast-forward the per-issuer index with `pnpm poller:fast-forward` (`--issuer <id>`, `--block <n>`, `--dry-run`, `--help`). It sets `last_processed_block` to (near) chain head so the poller stops chasing a backlog and just tracks new blocks — handy on fast chains (Plume) or after the server was offline. Skips events between the old pointer and head, which is fine for a cap table with no real positions yet.
Expand Down
2 changes: 1 addition & 1 deletion app/WARP.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ Frontend config lives in `app/.env.local` (git-ignored). All are build-time publ

See the root `.env.example` for the canonical list. Keep Mongo `factories` and this factory address aligned.

**Host vs Docker:** Prefer `pnpm app:dev` for product work. If `:3000` is Docker and you see `Can't resolve '@tap/units'`, either rebuild the app image (Dockerfile copies `packages/`) or `docker compose stop app` and use the host dev server.
**Host vs Docker:** Prefer `pnpm app:dev` for product work (`app/.env.local`). Docker app bind-mounts `app/` + `packages/` so source edits hot-reload; rebuild the image only after `package.json` / lockfile / Dockerfile changes. If `:3000` is Docker and you see `Can't resolve '@tap/units'`, `docker compose stop app` and use the host dev server. TAP Mongo is host **27027**.

## Git Workflow

Expand Down
3 changes: 3 additions & 0 deletions app/vercel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ignoreCommand": "git diff --quiet HEAD^ HEAD -- . \":!WARP.md\" \":!README.md\""
}
17 changes: 15 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
services:
mongodb:
# Host 27027 so TAP does not steal the default Mongo port other local apps use.
# Inside the compose network the process still listens on 27017.
image: mongo:latest
restart: always
restart: unless-stopped
environment:
MONGO_INITDB_ROOT_USERNAME: tap
MONGO_INITDB_ROOT_PASSWORD: tap
ports:
- 27017:27017
- "${MONGO_PORT:-27027}:27017"
volumes:
- offchain-db:/data/db
healthcheck:
Expand Down Expand Up @@ -52,6 +54,17 @@ services:
NEXT_PUBLIC_FACTORY_ADDRESS: ${NEXT_PUBLIC_FACTORY_ADDRESS:-}
NEXT_PUBLIC_CHAIN_ID: ${NEXT_PUBLIC_CHAIN_ID:-${CHAIN_ID:-98866}}
NEXT_PUBLIC_OPERATOR_ADDRESS: ${NEXT_PUBLIC_OPERATOR_ADDRESS:-}
# Docker Desktop bind mounts often miss inotify; poll so Next/Turbopack picks up edits.
WATCHPACK_POLLING: "true"
CHOKIDAR_USEPOLLING: "true"
CHOKIDAR_INTERVAL: "1000"
command: ["pnpm", "dev", "--hostname", "0.0.0.0"]
volumes:
# Source mounts: edit app/ or packages/ on the host without rebuilding the image.
# Rebuild (`pnpm docker:build`) is still required after package.json / lockfile / Dockerfile changes.
- ./app:/app/app
- ./packages:/app/packages
- /app/app/node_modules
depends_on:
mongodb:
condition: service_healthy
Expand Down
4 changes: 3 additions & 1 deletion docker/Dockerfile.app
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ EXPOSE 3000
# NEXT_PUBLIC_* must be provided at runtime via docker-compose (or build args
# for production images). Browser API rewrite should use a host-reachable URL
# such as http://localhost:8293 — not the compose DNS name "server".
CMD ["pnpm", "dev"]
# Compose overrides this with --hostname 0.0.0.0 and bind-mounts app/ + packages/
# so source edits hot-reload without an image rebuild.
CMD ["pnpm", "dev", "--hostname", "0.0.0.0"]
2 changes: 1 addition & 1 deletion docs/src/pages/development/factory-deploy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pnpm factory:register --factory 0xYourFactory
# implementation is read onchain when omitted
```

MongoDB Compass is optional for inspection (`mongodb://tap:tap@localhost:27017`, database `mongo`, collection `factories`). Prefer the CLI so the implementation stays live.
MongoDB Compass is optional for inspection (`mongodb://tap:tap@localhost:27027`, database `mongo`, collection `factories`). Prefer the CLI so the implementation stays live.

## Demo / issuer-dev: shared Plume factory

Expand Down
5 changes: 3 additions & 2 deletions docs/src/pages/development/run-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pnpm bootstrap
# or mongo+server only: SKIP_APP=1 pnpm bootstrap
```

Rebuild after dependency, contract artifact, or Dockerfile changes: `pnpm docker:build`.
Rebuild after dependency, lockfile, contract artifact, or Dockerfile changes: `pnpm docker:build`. App **source** edits do not need a rebuild — compose bind-mounts `app/` and `packages/` and polls for changes. Wallet work still prefers host `pnpm app:dev` (`app/.env.local`).

### Check the API

Expand Down Expand Up @@ -51,7 +51,7 @@ Wallet mint/manage needs `app/.env.local` (`NEXT_PUBLIC_*`). Docker app reads ro

```bash
pnpm docker:down
docker compose up -d mongodb
pnpm docker:mongo
pnpm dev
```

Expand All @@ -76,6 +76,7 @@ A fresh Mongo **does not** auto-import every historical issuer on Plume. Use min
| `Can't resolve '@tap/units'` on `:3000` | Docker app image must `COPY packages/`; or stop Docker app and run `pnpm app:dev` |
| Empty connect modal / no wallets | Install a browser extension wallet (Rabby, MetaMask, etc.) — EIP-6963, no cloud key needed |
| `EADDRINUSE` | Port 8293 or 3000 already in use (`lsof -i :3000`) |
| Other app wants Mongo 27017 | TAP publishes **27027**. Host `DATABASE_URL` must use 27027. `pnpm docker:down` if an old container is still on 27017. |
| History does not update | Poller running; issuer registered with `deployed_to` |
| Frontend API fails | `NEXT_PUBLIC_API_URL=http://localhost:8293` (host-reachable) |
| Factory misaligned | Mongo `factories` vs `NEXT_PUBLIC_FACTORY_ADDRESS` |
Expand Down
2 changes: 1 addition & 1 deletion docs/src/pages/development/setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Bootstrap is idempotent: deps, contract build (`chain/out`), Docker volume, comp
| --- | --- |
| API | http://localhost:8293 |
| Product UI | `pnpm app:dev` → http://localhost:3000/app |
| MongoDB | localhost:27017 |
| MongoDB | localhost:27027 (TAP does not use 27017) |

Prefer **host** `pnpm app:dev` for wallet work. Docker app is optional.

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"app:test:e2e": "pnpm --filter tap-app test:e2e",
"docker:up": "docker compose up -d",
"docker:down": "docker compose down",
"docker:mongo": "docker compose up -d mongodb",
"docker:logs": "docker compose logs -f",
"docker:build": "docker compose up -d --build",
"bootstrap": "./scripts/bootstrap-plume.sh",
Expand Down
Loading
Loading