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

ammmath.gno

5.34 Kb · 190 lines
  1// Package ammmath implements integer bonding-curve and constant-product AMM
  2// helpers for the gnomemepad factory (Pump-style virtual CPMM + real CPMM pool).
  3package ammmath
  4
  5import "math/overflow"
  6
  7// FeeResult is the split of a gross trade fee.
  8type FeeResult struct {
  9	Gross     int64
 10	Net       int64
 11	Fee       int64
 12	Creator   int64
 13	Protocol  int64
 14	Remainder int64
 15}
 16
 17// ApplyFee takes gross quote paid by the user and feeBPS (e.g. 120 = 1.20%).
 18func ApplyFee(gross, feeBPS, creatorShareBPS, protocolShareBPS int64) FeeResult {
 19	if gross <= 0 {
 20		panic("ammmath: gross must be positive")
 21	}
 22	if feeBPS < 0 || feeBPS >= 10000 {
 23		panic("ammmath: feeBPS out of range")
 24	}
 25	if creatorShareBPS < 0 || protocolShareBPS < 0 || creatorShareBPS+protocolShareBPS > 10000 {
 26		panic("ammmath: fee share BPS invalid")
 27	}
 28	fee := gross * feeBPS / 10000
 29	net := gross - fee
 30	creator := fee * creatorShareBPS / 10000
 31	protocol := fee * protocolShareBPS / 10000
 32	remainder := fee - creator - protocol
 33	return FeeResult{
 34		Gross:     gross,
 35		Net:       net,
 36		Fee:       fee,
 37		Creator:   creator,
 38		Protocol:  protocol,
 39		Remainder: remainder,
 40	}
 41}
 42
 43// ApplyFeeOnOutput charges fee on assets leaving the pool/curve.
 44func ApplyFeeOnOutput(grossOut, feeBPS, creatorShareBPS, protocolShareBPS int64) FeeResult {
 45	if grossOut <= 0 {
 46		panic("ammmath: grossOut must be positive")
 47	}
 48	if feeBPS < 0 || feeBPS >= 10000 {
 49		panic("ammmath: feeBPS out of range")
 50	}
 51	if creatorShareBPS < 0 || protocolShareBPS < 0 || creatorShareBPS+protocolShareBPS > 10000 {
 52		panic("ammmath: fee share BPS invalid")
 53	}
 54	fee := grossOut * feeBPS / 10000
 55	net := grossOut - fee
 56	if net <= 0 {
 57		panic("ammmath: fee consumes entire output")
 58	}
 59	creator := fee * creatorShareBPS / 10000
 60	protocol := fee * protocolShareBPS / 10000
 61	remainder := fee - creator - protocol
 62	return FeeResult{
 63		Gross:     grossOut,
 64		Net:       net,
 65		Fee:       fee,
 66		Creator:   creator,
 67		Protocol:  protocol,
 68		Remainder: remainder,
 69	}
 70}
 71
 72// BuyTokens quotes a virtual constant-product buy (net ugnot in).
 73func BuyTokens(virtualUgnot, virtualToken, ugnotIn int64) (tokensOut, newVU, newVT int64) {
 74	if ugnotIn <= 0 {
 75		panic("ammmath: ugnotIn must be positive")
 76	}
 77	if virtualUgnot <= 0 || virtualToken <= 0 {
 78		panic("ammmath: invalid virtual reserves")
 79	}
 80	newVU, ok := overflow.Add64(virtualUgnot, ugnotIn)
 81	if !ok {
 82		panic("ammmath: virtual ugnot overflow")
 83	}
 84	k, ok := overflow.Mul64(virtualUgnot, virtualToken)
 85	if !ok {
 86		panic("ammmath: k overflow")
 87	}
 88	newVT = k / newVU
 89	if newVT <= 0 {
 90		panic("ammmath: empty virtual token reserve")
 91	}
 92	if newVT >= virtualToken {
 93		panic("ammmath: zero tokens out")
 94	}
 95	tokensOut = virtualToken - newVT
 96	return tokensOut, newVU, newVT
 97}
 98
 99// SellTokens quotes a virtual constant-product sell (gross ugnot out).
