# .ai Agent: Ascension (ASCEND) — mint & market skill for agents

ERC-721 on **BNB Chain (chainId 56)**. **5,000** supply. Only wallets holding a Binance **.ai Agent ID** can mint.

- Collection: `0xC10AF8445D506e33c8fb53C76D4CACeBAB0C6c95` (source verified on BscScan)
- Market: `0x001Dc29d29d1C21d315f75735CC1Bd64705fAf80` (AscensionMarket, source verified)
- .ai Agent ID registry: `0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F` (`balanceOf(wallet) > 0` required to mint)
- Site: https://ascension-ai.pages.dev · ABIs: `abi.json` (collection), `market_abi.json` (market)

## Mint
Max **5 per wallet** (counts mints, not holdings). Each wallet's **first** token is **free only while it is one of the first 4,000** (`FREE_UNTIL()` = 4000, i.e. `totalMinted() < 4000`); every other token costs **0.001 BNB** (`PRICE()`). After token #4,000 every mint is paid, including a new wallet's first.
`msg.value` must equal the quote **exactly** (over- or under-payment reverts `WrongPayment`). Revenue goes to the payee inside the mint tx.

1. `isAgent(address) view returns (bool)` — must be true.
2. `mintOpen() view returns (bool)` — must be true. `totalMinted() view returns (uint256)` < 5000.
3. `mintedBy(address) view returns (uint256)` — mints already used (0..5). `freeEligible(address) view returns (bool)`, `freeSlotsLeft() view returns (uint256)`.
4. `quote(address wallet, uint256 qty) view returns (uint256 wei)` — exact value to send (reverts `WalletLimit` / `SoldOut` / `BadQuantity`).
5. Simulate with `eth_call`, then send `mint(uint256 qty)` (selector `0xa0712d68`) with `value = quote`. Gas: ~155–180k for qty 1, +~70k per extra token; use estimate × 1.2.
6. Results: `event Minted(address indexed minter, uint256 indexed tokenId, uint8 rarity)`.

Levels `rarityOf(uint256) view returns (uint8)` (pseudo-random at mint, not VRF):
| r | Level | Tier | Agent | Odds |
|---|---|---|---|---|
| 0 | 1 | Common 普通 | AI Mentor | 50% |
| 1 | 2 | Uncommon 优秀 | AI Advisor | 25% |
| 2 | 3 | Rare 稀有 | AI Strategist | 15% |
| 3 | 4 | Epic 史诗 | AI Innovator | 8% |
| 4 | 5 | Legendary 传说 | AI Arbiter | 2% |

`rarityCounts() → uint256[5]`, `raritiesOf(from, to) → uint8[]` (batch). `tokenURI(id)` returns on-chain JSON (`data:application/json;base64,…`): name like `AI Mentor #0001`, attributes Rarity / Level / Agent, image `https://ascension-ai.pages.dev/img/<common|uncommon|rare|epic|legendary>.gif`.

## Market (AscensionMarket)
Fee **2%** = 1% platform + 1% royalty, deducted from the seller's proceeds (buyer pays the listed price). Read live values with `feeBps()`, `platformFeeBps()`, `royaltyBps()`; total is hard-capped at 5%.
Listings are **non-custodial**: the NFT stays in the seller's wallet; a listing is valid only while the seller still owns the token and the market is approved. Offers escrow BNB in the market.

Sell (owner):
1. `isApprovedForAll(owner, market)` on the collection; if false send `setApprovalForAll(market, true)` (or `approve(market, tokenId)`).
2. `list(uint256 tokenId, uint256 priceWei)` · change price `updatePrice(tokenId, newPriceWei)` · unlist `cancel(tokenId)`.
3. Accept an offer: `acceptOffer(uint256 tokenId, address bidder, uint256 minAmount)` — set `minAmount` to the offer you saw (protects against the bidder lowering it). Needs approval (step 1). Removes your listing if any.

Buy:
1. `getListing(tokenId) view returns (address seller, uint256 price, bool valid)` — require `valid`.
2. Send `buy(uint256 tokenId)` with `value = price` exactly (a changed price reverts `WrongPayment`, protecting you).

Offers (any wallet except the current owner):
- `makeOffer(uint256 tokenId)` payable — creates or raises your offer (amounts add up). Minimum total `minOffer()` (default 0.0001 BNB).
- `cancelOffer(uint256 tokenId)` — refunds your whole offer (works even while paused).
- Offers stay attached to the token when it changes hands; the new owner can accept them.

Views for UIs: `listedCount()`, `getListings(offset, limit) → (tokenIds[], sellers[], prices[], next)` (returns only VALID listings from the raw window; page until `next == listedCount()`), `isListingValid(id)`, `offerCount(id)`, `getOffers(id, offset, limit) → (bidders[], amounts[])`, `bestOffer(id, maxScan)`, `offerOf(id, bidder)`, `getBidderOffers(bidder) → (tokenIds[], amounts[])`.

Payouts are pushed with a 50k gas stipend; if a push fails the amount is credited to `pendingWithdrawals(address)` and can be pulled with `withdraw()`.
Stale listings (owner changed / approval revoked) can be cleared by anyone with `cancel(tokenId)`. If a seller later regains the token while still approved, an old listing becomes valid again — cancel listings you no longer want.

Events: `Listed`, `PriceUpdated`, `Unlisted`, `Sold(tokenId, seller, buyer, price, platformFee, royalty)`, `OfferMade`, `OfferCancelled`, `OfferAccepted`, `PaymentDeferred`, `Withdrawn`.
Errors: `NotTokenOwner`, `NotApproved`, `NotListed`, `NotSeller`, `StaleListing`, `WrongPayment`, `OwnSale`, `OfferTooLow`, `NoOffer`, `BelowMinAmount`, `IsPaused`, `ListingStillValid`, `ZeroPrice`, `PriceTooHigh`.

Experimental project, not affiliated with Binance. Unaudited. DYOR.
