Skip to content

docs(vault): E3 sign-off — PhaseE fork tests ✅ + DeployAll dry-run ✅ (MOG-504)#9

Merged
moggedcapital merged 40 commits into
mainfrom
chore/repo-hardening
Jun 4, 2026
Merged

docs(vault): E3 sign-off — PhaseE fork tests ✅ + DeployAll dry-run ✅ (MOG-504)#9
moggedcapital merged 40 commits into
mainfrom
chore/repo-hardening

Conversation

@moggedcapital
Copy link
Copy Markdown
Member

Summary

  • All 3 PhaseE fork tests pass on Base mainnet fork
  • DeployAll.s.sol dry-run simulation clean — no reverts, all phases deploy, Morpho LLTV 77% confirmed enabled, ownership transferred
  • Sign-off doc committed to docs/vault/deploy-dryrun.md

Fork test results

[PASS] test_fork_agentRegistrationAndFeeReceipt() (gas: 139298)
[PASS] test_fork_vaultRateMonotone() (gas: 427794)
[PASS] test_fork_wstDIEMFeeRouterRoundtrip() (gas: 445252)
3 passed; 0 failed

Dry-run output

Contract Simulated address
InferenceVault (wstDIEM) 0x69F5b6A3588317C470c840335b3a86d14A218AA8
Curve DIEM/wstDIEM pool 0x12380121477335b9F91CE413850DBedb7CDB9fdD
FeeRouter 0xE2E092957369AE866CC0bF073E8d5d20b2bE0006
Router 0x208f354685163088f4d3A48D8E898DF037baEe58
AgentTGERegistry 0x8f129667BC02C9aaec1F419cd0D2d93eacCEF90A
SurplusStakingWrapper 0x9eBDF5696A2f3138d07e4135aB11010B095CCC24
Morpho oracle 0x4B510C8829378e973b3a10831140964148AFDc66

Gas estimate: ~16.3M gas / 0.00018 ETH

Before live deploy (MOG-501)

  • Confirm TREASURY_ADDRESS
  • Create 3-of-5 governance Safe (MOG-489) → set SAFE_MULTISIG_ADDRESS
  • Re-run dry-run with real addresses and get second approval
  • Fund deployer wallet (needs ≥ 0.001 ETH)

🤖 Generated with Claude Code

moggedcapital and others added 5 commits March 30, 2026 19:33
…emplates

- Add Foundry CI pipeline (forge fmt --check, forge build, forge test)
- Add PR workflow with concurrency control
- Add branch ruleset on main (1 approval, code owner review, linear history)
- Add SECURITY.md with audit history and private disclosure process
- Add CONTRIBUTING.md with setup and PR requirements
- Add CLAUDE.md for AI agent guidelines
- Add CODEOWNERS, dependabot, issue/PR templates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes CI format check so the hardening PR can pass QA.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
File was ILiquidLPLocker.sol (uppercase LP) but all imports reference
ILiquidLpLocker.sol (lowercase p). macOS is case-insensitive so this
built locally but failed on Linux CI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
qa.yml: keep composite action, add BASE_RPC_URL from main.
SECURITY.md: keep specific email addresses and audit context from main.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…simulation clean

Fork tests: 3/3 pass on Base mainnet fork (test_fork_agentRegistrationAndFeeReceipt,
test_fork_vaultRateMonotone, test_fork_wstDIEMFeeRouterRoundtrip).

Dry-run: DeployAll.s.sol simulation complete on Base fork. All phases pass,
Morpho LLTV 77% confirmed enabled, ownership transferred to Safe. Gas estimate:
~16.3M gas / 0.00018 ETH at current base fee.

Addresses marked [TBD] for TREASURY_ADDRESS and SAFE_MULTISIG_ADDRESS (MOG-489 Safe
not yet created). Re-run dry-run with real addresses before --broadcast.

Closes MOG-504. Unblocks MOG-501 (WP-14 live deploy).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@moggedcapital moggedcapital requested a review from mugrebot as a code owner June 1, 2026 19:06
@linear
Copy link
Copy Markdown

linear Bot commented Jun 1, 2026

MOG-504

moggedcapital and others added 23 commits June 1, 2026 12:11
… needed

Both TREASURY_ADDRESS and SAFE_MULTISIG_ADDRESS confirmed as the Liquid Protocol
governance Safe (0x872c561f...280c6c). Deployer (0x49f5b131) has ~0 ETH on Base
— needs ≥ 0.001 ETH funded before --broadcast.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tract addresses

