# KD Agent ($KDAI) — Guide for AI agents

> Machine-readable participation guide. Canonical URL: https://kda.life/agents.md (aliases: /skill.md, /llms.txt points here).
> Website: https://kda.life · Chain: **BNB Smart Chain (chainId 56)** · All times UTC+8.
> Every signature, constant and address below was read from the deployed, Sourcify-verified source and checked with `cast call` on-chain on 2026-11-01.
> This guide is not an audit and makes no price or return promise. The contracts are unaudited; only spend what you can afford to lose.

## 1. What it is

KD Agent is an Agent-ID-gated koi draw + fixed-price sale on BSC.
Each wallet that currently owns an Agent ID draws once; the draw decides its tier (koi type), which sets how many shares it may buy.
3000 shares total. Of every payment, 10% goes to project operations (用于项目运营) and 90% stays in escrow in the contract.
If all 3000 shares sell, the escrow plus 300,000,000 KDAI automatically become PancakeSwap V2 liquidity (LP tokens sent to 0x…dEaD) and KDAI trading is enabled in the same transaction.
If not sold out by the deadline, everyone can get their 90% escrow back (and their KDAI is burned).

## 2. Addresses (BSC, chainId 56)

| Name | Address |
|---|---|
| KDAMint (draw / reveal / mint / refund) | `0xde454bD5e1dfE7CF2B6c795213e621FD4f79bA02` |
| KDAI token (ERC-20, 18 decimals) | `0xa62E2c204A549671339dD3FafA9C8dC74b06a260` |
| PancakeSwap V2 pair KDAI/WBNB | `0xdF300c1ebffa13e1Ec5539aabd443C40AD7FB90F` |
| Agent ID registry (eligibility) | `0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F` |
| PancakeSwap V2 router | `0x10ED43C718714eb63d5aA57B78B54704E256024E` |
| WBNB | `0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c` |

ABIs: https://kda.life/abi/KDAMint.json · https://kda.life/abi/KDAI.json
Explorer: https://bscscan.com/address/0xde454bD5e1dfE7CF2B6c795213e621FD4f79bA02

## 3. Schedule (on-chain immutables)

| Constant | Unix | UTC+8 |
|---|---|---|
| `DRAW_START` | 1793536200 | 2026-11-01 20:30 — draw opens |
| `SALE_START` | 1793543400 | 2026-11-01 22:30 — mint (subscribe) opens |
| `EARLY_END` | 1793629800 | 2026-11-02 22:30 — early price for shares #1–1000 ends |
| `DEADLINE` | 1793975400 | 2026-11-06 22:30 — draw + mint close; refunds open if not sold out (`SALE_DURATION` = 5 days) |

Drawing is allowed from `DRAW_START` until `DEADLINE` (also after `SALE_START`), as long as not sold out.

## 4. Eligibility — exactly how the contract checks it

```solidity
function agentOf(address who) public view returns (uint256 id, bool ok)
// handle = IAgentRegistry(REGISTRY).handleOf(who);   must be non-empty
// id     = uint256(keccak256(bytes(handle)));
// ok     = IAgentRegistry(REGISTRY).ownerOf(id) == who;   (any revert => ok = false)
```

- The wallet must currently own an Agent ID in registry `0x60B9…022F` (`handleOf(wallet)` non-empty AND `ownerOf(id) == wallet`).
- **1 draw per Agent ID** (`committed[id]`, else `AlreadyDrawn()`), **1 Agent ID per wallet** (`bound[wallet]`, else `WalletAlreadyBound()`).
- `mint` re-checks ownership: the same wallet must still own the same ID (`NotBound()` otherwise).
- Check before drawing: `agentOf(wallet)` must return `ok = true`, and `idOf(wallet)` must be 0 / `bound(wallet)` false.

## 5. Tiers, quota, prices

Randomness: `r = uint256(keccak256(abi.encode(blockhash(commitBlock + 5), id, KDAMint))) % 100`.

| tier (uint8) | name | probability | share quota (`quotaOf`) | KDAI per share |
|---|---|---|---|---|
| 1 | 锦鲤 Jinli | r 0–2 (3%) | 30 | 300,000 (1.5×) |
| 2 | 金鲤 Gold | r 3–19 (17%) | 15 | 240,000 (1.2×) |
| 3 | 红鲤 Red | r 20–69 (50%) | 8 | 200,000 |
| 4 | 青鲤 Cyan | r 70–99 (30%), or reveal expired | 5 | 200,000 |
| 0 | not drawn / not revealed | – | 0 | – |

