Skip to main content

MultistrategyVaultFactory

Git Source

Inherits: IMultistrategyVaultFactory

Title: MultistrategyVault Factory

Author: yearn.finance; adapted by Golem Foundation

Factory for deploying MultistrategyVault instances with protocol fee management

Deploys minimal proxy clones of VAULT_ORIGINAL using CREATE2 for deterministic addresses

DEPLOYMENT MECHANISM:

  • Uses OpenZeppelin Clones library for minimal proxy (EIP-1167)
  • CREATE2 deployment for deterministic addresses
  • Salt = keccak256(deployer, asset, name, symbol)
  • Same deployer cannot deploy identical vault twice
  • Each vault is a lightweight proxy delegating to VAULT_ORIGINAL

PROTOCOL FEE SYSTEM:

Revenue Share Model:

  • Vaults charge total fees during processReport()
  • Protocol takes percentage of total fees
  • Formula: protocolFees = totalFees * protocolFeeBps / MAX_BPS
  • Remainder goes to vault-specific accountant

Example:

  • Vault charges 100 assets in fees
  • Protocol fee = 10% (1000 bps)
  • Protocol receives: 10 assets
  • Accountant receives: 90 assets

FEE PACKING OPTIMIZATION:

Protocol fee data packed into single uint256 storage slot:

Bit Layout (256 bits total):

BitsContentSizeMax Value
0-7Custom flag8 bits0 or 1
8-23Fee in basis points16 bits65535
24-183Fee recipient addr160 bitsaddress
184-255Unused72 bits-

Benefits:

  • Single SLOAD for fee queries (saves ~2100 gas)
  • Efficient storage (fits in one slot)

TWO-TIER FEE SYSTEM:

  • Default fee: Applied to all vaults unless custom set
  • Custom fee: Per-vault override (recipient always uses default)

Notes:

State Variables

API_VERSION

API version of vaults deployed by this factory

Must match VAULT_ORIGINAL's API version

string public constant override API_VERSION = "3.0.4"

MAX_FEE_BPS

Maximum protocol fee in basis points (50%)

Hard cap to prevent excessive fees. 5000 bps = 50%

uint16 public constant override MAX_FEE_BPS = 5_000

FEE_BPS_MASK

Bitmask for extracting fee basis points from packed data

16-bit mask: 0xFFFF (bits 8-23 in packed data)

uint256 public constant override FEE_BPS_MASK = 2 ** 16 - 1

VAULT_ORIGINAL

Address of the canonical vault implementation

All deployed vaults are minimal proxies pointing to this implementation Set once during construction, never changes

address public immutable override VAULT_ORIGINAL

shutdown

Whether factory has been permanently shutdown

When true, no new vaults can be deployed. Cannot be reversed

bool public override shutdown

governance

Address with governance authority over factory

Can set fees, shutdown factory, transfer governance

address public override governance

pendingGovernance

Address pending to become new governance

Two-step transfer process for safety

address public override pendingGovernance

name

Human-readable name for this factory instance

Useful for identifying different factory versions

string public override name

defaultProtocolFeeData

Default protocol fee configuration packed into single slot

Bit layout: [72 empty][160 recipient][16 fee bps][8 custom flag] Applied to all vaults unless custom fee is set Packing saves gas by requiring only one SLOAD

uint256 private defaultProtocolFeeData

customProtocolFeeData

Per-vault custom protocol fee overrides

Maps vault address → custom fee data (same bit layout as default) Custom flag (bit 0) set to 1 indicates custom fee active Recipient always uses default (custom only overrides fee bps)

mapping(address => uint256) private customProtocolFeeData

Functions

constructor

Initializes the factory with implementation and governance

Sets immutable values that cannot be changed after deployment

constructor(string memory _name, address _vaultOriginal, address _governance) ;

Parameters

NameTypeDescription
_namestringHuman-readable name for this factory (e.g., "Octant V3 Factory")
_vaultOriginaladdressAddress of MultistrategyVault implementation to clone
_governanceaddressAddress with authority to manage factory settings

deployNewVault

Deploys a new MultistrategyVault as a minimal proxy

Uses CREATE2 for deterministic addresses based on unique parameters DEPLOYMENT PROCESS:

  1. Validates factory not shutdown
  2. Generates unique salt from: deployer + asset + name + symbol
  3. Deploys minimal proxy clone via CREATE2
  4. Initializes the new vault with provided parameters
  5. Emits NewVault event DETERMINISTIC ADDRESSES:
  • Same parameters always produce same address
  • Different name/symbol required for same deployer+asset
  • Prevents duplicate deployments
  • Address predictable before deployment GAS COSTS:
  • Minimal proxy: ~50k gas (vs ~3M for full deployment)
  • All logic delegated to VAULT_ORIGINAL
  • Storage lives in proxy contract

Note: security: Reverts if factory is shutdown

function deployNewVault(
address asset,
string memory _name,
string memory symbol,
address roleManager,
uint256 profitMaxUnlockTime
) external returns (address);

Parameters

NameTypeDescription
assetaddressAddress of underlying ERC20 asset token
_namestringVault token name (must be unique for this deployer+asset combo)
symbolstringVault token symbol (must be unique for this deployer+asset combo)
roleManageraddressAddress to manage vault roles
profitMaxUnlockTimeuint256Profit unlock duration in seconds (0-31556952)

Returns

NameTypeDescription
<none>addressvaultAddress Address of the newly deployed vault

vaultOriginal

Returns the vault implementation address used for cloning

