Available Code

Code Structure & Playbooks

Deep dives, napkin implementations, and operational checklists for OctantFoundry builders. Use this page as the canonical reference when translating the docs into production-grade strategies and mechanisms.

Napkin Implementation — sDAI (DAI → sDAI)

Addresses. DAI (underlying), sDAI at 0x83F20F44975D03b1b09E64809B757c47f942BEeA. The contract exposes full ERC‑4626 semantics backed by DSR accounting (chi/rho/drip), so you never “claim rewards”; value accrues in the exchange rate surfaced by convertToAssets.

interface IERC4626 {
    function asset() external view returns (address);
    function balanceOf(address) external view returns (uint256);
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
    function convertToAssets(uint256 shares) external view returns (uint256);
}

contract YDS_SDAI_Strategy is BaseHealthCheck {
    IERC20   public immutable DAI;
    IERC4626 public immutable sDAI;

    /// Set up / initialize contract
    ...

    function _deployFunds(uint256 amount) internal override {
        sDAI.deposit(amount, address(this));
    }

    function _freeFunds(uint256 amount) internal override {
        if (amount == 0) return;
        sDAI.withdraw(amount, address(this), address(this));
    }

    function _harvestAndReport() internal override returns (uint256 _totalAssets) {
        uint256 idle   = DAI.balanceOf(address(this));
        uint256 shares = IERC20(address(sDAI)).balanceOf(address(this));
        _totalAssets = idle + sDAI.convertToAssets(shares);
    }

    /// Implement extra hooks
    ...
}

Napkin Implementation — sUSDS (USDS → sUSDS)

Addresses. USDS (underlying), sUSDS at 0xa3931d71877C0e7A3148CB7Eb4463524FEc27fbD on Ethereum mainnet. The Sky docs specify ERC‑4626 deposit/withdraw semantics and “no fees.” The yield source is SSR, reflected in convertToAssets.

contract YDS_SUSDS_Strategy is BaseHealthCheck {
    IERC20   public immutable USDS;
    IERC4626 public immutable sUSDS;
    ...

    function _deployFunds(uint256 amount) internal override {
        sUSDS.deposit(amount, address(this));
    }

    function _freeFunds(uint256 amount) internal override {
        sUSDS.withdraw(amount, address(this), address(this));
    }

    function _harvestAndReport() internal override returns (uint256 _totalAssets) {
        uint256 idle   = USDS.balanceOf(address(this));
        uint256 shares = IERC20(address(sUSDS)).balanceOf(address(this));
        _totalAssets = idle + sUSDS.convertToAssets(shares);
    }

    ...
}

Security — Mirror Upstream Limits & Add Safety Rails

Map upstream capacity into your strategy’s limits. Surface the wrapper’s maxDeposit/maxWithdraw as availableDepositLimit/availableWithdrawLimit so users get accurate gating.

function availableDepositLimit(address) public view override returns (uint256) {
    return IERC4626(address(sDAI /* or sUSDS */)).maxDeposit(address(this));
}

function availableWithdrawLimit(address) public view override returns (uint256) {
    return IERC4626(address(sDAI /* or sUSDS */)).maxWithdraw(address(this));
}

Use BaseHealthCheck. Configure profitLimitRatio/lossLimitRatio and keep doHealthCheck enabled. If _harvestAndReport() returns an out‑of‑bounds delta, report() reverts before donation shares are minted/burned, preventing faulty accounting.

Foundry: Prove It on a Mainnet Fork

Setup. Pin a recent mainnet block; fund test EOAs with DAI/USDS; set keeper/management roles; approve the strategy for the underlying.

A. Deposit / Withdraw paths. Deposit → strategy shares minted → _deployFunds increases wrapper share balance. Withdraw → _freeFunds pulls underlying via wrapper and returns it to the user.

B. Profit interval (donation mint). Warp time so convertToAssets increases (DSR/SSR accrual). Keeper calls report(). Assert donation address share balance captures profit while depositor PPS remains steady.

TAM: Writing a New Funding Mechanism (1-person-1-vote)

