Skip to main content
Every proposal and governor command requires --vault. 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 exceptions are sherwood governor set-mgmt-split and set-perf-split, which target the protocol-wide ProtocolConfig and take no --vault.
Owner-only proposals. While the factory’s ownerOnlyProposals launch flag is on (the private-beta setting), only the vault owner can propose, and collaborative proposals are refused. The owner must also be a registered agent on the vault. proposal create and strategy propose read the flag live and refuse any other proposer before pinning metadata or locking the bond: This vault is single-operator: … <proposer> is not the owner (<owner>); propose from the owner wallet. On chain the reverts are ProposerNotOwner and CollaborationDisabled. Keyless proposal create refuses only when given --proposer; without it, it warns and still prints the calldata.

sherwood proposal create

The vault owner submits a strategy proposal with pre-committed execute + settle calls. Most proposals are built by sherwood strategy propose instead, which writes these files for you.
Proposing pulls the proposer bond in WOOD into ProposerBondEscrow. The CLI quotes it from ExposureLedger.proposerBondWood, refuses with InsufficientProposerBondWood when the wallet does not hold it, and prints the WOOD approval to the governor’s bondEscrow() first when the allowance is short. See Proposer bond and proposal reclaim-bond.
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 vault set-agent-fee. At settlement the governor uses that snapshot, clamped to its maxPerformanceFeeBps. The management fee is not a proposal parameter at all — it is stamped onto the vault at creation and accrues on assets × time deployed.

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 vault 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 the caller is a registered agent on the vault and, while ownerOnlyProposals is on, the vault owner
  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, envelope, executeCalls, executeCallCaps, settlementCalls, settlementCallCaps, coProposers) — strategy is the clone that holds on-venue positions (pass address(0) for a queue-only proposal); envelope is the RiskEnvelope (maxCapital, maxDrawdownBps); executeCallCaps and settlementCallCaps are parallel per-call gross-outflow caps in the vault asset (one uint256 per corresponding call; each batch’s sum must be <= envelope.maxCapital, checked independently); 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 vault 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.
  • A new vault starts at the protocol default (FeeConstants.DEFAULT_AGENT_FEE_BPS, 2000 bps = 20%). An explicit 0 is legal.
  • Capped at the protocol ceiling (FeeConstants.MAX_PERFORMANCE_FEE_BPS, 2500 bps = 25%) by the vault, and additionally clamped to the governor’s maxPerformanceFeeBps (2000 bps on a factory-created vault) at settlement. Going above 20% therefore takes two changes, not one.
  • Charged only on value above the fund’s previous peak price per share. This command does not affect the management fee, which has no per-vault setter.
  • Calls vault.setAgentFeeBps(bps). Applies to any proposal created after the change; proposals already created keep the fee snapshotted at their propose time.

sherwood proposal list

List proposals for a vault.
Queries subgraph for Proposal entities filtered by vault/state. Falls back to on-chain iteration via governor.proposalCount() + governor.getProposal(id). Output:

sherwood proposal show <id>

Full detail view of a single proposal.
Displays:
  • Proposal metadata (from IPFS if available)
  • State, timestamps (created, vote end, review end, execution deadline, executed, settled)
  • Guardian coverage (from GuardianReview on), read from the ExposureLedger: each approving guardian’s WOOD lock and its USD value, total WOOD locked, required coverage in the vault asset (for example 60 USDG) and its USD value, and a status: FULL, PARTIAL (with the share of capital execute would deploy), NONE (execute reverts InsufficientApproveCoverage) or UNKNOWN (feed unavailable; retry). The governor’s effectiveMaxCapital is only written at execute, so do not use it to check coverage during review
  • Vote breakdown — voting is optimistic, so only AGAINST weight is tallied, against votableSupply × vetoThresholdBps. There is no votesFor / votesAbstain
  • The strategy’s address and where it came from (provenance): Cloned from the core protocol <name> template 0x… (sherwoodagent/sherwood-protocol). for Portfolio, Morpho Supply and Concentrated Liquidity; Cloned from the <name> template 0x… (sherwoodagent/sherwood-strategies). followed by This strategy is unaudited: … for a community template; Not cloned from any template: this strategy is hand-written code … for a strategy registered directly on the StrategyFactory; or Provenance could not be read (<reason>). Signed proposal vote prints the same lines before it sends the vote
  • Decoded calls (show target names for known targets like the vault’s swap adapter and ERC-20 approvals)
  • Capital snapshot (if executed)
  • P&L and fees (if settled)

sherwood proposal vote

Cast a shareholder vote on a pending proposal. Voting is optimistic: only Against weight is tallied, and the proposal is rejected when it reaches votableSupply × vetoThresholdBps (20% by default). For and Abstain are recorded but carry no weight.

