Skip to main content
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.
FlagRequiredDescription
--vaultYesVault address the proposal targets
--nameYesStrategy name (used in metadata JSON; ignored for pinning if --metadata-uri provided)
--descriptionYesStrategy rationale and risk summary (ignored for pinning if --metadata-uri provided)
--durationYesStrategy duration. Accepts seconds or human format (7d, 24h, 1h)
--execute-callsYesPath to JSON file with execute Call[] array (open positions)
--settle-callsYesPath to JSON file with settlement Call[] array (close positions)
--metadata-uriNoOverride — 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).

Metadata pinning (IPFS via Pinata)

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 }

Call JSON format

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

  1. Validate caller is a registered agent on the vault
  2. Parse and validate calls JSON
  3. If no --metadata-uri: build metadata JSON, pin to IPFS via Pinata, get ipfs://Qm... URI
  4. Display proposal summary for review (name, duration, call count, metadata URI)
  5. 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)
  6. 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):
  1. Proposal state is Executed (other states have their own recovery path — see below)
  2. Strategy duration has elapsed (emergencySettle requires it)
  3. The proposal is currently the vault’s active proposal
  4. 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.
OptionRequiredDescription
--id <proposalId>YesProposal ID to unstick
--vault <addr>YesVault the proposal belongs to (per-vault governor)
--dry-runNoCheck preconditions and print the fallback call without broadcasting
--yesNoSkip the interactive confirmation
Recovery paths for other states:
StateRecovery
Draft / Pendingsherwood proposal cancel --id <id> (proposer) or --emergency (vault owner)
GuardianReview / Approvedsherwood proposal cancel --id <id> (proposer only)
ExpiredVault is not locked — nothing to do
SettledAlready 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