From 3db7cfd77dbf0bccddc0a8064b3875c2d215428e Mon Sep 17 00:00:00 2001 From: Robert Leifke Date: Sun, 19 Jul 2026 14:58:06 +0100 Subject: [PATCH 1/2] ops: track Sepolia matching deployment --- deployments/84532/matching.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 deployments/84532/matching.json diff --git a/deployments/84532/matching.json b/deployments/84532/matching.json new file mode 100644 index 0000000..3253b55 --- /dev/null +++ b/deployments/84532/matching.json @@ -0,0 +1,14 @@ +{ + "atomicSigningExecutor": "0xc65640E1f86caf0A22f7343F8667151c77E27EE4", + "auctionUtil": "0x754ac571828376FA1668b4842087D080243544a4", + "deposit": "0xD9490e907b9F2a1E1a9b33aaDB129Bf293790f8e", + "liquidate": "0xB2C763168498CDdF89AD7CC1663ACF7A99D4476A", + "matching": "0x1599636347FD5bA1fBE21D58AfE0b8B9cbe283FF", + "rfq": "0x4dCBaEe090D6E788422d9C2a3682BE8F9f070D3C", + "settlementUtil": "0xEDA153C08E30c5AD779cC7A3e437131dF12CC489", + "subAccountCreator": "0x9C6e4a0b33a3589b898b017015A55eb32eAF8bF9", + "trade": "0x0AAE65AaA66Fe7f54486cDbD007956d3De611990", + "transfer": "0xAF77Ced04c2590bd15e1822443BD753Eb4ff63B5", + "tsaShareHandler": "0x78f00622dd00774Fb9dec93D240FF1314C9B28aF", + "withdrawal": "0xfdDb0D00Df6d1569E46e72D35e7B6CEE4Bb7F9FB" +} \ No newline at end of file From 7e9166d0441d8077fcf5e4cba2e237f40b481b2b Mon Sep 17 00:00:00 2001 From: Robert Leifke Date: Sun, 19 Jul 2026 14:58:06 +0100 Subject: [PATCH 2/2] style: forge fmt (stable), fix fmt config key, pin CI to stable --- .github/workflows/ci.yml | 2 +- foundry.toml | 2 +- src/fx/FXToken.sol | 194 +++++++------ src/modules/RfqModule.sol | 7 +- src/modules/TradeModule.sol | 2 +- src/periphery/LyraAuctionUtils.sol | 7 +- .../BaseOnChainSigningTSA.sol | 10 +- src/tokenizedSubaccounts/CCTSA.sol | 10 +- src/tokenizedSubaccounts/LevBasisTSA.sol | 24 +- src/tokenizedSubaccounts/PPTSA.sol | 4 +- test/DeriveForkUpgradePM2.t.sol | 9 +- test/LyraFork.t.sol | 15 +- test/LyraForkAddAssets.t.sol | 7 +- test/LyraForkAddPerp.t.sol | 3 +- test/LyraForkAddSolOptions.t.sol | 259 +++++++++--------- test/LyraForkUpgrade.t.sol | 21 +- test/LyraForkUpgradeLBTC.t.sol | 22 +- test/MatchingSignature.t.sol | 5 +- test/ReplayAPRFailure.t.sol | 47 ++-- test/SimulateCalldata.t.sol | 82 +++--- test/fx/FXToken.sol | 5 +- test/modules/DatedFuture.t.sol | 161 ++++++++--- test/modules/DatedFutureFork.t.sol | 22 +- test/modules/Deposit.t.sol | 5 +- test/modules/Liquidate.t.sol | 6 +- test/modules/Rfq.t.sol | 6 +- test/modules/Trade.t.sol | 15 +- test/modules/Transfer.t.sol | 4 +- test/periphery/LyraSettlementUtils.t.sol | 7 +- .../PPTSA/PPTSA_Admin.t.sol | 4 +- .../PPTSA/PPTSA_RFQ.t.sol | 4 +- .../PPTSA/PPTSA_Withdraw.t.sol | 6 +- .../utils/CCTSATestUtils.sol | 11 +- .../utils/LBTSATestUtils.sol | 11 +- .../utils/PPTSATestUtils.sol | 11 +- .../utils/TSATestUtils.sol | 22 +- 36 files changed, 547 insertions(+), 485 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc3183d..ddf4839 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: - name: Install Foundry uses: onbjerg/foundry-toolchain@v1 with: - version: nightly + version: stable # - name: Check snapshot # run: forge snapshot --check diff --git a/foundry.toml b/foundry.toml index abd7467..8c014fd 100644 --- a/foundry.toml +++ b/foundry.toml @@ -15,7 +15,7 @@ line_length = 120 tab_width = 2 bracket_spacing = false int_types = 'short' -func_attrs_with_params_multiline = false +multiline_func_header = 'attributes_first' quote_style = 'double' # See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/src/fx/FXToken.sol b/src/fx/FXToken.sol index 7775dcc..d60ca97 100644 --- a/src/fx/FXToken.sol +++ b/src/fx/FXToken.sol @@ -4,105 +4,103 @@ import {AccessControlUpgradeable} from "openzeppelin-upgradeable/access/AccessCo import {ERC20Upgradeable, Initializable} from "openzeppelin-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import {SafeCast} from "openzeppelin/utils/math/SafeCast.sol"; - contract FXToken is Initializable, ERC20Upgradeable, AccessControlUpgradeable { - using SafeCast for uint256; - - bytes32 public constant MINTER_ROLE = keccak256("MINTER"); - bytes32 public constant BLOCK_MANAGER_ROLE = keccak256("BLOCK_MANAGER"); - // keccak256(abi.encode(uint256(keccak256("FxToken")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant FxTokenStorageLocation = 0xfb8997de7bd810675586dece12917931ae29ba246c9d4d120b17fca6e2b68f00; - - - /// @custom:storage-location erc7201:FxToken - struct FxTokenStorage { - uint8 decimals; - mapping(address user => bool blocked) isBlocked; - } - - function _getStorage() internal pure returns (FxTokenStorage storage s) { - bytes32 position = FxTokenStorageLocation; - assembly { - s.slot := position - } - } - - /////////// - // Setup // - /////////// - - constructor() { - _disableInitializers(); - } - - function initialize(string memory _name, string memory _symbol, uint _decimals) external initializer { - __ERC20_init_unchained(_name, _symbol); - _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); - - FxTokenStorage storage s = _getStorage(); - s.decimals = _decimals.toUint8(); + using SafeCast for uint; + + bytes32 public constant MINTER_ROLE = keccak256("MINTER"); + bytes32 public constant BLOCK_MANAGER_ROLE = keccak256("BLOCK_MANAGER"); + // keccak256(abi.encode(uint256(keccak256("FxToken")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant FxTokenStorageLocation = 0xfb8997de7bd810675586dece12917931ae29ba246c9d4d120b17fca6e2b68f00; + + /// @custom:storage-location erc7201:FxToken + struct FxTokenStorage { + uint8 decimals; + mapping(address user => bool blocked) isBlocked; + } + + function _getStorage() internal pure returns (FxTokenStorage storage s) { + bytes32 position = FxTokenStorageLocation; + assembly { + s.slot := position } - - //////////////// - // Block List // - //////////////// - - function setBlocked(address user, bool blocked) public onlyRole(BLOCK_MANAGER_ROLE) { - require(user != address(0), "FxToken: cannot block zero address"); - FxTokenStorage storage s = _getStorage(); - s.isBlocked[user] = blocked; - emit Blocked(user, blocked); - } - - function isBlocked(address user) public view returns (bool) { - return _getStorage().isBlocked[user]; + } + + /////////// + // Setup // + /////////// + + constructor() { + _disableInitializers(); + } + + function initialize(string memory _name, string memory _symbol, uint _decimals) external initializer { + __ERC20_init_unchained(_name, _symbol); + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + + FxTokenStorage storage s = _getStorage(); + s.decimals = _decimals.toUint8(); + } + + //////////////// + // Block List // + //////////////// + + function setBlocked(address user, bool blocked) public onlyRole(BLOCK_MANAGER_ROLE) { + require(user != address(0), "FxToken: cannot block zero address"); + FxTokenStorage storage s = _getStorage(); + s.isBlocked[user] = blocked; + emit Blocked(user, blocked); + } + + function isBlocked(address user) public view returns (bool) { + return _getStorage().isBlocked[user]; + } + + /////////////// + // Mint/Burn // + /////////////// + + function mint(address to, uint amount) public onlyRole(MINTER_ROLE) { + FxTokenStorage storage s = _getStorage(); + require(!s.isBlocked[msg.sender], "FxToken: minter is blocked"); + _mint(to, amount); + } + + function burn(address from, uint amount) public onlyRole(MINTER_ROLE) { + FxTokenStorage storage s = _getStorage(); + require(!s.isBlocked[msg.sender], "FxToken: minter is blocked"); + + if (from == address(0)) { + revert ERC20InvalidSender(address(0)); } - - /////////////// - // Mint/Burn // - /////////////// - - function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) { - FxTokenStorage storage s = _getStorage(); - require(!s.isBlocked[msg.sender], "FxToken: minter is blocked"); - _mint(to, amount); - } - - function burn(address from, uint256 amount) public onlyRole(MINTER_ROLE) { - FxTokenStorage storage s = _getStorage(); - require(!s.isBlocked[msg.sender], "FxToken: minter is blocked"); - - if (from == address(0)) { - revert ERC20InvalidSender(address(0)); - } - // Skip the _update call to avoid checking blocked status - super._update(from, address(0), amount); - } - - ///////////////////// - // ERC20 Overrides // - ///////////////////// - - function _update(address from, address to, uint256 value) internal override { - FxTokenStorage storage s = _getStorage(); - require(!s.isBlocked[from], "FxToken: sender is blocked"); - require(!s.isBlocked[to], "FxToken: recipient is blocked"); - super._update(from, to, value); - } - - function _spendAllowance(address owner, address spender, uint256 value) internal override { - FxTokenStorage storage s = _getStorage(); - require(!s.isBlocked[spender], "FxToken: spender is blocked"); - super._spendAllowance(owner, spender, value); - } - - function decimals() public view virtual override returns (uint8) { - return _getStorage().decimals; - } - - //////////// - // Events // - //////////// - - event Blocked(address indexed user, bool blocked); + // Skip the _update call to avoid checking blocked status + super._update(from, address(0), amount); + } + + ///////////////////// + // ERC20 Overrides // + ///////////////////// + + function _update(address from, address to, uint value) internal override { + FxTokenStorage storage s = _getStorage(); + require(!s.isBlocked[from], "FxToken: sender is blocked"); + require(!s.isBlocked[to], "FxToken: recipient is blocked"); + super._update(from, to, value); + } + + function _spendAllowance(address owner, address spender, uint value) internal override { + FxTokenStorage storage s = _getStorage(); + require(!s.isBlocked[spender], "FxToken: spender is blocked"); + super._spendAllowance(owner, spender, value); + } + + function decimals() public view virtual override returns (uint8) { + return _getStorage().decimals; + } + + //////////// + // Events // + //////////// + + event Blocked(address indexed user, bool blocked); } diff --git a/src/modules/RfqModule.sol b/src/modules/RfqModule.sol index e79408d..b0fd1e5 100644 --- a/src/modules/RfqModule.sol +++ b/src/modules/RfqModule.sol @@ -130,7 +130,7 @@ contract RfqModule is IRfqModule, BaseModule { cashTransfer = perpDelta.multiplyDecimal(tradeData.amount); } else if (isDatedFutureAsset[tradeData.asset]) { cashTransfer = 0; - assetData = bytes32(uint256(tradeData.price)); + assetData = bytes32(uint(tradeData.price)); } else { cashTransfer = tradeData.price.toInt256().multiplyDecimal(tradeData.amount); } @@ -146,10 +146,7 @@ contract RfqModule is IRfqModule, BaseModule { }); matchedOrders[i] = IRfqModule.MatchedOrderData({ - asset: tradeData.asset, - subId: tradeData.subId, - quoteAmt: cashTransfer, - baseAmt: tradeData.amount + asset: tradeData.asset, subId: tradeData.subId, quoteAmt: cashTransfer, baseAmt: tradeData.amount }); } diff --git a/src/modules/TradeModule.sol b/src/modules/TradeModule.sol index 2f26a1d..024c416 100644 --- a/src/modules/TradeModule.sol +++ b/src/modules/TradeModule.sol @@ -223,7 +223,7 @@ contract TradeModule is ITradeModule, BaseModule { amtQuote = perpDelta.multiplyDecimal(int(fillDetails.amountFilled)); } else if (isDatedFutureAsset[matchedOrder.data.asset]) { amtQuote = 0; - assetData = bytes32(uint256(fillDetails.price)); + assetData = bytes32(uint(fillDetails.price)); } else { amtQuote = fillDetails.price.multiplyDecimal(int(fillDetails.amountFilled)); } diff --git a/src/periphery/LyraAuctionUtils.sol b/src/periphery/LyraAuctionUtils.sol index 01ed819..1e6caf1 100644 --- a/src/periphery/LyraAuctionUtils.sol +++ b/src/periphery/LyraAuctionUtils.sol @@ -121,12 +121,7 @@ contract LyraAuctionUtils { function _transferCash(uint fromId, uint toId, uint amount, bytes memory managerData) internal { ISubAccounts.AssetTransfer memory adjustment = ISubAccounts.AssetTransfer({ - fromAcc: fromId, - toAcc: toId, - asset: IAsset(cash), - subId: 0, - amount: amount.toInt256(), - assetData: bytes32(0) + fromAcc: fromId, toAcc: toId, asset: IAsset(cash), subId: 0, amount: amount.toInt256(), assetData: bytes32(0) }); subAccounts.submitTransfer(adjustment, managerData); } diff --git a/src/tokenizedSubaccounts/BaseOnChainSigningTSA.sol b/src/tokenizedSubaccounts/BaseOnChainSigningTSA.sol index 787b638..987ecbf 100644 --- a/src/tokenizedSubaccounts/BaseOnChainSigningTSA.sol +++ b/src/tokenizedSubaccounts/BaseOnChainSigningTSA.sol @@ -124,7 +124,15 @@ abstract contract BaseOnChainSigningTSA is BaseTSA { return bytes4(0); } - function _isValidSignature(bytes32 _hash, bytes memory /* _signature */ ) internal view virtual returns (bool) { + function _isValidSignature( + bytes32 _hash, + bytes memory /* _signature */ + ) + internal + view + virtual + returns (bool) + { BaseSigningTSAStorage storage $ = _getBaseSigningTSAStorage(); return !$.signaturesDisabled && $.signedData[_hash]; diff --git a/src/tokenizedSubaccounts/CCTSA.sol b/src/tokenizedSubaccounts/CCTSA.sol index dacd7fe..60612a3 100644 --- a/src/tokenizedSubaccounts/CCTSA.sol +++ b/src/tokenizedSubaccounts/CCTSA.sol @@ -18,9 +18,7 @@ import {IWithdrawalModule} from "../interfaces/IWithdrawalModule.sol"; import {ITradeModule} from "../interfaces/ITradeModule.sol"; import {IMatching} from "../interfaces/IMatching.sol"; -import { - StandardManager, IStandardManager, IVolFeed, IForwardFeed -} from "v2-core/src/risk-managers/StandardManager.sol"; +import {StandardManager, IStandardManager, IVolFeed, IForwardFeed} from "v2-core/src/risk-managers/StandardManager.sol"; import "./CollateralManagementTSA.sol"; /// @title CoveredCallTSA @@ -147,7 +145,11 @@ contract CoveredCallTSA is CollateralManagementTSA { /////////////////////// // Action Validation // /////////////////////// - function _verifyAction(IMatching.Action memory action, bytes32 actionHash, bytes memory /* extraData */ ) + function _verifyAction( + IMatching.Action memory action, + bytes32 actionHash, + bytes memory /* extraData */ + ) internal virtual override diff --git a/src/tokenizedSubaccounts/LevBasisTSA.sol b/src/tokenizedSubaccounts/LevBasisTSA.sol index 1e80a6d..2b745b3 100644 --- a/src/tokenizedSubaccounts/LevBasisTSA.sol +++ b/src/tokenizedSubaccounts/LevBasisTSA.sol @@ -20,9 +20,7 @@ import {IWithdrawalModule} from "../interfaces/IWithdrawalModule.sol"; import {IMatching} from "../interfaces/IMatching.sol"; import {IWrappedERC20Asset} from "v2-core/src/interfaces/IWrappedERC20Asset.sol"; -import { - StandardManager, IStandardManager, IVolFeed, IForwardFeed -} from "v2-core/src/risk-managers/StandardManager.sol"; +import {StandardManager, IStandardManager, IVolFeed, IForwardFeed} from "v2-core/src/risk-managers/StandardManager.sol"; import {ITradeModule} from "../interfaces/ITradeModule.sol"; import {CollateralManagementTSA} from "./CollateralManagementTSA.sol"; @@ -191,7 +189,11 @@ contract LeveragedBasisTSA is CollateralManagementTSA { /////////////////////// // Action Validation // /////////////////////// - function _verifyAction(IMatching.Action memory action, bytes32 actionHash, bytes memory /*extraData*/ ) + function _verifyAction( + IMatching.Action memory action, + bytes32 actionHash, + bytes memory /*extraData*/ + ) internal virtual override @@ -325,8 +327,9 @@ contract LeveragedBasisTSA is CollateralManagementTSA { } LBTSAStorage storage $ = _getLBTSAStorage(); - uint newUnderlyingBase = - isWithdrawal ? (tradeHelperVars.underlyingBase.toInt256() + amtDelta).toUint256() : tradeHelperVars.underlyingBase; + uint newUnderlyingBase = isWithdrawal + ? (tradeHelperVars.underlyingBase.toInt256() + amtDelta).toUint256() + : tradeHelperVars.underlyingBase; // Leverage uint leverage = tradeHelperVars.baseBalance.divideDecimal(tradeHelperVars.underlyingBase); @@ -339,7 +342,10 @@ contract LeveragedBasisTSA is CollateralManagementTSA { require( _isWithinBounds( - leverage.toInt256(), newLeverage.toInt256(), $.lbParams.leverageFloor.toInt256(), $.lbParams.leverageCeil.toInt256() + leverage.toInt256(), + newLeverage.toInt256(), + $.lbParams.leverageFloor.toInt256(), + $.lbParams.leverageCeil.toInt256() ), LBT_PostTradeLeverageOutOfRange() ); @@ -363,7 +369,9 @@ contract LeveragedBasisTSA is CollateralManagementTSA { return false; } - function _verifyEmaMarkLoss(ITradeModule.TradeData memory tradeData, TradeHelperVars memory tradeHelperVars) internal { + function _verifyEmaMarkLoss(ITradeModule.TradeData memory tradeData, TradeHelperVars memory tradeHelperVars) + internal + { LBTSAStorage storage $ = _getLBTSAStorage(); int priceToCheck = (tradeHelperVars.isBaseTrade ? tradeHelperVars.basePrice : tradeHelperVars.perpPrice).toInt256(); diff --git a/src/tokenizedSubaccounts/PPTSA.sol b/src/tokenizedSubaccounts/PPTSA.sol index 7801516..1faa676 100644 --- a/src/tokenizedSubaccounts/PPTSA.sol +++ b/src/tokenizedSubaccounts/PPTSA.sol @@ -18,9 +18,7 @@ import {IWithdrawalModule} from "../interfaces/IWithdrawalModule.sol"; import {IMatching} from "../interfaces/IMatching.sol"; import {IRfqModule} from "../interfaces/IRfqModule.sol"; -import { - StandardManager, IStandardManager, IVolFeed, IForwardFeed -} from "v2-core/src/risk-managers/StandardManager.sol"; +import {StandardManager, IStandardManager, IVolFeed, IForwardFeed} from "v2-core/src/risk-managers/StandardManager.sol"; import {ITradeModule} from "../interfaces/ITradeModule.sol"; import {CollateralManagementTSA} from "./CollateralManagementTSA.sol"; diff --git a/test/DeriveForkUpgradePM2.t.sol b/test/DeriveForkUpgradePM2.t.sol index 060af86..e2dcbf5 100644 --- a/test/DeriveForkUpgradePM2.t.sol +++ b/test/DeriveForkUpgradePM2.t.sol @@ -50,11 +50,8 @@ contract DeriveForkUpgradePM2 is ForkBase { console.log("- MM:", mm); console.log("- MTM:", mtm); - ProxyAdmin(_getV2CoreContract("ETH_2", "proxyAdmin")).upgradeAndCall( - ITransparentUpgradeableProxy(address(pm2)), - address(implementation), - new bytes(0) - ); + ProxyAdmin(_getV2CoreContract("ETH_2", "proxyAdmin")) + .upgradeAndCall(ITransparentUpgradeableProxy(address(pm2)), address(implementation), new bytes(0)); (mm, mtm, worstScen) = pm2.getMarginAndMtM(53874, false); console.log("Margin after upgrade for", uint(53874)); @@ -74,7 +71,7 @@ contract DeriveForkUpgradePM2 is ForkBase { address[] memory assets = SubAccounts(_getCoreContract("subAccounts")).getUniqueAssets(53874); - for (uint i=0; i