Midpoint Cross Testnet

chainId 139420 · tMIDX · temporary explorer

Address

Address
0x7d7b5a95823bef99040e25312b114eb654ac0d6d
Type
contract
Balance
0 tMIDX (0 wei)
Nonce
1
Code size
4,667 bytes

Contract

Creator
0x59de9c0c9e9ff2e83d21a44ebd4fa83eb9ac1394
Creation tx
0x27a7e1c50f1d78b649aa5f171403… (block 9,840)

Source code ✓ verified

Contract
FxSettlement
Compiler
solc 0.8.36+commit.8a079791
Optimizer
enabled, 200 runs
Verified
2026-09-22 20:53:44 UTC
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IERC20 {
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

/// @title FxSettlement (v2: with post-trade corrections)
/// @notice Settles an FX fill from the MIDX maker as TWO real ERC-20 transfers -- the base leg and the
///         quote leg -- between the maker wallet and a taker wallet, atomically, and remembers the trade so
///         it can be CORRECTED afterwards. A ledger cannot amend a rate the way FX back offices do, so a
///         correction is a compensating transfer that references the original:
///           * amendPrice: same quantity, new rate => only the quote leg changes, so only the DIFFERENCE moves
///             (whoever underpaid pays it). Re-amendable. The original settlement stays on-chain untouched.
///           * bust: reverses both legs at their current (post-amendment) amounts.
///           * OffMarket: at settle time, if the fill price deviates from the maker mid by more than
///             `offMarketBps`, the trade still settles but is flagged -- mirroring FX practice (identify and
///             approve off-market trades rather than silently accept them).
///         Both wallets pre-approve this contract once; it custodies nothing. Owner-only corrections are a
///         TESTNET simplification: a production version needs the counterparty to consent (dual signature or
///         a dispute window) -- one party re-rating a settled trade unilaterally is what a ledger exists to
///         prevent. Conventions: `side` is the TAKER side (1 = taker buys base, 2 = taker sells base);
///         priceE8/midE8 are rates scaled by 1e8; amounts are in each token's own base units.
contract FxSettlement {
    address public owner;
    address public maker;
    uint256 public settleCount;
    uint256 public offMarketBps = 50;   // flag threshold: |price - mid| in basis points of mid

    enum Status { None, Settled, Amended, Busted }

    /// @dev Calldata input to settle().
    struct Fill {
        bytes32 clOrdId;
        string symbol;        // e.g. "USD/MXN"
        uint8 side;           // taker side: 1 = buy base, 2 = sell base
        address baseToken;
        uint256 baseAmount;   // base token units
        address quoteToken;
        uint256 quoteAmount;  // quote token units
        uint256 priceE8;
        uint256 midE8;        // maker mid at fill time; 0 = unknown (no off-market check)
        address taker;
    }

    /// @dev Stored per settlement so it can be amended or busted later.
    struct Trade {
        bytes32 clOrdId;
        uint8 side;
        Status status;
        address baseToken;
        uint256 baseAmount;
        address quoteToken;
        uint256 quoteAmount;  // current, i.e. after any amendment
        uint256 priceE8;      // current
        address taker;
    }

    mapping(uint256 => Trade) public trades;   // seq => trade
    mapping(bytes32 => uint256) public seqOf;  // clOrdId => seq (FIX ops key on ClOrdID)

    event Settled(
        uint256 indexed seq,
        bytes32 indexed clOrdId,
        string symbol,
        uint8 side,
        address baseToken,
        uint256 baseAmount,
        address quoteToken,
        uint256 quoteAmount,
        uint256 priceE8,
        address taker
    );
    event OffMarket(uint256 indexed seq, bytes32 indexed clOrdId, uint256 priceE8, uint256 midE8, uint256 bps);
    event PriceAmended(
        uint256 indexed seq,
        bytes32 indexed clOrdId,
        uint256 oldPriceE8,
        uint256 newPriceE8,
        uint256 delta,        // quote token units that moved
        address payer,
        string reason
    );
    event Busted(uint256 indexed seq, bytes32 indexed clOrdId, uint256 baseAmount, uint256 quoteAmount, string reason);
    event OffMarketBpsChanged(uint256 previousBps, uint256 newBps);
    event MakerChanged(address indexed previousMaker, address indexed newMaker);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    constructor(address _owner, address _maker) {
        require(_owner != address(0) && _maker != address(0), "zero address");
        owner = _owner;
        maker = _maker;
        emit OwnershipTransferred(address(0), _owner);
        emit MakerChanged(address(0), _maker);
    }

    /// @notice Settle one fill: move the base leg and the quote leg in opposite directions, remember it.
    function settle(Fill calldata f) external onlyOwner {
        require(f.side == 1 || f.side == 2, "bad side");
        require(f.taker != address(0), "zero taker");
        require(seqOf[f.clOrdId] == 0, "duplicate clOrdId");
        settleCount += 1;
        _legs(f.baseToken, f.baseAmount, f.quoteToken, f.quoteAmount, f.side, f.taker, false);
        _store(settleCount, f);
        emit Settled(settleCount, f.clOrdId, f.symbol, f.side, f.baseToken, f.baseAmount,
                     f.quoteToken, f.quoteAmount, f.priceE8, f.taker);
        _flagOffMarket(settleCount, f.clOrdId, f.priceE8, f.midE8);
    }

    /// @notice Re-rate a settled trade: same quantity, new price, so only the quote-leg difference moves.
    function amendPrice(bytes32 clOrdId, uint256 newPriceE8, string calldata reason) external onlyOwner {
        uint256 seq = seqOf[clOrdId];
        Trade storage t = trades[seq];
        require(seq != 0 && t.status != Status.Busted, "not amendable");
        require(newPriceE8 > 0 && newPriceE8 != t.priceE8, "bad price");
        // Same base quantity => the quote amount scales with the rate (integer division: dust <= 1 unit).
        (uint256 delta, address payer) = _reprice(t, t.quoteAmount * newPriceE8 / t.priceE8);
        emit PriceAmended(seq, clOrdId, t.priceE8, newPriceE8, delta, payer, reason);
        t.priceE8 = newPriceE8;
        t.status = Status.Amended;
    }

    /// @notice Cancel a settled trade: both legs go back at their current amounts.
    function bust(bytes32 clOrdId, string calldata reason) external onlyOwner {
        uint256 seq = seqOf[clOrdId];
        Trade storage t = trades[seq];
        require(seq != 0 && t.status != Status.Busted, "not bustable");
        _legs(t.baseToken, t.baseAmount, t.quoteToken, t.quoteAmount, t.side, t.taker, true);
        t.status = Status.Busted;
        emit Busted(seq, clOrdId, t.baseAmount, t.quoteAmount, reason);
    }

    // --- admin ---

    function setOffMarketBps(uint256 bps) external onlyOwner {
        emit OffMarketBpsChanged(offMarketBps, bps);
        offMarketBps = bps;
    }

    function setMaker(address newMaker) external onlyOwner {
        require(newMaker != address(0), "zero maker");
        emit MakerChanged(maker, newMaker);
        maker = newMaker;
    }

    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "zero owner");
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }

    // --- internal ---

    /// @dev Side 1 (taker buys base): base maker->taker, quote taker->maker. Side 2: the opposite.
    ///      `reverse` flips both legs (used by bust).
    function _legs(address baseToken, uint256 baseAmount, address quoteToken, uint256 quoteAmount,
                   uint8 side, address taker, bool reverse) internal {
        bool takerGetsBase = (side == 1) != reverse;
        (address baseFrom, address baseTo) = takerGetsBase ? (maker, taker) : (taker, maker);
        require(IERC20(baseToken).transferFrom(baseFrom, baseTo, baseAmount), "base leg");
        require(IERC20(quoteToken).transferFrom(baseTo, baseFrom, quoteAmount), "quote leg");
    }

    function _store(uint256 seq, Fill calldata f) internal {
        Trade storage t = trades[seq];
        t.clOrdId = f.clOrdId;
        t.side = f.side;
        t.status = Status.Settled;
        t.baseToken = f.baseToken;
        t.baseAmount = f.baseAmount;
        t.quoteToken = f.quoteToken;
        t.quoteAmount = f.quoteAmount;
        t.priceE8 = f.priceE8;
        t.taker = f.taker;
        seqOf[f.clOrdId] = seq;
    }

    /// @dev Moves the quote-leg difference to the new amount. The base BUYER owes the quote: the taker on
    ///      side 1, the maker on side 2. A higher price means the buyer pays more; lower, they get a refund.
    function _reprice(Trade storage t, uint256 newQuote) internal returns (uint256 delta, address payer) {
        (address buyer, address seller) = t.side == 1 ? (t.taker, maker) : (maker, t.taker);
        address payee;
        if (newQuote > t.quoteAmount) {
            delta = newQuote - t.quoteAmount; payer = buyer; payee = seller;
        } else {
            delta = t.quoteAmount - newQuote; payer = seller; payee = buyer;
        }
        if (delta > 0) require(IERC20(t.quoteToken).transferFrom(payer, payee, delta), "amend leg");
        t.quoteAmount = newQuote;
    }

    function _flagOffMarket(uint256 seq, bytes32 clOrdId, uint256 priceE8, uint256 midE8) internal {
        if (midE8 == 0) return;
        uint256 diff = priceE8 > midE8 ? priceE8 - midE8 : midE8 - priceE8;
        uint256 bps = diff * 10000 / midE8;
        if (bps > offMarketBps) emit OffMarket(seq, clOrdId, priceE8, midE8, bps);
    }
}

ABI

[
 {
  "type": "constructor",
  "inputs": [
   {
    "name": "_owner",
    "type": "address",
    "internalType": "address"
   },
   {
    "name": "_maker",
    "type": "address",
    "internalType": "address"
   }
  ],
  "stateMutability": "nonpayable"
 },
 {
  "name": "Busted",
  "type": "event",
  "inputs": [
   {
    "name": "seq",
    "type": "uint256",
    "indexed": true,
    "internalType": "uint256"
   },
   {
    "name": "clOrdId",
    "type": "bytes32",
    "indexed": true,
    "internalType": "bytes32"
   },
   {
    "name": "baseAmount",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   },
   {
    "name": "quoteAmount",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   },
   {
    "name": "reason",
    "type": "string",
    "indexed": false,
    "internalType": "string"
   }
  ],
  "anonymous": false
 },
 {
  "name": "MakerChanged",
  "type": "event",
  "inputs": [
   {
    "name": "previousMaker",
    "type": "address",
    "indexed": true,
    "internalType": "address"
   },
   {
    "name": "newMaker",
    "type": "address",
    "indexed": true,
    "internalType": "address"
   }
  ],
  "anonymous": false
 },
 {
  "name": "OffMarket",
  "type": "event",
  "inputs": [
   {
    "name": "seq",
    "type": "uint256",
    "indexed": true,
    "internalType": "uint256"
   },
   {
    "name": "clOrdId",
    "type": "bytes32",
    "indexed": true,
    "internalType": "bytes32"
   },
   {
    "name": "priceE8",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   },
   {
    "name": "midE8",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   },
   {
    "name": "bps",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   }
  ],
  "anonymous": false
 },
 {
  "name": "OffMarketBpsChanged",
  "type": "event",
  "inputs": [
   {
    "name": "previousBps",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   },
   {
    "name": "newBps",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   }
  ],
  "anonymous": false
 },
 {
  "name": "OwnershipTransferred",
  "type": "event",
  "inputs": [
   {
    "name": "previousOwner",
    "type": "address",
    "indexed": true,
    "internalType": "address"
   },
   {
    "name": "newOwner",
    "type": "address",
    "indexed": true,
    "internalType": "address"
   }
  ],
  "anonymous": false
 },
 {
  "name": "PriceAmended",
  "type": "event",
  "inputs": [
   {
    "name": "seq",
    "type": "uint256",
    "indexed": true,
    "internalType": "uint256"
   },
   {
    "name": "clOrdId",
    "type": "bytes32",
    "indexed": true,
    "internalType": "bytes32"
   },
   {
    "name": "oldPriceE8",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   },
   {
    "name": "newPriceE8",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   },
   {
    "name": "delta",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   },
   {
    "name": "payer",
    "type": "address",
    "indexed": false,
    "internalType": "address"
   },
   {
    "name": "reason",
    "type": "string",
    "indexed": false,
    "internalType": "string"
   }
  ],
  "anonymous": false
 },
 {
  "name": "Settled",
  "type": "event",
  "inputs": [
   {
    "name": "seq",
    "type": "uint256",
    "indexed": true,
    "internalType": "uint256"
   },
   {
    "name": "clOrdId",
    "type": "bytes32",
    "indexed": true,
    "internalType": "bytes32"
   },
   {
    "name": "symbol",
    "type": "string",
    "indexed": false,
    "internalType": "string"
   },
   {
    "name": "side",
    "type": "uint8",
    "indexed": false,
    "internalType": "uint8"
   },
   {
    "name": "baseToken",
    "type": "address",
    "indexed": false,
    "internalType": "address"
   },
   {
    "name": "baseAmount",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   },
   {
    "name": "quoteToken",
    "type": "address",
    "indexed": false,
    "internalType": "address"
   },
   {
    "name": "quoteAmount",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   },
   {
    "name": "priceE8",
    "type": "uint256",
    "indexed": false,
    "internalType": "uint256"
   },
   {
    "name": "taker",
    "type": "address",
    "indexed": false,
    "internalType": "address"
   }
  ],
  "anonymous": false
 },
 {
  "name": "amendPrice",
  "type": "function",
  "inputs": [
   {
    "name": "clOrdId",
    "type": "bytes32",
    "internalType": "bytes32"
   },
   {
    "name": "newPriceE8",
    "type": "uint256",
    "internalType": "uint256"
   },
   {
    "name": "reason",
    "type": "string",
    "internalType": "string"
   }
  ],
  "outputs": [],
  "stateMutability": "nonpayable"
 },
 {
  "name": "bust",
  "type": "function",
  "inputs": [
   {
    "name": "clOrdId",
    "type": "bytes32",
    "internalType": "bytes32"
   },
   {
    "name": "reason",
    "type": "string",
    "internalType": "string"
   }
  ],
  "outputs": [],
  "stateMutability": "nonpayable"
 },
 {
  "name": "maker",
  "type": "function",
  "inputs": [],
  "outputs": [
   {
    "name": "",
    "type": "address",
    "internalType": "address"
   }
  ],
  "stateMutability": "view"
 },
 {
  "name": "offMarketBps",
  "type": "function",
  "inputs": [],
  "outputs": [
   {
    "name": "",
    "type": "uint256",
    "internalType": "uint256"
   }
  ],
  "stateMutability": "view"
 },
 {
  "name": "owner",
  "type": "function",
  "inputs": [],
  "outputs": [
   {
    "name": "",
    "type": "address",
    "internalType": "address"
   }
  ],
  "stateMutability": "view"
 },
 {
  "name": "seqOf",
  "type": "function",
  "inputs": [
   {
    "name": "",
    "type": "bytes32",
    "internalType": "bytes32"
   }
  ],
  "outputs": [
   {
    "name": "",
    "type": "uint256",
    "internalType": "uint256"
   }
  ],
  "stateMutability": "view"
 },
 {
  "name": "setMaker",
  "type": "function",
  "inputs": [
   {
    "name": "newMaker",
    "type": "address",
    "internalType": "address"
   }
  ],
  "outputs": [],
  "stateMutability": "nonpayable"
 },
 {
  "name": "setOffMarketBps",
  "type": "function",
  "inputs": [
   {
    "name": "bps",
    "type": "uint256",
    "internalType": "uint256"
   }
  ],
  "outputs": [],
  "stateMutability": "nonpayable"
 },
 {
  "name": "settle",
  "type": "function",
  "inputs": [
   {
    "name": "f",
    "type": "tuple",
    "components": [
     {
      "name": "clOrdId",
      "type": "bytes32",
      "internalType": "bytes32"
     },
     {
      "name": "symbol",
      "type": "string",
      "internalType": "string"
     },
     {
      "name": "side",
      "type": "uint8",
      "internalType": "uint8"
     },
     {
      "name": "baseToken",
      "type": "address",
      "internalType": "address"
     },
     {
      "name": "baseAmount",
      "type": "uint256",
      "internalType": "uint256"
     },
     {
      "name": "quoteToken",
      "type": "address",
      "internalType": "address"
     },
     {
      "name": "quoteAmount",
      "type": "uint256",
      "internalType": "uint256"
     },
     {
      "name": "priceE8",
      "type": "uint256",
      "internalType": "uint256"
     },
     {
      "name": "midE8",
      "type": "uint256",
      "internalType": "uint256"
     },
     {
      "name": "taker",
      "type": "address",
      "internalType": "address"
     }
    ],
    "internalType": "struct FxSettlement.Fill"
   }
  ],
  "outputs": [],
  "stateMutability": "nonpayable"
 },
 {
  "name": "settleCount",
  "type": "function",
  "inputs": [],
  "outputs": [
   {
    "name": "",
    "type": "uint256",
    "internalType": "uint256"
   }
  ],
  "stateMutability": "view"
 },
 {
  "name": "trades",
  "type": "function",
  "inputs": [
   {
    "name": "",
    "type": "uint256",
    "internalType": "uint256"
   }
  ],
  "outputs": [
   {
    "name": "clOrdId",
    "type": "bytes32",
    "internalType": "bytes32"
   },
   {
    "name": "side",
    "type": "uint8",
    "internalType": "uint8"
   },
   {
    "name": "status",
    "type": "uint8",
    "internalType": "enum FxSettlement.Status"
   },
   {
    "name": "baseToken",
    "type": "address",
    "internalType": "address"
   },
   {
    "name": "baseAmount",
    "type": "uint256",
    "internalType": "uint256"
   },
   {
    "name": "quoteToken",
    "type": "address",
    "internalType": "address"
   },
   {
    "name": "quoteAmount",
    "type": "uint256",
    "internalType": "uint256"
   },
   {
    "name": "priceE8",
    "type": "uint256",
    "internalType": "uint256"
   },
   {
    "name": "taker",
    "type": "address",
    "internalType": "address"
   }
  ],
  "stateMutability": "view"
 },
 {
  "name": "transferOwnership",
  "type": "function",
  "inputs": [
   {
    "name": "newOwner",
    "type": "address",
    "internalType": "address"
   }
  ],
  "outputs": [],
  "stateMutability": "nonpayable"
 }
]

Transactions (6 indexed)

HashBlockAgeFromToValue
0x33a5f27bb8c5f2bee7…98682h 45m ago0x59de9c0c9e…0x7d7b5a9582…0 tMIDX (0 wei)
0x098373de7a73782d89…98672h 45m ago0x59de9c0c9e…0x7d7b5a9582…0 tMIDX (0 wei)
0x16826d4e2352861750…98662h 45m ago0x59de9c0c9e…0x7d7b5a9582…0 tMIDX (0 wei)
0x215fad600a77f30636…98652h 45m ago0x59de9c0c9e…0x7d7b5a9582…0 tMIDX (0 wei)
0x73f0508621291b71e7…98642h 45m ago0x59de9c0c9e…0x7d7b5a9582…0 tMIDX (0 wei)
0x7be6c4985352c21bd6…98632h 45m ago0x59de9c0c9e…0x7d7b5a9582…0 tMIDX (0 wei)
Temporary placeholder infrastructure for chain registration. Not a production network - state may be reset without notice.