Context. Implement a simple one-person-one-vote (1p1v) mechanism on top of the shared TokenizedAllocationMechanism core. Implement hooks to grant each voter one unit of power, enforce single votes, define quorum, and convert votes to shares while respecting budget.

Step 0 — Base & Storage Pattern

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {BaseAllocationMechanism} from "./BaseAllocationMechanism.sol";
import {IERC20} from "./interfaces/IERC20.sol";

contract OnePersonOneVoteMechanism is BaseAllocationMechanism {
    bytes32 private constant OPOV_SLOT = keccak256("tam.mechanism.1p1v.storage.v1");

    struct OPOV {
        mapping(address => bool) registered;
        uint256 registeredCount;
        mapping(uint256 => mapping(address => bool)) voted;
        mapping(uint256 => uint256) forVotes;
        mapping(uint256 => uint256) againstVotes;
        uint256 totalForVotesSucceeded;
        uint256 quorumNum;
        uint256 quorumDen;
    }

    function _s() private pure returns (OPOV storage s) {
        bytes32 slot = OPOV_SLOT;
        assembly { s.slot := slot }
    }

    function setQuorum(uint256 num, uint256 den) external {
        require(msg.sender == address(_tokenizedAllocation().owner()), "!owner");
        require(den > 0 && num <= den, "bad quorum");
        OPOV storage s = _s();
        s.quorumNum = num; s.quorumDen = den;
    }
}

Step 1 — Registration

function _beforeSignupHook(address user) internal override returns (bool ok) {
    OPOV storage s = _s();
    if (!s.registered[user]) { s.registered[user] = true; s.registeredCount += 1; }
    return true;
}

function _getVotingPowerHook(address user, uint256) internal view override returns (uint256) {
    return _tokenizedAllocation().votingPower(user) == 0 ? 1 : 0;
}

Step 2 — Proposal Policy

function _beforeProposeHook(address proposer) internal view override returns (bool) {
    address mgmt = _tokenizedAllocation().management();
    address keep = _tokenizedAllocation().keeper();
    return (proposer == mgmt || proposer == keep);
}

Step 3 — Voting Semantics

enum VoteType { Against, For, Abstain }

function _processVoteHook(
    uint256 pid,
    address voter,
    VoteType choice,
    uint256 weight,
    uint256 oldPower
) internal override returns (uint256 newPower) {
    OPOV storage s = _s();
    require(!s.voted[pid][voter], "already voted");
    require(weight == 1, "1p1v: weight must be 1");
    s.voted[pid][voter] = true;
    if (choice == VoteType.For)       s.forVotes[pid]     += 1;
    else if (choice == VoteType.Against) s.againstVotes[pid] += 1;
    require(oldPower >= 1, "insufficient power");
    return oldPower - 1;
}

Steps 4–7 — Quorum, Finalization, Conversion, Redemption

function _hasQuorumHook(uint256 pid) internal view override returns (bool) {
    OPOV storage s = _s();
    uint256 minVotes = (s.registeredCount * s.quorumNum) / s.quorumDen;
    return s.forVotes[pid] >= minVotes && s.forVotes[pid] > s.againstVotes[pid];
}

function _beforeFinalizeVoteTallyHook() internal override returns (bool) {
    OPOV storage s = _s();
    s.totalForVotesSucceeded = 0;
    uint256 n = _getProposalCount();
    for (uint256 pid = 1; pid <= n; pid++) {
        if (_hasQuorumHook(pid)) { s.totalForVotesSucceeded += s.forVotes[pid]; }
    }
    return true;
}

function _convertVotesToShares(uint256 pid) internal view override returns (uint256) {
    OPOV storage s = _s();
    require(s.totalForVotesSucceeded > 0, "no passing proposals");
    uint256 assets = (_tokenizedAllocation().totalAssets() * s.forVotes[pid])
                     / s.totalForVotesSucceeded;
    return assets;
}

