ReserveGuard is an open-source Solidity library for building reserve-aware smart contracts on Monad.
It wraps Monad's MIP-4 Reserve Balance Introspection precompile at 0x1001 and provides simple primitives for detecting reserve violations, asserting healthy execution state, and placing explicit reserve checkpoints inside transaction flows.
v0.1.0-alpha.1 is the current stable field-testing baseline. The V1 API is intentionally small so Monad developers can try it in real flows before the library expands.
If you test ReserveGuard in a wallet, router, vault, paymaster, batch executor, or EIP-7702 flow, use docs/field-testing.md to report what happened. The use-case matrix in docs/use-case-matrix.md tracks what has been observed and what still needs evidence.
See the documentation map for stable V1, field testing, experimental work, dated observations, and historical release notes.
This project is scaffolded for Monad Foundry.
forge build
forge testIf forge is not on your shell path yet, run:
source ~/.bashrcor use the installed binary directly, for example:
~/.foundry/bin/forge testimport { ReserveGuard } from "./contracts/ReserveGuard.sol";
function execute() external {
swap();
ReserveGuard.checkpoint();
borrow();
ReserveGuard.checkpoint();
stake();
}With the base contract:
import { ReserveAware } from "./contracts/abstract/ReserveAware.sol";
contract Vault is ReserveAware {
function withdraw() external reserveHealthy {
// ...
}
}ReserveGuard.dipped()returns whether the current execution state has dipped into reserve.ReserveGuard.assertHealthy()reverts withReserveViolation()if reserve state is unhealthy.ReserveGuard.checkpoint()is a semantic alias forassertHealthy()in V1.ReserveAwareexposes internal helpers and thereserveHealthymodifier.ReserveProtectedexposes a stricterreserveProtectedmodifier that checks before and after function execution.
V1 intentionally keeps the API small. Checkpoints are explicit fail-fast boundaries; they do not emit events, label checkpoints, or attempt recovery.
MIP-4 defines a reserve balance precompile at 0x1001 with:
function dippedIntoReserve() external returns (bool);ReserveGuard preserves MIP-4 semantics and provides developer-friendly Solidity wrappers. A healthy checkpoint does not guarantee the transaction will remain healthy later; it only validates reserve state at the point where the check runs.
Developer-facing examples live in examples/.
Testnet experiments live in examples/testnet/, including EIP-7702 delegated drain/restore, tracing, agent-wallet, and recovery experiments. See docs/live-testnet.md for the live Monad testnet workflow, authorization commands, verified observations, and caveats.
The optional browser lab in app/ can be used to inspect the same testnet experiment patterns, but the Solidity library and examples are the primary integration path for Monad developers.
See docs/v1.md for the V1 release scope, non-goals, verification path, and testnet caveats. See docs/releases/v0.1.0-alpha.1.md for the alpha release notes.
Experimental post-V1 work lives under contracts/experimental/. See docs/experimental/reserve-trace.md for labeled observations and docs/experimental/reserve-recoverable.md for the application-defined recovery base contract.
Set your environment variables in Bash:
export MONAD_RPC_URL="https://rpc.testnet.monad.xyz"
export PRIVATE_KEY="0x..."Deploy the developer-facing examples:
forge script script/DeployExamples.s.sol:DeployExamples \
--rpc-url "$MONAD_RPC_URL" \
--private-key "$PRIVATE_KEY" \
--broadcastDeploy the live testnet experiment contracts:
forge script script/DeployTestnetExperiments.s.sol:DeployTestnetExperiments \
--rpc-url "$MONAD_RPC_URL" \
--private-key "$PRIVATE_KEY" \
--broadcastFor where to get each value, how to map Foundry's Contract Address: output to env vars, post-deploy smoke tests, drain/restore checks, and the EIP-7702 reserve-dip workflow, see docs/live-testnet.md.
After deploying testnet experiments, the 7702 drain/restore flow can be repeated with:
bash scripts/run-7702-drain-restore.shThe EIP-7702 delegated drain/restore experiment is intentionally documented as a live testnet workflow because local Monad Foundry validates delegated routing but may not reproduce the same reserve-dip semantics observed on testnet.
- Use disposable, testnet-only keys for deployment and EIP-7702 authorization experiments.
- Never commit private keys, funded account credentials, or populated environment files.
- Treat MIP-4 as a transaction-wide signal, not proof that
address(this)caused the reserve violation. - A healthy checkpoint only describes the point where it runs; later execution can still dip.
ReserveTraceandReserveRecoverableare experimental and are not part of the stable V1 API.
If a key is ever placed in a shared file, terminal transcript, issue, or chat, stop using that account and move any remaining test funds to a fresh testnet-only account.
ReserveGuard is available under the MIT License.