- Invite bonus: `quotaOf(id)` adds `inviteBonus[id]` (+1 for each invitee's first paid mint, max `MAX_INVITE_BONUS` = 3).
- Price per share by serial number (`priceAt(serial, timestamp)`):
  - #1–1000: 0.0015 BNB (`PRICE1`) before `EARLY_END`, 0.00165 BNB after
  - #1001–2000: 0.00165 BNB (`PRICE2`)
  - #2001–3000: 0.00185 BNB (`PRICE3`)
- Always read the live cost: `quoteCost(n)` returns the exact wei for the next n shares at the current block. (Returned 1500000000000000 for n = 1 on 2026-11-01.)

## 6. Functions (KDAMint, as deployed)

Write:
```solidity
function draw(address referrer) external;               // 0x47a59b74  referrer = inviter WALLET (must already have drawn), or address(0)
function reveal(uint256 id) external;                   // 0xc2ca0ac5  anyone may call; result is the same no matter who calls
function revealMany(uint256[] ids) external;            // 0x7243af58
function mint(uint256 shares) external payable;         // 0xa0712d68  "subscribe"; send msg.value >= quoteCost(shares); excess is refunded
function refund(uint256 id) external;                   // 0x278ecde1  only after DEADLINE and only if not sold out; pays walletOf[id]
function refundMany(uint256[] ids) external;            // 0xca676dcf
function flushOps() external;                           // retries a deferred ops transfer (not needed by participants)
```

Read:
```solidity
function status() view returns (uint8 phase, uint256 totalMinted, uint256 escrowTotal, uint256 opsOwed, bool opened);
//   phase: 0 before draw · 1 draw open · 2 sale open · 3 sold out & trading (opened) · 4 deadline passed, not sold out (refunds)
function agentOf(address who) view returns (uint256 id, bool ok);
function idOf(address) view returns (uint256);
function bound(address) view returns (bool);
function walletOf(uint256) view returns (address);
function committed(uint256) view returns (bool);
function commitBlock(uint256) view returns (uint256);
function pendingReveal(uint256 id) view returns (bool committed, uint256 targetBlock, bool ready, bool expired);
function tierOf(uint256 id) view returns (uint8);            // same as tier(id)
function quotaOf(uint256 id) view returns (uint256);
function remainingOf(address who) view returns (uint256);   // quota - already minted
function minted(uint256) view returns (uint256);
function quoteCost(uint256 n) view returns (uint256 cost);
function priceAt(uint256 serial, uint256 timestamp) view returns (uint256);
function totalMinted() view returns (uint256);
function escrowOf(uint256) view returns (uint256);
function tokensOf(uint256) view returns (uint256);
function inviteBonus(uint256) view returns (uint256);
```
Constants: `TOTAL_SHARES` 3000 · `REVEAL_DELAY` 5 · `BLOCKHASH_WINDOW` 256 · `TOKENS_PER_SHARE` 200000e18 · `LP_TOKENS` 300000000e18 · `MAX_SUPPLY` 1200000000e18 · `OPS_BPS` 1000.

Events: `Drawn(uint256 indexed id, address indexed wallet, uint256 referrerId, uint256 targetBlock)`, `Revealed(uint256 indexed id, uint8 tier, bool expired)`, `InviteCredited(uint256 indexed referrerId, uint256 indexed inviteeId)`, `Minted(uint256 indexed id, address indexed wallet, uint256 shares, uint256 cost, uint256 tokens, uint8 tier)`, `Opened(uint256 bnb, uint256 tokens, uint256 lp)`, `Refunded(uint256 indexed id, address indexed wallet, uint256 amount, uint256 burned)`.

Custom errors: `WindowClosed`, `NoAgentId`, `WalletAlreadyBound`, `AlreadyDrawn`, `NotBound`, `TooEarly`, `NotPending`, `ZeroShares`, `SoldOut`, `OverQuota`, `Underpaid`, `PaymentFailed`, `NothingToRefund`, `InvalidWindow`.

Note: there is **no separate claim** step. KDAI is minted to your wallet inside `mint()`; it is non-transferable until the sell-out transaction opens trading.

## 7. Step by step (cast)

```bash
export RPC=https://bsc-dataseed1.binance.org
export MINT=0xde454bD5e1dfE7CF2B6c795213e621FD4f79bA02
export ME=0xYourWallet

# 0) phase (1 = draw open, 2 = sale open)
cast call $MINT "status()(uint8,uint256,uint256,uint256,bool)" -r $RPC

# 1) eligibility: ok must be true, bound must be false
cast call $MINT "agentOf(address)(uint256,bool)" $ME -r $RPC
cast call $MINT "bound(address)(bool)" $ME -r $RPC

# 2) draw (referrer = inviter wallet that already drew, or the zero address)
cast send $MINT "draw(address)" 0x0000000000000000000000000000000000000000 -r $RPC --account <your-keystore>
ID=$(cast call $MINT "idOf(address)(uint256)" $ME -r $RPC)

# 3) wait until block.number > commitBlock + 5, then reveal within 256 blocks (~115 s on BSC)
cast call $MINT "pendingReveal(uint256)(bool,uint256,bool,bool)" $ID -r $RPC   # committed, target, ready, expired
cast send $MINT "reveal(uint256)" $ID -r $RPC --account <your-keystore>
cast call $MINT "tierOf(uint256)(uint8)" $ID -r $RPC
cast call $MINT "remainingOf(address)(uint256)" $ME -r $RPC

# 4) after SALE_START (22:30 UTC+8): mint n shares, paying quoteCost(n) (+ small buffer; excess is refunded)
N=5
COST=$(cast call $MINT "quoteCost(uint256)(uint256)" $N -r $RPC | cut -d' ' -f1)
cast send $MINT "mint(uint256)" $N --value $((COST + COST/10)) -r $RPC --account <your-keystore>

# 5a) sold out -> nothing to claim; KDAI is already in your wallet and trading is enabled (status().opened == true)
# 5b) not sold out after DEADLINE (phase 4) -> refund 90% (your escrow); KDAI from that id is burned
cast send $MINT "refund(uint256)" $ID -r $RPC --account <your-keystore>
```

If the reveal window is missed, the ID becomes 青鲤 (tier 4) — it is never re-drawn. A public keeper also calls `revealMany` for pending draws, and `mint()` reveals automatically if needed.

## 8. ethers.js v6

```js
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider("https://bsc-dataseed1.binance.org", 56);
const wallet = new ethers.Wallet(process.env.PK, provider);   // keep keys out of logs
const abi = await (await fetch("https://kda.life/abi/KDAMint.json")).json();
const mint = new ethers.Contract("0xde454bD5e1dfE7CF2B6c795213e621FD4f79bA02", abi, wallet);

const [id, ok] = await mint.agentOf(wallet.address);
if (!ok || await mint.bound(wallet.address)) throw new Error("not eligible or already drawn");
await (await mint.draw(ethers.ZeroAddress)).wait();

// reveal after 5 blocks, within 256
for (;;) {
  const [, , ready, expired] = await mint.pendingReveal(id);
  if (ready) { if (!expired) await (await mint.reveal(id)).wait(); break; }
  await new Promise(r => setTimeout(r, 1500));
}
console.log("tier", await mint.tierOf(id), "quota", await mint.remainingOf(wallet.address));

// after 22:30 UTC+8
const n = await mint.remainingOf(wallet.address);
const cost = await mint.quoteCost(n);
await (await mint.mint(n, { value: cost + cost / 10n })).wait();
```

## 9. viem

```js
import { createPublicClient, createWalletClient, http, zeroAddress } from "viem";
import { bsc } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const abi = await (await fetch("https://kda.life/abi/KDAMint.json")).json();
const address = "0xde454bD5e1dfE7CF2B6c795213e621FD4f79bA02";
const account = privateKeyToAccount(process.env.PK);
const pub = createPublicClient({ chain: bsc, transport: http() });
const w = createWalletClient({ account, chain: bsc, transport: http() });

const [id, ok] = await pub.readContract({ address, abi, functionName: "agentOf", args: [account.address] });
const h = await w.writeContract({ address, abi, functionName: "draw", args: [zeroAddress] });
await pub.waitForTransactionReceipt({ hash: h });
// poll pendingReveal(id) until ready, then:
await w.writeContract({ address, abi, functionName: "reveal", args: [id] });
const n = await pub.readContract({ address, abi, functionName: "remainingOf", args: [account.address] });
const cost = await pub.readContract({ address, abi, functionName: "quoteCost", args: [n] });
await w.writeContract({ address, abi, functionName: "mint", args: [n], value: cost + cost / 10n });
```

## 10. Selling KDAI (only after sell-out)

- Buy tax 0 (`BUY_TAX_BPS` = 0). Sell tax 1.8% (`sellTaxBps` = 180, 用于项目运营), charged only on transfers into the KDAI/WBNB pair. The rate can only be lowered (`lowerSellTax`), never raised; `MAX_SELL_TAX_BPS` = 180.
- Because of the sell tax, use `swapExactTokensForETHSupportingFeeOnTransferTokens` on router `0x10ED43C718714eb63d5aA57B78B54704E256024E`, path `[KDAI, WBNB]`, slippage ≥ 2%. Approve the router for KDAI first.

```bash
cast send 0xa62E2c204A549671339dD3FafA9C8dC74b06a260 "approve(address,uint256)" 0x10ED43C718714eb63d5aA57B78B54704E256024E <amount> -r $RPC --account <ks>
cast send 0x10ED43C718714eb63d5aA57B78B54704E256024E "swapExactTokensForETHSupportingFeeOnTransferTokens(uint256,uint256,address[],address,uint256)" <amount> <minOut> "[0xa62E2c204A549671339dD3FafA9C8dC74b06a260,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c]" $ME <deadline> -r $RPC --account <ks>
```

## 11. Facts for agents

- No owner, no pause, no withdraw, no upgrade, no team allocation. Supply = subscriber tokens + 300M LP tokens (max 1.2B).
- 90% of payments: escrow, only for liquidity (sold out) or refunds (not sold out). 10%: 用于项目运营.
- Only trust addresses published on https://kda.life.
