On-chain contracts keep the
Syndicate* naming for audit continuity; the product concept is a fund.Architecture
Each fund is a vault. Governance is per-vault: the factory deploys a dedicatedSyndicateGovernor for every vault as a BeaconProxy off a shared GovernorBeacon — there is no protocol-wide governor. The vault is the onchain identity and holds every position; agents never touch fund capital directly. Strategy execution flows governor → vault.executeGovernorBatch → delegatecall into the stateless BatchExecutorLib, so all external calls run as the vault under the vault’s own allowlist.
The vault is the identity — every position the fund opens lives on the vault address. Each vault gets its own governor, a BeaconProxy the factory deploys at createSyndicate, which runs that one vault’s proposal lifecycle, voting, and settlement. The vault resolves its governor from the factory that deployed it (factory.governorOf(vault)) — there is no governor address stored on the vault, and no rewire path, so a governor can never be retroactively swapped under a live vault. Upgrading every fund’s governor at once is a single GovernorBeacon.upgradeTo(newImpl) by the beacon owner.
Core contracts
SyndicateVault
The ERC-4626 vault that is the fund. It holds every position and is the only address that moves fund capital. It extendsERC20Votes on a timestamp voting clock, so shares double as governance weight and auto-delegate to the holder on deposit. Deposits are gated by pause state and an optional depositor whitelist.
Strategy calldata can only enter through executeGovernorBatch, which the vault accepts solely from its own governor and runs by delegatecall into BatchExecutorLib — the owner has no arbitrary-calldata path into the vault. Asset recovery is limited to dedicated rescueERC20 / rescueERC721 / rescueEth paths, all owner-only and blocked while a proposal is open.
totalAssets() is vault float minus the queue reserve minus escrowed unpaid fees — there is no strategy NAV term. While a strategy is executing, instant redeem / withdraw return 0 and depositors use requestRedeem on the withdrawal queue. Instant deposit is closed for the whole open-proposal window. At settlement the vault stamps one frozen post-fee price; anyone can claim.
The redemption lock is derived, not stored: redemptionsLocked() reads the governor’s active-proposal signal live on every call, so the vault and governor can never disagree about whether a strategy is live.
SyndicateGovernor
One governor per vault, deployed as a BeaconProxy so every fund shares an implementation but keeps isolated state. It runs the proposal lifecycle — Draft (collaborative consent) → Pending (voting) → GuardianReview → Approved → Executed → Settled, with terminal Rejected / Expired / Cancelled states. Only registered agents propose, and one strategy is live per vault at a time — an open proposal blocks new ones. Voting is optimistic: weight is theERC20Votes checkpoint taken one second before propose, and a proposal passes after voting ends unless Against votes reach the veto threshold (default 20% of past supply, bounds 20–50%). The vault owner can veto a Pending proposal or cancel a Draft/Pending one; once a proposal enters guardian review, only the guardian cohort and the execution window decide the outcome.
At settlement the governor pays a profit-only fee waterfall — protocol, then guardian, then agent (net, split across any co-proposers), then management — using rates snapshotted at propose time. Failed fee transfers escrow rather than bricking settlement. Parameter setters are owner-instant but frozen while any proposal is open, so the owner is expected to be a multisig; there is no separate onchain timelock. The governor also carries the four owner-only emergency-settlement paths for stuck strategies.
GovernorBeacon
TheUpgradeableBeacon holding the shared SyndicateGovernor implementation for every per-vault governor proxy. upgradeTo is a mass-upgrade primitive — it moves all vault governors to a new implementation atomically — so its owner is the factory-owner multisig.
GuardianRegistry
The review layer between proposal approval and execution. It holds zero assets: it orchestrates the guardian review window, records Approve/Block votes, and callsStakedWood to slash when a proposal is blocked. A permissionless review open at the end of voting snapshots the quorum denominator from cohort stake; if Block votes reach quorum (default 30%) the proposal is Rejected and its approvers are slashed. It also drives the owner-bond review for emergency settlement and holds a slash-appeal reserve that can refund wrongful slashes, capped per epoch. Guardian rewards are attributed here from approver and blocker weights but paid out off-chain via weekly Merkl airdrops — there is no onchain reward pool. See Guardian Review.
StakedWood (sWOOD)
The sole custodian of staked WOOD: guardian stake, vault-owner bonds, vote checkpoints, and slashing all live here. sWOOD is non-transferable vote-escrow. A guardian is active only while staked with no pending unstake; unstaking has a 7-day cooldown that must be at least as long as the guardian review period. Vote weight is the guardian’s own stake (growth-gatedgetPastStake), not inbound delegations. When the registry signals a blocked proposal, sWOOD slashes each approver’s own stake by the deterministic severity ramp and burns the slashed WOOD; a blocked emergency settlement burns the owner’s bond in full.
The in-repo
WoodToken.sol is a non-production test fixture, not the live token. Mainnet WOOD is an external ERC-20 launched on Robinhood Chain and tradeable on Uniswap. Bind to the external WOOD address from Deployments, not the in-repo artifact.VaultWithdrawalQueue
The per-vault async substrate for deposits and withdrawals while a strategy is live. When redemptions are locked, the vault escrows shares (redeem) or assets (deposit) here; at settlement the vault stamps one frozen, post-fee price for the whole proposal and every request in it claims at that single realized price. Because nothing mints or burns against an unrealized, strategy-influenced NAV, the mid-flight price-manipulation surface is removed. A request can be cancelled only before its proposal is stamped.ProtocolConfig
Holds the protocol-level fee parameters — protocol fee (cap 10%) and guardian fee (cap 5%) plus their recipients — shared by every per-vault governor. Values are read only at propose time and snapshotted into the proposal, never read live at settle, so changing a fee never affects an in-flight proposal. It is a plainOwnable2Step contract behind the protocol multisig.
SyndicateFactory
Deploys a fund in one transaction: an immutable ERC-1967 vault proxy, its dedicated governor (BeaconProxy offGovernorBeacon), and its VaultWithdrawalQueue, all wired together. It collects the creator’s WOOD owner bond through sWOOD at creation and records the vault ↔ fund mapping other contracts use to gate calls. The factory is UUPS-upgradeable so its config (creation fee, beacon, registries) can change, but deployed vaults are immutable. Where an ENS registrar is configured it also registers a <subdomain>.sherwoodagent.eth subname; that registrar is not live on the current deployment.
StrategyFactory
Clones and initializes a strategy template in a single transaction. Strategies are ERC-1167 clones, and a separate clone-then-initialize sequence would expose a front-running window where an attacker races the init to bind the clone to their own vault — this factory bundles both steps so that cannot happen. Only a vault’s owner or a registered agent may pre-deploy a strategy, and the vault must be registered on the factory so a rogue contract cannot spoof membership. Strategies are always pre-deployed before a proposal executes; the governor never deploys one during execution.BatchExecutorLib
A stateless library the vault delegatecalls to run a batch of protocol calls atomically. Under delegatecalladdress(this) is the vault, so every call executes as the vault and all positions land on the vault. It has no state and no access control by design — the calling vault enforces its allowlist before delegatecalling, and if any call in the batch fails the whole batch reverts.
Strategies
Strategies are the pluggable execute/settle logic a proposal points at. A strategy pulls vault capital, holds venue positions, and returns the asset on settle. It does not report a value, and the vault does not mark it —totalAssets() is float only.
PortfolioStrategy
Holds a weighted basket of tokenized stocks and rebalances toward target weights. Pricing is Chainlink Data Streams-assisted, and swaps route through a pluggable swap adapter rather than a hardcoded venue, so the strategy is DEX-agnostic. On the active deployment swaps route through Synthra (a Uniswap-V3-compatible DEX) via theUniswapSwapAdapter / SynthraSwapAdapter. See Portfolio Strategy.
Trust boundaries
A few surfaces rely on governance discipline rather than a runtime check. Integrators and auditors should hold these in context:- The governor is trusted on the fee path.
vault.transferPerformanceFeehas no amount or recipient cap on the vault side — the governor is trusted to transfer only what settlement math dictates, to a pre-vetted recipient, in the vault’s asset. The governor is upgradeable only through the multisig-controlledGovernorBeacon, not instantly. - WOOD is assumed to be a fixed-behavior ERC-20. The slashing path uses end-of-function bulk burns; a WOOD with transfer hooks would need that path revisited.
Building and testing
contracts/chains/{chainId}.json (the active chain is 46630.json), the source of truth for the addresses in Deployments.