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.
ReserveGuard is entering v0.1.0-alpha.1 field testing. 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.
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 an EIP-7702 delegated drain/restore experiment. 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.
Set your environment variables in Bash:
export MONAD_RPC_URL="https://..."
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.