New deployer: 0xeEd4c6fd...8698 (key in 1Password base vault).
Re-ran dry-run with nonce=0 — contract addresses updated to reflect
actual deployment from this wallet. Gas estimate: ~16.3M / 0.000176 ETH.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… on Base

Deployed 2026-06-01 via DeployAll.s.sol from 0xeEd4c6fd...8698.
All contracts live on Base mainnet (chain 8453):

  InferenceVault (wstDIEM): 0xd2069DB11f157C5d86b6ef2D36bAAd6411E14b63
  Curve DIEM/wstDIEM pool:  0x12380121477335b9F91CE413850DBedb7CDB9fdD
  FeeRouter:                0xc4845F25B84EA8970D622fbF4FF7d10a6Fb7829e
  Router:                   0x1C3709eCc560E3c5f529544ef36daA10E352f862
  AgentTGERegistry:         0x8Dc32dA92B89a0968BEc020924491FE94573bef2
  SurplusStakingWrapper:    0x93577aAA7469Ef62198680Bc006a45e9bd6292B3
  Morpho oracle:            0xE762e8011D453853638D1978398df8b1D383A2D9

Morpho wstDIEM/DIEM market created (LLTV 77%).
Ownership transferred to Safe: 0x872c561f...280c6c.

Closes MOG-501 (WP-14). Full broadcast in broadcast/DeployAll.s.sol/8453/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eployment

New contracts:
- InferenceProduct: USDC settlement layer for Venice inference capacity.
  Vault is the central sDIEM actor — no DIEM leaves. Buyers purchase
  time-bounded allocations against the vault's unified pool. buy() takes
  maxPriceUSDC slippage guard; expired allocations released by anyone.
- Per-model x402 pricing map (pricePerMilInByModel/pricePerMilOutByModel)
  for keeper to configure Surplus Intelligence and AntSeed price schedules.

FeeRouter: channel registry for inference marketplace integrations.
- addChannel(name, payoutWallet, platformFeeBps) — vault manager registers
  Surplus Intelligence, AntSeed, future platforms via Safe tx.
- receiveFromChannel(channelId, amount) — keeper routes x402 USDC settlement
  into the harvest queue, tagged by channel for analytics.
- Admin: setChannelActive, setChannelPayoutWallet, setChannelFee, getChannel.

Security fixes (from audit):
- InferenceProduct._computePrice: fix divide-before-multiply allowing
  sub-1e18 capacity to be purchased for free (critical).
- InferenceVault.maxWithdraw/maxRedeem: cap against idle DIEM balance only
  to satisfy EIP-4626 spec; previously overstated withdrawable assets (high).
- SurplusStakingWrapper.unstakeForUser: add minDiemOut slippage param;
  hardcoded 0 was sandwichable (high).
- InferenceVault.enableWithdrawals: restrict to onlyOwner.
- InferenceVault.cancelEnableWithdrawals: allow owner to abort pending timelock.

Router v7: dynamic wethIsCurrency0 immutable for V4 currency ordering.
v3 deployment: new deployer, all contracts wired via Safe batch, V4 pool
initialized with flipped ordering (wstDIEM=currency0, WETH=currency1).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds two new Morpho lending markets so wstDIEM holders can borrow liquid
assets without unwinding their staked inference position. Inference yield
continues accruing on deposited wstDIEM while it serves as collateral.

