Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

views.gno

7.20 Kb · 177 lines
  1package sales
  2
  3import (
  4	"strconv"
  5
  6	"chain"
  7	"gno.land/p/samcrew/launchpad/fairmath/v1"
  8	"gno.land/p/samcrew/launchpad/meta/v1"
  9	"gno.land/r/samcrew/launchpad/config/v1"
 10	"gno.land/r/samcrew/launchpad/tokens/v1"
 11)
 12
 13// Read views for launch clients. Every int64, base units and timestamps alike,
 14// is a decimal string so JavaScript keeps it exact; booleans stay booleans and
 15// counts that fit in an int are numbers. They change no state.
 16
 17func jsonInt(v int64) string { return meta.JSONString(strconv.FormatInt(v, 10)) }
 18
 19// fairRecordJSON reports a sale's terms and outcome. Once settled, the primary
 20// fee is what was raised minus the creator's share.
 21func fairRecordJSON(r FairSaleRecord) string {
 22	s := r.Schedule
 23	return "{" +
 24		`"creator":` + meta.JSONString(r.Creator.String()) +
 25		`,"quoteCurrency":` + meta.JSONString(r.QuoteCurrency) +
 26		`,"configVersion":` + jsonInt(r.ConfigVersion) +
 27		`,"primaryFeeBps":` + jsonInt(r.PrimaryFeeBPS) +
 28		`,"allocation":` + jsonInt(r.Allocation) +
 29		`,"lotSize":` + jsonInt(r.LotSize) +
 30		`,"walletCapLots":` + jsonInt(r.WalletCapLots) +
 31		`,"hardCapLots":` + jsonInt(r.Caps.HardLots) +
 32		`,"softCapQuote":` + jsonInt(r.Caps.SoftQuote) +
 33		`,"start":` + jsonInt(s.Start) +
 34		`,"end":` + jsonInt(s.End) +
 35		`,"startPrice":` + jsonInt(s.StartPrice) +
 36		`,"floorPrice":` + jsonInt(s.FloorPrice) +
 37		`,"decrement":` + jsonInt(s.Decrement) +
 38		`,"intervalSeconds":` + jsonInt(s.IntervalSeconds) +
 39		`,"allowlistRoot":` + meta.JSONString(r.AllowlistRoot) +
 40		`,"holdingGates":` + meta.JSONString(r.HoldingGates) +
 41		`,"totalLots":` + jsonInt(r.TotalLots) +
 42		`,"totalDeposits":` + jsonInt(r.TotalDeposits) +
 43		`,"hardClosedAt":` + jsonInt(r.HardClosedAt) +
 44		`,"cancelled":` + strconv.FormatBool(r.Cancelled) +
 45		`,"settled":` + strconv.FormatBool(r.Settled) +
 46		`,"succeeded":` + strconv.FormatBool(r.Succeeded) +
 47		`,"closeAt":` + jsonInt(r.CloseAt) +
 48		`,"closePrice":` + jsonInt(r.ClosePrice) +
 49		`,"soldTokens":` + jsonInt(r.SoldTokens) +
 50		`,"grossQuote":` + jsonInt(r.GrossQuote) +
 51		`,"primaryFeeQuote":` + jsonInt(r.GrossQuote-r.CreatorQuote) +
 52		`,"creatorQuote":` + jsonInt(r.CreatorQuote) +
 53		`,"proceedsReleased":` + strconv.FormatBool(r.ProceedsReleased) +
 54		`,"refundQuote":` + jsonInt(r.RefundQuote) +
 55		`,"unsoldTokens":` + jsonInt(r.UnsoldTokens) +
 56		`,"refundPaid":` + jsonInt(r.RefundPaid) +
 57		`,"tokensClaimed":` + jsonInt(r.TokensClaimed) + "}"
 58}
 59
 60func airdropRecordJSON(r AirdropRecord) string {
 61	return "{" +
 62		`"root":` + meta.JSONString(r.Root) +
 63		`,"total":` + jsonInt(r.Total) +
 64		`,"claimed":` + jsonInt(r.Claimed) + "}"
 65}
 66
 67// TermsJSON is what launching a token in a currency costs under the current
 68// config version: the fee a direct creation and a fair-sale creation attach,
 69// exactly; the most one fair sale may raise; and the primary fee a new sale
 70// pins. A fee of -1 means the currency has no terms. A creation names this
 71// version, so if the terms change before it lands it fails with "config
 72// changed" and takes nothing. Whether a lane takes new launches now is
 73// config's ActionStatusJSON.
 74func TermsJSON(currencyKey string) string {
 75	if currencyKey == "" || len(currencyKey) > 200 {
 76		panic("launchpad/sales: invalid currency")
 77	}
 78	version := config.GetCurrentVersion()
 79	p := config.GetVersion(version)
 80	direct, fair, raiseCap := int64(-1), int64(-1), int64(0)
 81	for _, c := range p.Currencies {
 82		if c.Key == currencyKey {
 83			direct, fair, raiseCap = c.DirectCreationFee, c.FairSaleCreationFee, c.FairSaleRaiseCap
 84		}
 85	}
 86	return "{" +
 87		`"schema":"launchpad-sales-terms-v1"` +
 88		`,"version":` + jsonInt(version) +
 89		`,"currency":` + meta.JSONString(currencyKey) +
 90		`,"directCreationFee":` + jsonInt(direct) +
 91		`,"fairSaleCreationFee":` + jsonInt(fair) +
 92		`,"fairSaleRaiseCap":` + jsonInt(raiseCap) +
 93		`,"primaryFeeBps":` + jsonInt(p.PrimaryFeeBPS) + "}"
 94}
 95
 96// SolvencyJSON is the realm's books in one currency: the balance it holds and
 97// what it owes, in total and by liability. fees is what SweepFees would pay
 98// now. Every movement of quote ends with balance >= owed, so a watcher that
 99// sees otherwise has found a fault; what the balance holds above owed was
