Skip to content

Architecture

LootFi has four moving parts. Two are smart contracts on Robinhood Chain and are permissionless once deployed. Two are services the operator runs, and they are the parts you have to trust.

This page draws that line explicitly, then walks the data flow for each of the three things the protocol actually does: take an item in, trade a token, give an item back.

The four boxes

                        ON-CHAIN (chain 4663)
        ┌──────────────────────────┐   ┌──────────────────────────┐
        │  LootSkins  (ERC-721)    │   │  LootMarket              │
        │  ----------------------  │   │  ----------------------  │
        │  • who owns which token  │   │  • verifies EIP-712      │
        │  • mint (minter role)    │   │    orders                │
        │  • deTokenize → burn     │   │  • splits payment        │
        │  • baseURI → tokenURI    │   │  • moves the NFT last    │
        │  • ERC-2981 royalty      │   │  • per-maker nonces      │
        └───────────┬──────────────┘   └────────────┬─────────────┘
                    │      events (logs)            │
        ════════════╪═══════════════════════════════╪═══════════════
                    │       TRUST BOUNDARY          │
        ════════════╪═══════════════════════════════╪═══════════════
                    ▼                               ▼
        ┌──────────────────────────────────────────────────────────┐
        │  Indexer                                                 │
        │  reads logs from a persisted cursor, never trusted for   │
        │  ownership — it only mirrors what the chain already said │
        └───────────┬──────────────────────────────────────────────┘

        ┌───────────▼──────────────┐   ┌──────────────────────────┐
        │  Custody service         │   │  Metadata service        │
        │  ----------------------  │   │  ----------------------  │
        │  • holds the real items  │   │  • serves tokenURI JSON  │
        │    in Steam accounts     │   │  • float / pattern /     │
        │  • sends + receives      │   │    stickers, resolved    │
        │    trade offers          │   │    after the item lands  │
        │  • decides when a        │   │                          │
        │    deposit may mint      │   │                          │
        └──────────────────────────┘   └──────────────────────────┘


              Steam (Valve)

What you must trust, and what you must not

The honest version, stated before anything else on this site tries to sell you on the design.

Requires trusting the operator

ThingWhy there is no trustless version
Custody of the physical itemA CS2 skin lives in Valve's database. No chain can hold one. Somebody has to run the Steam account, and that somebody can be compelled, hacked, or dishonest.
Mint authorityOnly an allow-listed minter can create a token. That role decides that a real item backs a new token. A bad minter can create a token backed by nothing.
Metadata hostingtokenURI points at an HTTP base URI the contract owner sets. The float, pattern and sticker data is served off-chain and can be changed or taken down.
The binding between token and itemThe claim "token 42 is backed by that AK" is an operator assertion, recorded off-chain. The chain does not know what a skin is.

Does not require trusting the operator

ThingWhy
Who owns a tokenERC-721 state on chain. The operator cannot move your token, cannot freeze it, and cannot mint a second copy of an id that already exists.
Transferring a tokenAn ordinary transferFrom. No permission, no allow-list of recipients, no operator involvement.
Settlement of a salefulfillOrder is atomic: the buyer's payment is split and the NFT moves in the same transaction, or the whole thing reverts. The operator is not in the path and never custodies the funds.
Cancelling your own listingcancel(nonce) and setMinimumNonce are called by the maker. Nobody else can cancel for you, and nobody can stop you cancelling.
BurningdeTokenize requires you to be the direct owner. Nobody can burn your token, and the operator cannot prevent you burning it.

The uncomfortable summary: on-chain, LootFi cannot take your token. Off-chain, LootFi custodies your skin. Everything on this site is designed around shrinking the second half, not pretending it does not exist. See Trust & security for the full list of admin powers and their blast radius.

Data flow: deposit

 user            custody service         Steam            LootSkins
  │                    │                   │                  │
  │ pick items ───────►│                   │                  │
  │                    │ send trade offer ►│                  │
  │◄─────────── offer appears in Steam ────│                  │
  │ accept ───────────────────────────────►│                  │
  │                    │◄── offer accepted │                  │
  │                    │                                      │
  │                    │ read the TRADE RECORD:               │
  │                    │  receipt time, escrow, asset identity│
  │                    │                                      │
  │                    │ ── compute unlock (MAX of holds) ──  │
  │                    │                                      │
  │                    │ … Steam's protection window …        │
  │                    │   (item is in NEITHER inventory)     │
  │                    │                                      │
  │                    │ re-verify custody under a lock       │
  │                    │ allocate tokenId                     │
  │                    │ mint(to, tokenId) ──────────────────►│
  │◄──────────── token appears in your wallet ────────────────│

