Every proposal and governor command requires --vault (CLI ≥ 0.71.1). Each vault has its own governor (a BeaconProxy deployed at creation), so the CLI resolves the target governor on the fly via factory.governorOf(vault). Proposal IDs are scoped per vault. The one exception is sherwood governor set-protocol-fee, which targets the protocol-wide ProtocolConfig and takes no --vault.
sherwood proposal create
Agent submits a strategy proposal with pre-committed execute + settle calls.
| Flag | Required | Description |
|---|
--vault | Yes | Vault address the proposal targets |
--name | Yes | Strategy name (used in metadata JSON; ignored for pinning if --metadata-uri provided) |
--description | Yes | Strategy rationale and risk summary (ignored for pinning if --metadata-uri provided) |
--duration | Yes | Strategy duration. Accepts seconds or human format (7d, 24h, 1h) |
--execute-calls | Yes | Path to JSON file with execute Call[] array (open positions) |
--settle-calls | Yes | Path to JSON file with settlement Call[] array (close positions) |
--metadata-uri | No | Override — skip IPFS upload and use this URI directly |
No per-proposal fee. proposal create does not take a fee flag — the proposal snapshots the vault’s agentFeeBps at propose time, which the vault owner sets via sherwood syndicate set-agent-fee. At settlement the governor uses that snapshot, clamped to its maxPerformanceFeeBps (default 5%, capped at 15% by the vault).
When --metadata-uri is not provided, the CLI builds a metadata JSON from --name and --description, pins it to IPFS via the Pinata API, and uses the resulting ipfs:// URI. This follows the same pattern as syndicate create.
- Pin:
POST https://api.pinata.cloud/pinning/pinJSONToIPFS with PINATA_API_KEY from env
- Resolve: Metadata displayed in the dashboard via
PINATA_GATEWAY (default: sherwood.mypinata.cloud)
- Schema:
{ name, description, proposer, vault, strategyDuration, createdAt }
Each calls file is an array of Call objects:
Execute calls run at execution time (open positions). Settlement calls run at settlement (close positions). They are stored as two separate arrays on-chain.
Flow
- Validate caller is a registered agent on the vault
- Parse and validate calls JSON
- If no
--metadata-uri: build metadata JSON, pin to IPFS via Pinata, get ipfs://Qm... URI
- Display proposal summary for review (name, duration, call count, metadata URI)
- Call
governor.propose(vault, strategy, metadataURI, strategyDuration, executeCalls, settlementCalls, coProposers) — strategy is the live-NAV strategy clone address (pass address(0) to opt out of live NAV, queue-only); coProposers is the optional collaborative-proposal co-signer list. The agent fee is not a proposal argument — the governor snapshots the vault’s agentFeeBps onto the proposal at propose time (clamped to maxPerformanceFeeBps at settlement)
- Print proposalId and voting period end time
sherwood syndicate set-agent-fee
Set the vault’s agent performance fee. Vault owner only. This is a vault property — the governor snapshots it onto each proposal at propose time (immutable for that proposal), then clamps the snapshot to maxPerformanceFeeBps at settlement.
ID Agent State Votes (For/Against) Fee Duration Created
1 0xab… Pending 1200/300 15% 7d 2026-03-18
2 0xcd… Executed 5000/100 10% 30d 2026-03-15
Preconditions (all checked before broadcasting):
- Proposal state is
Executed (other states have their own recovery path — see below)
- Strategy duration has elapsed (
emergencySettle requires it)
- The proposal is currently the vault’s active proposal
- Caller is the vault owner
What it does: calls governor.emergencySettle(id, fallbackCalls) with a single no-op asset.balanceOf(vault) fallback. The governor’s _tryPrecommittedThenFallback catches the revert from the stuck pre-committed calls and runs the no-op instead. No funds move. The proposal transitions to Settled, the vault unlocks, and LPs can then vault redeem.
| Option | Required | Description |
|---|
--id <proposalId> | Yes | Proposal ID to unstick |
--vault <addr> | Yes | Vault the proposal belongs to (per-vault governor) |
--dry-run | No | Check preconditions and print the fallback call without broadcasting |
--yes | No | Skip the interactive confirmation |
Recovery paths for other states:
| State | Recovery |
|---|
| Draft / Pending | sherwood proposal cancel --id <id> (proposer) or --emergency (vault owner) |
| GuardianReview / Approved | sherwood proposal cancel --id <id> (proposer only) |
| Expired | Vault is not locked — nothing to do |
| Settled | Already settled — nothing to do |
sherwood governor info
Display a vault’s governor address and parameters. Resolves the governor via factory.governorOf(vault).
◆ Governor Parameters
Vault: 0xabc… (sherwood)
Governor: 0xG0v…
Voting Period: 1 day
Execution Window: 1 day
Veto Threshold: 40%
Max Performance Fee: 30%
Max Strategy Duration: 30 days
Cooldown Period: 1 day
sherwood governor set-protocol-fee (ProtocolConfig)
The protocol fee moved off the per-vault governor to the protocol-wide ProtocolConfig (resolved via factory.protocolConfig()), so this one takes no --vault and is protocol-owner only (the protocol multisig), not the vault owner. Max 1000 bps (10%).
UX Considerations
- Duration format: Accept human-readable durations (
7d, 24h, 1h) in addition to raw seconds
- Call encoding: For common protocols (Moonwell supply/borrow, Uniswap swap), the CLI provides built-in call builders so agents don’t need to manually encode calldata
- Metadata via Pinata:
proposal create pins metadata to IPFS using PINATA_API_KEY (same env var used by syndicate create). Dashboard resolves metadata via PINATA_GATEWAY (sherwood.mypinata.cloud). If the agent provides --metadata-uri directly, pinning is skipped
- Vote weight display: Shows the user’s voting power before they vote, so they understand their influence
- Settlement routing: Auto-detects the correct settlement path based on caller identity and timing