Spell Syntax
Reference for the Grimoire .spell format.
Structure
Every spell follows this top-level form:
spell MyStrategy {
version: "1.0.0"
description: "Description of what this spell does"
assets: [USDC, WETH]
params: { amount: 1000000 }
venues: { dex: @uniswap_v3 }
on manual: {
dex.swap(USDC, WETH, params.amount)
}
}
The spell name and at least one on trigger handler are required. All other sections are optional.
Top-Level Sections
params
Input parameters with optional type annotations and defaults:
params: {
amount: 1000000
enabled: true
target: {
type: amount
asset: USDC
default: 1.5 USDC
min: 0
max: 1000000
}
}
Override at runtime with --params '{"amount": 500000}'.
assets
Declare assets used in the spell. Simple list or with explicit chain/address:
assets: [USDC, WETH]
venues
Map aliases to protocol adapters. The alias is how you call actions in the spell body:
venues: {
dex: @uniswap_v3
lender: @aave_v3
lenders: [@aave_v3, @morpho_blue]
}
limits
Runtime policy constraints applied to every action in the spell:
limits: {
max_single_move: 500000
approval_required_above: 100000
}
state
Persistent state that survives across runs:
state: {
persistent: {
last_rebalance: 0
total_swapped: 0
}
ephemeral: {
current_price: 0
}
}
Persistent state is saved to the local SQLite store after each run. Ephemeral state resets every run. See State Persistence for details.
advisors
AI model configuration for advise decision points:
advisors: {
risk: {
model: "anthropic:claude-haiku-4-5-20251001"
system_prompt: "Return JSON only."
timeout: 20
fallback: false
}
}
guards
Preconditions that halt, revert, or warn before action execution:
guards: {
enough_balance: balance(USDC) > 1000 with (
severity="halt",
message="Insufficient USDC balance",
)
}
Guard severity options: warn, revert, halt.
Triggers
Every spell has one or more on <trigger>: handlers. Multiple handlers compile to trigger.any — the first matching trigger fires.
Manual
Run on demand via grimoire cast or grimoire simulate:
on manual: {
dex.swap(USDC, WETH, params.amount)
}
Scheduled
on hourly: { ... }
on daily: { ... }
on "0 9 * * 1-5": { ... } # weekdays at 9am UTC
Condition-based
Polls every <seconds> and fires when the expression is true:
on condition price(ETH, USDC) < 2000 every 60: {
lender.lend(USDC, params.amount)
}
Event-based
on event "PriceAlert" where event.asset == "ETH": {
dex.swap(USDC, ETH, params.amount)
}
Statements
Supported in any trigger body:
| Statement | Example |
|---|---|
| Assignment | x = expr |
| Action call | dex.swap(USDC, WETH, amount) |
| Conditional | if x > 0 { ... } elif ... { ... } else { ... } |
| Loop | for item in list { ... } |
| Repeat | repeat 3 { ... } |
| Loop until | loop until cond max 10 { ... } |
| Try/catch | try { ... } catch err { ... } finally { ... } |
| Parallel | parallel { branch1: { ... } branch2: { ... } } |
| Atomic | atomic { ... } |
| Emit | emit settled(amount=result) |
| Halt | halt "reason" |
| Wait | wait 60 |
| Advisory | decision = advise risk: "prompt" { ... } |
Action Calls
Call protocol actions through your venue aliases:
dex.swap(USDC, WETH, params.amount)
lender.lend(USDC, params.amount)
lender.borrow(USDC, params.amount, WETH)
morpho.supply_collateral(WETH, amount, "weth-usdc-86")
bridge.bridge(USDC, amount, 42161)
Constraints
Apply per-action constraints with the with clause:
dex.swap(USDC, WETH, amount) with max_slippage=50, deadline=300
Constraint aliases: slippage → max_slippage, min_out → min_output, max_in → max_input.
Advisory Decisions
Embed typed AI decision points that always have a fallback:
decision = advise risk: "Should we rebalance given current rates?" {
context: {
balance: balance(USDC)
price: price(ETH, USDC)
}
within: "execution"
output: {
type: object
fields: {
allow: boolean
reason: string
}
}
on_violation: reject
timeout: 20
fallback: { allow: false, reason: "timeout" }
}
if decision.allow {
dex.swap(USDC, WETH, params.amount)
}
See Advisory Decisions for the full how-to guide.
Expressions
Built-in functions:
| Function | Description |
|---|---|
balance(asset) | On-chain token balance of the vault |
balance(asset, address) | On-chain balance of a specific address |
price(base, quote) | Token price via Alchemy (requires Alchemy RPC) |
min(a, b) | Minimum of two values |
max(a, b) | Maximum of two values |
Unit literals: 1.5 USDC, 50 bps, 5m, 1h, 1d, 50%.