function _availableWithdrawLimit(address) internal view override returns (uint256) {
    (uint48 start, uint48 grace) = (_tokenizedAllocation().globalRedemptionStart(),
                                    _tokenizedAllocation().gracePeriod());
    uint256 nowTs = block.timestamp;
    if (nowTs < start || nowTs > start + grace) return 0;
    return type(uint256).max;
}

function _requestCustomDistributionHook(address, uint256)
    internal override returns (bool handled, uint256 assetsTransferred)
{
    return (false, 0);
}

Foundry Checklist (PR-sized Tests)

  • Registration remains idempotent; votingPower(user) is 1 after first signup.
  • Proposal creation restricted to keeper/management (or relax via hook edits).
  • Voting enforces single vote per proposal; tallies update once.
  • Finalization snapshots totalForVotesSucceeded.
  • Queueing mints shares ≤ budget; conversion uses floor rounding.
  • Redemption window enforced; share transfers blocked outside window.
  • Post-grace dust recoverable via sweep.

Operational Notes & Extensions

Open proposing. Return true in _beforeProposeHook to let anyone propose; rely solely on quorum.

Fixed-grant variant. Use a fixed grantShares per passing proposal, tracking remaining budget at finalization.

Access-controlled queuing. Enforce roles in _requestCustomDistributionHook if governance demands it, reporting assetsTransferred for accounting.

BaseStrategy: TokenizedStrategy Adapter

Author: Golem Foundation • Source: TokenizedStrategy reference implementation. Purpose: Provide a minimal abstract contract that inherits all TokenizedStrategy plumbing while delegating strategy-specific logic to three hooks: _deployFunds, _freeFunds, and _harvestAndReport.

Immutable wiring. The constructor pins the underlying asset, management/keeper/emergency roles, donation address, and the shared TokenizedStrategy implementation address (TOKENIZED_STRATEGY_ADDRESS). Calls that miss locally defined functions fall through the fallback and delegatecall into the implementation, meaning your strategy contract can safely define custom storage without collisions.

address public immutable TOKENIZED_STRATEGY_ADDRESS;
IERC20 internal immutable asset;
ITokenizedStrategy internal immutable TokenizedStrategy;

modifier onlySelf() {
    _onlySelf();
    _;
}

constructor(
    address _asset,
    string memory _name,
    address _management,
    address _keeper,
    address _emergencyAdmin,
    address _donationAddress,
    bool _enableBurning,
    address _tokenizedStrategyAddress
) {
    TOKENIZED_STRATEGY_ADDRESS = _tokenizedStrategyAddress;
    asset = IERC20(_asset);
    TokenizedStrategy = ITokenizedStrategy(address(this));
    // delegatecall init storage via _delegateCall(...)
}

Lifecycle hooks. Implement the trio below in your concrete strategy:

  • _deployFunds: Move freshly deposited assets into the yield source; be mindful of dust thresholds and sandwich protection.
  • _freeFunds: Free liquidity for withdrawals; revert on illiquidity when losses would exceed maxLoss.
  • _harvestAndReport: Harvest external rewards, convert to base asset, and return the canonical total balance; this drives profit/loss accounting.

Optional overrides. Strategies can define availableDepositLimit, availableWithdrawLimit, _tend, _tendTrigger, and _emergencyWithdraw for nuanced behavior. Externally visible wrappers such as deployFunds, freeFunds, and harvestAndReport are onlySelf gated to guarantee delegatecall context.

BaseYieldSkimmingHealthCheck

Author: Yearn → Golem Foundation adaptation. Wraps BaseStrategy with guardrails that ensure harvestAndReport never records out-of-band profits or losses during yield skimming.

bool public doHealthCheck = true;
uint16 private _profitLimitRatio = uint16(MAX_BPS); // default 100%
uint16 private _lossLimitRatio;                     // default 0

function harvestAndReport()
    external
    override
    onlySelf
    returns (uint256 _totalAssets)
{
    _totalAssets = super.harvestAndReport();
    if (doHealthCheck) {
        _executeHealthCheck(_totalAssets);
    }
}

