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:
| Value | Formula, micro-units |
|---|---|
| Marginal price | totalSupply² / 720_000_000_000_000 |
| Reserve implied by supply | totalSupply³ / 2_160_000_000_000_000_000_000 |
| Market cap, displayed | totalSupply × price / 1_000_000 |
| Liquidity | reserveBalance |
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 claimedShow 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 blockedRemaining 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 volumecurrentCreatorFeeShareBps 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
totalSupplyequalsreserveBalance(), to dust. - Your indexed supply at chain head equals on-chain
totalSupply().