The gating fact: LootFi does not mint when a timer says so. It mints when it can re-prove, at that moment, that the item is still in custody and the trade that delivered it was never rolled back. Details in Deposit lifecycle and Minting.

Data flow: trade

 seller                      buyer                LootMarket      LootSkins
   │                           │                      │               │
   │ approve market (once) ────────────────────────────────────────► │
   │                           │                      │               │
   │ sign Order (EIP-712)      │                      │               │
   │ — no transaction, no gas  │                      │               │
   │ ── order + signature ────►│                      │               │
   │                           │ fulfillOrder(order,  │               │
   │                           │   sig) + payment ───►│               │
   │                           │                      │ validate      │
   │                           │                      │ consume nonce │
   │                           │◄── royalty/fee/────  │               │
   │◄────── proceeds ───────── │    seller split      │               │
   │                           │                      │ transfer ───► │
   │                           │◄──────── NFT ────────────────────────│

Nothing is escrowed. The token stays in the seller's wallet from the moment they sign until the moment somebody fills. A listing is a signature sitting in a database; if that database vanishes, the signature still works and the seller can still cancel on-chain. See Marketplace.

Data flow: withdrawal

 owner            backend            LootSkins        indexer       custody
   │                 │                   │               │             │
   │ request ───────►│                   │               │             │
   │                 │ preflight:        │               │             │
   │                 │  ownerOf == you? ►│               │             │
   │                 │  no live listing? │               │             │
   │                 │  item held? ──────────────────────────────────► │
   │◄── ok, burn enabled                 │               │             │
   │ deTokenize([ids]) ─────────────────►│               │             │
   │                 │                   │ Detokenized ─►│             │
   │                 │◄── burn confirmed ────────────────│             │
   │                 │                   │               │ send return │
   │                 │                   │               │ offer ─────►│
   │◄──────────── trade offer in Steam ──────────────────────────────  │

The preflight exists because a burn is irreversible and a Steam trade is not guaranteed. Everything that could make the return fail is checked before the UI will let you burn. See Withdrawal lifecycle.

Failure posture: fail closed

Every custody-relevant decision in this system refuses when it cannot prove the thing it needs. Concretely:

  • Cannot confirm on-chain ownership (RPC error, revert) → withdrawal preflight returns "could not confirm ownership right now" and the burn stays disabled. It does not fall back to a cached owner.
  • Item's category cannot be read from live Steam tags → the deposit is held for a human instead of guessing. Category is packed into the tokenId and can never be corrected. See Token IDs.
  • Item not where custody expects it at mint time, but the trade record shows no rollback → held for review. Not minted, not destroyed.
  • Trade status says a rollback started but did not complete → held for review. The protocol never auto-destroys a deposit on an ambiguous signal.
  • Two indistinguishable items could satisfy one deposit → refuse to pick. Over-holding costs a person a look; picking wrong hands one depositor another depositor's skin, and is unrecoverable.
  • No ETH/USD rate, or a reference price of zero → no discount figure is shown at all, rather than a confident wrong one. See Reference prices.
  • Indexer handler throws → the block cursor does not advance and the range is replayed. It never skips a log to make progress.

The asymmetry driving all of it: waiting too long costs a user time. Acting too early costs somebody their skin.

Why the chain is authoritative

LootFi's own records mirror the chain and coordinate the Steam side of a deposit or redemption. They are not the record of which tokens exist or who owns them, and treating them as such has a specific, concrete failure mode:

The per-category tokenId sequence counter lives off-chain. If that database is restored from a backup, or rebuilt, the counter rewinds — and it will happily hand out ids that are already minted on chain. So before every mint the minter compares ownerOf(tokenId) against the intended recipient:

  • token does not exist → mint it.
  • token exists and belongs to the intended recipient → this was our own earlier attempt that landed; finalize without sending again.
  • token exists and belongs to anyone else → this is a collision, never a success. The id is released, the counter is fast-forwarded past the occupied range, and the deposit re-allocates.

Reading "the token already exists" as "our earlier attempt succeeded" would mark a deposit complete and credit a depositor with a stranger's NFT while consuming their real skin. The counter only ever moves forward; gaps are never reclaimed.

The same principle applies to ownership. The indexer mirrors Transfer logs into a database so pages load fast, but every decision that matters — can you withdraw, are you the maker of this order — is re-checked against the contract at the moment it matters.

Where to go next

LootFi is not affiliated with, endorsed by, or sponsored by Valve Corporation. Counter-Strike and Steam are trademarks of Valve Corporation.