Type-safe agent-based simulation framework for Foundry/EVM protocols
npm install @elata-biosciences/agentforge
Stress-test your smart contracts before mainnet.
---
AgentForge is an agent-based simulation framework for Foundry/EVM protocols. Define autonomous agents, run them against your smart contracts, and validate economic invariants with deterministic, reproducible results.
Traditional testing validates individual functions. Agent-based modeling validates emergent system behavior—what happens when hundreds of users interact simultaneously? Does your AMM stay solvent under volatility? How do rational actors exploit edge cases?
``bash`
pnpm add @elata-biosciences/agentforge
Requirements: Node.js 18+ and Foundry with Anvil for EVM simulations.
`bashInitialize project structure
npx agentforge init
Writing a Scenario
`typescript
import { defineScenario } from '@elata-biosciences/agentforge';
import { ToyPack, RandomTraderAgent, MomentumAgent } from '@elata-biosciences/agentforge/toy';export default defineScenario({
name: 'market-stress',
seed: 42,
ticks: 100,
tickSeconds: 3600,
pack: new ToyPack({
assets: [{ name: 'TOKEN', initialPrice: 100, volatility: 0.05 }],
initialCash: 10000,
}),
agents: [
{ type: RandomTraderAgent, count: 10 },
{ type: MomentumAgent, count: 5, params: { threshold: 0.02 } },
],
assertions: [
{ type: 'gt', metric: 'totalVolume', value: 0 },
{ type: 'gte', metric: 'successRate', value: 0.9 },
],
});
`Run it:
`bash
npx agentforge run sim/scenarios/market-stress.ts
`Writing an Agent
Extend
BaseAgent and implement step():`typescript
import { BaseAgent, type Action, type TickContext } from '@elata-biosciences/agentforge';export class MyAgent extends BaseAgent {
async step(ctx: TickContext): Promise {
// 30% chance to buy each tick
if (ctx.rng.chance(0.3)) {
return {
id: this.generateActionId('buy', ctx.tick),
name: 'buy',
params: { amount: ctx.rng.nextInt(1, 100), asset: 'TOKEN' },
};
}
return null; // Skip this tick
}
}
`Agents have access to:
-
ctx.rng — Deterministic random number generator
- this.remember() / this.recall() — Persist state across ticks
- this.setCooldown() / this.isOnCooldown() — Rate-limit actions
- this.getParam() — Access scenario-defined parametersCLI
`bash
agentforge init [path] # Initialize simulation folder
agentforge run # Execute a scenario
agentforge run --toy # Run built-in demo
agentforge doctor # Check dependencies
agentforge types # Generate types from Foundry artifacts
`Options for
run:`bash
--seed # Override random seed
--ticks # Override tick count
--out # Output directory
--ci # CI mode (no colors)
--verbose # Verbose logging
`Output
Each run produces:
`
results/-/
├── summary.json # Metadata, metrics, assertion results
├── metrics.csv # Time-series data
├── actions.ndjson # Action log
└── config_resolved.json # Resolved configuration
`Core Concepts
Scenarios define simulation parameters: seed, duration, agents, and assertions.
Packs are protocol adapters that set up blockchain state and handle contract interactions.
Agents are autonomous actors that observe state and decide actions each tick.
Determinism: Same seed + same scenario = identical results. All randomness derives from seeded RNG.
Examples
See examples/ for working code:
-
basic-simulation/ — Minimal setup
- custom-agent/ — Memory, cooldowns, custom logic
- assertions/ — Validation and failure handling
- metrics-tracking/ — CSV analysisCI Integration
`yaml
- name: Run simulations
run: npx agentforge run sim/scenarios/stress.ts --ci --seed 42
`Assertions fail CI on invariant violations.
API
`typescript
// Core
import { defineScenario, BaseAgent, SimulationEngine } from '@elata-biosciences/agentforge';
import type { Scenario, Action, TickContext, Pack } from '@elata-biosciences/agentforge';// Adapters
import { spawnAnvil, createViemClient } from '@elata-biosciences/agentforge/adapters';
// Toy simulation
import { ToyPack, RandomTraderAgent, MomentumAgent } from '@elata-biosciences/agentforge/toy';
``See CONTRIBUTING.md.
MIT