function _executeHealthCheck(uint256 _totalAssets) internal virtual {
    // Child implementations compare _totalAssets vs last report,
    // revert when profit/loss breaches configured basis-point bands.
}

Management knobs. Use setProfitLimitRatio / setLossLimitRatio (bps) to bound acceptable variance. Toggle the entire check via setDoHealthCheck; the next passing report re-enables it automatically.

Rate tracking. Yield-scaling strategies often rely on wrapper exchange rates. Override getCurrentRateRay and _executeHealthCheck to compare current vs previous rate, reverting if delta exceeds tolerance.

DebtManagementLib: Multistrategy Rebalancer

Purpose: Extract Yearn V3 vault rebalancing logic into a standalone Solidity library. Keeps multistrategy vault contracts below size limits while handling exact accounting for idle vs deployed capital, max loss tolerances, and shutdown flows.

function updateDebt(
    mapping(address => StrategyParams) storage strategies,
    uint256 totalIdle,
    uint256 totalDebt,
    address strategy,
    uint256 targetDebt,
    uint256 maxLoss,
    uint256 minimumTotalIdle,
    address asset,
    bool isShutdown
) external returns (UpdateDebtResult memory result);

Key behaviours.

  • Increase debt: Move idle assets into strategies respecting maxDeposit and maxDebt, never breaching minimumTotalIdle.
  • Reduce debt: Withdraw from strategies according to maxRedeem; compute realized loss and revert if it exceeds maxLoss bps.
  • Shutdown override: When isShutdown is true, forces targetDebt = 0 to unwind strategies.

Usage. After calling updateDebt, update vault storage with the returned newDebt, newTotalIdle, and newTotalDebt.

ERC20SafeApproveLib

Problem: Legacy tokens (USDT, BNB, etc.) implement non-standard approve that either return no bool or silently fail. This library wraps approvals in a low-level call that validates both the status flag and returned data when present.

function safeApprove(address token, address spender, uint256 amount) external {
    (bool success, bytes memory data) =
        token.call(abi.encodeWithSelector(IERC20.approve.selector, spender, amount));
    if (!success || (data.length > 0 && !abi.decode(data, (bool)))) {
        revert ApprovalFailed();
    }
}

Integration tips. Always reset allowance to zero before setting a new value when interacting with non-standard tokens. Use this helper from strategy constructors or pipeline steps that refresh allowances for routers, wrappers, or bridges.

MultistrategyLockedVault

Goal: Extend the multistrategy vault with a custody-based rage quit flow that prevents cooldown bypass attacks and adds a two-step governance delay for cooldown changes.

Custody Lifecycle

  • Initiate: initiateRageQuit(shares) locks exact shares; they become non-transferable until cooldown ends.
  • Cooldown: Track via custodyInfo[user]; maxWithdraw/maxRedeem return zero unless custody is active and unlocked.
  • Withdraw: _processCustodyWithdrawal enforces share caps per custody and clears state when fully withdrawn.

Transfer guard. Overrides ERC20 _transfer to block moving locked shares. Query helpers: getTransferableShares and getRageQuitableShares.

Two-Step Cooldown Governance

  1. Propose: Regen governance calls proposeRageQuitCooldownPeriodChange (must be 1–30 days). Starts 14-day grace period.
  2. Grace: Users can rage quit under old duration; pending value stored in pendingRageQuitCooldownPeriod.
  3. Finalize: Anyone may call finalizeRageQuitCooldownPeriodChange after the grace period. Governance may cancel mid-grace.

Example. With a 7-day current cooldown, governance proposes 14 days. Users initiating rage quit within the grace window complete in 7 days; after finalization, new rage quits adopt 14-day locks.

RegenEarningPowerCalculator

Author: Golem Foundation • Purpose: Gate staking rewards with configurable allow/block sets while keeping the earning-power calculation linear.

  • Access modes: NONE (permissionless), ALLOWSET (must appear in external allowset), BLOCKSET (everyone except listed addresses).
  • Earning power cap: Linear in staked amount but capped to type(uint96).max to fit downstream accounting.
  • Bump logic: Front-ends call getNewEarningPower to detect whether deposits should be rebalanced when access or stake changes.
