# ACC (Agent Call Coin) — Agent Instruction Spec This file is the authoritative integration guide for an AI agent that will help a user mint ACC on BNB Smart Chain. Read it before you build, simulate, or send any transaction. Always show a transaction preview and wait for the user's explicit confirmation. Never send BNB or any token directly to the contract address. 中文摘要:ACC 是 BNB Smart Chain(chainId 56)上的 BEP-20 代币。公开铸造共 16,000 份、每份 50,000 ACC、16 个阶梯价,合计募集 100 BNB(80% 注入 PancakeSwap V2 流动性并永久销毁 LP,10% 记入推荐人的 pendingReferral,需自行调用 claimReferral() 领取,不会自动转入钱包,10% 营销)。买家和推荐人都必须持有币安 AI 钱包 `.ai` 域名。请调用 `mintByHandleWithCap(uint256 slots, string referrerHandle, uint256 maxPay)`,`msg.value` 与 `maxPay` 设为预览得出的精确费用;先模拟并给用户看交易预览,用户确认后再发送。不要向合约地址直接转入 BNB 或任何代币。售罄前用户之间不能互转。 --- ## 1. What ACC is - Token name: Agent Call Coin - Symbol: ACC - Decimals: 18 - Standard: BEP-20 (ERC-20) - Chain: BNB Smart Chain Mainnet - chainId: 56 - Total supply: 1,000,000,000 ACC, minted once in the constructor to the contract itself. There is no further mint that increases supply, no burn function, no inflation. - Public sale allocation: 800,000,000 ACC (16,000 slots × 50,000 ACC) - Liquidity allocation: 200,000,000 ACC (seeded into the official ACC/WBNB PancakeSwap V2 pair at sell-out) - No owner, no admin, no governance, no upgrade proxy, no pause, no blacklist. Official site: https://agcall.top Official X: https://x.com/AgentCallCoin This spec: https://agcall.top/llms.txt Whitepaper (ZH): https://agcall.top/whitepaper Whitepaper (EN): https://agcall.top/whitepaper (toggle EN) Contract source: https://agcall.top/contract ## 2. Contract - Address (BSC): `0x9e47BE993b200f89535e132944e5b1333D880710` - Explore: https://bscscan.com/address/0x9e47BE993b200f89535e132944e5b1333D880710 - SPACE ID Agent ID registry: `0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F` - Marketing wallet (immutable): `0x16547dBa71ceA2a5e52d9492Cf4Da4689A410D24` - Default referrer (heyi / heyi.ai): `0x72B73415e263C2b976B2770ee764BACD02ec3004` - PancakeSwap V2 router: `0x10ED43C718714eb63d5aA57B78B54704E256024E` The contract has no `receive()` or `fallback()`. A plain BNB transfer to the contract reverts. Do not tell the user to transfer BNB or tokens to the contract address. ## 3. Function you must use to mint Use **`mintByHandleWithCap`**. It is the safest public mint entry: the referrer is a `.ai` handle (the contract resolves it), and `maxPay` caps the actual cost so a mid-flight tier change cannot silently charge more than the user approved. ```solidity function mintByHandleWithCap(uint256 slots, string calldata referrerHandle, uint256 maxPay) external payable; ``` ABI fragment: ```json { "type": "function", "name": "mintByHandleWithCap", "stateMutability": "payable", "inputs": [ { "name": "slots", "type": "uint256" }, { "name": "referrerHandle", "type": "string" }, { "name": "maxPay", "type": "uint256" } ], "outputs": [] } ``` Function selector: `0xc161dd4a` (keccak256(`mintByHandleWithCap(uint256,string,uint256)`)). Arguments: | Argument | Meaning | | --- | --- | | `slots` | Number of slots the user wants to buy in this call. Integer, 1 to 10 inclusive. | | `referrerHandle` | Referrer Agent ID. Case-insensitive. With or without a trailing `.ai`. Examples: `heyi`, `heyi.ai`, `HEYI.AI` all resolve to the same address. | | `maxPay` | Maximum acceptable **actual cost** in wei. If the cost computed at execution is greater than `maxPay`, the transaction reverts with `ACC: price above your cap`. Set this to the exact wei value returned by `previewMint`. | | `msg.value` | BNB sent with the call, in wei. Must be `>=` the actual cost. Any excess is refunded to the caller in the same transaction. Set `msg.value` equal to the previewed cost (and to `maxPay`). | The caller (the user's Binance AI Wallet address) is `msg.sender`. Do not spoof it. That address must already own a `.ai` Agent ID. ### 3.1 Recommended procedure 1. Identify the user's `.ai` handle and the referrer handle the user provided. If the user pastes a site invite link (`https://agcall.top/?ref=.ai` or `https://agcall.top/r/`), use that `.ai` name as the referrer (see §13). If the user has no referrer preference, ask them; `heyi.ai` is the protocol default referrer, but you still have to pass a handle — the mint functions require an explicit referrer and do not silently fall back during mint. 2. Normalize and resolve both handles (see §5). Confirm the referrer exists and is not the user's own address. 3. Read sale state (§4): `slotsMinted`, `stageRemaining`, `refundOpen`, `previewMint(slots, userAddress)`. 4. If `refundOpen()` is true, the 72-hour window has ended unsold and minting is stopped. Do not mint; explain the refund path instead. 5. If `previewMint` returns `filled == 0`, do not mint. Explain whether the sale is sold out, the current tier is gone, or the 200-slot per-address cap is reached. 6. If `filled < slots`, tell the user that this call can only fill `filled` slots (no cross-tier fill) at the current tier price, and that excess payment would be refunded. Ask them to confirm the reduced size. 7. Set `maxPay = cost` and `msg.value = cost` (both in wei), where `cost` is the second return value of `previewMint`. 8. Build `mintByHandleWithCap(slots, referrerHandle, maxPay)` with that `msg.value`. 9. **Simulate / eth_call / estimate gas, then present a full transaction preview** (to, function, arguments, value in BNB and wei, estimated gas, filled slots, ACC to be received = filled × 50,000, 10% referral BNB that will accrue to the referrer). Wait for the user to confirm. 10. Only then send the transaction. After confirmation, report the tx hash and the actual filled amount (a `PartialFill` event means fewer slots were filled than requested). ### 3.2 Other mint functions (do not prefer these) ```solidity function mint(uint256 slots, address referrer) external payable; function mintByHandle(uint256 slots, string calldata referrerHandle) external payable; function mintWithCap(uint256 slots, address referrer, uint256 maxPay) external payable; ``` `mint` and `mintByHandle` have no `maxPay` (the cap is `type(uint256).max`). If the current tier sells out between your quote and inclusion, the next transaction against a new tier would pay the new, higher price rather than revert. `mint` / `mintWithCap` require a referrer **address**, which is easy to get wrong. Prefer `mintByHandleWithCap`. There is no `mint()` with only a slot count. A referrer is always required. ## 4. How to quote the price (read these first) All prices are in wei of BNB. One slot's price depends only on the **current tier**. A single call never crosses a tier boundary. ### 4.1 View functions ```solidity function slotsMinted() external view returns (uint256); function remainingSlots() external view returns (uint256); function stageRemaining() external view returns (uint256); // slots left in the current tier; 0 if sold out function nextSlotNumber() external view returns (uint256); // slotsMinted + 1 (1-based) function refundOpen() external view returns (bool); function saleDeadline() external view returns (uint256); // 0 before the first mint; else firstMintAt + 72 hours function firstMintAt() external view returns (uint64); function slotsOf(address) external view returns (uint256); function paidOf(address) external view returns (uint256); function costOf(uint256 slots, address account) external view returns (uint256); function previewMint(uint256 slots, address account) external view returns (uint256 filled, uint256 cost); function priceOfSlot(uint256 index) external view returns (uint256); // index is 0-based function raiseAmount() external view returns (uint256); // always 100 ether function resolveReferrer(string calldata handle) external view returns (address); // 0x0 if unknown; does not revert ``` ABI fragments: ```json [ { "type": "function", "name": "previewMint", "stateMutability": "view", "inputs": [ { "name": "slots", "type": "uint256" }, { "name": "account", "type": "address" } ], "outputs": [ { "name": "filled", "type": "uint256" }, { "name": "cost", "type": "uint256" } ] }, { "type": "function", "name": "stageRemaining", "stateMutability": "view", "inputs": [], "outputs": [{ "type": "uint256" }] }, { "type": "function", "name": "slotsMinted", "stateMutability": "view", "inputs": [], "outputs": [{ "type": "uint256" }] }, { "type": "function", "name": "slotsOf", "stateMutability": "view", "inputs": [{ "name": "", "type": "address" }], "outputs": [{ "type": "uint256" }] }, { "type": "function", "name": "refundOpen", "stateMutability": "view", "inputs": [], "outputs": [{ "type": "bool" }] }, { "type": "function", "name": "resolveReferrer", "stateMutability": "view", "inputs": [{ "name": "handle", "type": "string" }], "outputs": [{ "type": "address" }] } ] ``` `previewMint(slots, account)` already applies: current-tier remainder, global remainder, and the 200-slot cap of `account`. Use this as the source of truth for `filled` and `cost`. If you cannot pass the account (you should), `previewMint(slots)` uses `msg.sender` — only correct when the calling address is the buyer's wallet (e.g. eth_call with `from` set to the user). `costOf` returns 0 when the account cannot buy any of the requested slots. ### 4.2 Tier prices (BNB per slot) Tiers are 1–16, 1,000 slots each. Implemented as a lookup table in units of 0.00001 BNB (`price = units * 10^13 wei`). | Tier | Slot numbers (1-based) | Units | Price (BNB) | Price (wei) | | --- | --- | --- | --- | --- | | 1 | 1–1000 | 280 | 0.0028 | 2800000000000000 | | 2 | 1001–2000 | 310 | 0.0031 | 3100000000000000 | | 3 | 2001–3000 | 340 | 0.0034 | 3400000000000000 | | 4 | 3001–4000 | 370 | 0.0037 | 3700000000000000 | | 5 | 4001–5000 | 410 | 0.0041 | 4100000000000000 | | 6 | 5001–6000 | 450 | 0.0045 | 4500000000000000 | | 7 | 6001–7000 | 490 | 0.0049 | 4900000000000000 | | 8 | 7001–8000 | 540 | 0.0054 | 5400000000000000 | | 9 | 8001–9000 | 600 | 0.0060 | 6000000000000000 | | 10 | 9001–10000 | 660 | 0.0066 | 6600000000000000 | | 11 | 10001–11000 | 720 | 0.0072 | 7200000000000000 | | 12 | 11001–12000 | 790 | 0.0079 | 7900000000000000 | | 13 | 12001–13000 | 870 | 0.0087 | 8700000000000000 | | 14 | 13001–14000 | 960 | 0.0096 | 9600000000000000 | | 15 | 14001–15000 | 1060 | 0.0106 | 10600000000000000 | | 16 | 15001–16000 | 1150 | 0.0115 | 11500000000000000 | Current 0-based tier index = `floor(slotsMinted / 1000)` while `slotsMinted < 16000`. Cost of a fill = current tier price × filled slots. The 16-tier table sums to exactly 100 BNB. ### 4.3 Why `maxPay` matters A call will not buy across two tiers. But if someone else buys the **rest of the current tier** between your preview and inclusion, your transaction sees the **next** tier and would pay the next (higher) price if you used `mint` / `mintByHandle`. With `mintByHandleWithCap` and `maxPay` set to the previewed cost, that case reverts instead of overcharging. ## 5. Handle format and resolution The contract normalizes handles in `_normalizeHandle`: - ASCII `A–Z` are lowercased. - A trailing `.ai` (lowercase after the step above, i.e. `.ai` / `.AI` / `.Ai`) is stripped. - The result is passed to `IAiDomainRegistry.ownerOfHandle`. - Unknown handles, empty results, and registry failures return the zero address; `resolveReferrer` does **not** revert. The registry itself does **not** normalize and does **not** accept a `.ai` suffix: calling the registry's `ownerOfHandle("heyi.ai")` returns `0x0`. Always normalize before talking to the registry, or simply use the ACC contract's `resolveReferrer(string)`. Reverse lookup (address → handle, no suffix): registry `handleOf(address) returns (string)`. Empty string means none. A valid buyer or referrer must have `aiDomainRegistry.balanceOf(address) > 0`. ## 6. Caps, eligibility, and partial fills Hard rules, all enforced on-chain: - `slots` must be > 0, otherwise `ACC: zero slots`. - `slots` must be ≤ 10 (`MAX_SLOTS_PER_TX`), otherwise `ACC: exceed per-tx cap`. - A single address may buy at most 200 slots over the whole sale (`MAX_SLOTS_PER_ADDRESS`), otherwise `ACC: per-address cap reached`. - Buyer `msg.sender` must hold a `.ai` domain, otherwise `ACC: .ai domain required`. - Referrer is required and cannot be the zero address, the caller, or the ACC contract itself. - Referrer must hold a `.ai` domain, otherwise `ACC: referrer needs .ai domain`. - Self-referral is forbidden: `ACC: no self referral`. - If `refundOpen()` is true: `ACC: sale failed, refund open`. - Handle that does not resolve: `ACC: handle not found` (on the ByHandle functions). - `msg.value < cost`: `ACC: insufficient payment`. - `cost > maxPay`: `ACC: price above your cap`. Partial fills: - The contract fills `min(requested, slots remaining in the current tier, slots remaining in the sale, slots remaining under the caller's 200 cap)`. - It does **not** spill into the next tier in the same call. - The user is charged only for `filled` slots at the current tier price. Excess `msg.value` is refunded (`ExcessRefunded`). - If `filled < slots`, a `PartialFill` event is emitted. - If `filled == 0` and the caller is already at the 200 cap: revert `ACC: per-address cap reached`. If `filled == 0` otherwise: revert `ACC: sold out` (this also covers a fully sold-out sale). ACC received = `filled * 50_000e18`. Proceeds split of the cost (not of `msg.value`): 80% to the LP reserve, 10% accrued to the locked referrer (`pendingReferral` / `referralEarned`), 10% to marketing (`pendingMarketing`). Referral BNB is claimed later via `claimReferral()`; it is not pushed automatically. ## 7. Referral lock - Each address has at most one referrer. It is written once and never changes. - The first successful mint (or `bindReferrer(handle)`) locks the referrer and increments `inviteCount[referrer]`. - If the caller already has a locked referrer, the referrer passed to mint must be that same address, otherwise `ACC: referrer already locked`. - `bindReferrer` is only for addresses that have no referrer yet; it cannot change an existing one. - After sell-out, an open-market trader who never locked a referrer is assigned the default referrer `heyi` (`0x72B73415e263C2b976B2770ee764BACD02ec3004`) on first taxed trade. That fallback does **not** apply to mint — mint always requires an explicit referrer. ## 8. Revert messages | Message | What it means / what to do | | --- | --- | | `ACC: zero slots` | `slots` was 0. Pass 1–10. | | `ACC: exceed per-tx cap` | `slots > 10`. Split into multiple calls. | | `ACC: handle not found` | Referrer handle did not resolve. Check spelling; strip or keep `.ai` is fine for ACC functions, but the name must be registered. | | `ACC: referrer required` | Zero-address referrer. | | `ACC: no self referral` | Buyer and referrer are the same address. Use a different referrer. | | `ACC: bad referrer` | Referrer is the ACC contract itself. | | `ACC: .ai domain required` | The buyer's wallet does not hold a SPACE ID Agent ID (`.ai`). | | `ACC: referrer needs .ai domain` | The resolved referrer no longer / does not hold a `.ai`. | | `ACC: sale failed, refund open` | 72 hours passed after the first mint without a sell-out. Minting has stopped. See §9. | | `ACC: referrer already locked` | This buyer already locked a different referrer. Use the locked one (query `referrerOf(buyer)` / `referrerStats`). | | `ACC: per-address cap reached` | The buyer already holds 200 slots. | | `ACC: sold out` | Nothing fillable in the current tier / sale for this call. Re-read `stageRemaining` and `remainingSlots`. | | `ACC: insufficient payment` | `msg.value` is below the actual cost. Re-quote and resend with the correct value. | | `ACC: price above your cap` | Actual cost exceeded `maxPay`. Re-quote; the tier may have advanced. | | `ACC: refund of excess failed` | Refund of excess `msg.value` failed (caller's callback / receiver issue). | | `ACC: transfers locked until sold out` | A user-to-user transfer was attempted before all 16,000 slots were sold. | | `ACC: refund not open` | `refund()` called before the 72h unsold condition. | | `ACC: nothing to refund` | Caller has no LP credit to refund. | | `ACC: nothing to claim` | No pending referral / marketing BNB. | | `ACC: not marketing wallet` | `claimMarketing` is only for the marketing wallet. | | `ACC: index out of range` | `priceOfSlot` index ≥ 16,000. | ## 9. After the sale: migration, refund, tax - When `slotsMinted` reaches 16,000, the same mint transaction seeds the official PancakeSwap V2 ACC/WBNB pair with 200,000,000 ACC and the 80 BNB LP reserve, mints LP to `0x000000000000000000000000000000000000dEaD`, and sets `migrated` / `lpBurned`. If migration fails, the whole mint reverts and the sale stays open (the 72h refund path remains available). - Opening pool price: 80 BNB / 200,000,000 ACC = 4×10⁻⁷ BNB per ACC, equivalent to 0.02 BNB per 50,000-ACC slot. This is not a promise of market price. - Transfers between users are disabled until sell-out (`transfersEnabled()` is true only when `slotsMinted == 16000`). - After migration, buys and sells against the official pair pay a 1% tax in ACC to the trader's locked referrer (or to heyi if none was locked). Wallet-to-wallet transfers are untaxed. Unofficial pools are not taxed by this contract. - Because sells are fee-on-transfer, DEX front ends and agents must use supporting-fee-on-transfer router methods (for example `swapExactTokensForETHSupportingFeeOnTransferTokens`). Ordinary swap methods may revert. - If 72 hours elapse after `firstMintAt` without a sell-out, `refundOpen()` becomes true, minting stops, and each buyer can call `refund()` to receive 80% of what they paid (their `lpCreditOf`) and return the corresponding ACC. The 10% referral and 10% marketing shares are not refunded. There is no deadline to call `refund()`. ```solidity function refund() external; function claimReferral() external; function transfersEnabled() external view returns (bool); function migrated() external view returns (bool); function lpToken() external view returns (address); ``` ## 10. Safety rules (mandatory) 1. Always simulate and show a transaction preview. Execute only after the user confirms. 2. Never send BNB or any token directly to `0x9e47BE993b200f89535e132944e5b1333D880710`. 3. Never set `msg.value` above what the user approved. Prefer `msg.value == maxPay == preview cost`. 4. Never silently change the referrer, the slot count, or the contract address. 5. Never approve unlimited ACC or set a token allowance unless the user clearly asked for a post-sale swap, and even then request the minimum allowance. 6. Do not use a private key, do not ask the user to paste a seed phrase, and do not redirect them to a look-alike contract. 7. If anything in this file conflicts with the on-chain bytecode, the on-chain contract wins. Re-read the view functions in the same block you quote. ## 11. Quick constants - `TOTAL_SUPPLY` = 1_000_000_000e18 - `MINT_SUPPLY` = 800_000_000e18 - `LP_SUPPLY` = 200_000_000e18 - `TOKENS_PER_SLOT` = 50_000e18 - `TOTAL_SLOTS` = 16_000 - `MAX_SLOTS_PER_TX` = 10 - `MAX_SLOTS_PER_ADDRESS` = 200 - `SLOTS_PER_TIER` = 1_000 - `TIER_COUNT` = 16 - `LP_BPS` = 8_000, `REF_BPS` = 1_000, `MKT_BPS` = 1_000, `BPS` = 10_000 - `TAX_BPS` = 100 (1%) - `REFUND_WINDOW` = 72 hours ## 12. Claiming referral BNB The 10% referral share of each mint is **accrued**, not pushed. In the mint transaction the contract adds `cost * 10%` to `pendingReferral[referrer]` and to `referralEarned[referrer]`. The referrer must later call `claimReferral()` from the address that accrued the rewards (the .ai wallet that was used as referrer). There is no deadline. Claiming does not change `referralEarned` (that figure is lifetime and is what leaderboards should show). ```solidity function claimReferral() external nonReentrant; ``` Function selector: `0xe02f1ebd` (keccak256(`claimReferral()`)). ABI fragment: ```json { "type": "function", "name": "claimReferral", "stateMutability": "nonpayable", "inputs": [], "outputs": [] } ``` Related views: ```json [ { "type": "function", "name": "pendingReferral", "stateMutability": "view", "inputs": [{ "name": "", "type": "address" }], "outputs": [{ "type": "uint256" }] }, { "type": "function", "name": "referralEarned", "stateMutability": "view", "inputs": [{ "name": "", "type": "address" }], "outputs": [{ "type": "uint256" }] }, { "type": "function", "name": "referrerStats", "stateMutability": "view", "inputs": [{ "name": "user", "type": "address" }], "outputs": [ { "name": "ref", "type": "address" }, { "name": "invites", "type": "uint256" }, { "name": "earned", "type": "uint256" }, { "name": "claimed", "type": "uint256" } ] } ] ``` `referrerStats(user).claimed` equals `referralEarned[user] - pendingReferral[user]`. Event: ```solidity event ReferralClaimed(address indexed referrer, uint256 amount); ``` ```json { "type": "event", "name": "ReferralClaimed", "inputs": [ { "name": "referrer", "type": "address", "indexed": true }, { "name": "amount", "type": "uint256", "indexed": false } ] } ``` ### 12.1 Procedure 1. Read `pendingReferral(userAddress)` (wei of BNB). If it is 0, do not send a transaction; the call will revert with `ACC: nothing to claim`. 2. Build `claimReferral()` to `0x9e47BE993b200f89535e132944e5b1333D880710` with **`msg.value = 0`**. Do not send BNB. 3. Simulate, show a preview (function, expected amount, gas), and wait for the user to confirm. 4. After inclusion, `pendingReferral` is zeroed and the full pending amount has been sent to `msg.sender`. Report the tx hash and the amount from the `ReferralClaimed` event. ### 12.2 Revert strings (claim) | Message | What it means / what to do | | --- | --- | | `ACC: nothing to claim` | `pendingReferral[msg.sender]` is 0. Re-read the view; do not retry until it is greater than 0. | | `ACC: claim transfer failed` | The BNB transfer to the caller failed (callback / receiver issue). | `claimMarketing()` is only for the immutable marketing wallet and is not a referral claim. ## 13. Invite links Every registered `.ai` Agent ID is an invite code. The official site accepts these forms (case-insensitive, with or without a trailing `.ai`): - Canonical: `https://agcall.top/?ref=.ai` — example: `https://agcall.top/?ref=heyi.ai` - Short: `https://agcall.top/r/` or `https://agcall.top/r/.ai` — these redirect to the canonical URL Normalization matches the contract's `_normalizeHandle`: lowercase ASCII letters, then strip a trailing `.ai`. Display the name as `name.ai`. Invalid characters or length are ignored (not used as a referrer). **If the user pastes an invite link**, extract the `.ai` name and use it as `referrerHandle` in `mintByHandleWithCap`. Do not ignore the link or silently replace that referrer with another one (including the default `heyi.ai`) unless the user explicitly asks you to. Still resolve the handle, reject self-referral, and if `referrerOf(buyer)` is already set, you must pass that locked referrer instead (see §7).