> For the complete documentation index, see [llms.txt](https://docs.trover.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.trover.tech/engineering/contracts-runbook.md).

# Contracts runbook: deploy, rehearse, back up, force-release

## Inventory (Robinhood Chain 4663)

| Contract                                                            | Status | Admin                 | Rescue paths                                                               |
| ------------------------------------------------------------------- | ------ | --------------------- | -------------------------------------------------------------------------- |
| TroverStockDistributor `0x53f6B2498374ebEB8c47B0C4a83b0eF78ea071CC` | live   | Safe `0x0e49…FaF35`   | pause, cancelTranche, releaseExpired, recoverUnreserved (ERC-20 only)      |
| Safe `0x0e491ff9dB0956558891a71587015a8Cc64FaF35` (2-of-2)          | live   | owners                | execTransaction                                                            |
| TroverBuybackBurn                                                   | stack  | admin (AccessControl) | pause, sweep, setRouterAllowed, setToken (once)                            |
| TroverEthDistributor                                                | stack  | admin                 | pause, cancelTranche, releaseExpired, recoverUnreserved                    |
| TroverLeagueVault                                                   | stack  | admin                 | pause, recoverUnclaimed (30-day grace), sweep                              |
| TroverRevenueRouter                                                 | stack  | admin (Ownable)       | pause (defers), withdrawOwed, reassignOwed, sweep, setRecipients, setSplit |
| TroverXClaimEscrow                                                  | stack  | admin                 | pause, forceRelease (6× recovery delay), cancelExpiredDeposit (sender)     |
| TroverAtomicFeeExecutor                                             | stack  | admin (Ownable)       | pause, sweep                                                               |

"stack" = the six token-economics contracts. They are deployed either from the admin page (`/internal`, "Deploy the token stack", connected wallet, one signer) or by `packages/contracts/scripts/deploy-stack.mjs`. Addresses live in `packages/contracts/deployments/robinhood-<chainId>-stack.json` and the ABIs in `deployments/abi/`; the admin page keeps its working record in the API (`GET /v1/admin/contracts/manifest`) and shows the JSON to commit.

The planet collection (`TroverPlanets`, `TroverPlanetRenderer`, `TroverAgentProgress`, `ERC721SeaDrop`) was removed on 2026-09-08. Its testnet manifests are kept as `*.planets-2026-09-08.json`.

## Wallets (decided 2026-09-13)

| Wallet                                 | Address                                      | Key                                                                                                             | Job                                                                                                                                                                                                                               |
| -------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Platform fee wallet = admin = deployer | `0x61eAA89638B4425278EB4B6978c5C0d7bADacc9C` | server `FEE_ACCUMULATOR_PRIVATE_KEY` (also `SOCIAL_CLAIM_VOUCHER_SIGNER_PRIVATE_KEY`); MetaMask for `/internal` | receives every 1% fee (executor `feeRecipient`, NFT fee transfers, Relay app fee); runs the split; keeps the holders' share and buys the stock basket from it; funds `TroverStockDistributor` tranches; owns every stack contract |
| Treasury                               | `0x6eC1c1DAFD863081db0760c19870d487dd9064B8` | none on the server                                                                                              | receives the treasury share of the split (ETH first, USDG for the rest) and the router treasury share                                                                                                                             |
| Claim automation keeper                | `0xD21458FDc7F4C2389193bdA7aBE2eA0E1CD1F63e` | server `CLAIM_AUTOMATION_PRIVATE_KEY`                                                                           | gas-only signer: `TRANCHE_MANAGER_ROLE` on the stock distributor (creates the merkle tranches holders claim from), `KEEPER_ROLE` on `TroverBuybackBurn` (calls `buyAndBurn`)                                                      |
| Stock distributor                      | `0x53f6B2498374ebEB8c47B0C4a83b0eF78ea071CC` | contract                                                                                                        | holders claim stock tokens per tranche                                                                                                                                                                                            |
| Distributor admin Safe                 | `0x0e491ff9dB0956558891a71587015a8Cc64FaF35` | 2-of-2 Safe                                                                                                     | pause, recovery, project-token configuration on the distributor                                                                                                                                                                   |
| Token fee accumulator                  | `0xdaf20dd5B1A15d930aE05f2082a6571bD277cc7C` | server `TOKEN_FEE_ACCUMULATOR_PRIVATE_KEY`                                                                      | only if the token has a transfer tax: swept into the fee wallet for stock buys                                                                                                                                                    |

`stockBuyerAddress()` in `apps/worker/src/trading.ts` is the platform fee wallet unless `TREASURY_PRIVY_WALLET_ID` is set, and `feeWalletReady()` refuses every send when the configured key does not control `PLATFORM_FEE_WALLET`.

## Admin: one signer

The admin is the platform fee wallet above (an EOA). Every role grant and ownership goes to it; a Safe can replace it later by granting `DEFAULT_ADMIN_ROLE` / `transferOwnership` from that key. `safe-rescue.mjs` detects an EOA admin (no bytecode) and executes directly with `SAFE_EXEC_PRIVATE_KEY` instead of building a Safe transaction.

## Deploy from the admin page (preferred)

1. Open `/internal`, unlock with the admin code, sign in and connect the wallet that will pay gas (it becomes the deployer; it does not need to be the admin, but if it is, the burn-sink router allowlist is done in the same run).
2. Pick the network, check the inputs (admin, treasury, league manager, voucher signer = address of `SOCIAL_CLAIM_VOUCHER_SIGNER_PRIVATE_KEY`, fee wallet, keeper = address of `CLAIM_AUTOMATION_PRIVATE_KEY`, routers, token empty before launch, escrow recovery delay), type `DEPLOY <chainId>`, press deploy. Each of the six deployments and every wiring step is one wallet confirmation; the page waits for 3 confirmations on mainnet (2 on testnet), refuses any transaction estimated above 6M gas, records progress after every transaction and resumes on rerun.
3. When the assertions pass the page shows the `.env` lines and the manifest JSON. Save the JSON as `packages/contracts/deployments/robinhood-<chainId>- stack.json`, commit it, paste the env lines into the server `.env` (backup first) and redeploy api + worker.

Artifacts come from `apps/web/src/lib/contracts/stack-artifacts.json`, written by `pnpm --filter @trover/contracts export:artifacts` after a compile. Re-export and commit whenever a stack contract changes.

## Deploy with the script

Inputs (deploy-time only, never in the server `.env`):

```
TROVER_STACK_NETWORK=mainnet|testnet
TROVER_STACK_DEPLOY_CONFIRM=DEPLOY_TROVER_STACK_4663   # or _46630
CONTRACT_DEPLOYER_PRIVATE_KEY=…       # mainnet; TROVER_TESTNET_DEPLOYER_PRIVATE_KEY on testnet
CLAIM_ADMIN_ADDRESS=0x1cb5…F631       # mainnet admin; TROVER_TESTNET_ADMIN_ADDRESS on testnet
TROVER_STACK_ALLOW_EOA_ADMIN=true     # required while the admin is a single signer
TROVER_TREASURY_ADDRESS=…  TROVER_LEAGUE_MANAGER_ADDRESS=…  TROVER_BUYBACK_KEEPER_ADDRESS=…
SOCIAL_CLAIM_VOUCHER_SIGNER_ADDRESS=…  SOCIAL_CLAIM_RECOVERY_DELAY_SECONDS=2592000
PLATFORM_FEE_WALLET=…  AUTO_INVEST_FEE_ROUTER_ADDRESSES=0x…,0x…
TROVER_TOKEN_ADDRESS=                 # leave empty until the TROVER token launches
TROVER_STACK_MAX_GAS=6000000  TROVER_STACK_MAX_GAS_PRICE_GWEI=0.5  TROVER_STACK_MAX_COST_ETH=0.05
```

`pnpm --filter @trover/contracts deploy:stack`. Order: buyback sink → ETH distributor (holder vault) → league vault → revenue router(admin, holder vault, league, sink, treasury) → sink router allowlist (when deployer is admin) → X claim escrow → fee executor (router allowlist, ownership to the admin). Every step is recorded; a rerun skips recorded steps and re-runs the nine assertions. The script prints the backend `.env` lines at the end.

## Testnet rehearsal (46630)

Automated: `pnpm --filter @trover/contracts deploy:stack:testnet` then `pnpm --filter @trover/contracts rehearse:testnet` (both read `packages/contracts/.env`, gitignored). The rehearsal deploys a mock TROVER and a mock swap router, then drives every rescue path once.

### Rehearsed 2026-09-08 on 46630 (token stack)

Deployed by `0xD21458FDc7F4C2389193bdA7aBE2eA0E1CD1F63e` (EOA admin): buyback sink `0xaF740795dB64Cf13260A7AC8514493d304436133`, ETH distributor `0x68cF9b6a711c8Ce7Adf8E1e066CcBCaBDE542A71`, league vault `0xE3a39500e5251B713Ff29D2838A31782B25d7fcD`, router `0xd93D0F7fD5376A8fecF3dB856815B8Eb53f25DD9`, escrow `0xb94B086cfa56e5E5e7BF1fc5e561bA054b453711`, fee executor `0x3ecbb30B2aE76EdbE666564EFe2565188505C1AB`. All nine assertions passed; `deployments/robinhood-46630-rehearsal.json` holds the 39 rehearsal transactions: a 0.002 ETH bot-fee route landing 35/15/20/30 in the four recipients, `setToken` (second call refused), `buyAndBurn` through a router not allowlisted (refused) then through the allowlisted mock (0.2 tokens to the dead address), allowlist removal, deferred router payout through a rejecting treasury then reassigned and withdrawn, `setSplit` changed and restored, sweeps of force-sent ETH on router, league and executor, `recoverUnclaimed` before grace (refused), `recoverUnreserved` on the ETH distributor, escrow `forceRelease` after six 60-second delays (refused before), and pause/unpause on all six contracts. Whole run under 0.003 test ETH at 0.01 gwei.

## Testnet rehearsal checklist (manual)

One fee routed and the four balances checked; one deferred payout withdrawn; one buyback through the mock router; one league epoch funded, published and claimed; one stock tranche funded, one claim, `releaseExpired` after the deadline; one escrow deposit force-released; pause and unpause everything.

## Mainnet

One signer, the admin above. The deployer is whichever wallet connects on the admin page (or a funded EOA for the script); it ends the run holding no role unless it is the admin. After the run: commit the manifest and `deployments/abi/`, paste the env lines into the server `.env` (backup first), redeploy api + worker, confirm the `contract-balance-watch` job logs one line per contract, then `setToken` once the token exists and set `BUYBACK_ENABLED=true`.

## Backups

* Admin key: offline copy on two machines; nothing else can pause, sweep or reassign.
* Manifest + ABIs: committed to `main` after every deployment; the admin page also keeps the working record in the API.
* Deployer key: retired after the run when it is not the admin.

## Force-release procedures

All go through `packages/contracts/scripts/safe-rescue.mjs` (`build` on any machine; with an EOA admin `build` executes directly using `SAFE_EXEC_PRIVATE_KEY`; with a Safe, `sign` on each owner's machine and `exec` with any funded key). Each `build` prints the current on-chain value it will change.

| Situation                                               | Command                                                                           |
| ------------------------------------------------------- | --------------------------------------------------------------------------------- |
| League epoch published, winners never claimed (30 days) | `build league.recoverUnclaimed <epoch> <treasury>`                                |
| League epoch never published (epoch end + 30 days)      | same                                                                              |
| ETH force-sent to the league vault                      | `build league.sweep <treasury>`                                                   |
| Router recipient rejecting, share deferred              | `build router.withdrawOwed <recipient>`                                           |
| Router recipient is a dead contract                     | `build router.reassignOwed <dead> <replacement>` then `withdrawOwed`              |
| Router holds ETH nobody is owed                         | `build router.sweep <treasury>`                                                   |
| Replace a vault, the sink or the treasury               | `build router.setRecipients <holder> <league> <burn> <treasury>`                  |
| Change a split                                          | `build router.setSplit <source> <holderBps> <leagueBps> <burnBps>`                |
| Stop routing while replacing (defers, never reverts)    | `build pause router` / `unpause router`                                           |
| TROVER token launched                                   | `build burn.setToken <token>`                                                     |
| Allow or remove a swap router on the sink               | \`build burn.setRouterAllowed true                                                |
| Manual buyback                                          | `build burn.buyAndBurn <router> <calldata> <ethIn> <minOut>`                      |
| Tokens or ETH stuck in the sink                         | \`build burn.sweep \<token                                                        |
| ETH or tokens stuck in the fee executor                 | `build executor.sweep native <to> <wei>` / `executor.sweep <token> <to> <amount>` |
| ETH distributor tranche expired                         | `build eth.releaseExpired <trancheId>` then `eth.recoverUnreserved <to> <eth>`    |
| Escrow deposit whose sender contract reverts (6× delay) | `build escrow.forceRelease <claimId> <to>`                                        |
| Anything on fire                                        | \`build pause \<league                                                            |

## Monitoring

`apps/worker/src/contract-watch.ts` reads every configured stack address every 10 minutes: balances, `router.totalOwed`, `league.totalAttributed` against balance, `eth.reservedByToken`, the sink's pending ETH, and warns when anything is owed or unattributed. Gauge `trover_contract_balance_eth{contract}`.

## Server `.env` after mainnet

`OPENSEA_REVENUE_ROUTER_ADDRESS`, `BUYBACK_BURN_ADDRESS`, `ETH_CLAIM_DISTRIBUTOR_ADDRESS`, `SOCIAL_CLAIM_ESCROW_ADDRESS`, `SOCIAL_CLAIM_VOUCHER_SIGNER_PRIVATE_KEY`, `AUTO_INVEST_FEE_EXECUTOR_ADDRESS`, `LEAGUE_VAULT_ADDRESS`, `CONTRACT_BALANCE_ALERT_ETH`; after the token launch `BUYBACK_ENABLED=true` and optionally `BUYBACK_MIN_ETH`, `BUYBACK_MAX_ETH_PER_RUN`, `BUYBACK_MAX_SLIPPAGE_BPS`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.trover.tech/engineering/contracts-runbook.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