function _hasAccess(address staker) internal view returns (bool) {
    if (accessMode == AccessMode.NONE) return true;
    if (accessMode == AccessMode.ALLOWSET) return allowset.contains(staker);
    if (accessMode == AccessMode.BLOCKSET) return !blockset.contains(staker);
    return false;
}

function getEarningPower(uint256 stakedAmount, address staker, address)
    external
    view
    override
    returns (uint256 earningPower)
{
    if (!_hasAccess(staker)) return 0;
    return stakedAmount > type(uint96).max ? type(uint96).max : stakedAmount;
}

function getNewEarningPower(
    uint256 stakedAmount,
    address staker,
    address,
    uint256 oldEarningPower
) external view override returns (uint256 newPower, bool qualifies) {
    newPower = _hasAccess(staker)
        ? (stakedAmount > type(uint96).max ? type(uint96).max : stakedAmount)
        : 0;
    qualifies = newPower != oldEarningPower;
}

Governance knobs. Owners can hot-swap allow/block sets and switch the access mode via setAccessMode; existing stakes require a bump to take effect.

RegenStaker with Delegation Surrogates

Pattern: Users stake governance tokens while delegating votes through lightweight surrogate contracts deployed deterministically per delegatee.

  • Surrogate lifecycle: First delegation to an address deploys a DelegationSurrogate via CREATE2 (~300k gas); subsequent delegations reuse it (~5k gas).
  • Custody: Tokens live in the surrogate, which immediately delegates to the target address; the main staker contract retains withdrawal authority.
  • Predictability: Front-ends can precompute surrogate addresses to surface gas estimates or pre-fund allowance flows.
function _fetchOrDeploySurrogate(address delegatee)
    internal
    override
    returns (DelegationSurrogate surrogate)
{
    surrogate = _surrogates[delegatee];
    if (address(surrogate) != address(0)) return surrogate;

    bytes32 salt = keccak256(abi.encode(delegatee));
    surrogate = new DelegationSurrogate{salt: salt}(VOTING_TOKEN, delegatee);
    _surrogates[delegatee] = surrogate;

    emit SurrogateDeployed(delegatee, address(surrogate));
    return surrogate;
}

API helpers. Use predictSurrogateAddress to derive CREATE2 outcomes off-chain and getDelegateeFromSurrogate to audit live delegates.

YieldSkimmingTokenizedStrategy

Goal: Maintain solvency-aware accounting when underlying assets appreciate via exchange rates (e.g., yield-bearing vault tokens) and skim the profit buffer (“dragon router”) on each report.

  • Value debt ledgers: Track user debt and dragon router debt separately in value terms (1 share = 1 unit of asset value).
  • Profit capture: Appreciation mints donation shares to the dragon router; losses burn dragon shares before touching user balances.
  • Solvency gates: Deposits/withdrawals revert when the vault is insolvent; dragon transfers trigger debt rebalancing.
function report()
    public
    override(TokenizedStrategy)
    nonReentrant
    onlyKeepers
    returns (uint256 profit, uint256 loss)
{
    StrategyData storage S = _strategyStorage();
    YieldSkimmingStorage storage YS = _strategyYieldSkimmingStorage();

    uint256 currentRate = _currentRateRay();
    uint256 totalValue = _totalAssetsValue(S, currentRate);
    uint256 totalDebt = YS.totalUserDebtInAssetValue + YS.dragonRouterDebtInAssetValue;

    if (totalValue >= totalDebt) {
        profit = totalValue - totalDebt;
        _mintDragonDonation(S, YS, profit, currentRate);
    } else {
        uint256 lossValue = totalDebt - totalValue;
        loss = _handleDragonLossProtection(S, YS, lossValue, currentRate);
    }

    YS.lastReportedRate = currentRate;
    emit Harvest(msg.sender, currentRate);
}

Developer tips: Override _currentRateRay to surface protocol-specific rates and ensure reports run frequently enough for health-check thresholds to remain meaningful.