Oracles (src/vault/oracles/):
- WstDiemUsdcOracle: price() = vault.convertToAssets(1e18) * 1e6
  Assumes DIEM = $1 USDC (Venice's intrinsic value floor). LLTV 62.5%
  absorbs any DIEM/USD deviation until a live on-chain price feed exists.
- WstDiemWethOracle: price() = vault.convertToAssets(1e18) * 1e26 / ethUsdPrice
  Chains vault rate with Chainlink ETH/USD (Base: 0x71041...Bb70, 8 dec).
  Includes staleness check (1h threshold, Chainlink heartbeat ~20 min).

Markets (both 62.5% LLTV, AdaptiveCurveIRM):
- wstDIEM collateral / USDC loan — borrow stables against inference capacity
- wstDIEM collateral / WETH loan — borrow ETH against inference capacity

Deploy: script/vault/DeployMorphoMarketsV2.s.sol (DEPLOYER_PK env var)
Tests: 10 new fork tests covering oracle pricing, rate-sensitivity, and
       Morpho market creation; 135/135 vault tests passing.

Also: fix stale test_depositWETH_revertWithPoolNotSet (guard was
intentionally removed from depositWETH in a prior session).

New deployer v3: 0x66205fdA77114A5357E7bDcac6dDb356cfF0063b (1P: jq4xbffwt3m6nrfn6ompar6nzm)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n, events

Security fixes (8 audit findings):
- InferenceVault: add seed deposit to DeployAll (prevents first-depositor inflation
  attack; deployer burns 1 DIEM to address(1) before ownership transfer)
- FeeRouter.receiveFromChannel: restrict caller to channel.payoutWallet or owner;
  previous open-call allowed analytics corruption and keeper griefing
- WstDiemWethOracle: add zero-address checks in constructor

Keeper / operational:
- FeeRouter: add keeper address + onlyOwnerOrKeeper modifier; harvest() and
  harvestVVV() now callable by keeper EOA without Safe signature
- FeeRouter: add settleAndHarvest(channelId, amount) — single-call entry for
  keeper to settle x402 USDC revenue and trigger harvest in one tx
- InferenceVault: add keeper address, setKeeper(), and fundKeeperVVV() so Safe
  can stake VVV to keeper EOA enabling autonomous Venice API key minting
- FeeRouter: add setKeeper() admin function

Event coverage (all silent state changes now emit):
- InferenceVault: DIEMCredited(amount) — wstDIEM rate changes now indexable
- InferenceVault: KeeperUpdated, KeeperFunded
- FeeRouter: WstDIEMReceived, KeeperUpdated; _pendingWstDIEM tracker added
- FeeRouter: Harvested event now accumulates totalWstToVol across ALL CURVE_VOL
  paths (was only counting receivewstDIEM path — fix #6 from audit)
- FeeRouter: harvest() extracted to internal _harvest() shared with settleAndHarvest
- AgentTGERegistry: AgentRegistered, AgentTerminated, AgentDormant,
  FeeReceiptRecorded — contract had zero events making lifecycle un-indexable

LLTV note: DeployAll LLTV (77%) is correct for the DIEM market (which has DIEM
as both collateral backing and loan token). New USDC/WETH markets use 62.5%.
CLAUDE.md updated to clarify per-market LLTV rationale.

135/135 vault tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…r vault addr

DeployAll.s.sol now deploys the complete stack in one run:
- InferenceVault (with seed deposit → inflation attack protection)
- Curve DIEM/wstDIEM pool
- FeeRouter (with keeper, settleAndHarvest, channel registry)
- Router v7
- AgentTGERegistry + SurplusStakingWrapper + InferenceProduct
- Morpho wstDIEM/DIEM market  (38.5% LLTV, WstDIEMMorphoOracle)
- Morpho wstDIEM/USDC market  (62.5% LLTV, WstDiemUsdcOracle)
- Morpho wstDIEM/WETH market  (62.5% LLTV, WstDiemWethOracle + Chainlink)
- Ownership of all contracts transferred to Safe

LLTVs locked to what Morpho Blue actually has enabled on Base:
  DIEM=38.5% (matching existing mainnet market), USDC/WETH=62.5%

DeployMorphoMarketsV2.s.sol: vault address now via WSTDIEM_ADDRESS env var
so it can be run standalone against any deployed vault.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…able

Any non-zero seed provides the inflation attack protection needed with the +1
virtual buffer. 0.01 DIEM requires attacker to donate ~1e34 DIEM to steal
from a 1 DIEM depositor — economically infeasible.

Simulation verified: all 3 Morpho markets deploy, 0.000267 ETH gas on Base.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
V4 pool initialization now in DeployAll — no separate InitPools run needed:
- Detects currency ordering at deploy time (vault addr vs WETH addr)
- Computes correct sqrtPriceX96 branch for each ordering
- Calls router.setV4Pool(POOL_MANAGER) in same tx — Safe batch no longer needed

Morpho LLTV corrections (audit finding — previous ordering was backwards):
- wstDIEM/DIEM: 38.5% → 86%  (borrowing your own underlying, oracle is exact rate)
- wstDIEM/USDC: 62.5%         (single oracle risk: DIEM=$1, conservative for launch)
- wstDIEM/WETH: 62.5%         (dual oracle risk: DIEM=$1 + Chainlink, upgrade later)
All three LLTVs confirmed enabled on Morpho Blue Base mainnet.

Simulation verified: V4 tick 75981 @ $1993 ETH, WETH=currency0. 135/135 tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ONCHAIN EXECUTION COMPLETE — all 11 contracts deployed and verified on Base.

Contract addresses (v4, deployer v3):
  InferenceVault (wstDIEM): 0x4751BA2b09374C1929FC01734a166e3c8cd75810
  FeeRouter:                 0x21fe048B10dC9bED2Ee0Ae76724C627CA7F35F61
  Router v8:                 0x6f5FF03a91cb1703B7CB8d85572f990bcB04273D
  AgentTGERegistry:          0x49be7fE8D661b892AC0461818a5C714574e83998
  SurplusStakingWrapper:     0xB0f9c45dAacD89F0d90cbE0E65d0dA20fa1ac415
  InferenceProduct:          0x9b7d8B23cb223F75F5F1Ead25f12205940960F62
  Curve DIEM/wstDIEM:        0x39A4b4779C71E1A18d500627639682c9583Ee86f
  Morpho DIEM oracle (86%):  0xbaEc9cCcBa9884D403dBcEe15455E28781f1fd72
  Morpho USDC oracle (62.5%):0x556B3B1a0de988407EF39e4a775d33280C06EEeb
  Morpho WETH oracle (62.5%):0x25AcE9baFad49f0e7239E4b469edEEDc97d176fd

V4 wstDIEM/WETH pool: tick 75981, WETH=currency0 (0x4200 < 0x4751)
All ownership transferred to Safe: 0x872c561f699B42977c093F0eD8b4C9a431280c6c

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three Safe batch scripts for initial capital deployment. DO NOT BROADCAST
until reviewed — see script headers for pre-requisites.

SafeWrapETH.s.sol (run first if Safe holds ETH, not WETH):
  - 1 Safe tx: WETH.deposit{value: 2 ETH} — wraps Safe's ETH to WETH

SafeSeedCapital.s.sol (4 Safe txns):
  - Tx1: DIEM.approve(vault, 2.74 DIEM)
  - Tx2: vault.deposit(2.74 DIEM, Safe)   -> Safe gets ~2.74 wstDIEM
  - Tx3: WETH.approve(Router, 1 WETH)
  - Tx4: Router.depositWETH(1 WETH, Safe) -> Safe gets ~1990 wstDIEM
  Total: ~1993 wstDIEM in Safe; 1 WETH reserved for V4 LP

SafeAddV4LP.s.sol (3 Safe txns, run after SafeSeedCapital):
  - Deploys LiquidityHelper (intermediate unlock callback contract)
  - Tx1: transfer 1 WETH from Safe to helper
  - Tx2: transfer 2000 wstDIEM from Safe to helper (excess returned)
  - Tx3: helper.addLiquidity(3e18) via PoolManager.unlock() pattern
  - Position: WETH/wstDIEM tick 62160-92100 (~$500-$10000 ETH range)
  - Any unused tokens returned to Safe after unlock

Run pattern (all scripts):
  SAFE_SK1=<bytes32> SAFE_SK2=<bytes32> EXECUTOR_PK=<uint256> \
  forge script script/vault/SafeXxx.s.sol --rpc-url $BASE_RPC_URL [--broadcast]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l WETH

SafeSeedCapital: removed WETH routing steps — only deposits 2.74 DIEM.
  WETH stays in Safe; 2 txns (approve + deposit), not 4.

SafeAddV4LP: corrected budgets using actual LP math:
  At tick range 62160-92100, tick 75981 (~$1993/ETH):
    L = 2.74e18 / (sqrt(1993) - sqrt(500)) = 1.23e17 liquidity units
    WETH needed = L * (1/sqrt(1993) - 1/sqrt(10000)) ~= 0.00153 WETH (~$3.04)
  WSTDIEM_BUDGET: 2000e18 -> 2.74e18 (exact Safe balance after seed)
  WETH_BUDGET:    1.0e18  -> 0.002e18 (buffer, excess returned to Safe)
  LIQUIDITY:      3e18    -> 1.23e17
  Remaining ~1.998 WETH stays in Safe after LP.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SafeSeedCapital: 2.74 DIEM deposited from Safe into vault
  Safe nonce 51->53, Safe receives ~2.74 wstDIEM

SafeAddV4LP: wstDIEM/WETH V4 position seeded
  LiquidityHelper deploys, unlocks PoolManager, settles:
    WETH consumed:    1,511,759,434,472,863  (~0.00151 WETH, ~$3.01)
    wstDIEM consumed: 2,718,050,846,991,192,144 (~2.718 wstDIEM, ~$2.72)
  Position: tick 62160-92100 (~$500-$10000 ETH), L=1.22e17
  Remaining ~1.998 WETH returned to Safe

Fix: BalanceDelta decode — modifyLiquidity returns packed int256
  (upper 128 bits = amount0, lower 128 bits = amount1), not 3 separate values.
Fix: LIQUIDITY 1.23e17->1.22e17 to fit within 2.74e18 wstDIEM budget.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… on FeeRouter+vault

6 Safe txns (nonce 56->62):
  Tx1: WETH.approve(V3Router, 0.01 WETH)
  Tx2: V3Router WETH->VVV swap (Safe receives VVV)
  Tx3: VVV.approve(vault, 1 VVV)
  Tx4: vault.fundKeeperVVV(0x32fD...) -> keeper gets sVVV -> can mint Venice API key
  Tx5: FeeRouter.setKeeper(0x32fD...) -> keeper can call harvest/settleAndHarvest
  Tx6: vault.setKeeper(0x32fD...)

Keeper EOA: 0x32fDdfB0eeC6c638d5C8b7cabF3bE9065478e90E (1P: zfk52wt5di6kn3j76o6o7kngi4)
Next: keeper self-mints Venice API key, registers on AntSeed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comprehensive documentation for the wstDIEM Liquid Inference Vault v4.

SYSTEM_OVERVIEW.md (192 lines):
- Full architecture: InferenceVault (totalAssets accounting), FeeRouter
  (settleAndHarvest/harvest/harvestVVV — all onlyOwnerOrKeeper), Router v8
  (depositWETH/VVV, exitToWETH via V4 unlockCallback)
- All v4 deployed addresses (2026-06-01)
- V4 pool: WETH=currency0 (0x4200 < 0x4751), wstDIEM=currency1
- Three Morpho markets: wstDIEM/DIEM 86%, USDC 62.5%, WETH 62.5%
- ASCII revenue flow: AntSeed/Surplus -> keeper -> FeeRouter -> vault
- All yield sources and governance/access control

KEEPER_RUNBOOK.md (225 lines):
- settleAndHarvest walkthrough and keeper vs Safe access boundaries
- Railway monitoring and restart procedures
- Venice API key rotation (3-step generate_web3_key curl flow)
- settleAndHarvest failure table (not-keeper, allowance, balance, channel)
- Split receiveFromChannel + harvest pattern for thin liquidity
- Hetzner VPS migration with systemd service config

DEPOSIT_GUIDE.md (258 lines):
- Three deposit paths (DIEM direct, WETH via Router, VVV via Router)
- Deposit fee schedule (10bps / 50bps at 5M TVL threshold)
- 14-day withdrawal timelock + 24h DIEM unstake cooldown
- Two exit paths (V4 exitToWETH 0.3%, Curve StableSwap ~0.04%)
- Morpho collateral borrowing (all three markets, supplyCollateral+borrow)
- Security properties (no upgrade proxy, EIP-4626 compliant, inflation guard)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Safe tx: FeeRouter.addChannel("SurplusIntelligence", 0x32fD..., 0)
Channel 1 active, payoutWallet=keeper, platformFeeBps=0.

FeeRouter now has 2 active inference channels:
  Channel 0: AntSeed     (9.98 USDC earned, settled to vault)
  Channel 1: SurplusIntelligence (ready for x402 USDC settlement)

Both channels use the keeper EOA (0x32fD...) as payoutWallet.
The Railway settle loop calls settleAndHarvest(channelId, amount)
for whichever channel receives USDC.

To activate Surplus Intelligence:
  1. Sign in at surplusintelligence.ai/sell with keeper wallet
  2. Add Venice offer (venice_api_key, base_url, models, pricing)
  3. Surplus routes buyers -> keeper receives USDC x402
  4. Railway settle loop auto-harvests to vault every 2min

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Core architectural fix: deposits now route to the keeper's Venice account
directly, making keeper.sDIEM == vault's total user deposits.

IDIEM interface additions:
  stakeFor(address to, uint256 amount)     -- vault -> keeper Venice account
  initiateUnstakeFor(address who, uint256) -- vault controls keeper unstaking
  unstakeFor(address who, uint256)         -- vault completes keeper unstaking

InferenceVault changes:
  totalAssets(): now sums stakedInfos(vault) + stakedInfos(keeper)
    -- no separate tracking variable, DIEM contract is source of truth
    -- vault own sDIEM and keeper sDIEM are one unified pool
  _deposit(): when keeper set, calls stakeFor(keeper, assets) so every
    user deposit immediately increases keeper's Venice inference budget
  creditDIEM(): when keeper set, credits yield to keeper account too
    so inference revenue compounds into the same budget
  initiateUnstake(): uses initiateUnstakeFor(keeper) when keeper set
  completeUnstake(): uses unstakeFor(keeper) when keeper set

Flow:
  User deposits DIEM -> vault.stakeFor(keeper, DIEM)
  -> stakedInfos[keeper] grows -> keeper's Venice API key budget grows
  -> keeper serves inference on AntSeed/Surplus
  -> USDC -> FeeRouter -> creditDIEM -> stakeFor(keeper, DIEM)
  -> budget compounds

Venice note: EIP-1271 not yet supported (ecrecover only), so vault
  cannot be its own Venice account. keeper EOA holds the API key.
  File Venice feature request for EIP-1271 to collapse keeper and vault.

135/135 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…meta-wrapper

## Core changes

**Oracle inflation fix (critical)**
- Introduced `pendingWithdrawalDiem` liability subtracted from `totalAssets()`.
  Without it, burning shares at requestRedeem left the earmarked DIEM in
  Venice's `unstakingAmount`, spiking `convertToAssets()` during the 24h
  cooldown window — exploitable via the Morpho collateral oracle.

**Async redemption redesign (Lido-inspired pull model)**
- `requestRedeem(shares, receiver)` — burns shares immediately, locks DIEM
  at current rate, returns a `requestId`. Rate stable from this point forward.
- `flush()` — permissionless after `minBatchOpenSecs` (default 1d) or when
  batch is full; initiates Venice unstake. Prevents single-user batch griefing.
- `settle()` — permissionless after Venice cooldown; not pausable so committed
  exits always complete.
- `claimRedeem(requestId)` — receiver pulls DIEM; anyone can trigger on their
  behalf. Not pausable. Replaces auto-push loop with gas-predictable pull.

**Security hardening**
- `Pausable`: pause blocks deposits/requests/flush; settle/claim always live.
- `ReentrancyGuard`: all external state-changing functions.
- `maxTotalStake`: Venice staking cap (0 = uncapped).

**ERC-1271 / Venice API key**
- `veniceSigner` separate from `owner()` — Privy-managed hot key signs Venice
  challenges; rotate without Safe transaction via `setVeniceSigner()`.

**wstDIEM meta-wrapper for tokenized inference (Path A)**
- `creditWstDIEM(amount, recipient)` — inference source reinvests their cut of
  revenue as a wstDIEM position. No entry fee; shares calculated pre-transfer
  to avoid same order-of-operations rate inflation.
- `isInferenceToken` registry + `inferenceTokenList()` — bounded to 16 sources.
- `IInferenceToken` interface: standard for plugging new inference sources in.
- `IInferenceVault` updated to current API.

**Scripts + tests**
- `DeployAll.s.sol`, `SafeBatchV3.s.sol` updated for new constructor signature
  (`veniceSigner` param) and `setVenueAdapter` replacing `setFeeRouter`.
- 182 tests passing across all suites (unit + fork + integration).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…timestamp), not unstakingAmount

Real Diem.sol (verified on Sourcify, Base 0xF4d97F2...):
  struct StakedInfo { amountStaked, coolDownEnd, coolDownAmount }

Our IDIEM interface had (stakedAmount, unstakingAmount, cooldownEnd) which
maps to (amount, TIMESTAMP, amount) — treating coolDownEnd (a Unix timestamp
~1.748e9) as the unstaking DIEM amount. During flush→settle the oracle would
read totalAssets() inflated by ~1.748 billion DIEM, enabling Morpho
over-borrow against artificially priced wstDIEM collateral.

Fix:
- IDIEM.stakedInfos now returns (amountStaked, coolDownEnd, coolDownAmount)
- totalAssets() and _deposit cap-check use (amountStaked,, coolDownAmount)
- MockDIEM.stakedInfos return order updated to match real contract
- Test destructuring at position 1 updated to position 2 for unstaking amount

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ress)

