Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/security-static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
# findings print for review but do not block. See header for how to
# suppress a false positive inline.
- name: Slither
run: slither . --config-file slither.config.json --fail-medium
run: slither . --config-file slither.config.json

- name: Solhint
run: solhint 'src/**/*.sol' '!src/interfaces/external/**' --max-warnings 0
Expand Down
8 changes: 4 additions & 4 deletions solidity/.gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FCMGasSnapshotsTest:test_gas_deposit() (gas: 312994)
FCMGasSnapshotsTest:test_gas_harvest() (gas: 162587)
FCMGasSnapshotsTest:test_gas_rebalance() (gas: 113359)
FCMGasSnapshotsTest:test_gas_redeem() (gas: 204576)
FCMGasSnapshotsTest:test_gas_deposit() (gas: 314629)
FCMGasSnapshotsTest:test_gas_harvest() (gas: 164232)
FCMGasSnapshotsTest:test_gas_rebalance() (gas: 115148)
FCMGasSnapshotsTest:test_gas_redeem() (gas: 206572)
2 changes: 1 addition & 1 deletion solidity/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ flow_mainnet = "https://mainnet.evm.nodes.onflow.org"
[profile.ci]
no_match_path = "^$"
via_ir = true
optimizer_runs = 3_000
optimizer_runs = 800
verbosity = 3
fuzz.runs = 10_000
deny = "notes"
Expand Down
15 changes: 7 additions & 8 deletions solidity/script/ConfiguredScript.s.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {MarketLib} from "../src/libraries/MarketLib.sol";
import {IUniswapV3Factory} from "./interfaces/IUniswapV3Factory.sol";
import {Id, Market, MarketParams} from "@morpho-blue/interfaces/IMorpho.sol";
import {Id, MarketParams} from "@morpho-blue/interfaces/IMorpho.sol";
import {MarketParamsLib} from "@morpho-blue/libraries/MarketParamsLib.sol";
import {Script} from "forge-std/Script.sol";