100func SellTokens(virtualUgnot, virtualToken, tokensIn int64) (ugnotOut, newVU, newVT int64) {
101	if tokensIn <= 0 {
102		panic("ammmath: tokensIn must be positive")
103	}
104	if virtualUgnot <= 0 || virtualToken <= 0 {
105		panic("ammmath: invalid virtual reserves")
106	}
107	newVT, ok := overflow.Add64(virtualToken, tokensIn)
108	if !ok {
109		panic("ammmath: virtual token overflow")
110	}
111	k, ok := overflow.Mul64(virtualUgnot, virtualToken)
112	if !ok {
113		panic("ammmath: k overflow")
114	}
115	newVU = k / newVT
116	if newVU <= 0 {
117		panic("ammmath: empty virtual ugnot reserve")
118	}
119	if newVU >= virtualUgnot {
120		panic("ammmath: zero ugnot out")
121	}
122	ugnotOut = virtualUgnot - newVU
123	return ugnotOut, newVU, newVT
124}
125
126// PoolSwapUgnotForToken swaps net ugnot for tokens; remainderToPool adds to ugnot reserve.
127func PoolSwapUgnotForToken(poolUgnot, poolToken, ugnotIn, remainderToPool int64) (tokensOut, newPU, newPT int64) {
128	if ugnotIn <= 0 {
129		panic("ammmath: ugnotIn must be positive")
130	}
131	if poolUgnot <= 0 || poolToken <= 0 {
132		panic("ammmath: invalid pool reserves")
133	}
134	if remainderToPool < 0 {
135		panic("ammmath: negative remainder")
136	}
137	addU, ok := overflow.Add64(ugnotIn, remainderToPool)
138	if !ok {
139		panic("ammmath: add overflow")
140	}
141	newPU, ok = overflow.Add64(poolUgnot, addU)
142	if !ok {
143		panic("ammmath: pool ugnot overflow")
144	}
145	tokensOut = poolToken * ugnotIn / (poolUgnot + ugnotIn)
146	if tokensOut <= 0 {
147		panic("ammmath: zero tokens out of pool")
148	}
149	if tokensOut >= poolToken {
150		panic("ammmath: would drain pool tokens")
151	}
152	newPT = poolToken - tokensOut
153	return tokensOut, newPU, newPT
154}
155
156// PoolSwapTokenForUgnot swaps tokens for gross ugnot out.
157func PoolSwapTokenForUgnot(poolUgnot, poolToken, tokensIn int64) (ugnotOut, newPU, newPT int64) {
158	if tokensIn <= 0 {
159		panic("ammmath: tokensIn must be positive")
160	}
161	if poolUgnot <= 0 || poolToken <= 0 {
162		panic("ammmath: invalid pool reserves")
163	}
164	newPT, ok := overflow.Add64(poolToken, tokensIn)
165	if !ok {
166		panic("ammmath: pool token overflow")
167	}
168	ugnotOut = poolUgnot * tokensIn / (poolToken + tokensIn)
169	if ugnotOut <= 0 {
170		panic("ammmath: zero ugnot out of pool")
171	}
172	if ugnotOut >= poolUgnot {
173		panic("ammmath: would drain pool ugnot")
174	}
175	newPU = poolUgnot - ugnotOut
176	return ugnotOut, newPU, newPT
177}
178
179// CanGraduate reports whether raised net ugnot meets the threshold.
180func CanGraduate(raisedUgnot, threshold int64) bool {
181	return raisedUgnot >= threshold && threshold > 0
182}
183
184// SpotPriceUgnotPerToken returns ugnot per token scaled by 1e6 (display only).
185func SpotPriceUgnotPerToken(ugnotReserve, tokenReserve int64) int64 {
186	if tokenReserve <= 0 {
187		return 0
188	}
189	return ugnotReserve * 1000000 / tokenReserve
190}