Mainnet Follow
Pin contract state to the live chain your Stagenet forks. A followed account or slot always reads the current mainnet value — even after transactions on your Stagenet have written to it.
Use this to stop market state from freezing and drifting on a long-lived Stagenet:
- Keep DEX pool prices live, so aggregator quotes (computed against mainnet) keep matching what your fork executes.
- Keep a market maker’s inventory live, so RFQ fills don’t start reverting after heavy testing.
- Keep oracle data fresh.
Your own wallets, deployments, and balances keep persisting normally. For the full mechanics, see Mainnet Follow.
How it works
Following is a standing mode, not a one-shot copy:
- Reads of a followed key always return the live mainnet value.
- Writes still work normally within a transaction (and block) — protocols that write-then-read their own state behave correctly — but are discarded at block seal. The next block reads mainnet again.
- The follow registry survives Stagenet restarts.
followAccount / unfollowAccount
Follow everything on a contract: every storage slot, plus the account record (ETH balance, nonce) and its code. Use for contracts holding only market state — AMM pools, settlement contracts, oracles.
import createStagenet from "contract.dev";
const stagenet = createStagenet(); // reads contract.dev.js
// A Uniswap pool your routes trade through: price/reserves stay live forever.
await stagenet.followAccount("0xc9034c3E7F58003E6ae0C8438e7c8f4598d5ACAA");
// Later:
await stagenet.unfollowAccount("0xc9034c3E7F58003E6ae0C8438e7c8f4598d5ACAA");| Parameter | Type | Description |
|---|---|---|
address | string | 0x-prefixed contract address |
Returns { address, followed, residueCleared } — residueCleared is the number of previously-persisted local slots that were removed (any locally-persisted account record or code is cleared as well). Unfollowing clears local history too, so the contract behaves as never-touched.
Don’t followAccount a token: balanceOf holds user balances alongside market inventory, and following the whole contract would stop your balances from persisting. Use followTokenBalance instead.
followTokenBalance / unfollowTokenBalance
Follow one holder’s balance on a token — the slot is discovered automatically (works with proxies and non-standard layouts). Other balances on the token are untouched.
// Keep an RFQ market maker's USDC inventory live:
const res = await stagenet.followTokenBalance(
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
"0x67336CeC42645F55059efF241CB02eA5cC52FF86", // the maker
);
console.log(res.slot); // the discovered balanceOf slot| Parameter | Type | Description |
|---|---|---|
token | string | ERC20 contract address |
holder | string | The holder whose balance should track mainnet |
Returns { token, holder, slot, residueCleared }.
followSlots / unfollowSlots
Follow specific storage slots when you know the keys (allowances, custom mappings, packed words). Mapping entries take the full 32-byte hex key; declared slots can be plain decimal.
await stagenet.followSlots("0xA0b8…eB48", [
"0x6e91f60197c982353033e86512311820683e018e0f39963c5d00c2c490bc45d3",
]);
// Declared slots by number — e.g. a V3 pool's slot0 and fee-growth accumulators:
await stagenet.followSlots("0xc903…ACAA", [0, 1, 4]);| Parameter | Type | Description |
|---|---|---|
address | string | Contract address |
slots | (string | number | bigint)[] | 0x-prefixed 32-byte slot keys, or decimal slot numbers (10 is slot ten, not 0x10) |
Returns { address, followedSlots, residueCleared }.
getFollowed
Everything currently followed on the Stagenet.
const followed = await stagenet.getFollowed();
// { accounts: ["0xc903…"], slots: { "0xa0b8…": ["0x6e91…"] } }