support.eth curator

Security & Roadmap

Trust model, security features, known limitations, invariants, and production roadmap

Security & Roadmap

This page describes the protocol's trust model, active security features, known limitations, invariants that should hold, and the remaining production-hardening roadmap. For the system design that these properties derive from, see Architecture.

Trust Model

ActorTrust LevelCapabilities
Strategy OwnerTrusted for allocations and circuit-breakerCan rebalance and pause/unpause. Cannot withdraw or upgrade.
DonorsTrustlessSend funds directly. No approvals, no routing.
RecipientsTrustlessPull from warehouse. Isolated failures.
ProtocolTrustlessNo admin keys. No upgrade authority. Per-tenant factories.

Non-Custodial Properties

  • The Strategy contract has no withdrawal function. Funds can only leave via distribute(), which routes to configured recipients through the SplitsWarehouse or, for same-factory child strategies, by direct transfer to the child contract. The owner can change where funds go (rebalance) but can never extract funds.
  • Distribution is permissionless — anyone can call distribute(). This prevents funds from being locked by an inactive owner.
  • Recipients self-custody — only the balance owner (or delegate) can call warehouse.withdraw(). No one can redirect, freeze, or force withdrawals.
  • All off-chain social data (drafts, comments, reactions, notifications) is non-financial. The server can be compromised without risk to funds — on-chain state remains the source of truth.

Failure Isolation

  • Each strategy is independent — no shared state except the warehouse.
  • Batch deposits to the warehouse mean a reverting recipient does not block the others.
  • Child strategies registered with the same factory receive allocations by direct transfer; the parent does not call distribute on the child, so the child's pause state or internal errors cannot block the parent's payout to other recipients.
  • Factories are stateless beyond the implementation address and registry mappings — no admin path, no pause.

Security Features

  • ReentrancyGuard on all state-changing functions (distribute, harvest, deposit, withdraw).
  • PausableUpgradeable on Strategy and YieldRedirector4626 (owner-only circuit breaker). withdraw/redeem on the redirector remain callable while paused so depositors can always exit.
  • Rebalance → distribute cooldown: rebalanceDistributeCooldown is an immutable on the factory; each clone reads it once at init. Every allocation change (including initialize) sets distributeUnlockAt = block.timestamp + cooldown. distribute() reverts with DistributeLocked(distributeUnlockAt) until it expires. This mitigates rebalance-front-running of large incoming donations without adding a two-step UX.
  • Factory-owned registries: isFactoryStrategy and isFactoryRedirector mappings anchor identity to the deploying factory. Strategy._isStrategy() staticcalls the factory's registry rather than trusting a spoofable factory() return.
  • Sender-bound deterministic CREATE2: createDeterministic derives keccak256(abi.encode(msg.sender, salt)), so different deployers cannot collide on the same salt. predictDeterministicAddress(deployer, salt) mirrors this.
  • ENS label claim guards: StrategyFactory.assignPendingLabel(node, claimant) must pre-approve a strategy before it can consume an ENS name transferred to the factory. setENSName reverts UnknownStrategy for non-registered addresses.
  • Harvest thresholds: YieldRedirector4626.harvest() reverts HarvestTooSoon when called inside minHarvestInterval, and HarvestBelowMinimum when harvestable() < minHarvestAmount. Both thresholds are set from factory immutables.
  • ERC-4626 max* overrides: maxDeposit, maxMint, maxWithdraw, and maxRedeem clamp to the source vault's limits and the currently tracked principal.
  • sweep(token, to) rescues accidentally-sent tokens on the redirector. Source-vault shares are explicitly excluded (CannotSweepSourceShares).
  • SafeERC20 for all token operations; forceApprove(warehouse, 0) after every ERC-20 batch deposit.
  • Checks-Effects-Interactions pattern throughout.
  • Custom errors for gas efficiency.
  • _disableInitializers() on implementation contracts.

Invariants

