diff --git a/cadence/scripts/diag_evm_token_balances.cdc b/cadence/scripts/diag_evm_token_balances.cdc new file mode 100644 index 00000000..00bfd1ae --- /dev/null +++ b/cadence/scripts/diag_evm_token_balances.cdc @@ -0,0 +1,71 @@ +import "EVM" +import "FlowEVMBridgeUtils" + +/// Returns the ERC20 balances of all tokens relevant to FlowYieldVaultsStrategiesV2 +/// for a given EVM address (e.g. a user's COA, the strategy contract, or a pool address). +/// +/// For each token reports: +/// balance – human-readable amount (token units, 8 dp precision) +/// balance_wei – raw amount in the token's smallest unit +/// decimals – the token's ERC20 decimal count +/// +/// Run: +/// flow scripts execute cadence/scripts/diag_evm_token_balances.cdc \ +/// --args-json '[{"type":"String","value":"0xYOUR_EVM_ADDRESS"}]' \ +/// --network mainnet +access(all) fun main(evmAddressHex: String): {String: {String: AnyStruct}} { + + let target = EVM.addressFromString(evmAddressHex) + let caller = EVM.addressFromString("0xca6d7Bb03334bBf135902e1d919a5feccb461632") // factory, used as from + + // ── Token EVM addresses ──────────────────────────────────────────────────── + let tokens: {String: EVM.EVMAddress} = { + "MOET": EVM.addressFromString("0x213979bb8a9a86966999b3aa797c1fcf3b967ae2"), + "PYUSD0": EVM.addressFromString("0x99aF3EeA856556646C98c8B9b2548Fe815240750"), + "FUSDEV": EVM.addressFromString("0xd069d989e2F44B70c65347d1853C0c67e10a9F8D"), + "WFLOW": EVM.addressFromString("0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e"), + "syWFLOWv": EVM.addressFromString("0xCBf9a7753F9D2d0e8141ebB36d99f87AcEf98597"), + "WETH": EVM.addressFromString("0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590"), + "WBTC": EVM.addressFromString("0x717DAE2BaF7656BE9a9B01deE31d571a9d4c9579") + } + + // ── Helpers ──────────────────────────────────────────────────────────────── + fun call(_ to: EVM.EVMAddress, _ data: [UInt8]): EVM.Result { + return EVM.dryCall( + from: caller, to: to, data: data, + gasLimit: 100_000, value: EVM.Balance(attoflow: 0) + ) + } + + fun toHuman(_ wei: UInt256, _ decimals: UInt8): UFix64 { + if decimals <= 8 { + return FlowEVMBridgeUtils.uint256ToUFix64(value: wei, decimals: decimals) + } + let quantum = FlowEVMBridgeUtils.pow(base: 10, exponent: decimals - 8) + return FlowEVMBridgeUtils.uint256ToUFix64(value: wei - (wei % quantum), decimals: decimals) + } + + // ── Query each token ─────────────────────────────────────────────────────── + var result: {String: {String: AnyStruct}} = {} + + for name in tokens.keys { + let tokenAddr = tokens[name]! + var entry: {String: AnyStruct} = {} + + let decimals = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: tokenAddr) + entry["decimals"] = decimals + + let balRes = call(tokenAddr, EVM.encodeABIWithSignature("balanceOf(address)", [target])) + if balRes.status == EVM.Status.successful { + let wei = EVM.decodeABI(types: [Type()], data: balRes.data)[0] as! UInt256 + entry["balance"] = toHuman(wei, decimals) + entry["balance_wei"] = wei + } else { + entry["error"] = "balanceOf call failed: ".concat(balRes.errorMessage) + } + + result[name] = entry + } + + return result +} diff --git a/cadence/scripts/diag_vault_prices.cdc b/cadence/scripts/diag_vault_prices.cdc new file mode 100644 index 00000000..bb86c15d --- /dev/null +++ b/cadence/scripts/diag_vault_prices.cdc @@ -0,0 +1,94 @@ +import "EVM" +import "FlowToken" +import "ERC4626Utils" +import "FlowEVMBridgeUtils" +import "BandOracle" + +/// Reports the current redemption price of the syWFLOWv and FUSDEV ERC4626 vaults, +/// both in their underlying token and in USD (via Band Protocol oracle). +/// +/// syWFLOWv price (USD) = (syWFLOWv / WFLOW) × FLOW/USD (Band symbol: "FLOW") +/// FUSDEV price (USD) = (FUSDEV / PYUSD0) × PYUSD/USD (Band symbol: "PYUSD") +/// +/// Note: syWFLOWv does not implement convertToAssets(); share price is derived from +/// totalAssets / totalSupply instead. +/// +/// Note: BandOracle.getReferenceData requires a FLOW fee. This script works only when +/// BandOracle.getFee() == 0.0. If the fee is non-zero the assertion will panic. +/// +/// Run: flow scripts execute cadence/scripts/diag_vault_prices.cdc --network mainnet +access(all) fun main(): {String: {String: AnyStruct}} { + let syWFLOWv = EVM.addressFromString("0xCBf9a7753F9D2d0e8141ebB36d99f87AcEf98597") + let fusdev = EVM.addressFromString("0xd069d989e2F44B70c65347d1853C0c67e10a9F8D") + + // ── Band Oracle USD prices ───────────────────────────────────────────────── + fun bandPrice(_ symbol: String): UFix64 { + let fee = BandOracle.getFee() + assert(fee == 0.0, message: "BandOracle fee is non-zero (".concat(fee.toString()).concat(" FLOW). Use a transaction to pay the fee.")) + let payment <- FlowToken.createEmptyVault(vaultType: Type<@FlowToken.Vault>()) + let data = BandOracle.getReferenceData(baseSymbol: symbol, quoteSymbol: "USD", payment: <-payment) + return data.fixedPointRate + } + + // ── Wei → human-readable token units (8 dp) ─────────────────────────────── + fun toHuman(_ wei: UInt256, _ decimals: UInt8): UFix64 { + if decimals <= 8 { + return FlowEVMBridgeUtils.uint256ToUFix64(value: wei, decimals: decimals) + } + let quantum = FlowEVMBridgeUtils.pow(base: 10, exponent: decimals - 8) + return FlowEVMBridgeUtils.uint256ToUFix64(value: wei - (wei % quantum), decimals: decimals) + } + + // ── Per-vault price computation ──────────────────────────────────────────── + fun vaultPrice( + _ vault: EVM.EVMAddress, + _ name: String, + _ underlyingName: String, + _ bandSymbol: String + ): {String: AnyStruct} { + let shareDec = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: vault) + + // Resolve underlying asset address + decimals via ERC4626 asset() + let underlyingRes = EVM.dryCall( + from: vault, to: vault, + data: EVM.encodeABIWithSignature("asset()", []), + gasLimit: 100_000, value: EVM.Balance(attoflow: 0) + ) + let underlying = underlyingRes.status == EVM.Status.successful + ? (EVM.decodeABI(types: [Type()], data: underlyingRes.data)[0] as! EVM.EVMAddress) + : vault + let assetDec = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: underlying) + + let totalAssetsWei = ERC4626Utils.totalAssets(vault: vault) ?? UInt256(0) + let totalSharesWei = ERC4626Utils.totalShares(vault: vault) ?? UInt256(0) + + let assetsHuman = toHuman(totalAssetsWei, assetDec) + let sharesHuman = toHuman(totalSharesWei, shareDec) + + // price per share in underlying token + let priceInUnderlying = sharesHuman > 0.0 ? assetsHuman / sharesHuman : 0.0 + + // USD price of the underlying from Band Oracle, then multiply through + let underlyingUSD = bandPrice(bandSymbol) + let priceUSD = priceInUnderlying * underlyingUSD + + return { + "price_usd": priceUSD, + "price_in_underlying": priceInUnderlying, + "underlying_usd": underlyingUSD, + "interpretation": "1 ".concat(name).concat(" = $").concat(priceUSD.toString()), + "totalAssets": assetsHuman, + "totalAssets_wei": totalAssetsWei, + "totalShares": sharesHuman, + "totalShares_wei": totalSharesWei, + "underlying_address": underlying.toString(), + "asset_decimals": assetDec, + "share_decimals": shareDec + } + } + + return { + "syWFLOWv": vaultPrice(syWFLOWv, "syWFLOWv", "WFLOW", "FLOW"), + "FUSDEV": vaultPrice(fusdev, "FUSDEV", "PYUSD0", "PYUSD") + } +} diff --git a/cadence/scripts/diag_verify_pools.cdc b/cadence/scripts/diag_verify_pools.cdc new file mode 100644 index 00000000..a7d3e6f2 --- /dev/null +++ b/cadence/scripts/diag_verify_pools.cdc @@ -0,0 +1,189 @@ +import "EVM" +import "FlowEVMBridgeUtils" + +/// Verifies all Uniswap V3 pools required by FlowYieldVaultsStrategiesV2 on Flow EVM mainnet. +/// +/// For each pool reports: +/// poolAddress – address from factory.getPool(); zero = pool never deployed +/// exists – true if pool address is non-zero +/// initialized – true if slot0.sqrtPriceX96 != 0 (initial price has been set) +/// hasLiquidity – true if current in-range liquidity > 0 +/// _balance – human-readable reserve of tokenA (token units, 8 dp precision) +/// _balance – human-readable reserve of tokenB (token units, 8 dp precision) +/// _balance_wei – raw ERC20 reserve of tokenA in smallest unit +/// _balance_wei – raw ERC20 reserve of tokenB in smallest unit +/// +/// Interpretation: +/// exists=false → pool contract was never created; must be deployed by an LP +/// initialized=false → pool exists but no price was ever set; add initial liquidity first +/// hasLiquidity=false → pool is initialised but all LP positions are currently out of range +/// balance=0.0 → no reserves in pool; needs liquidity seeded before swaps will work +/// +/// Run: +/// flow scripts execute cadence/scripts/diag_verify_pools.cdc --network mainnet +/// +/// Pools checked (all Uniswap V3, factory 0xca6d7Bb03334bBf135902e1d919a5feccb461632): +/// +/// ┌─ User-requested pools ─────────────────────────────────────────────────────────┐ +/// │ 1. MOET / PYUSD0 fee=100 pre-swap: PYUSD0 collateral → MOET for FlowALP │ +/// │ 2. FUSDEV / PYUSD0 fee=100 FUSDEVStrategy: yield token ↔ stablecoin │ +/// │ 3. syWFLOWv / WFLOW fee=100 syWFLOWvStrategy: yield token ↔ WFLOW │ +/// └────────────────────────────────────────────────────────────────────────────────┘ +/// ┌─ Multi-hop path pools (also required) ─────────────────────────────────────────┐ +/// │ 4. PYUSD0 / WFLOW fee=3000 FUSDEVStrategy FLOW collateral path │ +/// │ 5. PYUSD0 / WETH fee=3000 FUSDEVStrategy WETH collateral path │ +/// │ 6. PYUSD0 / WBTC fee=3000 FUSDEVStrategy WBTC collateral path │ +/// │ 7. WFLOW / WETH fee=3000 syWFLOWvStrategy WETH/WBTC collateral first hop │ +/// │ 8. WETH / WBTC fee=3000 syWFLOWvStrategy WBTC collateral second hop │ +/// └────────────────────────────────────────────────────────────────────────────────┘ +access(all) fun main(): {String: {String: AnyStruct}} { + + let factory = EVM.addressFromString("0xca6d7Bb03334bBf135902e1d919a5feccb461632") + + // ── Token EVM addresses ──────────────────────────────────────────────────── + let moet = EVM.addressFromString("0x213979bb8a9a86966999b3aa797c1fcf3b967ae2") + let pyusd0 = EVM.addressFromString("0x99aF3EeA856556646C98c8B9b2548Fe815240750") + let fusdev = EVM.addressFromString("0xd069d989e2F44B70c65347d1853C0c67e10a9F8D") + let wflow = EVM.addressFromString("0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e") + let sywflowv = EVM.addressFromString("0xCBf9a7753F9D2d0e8141ebB36d99f87AcEf98597") + let weth = EVM.addressFromString("0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590") + let wbtc = EVM.addressFromString("0x717DAE2BaF7656BE9a9B01deE31d571a9d4c9579") + + // ── EVM call helper ──────────────────────────────────────────────────────── + fun call(_ to: EVM.EVMAddress, _ data: [UInt8]): EVM.Result { + return EVM.dryCall( + from: factory, to: to, data: data, + gasLimit: 1_000_000, value: EVM.Balance(attoflow: 0) + ) + } + + // ── Wei → token units (truncated to UFix64's 8 decimal places) ──────────── + fun toHuman(_ wei: UInt256, _ decimals: UInt8): UFix64 { + if decimals <= 8 { + return FlowEVMBridgeUtils.uint256ToUFix64(value: wei, decimals: decimals) + } + // Floor to 8-decimal boundary before converting to avoid precision loss + let quantum = FlowEVMBridgeUtils.pow(base: 10, exponent: decimals - 8) + return FlowEVMBridgeUtils.uint256ToUFix64(value: wei - (wei % quantum), decimals: decimals) + } + + // ── Single-pool checker ──────────────────────────────────────────────────── + fun checkPool( + label: String, + tokenA: EVM.EVMAddress, tokenAName: String, + tokenB: EVM.EVMAddress, tokenBName: String, + fee: UInt256 + ): {String: AnyStruct} { + var out: {String: AnyStruct} = {"label": label} + + // 1. Resolve pool address from factory + let poolRes = call(factory, EVM.encodeABIWithSignature( + "getPool(address,address,uint24)", [tokenA, tokenB, fee] + )) + if poolRes.status != EVM.Status.successful { + out["error"] = "getPool failed: ".concat(poolRes.errorMessage) + return out + } + let poolAddr = EVM.decodeABI(types: [Type()], data: poolRes.data)[0] as! EVM.EVMAddress + let poolStr = poolAddr.toString() + out["poolAddress"] = poolStr + + let exists = poolStr != "0000000000000000000000000000000000000000" + out["exists"] = exists + if !exists { + out["initialized"] = false + out["hasLiquidity"] = false + return out + } + + // 2. slot0() → sqrtPriceX96 (uint160 ABI-padded to 32 bytes, decoded as UInt256) + // non-zero means the pool was initialised with a starting price + let slot0Res = call(poolAddr, EVM.encodeABIWithSignature("slot0()", [])) + if slot0Res.status != EVM.Status.successful { + out["error"] = "slot0 call failed: ".concat(slot0Res.errorMessage) + return out + } + let sqrtPriceX96 = EVM.decodeABI(types: [Type()], data: slot0Res.data)[0] as! UInt256 + out["sqrtPriceX96"] = sqrtPriceX96 + out["initialized"] = sqrtPriceX96 != UInt256(0) + + // 3. liquidity() → current in-range liquidity (uint128, decoded as UInt256) + let liqRes = call(poolAddr, EVM.encodeABIWithSignature("liquidity()", [])) + if liqRes.status == EVM.Status.successful { + let liq = EVM.decodeABI(types: [Type()], data: liqRes.data)[0] as! UInt256 + out["liquidity"] = liq + out["hasLiquidity"] = liq > UInt256(0) + } + + // 4. ERC20 decimals + balanceOf(pool) — actual reserves held in the pool contract + let decA = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: tokenA) + let decB = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: tokenB) + + let balARes = call(tokenA, EVM.encodeABIWithSignature("balanceOf(address)", [poolAddr])) + if balARes.status == EVM.Status.successful { + let wei = EVM.decodeABI(types: [Type()], data: balARes.data)[0] as! UInt256 + out[tokenAName.concat("_balance")] = toHuman(wei, decA) + out[tokenAName.concat("_balance_wei")] = wei + } + let balBRes = call(tokenB, EVM.encodeABIWithSignature("balanceOf(address)", [poolAddr])) + if balBRes.status == EVM.Status.successful { + let wei = EVM.decodeABI(types: [Type()], data: balBRes.data)[0] as! UInt256 + out[tokenBName.concat("_balance")] = toHuman(wei, decB) + out[tokenBName.concat("_balance_wei")] = wei + } + + return out + } + + // ── Run all checks ───────────────────────────────────────────────────────── + return { + "1_moet_pyusd0_fee100": checkPool( + label: "MOET / PYUSD0 fee=100 [pre-swap: PYUSD0 collateral → MOET for FlowALP]", + tokenA: moet, tokenAName: "MOET", + tokenB: pyusd0, tokenBName: "PYUSD0", + fee: UInt256(100) + ), + "2_fusdev_pyusd0_fee100": checkPool( + label: "FUSDEV / PYUSD0 fee=100 [FUSDEVStrategy: yield token ↔ stablecoin]", + tokenA: fusdev, tokenAName: "FUSDEV", + tokenB: pyusd0, tokenBName: "PYUSD0", + fee: UInt256(100) + ), + "3_sywflowv_wflow_fee100": checkPool( + label: "syWFLOWv / WFLOW fee=100 [syWFLOWvStrategy: yield token ↔ WFLOW]", + tokenA: sywflowv, tokenAName: "syWFLOWv", + tokenB: wflow, tokenBName: "WFLOW", + fee: UInt256(100) + ), + "4_pyusd0_wflow_fee3000": checkPool( + label: "PYUSD0 / WFLOW fee=3000 [FUSDEVStrategy: FLOW collateral exit path]", + tokenA: pyusd0, tokenAName: "PYUSD0", + tokenB: wflow, tokenBName: "WFLOW", + fee: UInt256(3000) + ), + "5_pyusd0_weth_fee3000": checkPool( + label: "PYUSD0 / WETH fee=3000 [FUSDEVStrategy: WETH collateral exit path]", + tokenA: pyusd0, tokenAName: "PYUSD0", + tokenB: weth, tokenBName: "WETH", + fee: UInt256(3000) + ), + "6_pyusd0_wbtc_fee3000": checkPool( + label: "PYUSD0 / WBTC fee=3000 [FUSDEVStrategy: WBTC collateral exit path]", + tokenA: pyusd0, tokenAName: "PYUSD0", + tokenB: wbtc, tokenBName: "WBTC", + fee: UInt256(3000) + ), + "7_wflow_weth_fee3000": checkPool( + label: "WFLOW / WETH fee=3000 [syWFLOWvStrategy: WETH & WBTC collateral first hop]", + tokenA: wflow, tokenAName: "WFLOW", + tokenB: weth, tokenBName: "WETH", + fee: UInt256(3000) + ), + "8_weth_wbtc_fee3000": checkPool( + label: "WETH / WBTC fee=3000 [syWFLOWvStrategy: WBTC collateral second hop]", + tokenA: weth, tokenAName: "WETH", + tokenB: wbtc, tokenBName: "WBTC", + fee: UInt256(3000) + ) + } +} diff --git a/docs/v1_supported_features.md b/docs/v1_supported_features.md new file mode 100644 index 00000000..a42ebcd2 --- /dev/null +++ b/docs/v1_supported_features.md @@ -0,0 +1,153 @@ +# FlowYieldVaults v1 — Supported Features + +> **Status:** Draft — items marked ⚠️ require confirmation before finalization. + +--- + +## 1. Strategies Supported + +### TracerStrategy +The flagship v1 strategy. Deposits FLOW as collateral into a FlowALP lending position, borrows MOET against it, and swaps into a yield-bearing token for ongoing yield. + +- **Collateral**: FLOW +- **Borrow token**: MOET (via FlowALP) +- **Yield token**: YieldToken (e.g. tauUSDF or mUSDC, depending on configuration) +- **Integrated protocols**: FlowALP v1, Uniswap V3 (EVM), ERC4626 vaults + +### mUSDCStrategy +ERC4626 vault integration strategy. Swaps into mUSDC and deposits into a Morpho-compatible vault. + +- **Collateral**: FLOW +- **Yield source**: mUSDC (ERC4626 vault) +- **Integrated protocols**: Uniswap V3, ERC4626 + +### mUSDFStrategy *(v1.1)* +Advanced strategy targeting USDF yield via Morpho Finance. + +- **Collateral**: FLOW +- **Yield source**: mUSDDF (Morpho Finance) +- **Integrated protocols**: Uniswap V3, Morpho Finance, ERC4626 + +### Simple ERC4626 Strategies *(PMStrategiesV1)* + +| Strategy | Yield Source | +|----------|-------------| +| `syWFLOWvStrategy` | Swap-based Yield FLOW | +| `tauUSDFvStrategy` | Tau Labs USDF vault | +| `FUSDEVStrategy` | Flow USD Expeditionary Vault | + +--- + +## 2. Supported Input (Deposit) Token + +| Token | Notes | +|-------|-------| +| **FLOW** | Only supported deposit token in v1 | + +> ⚠️ Multi-collateral support (WETH, WBTC, etc.) is not in v1 scope — confirm if any bridged assets are accepted directly. + +--- + +## 3. Output / Receipt Token + +- **YieldToken** (`@YieldToken.Vault`) — issued to users representing their share of the yield-bearing position. +- Each YieldVault also tracks a **position ID** (UInt64) tied to the underlying FlowALP position. + +--- + +## 4. Rebalancing + +| Parameter | Value | +|-----------|-------| +| Rebalancing frequency | **10 minutes** (600 seconds) | +| Scheduling mechanism | Flow native `FlowTransactionScheduler` (FLIP 330) | +| Lower rebalance threshold | **0.95** (5% below target triggers recollateralization) | +| Upper rebalance threshold | **1.05** (5% above target triggers rebalancing) | +| Force rebalance option | Available (bypasses threshold checks) | +| Fee margin multiplier | 1.2× (20% buffer on estimated scheduling fees) | +| Supervisor recovery batch | Up to 50 stuck vaults per recovery run | + +The AutoBalancer self-schedules each subsequent execution at creation and after each run. A Supervisor contract handles recovery for vaults that fall out of the schedule. + +--- + +## 5. Health Factors (FlowALP Position) + +| Parameter | Value | +|-----------|-------| +| Target health | 1.30 | +| Minimum health (liquidation threshold) | 1.10 | +| Liquidation target health factor | 1.05 | + +--- + +## 6. Position & Deposit Limits + +| Parameter | Value | Notes | +|-----------|-------|-------| +| Minimum deposit | ⚠️ ??? | No contract-enforced minimum found | +| Maximum deposit capacity | 1,000,000 FLOW (default) | Governance-configurable cap | +| Deposit rate limit | 1,000,000 FLOW (default) | Per-block rate limiting via FlowALP | + +--- + +## 7. Fees + +| Fee Type | Value | Notes | +|----------|-------|-------| +| Scheduling / rebalancing fee | Paid in FLOW from AutoBalancer fee source | Min fallback: governance-set | +| Protocol / interest fee | Dynamic (utilization-based via FlowALP) | | +| Insurance reserve | 0.1% of credit balance | Taken before distributing credit interest | +| Management / performance fee | ⚠️ ??? | Confirm if any protocol-level fee applies | +| Withdrawal fee | ⚠️ ??? | Not observed in contracts — confirm | + +--- + +## 8. Access Control (Closed Beta) + +| Feature | Details | +|---------|---------| +| YieldVault creation | Requires a `BetaBadge` capability | +| Badge grant / revoke | Admin-only via `FlowYieldVaultsClosedBeta` contract | +| Rebalancing | Any account can trigger; Supervisor handles recovery | +| Governance params | Admin / Configure entitlements only | + +--- + +## 9. Contracts & Mainnet Addresses + +All core contracts are deployed to **`0xb1d63873c3cc9f79`**. + +| Contract | Address | +|----------|---------| +| `FlowYieldVaults` | `0xb1d63873c3cc9f79` | +| `FlowYieldVaultsStrategies` | `0xb1d63873c3cc9f79` | +| `FlowYieldVaultsAutoBalancers` | `0xb1d63873c3cc9f79` | +| `FlowYieldVaultsClosedBeta` | `0xb1d63873c3cc9f79` | +| `FlowYieldVaultsSchedulerV1` | `0xb1d63873c3cc9f79` | +| `FlowYieldVaultsSchedulerRegistry` | `0xb1d63873c3cc9f79` | +| FlowALP (lending) | `0x6b00ff876c299c61` | +| DeFiActions platform | `0x6d888f175c158410` | +| EVM Bridge | `0x1e4aa0b87d10b141` | + +### Key EVM Asset Addresses (Mainnet) + +| Token | EVM Address | +|-------|-------------| +| USDC | `0xF1815bd50389c46847f0Bda824eC8da914045D14` | +| wETH | `0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590` | +| cbBTC | `0xA0197b2044D28b08Be34d98b23c9312158Ea9A18` | +| tauUSDF (ERC4626) | `0xc52E820d2D6207D18667a97e2c6Ac22eB26E803c` | + +--- + +## 10. Open Items + +| # | Item | Owner | +|---|------|-------| +| 1 | Minimum deposit value (is there a floor?) | | +| 2 | Management / performance fee — does one exist? | | +| 3 | Withdrawal fee — confirm none | | +| 4 | Are any bridged assets (WETH, WBTC) accepted as direct deposits in v1? | | +| 5 | Confirm deposit capacity cap of 1,000,000 FLOW for v1 launch | | +| 6 | mUSDFStrategy and simple ERC4626 strategies — in v1 or later? | |