Addresses two gaps identified by comparison with Lido's WithdrawalQueueERC721:

**minRedeemShares (default 0.001 wstDIEM)**
Lido enforces MIN_STETH_WITHDRAWAL_AMOUNT = 100 wei to prevent dust requests
from consuming slots in the 50-user batch cap. Without a floor, a griefer
could fill a batch with 50 requests for 1 wei each, blocking real withdrawers.
Owner-settable via setMinRedeemShares(). Applied at requestRedeem entry.

**getRedeemRequests(address) → uint256[]**
Lido provides getWithdrawalRequests(owner) so frontends can display pending
withdrawals without requiring event indexing. We track owner → requestId[]
in _ownerRequests at requestRedeem time. Keyed by receiver (not msg.sender)
to handle the case where shares are redeemed on behalf of a different address.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… Safe

Creates a new Morpho Blue lending market (wstDIEM collateral / DIEM loan)
at 77% LLTV — the nearest Morpho-whitelisted value to the 75% target
(70% and 75% are not enabled on Base; 77% is, giving 4.35x max leverage
vs 1.63x at the existing 38.5% market). Reuses the deployed wstDIEM/DIEM
oracle (0xE762e8…a2d9) and AdaptiveCurveIRM. Market ID computed and
documented in the file header:
  0x96af141c5ac70610ee0c4d8b5cf72205a8358e888407e0ba45c1cb21f9449f1e

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SafeBatchV3.s.sol: VAULT 0x3394→0x4751, FEEROUTER→v4, ROUTER→v8
- InitPools.s.sol: WSTDIEM→v4, CURVE_DIEM_WSTDIEM→v4
- ComputePoolId.s.sol: wstDIEM currency1→v4
- CLAUDE.md: add full v4 address table, note new InferenceVault needs fresh deploy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
moggedcapital and others added 12 commits June 2, 2026 14:27
…V Morpho market script

