Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.sherwood.sh/llms.txt

Use this file to discover all available pages before exploring further.

Redemption Lock During Active Strategy

When a strategy is live (proposal in Executed state), the vault’s redemptionsLocked() returns true and withdraw / redeem / deposit / rescueERC20 all revert. Depositors can exit only after settlement, during the cooldown window.
WOOD/SHARES early-exit pool — aspirational, not live in V1. An earlier design proposed one-sided liquidity pools pairing WOOD with each vault’s share token, letting depositors swap out of locked vaults at a market-discovered discount. That design is not implemented in the shipped contracts; today the only exit path is waiting for settlement + cooldown. Early-exit liquidity is tracked for a future release.

WOOD Tokenomics (ve(3,3) stack)

The protocol ships a full ve(3,3)-style token economy (VotingEscrow, Voter, SyndicateGauge, Minter, VoteIncentive, RewardsDistributor, VaultRewardsDistributor) in addition to the governance + fee logic on this page. The surface is live in contracts/src/ but the public documentation is deliberately deferred — the surface is still stabilising around PR #229 (guardian rewards) and the early-exit design above.
Expanded ve(3,3) docs are forthcoming. See the in-repo reference docs/tokenomics-wood.md for the current internal description of lock mechanics, gauge weights, bribe flows, rebase math, and Minter emissions. A dedicated tokenomics section will be added to these public docs once the guardian-reward epoch wiring (fundEpoch) and the Minter → registry integration land in V1.5.
Known gaps flagged in that reference that LPs and integrators should be aware of until the public expansion lands:
  • SyndicateGauge.claimLPRewards always reverts (_calculateLPReward is stubbed). Gauge LP reward accrual is tracked but not claimable. Slices that already accrued under the legacy bootstrap schedule (epochs ≤ 12) can be recovered by the gauge owner via rescueStuckLPRewards(epoch, recipient) — emissions for new epochs no longer accrue to this slice (getLPRewardPercentage returns 0).
  • Price and lock-ratio circuit breakers in Minter are manual (multisig) today, not triggered automatically.
  • No WOOD/SHARES Uniswap V3 early-exit pool is deployed; the design in older drafts is not live.
  • Guardian Block-side rewards (fundEpoch + claimEpochReward on GuardianRegistry) are wired, but Minter → registry auto-funding is V1.5; V1 relies on multisig-funded epochs (see Guardian Review).

Fee Structure

Three fees are distributed from strategy profits at settlement:
FeeRecipientSet byPurpose
Protocol feeprotocolFeeRecipientFactory owner (timelocked)Protocol revenue
Performance feeAgent (proposer)Agent at proposal timeIncentivize good strategy proposals
Management feeVault ownerFactory (per-vault, configurable)Incentivize vault operation and curation
All fees only apply when P&L > 0. On loss, no fees are charged.

Protocol Fee

A protocol-level fee taken from gross profit before agent and management fees. This funds protocol development and operations.
  • protocolFeeBps — percentage of gross profit taken as protocol fee (max 10% / 1000 bps)
  • protocolFeeRecipient — address that receives the protocol fee
  • Both live on the governor and apply to every registered vault
  • Both setProtocolFeeBps and setProtocolFeeRecipient are timelocked through the same queue → delay → finalize pattern as every other governance parameter (6h–7d delay). Neither can be changed instantly. Historically setProtocolFeeRecipient was owner-instant; finding G-C5 in the pre-mainnet review closed the gap by routing the recipient change through _queueChange / _applyChange (address encoded as uint160). Vault operators and depositors now always have visibility into a pending recipient rotation.

Fee distribution order

Fees are calculated and distributed in a strict order — protocol fee first, then agent fee, then management fee:
profit = balanceAfter - capitalSnapshot
if profit > 0:
  protocolFee   = profit * protocolFeeBps / 10000
  netProfit     = profit - protocolFee
  agentFee      = netProfit * performanceFeeBps / 10000
  managementFee = (netProfit - agentFee) * managementFeeBps / 10000
  transfer protocolFee to protocolFeeRecipient (try/catch → escrow on fail)
  transfer agentFee to agent                   (try/catch → escrow on fail)
  transfer managementFee to vault owner        (try/catch → escrow on fail)
  remaining profit stays in vault (accrues to all shareholders)
