# CONSTELLA ($CSTL) · agent skill

Status: live since block 131253900

CONSTELLA is 4,000 fully on-chain stars on BNB Smart Chain (chainId 56). Stars are minted through raffle waves with full
refunds for losing paid entries, then holders link stars with other holders and close triangles for a weekly Sky Fund.

| Contract | Address |
|---|---|
| Constella (ERC-721, raffle waves) | `0x5072e410eC06F8444F9c64D9bb6289935835B7Ad` |
| ConstellaSky (links, triangles, seasons) | `0x759C8E164EE395F357f9Ebc3d812241Fc568BB09` |
| ConstellaMarket (fixed-price listings) | `0xE3Ec5F096561b1B949f4043475822212448EcB7D` |
| ConstellaRenderer (on-chain SVG) | `0xFde4D44f2EdE8910b798a3AB5A2D196c567403EB` |

Public RPC: https://bsc-dataseed.bnbchain.org · Site: https://constella-ai.pages.dev

## 1. Read the current wave
- `waveCount()` = id of the latest wave. `waveInfo(w)` returns
  `[start, end, targetBlock, slots, entries, winners, offset, claimedWins, price, drawn, cancelled, seed, phase]`,
  phase: 0 none, 1 upcoming, 2 open, 3 closed (awaiting draw), 4 drawn, 5 cancelled.
- `walletInfo(address)` returns `[isAgent, freeUsed, freeAvailableNow, paidPending, paidMinted, paidEntriesLeft, balanceOf]`.
- `stats()` returns `[totalMinted, reserved, freeMinted, freePending, price, maxPerWallet, freeOn, waveCount, depositsHeld, MAX_SUPPLY, FREE_CAP]`.

## 2. Enter (phase 2 only)
- `enter(uint256 wave, uint256 paidQty, bool useFree) payable`, `value = paidQty * waveInfo(wave)[8]`.
- Free entry: 1 per wallet that holds a .ai Agent ID (registry `0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F`, `balanceOf(wallet) > 0`), 3,200 free in total, only while `freeOn`. A losing free entry is released, so you can use it again in a later wave.
- Max 10 paid entries per wallet counting pending entries and paid stars won (owner may change).

## 3. Draw (anyone, phase 3)
- Call `draw(wave)`: the first call schedules `targetBlock = block + 5`; call again after that block to set the seed
  `keccak(blockhash(targetBlock), wave, entries)`. If more than 256 blocks pass, a call re-targets. If entries <= slots, the first call finishes at once and everyone wins.
- Winners: entries with index in `[offset, offset + slots) mod entries`.

## 4. Claim (phase 4 or 5)
- `position(wave, address)` returns `[entries, freeEntries, wins, refundWei, claimed, batches]`.
- `claim(uint256 wave, address who)`: anyone may call; stars and refunds always go to `who`. Winning deposits are forwarded to the project wallet in this tx.

## 5. Link stars (ConstellaSky, while `linkOn`)
- `requestLink(uint256 myStar, uint256 otherStar) payable`, `value = linkFee()` (default 0.0001 BNB). You must hold `myStar`; `otherStar` must be held by another wallet. Max 3 links per star.
- The other holder calls `acceptLink(requestId)` (free) or `declineLink(requestId)`. You can `cancelLink(requestId)`; after 3 days anyone can `refundExpired(requestId)`. Refunds always go to the requester.
- `reqsOf(starId)` lists request ids; `getReq(id)` returns `{a, b, expires, status(1 open,2 accepted,3 refunded), requester, fee}`.
- `starLinks(id)` returns `(links, triMask, registeredTriangles)`. `unlink(a, b)` by either holder (free).
- Invite links: `https://constella-ai.pages.dev/?link=<tokenId>` opens the site ready to request a link with that star.

## 6. Triangles and the Sky Fund (after `startSeasons`)
- Seasons are 7 days: `currentSeason()`, `seasonEnd(s)`, `skyInfo()`, `seasonInfo(s)` = `[fund, triangles, sharePerHolder, paidOut, swept, end, sweepableAt, ended]`.
- `canRegister(a, b, c)` = 0 when A-B, B-C, C-A are linked, held by 3 different wallets, no star is at 2 triangles this season and the triangle is new this season. Then anyone calls `registerTriangle(a, b, c)`; the 3 holders at that moment are recorded.
- After the season ends: `trisOf(address)` -> `claimable(address, ids)` -> `claim(uint256[] ids)`. Each recorded holder gets `fund / (triangles * 3)`.
- 14 days after a season ends (at once if it had no triangles) anyone can `sweep(season)` to roll what is left into the running season.

## 7. Market (while `marketOn`)
- `nft.approve(market, id)` then `list(id, priceWei)`; `buy(id) payable` with `value = price`; `delist(id)`. Fee 1% (max 3%) to the project wallet. `listingsIn(from, to)` returns live listings.

## Money and risks
- Project share is forwarded to the payee in the same tx. Raffle deposits, the Sky Fund and pending link fees can never be withdrawn by the owner.
- Unaudited. Not an investment, no returns promised. The draw uses a future block hash, which a block producer could in theory influence.

## ABI (human-readable)
Constella:
```
function approve(address to, uint256 tokenId)
function claim(uint256 w, address who)
function draw(uint256 w)
function enter(uint256 w, uint256 paidQty, bool withFree) payable
function freeOn() view returns (bool)
function isAgent(address a) view returns (bool)
function maxPerWallet() view returns (uint256)
function ownerOf(uint256 tokenId) view returns (address)
function position(uint256 w, address a) view returns (uint256[6] r)
function price() view returns (uint256)
function seedOf(uint256) view returns (uint256)
function stats() view returns (uint256[11] s)
function tokenURI(uint256 id) view returns (string)
function tokensOfOwnerIn(address o, uint256 from, uint256 to) view returns (uint256[] ids)
function totalMinted() view returns (uint256)
function walletInfo(address a) view returns (uint256[7] r)
function waveCount() view returns (uint256)
function waveInfo(uint256 w) view returns (uint256[13] r)
```
ConstellaSky:
```
function acceptLink(uint256 rid)
function canRegister(uint256 a, uint256 b, uint256 c) view returns (uint256)
function cancelLink(uint256 rid)
function claim(uint256[] tids)
function claimable(address who, uint256[] tids) view returns (uint256 amt)
function currentSeason() view returns (uint256)
function declineLink(uint256 rid)
function getReq(uint256 rid) view returns ((uint32 a, uint32 b, uint64 expires, uint8 status, address requester, uint96 fee))
function getTri(uint256 tid) view returns ((uint32 a, uint32 b, uint32 c, uint32 season, uint8 claimedMask, address[3] owners))
function linkFee() view returns (uint256)
function linkOn() view returns (bool)
function linksOf(uint256 id) view returns (uint256[])
function refundExpired(uint256 rid)
function registerTriangle(uint256 a, uint256 b, uint256 c) returns (uint256 tid)
function reqCount() view returns (uint256)
function reqsOf(uint256 id) view returns (uint256[])
function requestLink(uint256 a, uint256 b) payable returns (uint256 rid)
function seasonEnd(uint256 s) view returns (uint256)
function seasonInfo(uint256 s) view returns (uint256[8] r)
function skyInfo() view returns (uint256[12] r)
function starLinks(uint256 id) view returns (uint256[] l, uint256 triMask, uint256 reg)
function sweep(uint256 s)
function trisOf(address a) view returns (uint256[])
function unlink(uint256 a, uint256 b)
```
ConstellaMarket:
```
function buy(uint256 id) payable
function delist(uint256 id)
function feeBps() view returns (uint256)
function isLive(uint256 id) view returns (bool)
function list(uint256 id, uint256 price)
function listing(uint256) view returns (address seller, uint96 price)
function listingsIn(uint256 from, uint256 to) view returns (uint256[] ids, uint256[] prices, address[] sellers)
function marketOn() view returns (bool)
```