## Venue adapters (IInferenceToken)

BaseInferenceAdapter: abstract base implementing IInferenceToken. Accepts USDC
via receiveSettlement (authorized) or venue-specific paths. routeYield() swaps
USDC→WETH→DIEM (Uniswap V3 multi-hop) then splits yield:
  holderDiem  → vault.creditDIEM()    (raises wstDIEM rate for all holders)
  operatorDiem → vault.creditWstDIEM() (compounds adapter's wstDIEM position)

AntSeedAdapter: AntSeed marketplace. Notes AntSeed #627 display issue.
SurplusAdapter: Surplus Intelligence marketplace.
X402Adapter:    HTTP 402 micropayments — adds permissionless recordX402Settlement().

MockUSDC, MockSwapRouter: test infrastructure (1 USDC-6dec → 1 DIEM-18dec rate).
25/25 adapter tests pass.

## Morpho 77% LLTV market script

CreateMorphoMarket75.s.sol: Safe batch script to create Morpho Blue market
  loanToken=DIEM, collateral=wstDIEM, oracle=0xE762e8... (existing),
  irm=AdaptiveCurveIRM, lltv=77% (750bps not whitelisted; 77% is nearest enabled).
  4.35x leverage at 77% vs 1.63x at current 38.5% market.

WST_DIEM constant marked TODO — update to new vault address after DeployAll.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oop for wstDIEM

At 77% LLTV (nearest enabled on Base): 4.35x leverage. At 80% utilization and
12.8% native APY: ~27% leveraged APY net of 8% borrow cost.

## loopDeposit(diemAmount, targetLTV, minWstOut)
- Pulls equity DIEM from caller
- Flash-borrows flashAmount = equity / (1 - LTV) - equity (Morpho free flash loan)
- Deposits total DIEM into vault → wstDIEM
- Supplies ALL wstDIEM as Morpho collateral on behalf of caller
- Borrows flashAmount DIEM back to repay flash loan
- Caller's net: Morpho position with totalWst collateral and flashAmount debt

## unloopDeposit(wstAmount, borrowRepay, minDiemOut)
- Flash-borrows borrowRepay DIEM → repays caller's Morpho debt
- Withdraws caller's wstDIEM collateral
- Swaps wstDIEM→DIEM via Curve StableSwap (synchronous exit; vault withdrawal
  queue is async so Curve is the only single-tx path)
- Net DIEM (Curve output - flash repayment) sent to caller

## Other changes
- Router constructor gains 5th param `_morpho` (pass address(0) for Base mainnet
  default 0xBBBBBbbBBb...; inject MockMorpho in tests)
- ReentrancyGuard + IMorphoFlashLoanCallback added to Router
- onMorphoFlashLoan: NOT nonReentrant (OZ counter already locked by caller;
  msg.sender==morpho guard blocks unauthorized calls)
- LTV guard: targetLTV + 100bps < lltv (covers 50bps max deposit fee)
- DeployRouter.s.sol: updated to v4 vault address + 5-arg constructor

15/15 leverage unit tests pass. 226/226 total vault tests green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eld names

- CLAUDE.md: v4 live address table, stakedInfos field order note
- docs/vault/mainnet-addresses.md: v4 addresses, DEPRECATED v3 table
- docs/vault/SYSTEM_OVERVIEW.md: totalAssets formula — amountStaked + coolDownAmount
- docs/vault/KEEPER_RUNBOOK.md: correct env vars, withdrawal queue automation section,
  ERC-1271 facts (vault IS Venice account via isValidSignature)
- docs/vault/DEPOSIT_GUIDE.md: stakedInfos field names corrected
- docs/vault/wstdiem-economics.md: v4 vault address, ERC-1271 limitation removed
- docs/inference-vault-spec.md: DIEM mint contract resolved to 0x321b7ff7...
- DeployAll.s.sol: Router constructor updated to 5-arg (morpho param)
- Router.t.sol, RouterV4.t.sol, VaultStack.t.sol: Router 5-arg constructor

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d vault v4

Initiates 14-day timelock on old InferenceVault v4 (0x4751BA2b...).
enableWithdrawals() can be called 2026-07-01 03:32 UTC.

TX: 0x98ab72c6a006d5509f1cd17081b2f2cd054ebbd64e33d1c9d0a734029f5b72db

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…remove/collectFees

Replaces the single-use LiquidityHelper pattern from SafeAddV4LP.s.sol
that left 2.718 wstDIEM permanently locked in V4 (no removeLiquidity,
no allowOperator path).

LiquidityManager is a persistent contract (owned by Safe) that:
- addLiquidity(uint128): Safe pre-sends tokens, unlock -> modifyLiquidity(+delta) -> settle debts -> excess returned
- removeLiquidity(uint128): unlock -> modifyLiquidity(-delta) -> take tokens -> return to Safe
- collectFees(): delta=0 touch -> take accrued fees -> return to Safe
- grantOperator(address,bool): calls PoolManager.allowOperator so a successor manager can take over positions without losing funds

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… locked wstDIEM

The LP position owned by the old LiquidityHelper (0x7060d57e) cannot have
removeLiquidity called on it -- but we can trade AGAINST our own LP as a
normal swapper. Paying ~0.00272 WETH (zeroForOne swap) drains all 2.718
wstDIEM from the position; the WETH replaces it in the locked LP.

Net: convert trapped wstDIEM into redeemable wstDIEM at cost of ~0.3% fee.
Recovered wstDIEM can be redeemed via vault after July 1 enableWithdrawals.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e9bB66966274b5D

Update CLAUDE.md with new deployer address and 1P item (el4qwixmdot757dpxcqgfo43qe,
vault mog.capital). Mark CreateMorphoMarket75 WST_DIEM as placeholder pending v5 deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…, batch tests

- Fix _deposit cap check: (staked, unstaking,) -> (staked,, unstaking) so slot 2
  (coolDownAmount) is used, not slot 1 (coolDownEnd timestamp)
- Fix DeployAll LLTV require messages: "38.5%" -> "86%"
- Add 6 missing tests: batch saturation (50-user cap), immediate flush when full,
  setMinBatchOpenSecs > 7-day max, deregistered adapter creditDIEM revert,
  unpause restores deposit, creditWstDIEM zero-recipient revert
- 364 unit tests passing, 0 logic failures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Base 2026-06-03

Update CLAUDE.md and CreateMorphoMarket75 with v5 deployed addresses:
- InferenceVault v5: 0xb9f23c33FfD2213f31C0cFb6c9e2fDf525a9Dd2D
- FeeRouter: 0x3b8d968DCca09E319fac7Df741804Af5644E3a60
- Router: 0x6fF481F4B3B0E2ADa548D454F7011D1ed51532B6
- Curve DIEM/wstDIEM: 0xB9c7F62e4EeC145bFa1C6bBc5fFdFf246181FdA2
All contracts verified on Basescan. Ownership transferred to Safe.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…enue adapters

Deploys AntSeedAdapter, SurplusAdapter, X402Adapter (owned by Safe)
then registers each on InferenceVault v5 via Safe multisig txs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AntSeedAdapter:  0xE9C2BE3ab25E97Ef4364c505202016106Bec6a6e
SurplusAdapter:  0xB67A86Ab50e30d7509eeD205Fc01A70758B227Db
X402Adapter:     0xC3C3CaC663f88304a38Cb9C4e9c02bB57DB00142

All owned by Safe, registered on InferenceVault v5, verified on Basescan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…6-06-03

wstDIEM collateral / DIEM loan, 77% LLTV, AdaptiveCurveIRM.
Enables single-tx leverage loop via Router.loopDeposit().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@moggedcapital moggedcapital merged commit 74ca7b9 into main Jun 4, 2026
4 of 5 checks passed
@moggedcapital moggedcapital deleted the chore/repo-hardening branch June 4, 2026 00:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant