# Muon Architecture

Muon is the off-chain computation layer that provides cryptographically signed data to Symmio. All critical operation in the protocol — opening positions, closing positions, liquidations, deallocations — requires a valid Muon signature proving the current state of prices and unrealized PnL.

### Why Symmio Needs an Oracle

Symmio is a bilateral derivatives protocol. At any moment, the protocol needs to know:

* **Current prices** for all trading symbols
* **Unrealized PnL (uPnL)** for PartyA and PartyB
* **Whether a party is solvent** and can execute their requested action

Calculating uPnL on-chain would require iterating through all open positions and fetching current prices — prohibitively expensive on most chains. Muon solves this by performing calculations off-chain and delivering signed attestations that the contracts can verify cheaply.

### Dual-Layer Signature Verification

Symmio doesn't trust a single signer. Every Muon signature passes through **two independent verification layers** before the contract accepts it.

#### Layer 1: TSS (Threshold Signature Scheme)

Muon operates a distributed network of nodes. When a signature request comes in, multiple nodes independently:

1. Fetch price data from exchanges
2. Calculate the requested values (uPnL, solvency, etc.)
3. Contribute to a threshold signature

The result is a Schnorr signature that proves a threshold of nodes agreed on the data. The contract verifies this against the stored `muonPublicKey` in MuonStorage.

```solidity
// Verification happens in LibMuonV04ClientBase.muonVerify()
// Checks Schnorr signature against muonLayout.muonPublicKey
```

#### Layer 2: Gateway ECDSA

After TSS verification, the contract performs a second check: it recovers the signer from a standard ECDSA signature and verifies it matches the registered `validGateway` address.

```solidity
// In LibMuon.verifyTSSAndGateway()
address gatewayRecovered = hash.recover(gatewaySignature);
require(gatewayRecovered == muonLayout.validGateway, "Invalid gateway");
```

### What Gets Signed

Different operations require different signature types. Each includes specific data fields that get hashed and signed.

#### SingleUpnlSig

Used for: `deallocate()`, `sendQuote()`

Contains:

* `partyA` address
* `nonce` (replay protection)
* `uPnL` value
* `timestamp`
* `chainId`

#### SingleUpnlAndPriceSig

Used for: `sendQuote()` (with price validation)

Contains everything in SingleUpnlSig plus:

* `symbolId`
* `price` for that symbol

#### PairUpnlSig

Used for: `openPosition()`, `fillCloseRequest()`

Contains:

* `partyA` address
* `partyB` address
* `partyA nonce`
* `partyB nonce`
* `partyA uPnL`
* `partyB uPnL`
* `timestamp`
* `chainId`

#### PairUpnlAndPriceSig

Used for: Position operations requiring price verification

Contains everything in PairUpnlSig plus:

* `symbolId`
* `price`

#### LiquidationSig

Used for: `liquidatePartyA()`, `liquidatePartyB()`

Contains:

* Party addresses
* Nonces
* Liquidation prices for all relevant symbols
* Total uPnL
* Symbol IDs array
* `timestamp`
* `chainId`

### Replay Protection via Nonces

Muon signatures include **two distinct identifiers** that serve different purposes:

#### reqId (Muon Request ID)

Each Muon request generates a unique `reqId`. This is included in the signed hash:

```solidity
bytes32 hash = keccak256(
    abi.encodePacked(
        muonLayout.muonAppId,
        upnlSig.reqId,           // <-- Muon request ID
        address(this),
        partyA,
        ...
    )
);
```

The `reqId` serves as request identification within the Muon network&#x20;

#### Contract Nonces (On-Chain State Binding)

The contract maintains nonce counters in AccountStorage:

```solidity
mapping(address => uint256) public partyANonces;
mapping(address => mapping(address => uint256)) public partyBNonces;
```

This nonce is included in the signed hash. The contract then verifies the signature was made with the **current** nonce value:

```solidity
bytes32 hash = keccak256(
    abi.encodePacked(
        ...
        AccountStorage.layout().partyANonces[partyA],  // Current on-chain nonce
        ...
    )
);
```

#### How Replay Protection Works

1. Muon reads current on-chain nonce (e.g., `5`)
2. Muon signs data including that nonce
3. User submits signature to contract
4. Contract verifies signature hash includes current nonce (`5`) ✓
5. State changes → nonce increments to `6`

***

### Signature Expiration

Signatures have a limited validity window defined by `upnlValidTime` in MuonStorage:

```solidity
require(
    block.timestamp <= signature.timestamp + muonLayout.upnlValidTime,
    "Signature expired"
);
```

This prevents old signatures from being used after market conditions have changed significantly.
