Skip to main content

Platform Architecture

A plain-English tour of what Primit is, why it lives on Avalanche's C-Chain, and how the parts fit together. Written for a reader who wants the story of what Primit built, not the file tree.


1. Why Avalanche, and why the name says it

Primit was born on Avalanche's C-Chain, and the choice was neither a fashion statement nor a hedge. Every design decision — from the vault that holds user USDC, to the referral network that rewards early signal-boosters, to the yield product that lets idle stablecoin earn, to the low-latency price feed the matcher runs against — was shaped by three Avalanche properties that no rival L1 delivers together:

  • Sub-second finality. Snowman++, the consensus that runs on the C-Chain, finalizes transactions in roughly one second. A perpetuals venue on Ethereum L1 has to pretend blocks are final long before they actually are; a Primit deposit or withdrawal is actually done by the time the user reads the toast in the UI.
  • Native EVM compatibility. Every contract Primit deploys — the Vault, the ReferralStorage, the ReferralRebate distributor, the ERC-1155 Earn product — is byte-for-byte the same Solidity you would write for Ethereum, but running on top of a consensus that clears in 1 s and a fee market that is cheap enough for a real trader to redeposit collateral after a small loss without wincing.
  • Ecosystem locality. Avalanche's Subnet architecture gives Primit a straight path from "shared C-Chain today" to "sovereign Subnet tomorrow" the moment throughput demands it. The codebase is written so that Primit never has to rewrite its business logic to move — the RPC changes, nothing else does.

The very name of the repositoryprimit-avax-backend — is a promise. This is not an EVM-generic exchange that happens to be deployed on Avalanche. It is a perpetuals platform written to sit on Avalanche's specific mix of finality, fees, and tooling, and to lean into it hard.


2. The shape of the platform

At the highest level Primit is a matched pair: a Rust backend that trades at the speed of memory, and a family of Solidity contracts on the Avalanche C-Chain that owns the money.

The backend never touches user funds directly. It cannot. Every USDC a user holds on Primit lives inside a Vault contract on Avalanche, and the only way that USDC changes owner is a transaction signed by the user's own wallet against that Vault. What the backend does is keep the ledger honest: it watches the chain for deposits, replays them into a database, matches orders in nanosecond-scale in-memory books, settles PnL into that same database, and — when the user wants to withdraw — signs a permit that lets the user take their money back out of the Vault. The backend is a market maker's trading brain wired to a decentralized money vault.

That symmetry is the whole product. When people ask "is Primit centralized?", the honest answer is: the matcher is fast because it is centralized, and the money is safe because it isn't. Any competent auditor can watch the Vault contract on Snowtrace and prove exactly how much every user has deposited and withdrawn; no promise from Primit is required. And any trader who has felt the pain of a Layer-1 DEX knows that a matcher running at in-memory speed on Rust + Tokio + Axum feels categorically different from a matcher running as an on-chain smart contract. Primit puts those two things in the same room and lets each do what it is good at.


3. Everything runs on Rust, and the reason is speed

The backend is Rust 2021 on Axum with Tokio, PostgreSQL 16 (extended by TimescaleDB for the k-line hypertable), and an optional Redis layer that the codebase treats as a nice-to-have — pull Redis out and the system keeps running from in-process caches. ethers-rs talks to the Avalanche RPC. serde_json and rust_decimal do the plumbing. Nothing in that stack is exotic; everything in it is chosen because it runs quickly and predictably under load. The trader has no patience for garbage-collection pauses.

The interesting piece is not the choice of Rust — that is table stakes for a serious trading system — but what Rust lets Primit do that a slower runtime cannot. The order book is an in-memory data structure, replayed from PostgreSQL at startup, and every fill is broadcast on a Tokio channel to a persistence worker that writes it back to the database inside a transaction. The whole flow — order in, match, trade event, persistence, position update, user balance mutated — happens in a small number of milliseconds. A trader submitting a market order sees their position appear before they can blink.

Around that hot path sits a small constellation of always-on services. A Keeper loop scans trigger orders (stop-loss, take-profit, trailing-stop) every 500 ms and executes any that have crossed their threshold. A Liquidation service continuously walks every open position and flags the ones whose remaining collateral has slipped below either a hard floor or the maintenance-margin ratio. A Funding-rate service settles perpetual funding every eight hours, clamping the rate to ±1% per interval as a safety valve. A k-line worker rolls trade events into candles across every timeframe from one minute to one month. These are the workers a perp trader takes for granted, and Primit built each one as its own Tokio task with its own cadence, listening to the trade-event broadcast the way an old radio tower listens to airwaves.

The one worker that ties everything back to Avalanche is the blockchain sync worker. Its job is dead simple and its job is mission-critical: read every new block, look for Vault Deposit events, and credit the user's balance in the database. The transaction hash is the idempotency key — an INSERT OR IGNORE guards every write, so replays and reorgs cannot double-credit. When a user deposits USDC, this worker is the one that turns that on-chain reality into a UI-visible balance.


4. A trade, from click to settlement

The clearest way to see the platform is to follow a single order.

