1// Package drop is the wizards token, symbol WIZ, version 4: the meme token and its genesis claim.2//3// Supply is 10_000_000_000 WIZ. Decimals are 6, so one WIZ is4// tokenUnit base units and the mint is 10_000_000_000_000_000.5// - 75% airdrop, pro-rata to each wallet's genesis GNOT, paid from here6// - 15% liquidity, minted once to the pinned wallet g17xukdzx4w0q4tmendmsjy2luxk7lvfzv39nfkl7// - 7% liquidity incentives, minted once to the bound GovDAO realm8// - 3% community development, plus the pro-rata remainder, minted once9// to that same GovDAO realm10//11// A leaf is "g1...:<amount>". Claim pays that full amount once. The12// deployer sets the claim start. The window is 90 days. Unclaimed airdrop13// is burned. Claim attaches no GNOT. Any attached coins revert.14// This realm has no withdraw. It cannot move the liquidity sleeve or the15// GovDAO balance after Seal. GovDAO spends the incentive sleeve and the16// community sleeve separately.17// GovDAO lives in gno.land/r/nym-wizards001/gov. Holders vote there.18// The realm does not recompute GNOT weights.19// SetTradeOpen stores the instant other wallets may transfer. Until that20// instant, only the liquidity wallet, the airdrop escrow, and GovDAO may21// move WIZ through Transfer or TransferFrom. Claim and Seal move the22// ledger directly and stay available. After SetPool, tokens that leave23// that pool share one buy cap for a short window. The liquidity wallet24// is never capped, so it can add liquidity during the window.25package drop2627import (28 "chain"29 "chain/runtime/unsafe"30 "crypto/merkle"31 "encoding/hex"32 "math"33 "strconv"34 "time"3536 "gno.land/p/nt/grc20/v0"37 "gno.land/p/nt/avl/v0"38 "gno.land/p/nt/ownable/v0"39 "gno.land/p/nt/ufmt/v0"40)4142const (43 // tokenDecimals is the GRC20 display scale. One WIZ is tokenUnit base units.44 tokenDecimals = 645 // tokenUnit is 10^tokenDecimals.46 tokenUnit int64 = 1_000_00047 // wholeSupply is the mint in whole WIZ. mintSupply is the same mint in base units.48 // TotalSupply() is the circulating supply and shrinks when leftover airdrop is burned.49 wholeSupply int64 = 10_000_000_00050 mintSupply int64 = wholeSupply * tokenUnit51 // CommunityBps is 3% of supply. The pro-rata remainder is added on top.52 CommunityBps int64 = 30053 // IncentiveBps is the liquidity-incentive sleeve, 7% of supply.54 IncentiveBps int64 = 70055 // LiquidityBps is the liquidity sleeve, 15% of supply.56 LiquidityBps int64 = 1_50057 // BpsBasis is 100%.58 BpsBasis int64 = 10_0005960 // ClaimWindow is the claim period after the deployer-set start: 90 days.61 ClaimWindow int64 = 90 * 24 * 60 * 6062 // VoteWindow is the longest a GovDAO ballot may lock a balance.63 // gno.land/r/nym-wizards001/gov uses the same duration.64 VoteWindow int64 = 7 * 24 * 60 * 6065 // quorumBps is the yes-vote floor, measured against the sealed airdrop budget.66 quorumBps int64 = 1_0006768 // BuyCapWindow is how long buys that leave the pool stay capped.69 BuyCapWindow int64 = 10 * 6070 // BuyCapBucket is one slice of that window. The cap resets each slice.71 BuyCapBucket int64 = 6072 // BuyCapPerBucket is 1% of the liquidity sleeve. Divide before multiply so the product fits in int64.73 BuyCapPerBucket int64 = mintSupply / BpsBasis * LiquidityBps / 1007475 tokenVersion = "4"7677 maxProofAunts = 10078 hashLen = 3279)8081// hold is the largest ballot still locking an address.82type hold struct {83 amount int6484 until int6485}8687var (88 // publisher is the addpkg signer. Captured once, when this package is initialized.89 publisher = unsafe.OriginCaller()9091 token *grc20.Token92 ledger *grc20.PrivateLedger93 admin *ownable.Ownable94 adminAddr address9596 escrow address97 gov address9899 sealed bool100 root []byte101 eligible int102 allocated int64103 communitySent int64104 liquidityTo address105 liquiditySent int64106 incentiveSent int64107 airdropLeft int64108 claimStart int64109 tradeOpen int64110 burned bool111 claimed avl.Tree112 claimedSupply int64113 locks avl.Tree114115 // pair is the pool. Buys are transfers that leave it.116 pair address117 pairSetAt int64118 buyBucket int64119 buySpent int64120)121122// liquidityRecipient is the only wallet Seal may pay the 15% sleeve.123var liquidityRecipient = address("g17xukdzx4w0q4tmendmsjy2luxk7lvfzv39nfkl")124125// Version is the token book. Version 4 sends liquidity to the named address126// and sends incentive plus community to the bound GovDAO.127func Version() string { return tokenVersion }128129// Init binds the addpkg signer as admin. Any other caller panics.130func Init(cur realm) {131 mustUser(cur)132 mustNoPayment()133 if token != nil {134 panic("already initialized")135 }136 caller := cur.Previous().Address()137 // A deployed package records the addpkg signer in publisher.138 // The test VM leaves that address empty; the first Init caller is recorded.139 if !publisher.IsValid() {140 publisher = caller141 }142 if caller != publisher {143 panic("deployer")144 }145 admin = ownable.NewWithAddress(caller)146 adminAddr = caller147 escrow = cur.Address()148 token, ledger = grc20.NewToken("wizards", "WIZ", tokenDecimals, 0, cur)149 chain.Emit("Init", "admin", caller.String(), "version", tokenVersion)150}151152// TransferAdmin moves the admin seat. Admin cannot move funds.153func TransferAdmin(cur realm, next address) {154 mustAdmin(cur)155 mustNoPayment()156 if err := admin.TransferOwnership(0, cur, next); err != nil {157 panic(err.Error())158 }159 adminAddr = next160}161162// SetGov binds the GovDAO. The caller must be that realm, and the163// transaction signer must be this token's admin. One call, before Seal.164// Seal sends both the incentive sleeve and the community sleeve there.165func SetGov(cur realm) {166 mustCurrent(cur)167 mustNoPayment()168 if token == nil {169 panic("not initialized")170 }171 if sealed {172 panic("sealed")173 }174 if gov.IsValid() {175 panic("gov set")176 }177 prev := cur.Previous()178 if prev.IsUserCall() || prev.IsUserRun() {179 panic("gov")180 }181 if unsafe.OriginCaller() != adminAddr {182 panic("admin")183 }184 caller := prev.Address()185 if !caller.IsValid() || caller == escrow {186 panic("gov")187 }188 gov = caller189 chain.Emit("SetGov", "gov", caller.String())190}191192// Seal mints the whole supply, commits the eligible-set Merkle root, and193// sends the sleeves out. airdropTotal is the sum of the leaf amounts,194// at most the 75% pool. Integer dust is added to the community mint.195// liquidityTo must be liquidityRecipient. Any other address panics.196// Incentive and community both go to the realm bound with SetGov.197func Seal(cur realm, rootHex string, eligibleCount int, airdropTotal int64, liquidityToArg address) {198 mustAdmin(cur)199 mustNoPayment()200 if sealed {201 panic("already sealed")202 }203 if eligibleCount <= 0 {204 panic("eligible count")205 }206 decoded, err := hex.DecodeString(rootHex)207 if err != nil || len(decoded) != hashLen {208 panic("root")209 }210 communityAmt, incentiveAmt, liqAmt, pool := sleeves()211 if airdropTotal <= 0 || airdropTotal > pool {212 panic("allocated")213 }214 if !gov.IsValid() {215 panic("gov")216 }217 if liquidityToArg != liquidityRecipient || liquidityToArg == escrow || liquidityToArg == gov {218 panic("recipient")219 }220 communityAmt += pool - airdropTotal221222 mustMint(escrow, communityAmt+incentiveAmt+liqAmt+airdropTotal)223 mustTransfer(escrow, liquidityToArg, liqAmt)224 mustTransfer(escrow, gov, incentiveAmt)225 mustTransfer(escrow, gov, communityAmt)226227 root = append([]byte(nil), decoded...)228 eligible = eligibleCount229 allocated = airdropTotal230 communitySent = communityAmt231 liquidityTo = liquidityToArg232 liquiditySent = liqAmt233 incentiveSent = incentiveAmt234 airdropLeft = airdropTotal235 sealed = true236237 chain.Emit(238 "Seal",239 "root", hex.EncodeToString(root),240 "eligible", strconv.Itoa(eligibleCount),241 "allocated", strconv.FormatInt(airdropTotal, 10),242 "community", strconv.FormatInt(communityAmt, 10),243 "liquidity", strconv.FormatInt(liqAmt, 10),244 "incentive", strconv.FormatInt(incentiveAmt, 10),245 "liquidityTo", liquidityToArg.String(),246 "gov", gov.String(),247 )248}249250// SetClaimStart opens claims at start (unix seconds). The deployer may251// call it once. start must be in the future. Claims run for ClaimWindow.252func SetClaimStart(cur realm, start int64) {253 mustAdmin(cur)254 mustNoPayment()255 if !sealed {256 panic("not sealed")257 }258 if claimStart != 0 {259 panic("claim start set")260 }261 if start <= time.Now().Unix() || start > math.MaxInt64-ClaimWindow {262 panic("claim start")263 }264 claimStart = start265 chain.Emit(266 "SetClaimStart",267 "start", strconv.FormatInt(start, 10),268 "end", strconv.FormatInt(start+ClaimWindow, 10),269 )270}271272// SetTradeOpen stores the instant public transfers open. One call.273// The instant has to be in the future. Until it arrives, Transfer and274// TransferFrom move tokens only for the liquidity wallet, the airdrop275// escrow, and the bound GovDAO.276func SetTradeOpen(cur realm, start int64) {277 mustAdmin(cur)278 mustNoPayment()279 if tradeOpen != 0 {280 panic("trade open set")281 }282 if start <= time.Now().Unix() {283 panic("trade open")284 }285 tradeOpen = start286 chain.Emit("SetTradeOpen", "start", strconv.FormatInt(start, 10))287}288289// SetPool registers the pool once. The buy cap starts at this call.290// The liquidity wallet cannot be the pool. That wallet sends tokens291// into the pool, and those sends are not buys.292func SetPool(cur realm, market address) {293 mustAdmin(cur)294 mustNoPayment()295 if pair.IsValid() {296 panic("pool")297 }298 if !market.IsValid() || market == escrow || market == gov || market == liquidityRecipient {299 panic("pool")300 }301 pair = market302 pairSetAt = time.Now().Unix()303 buyBucket = 0304 buySpent = 0305 chain.Emit(306 "SetPool",307 "pool", market.String(),308 "start", strconv.FormatInt(pairSetAt, 10),309 )310}311312// Claim pays the caller's full sealed amount. Attaching coins reverts.313// There is no withdraw.314func Claim(cur realm, index int, amount int64, auntsHex string) {315 var to address316 claim(cur, index, amount, auntsHex, to)317}318319// ClaimTo pays the caller's full sealed amount to another address.320// The caller must still be the leaf. An invalid to pays the caller.321// to cannot be this realm. Attaching coins reverts. There is no withdraw.322func ClaimTo(cur realm, index int, amount int64, auntsHex string, to address) {323 claim(cur, index, amount, auntsHex, to)324}325326func claim(cur realm, index int, amount int64, auntsHex string, to address) {327 mustUser(cur)328 mustNoPayment()329 if !sealed {330 panic("not sealed")331 }332 if !claimOpen() {333 panic("claim closed")334 }335 if amount <= 0 || amount > allocated {336 panic("amount")337 }338 caller := cur.Previous().Address()339 key := caller.String()340 dest := to341 if !dest.IsValid() {342 dest = caller343 }344 if dest == escrow {345 panic("recipient")346 }347 if index < 0 || index >= eligible {348 panic("index")349 }350 aunts, err := hex.DecodeString(auntsHex)351 if err != nil || len(aunts)%hashLen != 0 || len(aunts)/hashLen > maxProofAunts {352 panic("bad proof")353 }354 if !merkle.VerifySimpleProof(root, leafData(key, amount), index, eligible, aunts) {355 panic("bad proof")356 }357 if claimed.Has(key) {358 panic("already claimed")359 }360 if airdropLeft < amount {361 panic("airdrop empty")362 }363 airdropLeft -= amount364 claimedSupply += amount365 claimed.Set(key, amount)366 mustTransfer(escrow, dest, amount)367 chain.Emit(368 "Claim",369 "leaf", key,370 "to", dest.String(),371 "index", strconv.Itoa(index),372 "amount", strconv.FormatInt(amount, 10),373 )374}375376// BurnUnclaimed destroys the airdrop budget still here after the window.377// Anyone may call it. A second call reverts. GovDAO cannot recover it.378// The sleeves already sent are not burned.379func BurnUnclaimed(cur realm) {380 mustCurrent(cur)381 if cur.Address() != escrow {382 panic("escrow")383 }384 mustNoPayment()385 if !sealed {386 panic("not sealed")387 }388 if burned {389 panic("already burned")390 }391 if claimStart == 0 || time.Now().Unix() < claimStart+ClaimWindow {392 panic("claim open")393 }394 left := airdropLeft395 airdropLeft = 0396 burned = true397 if left > 0 {398 mustBurn(escrow, left)399 }400 chain.Emit("BurnUnclaimed", "amount", strconv.FormatInt(left, 10))401}402403// NoteVote locks the signing caller's current balance until until.404// Only the bound GovDAO may call it. until cannot pass VoteWindow.405func NoteVote(cur realm, until int64) {406 mustCurrent(cur)407 mustNoPayment()408 if !gov.IsValid() || cur.Previous().Address() != gov {409 panic("gov")410 }411 now := time.Now().Unix()412 if until <= now || until > now+VoteWindow {413 panic("lock")414 }415 voter := unsafe.OriginCaller()416 if !voter.IsValid() || voter == gov || voter == escrow {417 panic("voter")418 }419 weight := BalanceOf(voter)420 if weight <= 0 {421 panic("vote weight")422 }423 key := voter.String()424 amt := weight425 end := until426 if v := locks.Get(key); v != nil {427 prev, ok := v.(*hold)428 if !ok || prev == nil {429 panic("lock")430 }431 if now < prev.until {432 if prev.amount > amt {433 amt = prev.amount434 }435 if prev.until > end {436 end = prev.until437 }438 }439 }440 locks.Set(key, &hold{amount: amt, until: end})441 chain.Emit(442 "NoteVote",443 "voter", voter.String(),444 "weight", strconv.FormatInt(amt, 10),445 "until", strconv.FormatInt(end, 10),446 )447}448449func Transfer(cur realm, to address, amount int64) {450 mustCurrent(cur)451 mustToken()452 mustNoPayment()453 caller := cur.Previous().Address()454 mustTradable(caller)455 mustUnlocked(caller, amount)456 mustBuyCap(caller, amount)457 teller := ledger.ImpersonateTeller(caller)458 if err := teller.Transfer(0, cur, to, amount); err != nil {459 panic(err.Error())460 }461}462463func Approve(cur realm, spender address, amount int64) {464 mustCurrent(cur)465 mustToken()466 mustNoPayment()467 caller := cur.Previous().Address()468 teller := ledger.ImpersonateTeller(caller)469 if err := teller.Approve(0, cur, spender, amount); err != nil {470 panic(err.Error())471 }472}473474func TransferFrom(cur realm, from, to address, amount int64) {475 mustCurrent(cur)476 mustToken()477 mustNoPayment()478 mustTradable(from)479 mustUnlocked(from, amount)480 mustBuyCap(from, amount)481 caller := cur.Previous().Address()482 teller := ledger.ImpersonateTeller(caller)483 if err := teller.TransferFrom(0, cur, from, to, amount); err != nil {484 panic(err.Error())485 }486}487488func Name() string {489 mustToken()490 return token.GetName()491}492493func Symbol() string {494 mustToken()495 return token.GetSymbol()496}497498func Decimals() int {499 mustToken()500 return token.GetDecimals()501}502503func Supply() int64 { return TotalSupply() }504505func TotalSupply() int64 {506 mustToken()507 return token.TotalSupply()508}509510func GetName() string { return Name() }511func GetSymbol() string { return Symbol() }512func GetDecimals() int { return Decimals() }513514func BalanceOf(owner address) int64 {515 mustToken()516 return token.BalanceOf(owner)517}518519func Allowance(owner, spender address) int64 {520 mustToken()521 return token.Allowance(owner, spender)522}523524func Admin() address { return adminAddr }525526func Escrow() address { return escrow }527func Gov() address { return gov }528func Sealed() bool { return sealed }529func Eligible() int { return eligible }530func Allocated() int64 { return allocated }531func CommunitySent() int64 { return communitySent }532// LiquidityRecipient is the only address Seal accepts for the 15% sleeve.533func LiquidityRecipient() address { return liquidityRecipient }534535func Pool() address { return pair }536func PoolSetAt() int64 { return pairSetAt }537func BuySpent() int64 { return buySpent }538func LiquidityTo() address { return liquidityTo }539func LiquiditySent() int64 { return liquiditySent }540func IncentiveSent() int64 { return incentiveSent }541func AirdropLeft() int64 { return airdropLeft }542func ClaimStart() int64 { return claimStart }543func TradeOpen() int64 { return tradeOpen }544545func ClaimEnd() int64 {546 if claimStart == 0 {547 return 0548 }549 return claimStart + ClaimWindow550}551552func Burned() bool { return burned }553func Claimed() int { return claimed.Size() }554555// ClaimsStarted reports whether the claim window has opened.556func ClaimsStarted() bool { return claimsStarted() }557558func Quorum() int64 {559 if !sealed {560 return 0561 }562 // Floor on whole WIZ, then scale. allocated * quorumBps can overflow,563 // and dividing the base amount first would keep a fraction the 0-decimal book dropped.564 whole := allocated / tokenUnit565 return whole * quorumBps / BpsBasis * tokenUnit566}567568// ClaimedSupply is the sum of leaves already paid. Burns and transfers do not change it.569func ClaimedSupply() int64 { return claimedSupply }570571// Publisher is the only address allowed to call Init.572func Publisher() address { return publisher }573574func RootHex() string {575 if len(root) == 0 {576 return ""577 }578 return hex.EncodeToString(root)579}580581func HasClaimed(owner address) bool {582 if !owner.IsValid() {583 return false584 }585 return claimed.Has(owner.String())586}587588func ClaimedOf(owner address) int64 {589 if !owner.IsValid() {590 return 0591 }592 return claimedOf(owner.String())593}594595// Render shows the version-4 split and the claim window.596func Render(path string) string {597 if token == nil {598 return "# WIZ\n\nNot initialized.\n"599 }600 out := "# WIZ\n\n"601 out += ufmt.Sprintf("* **Version**: %s\n", tokenVersion)602 out += ufmt.Sprintf("* **Symbol**: %s\n", token.GetSymbol())603 out += ufmt.Sprintf("* **Decimals**: %d\n", token.GetDecimals())604 out += ufmt.Sprintf("* **One WIZ**: %d base units\n", tokenUnit)605 out += ufmt.Sprintf("* **Total supply**: %d WIZ\n", token.TotalSupply()/tokenUnit)606 out += ufmt.Sprintf("* **Total supply base**: %d\n", token.TotalSupply())607 out += "* **Airdrop**: 75%, pro-rata to genesis GNOT, 100% unlocked at claim\n"608 out += "* **Claim fee**: none\n"609 out += "* **Claim recipient**: the caller, or another address in the same call\n"610 out += "* **Claim window**: 90 days from the deployer start\n"611 out += "* **Unclaimed**: burned after the window\n"612 out += "* **Liquidity**: 15%, sent once at seal to the named address\n"613 out += "* **Incentives**: 7%, sent once at seal to GovDAO\n"614 out += "* **Community development**: 3% plus the pro-rata remainder, sent once at seal to GovDAO\n"615 out += "* **GovDAO**: a separate realm. Holders vote there on incentive and community, separately\n"616 out += "* **No withdraw**\n"617 out += ufmt.Sprintf("* **Trade open**: %d\n", tradeOpen)618 out += "* **Before trade open**: only the liquidity wallet, the airdrop escrow, and GovDAO can transfer\n"619 out += ufmt.Sprintf("* **Buy cap**: %d WIZ may leave the pool every %d seconds, for %d seconds after the pool is registered\n", BuyCapPerBucket/tokenUnit, BuyCapBucket, BuyCapWindow)620 out += "* **Liquidity add**: the liquidity wallet is not capped\n"621 if pair.IsValid() {622 out += ufmt.Sprintf("* **Pool**: %s\n", pair.String())623 out += ufmt.Sprintf("* **Buy cap ends**: %d\n", pairSetAt+BuyCapWindow)624 }625 if !sealed {626 out += "\nRoot is not sealed.\n"627 return out628 }629 out += ufmt.Sprintf("\n* **Eligible**: %d\n", eligible)630 out += ufmt.Sprintf("* **Airdrop budget left**: %d base units\n", airdropLeft)631 out += ufmt.Sprintf("* **Community sent**: %d base units\n", communitySent)632 out += ufmt.Sprintf("* **Liquidity sent**: %d base units\n", liquiditySent)633 out += ufmt.Sprintf("* **Incentive sent**: %d base units\n", incentiveSent)634 out += ufmt.Sprintf("* **Claim start**: %d\n", claimStart)635 out += ufmt.Sprintf("* **Claim end**: %d\n", ClaimEnd())636 out += ufmt.Sprintf("* **Burned**: %t\n", burned)637 out += ufmt.Sprintf("* **Root**: %s\n", hex.EncodeToString(root))638 out += ufmt.Sprintf("* **Claimed supply**: %d base units\n", claimedSupply)639 return out640}641642func leafData(bech32 string, amount int64) []byte {643 return []byte(bech32 + ":" + strconv.FormatInt(amount, 10))644}645646func sleeves() (community, incentive, liquidity, pool int64) {647 // Divide first. mintSupply * LiquidityBps does not fit in int64.648 community = mintSupply / BpsBasis * CommunityBps649 incentive = mintSupply / BpsBasis * IncentiveBps650 liquidity = mintSupply / BpsBasis * LiquidityBps651 pool = mintSupply - community - incentive - liquidity652 return653}654655func claimOpen() bool {656 if claimStart == 0 {657 return false658 }659 now := time.Now().Unix()660 return now >= claimStart && now < claimStart+ClaimWindow661}662663func claimsStarted() bool {664 return claimStart != 0 && time.Now().Unix() >= claimStart665}666667func mustCurrent(cur realm) {668 if !cur.IsCurrent() {669 panic("realm")670 }671}672673func mustUser(cur realm) {674 mustCurrent(cur)675 if !cur.Previous().IsUserCall() {676 panic("user call only")677 }678}679680// mustTradable panics while public transfers are closed.681// The liquidity wallet, the airdrop escrow, and GovDAO stay able to move tokens.682// Claim and Seal do not call this.683func mustTradable(from address) {684 if from == liquidityRecipient || (escrow.IsValid() && from == escrow) || (gov.IsValid() && from == gov) {685 return686 }687 if tradeOpen == 0 || time.Now().Unix() < tradeOpen {688 panic("trade closed")689 }690}691692// mustUnlocked panics when amount would push owner's balance under the693// largest weight they already cast on a ballot that is still open.694// An amount above the balance is left to the token transfer.695// mustBuyCap limits tokens leaving the registered pool during the short window.696// The liquidity wallet is exempt. A transfer that does not leave the pool is not a buy.697func mustBuyCap(from address, amount int64) {698 if amount <= 0 || from == liquidityRecipient || !pair.IsValid() || from != pair {699 return700 }701 now := time.Now().Unix()702 if pairSetAt <= 0 || now < pairSetAt {703 panic("buy cap")704 }705 if now >= pairSetAt+BuyCapWindow {706 return707 }708 bucket := (now - pairSetAt) / BuyCapBucket709 if bucket != buyBucket {710 buyBucket = bucket711 buySpent = 0712 }713 if amount > BuyCapPerBucket || buySpent > BuyCapPerBucket-amount {714 panic("buy cap")715 }716 buySpent += amount717}718719func mustUnlocked(owner address, amount int64) {720 if amount <= 0 {721 return722 }723 bal := BalanceOf(owner)724 if amount > bal {725 return726 }727 if bal-amount < voteLock(owner) {728 panic("vote lock")729 }730}731732func voteLock(owner address) int64 {733 if !owner.IsValid() {734 return 0735 }736 v := locks.Get(owner.String())737 if v == nil {738 return 0739 }740 h, ok := v.(*hold)741 if !ok || h == nil {742 panic("lock")743 }744 if time.Now().Unix() >= h.until {745 return 0746 }747 return h.amount748}749750func mustNoPayment() {751 if len(unsafe.OriginSend()) != 0 {752 panic("unexpected payment")753 }754}755756func mustAdmin(cur realm) {757 if admin == nil || token == nil {758 panic("not initialized")759 }760 mustUser(cur)761 admin.AssertOwnedBy(cur.Previous().Address())762}763764func mustToken() {765 if token == nil {766 panic("not initialized")767 }768}769770func claimedOf(key string) int64 {771 v := claimed.Get(key)772 if v == nil {773 return 0774 }775 n, ok := v.(int64)776 if !ok {777 panic("claimed amount")778 }779 return n780}781782func mustMint(to address, amount int64) {783 if err := ledger.Mint(to, amount); err != nil {784 panic(err.Error())785 }786}787788func mustTransfer(from, to address, amount int64) {789 if err := ledger.Transfer(from, to, amount); err != nil {790 panic(err.Error())791 }792}793794func mustBurn(from address, amount int64) {795 if err := ledger.Burn(from, amount); err != nil {796 panic(err.Error())797 }798}799Admin() string
AirdropLeft() int64
Allocated() int64
Allowance(owner string, spender string) int64
Approve(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, spender string, amount int64)
BalanceOf(owner string) int64
Burned() bool
BurnUnclaimed(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
BuySpent() int64
Claim(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, index int, amount int64, auntsHex string)
Claimed() int
ClaimedOf(owner string) int64
ClaimedSupply() int64
ClaimEnd() int64
ClaimsStarted() bool
ClaimStart() int64
ClaimTo(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, index int, amount int64, auntsHex string, to string)
CommunitySent() int64
Decimals() int
Eligible() int
Escrow() string
GetDecimals() int
GetName() string
GetSymbol() string
Gov() string
HasClaimed(owner string) bool
IncentiveSent() int64
Init(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
LiquidityRecipient() string
LiquiditySent() int64
LiquidityTo() string
Name() string
NoteVote(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, until int64)
Pool() string
PoolSetAt() int64
Publisher() string
Quorum() int64
Render(path string) string
RootHex() string
Seal(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, rootHex string, eligibleCount int, airdropTotal int64, liquidityToArg string)
Sealed() bool
SetClaimStart(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, start int64)
SetGov(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
SetPool(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, market string)
SetTradeOpen(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, start int64)
Supply() int64
Symbol() string
TotalSupply() int64
TradeOpen() int64
Transfer(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string, amount int64)
TransferAdmin(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, next string)
TransferFrom(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, from string, to string, amount int64)
Version() string
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.