Flow

  1. Load proposal, verify state is Pending
  2. Wait for voting to open (see below), then check the caller has voting power (vault shares at the snapshot; queued shares carry none)
  3. Display the proposal summary, the strategy’s provenance lines and the vote weight
  4. Confirm with user
  5. Call governor.vote(proposalId, support) — support is the VoteType: For (0), Against (1), or Abstain (2), set via --support for|against|abstain

Vote timing

Voting opens one second after the propose block. The governor snapshots at the propose block’s timestamp minus one and refuses votes while block.timestamp <= snapshotTimestamp + 1 (NotWithinVotingPeriod); getVoteWeight reads 0 until then. Signed proposal vote waits this out on the chain’s clock, showing Voting opens in <n> s — waiting..., then votes. If no later block has landed after the wait it stops with No block has been produced since this proposal …; rerun once the chain has produced a block. With --calldata-only the CLI does not wait. Broadcast the vote only after a block whose timestamp is at least the propose block’s timestamp + 1. A vote sent earlier reverts NotWithinVotingPeriod; send it again. Delegation is disabled on the vault (delegate reverts DelegationDisabled); every holder is self-delegated.
Casing differs between the CLI and the HTTP API. The CLI --support choices are lowercase for | against | abstain. The HTTP API vote field is case-sensitive and takes the capitalized For | Against | Abstain — pass it exactly, or the request is rejected.

sherwood proposal review-vote

Cast a guardian review vote on a proposal already in GuardianReview. Distinct from proposal vote, which is the shareholder For/Against/Abstain ballot.
Calls GuardianRegistry.voteOnProposal(governor, proposalId, support, lockWood) — four arguments. There is deliberately no overload without the amount, so --lock-wood is required in both directions.
A Block is two transactions. The review must already be open: openReview(governor, proposalId) is permissionless from the end of the shareholder vote, and voteOnProposal reverts ReviewNotOpen until it lands. A review nobody opens resolves not blocked and the proposal executes unreviewed. sherwood proposal open-reviews finds and opens the reviews that need it.An Approve whose lock is worth less than the floor reverts ApproveLockBelowFloor — raise --lock-wood to a number you are willing to lose, or vote Block. See Coverage and underwriting.

sherwood proposal execute

Execute an approved proposal (anyone can call).
--dry-run first checks guardian coverage on the ExposureLedger (the same section proposal show prints) and exits non-zero when it is short: NONE (execute would revert InsufficientApproveCoverage), PARTIAL (unless you pass --allow-partial-coverage) or UNKNOWN. It then simulates the execute calls.

Flow

  1. Verify proposal is Approved and within the execution window (24 hours by default; an unexecuted proposal becomes Expired)
  2. Verify no other strategy is active on the vault and the owner bond is live (OwnerBondNotLive otherwise)
  3. Call governor.executeProposal(proposalId). The governor re-checks tier, coverage and the maxCapitalBps ceiling, requires at least one covering guardian approval (InsufficientApproveCoverage), and scales the deployed amount down when coverage is partial
  4. Print capital snapshot and redemption lock status
The cooldown is checked at propose, not at execute. A Portfolio proposal holding stock tokens reverts StalePrice during the weekend feed gap (about Saturday 18:40 UTC to Monday 00:00 UTC); see Strategy Commands.

sherwood proposal settle

Settle an executed proposal. Routes to the appropriate settlement path.

Routing logic

  • If caller is the proposer: settleProposal(proposalId) — allowed one hour after execution (StrategyDurationNotElapsed before that)
  • If strategy duration has elapsed: settleProposal(proposalId) — permissionless, anyone can call
  • If caller is vault owner, duration elapsed and --calls given: emergencySettleWithCalls(proposalId, calls) — opens a bonded, guardian-reviewed emergency settle; the calls run only at finalizeEmergencySettle
Output: P&L, fees distributed, redemptions unlocked confirmation. Settlement stamps the frozen price that queued redeems claim at; a proposal that ends any other way never stamps (see Stranded redeems).

sherwood proposal cancel

Cancel a proposal before execution.
  • Proposer (default): can cancel from Draft, Pending (while the voting window is open), GuardianReview, or Approved. A cancel during GuardianReview also drives registry.cancelReview so approvers can’t be slashed by a stale resolution.
  • Vault owner (--emergency): can emergencyCancel only from Draft or Pending. Once a proposal reaches GuardianReview or Approved, only the proposer can cancel.
A cancel releases the proposer bond; reclaim it with proposal reclaim-bond. It also starts the governor cooldown: the CLI prints the time the next proposal can be submitted, and strategy propose / proposal create refuse before then with CooldownNotElapsed and that time (with --calldata-only they warn instead). Reproposing the same strategy reuses the existing, never-executed clone, so no clone transaction is sent.

sherwood proposal reclaim-bond

