Skip to content
Draft
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
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,10 @@ soljson-latest.js

# Gas report
gasreport.ansi

# Claude
.claude/
.planning/

# Seismic toolchain (repo-local install — see scripts/seismic-env.sh)
.seismic-toolchain/
1 change: 1 addition & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[submodule "lib/common"]
path = lib/common
url = https://github.com/m0-foundation/common
branch = feat/erc20-virtual
[submodule "lib/openzeppelin-foundry-upgrades"]
path = lib/openzeppelin-foundry-upgrades
url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades
Expand Down
11 changes: 11 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
# If the repo-local Seismic toolchain is installed (see scripts/seismic-env.sh),
# put sforge/ssolc on PATH and pin FOUNDRY_PROFILE=seismic so the test step
# below compiles shielded-type code (suint256, etc.) that stock solc rejects.
# When the toolchain isn't present, the hook falls back to stock forge.
if [ -x ".seismic-toolchain/bin/sforge" ]; then
PATH="$PWD/.seismic-toolchain/bin:$PATH"
export PATH
FOUNDRY_PROFILE=seismic
export FOUNDRY_PROFILE
fi

npm run lint-staged && npm test
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ deploy-sepolia :; FOUNDRY_PROFILE=production forge script script/Deploy.s.sol --
slither :; FOUNDRY_PROFILE=production forge build --build-info --skip '*/test/**' --skip '*/script/**' --force && slither --compile-force-framework foundry --ignore-compile --sarif results.sarif --config-file slither.config.json .

# Common tasks
profile ?=default
# Honor an inherited FOUNDRY_PROFILE (e.g. exported by .husky/pre-commit when the
# Seismic toolchain is detected). Falls back to "default" when neither is set.
profile ?= $(if $(FOUNDRY_PROFILE),$(FOUNDRY_PROFILE),default)

build:
@./build.sh -p production
Expand Down
31 changes: 31 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,37 @@ lint_on_build = false
build_info = true
sizes = true

# Seismic profile — builds the MYieldToOne implementation with ssolc (currently 0.8.31).
# `evm_version = "mercury"` is the Seismic EVM revision that supports shielded types
# (suint, sbool, saddress, sbytes). The default profile keeps "cancun" + solc 0.8.26
# for the rest of the suite so audit-reproducibility of sibling extensions is preserved.
# Activate with: `FOUNDRY_PROFILE=seismic sforge build`
#
# `no_match_path` skips two flavors of out-of-scope tests under the Seismic profile:
#
# (1) The entire `test/integration/` directory. Every integration suite forks
# ETH_MAINNET via `BaseIntegrationTest`, which deploys a shielded MYieldToOne;
# mainnet's JSON-RPC doesn't implement `eth_getFlaggedStorageAt`, so any setUp
# that touches a shielded balance aborts with HTTP 400. Integration coverage
# for this profile lives on Seismic devnet, not locally.
#
# (2) `test/unit/projects/JMIExtension.t.sol` — JMIExtension inherits MYieldToOne.
# At `optimizer_runs = 800` (see below) the contract is 23 240 bytes, within the
# EIP-170 limit (24 576); it overflows to 25 853 at the default 19 999 runs. The
# suite is still skipped here because it was written against the unshielded
# `balanceOf` surface, so it trips `Unauthorized()` on the new gated read. JMI is
# not a Seismic deployment target; its unit tests live under the default profile
# where the unshielded path still works.
[profile.seismic]
evm_version = "mercury"
# `optimizer_runs = 800` overrides the default profile's 19 999. The shielded
# MYieldToOne closure pushes JMIExtension to 25 853 bytes at 19 999 runs — 1 277 over
# the EIP-170 24 576-byte limit. 800 is the knee of the size/gas curve: it brings the
# contract to 23 240 bytes (+1 336 headroom) while keeping as much runtime-gas
# optimization as the size limit allows. Lower runs -> smaller code, higher per-call gas.
optimizer_runs = 800
no_match_path = "test/{integration/**,unit/projects/JMIExtension.t.sol}"

[fuzz]
runs = 5_000

Expand Down
2 changes: 1 addition & 1 deletion lib/common
2 changes: 1 addition & 1 deletion script/Config.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
pragma solidity ^0.8.26;

