Private beta · test money onlyRequest an invite

IntegrationEnglish

Reading market state

The reads that tell you what a market is right now

The present is read from the chain. History is read from the index.

Price, market cap, liquidity and exit value are functions of supply. Supply is one chain read.

The two reads that give you everything

const [totalSupply, reserveBalance] = await client.multicall({
  contracts: [
    { address: market, abi, functionName: "totalSupply" },
    { address: market, abi, functionName: "reserveBalance" },
  ],
});

Everything else computes locally:

ValueFormula, micro-units
Marginal pricetotalSupply² / 720_000_000_000_000
Reserve implied by supplytotalSupply³ / 2_160_000_000_000_000_000_000
Market cap, displayedtotalSupply × price / 1_000_000
LiquidityreserveBalance

reserveBalance equals the reserve implied by supply, to sub-cent dust that favours the reserve.

Polling. Refresh those two reads every 12 seconds, while the tab is visible, shared across every component on the page. After a trade, refresh immediately. A server-rendered snapshot is the starting value so the page opens full; the chain overwrites it.

Per-wallet reads

balanceOf(account)     // claims held, 6 decimals
positionCost(account)  // net cost basis, what the young-market cap measures
exitValue(account)     // what selling everything returns, fees included
feesOwed(account)      // creator / talent / referrer earnings waiting to be claimed

Show exitValue next to the price. They differ, and the difference grows with position size. exitValue lives on chain for this.

Guards, read before the signature

Every rule the contract enforces is known to the interface before the send, not translated after the refusal. A user who spends gas to discover a rule existed has been failed.

minBuyCost()             // 1 USD minimum reserve cost per buy
maxBuyCost()             // fat-finger ceiling on a single buy
youngReserveThreshold()  // 20,000 USD, the cap applies below this reserve
youngWalletCap()         // 1,000 USD per wallet while young
positionCost(account)    // how much of that cap this wallet has spent
buysPaused()             // buys blocked, sells open
paused()                 // everything blocked

Remaining room for a wallet on a young market:

room = reserveBalance >= youngReserveThreshold
     ? unlimited
     : youngWalletCap - positionCost(account)

Clamp the Max button to that. An order the chain refuses never leaves your interface.

Identity and fee reads

profileId()                    // keccak256 of the catalogue UUID
metadataHash()                 // fingerprint of the profile at market birth
creator()                      // the opener, for life
talent()                       // zero until verified
feeBps()                       // 100 = 1%
currentCreatorFeeShareBps()    // the creator's live share, in bps OF THE FEE
referrerFeeShareBps()          // 2500 = 25% of the fee = 0.25% of volume

currentCreatorFeeShareBps tracks the reserve: creatorFeeShareBps below creatorFeeFullReserve, creatorFeeFloorBps above creatorFeeDecayReserve, linear between them.

Discovering markets

factory.marketByProfile(profileId)      // zero when the profile has no market
factory.predictMarketAddress(profileId) // the address it takes, before it exists
factory.isMarket(address)               // born here?

Ask the chain whether a market exists. A page reloaded seconds after a creation reads "no market here" from a database that has not caught up, and that is how a confirmed payment ends up asking to be paid again.

Invariants worth asserting

  • actualReserveBalance() >= reserveBalance() + totalFeesOwed(). True by construction.
  • Reserve implied by totalSupply equals reserveBalance(), to dust.
  • Your indexed supply at chain head equals on-chain totalSupply().