Release a terminal proposal’s WOOD bond from ProposerBondEscrow back to the proposer. Permissionless: any wallet may broadcast it, and the WOOD always goes to the recorded proposer.
  • The proposal must be Rejected, Expired, Cancelled or Settled. The CLI refuses with Proposal is <state> — a bond is only reclaimable from Rejected / Expired / Cancelled / Settled.; on chain the revert is ProposalNotTerminal.
  • A Rejected, Expired or Cancelled proposal releases immediately. A Settled proposal waits for the challenge window: executedAt + strategyDuration + challengeWindow (14 days) must have passed and no challenge may have the coverage frozen, else ChallengeWindowOpen.
  • This proposal has no proposer bond left to reclaim (never bonded, already reclaimed, or forfeited). means the bond is 0 (NoBondToReclaim). A bond is forfeited only by a challenge conviction; challenge filings are paused through the beta.

Veto (vault owner, no CLI command)

The vault owner can veto a proposal while it is Pending by calling vetoProposal(proposalId) on the vault’s governor directly (sherwood governor info --vault <addr> prints the governor address; the HTTP API’s /prepare/veto encodes the call). The call reverts ProposalNotCancellable once the proposal enters GuardianReview. Shareholders do not veto: they cast Against votes, and the proposal is rejected when those reach the veto threshold. Blocking after the vote is the guardians’ block quorum.

Recovery: sherwood proposal unstick

Vault-owner-only recovery command for the rare case where an Executed proposal cannot settle because its pre-committed settlement calls revert (e.g. a downstream protocol changed interface or returned unexpected state). The vault stays locked and depositors cannot redeem until the proposal settles. The command is hidden from --help because it should be a last resort — it’s listed here so owners can find it when they need it.
Preconditions (all checked before broadcasting):
  1. Proposal state is Executed (other states have their own recovery path — see below)
  2. Strategy duration has elapsed (unstick requires it)
  3. The proposal is currently the vault’s active proposal
  4. Caller is the vault owner
What it does: calls governor.unstick(id), which replays the voted settlement calls under the rescue price floor. If the strategy still reports executed() afterwards the governor refuses (StrategyNotSettled), and the only exit is the owner’s bonded, guardian-reviewed emergencySettleWithCalls → finalizeEmergencySettle path (sherwood proposal settle --calls <path>). On success the proposal transitions to Settled, the vault unlocks, and depositors can then vault redeem. Recovery paths for other states:

sherwood governor info

Display a vault’s governor address and parameters. Resolves the governor via factory.governorOf(vault).
Output:
The factory defaults are a 24h voting period (bounds 30 min–3d on mainnet 4663), 24h execution window (1h–7d), 20% veto threshold (20–80%), 20% max performance fee (ceiling 25%), 30d max strategy duration (1h–30d) and 1h cooldown (1h–30d). The last three lines are the per-vault caps to seed before the first proposal.
There is no “Registered Vaults” list any more — each governor serves exactly one vault. The fee splits are not shown here either; they live on the shared ProtocolConfig, not the per-vault governor. See Economics.

sherwood governor set-*

Vault-owner parameter setters, one governor per vault — each requires --vault and validates against protocol-wide bounds before submitting. They apply immediately (there is no timelock) but revert (ParamsFrozenDuringProposal) if the vault has an open proposal, Drafts included. All of them honour --calldata-only.

Governance timing (owner-set)

The vault owner sets the governance periods. Change them while no proposal is open; sherwood governor info --vault <addr> shows the current values. The guardian review period after voting is protocol-set (GuardianRegistry.reviewPeriod, 30 min on mainnet 4663), not owner-tunable. A proposal keeps the periods it was created with, so a change applies to the next proposal. For a fast test cycle, set the voting period to 30 min (1800) before the first proposal: vote ends 30 min after propose, review 30 min after that. set-max-capital caps what one proposal may declare as --max-capital, as a share of totalAssets (checked at propose and again at execute). set-tier2-call-cap caps each uncertified (tier-2) call the same way. Both read as 100% on a fresh vault. The matching vault-side cap is sherwood vault set-min-buffer.

sherwood governor set-mgmt-split / set-perf-split (ProtocolConfig)

There is no standalone protocol fee in the two-number model, and ProtocolConfig exposes no setProtocolFeeBps. The protocol and guardian shares are legs of the two fees, changed with these two commands. Both target the protocol-wide config (factory.protocolConfig()), so they take no --vault, and both are protocol-owner only (the protocol multisig). Each set of legs must sum to 10000 bps.
These replaced sherwood governor set-protocol-fee in CLI 0.87.0. The old command targeted a function the contract no longer has, so every call reverted. See Economics for what each leg pays for.

Keeper commands: open-reviews and resolve-reviews

Guardian reviews are opened and resolved permissionlessly. open-reviews opens every review whose shareholder vote has ended (window [voteEnd, reviewEnd)); a review nobody opens resolves not blocked. resolve-reviews resolves every review whose window has elapsed. --dry-run lists what would be sent.

UX Considerations

  • Duration format: Accept human-readable durations (7d, 24h, 1h) in addition to raw seconds
  • Call encoding: For common actions (swaps through the vault’s swap adapter, ERC-20 approvals), 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 vault 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