Strategy

  • totalWeight == sum(allocations[i].weight).
  • allocations.length > 0 && allocations.length <= 50.
  • All recipients non-zero; no recipient equals the strategy itself.
  • While a call to distribute is executing, block.timestamp >= distributeUnlockAt.

YieldRedirector4626

  • totalAssets() == principal (wrapper share price stays 1:1 with the underlying asset).
  • harvestable() == max(sourceVault.convertToAssets(vaultShares) - principal, 0).
  • principal is denominated in asset units and is decremented by the delivered amount on withdraw and harvest (never by the shares burned).
  • Under source-vault losses, principal is best-effort: wrapper depositors share the loss proportionally via max* clamps.

Known Limitations

LimitationRiskPath to Resolution
No minimum distributionGas griefing via tiny distributionsMIN_DISTRIBUTION threshold on distribute
Pause authority is a single keyOwner-key compromise can halt distributionsMultisig + timelock ownership of strategies and factories
Fee-on-transfer tokensBalance calculations incorrectToken allowlist or explicit warning at deposit-time
Rebasing tokensShare accounting breaksNot supported
Source-vault lossesRedirector principal is best-effortAllowlist audited source vaults; document per-vault risk
External reward tokensRewards on the source vault are not harvestedAdd reward-token harvesting path

Token Compatibility

Token TypeStatus
Standard ERC-20Supported
Native ETHSupported
Fee-on-transferNot supported
RebasingNot supported
ERC-777Untested

Production Roadmap

Shipped items reflect the audit-fix pass.

Security hardening

  • Rebalance → distribute cooldown (configurable per factory)
  • Pausable Strategy and YieldRedirector4626
  • Factory-owned strategy / redirector registries + spoof-resistant routing
  • Sender-bound deterministic CREATE2 salts
  • Child strategies: push-only transfer from parent (no nested distribute)
  • Internal audit pass
  • Minimum-distribution threshold
  • Multisig + timelock ownership for factory / strategy pause authority
  • External security audit by a reputable firm

Yield Redirector

  • Live-value accounting (totalAssets() returns principal, harvestable() returns live surplus)
  • minHarvestAmount and minHarvestInterval
  • sweep() for non-source tokens
  • ERC-4626 max* overrides
  • External reward-token harvesting
  • Additional vault standards beyond ERC-4626

Token support

  • Allowlist or runtime detection for non-standard tokens
  • Wrapper strategies for fee-on-transfer tokens
  • Documented deposit warnings in the UI

Governance

  • Governor-controlled strategies
  • Multi-sig approval on large rebalances
  • Time-weighted allocation changes

Social layer

  • Rate limiting on REST endpoints
  • Spam prevention for comments/reactions
  • Notification delivery (email, push) beyond polling
  • Comment moderation tools

Ecosystem

  • Base Sepolia multi-tenant deployment
  • Additional L2 deployments (Optimism, Arbitrum)
  • Integration with streaming distribution (Drips)
  • On-chain curator reputation / scoring
  • Additional tenants beyond support.eth

Technical Specifications

Contract Standards

  • Solidity ^0.8.20
  • OpenZeppelin Contracts (Upgradeable)
  • EIP-1167 Minimal Proxy Clones
  • ERC-6909 Multi-Token Warehouse (via 0xSplits)
  • ERC-4626 Vault Standard (Yield Redirector)
  • ERC-7528 ETH Address Convention

Gas (approximate)

OperationGas
Create strategy~150,000
Rebalance (5 allocations)~80,000
Distribute (5 recipients)~120,000
Withdraw from warehouse~50,000
Create yield redirector~200,000
Harvest yield~100,000

Networks

NetworkChain IDRole
Hardhat31337Local development
Sepolia11155111Testnet
Base Sepolia84532Multi-tenant testnet
Mainnet1Planned

External Dependencies

  • SplitsWarehouse (0xSplits)
  • ENS (Ethereum Name Service)
  • Ponder (indexer)
  • viem / wagmi (SDK)

Ownership of strategies and factories is currently a single key. For production use, move factory ownership to a multisig (with a timelock where appropriate) before deploying to mainnet.

On this page