Documentation Index
Fetch the complete documentation index at: https://docs.generalmarket.io/llms.txt
Use this file to discover all available pages before exploring further.
Bridge Architecture
The bridge exists because assets live on one chain and users live on another. Geography, even digital, is a curse. Two chains. Different trust models. Different decimal conventions. The bridge reconciles them while enforcing the one rule that cannot be broken: no DTF shares are minted without confirmed collateral. Everything else is negotiable. This is not.Chain Overview
+=========================================+ +=========================================+
| SETTLEMENT (Arbitrum) | | INDEX L3 (Orbit) |
| Chain ID: 14601 | | Chain ID: 111222333 |
| USDC: 6 decimals | | USDC: 18 decimals |
| | | |
| Users deposit/withdraw here | | Core protocol logic lives here |
| BridgedITP tokens (ERC-20 replicas) | | ITP shares, NAV, order matching |
| AP receives USDC for asset purchases | | BLS consensus, batch/fill lifecycle |
+=========================================+ +=========================================+
Contract Layout
Five contracts. Three on Settlement, two on L3. Each holds custody of something the others need. SETTLEMENT (Chain 14601) INDEX L3 (Chain 111222333)
+-----------------------------------------+ +-----------------------------------------+
| | | |
| +-----------------------------------+ | | +-----------------------------------+ |
| | SettlementBridgeCustody.sol | | | | Index.sol | |
| | (UUPS Proxy) | | | | (Core protocol) | |
| | | | | | | |
| | - Holds user USDC for buy orders | | | | - submitOrder / confirmBatch | |
| | - Escrows BridgedITP for sells | | | | - confirmFills (mints/burns) | |
| | - completeBuyOrder (releases USDC) | | | | - NAV computation | |
| | - completeSellOrder (pays user) | | | +-----------------------------------+ |
| | - refundBuyOrder / refundSellOrder | | | |
| +-----------------------------------+ | | +-----------------------------------+ |
| | | | L3BridgeCustody.sol | |
| +-----------------------------------+ | | | (UUPS Proxy) | |
| | BridgeProxy.sol | | | | | |
| | (UUPS Proxy) | | | | - Locks L3 USDC for bridging | |
| | | | | | - Two-phase commit (lock/release) | |
| | - mintBridgedShares (BLS-gated) | | | | - 1-hour timeout for reversals | |
| | - burnBridgedShares (BLS-gated) | | | +-----------------------------------+ |
| | - ITP creation relay | | | |
| | - Replay protection (orderId map) | | +-----------------------------------------+
| +-----------------------------------+ |
| |
| +-----------------------------------+ |
| | BridgedItpFactory.sol | |
| | | |
| | - CREATE2 deploys BridgedITP | |
| | - One BridgedITP per L3 ITP | |
| +-----------------------------------+ |
| |
| +-----------------------------------+ |
| | BridgedITP.sol (one per ITP) | |
| | | |
| | - ERC-20 replica of L3 ITP shares | |
| | - mint/burn only by BridgeProxy | |
| | - 18 decimals | |
| +-----------------------------------+ |
| |
+-----------------------------------------+
All bridge contracts use the UUPS proxy pattern (OpenZeppelin 5.x). Every state-changing bridge operation requires a valid aggregated BLS signature from the oracle nodes — there are no admin bypasses.
Decimal Conversion
Six decimals on one side. Eighteen on the other. The bridge converts at the boundary. Get this wrong and users receive 1e12x too much or too little. User deposits 100 USDC on Settlement
====================================
Settlement (6 dec): 100_000_000 (100 * 1e6)
|
DecimalLib.toInternal()
|
v
L3 internal (18 dec): 100_000_000_000_000_000_000 (100 * 1e18)
USDC released back to AP on Settlement
=======================================
L3 internal (18 dec): 100_000_000_000_000_000_000
|
DecimalLib.toUsdc()
|
v
Settlement (6 dec): 100_000_000 (100 * 1e6)
Never assume USDC is 6 decimals everywhere. L3 uses 18-decimal USDC. Mixing up decimals in bridge logic would mean users receive 1e12x too much or too little.
Bridge Buy Flow
Ten steps. Four BLS consensus rounds. Two chains. One invariant: USDC must be released to the AP before shares are minted. Always. SETTLEMENT (6-dec USDC) INDEX L3 (18-dec USDC)
======================= ======================
1. User calls buyITPFromSettlement()
on SettlementBridgeCustody
|
| USDC transferred from user
| to SettlementBridgeCustody
| (escrowed)
|
v
2. Event: CrossChainOrderCreated
(orderId, itpId, user, amount)
|
| 3. Oracles detect event on Settlement
| |
| v
| 4. Oracles submit order to Index.sol
| (BLS consensus round #1)
| |
| v
| 5. Oracles batch the order
| (BLS consensus round #2)
| |
| v
| 6. AP executes trade on Bitget
| (buys underlying assets)
| |
| v
| 7. Oracles confirm fill on L3
| (BLS consensus round #3)
| |
| +---> ITP shares minted to user on L3
| (Index.sol)
|
| <<<< CRITICAL CHECKPOINT >>>>
|
8. Oracles call completeBuyOrder()
on SettlementBridgeCustody
(BLS consensus round #4)
|
| - Verifies BLS signature
| - Stores PendingMint for crash recovery
| - Releases USDC to AP vault
| - Deletes order from storage
|
v
9. Oracles call mintBridgedShares()
on BridgeProxy
(BLS-verified)
|
| - BridgeProxy calls BridgedITP.mint()
| - User receives BridgedITP on Settlement
| - Replay protection: orderId marked processed
|
v
10. User holds both:
- ITP shares on L3
- BridgedITP (ERC-20 replica) on Settlement
The Backing Invariant:
completeBuyOrder on Settlement MUST succeed BEFORE mintBridgedShares is called. If completeBuyOrder fails (gas issue, revert, timeout), the entire order is rolled back via refundBuyOrder — USDC is returned to the user and no shares are minted anywhere. There is no optimistic minting.Crash Recovery
Machines crash. The question is not whether, but when. ThependingMints mapping in SettlementBridgeCustody is the recovery anchor. On restart, oracles query it to find orders where USDC was released but BridgedITP was not yet minted, then resume.
completeBuyOrder succeeds
|
| pendingMints[orderId] = { itpId, user, amount }
|
v
mintBridgedShares called?
|
+----+----+
| |
YES NO (crash)
| |
v v
clearPendingMint() Oracle restarts, queries pendingMints,
(only if BridgeProxy retries mintBridgedShares
confirms mint done)
Bridge Sell Flow
The reverse. Burn the tokens, return the USDC. Same number of steps, same number of consensus rounds, same refusal to compromise on backing. SETTLEMENT (6-dec USDC) INDEX L3 (18-dec USDC)
======================= ======================
1. User calls sellITPFromSettlement()
on SettlementBridgeCustody
|
| BridgedITP transferred from user
| to SettlementBridgeCustody
| (escrowed)
|
v
2. Event: CrossChainSellOrderCreated
(orderId, itpId, user, amount)
|
| 3. Oracles detect event on Settlement
| |
| v
| 4. Oracles submit sell order to Index.sol
| (BLS consensus round #1)
| |
| v
| 5. Oracles batch the order
| (BLS consensus round #2)
| |
| v
| 6. AP sells underlying assets on Bitget
| |
| v
| 7. Oracles confirm fill on L3
| (BLS consensus round #3)
| |
| +---> ITP shares burned on L3
| USDC released to L3BridgeCustody
|
8. Oracles call burnSellOrderShares()
on SettlementBridgeCustody
|
| - Burns escrowed BridgedITP via BridgeProxy
| - Marks order as burned
|
v
9. Oracles call completeSellOrder()
on SettlementBridgeCustody
(BLS consensus round #4)
|
| - Verifies BLS signature
| - Transfers USDC proceeds from AP vault to user
| - Deletes sell order from storage
|
v
10. User receives USDC on Settlement
BridgedITP destroyed
L3 shares burned
Failed Sell Recovery
IfBridgedITP is burned but the L3 fill never completes, the user’s tokens are gone with no USDC to show for it. This cannot stand. A safety valve exists:
burnSellOrderShares() succeeds
BridgedITP is destroyed
|
v
L3 fill fails or times out?
|
+----+----+
| |
NO YES
| |
v v
Normal remintAndRefundFailedSell()
completion (BLS-gated, only after MIN_REMINT_DELAY = 1 hour)
|
v
BridgedITP re-minted to user
Sell order deleted
The Backing Invariant
The single most important property of the entire protocol. Not the most interesting. Not the most clever. The most important. Every minted DTF share must be 1:1 backed by underlying assets. Without this, everything else is theater. +-----------------------------------------------------------------------+
| THE BACKING INVARIANT |
| |
| For every ITP share in existence, the underlying assets MUST |
| have been purchased and confirmed on a real exchange. |
| |
| Minting shares without backing = protocol insolvency. |
| This is the WORST possible failure mode. |
+-----------------------------------------------------------------------+
HOW THE BRIDGE ENFORCES IT:
+-----------+ +-----------+ +-----------+ +-----------+
| User | | USDC | | AP | | Shares |
| deposits |---->| escrowed |---->| buys |---->| minted |
| USDC | | in | | assets | | on L3 |
| | | custody | | on | | |
+-----------+ +-----------+ | Bitget | +-----------+
+-----------+
^ |
| |
completeBuyOrder ONLY happens AFTER
releases USDC completeBuyOrder
to AP vault succeeds
| |
+------------ MUST PRECEDE ----------+
WHAT CANNOT HAPPEN:
+-----------+ +-----------+
| User | XXXXXXXXXXXX | Shares |
| deposits |---X----> "optimistic -----X----->| minted |
| USDC | minting" | without |
+-----------+ XXXXXXXXXXXX | backing |
+-----------+
^^^ THIS PATH DOES NOT EXIST ^^^
Never mint DTF shares without confirmed backing. The bridge enforces this by requiring
completeBuyOrder (which releases escrowed USDC to the AP for asset purchases) to succeed before mintBridgedShares is called. If completeBuyOrder reverts, the oracles execute refundBuyOrder instead — USDC goes back to the user, zero shares minted. An unbacked DTF is worse than stuck orders, worse than slow consensus, worse than any other failure mode.Order Lifecycle on the Bridge
BUY ORDER STATES
================
buyITPFromSettlement() completeBuyOrder() mintBridgedShares()
| | |
v v v
+------------+ BLS x3 +---------------+ BLS x1 +-----------+
| ESCROWED |------------->| COMPLETING |------------>| MINTED |
| (USDC in | (submit, | (USDC to AP, | (BridgedITP| (done) |
| custody) | batch, | pending | minted) | |
+------------+ fill) | mint stored)| +-----------+
| +---------------+
| |
v v
+------------+ +---------------+
| REFUNDED | | clearPending |
| (timeout/ | | Mint() |
| failure) | +---------------+
+------------+
SELL ORDER STATES
=================
sellITPFromSettlement() burnSellOrderShares() completeSellOrder()
| | |
v v v
+------------+ BLS x3 +------------+ BLS x1 +-----------+
| ESCROWED |------------>| BURNED |------------>| SETTLED |
| (BridgedITP| (submit, | (BridgedITP| (USDC to | (done) |
| in custody)| batch, | destroyed)| user) | |
+------------+ fill) +------------+ +-----------+
| |
v v
+------------+ +----------------+
| REFUNDED | | remintAndRefund|
| (pre-burn | | FailedSell() |
| only) | | (after 1hr) |
+------------+ +----------------+
Replay Protection
Every operation is recorded so it cannot happen twice. The mappings are the protocol’s memory of what it has already done. BridgeProxy:
mintProcessed[orderId] = true // prevents double-mint of BridgedITP
burnProcessed[orderId] = true // prevents double-burn of BridgedITP
SettlementBridgeCustody:
bridgeCompleted[chainId][nonce] // prevents double-release of USDC
crossChainOrders[orderId] // deleted after completion (CEI pattern)
crossChainSellOrders[orderId] // deleted after completion (CEI pattern)
Timing
Ninety seconds, end to end. Not fast. But correct — and correctness is not negotiable here. Round 1: Submit order to L3 ~1s cycle + tx confirmation
Round 2: Batch the order ~1s cycle + tx confirmation
Round 3: Fill (AP executes trade) ~1s cycle + exchange execution
Round 4: completeBuyOrder + ~1s cycle + tx confirmation
mintBridgedShares
Total: ~1-2 minutes (dominated by exchange execution time)
ceil(2n/3) registered oracle nodes. Patience is the price of integrity.