Search Apps Documentation Source Content File Folder Download Copy Actions Download 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}