function vaultOriginal() external view returns (address);

Returns

NameTypeDescription
<none>addressimplementation Vault implementation contract address

apiVersion

Returns the API version string

function apiVersion() external pure override returns (string memory);

Returns

NameTypeDescription
<none>stringversion API version identifier

protocolFeeConfig

Returns protocol fee configuration for a vault

Checks for vault-specific custom fee, falls back to default Called by vaults during processReport() to calculate protocol fees LOGIC:

  1. If vault has custom fee (custom flag set): Return (custom fee bps, default recipient)
  2. Otherwise: Return (default fee bps, default recipient) NOTE: Recipient ALWAYS comes from default config (custom only overrides bps)
function protocolFeeConfig(address vault) external view override returns (uint16, address);

Parameters

NameTypeDescription
vaultaddressAddress of the vault to query (or address(0) to use msg.sender)

Returns

NameTypeDescription
<none>uint16feeBps Protocol fee in basis points (0-5000, where 5000 = 50%)
<none>addressrecipient Address to receive protocol fees

useCustomProtocolFee

Returns whether vault uses custom protocol fee

function useCustomProtocolFee(address vault) external view override returns (bool);

Parameters

NameTypeDescription
vaultaddressVault address to query

Returns

NameTypeDescription
<none>boolusesCustomFee True if vault has custom fee configured

setProtocolFeeBps

Sets the default protocol fee basis points

Updates default fee applied to all vaults without custom fees Requires fee recipient to be set first

Notes:

  • security: Only callable by governance

  • security: Capped at MAX_FEE_BPS (50%)

function setProtocolFeeBps(uint16 newProtocolFeeBps) external override;

Parameters

NameTypeDescription
newProtocolFeeBpsuint16New fee in basis points (0-5000, where 5000 = 50%)

setProtocolFeeRecipient

Sets the default protocol fee recipient address

Updates recipient for all fees (default and custom) Custom fees only override bps, recipient always comes from default

Note: security: Only callable by governance

function setProtocolFeeRecipient(address newProtocolFeeRecipient) external override;

Parameters

NameTypeDescription
newProtocolFeeRecipientaddressAddress to receive protocol fees (cannot be zero)

setCustomProtocolFeeBps

Sets a custom protocol fee for a specific vault

Allows per-vault fee overrides while using default recipient Requires default recipient to be set first

Notes:

  • security: Only callable by governance

  • security: Capped at MAX_FEE_BPS (50%)

function setCustomProtocolFeeBps(address vault, uint16 newCustomProtocolFee) external override;

Parameters

NameTypeDescription
vaultaddressAddress of the vault to set custom fee for
newCustomProtocolFeeuint16Custom fee in basis points (0-5000)

removeCustomProtocolFee

Removes custom protocol fee for a vault (reverts to default)

Clears custom fee data, vault will use default fee configuration

Note: security: Only callable by governance

function removeCustomProtocolFee(address vault) external override;

Parameters

NameTypeDescription
vaultaddressAddress of the vault to clear custom fee for

shutdownFactory

Permanently shuts down the factory

IRREVERSIBLE: Prevents new vault deployments forever Existing vaults continue operating normally

Notes:

  • security: Only callable by governance

  • security: Cannot be reversed

function shutdownFactory() external override;

transferGovernance

Initiates governance transfer (step 1 of 2)

Two-step process prevents accidental transfer to wrong address

Note: security: Only callable by current governance

function transferGovernance(address newGovernance) external override;

Parameters

NameTypeDescription
newGovernanceaddressAddress to become new governance

acceptGovernance

Completes governance transfer (step 2 of 2)

Caller must be the pendingGovernance address

Note: security: Only callable by pendingGovernance

function acceptGovernance() external override;

_createClone

Creates a minimal proxy clone using CREATE2

function _createClone(address target, bytes32 salt) internal returns (address);

Parameters

NameTypeDescription
targetaddressImplementation address to clone
saltbytes32Unique salt for deterministic address

Returns

NameTypeDescription
<none>addressclone Address of the deployed clone

_unpackProtocolFee

Extracts fee basis points from packed data

function _unpackProtocolFee(uint256 configData) internal pure returns (uint16);

Parameters

NameTypeDescription
configDatauint256Packed fee configuration

Returns

NameTypeDescription
<none>uint16fee Fee in basis points (bits 8-23)

_unpackFeeRecipient

Extracts fee recipient address from packed data

function _unpackFeeRecipient(uint256 configData) internal pure returns (address);

Parameters

NameTypeDescription
configDatauint256Packed fee configuration

Returns

NameTypeDescription
<none>addressrecipient Fee recipient address (bits 24-183)

_unpackCustomFlag

Extracts custom flag from packed data

function _unpackCustomFlag(uint256 configData) internal pure returns (bool);

Parameters

NameTypeDescription
configDatauint256Packed fee configuration

Returns

NameTypeDescription
<none>boolisCustom True if custom fee is set (bit 0)

_packProtocolFeeData

Packs fee data into single uint256

Bit packing layout: [72 empty bits][160 recipient][16 fee bps][8 custom flag]

function _packProtocolFeeData(address recipient, uint16 fee, bool custom) internal pure returns (uint256);

Parameters

NameTypeDescription
recipientaddressFee recipient address (160 bits)
feeuint16Fee in basis points (16 bits, 0-65535)
customboolCustom flag (8 bits, 0 or 1)

Returns

NameTypeDescription
<none>uint256packed Single uint256 with all data packed