The protocol fee is calculated on gross profit. Agent and management fees are calculated on the net profit (after protocol fee). Management fee is calculated on the remaining profit after the agent’s cut. This ensures combined fees never exceed profit and maintains a clear priority order.

Management Fee

The management fee incentivizes vault operation — the owner curates agents, manages parameters, handles emergencies.
  • managementFeeBps — configurable per-factory (max 10% / 1000 bps)
  • Applied to new vaults at creation time
  • Owner only earns on profit — aligned with depositor outcomes

Try/catch fee transfers + unclaimed-fee escrow

Every fee transfer — protocol fee, agent / co-proposer performance fee, and vault owner management fee — is wrapped in try/catch around vault.transferPerformanceFee(asset, recipient, amount). If the transfer reverts (USDC blacklist, paused token, contract recipient with a failing receive, etc.) the governor:
  1. Credits the owed amount to an on-chain _unclaimedFees[recipient][token] escrow mapping
  2. Emits FeeTransferFailed(recipient, token, amount, reason) so indexers can surface the stuck fee
  3. Continues settlement without reverting
Affected recipients pull their escrow later with claimUnclaimedFees(vault, token) once the failure condition is cleared (they un-blacklist, rotate the recipient, unpause, etc.). Read via unclaimedFees(recipient, token). This ensures:
  • Depositor capital is never held hostage by a blacklisted fee recipient — settlement always completes
  • Fees are not lost — they are escrowed on the governor and pullable by the rightful recipient
  • Every recipient gets the same protection (protocol, agent, co-proposers, vault owner)
Tracked as item W-1 in the pre-mainnet punch list; regression coverage in test/governor/FeeBlacklistResilience.t.sol. Safety: performanceFeeBps is capped by maxPerformanceFeeBps (governor parameter). protocolFeeBps is capped at 1000 bps (10%). managementFeeBps is capped at 1000 bps (10%) and set at the factory level. Why a management fee? Without it, there is no incentive to operate a vault — the owner curates agents, manages targets, sets parameters, handles emergencies, but earns nothing. The management fee aligns vault owner incentives with depositor outcomes (owner only earns on profit).

Single Strategy Per Vault

Only one strategy can be live (Executed state) per vault at a time. This simplifies capital accounting, eliminates cross-strategy risk, and makes the redemption lock/cooldown model clean.
  • Governor tracks activeProposal[vault] — the currently executing proposal ID (0 if none)
  • executeProposal reverts if activeProposal[vault] != 0
  • executeProposal also reverts if the vault is in its cooldown window
  • Multiple proposals can be in Pending/Approved state simultaneously — they queue up
  • Only one can be executed at a time

Open Design Questions

Strategy Carry Model

Two possible models: A. Per-proposal performance fee (current design)
  • Agent sets fee when proposing
  • Fee paid on settlement from profits only
  • Simple, clear, hackathon-ready
B. Protocol-level revenue share (v2)
  • Strategy creators earn ongoing % of all TVL running their strategy
  • More DeFi-native (like Uniswap LP fees)
  • Needs TVL tracking, streaming payments, strategy template integration
Recommendation: Model A for now. Model B is the long-term vision.

What Happens if a Strategy Loses Money?

  • Agent earns nothing (performance fee only applies to profits)
  • Loss is socialized across all shareholders (standard fund behavior)
  • Loss is recorded on-chain via the ProposalSettled(proposalId, pnl, totalFee) event emitted by _finishSettlement — indexers can aggregate into per-agent track records. A dedicated STRATEGY_PNL EAS attestation for richer agent reputation is planned for V1.5 and is NOT shipped in V1.
  • No slashing mechanism in V1 for agents — see Guardian Review for the separate guardian / vault-owner slashing layer.
Future consideration: Agent bonds / slashing for repeated losses.

Can Agents Update a Live Proposal?

No. Once submitted, proposal params are immutable. If an agent wants different terms, they cancel and create a new proposal. This keeps voting clean — shareholders know exactly what they are voting on.