Foundry repository for Morpho Stock Vault on Robinhood Chain (EIP-155 4663).
The vault is an official Morpho Vault V2 instance. Depositors put in USDG. The curator allocates that USDG into isolated Morpho Blue markets collateralized by Robinhood Stock Tokens. This repo does not fork Vault V2 or Morpho Blue. It deploys the curator-owned pieces around Morpho’s factories:
| Contract | Role |
|---|---|
Morpho Vault V2 (msvUSDG) |
ERC-4626 USDG vault, created through Morpho’s factory |
| Morpho market adapter | Supplies and withdraws USDG on Blue |
MsvFeeSplitter |
performanceFeeRecipient. Splits minted vault shares 50/50 |
MsvFeeDistributor |
Stake $MSV, stream USDG to stakers |
MsvHolderRewards |
Holder USDG claims. 50% × creator tax after last $MSV tx × holding / 1B |
$MSV (0xc6e0A237844AFCa52D9C1B94b612134668a98c2F) is the staking token. It is not the vault asset.
Independent of Morpho, Robinhood, and Paxos. Not an offer of securities. Stock Tokens are not DTCC shares.
| Piece | Status |
|---|---|
| Vault V2, adapter, splitter, distributor | Deployed. Do not re-run DeployMsv.s.sol against this instance |
$MSV staking |
Live. Min 1 $MSV. 7-day reward stream, 7-day unbond |
$MSV holder rewards |
Live. Owner must recordTax and setLastMsvTx before first claims pay. $100 is reserve |
| Pilot caps | Submitted. Morpho 3-day timelock. Execute after 17 September 2026, 21:57 UTC+3 with ExecuteMarkets.s.sol |
| Allocation / depositor APY | 0 until caps execute, then allocate and harvest |
| Keeper / surplus router | Not in this repo |
Until caps execute, USDG sent to the vault sits idle. The production app keeps deposit/withdraw closed and staking open.
Do not use ConfigureMarkets.s.sol on this vault. That script submits and executes caps in one run. Caps on this instance are already submitted; a second execute before the timelock reverts.
Verify on Blockscout. Never take an address from a DM.
| Name | Address |
|---|---|
Vault V2 (msvUSDG) |
0xe2b27f8A96778f3D6C243687A73FC9836621E5eC |
| Market adapter | 0xB37FE8Bb4169D2De64e5ADE7AfB1b342fCF79393 |
$MSV staking / fee distributor |
0x1412a0C9844537561838B4e642AC920EB0a10e14 |
$MSV holder rewards |
0xe698c140588C3C7Cf0DC9e4254096F1D81f206e4 |
| Fee splitter | 0x8b6e8F7B7cc46FEe428AA3BE5437fB4C5D2e0967 |
$MSV |
0xc6e0A237844AFCa52D9C1B94b612134668a98c2F |
| Owner / curator / allocator | 0x6A39c2229ba13732068ED93Bc5FA02528c2e6B42 |
| Team fee wallet | 0xfA34e3c534CF5C1d958f369372609e3A03C854b1 |
Share token name/symbol: MSV USDG Vault / msvUSDG (Morpho forbids “morpho” in the ERC-20 name). Product copy still uses Morpho Stock Vault.
| Name | Address |
|---|---|
| Morpho Blue | 0x9D53d5E3bd5E8d4Cbfa6DB1ca238AEA02E651010 |
| Adaptive Curve IRM | 0x2BD3d5965B26B51814AC95127B2b80dD6CcC0fa1 |
| USDG (6 decimals) | 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 |
| VaultV2Factory | 0x0FBad98595b0186dA120E41f77C102beb49f803c |
| MorphoMarketV1AdapterV2Factory | 0x79370Ed003CE325C088E530d5e8655c99c2993e1 |
| Adapter registry | 0xe785a2eFD384BA7B95BaEd3851BC76aeD67C676f |
RPC: https://rpc.mainnet.chain.robinhood.com
Constants also live in src/MsvAddresses.sol. Confirm factories on Blockscout before any new broadcast.
USDG depositor ──deposit──► Vault V2 (msvUSDG)
│
│ Morpho market adapter
▼
Morpho Blue (isolated books)
NVDA / AAPL / GOOGL / SPY / TSLA
all 62.5% LLTV in this pilot
│
Morpho interest ──┤
├ 80% stays in share price (depositors)
├ 20% performance ──► MsvFeeSplitter.release()
│ ├ 10% of interest → team (vault shares)
│ └ 10% of interest → distributor
│ harvest() → USDG, 7-day stream
└ 2% APY management ──► team (vault shares)
Borrowers never enter this vault. They post Stock Tokens on Morpho Blue and borrow USDG there.
Idle USDG in the vault is depositor principal (withdrawal buffer), not the $MSV fee pot.
Morpho does not pay a fixed APY. Supply rates follow the Adaptive Curve IRM and can be near zero until utilization appears.
| Fee | Parameter | Recipient |
|---|---|---|
| Performance | 20% of Morpho interest (Morpho cap 50%) | Splitter: 50/50 team / stakers → 10% of interest each |
| Management | 2% APY of AUM (Morpho cap 5%/year) | Team wallet, as vault shares |
| Adapter skim | Morpho adapter leftover | Team wallet |
Depositors keep 80% of Morpho interest. Management still mints shares on AUM.
After the vault mints performance-fee shares to the splitter, anyone may call MsvFeeSplitter.release(). The distributor then needs harvest() (anyone) to redeem those shares to USDG and start the 7-day stream.
Five liquid 62.5% LLTV USDG books. Absolute caps are USDG (6 decimals). Relative caps are WAD (1e18 = 100%). Config: script/MsvPilotMarkets.sol.
Idle-liquidity adapter is SPY 62.5% (lowest utilization of the sized books).
| Market | Abs cap | Rel cap | Morpho market id |
|---|---|---|---|
| NVDA 62.5% | $35,000 | 35% | 0x8b16891f032a93b771347c9cb470a780e6699dd701553d3402aa3cdba6189c3e |
| AAPL 62.5% | $25,000 | 25% | 0xdeb4782d012d5fd3b24962538c2f6559049d70bda4dabd2e4212dacb96c28d45 |
| GOOGL 62.5% | $20,000 | 20% | 0x7fa81b10e5d21b2e4c571f862442bc11aff2ed14f02335868d0ff933fd40d0ba |
| SPY 62.5% | $10,000 | 15% | 0x50bc39b5722fb5634c436d74c6787f3c125b879e7b73cf9e9ecc01bbb57b8e55 |
| TSLA 62.5% | $8,000 | 10% | 0xb41b34c5989420ad080e79363a9cfe3e23bec7459fcd2d88029250da370288df |
Timelock on cap increases is 3 days (259200 seconds). That is intentional.
Idle $MSV earns nothing. Stakers earn only USDG that streams while they are staked.
| Parameter | Value |
|---|---|
| Stake asset | $MSV (18 decimals) |
| Reward asset | USDG (6 decimals) |
| Accounting | Reward-per-token. New stakers do not inherit already-streamed fees |
| Min stake | 1 $MSV (1e18). Remaining stake after unbond must be 0 or ≥ 1 |
| Stake | Instant |
| Reward stream | Each harvested USDG amount is paid linearly over 7 days |
| Empty stake pool | Incoming USDG is queued, then streamed over 7 days once someone stakes (no lump to the first wallet) |
| Claim | Anytime; only USDG already accrued to msg.sender |
| Unbond | 7 days. One pending unbond per wallet. No new accrual during unbond. Accrued USDG still claimable |
| Exit | After unlock, withdraw $MSV |
harvest() |
Anyone. Redeems distributor vault shares up to maxRedeem, then streams USDG |
notifyRewardAmount |
Optional keeper/surplus path. Pulls USDG from caller |
recover |
Owner only. Cannot recover $MSV, USDG, or vault shares |
pending = stake × (rewardPerToken − userCheckpoint) / 1e18 + storedRewards
src/
MsvFeeDistributor.sol # staking + USDG stream
MsvFeeSplitter.sol # 50/50 performance-fee share split (immutable)
MsvAddresses.sol # 4663 constants
script/
DeployMsv.s.sol # factory vault + adapter + fees + timelocks (already broadcast)
SubmitMarkets.s.sol # set liquidity adapter + submit caps (already broadcast)
ExecuteMarkets.s.sol # execute those caps after the 3-day timelock
MsvPilotMarkets.sol # shared five-market config
ConfigureMarkets.s.sol # one-market submit+execute — do not run on the live vault
interfaces/ # local Morpho Vault V2 / Blue interfaces
test/ # Foundry tests for distributor and splitter
Vault V2 and Morpho Blue source are not vendored. Scripts talk to on-chain factories through script/interfaces/IMorphoVaultV2.sol.
- Foundry (
forge,cast) - solc 0.8.28 (see
foundry.toml; EVMcancun, optimizer 200 runs) - OpenZeppelin Contracts v5.4.0
- forge-std
lib/ is gitignored. Clone dependencies once:
mkdir -p lib
git clone --depth 1 https://github.com/foundry-rs/forge-std.git lib/forge-std
git clone --depth 1 --branch v5.4.0 https://github.com/OpenZeppelin/openzeppelin-contracts.git lib/openzeppelin-contractsvault-v2/ and morpho-blue/ remappings exist for optional local research. They are not required to compile or test this repo.
Windows: if forge test fails while fetching solc, install solc 0.8.28 to %USERPROFILE%\.svm\0.8.28\solc-0.8.28.exe and pass --use.
forge test -vvvCoverage is the distributor (stake, unbond, stream, empty-pool queue, harvest) and the splitter (release, bps). Vault V2 behavior is Morpho’s; it is not re-tested here.
Copy .env.example to .env. Never commit .env. Do not put a private key in git, issues, or chat.
set -a && source .env && set +aPowerShell:
Get-Content .env | ForEach-Object {
if ($_ -match '^\s*#' -or $_ -notmatch '=') { return }
$k,$v = $_.Split('=',2)
Set-Item -Path "Env:$k" -Value $v.Trim().Trim('"')
}PRIVATE_KEY is only required for --broadcast. Simulation does not need it.
Simulate first (-vvv, no --broadcast). Add --broadcast --private-key $PRIVATE_KEY only when the calldata is correct.
Creates Vault V2 via VaultV2Factory.createVaultV2, deploys the adapter, distributor, and 50/50 splitter, sets 20% performance fee, 2% management fee, adapter skim, maxRate, dead deposit, and 3-day timelocks.
Do not re-deploy if the goal is the live msvUSDG instance above. A new salt produces a different vault.
forge script script/DeployMsv.s.sol --rpc-url "$RPC_URL" -vvvSets the liquidity adapter to SPY 62.5% (instant) and submits absolute/relative caps for the five markets. Does not execute.
Executes the submitted caps. Calldata must match MsvPilotMarkets.sol exactly (same ids and cap values as the submit).
forge script script/ExecuteMarkets.s.sol --rpc-url "$RPC_URL" -vvv
# then, only after the timelock:
# forge script script/ExecuteMarkets.s.sol --rpc-url "$RPC_URL" --broadcast --private-key "$PRIVATE_KEY" -vvvAfter execute, an allocator must allocate USDG into the enabled markets. Staking APY remains ~0 until there is interest, release(), and harvest().
Single-market submit and execute in one transaction. Useful only for a new vault with no pending cap timelock. Running it on the live instance will fight the already-submitted caps.
Live: 0xe698c140588C3C7Cf0DC9e4254096F1D81f206e4. Owner is the same curator EOA. The contract does not need ETH; the owner wallet does (gas + USDG reserve).
Creator tax is recorded with recordTax / recordTaxBatch (full creator amount, not the 50% cut, non-decreasing taxTime, >= taxOrigin 12 Sep 2026 00:00 UTC). USDG deposit is reserve only. It is not the reward numerator.
First claim needs setLastMsvTx(wallet, lastTransferUnix) once (indexer: last $MSV Transfer, ignoring stake/unstake). Window start is max(taxOrigin, lastMsvTx) until the wallet claims, then checkpoint is now. Min hold 100,000 $MSV.
USDG = 50% × creatorTaxAfter(windowStart) × (wallet $MSV + staked $MSV) / 1,000,000,000
# USDG reserve (FUND_AMOUNT = USDG 6 decimals, e.g. 100000000 = $100)
export FUND_AMOUNT=100000000
forge script script/FundHolderRewards.s.sol --rpc-url "$RPC_URL" --broadcast --private-key "$PRIVATE_KEY" -vvv
# backfill creator tax from script/taxes.json
python3 script/scanTaxes.py
forge script script/RecordTax.s.sol --rpc-url "$RPC_URL" --broadcast --private-key "$PRIVATE_KEY" -vvv
# first-time last $MSV tx
python3 script/scanLastMsvTx.py
forge script script/BackfillLastMsvTx.s.sol --rpc-url "$RPC_URL" --broadcast --private-key "$PRIVATE_KEY" -vvv
# withdraw leftover USDG (or any token) to OWNER
export RECOVER_TOKEN=0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
export RECOVER_AMOUNT=1000000
forge script script/RecoverHolderRewards.s.sol --rpc-url "$RPC_URL" --broadcast --private-key "$PRIVATE_KEY" -vvvrecover can sweep USDG at any time. If you pull below pending claims, the next claim reverts InsufficientReserve.
- Vault roles (owner, curator, allocator) are Morpho Vault V2 roles. Cap increases are timelocked; that is the listing-friendly path, not a bug.
MsvFeeSplitteris not upgradeable.teamBpsis immutable (5000 at deploy).MsvFeeDistributorowner canrecoverstray tokens only. Stake, reward, and vault share tokens are protected.MsvHolderRewardsrecordTaxandsetLastMsvTxare owner-only.setLastMsvTxcannot overwrite. Ownerrecovercan sweep leftover USDG.- Dead deposit to
0xdeadis Morpho’s inflation guard (dust). Seed USDG, if any, is working capital held as vault shares — not a protocol fee. - This code has unit tests. It is not a substitute for Morpho’s own Vault V2 audits.
| Site | URL |
|---|---|
| Markets | https://msv.gold |
| App (stake live) | https://lend.msv.gold |
| Docs | https://docs.msv.gold |
$MSV |
https://token.msv.gold |
GPL-2.0-or-later. See SPDX headers in src/ and script/. Morpho Blue, Vault V2, USDG, and Stock Tokens are third-party deployments with their own licenses and terms.