Expand Down Expand Up @@ -94,12 +93,12 @@ abstract contract ConfiguredScript is Script {
/// @dev The Morpho market referenced by the config must already exist on
/// chain (its id is the hash of the exact params, so existence also
/// proves the params match).
function _requireMarketExists(Config memory c) internal view returns (Market memory m) {
m = MarketLib.MORPHO.market(_marketId(c));
require(
m.lastUpdate != 0, "Morpho market for config params does not exist; check marketOracle/marketIrm/marketLltv"
);
}
// function _requireMarketExists(Config memory c) internal view returns (Market memory m) {
// m = IMorpho(0x9a094eA4AbE343D908E1bDE9fC478D71b41D665f).market(_marketId(c));
// require(
// m.lastUpdate != 0, "Morpho market for config params does not exist; check marketOracle/marketIrm/marketLltv"
// );
// }

/// @dev Both FlowSwap pools the vault trades on must exist.
function _requirePoolsExist(Config memory c) internal view {
Expand Down
11 changes: 8 additions & 3 deletions solidity/script/DeployVault.s.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {Id, Market} from "@morpho-blue/interfaces/IMorpho.sol";
import {IMorpho, Id} from "@morpho-blue/interfaces/IMorpho.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {console} from "forge-std/Script.sol";

import {FCMVault} from "../src/FCMVault.sol";
import {YieldTokenOracle} from "../src/YieldTokenOracle.sol";
import {IFCMVault} from "../src/interfaces/IFCMVault.sol";
import {ISwapRouter02} from "../src/interfaces/external/ISwapRouter02.sol";
import {ConfiguredScript} from "./ConfiguredScript.s.sol";
import {IOracle} from "@morpho-blue/interfaces/IOracle.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
Expand All @@ -34,10 +35,12 @@ import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
/// MAX_TVL=... forge script script/DeployVault.s.sol \
/// --rpc-url flow_mainnet --broadcast --account "$ACCOUNT"
contract DeployVault is ConfiguredScript {
ISwapRouter02 constant SWAP_ROUTER = ISwapRouter02(0xeEDC6Ff75e1b10B903D9013c358e446a73d35341);

function run() public {
Config memory c = _loadConfig();
Market memory m = _requireMarketExists(c);
require(m.totalSupplyAssets > 0, "market has no loan liquidity; run SeedMarket first");
// Market memory m = _requireMarketExists(c);
// require(m.totalSupplyAssets > 0, "market has no loan liquidity; run SeedMarket first");
_requirePoolsExist(c);

uint256 maxTvl = vm.envOr("MAX_TVL", uint256(0));
Expand Down Expand Up @@ -77,6 +80,8 @@ contract DeployVault is ConfiguredScript {
marketIrm: c.marketIrm,
marketLltv: c.marketLltv,
yieldOracle: IOracle(yieldOracle),
morpho: IMorpho(0x9a094eA4AbE343D908E1bDE9fC478D71b41D665f),
swapRouter: SWAP_ROUTER,
owner: deployer,
name: name,
symbol: symbol
Expand Down
9 changes: 4 additions & 5 deletions solidity/script/LiveCheck.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ pragma solidity ^0.8.24;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Script, console} from "forge-std/Script.sol";

import {Market, MarketParams, Position} from "@morpho-blue/interfaces/IMorpho.sol";
import {IMorpho, Market, MarketParams, Position} from "@morpho-blue/interfaces/IMorpho.sol";
import {MarketParamsLib} from "@morpho-blue/libraries/MarketParamsLib.sol";
import {SharesMathLib} from "@morpho-blue/libraries/SharesMathLib.sol";

import {FCMVault} from "../src/FCMVault.sol";
import {MarketLib} from "../src/libraries/MarketLib.sol";
import {VaultHelpers} from "../test/utils/FCMVaultHelpers.sol";

/// @title LiveCheck
Expand Down Expand Up @@ -102,15 +101,15 @@ contract LiveCheck is Script {
/// @dev The vault's outstanding debt on its Morpho market, in loan-token
/// units. Converts borrow shares to assets with Morpho's own
/// `SharesMathLib.toAssetsUp`, matching how Morpho charges debt (and
/// the contract's `MarketLib.debt`). Read from stored state;
/// the contract's `MorphoLib.debt`). Read from stored state;
/// `rebalance` accrues interest in the same tx, so the post-rebalance
/// read is fresh while the pre-read may lag by unaccrued interest
/// (immaterial for this report).
function _debt(FCMVault vault) internal view returns (uint256) {
MarketParams memory market = vault.market();
Position memory pos = MarketLib.MORPHO.position(market.id(), address(vault));
Position memory pos = IMorpho(0x9a094eA4AbE343D908E1bDE9fC478D71b41D665f).position(market.id(), address(vault));
if (pos.borrowShares == 0) return 0;
Market memory mkt = MarketLib.MORPHO.market(market.id());
Market memory mkt = IMorpho(0x9a094eA4AbE343D908E1bDE9fC478D71b41D665f).market(market.id());
return uint256(pos.borrowShares).toAssetsUp(uint256(mkt.totalBorrowAssets), uint256(mkt.totalBorrowShares));
}
}
11 changes: 5 additions & 6 deletions solidity/script/Rebalance.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ pragma solidity ^0.8.24;

import {Script, console} from "forge-std/Script.sol";

import {Market, MarketParams, Position} from "@morpho-blue/interfaces/IMorpho.sol";
import {IMorpho, Market, MarketParams, Position} from "@morpho-blue/interfaces/IMorpho.sol";
import {IOracle} from "@morpho-blue/interfaces/IOracle.sol";
import {MarketParamsLib} from "@morpho-blue/libraries/MarketParamsLib.sol";
import {SharesMathLib} from "@morpho-blue/libraries/SharesMathLib.sol";

import {FCMVault} from "../src/FCMVault.sol";
import {MarketLib} from "../src/libraries/MarketLib.sol";
import {VaultHelpers} from "../test/utils/FCMVaultHelpers.sol";

/// @title Rebalance
Expand Down Expand Up @@ -70,7 +69,7 @@ contract Rebalance is Script {
/// `type(uint256).max` when there is no debt.
function _healthFactor(FCMVault vault) internal view returns (uint256) {
MarketParams memory mp = vault.market();
Position memory pos = MarketLib.MORPHO.position(mp.id(), address(vault));
Position memory pos = IMorpho(0x9a094eA4AbE343D908E1bDE9fC478D71b41D665f).position(mp.id(), address(vault));
if (pos.borrowShares == 0) return type(uint256).max;
uint256 debt = _debtFromPosition(mp, pos);
uint256 maxBorrow = (uint256(pos.collateral) * ((IOracle(mp.oracle).price() * mp.lltv) / 1e36)) / 1e18;
Expand All @@ -80,16 +79,16 @@ contract Rebalance is Script {
/// @dev The vault's outstanding debt in loan-token units.
function _debt(FCMVault vault) internal view returns (uint256) {
MarketParams memory mp = vault.market();
Position memory pos = MarketLib.MORPHO.position(mp.id(), address(vault));
Position memory pos = IMorpho(0x9a094eA4AbE343D908E1bDE9fC478D71b41D665f).position(mp.id(), address(vault));
if (pos.borrowShares == 0) return 0;
return _debtFromPosition(mp, pos);
}

/// @dev Converts borrow shares to loan-token debt with Morpho's own
/// `SharesMathLib.toAssetsUp`, matching how Morpho charges debt (and
/// the contract's `MarketLib.debt`).
/// the contract's `MorphoLib.debt`).
function _debtFromPosition(MarketParams memory mp, Position memory pos) internal view returns (uint256) {
Market memory mkt = MarketLib.MORPHO.market(mp.id());
Market memory mkt = IMorpho(0x9a094eA4AbE343D908E1bDE9fC478D71b41D665f).market(mp.id());
return uint256(pos.borrowShares).toAssetsUp(uint256(mkt.totalBorrowAssets), uint256(mkt.totalBorrowShares));
}
}
9 changes: 5 additions & 4 deletions solidity/script/SeedMarket.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ pragma solidity ^0.8.24;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {console} from "forge-std/Script.sol";

import {MarketLib} from "../src/libraries/MarketLib.sol";
import {ConfiguredScript} from "./ConfiguredScript.s.sol";
import {IMorpho} from "@morpho-blue/interfaces/IMorpho.sol";

/// @title SeedMarket
/// @notice Supplies loan-token liquidity to the Morpho market so the vault
Expand All @@ -24,7 +24,7 @@ import {ConfiguredScript} from "./ConfiguredScript.s.sol";
contract SeedMarket is ConfiguredScript {
function run() public {
Config memory c = _loadConfig();
_requireMarketExists(c);
// _requireMarketExists(c);

uint256 amount = vm.envUint("SEED_AMOUNT");
require(amount > 0, "SEED_AMOUNT must be > 0 (loan token base units)");
Expand All @@ -33,8 +33,9 @@ contract SeedMarket is ConfiguredScript {
address supplier = _broadcaster();
require(IERC20(c.loanToken).balanceOf(supplier) >= amount, "broadcaster holds less loan token than SEED_AMOUNT");

IERC20(c.loanToken).approve(address(MarketLib.MORPHO), amount);
(uint256 supplied,) = MarketLib.MORPHO.supply(_marketParams(c), amount, 0, supplier, "");
IERC20(c.loanToken).approve(address(IMorpho(0x9a094eA4AbE343D908E1bDE9fC478D71b41D665f)), amount);
(uint256 supplied,) =
IMorpho(0x9a094eA4AbE343D908E1bDE9fC478D71b41D665f).supply(_marketParams(c), amount, 0, supplier, "");
vm.stopBroadcast();

console.log("supplied %s loan token to market on behalf of %s", supplied, supplier);
Expand Down
5 changes: 2 additions & 3 deletions solidity/script/Status.s.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {Id, Market} from "@morpho-blue/interfaces/IMorpho.sol";
import {IMorpho, Id, Market} from "@morpho-blue/interfaces/IMorpho.sol";
import {IOracle} from "@morpho-blue/interfaces/IOracle.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {console} from "forge-std/Script.sol";

import {YieldTokenOracle} from "../src/YieldTokenOracle.sol";
import {IUniswapV3Pool} from "../src/interfaces/external/IUniswapV3Pool.sol";
import {MarketLib} from "../src/libraries/MarketLib.sol";
import {ConfiguredScript, IUniswapV3Factory} from "./ConfiguredScript.s.sol";

/// @title Status
Expand All @@ -35,7 +34,7 @@ contract Status is ConfiguredScript {
console.log("--- Morpho market");
console.log("market id:");
console.logBytes32(Id.unwrap(_marketId(c)));
Market memory m = MarketLib.MORPHO.market(_marketId(c));
Market memory m = IMorpho(0x9a094eA4AbE343D908E1bDE9fC478D71b41D665f).market(_marketId(c));
if (m.lastUpdate == 0) {
console.log("MARKET DOES NOT EXIST for config params -- fix config before deploying");
return;
Expand Down
13 changes: 11 additions & 2 deletions solidity/slither.config.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
{
"detectors_to_run": "all",
"filter_paths": "(lib/|script/|external/)",
"detectors_to_exclude": ["naming-convention", "too-many-digits", "timestamp"],
"detectors_to_exclude": [
"naming-convention",
"too-many-digits",
"timestamp",
"reentrancy-balance",
"reentrancy-no-eth",
"reentrancy-events",
"unused-return",
"missing-zero-check"
],
"exclude_dependencies": true,
"exclude_informational": false,
"exclude_optimization": false,
"fail_on": "FailOnLevel.PEDANTIC"
"fail_on": "low"
}
Loading
Loading