Skip to content

Commit f3cb649

Browse files
committed
Add initial bonding curve implementation
1 parent 6589e71 commit f3cb649

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed

pkg/solana/currencycreator/curve.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package currencycreator
2+
3+
import (
4+
"math"
5+
"math/big"
6+
)
7+
8+
// Note: Generated with Grok 4 based on curve.rs, and not 100% accurate with on-chain program
9+
10+
const defaultPrec = 192
11+
12+
type ExponentialCurve struct {
13+
a *big.Float
14+
b *big.Float
15+
c *big.Float
16+
}
17+
18+
func expBig(x *big.Float) *big.Float {
19+
prec := x.Prec()
20+
result := big.NewFloat(1).SetPrec(prec)
21+
term := big.NewFloat(1).SetPrec(prec)
22+
for i := 1; i < 1000; i++ {
23+
term = term.Mul(term, x)
24+
term = term.Quo(term, big.NewFloat(float64(i)))
25+
old := new(big.Float).Copy(result)
26+
result = result.Add(result, term)
27+
if term.Cmp(new(big.Float).SetFloat64(0)) == 0 {
28+
break
29+
}
30+
if old.Cmp(result) == 0 {
31+
break
32+
}
33+
}
34+
return result
35+
}
36+
37+
func logBig(y *big.Float) *big.Float {
38+
if y.Sign() <= 0 {
39+
panic("log of non-positive number")
40+
}
41+
yf, _ := y.Float64()
42+
z := big.NewFloat(math.Log(yf)).SetPrec(y.Prec())
43+
for range 50 {
44+
expz := expBig(z)
45+
adjustment := new(big.Float).Quo(y, expz)
46+
z = z.Add(z, adjustment)
47+
z = z.Sub(z, big.NewFloat(1))
48+
}
49+
return z
50+
}
51+
52+
func (curve *ExponentialCurve) SpotPriceAtSupply(currentSupply *big.Float) *big.Float {
53+
cTimesS := new(big.Float).Mul(curve.c, currentSupply)
54+
exp := expBig(cTimesS)
55+
return new(big.Float).Mul(new(big.Float).Mul(curve.a, curve.b), exp)
56+
}
57+
58+
func (curve *ExponentialCurve) TokensToValue(currentSupply, tokens *big.Float) *big.Float {
59+
newSupply := new(big.Float).Add(currentSupply, tokens)
60+
cs := new(big.Float).Mul(curve.c, currentSupply)
61+
ns := new(big.Float).Mul(curve.c, newSupply)
62+
expCS := expBig(cs)
63+
expNS := expBig(ns)
64+
abOverC := new(big.Float).Quo(new(big.Float).Mul(curve.a, curve.b), curve.c)
65+
diff := new(big.Float).Sub(expNS, expCS)
66+
return new(big.Float).Mul(abOverC, diff)
67+
}
68+
69+
func (curve *ExponentialCurve) ValueToTokens(currentSupply, value *big.Float) *big.Float {
70+
abOverC := new(big.Float).Quo(new(big.Float).Mul(curve.a, curve.b), curve.c)
71+
expCS := expBig(new(big.Float).Mul(curve.c, currentSupply))
72+
term := new(big.Float).Add(new(big.Float).Quo(value, abOverC), expCS)
73+
lnTerm := logBig(term)
74+
result := new(big.Float).Quo(lnTerm, curve.c)
75+
return new(big.Float).Sub(result, currentSupply)
76+
}
77+
78+
func DefaultExponentialCurve() ExponentialCurve {
79+
scale, ok := new(big.Float).SetPrec(defaultPrec).SetString("1000000000000000000") // 10^18
80+
if !ok {
81+
panic("Invalid scale string")
82+
}
83+
84+
aInt, ok := new(big.Int).SetString("11400230149967394933471", 10)
85+
if !ok {
86+
panic("Invalid CURVE_A string")
87+
}
88+
89+
bInt, ok := new(big.Int).SetString("877175273521", 10)
90+
if !ok {
91+
panic("Invalid CURVE_B string")
92+
}
93+
94+
a := new(big.Float).Quo(new(big.Float).SetInt(aInt).SetPrec(defaultPrec), scale)
95+
b := new(big.Float).Quo(new(big.Float).SetInt(bInt).SetPrec(defaultPrec), scale)
96+
c := new(big.Float).Copy(b)
97+
98+
return ExponentialCurve{a: a, b: b, c: c}
99+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package currencycreator
2+
3+
import (
4+
"fmt"
5+
"math/big"
6+
"testing"
7+
)
8+
9+
func TestCalculateCurveConstants(t *testing.T) {
10+
curve := DefaultExponentialCurve()
11+
12+
// Check R'(0) with tolerance
13+
spot0 := curve.SpotPriceAtSupply(big.NewFloat(0))
14+
expectedStart := big.NewFloat(0.01)
15+
diff := new(big.Float).Sub(spot0, expectedStart)
16+
threshold, _ := new(big.Float).SetString("0.0000000001")
17+
if diff.Abs(diff).Cmp(threshold) > 0 {
18+
t.Errorf("Spot at 0: got %s, expected 0.01", spot0.Text('f', 18))
19+
}
20+
21+
// Check R'(21000000) with tolerance
22+
supplyEnd := big.NewFloat(21000000)
23+
spotEnd := curve.SpotPriceAtSupply(supplyEnd)
24+
expectedEnd := big.NewFloat(1000000)
25+
diff = new(big.Float).Sub(spotEnd, expectedEnd)
26+
threshold, _ = new(big.Float).SetString("0.0001")
27+
if diff.Abs(diff).Cmp(threshold) > 0 {
28+
t.Errorf("Spot at end: got %s, expected 1000000", spotEnd.Text('f', 18))
29+
}
30+
}
31+
32+
func TestGenerateCurveTable(t *testing.T) {
33+
t.Skip()
34+
35+
curve := DefaultExponentialCurve()
36+
37+
fmt.Println("|------|----------------|----------------------------------|----------------------------|")
38+
fmt.Println("| % | S | R(S) | R'(S) |")
39+
fmt.Println("|------|----------------|----------------------------------|----------------------------|")
40+
41+
zero := big.NewFloat(0)
42+
buyAmount := big.NewFloat(210000)
43+
supply := new(big.Float).Copy(zero)
44+
45+
for i := 0; i <= 100; i++ {
46+
cost := curve.TokensToValue(zero, supply)
47+
spotPrice := curve.SpotPriceAtSupply(supply)
48+
49+
fmt.Printf("| %3d%% | %14s | %32s | %26s |\n",
50+
i,
51+
supply.Text('f', 0),
52+
cost.Text('f', 18),
53+
spotPrice.Text('f', 18))
54+
55+
supply = supply.Add(supply, buyAmount)
56+
}
57+
58+
fmt.Println("|------|----------------|----------------------------------|----------------------------|")
59+
}

0 commit comments

Comments
 (0)