100// sent outside the entry points and is owed to no one. An empty key or one
101// over 200 bytes panics "invalid currency"; a key that is neither ugnot nor a
102// registered GRC20 aborts in the registry ("unknown token").
103func SolvencyJSON(currencyKey string) string {
104	if currencyKey == "" || len(currencyKey) > 200 {
105		panic("launchpad/sales: invalid currency")
106	}
107	return "{" +
108		`"schema":"launchpad-sales-solvency-v1"` +
109		`,"currency":` + meta.JSONString(currencyKey) +
110		`,"balance":` + jsonInt(quoteBalance(chain.PackageAddress(salesPath), currencyKey)) +
111		`,"owed":` + jsonInt(owedIn(currencyKey)) +
112		`,"fees":` + jsonInt(feeLiability[currencyKey]) +
113		`,"escrow":` + jsonInt(fairEscrowLiability[currencyKey]) +
114		`,"refunds":` + jsonInt(fairRefundLiability[currencyKey]) +
115		`,"proceeds":` + jsonInt(fairProceedsLiability[currencyKey]) + "}"
116}
117
118// LaunchJSON is a token's launch state: the ledger's record of the token, what
119// sales still owes in it, how many vesting records it has, and its fair sale
120// and airdrop campaign, each null when the token has none.
121func LaunchJSON(id string) string {
122	token := tokens.TokenJSON(id)
123	fair := "null"
124	if r, ok := fairSales.Get(id).(FairSaleRecord); ok {
125		fair = fairRecordJSON(r)
126	}
127	airdrop := "null"
128	if r, ok := airdrops.Get(id).(AirdropRecord); ok {
129		airdrop = airdropRecordJSON(r)
130	}
131	return "{" +
132		`"schema":"launchpad-sales-v1"` +
133		`,"token":` + token +
134		`,"tokenLiability":` + jsonInt(tokenOwed(id)) +
135		`,"vestingCount":` + strconv.Itoa(vestingCount(id)) +
136		`,"fairSale":` + fair +
137		`,"airdrop":` + airdrop + "}"
138}
139
140// FairBuyerJSON is one buyer's record in a sale and, once the sale is settled
141// and until the buyer is paid, what ClaimFair would send them. Before
142// settlement both amounts are 0 and settled is false: settle first.
143func FairBuyerJSON(id string, buyer address) string {
144	r := GetFairSale(id)
145	b := GetFairBuyer(id, buyer)
146	claimTokens, claimRefund := int64(0), int64(0)
147	if r.Settled && b.Lots > 0 && !b.Claimed {
148		c := fairmath.Claim(r.Succeeded, b.Lots, b.Deposit, r.LotSize, r.ClosePrice)
149		claimTokens, claimRefund = c.Tokens, c.RefundQuote
150	}
151	return "{" +
152		`"buyer":` + meta.JSONString(buyer.String()) +
153		`,"lots":` + jsonInt(b.Lots) +
154		`,"deposit":` + jsonInt(b.Deposit) +
155		`,"claimableTokens":` + jsonInt(claimTokens) +
156		`,"claimableRefund":` + jsonInt(claimRefund) +
157		`,"settled":` + strconv.FormatBool(r.Settled) +
158		`,"claimed":` + strconv.FormatBool(b.Claimed) + "}"
159}
160
161// VestingJSON is one vesting record: its schedule, who holds the claim right
162// and who has been proposed to take it.
163func VestingJSON(id string, index int) string {
164	r := GetVesting(id, index)
165	return "{" +
166		`"index":` + strconv.Itoa(index) +
167		`,"beneficiary":` + meta.JSONString(r.Beneficiary.String()) +
168		`,"pendingBeneficiary":` + meta.JSONString(r.PendingBeneficiary.String()) +
169		`,"total":` + jsonInt(r.Total) +
170		`,"claimed":` + jsonInt(r.Claimed) +
171		`,"start":` + jsonInt(r.Start) +
172		`,"cliff":` + jsonInt(r.Cliff) +
173		`,"duration":` + jsonInt(r.Duration) +
174		`,"revocable":` + strconv.FormatBool(r.Revocable) +
175		`,"revoked":` + strconv.FormatBool(r.Revoked) +
176		`,"revokedVested":` + jsonInt(r.RevokedVested) + "}"
177}