contract Config {
error UnsupportedChain(uint256 chainId);
Expand Down
2 changes: 1 addition & 1 deletion script/ProposeTransferSwapFacilityOwner.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { console } from "../lib/forge-std/src/console.sol";
import { AccessControl } from "../lib/common/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/access/AccessControl.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/ScriptBase.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

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

Expand Down
2 changes: 1 addition & 1 deletion script/deploy/DeployBase.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { DeployHelpers } from "../../lib/common/script/deploy/DeployHelpers.sol";

Expand Down
2 changes: 1 addition & 1 deletion script/deploy/DeployJMIExtension.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { DeployBase } from "./DeployBase.s.sol";
import { console } from "forge-std/console.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/deploy/DeployMEarnerManager.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { DeployBase } from "./DeployBase.s.sol";
import { console } from "forge-std/console.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/deploy/DeploySwapAdapter.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { DeployBase } from "./DeployBase.s.sol";
import { console } from "forge-std/console.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/deploy/DeploySwapFacility.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { DeployBase } from "./DeployBase.s.sol";
import { console } from "forge-std/console.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/deploy/DeployYieldToAllWithFee.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { DeployBase } from "./DeployBase.s.sol";
import { console } from "forge-std/console.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/deploy/DeployYieldToOne.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { DeployBase } from "./DeployBase.s.sol";
import { console } from "forge-std/console.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/deploy/DeployYieldToOneForcedTransfer.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { DeployBase } from "./DeployBase.s.sol";
import { console } from "forge-std/console.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/deploy/MainnetDeploymentSim.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { DeployBase } from "./DeployBase.s.sol";
import { console } from "forge-std/console.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/deploy/interfaces/ICreateXLike.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

/**
* @title CreateX Factory Interface Definition
Expand Down
2 changes: 1 addition & 1 deletion script/upgrade/ExecuteTimelockSwapFacilityUpgrade.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { IProxyAdmin } from "../../lib/openzeppelin-foundry-upgrades/src/internal/interfaces/IProxyAdmin.sol";
import { Upgrades } from "../../lib/openzeppelin-foundry-upgrades/src/Upgrades.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/upgrade/ProposeSwapFacilityUpgrade.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { ProposeUpgradeBase } from "./ProposeUpgradeBase.sol";

Expand Down
2 changes: 1 addition & 1 deletion script/upgrade/ProposeTimelockSwapFacilityUpgrade.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { ProposeTimelockUpgradeBase } from "./ProposeTimelockUpgradeBase.sol";

Expand Down
2 changes: 1 addition & 1 deletion script/upgrade/ProposeTimelockUpgradeBase.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { console } from "../../lib/forge-std/src/console.sol";

Expand Down
2 changes: 1 addition & 1 deletion script/upgrade/ProposeUpgradeBase.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { console } from "../../lib/forge-std/src/console.sol";

Expand Down
2 changes: 1 addition & 1 deletion script/upgrade/UpgradeBase.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { SwapFacility } from "../../src/swap/SwapFacility.sol";
import { JMIExtension } from "../../src/projects/jmi/JMIExtension.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/upgrade/UpgradeJMIExtension.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { UpgradeBase } from "./UpgradeBase.sol";

Expand Down
2 changes: 1 addition & 1 deletion script/upgrade/UpgradeOldSwapFacility.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { UpgradeBase } from "./UpgradeBase.sol";

Expand Down
2 changes: 1 addition & 1 deletion script/upgrade/UpgradeSwapFacility.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { UpgradeBase } from "./UpgradeBase.sol";

Expand Down
61 changes: 61 additions & 0 deletions scripts/seismic-env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# Seismic toolchain environment — sources sforge / ssolc / sanvil from a
# repo-local install at .seismic-toolchain/ (git-ignored).
#
# USAGE
# From the repo root in any bash/zsh shell:
# source scripts/seismic-env.sh
# After sourcing, `sforge`, `ssolc`, and `sanvil` resolve to the repo-local
# binaries. The export survives until the shell exits.
#
# FIRST-TIME INSTALL
# 1. source scripts/seismic-env.sh
# 2. curl -L -H "Accept: application/vnd.github.v3.raw" \
# "https://api.github.com/repos/SeismicSystems/seismic-foundry/contents/sfoundryup/install?ref=seismic" | bash
# The installer writes sfoundryup to $FOUNDRY_DIR/bin/sfoundryup.
# 3. sfoundryup
# Fetches sforge / ssolc / sanvil into $FOUNDRY_DIR/bin.
# 4. sforge --version && ssolc --version && sanvil --version
#
# NOTES
# - FOUNDRY_DIR is the env var the Seismic installer honors. Pointing it at
# the repo-local path overrides the default of "$HOME/.seismic".
# - Pre-prepending $FOUNDRY_DIR/bin to PATH causes the installer's rc-edit
# check to find the bin dir already on PATH and skip mutating ~/.zshenv
# on most versions of the upstream script. If a future installer revision
# still appends an export line, remove it from ~/.zshenv by hand — the
# binaries continue to work via this sourced env.
# - Coexists with stock Foundry: `forge` (stock) and `sforge` (Seismic) live
# in different bin dirs and have different names — no collisions.

# Resolve repo root from this script's own path (works whether sourced from
# repo root, from a subdir, or from $PATH after `chmod +x`).
__SEISMIC_ENV_SH_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
SEISMIC_REPO_ROOT="$(cd "$__SEISMIC_ENV_SH_DIR/.." && pwd)"

export FOUNDRY_DIR="$SEISMIC_REPO_ROOT/.seismic-toolchain"
export FOUNDRY_BIN_DIR="$FOUNDRY_DIR/bin"

# Only prepend if not already present (avoids growing PATH on repeated sources).
case ":$PATH:" in
*":$FOUNDRY_BIN_DIR:"*) ;;
*) export PATH="$FOUNDRY_BIN_DIR:$PATH" ;;
esac

# Shell function that pins FOUNDRY_PROFILE=seismic for sforge invocations so
# `evm_version = "mercury"` (required for shielded types) is applied without
# the caller having to remember the flag. `forge` (stock) is untouched and
# continues using the default profile + cancun.
# Override per-call with `FOUNDRY_PROFILE=default sforge ...` if needed.
sforge() {
FOUNDRY_PROFILE="${FOUNDRY_PROFILE:-seismic}" command sforge "$@"
}

# Surface a one-line confirmation so the user knows the env is active.
if [ -x "$FOUNDRY_BIN_DIR/sforge" ]; then
echo "seismic-env: ready — $($FOUNDRY_BIN_DIR/sforge --version 2>/dev/null | head -1) (sforge auto-uses FOUNDRY_PROFILE=seismic)"
else
echo "seismic-env: FOUNDRY_DIR=$FOUNDRY_DIR (binaries not yet installed — run sfoundryup)"
fi

unset __SEISMIC_ENV_SH_DIR
4 changes: 2 additions & 2 deletions src/MExtension.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { ERC20ExtendedUpgradeable } from "../lib/common/src/ERC20ExtendedUpgradeable.sol";

Expand Down Expand Up @@ -288,7 +288,7 @@ abstract contract MExtension is IMExtension, ERC20ExtendedUpgradeable {
* @param account Address of an account.
* @param amount Amount to transfer or burn.
*/
function _revertIfInsufficientBalance(address account, uint256 amount) internal view {
function _revertIfInsufficientBalance(address account, uint256 amount) internal view virtual {
uint256 balance = balanceOf(account);

if (balance < amount) revert InsufficientBalance(account, balance, amount);
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IMExtension.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { IERC20Extended } from "../../lib/common/src/interfaces/IERC20Extended.sol";

Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IMTokenLike.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

/**
* @title Subset of M Token interface required for source contracts.
Expand Down
2 changes: 1 addition & 1 deletion src/projects/earnerManager/IMEarnerManager.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

/**
* @title M Extension where Earner Manager whitelists earners and sets fee rates for them.
Expand Down
2 changes: 1 addition & 1 deletion src/projects/earnerManager/MEarnerManager.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { IERC20 } from "../../../lib/common/src/interfaces/IERC20.sol";

Expand Down
2 changes: 1 addition & 1 deletion src/projects/jmi/IJMIExtension.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { IMYieldToOne } from "../yieldToOne/interfaces/IMYieldToOne.sol";

Expand Down
2 changes: 1 addition & 1 deletion src/projects/jmi/JMIExtension.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { IERC20Metadata as IERC20 } from "../../../lib/common/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";

Expand Down
2 changes: 1 addition & 1 deletion src/projects/yieldToAllWithFee/MSpokeYieldFee.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;
pragma solidity ^0.8.26;

import { MYieldFee } from "./MYieldFee.sol";

Expand Down
Loading
Loading