Skip to Content
SDK & CLISDKFunction Overrides

Function Overrides

Force a contract function on your Stagenet to return a fixed value, without changing its code.

Use this to:

  • Mock a price oracle, vault rate, or any external view function during testing.
  • Force a contract into a specific branch (e.g. paused() = true) without writing to storage.
  • Reproduce a production state that depends on a registry, ACL, or feed you can’t easily seed.

How it works

When you install an override, your Stagenet records it in the DB and loads it into the EVM’s call interceptor. Every call to the target function — from eth_call, an eth_sendTransaction, or an inner call from another contract — returns your fixed value before the contract’s real code runs.

The override sits at the EVM layer, so contracts that staticcall the overridden function see the override too. This is the difference vs. simply changing storage: a function that does extra work (math, lookups, packing) still returns your exact value, because the function body is skipped.

You can target the same selector with multiple overrides — one per set of input params, plus an optional wildcard:

  • An override with inputParams only matches calls whose ABI-encoded inputs match exactly.
  • An override without inputParams is a wildcard for every call to that selector.
  • When both apply, the exact-input override wins.

Methods

stagenet.addFunctionOverride(input)

Install an override.

const override = await stagenet.addFunctionOverride({ contractAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC functionAbi: JSON.stringify({ type: "function", name: "totalSupply", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view", }), returnValues: ["1000000000000"], // 1M USDC, 6 decimals });
FieldTypeDescription
contractAddressstringTarget contract
functionAbistring | objectJSON-encoded ABI fragment, or the fragment object
inputParamsstring[]?Omit for a wildcard; pass values to match only that input
returnValuesstring[]One entry per output in the fragment

Returns the created override ({ id, contractAddress, functionAbi, inputParams, returnValues, enabled, createdAt }).

stagenet.updateFunctionOverride(id, returnValues)

Replace the return values of an existing override. contractAddress, the function selector, and inputParams are immutable — remove and re-add to change them.

await stagenet.updateFunctionOverride(override.id, ["2000000000000"]);

Returns the updated override.

stagenet.removeFunctionOverride(id)

Delete an override.

await stagenet.removeFunctionOverride(override.id);

Returns { id, removed: true }.

stagenet.enableFunctionOverride(id) / stagenet.disableFunctionOverride(id)

Toggle an override without deleting it. A disabled override stays in the DB but is not applied — calls go through to the real contract.

await stagenet.disableFunctionOverride(override.id); // ... later ... await stagenet.enableFunctionOverride(override.id);

Returns the updated override.

stagenet.getFunctionOverrides()

List every override on the Stagenet, most recent first.

const overrides = await stagenet.getFunctionOverrides();

Returns FunctionOverride[].

stagenet.getFunctionOverridesByContract(contractAddress)

List overrides for a single contract.

const overrides = await stagenet.getFunctionOverridesByContract(usdc);

Returns FunctionOverride[].

Usage

Mock a price feed

Pin a Chainlink-style latestAnswer() to a fixed value so the rest of your protocol can be exercised against a known price:

import { createStagenet } from "contract.dev"; const stagenet = createStagenet("<YOUR_STAGENET_RPC_URL>"); const ethUsdFeed = "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419"; await stagenet.addFunctionOverride({ contractAddress: ethUsdFeed, functionAbi: "function latestAnswer() view returns (int256)", returnValues: ["300000000000"], // $3,000 with 8 decimals });

Per-account balanceOf

Give a single address a fake USDC balance while every other account stays at its real value:

const usdc = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; const target = "0x1111111111111111111111111111111111111111"; await stagenet.addFunctionOverride({ contractAddress: usdc, functionAbi: "function balanceOf(address) view returns (uint256)", inputParams: [target], returnValues: ["1000000000000"], });

Toggle during a test run

const paused = await stagenet.addFunctionOverride({ contractAddress: vault, functionAbi: "function paused() view returns (bool)", returnValues: ["true"], }); // run "happy path" with paused=true... await stagenet.disableFunctionOverride(paused.id); // run "happy path" with the contract's real paused state...

When to use storage instead

Overrides intercept reads at the function boundary. If you need state changes that other functions observe (e.g. a balance that downstream transfer() calls should decrement), set storage directly with setStorageAt or set token balances with setERC20Balance instead.

Last updated on