A user opens Primit's front-end, connects their wallet, and deposits USDC into the Vault contract on Avalanche's C-Chain. The C-Chain finalizes the transaction in about a second. Within a few blocks — under two seconds end-to-end — Primit's blockchain sync worker sees the Deposit event, writes a row into the deposits table, upserts the user into users, and increments their balances.available. The user reloads and sees their USDC available.

The user chooses a market — say BTC-USDC-perp — sizes an order, and signs it with EIP-712. The backend's auth_middleware verifies the JWT that fronts the API and the EIP-712 layer verifies that the signature matches the exact bytes of the order the user meant to place. If the user has enough balance, the backend freezes the margin (UPDATE balances SET frozen += margin, available -= margin), writes the order to the orders table with status = 'open', and hands it to the matching engine. The matching engine finds a counterparty on the other side of the book, prints a fill, broadcasts a TradeEvent, and returns to listening on its channel.

Downstream, the persistence worker picks up the TradeEvent, opens a database transaction, writes the fill into trades, credits any referrer with a row in referral_earnings, updates the order's status to filled or partially_filled, and — inside the same transaction — kicks off the position update. A pg_advisory_lock scoped to (user, symbol) guarantees that two concurrent fills for the same user on the same market cannot race, and the position service either creates a new row or updates an existing one, netting Long and Short as appropriate. The user's frozen margin gets released back to available, the position is now real, and the WebSocket layer pushes the fill and the position update to every subscriber in real time. The whole cycle, from the moment the SDK's submit call reaches the server to the moment the UI shows a filled position, is measured in low tens of milliseconds.

While the position lives, a mark-price sync worker keeps the reference price fresh; the Funding-rate service accrues funding on every open position every eight hours; the Liquidation service watches remaining collateral tick by tick and executes a full-size decrease if any of three thresholds go red. And when the user is ready to leave, they hit "Withdraw", the backend's WithdrawService signs a permit (EIP-712 against the Vault's domain separator), and the user takes that permit to their wallet and calls Vault.withdraw themselves. The Vault verifies the backend's signature on chain, transfers the USDC, and emits a Withdrawn event. The blockchain sync worker sees that event and debits the ledger. Money back in the wallet, ledger honest, no one in the middle.

That entire loop is deposit → trade → settle → withdraw, and it never once required Primit to hold user funds off-chain. The Vault is the single source of truth on money; the database is the source of truth on positions and orders; every path between the two is idempotent, guarded by an EIP-712 signature or a unique on-chain tx hash, and small enough to reason about.


5. What the on-chain surface looks like

Beyond the Vault, Primit deploys several purpose-built contracts on Avalanche:

  • The Vault owns user USDC and gates deposits and withdrawals behind backend-signed permits.
  • ReferralStorage captures who referred whom on chain, in a form no central party can rewrite; the referral relationship becomes a durable Avalanche fact, not a database row someone could edit.
  • ReferralRebate distributes rebates on chain from the same trades the database tracks; the two must agree, and any auditor can walk Snowtrace and check.
  • EarnContract is an ERC-1155 that turns idle USDC into a yield-bearing position. The design here mirrors Primit's philosophy at large: don't ask the user to trust an off-chain balance; give them an on-chain token that says how much they own.

Every one of those contracts leaned on Avalanche's specific cost structure. On a chain where deploying and calling contracts is prohibitively expensive, splitting logic across multiple purpose-built contracts is a luxury; on Avalanche C-Chain, it is just clean architecture.


6. Concurrency, safety, and the quiet parts of the design

The interesting failure modes in a perpetuals venue are not "the server crashed" — that is easy. The interesting failure modes are things like "two order fills raced and the position went into an impossible state", or "a block reorg replayed the same deposit twice and the user got double credit", or "a trigger order was executed and simultaneously cancelled". Primit's design puts a small number of well-placed guards across those seams:

  • Per-(user, symbol) advisory locks in PostgreSQL on any position mutation. Two fills on the same market for the same user can never reorder.
  • Trade UNIQUE index + database transactions across every persistence path. Double-writes are impossible even if the persistence worker retries.
  • Trigger status CAS: only a row still marked active can be triggered, so an in-flight execute_trigger_order and a user cancel cannot both win.
  • On-chain nonce on every withdraw signature. A permit is spent exactly once even under adversarial retry.
  • Monotonic last_synced_block + tx_hash UNIQUE. Block reorgs cannot double-credit deposits.

None of these are novel individually. Together they are the difference between "a system that works when everyone plays fair" and "a system that works when someone is trying".


7. The bet

The bet Primit is making is simple: perpetuals on Avalanche's C-Chain, with a Rust matcher up front and a family of verifiable contracts holding the money, is a defensible position both technically and commercially. Sub-second finality means users don't wait on their own money. EVM parity means every contract can be read in the same tooling as Ethereum. Low fees mean the cost of trading does not erode the edge. And a matcher written in Rust means the platform can absorb bursts of activity without turning them into lag.

Everything else — the API surface documented on these pages, the referral network, the earn product, the fee tiers, the reward loops — sits on top of that bet. The rest is execution.