Skip to content

Commit 11b3d24

Browse files
authored
Currency creator Solana program client and utilities (#201)
* Add initial bonding curve implementation * Implement Go interface to currency creator Solana program * Update currency creator program public key * Minor tweaks and constant additions
1 parent 6589e71 commit 11b3d24

18 files changed

+1280
-100
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package currencycreator
2+
3+
import (
4+
"bytes"
5+
"crypto/ed25519"
6+
"fmt"
7+
8+
"github.com/mr-tron/base58"
9+
)
10+
11+
const (
12+
DefaultMintMaxSupply = 21_000_000_000_000 // 21mm tokens with 6 decimals
13+
DefaultMintDecimals = 6
14+
)
15+
16+
const (
17+
MaxCurrencyConfigAccountNameLength = 32
18+
MaxCurrencyConfigAccountSymbolLength = 8
19+
)
20+
21+
const (
22+
CurrencyConfigAccountSize = (8 + //discriminator
23+
32 + // authority
24+
32 + // creator
25+
32 + // mint
26+
MaxCurrencyConfigAccountNameLength + // name
27+
MaxCurrencyConfigAccountSymbolLength + // symbol
28+
32 + // seed
29+
8 + // max_supply
30+
8 + // current_supply
31+
1 + // decimal_places
32+
1 + // bump
33+
1 + // mint_bump
34+
5) // padding
35+
)
36+
37+
var CurrencyConfigAccountDiscriminator = []byte{byte(AccountTypeCurrencyConfig), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
38+
39+
type CurrencyConfigAccount struct {
40+
Authority ed25519.PublicKey
41+
Creator ed25519.PublicKey
42+
Mint ed25519.PublicKey
43+
Name string
44+
Symbol string
45+
Seed ed25519.PublicKey
46+
MaxSupply uint64
47+
CurrentSupply uint64
48+
DecimalPlaces uint8
49+
Bump uint8
50+
MintBump uint8
51+
}
52+
53+
func (obj *CurrencyConfigAccount) Unmarshal(data []byte) error {
54+
if len(data) < CurrencyConfigAccountSize {
55+
return ErrInvalidAccountData
56+
}
57+
58+
var offset int
59+
60+
var discriminator []byte
61+
getDiscriminator(data, &discriminator, &offset)
62+
if !bytes.Equal(discriminator, CurrencyConfigAccountDiscriminator) {
63+
return ErrInvalidAccountData
64+
}
65+
66+
getKey(data, &obj.Authority, &offset)
67+
getKey(data, &obj.Creator, &offset)
68+
getKey(data, &obj.Mint, &offset)
69+
getFixedString(data, &obj.Name, MaxCurrencyConfigAccountNameLength, &offset)
70+
getFixedString(data, &obj.Symbol, MaxCurrencyConfigAccountSymbolLength, &offset)
71+
getKey(data, &obj.Seed, &offset)
72+
getUint64(data, &obj.MaxSupply, &offset)
73+
getUint64(data, &obj.CurrentSupply, &offset)
74+
getUint8(data, &obj.DecimalPlaces, &offset)
75+
getUint8(data, &obj.Bump, &offset)
76+
getUint8(data, &obj.MintBump, &offset)
77+
offset += 5 // padding
78+
79+
return nil
80+
}
81+
82+
func (obj *CurrencyConfigAccount) String() string {
83+
return fmt.Sprintf(
84+
"CurrencyConfig{authority=%s,creator=%s,mint=%s,name=%s,symbol=%s,seed=%s,max_supply=%d,currency_supply=%d,decimal_places=%d,bump=%d,mint_bump=%d}",
85+
base58.Encode(obj.Authority),
86+
base58.Encode(obj.Creator),
87+
base58.Encode(obj.Mint),
88+
obj.Name,
89+
obj.Symbol,
90+
base58.Encode(obj.Seed),
91+
obj.MaxSupply,
92+
obj.CurrentSupply,
93+
obj.DecimalPlaces,
94+
obj.Bump,
95+
obj.MintBump,
96+
)
97+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package currencycreator
2+
3+
import (
4+
"bytes"
5+
"crypto/ed25519"
6+
"fmt"
7+
"time"
8+
9+
"github.com/mr-tron/base58"
10+
)
11+
12+
const (
13+
DefaultBuyCap = 0 // unlimited base tokens
14+
DefaultSaleCap = 0 // unlimited target tokens
15+
16+
DefaultBuyFeeBps = 0 // 0% fee
17+
DefaultSellFeeBps = 100 // 1% fee
18+
)
19+
20+
const (
21+
LiquidityPoolAccountSize = (8 + //discriminator
22+
32 + // authority
23+
32 + // currency
24+
32 + // target_mint
25+
32 + // base_mint
26+
32 + // vault_target
27+
32 + // vault_base
28+
32 + // fee_target
29+
32 + // fee_base
30+
4 + // buy_fee
31+
4 + // sell_fee
32+
8 + // created_unix_time
33+
8 + // go_live_unix_time
34+
8 + // purchase_cap
35+
8 + // sale_cap
36+
RawExponentialCurveSize + // curve
37+
8 + // supply_from_baseonding
38+
1 + // bump
39+
1 + // currency_baseump
40+
1 + // vault_target_baseump
41+
1 + // vault_base_baseump
42+
4) // padding
43+
)
44+
45+
var LiquidityPoolAccountDiscriminator = []byte{byte(AccountTypeLiquidityPool), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
46+
47+
type LiquidityPoolAccount struct {
48+
Authority ed25519.PublicKey
49+
Currency ed25519.PublicKey
50+
TargetMint ed25519.PublicKey
51+
BaseMint ed25519.PublicKey
52+
VaultTarget ed25519.PublicKey
53+
VaultBase ed25519.PublicKey
54+
FeeTarget ed25519.PublicKey
55+
FeeBase ed25519.PublicKey
56+
BuyFee uint32
57+
SellFee uint32
58+
CreatedUnixTime int64
59+
GoLiveUnixTime int64
60+
PurchaseCap uint64
61+
SaleCap uint64
62+
Curve RawExponentialCurve
63+
SupplyFromBonding uint64
64+
Bump uint8
65+
CurrencyBump uint8
66+
VaultTargetBump uint8
67+
VaultBaseBump uint8
68+
}
69+
70+
func (obj *LiquidityPoolAccount) Unmarshal(data []byte) error {
71+
if len(data) < LiquidityPoolAccountSize {
72+
return ErrInvalidAccountData
73+
}
74+
75+
var offset int
76+
77+
var discriminator []byte
78+
getDiscriminator(data, &discriminator, &offset)
79+
if !bytes.Equal(discriminator, LiquidityPoolAccountDiscriminator) {
80+
return ErrInvalidAccountData
81+
}
82+
83+
getKey(data, &obj.Authority, &offset)
84+
getKey(data, &obj.Currency, &offset)
85+
getKey(data, &obj.TargetMint, &offset)
86+
getKey(data, &obj.BaseMint, &offset)
87+
getKey(data, &obj.VaultTarget, &offset)
88+
getKey(data, &obj.VaultBase, &offset)
89+
getKey(data, &obj.FeeTarget, &offset)
90+
getKey(data, &obj.FeeBase, &offset)
91+
getUint32(data, &obj.BuyFee, &offset)
92+
getUint32(data, &obj.SellFee, &offset)
93+
getInt64(data, &obj.CreatedUnixTime, &offset)
94+
getInt64(data, &obj.GoLiveUnixTime, &offset)
95+
getUint64(data, &obj.PurchaseCap, &offset)
96+
getUint64(data, &obj.SaleCap, &offset)
97+
getRawExponentialCurve(data, &obj.Curve, &offset)
98+
getUint64(data, &obj.SupplyFromBonding, &offset)
99+
getUint8(data, &obj.Bump, &offset)
100+
getUint8(data, &obj.CurrencyBump, &offset)
101+
getUint8(data, &obj.VaultTargetBump, &offset)
102+
getUint8(data, &obj.VaultBaseBump, &offset)
103+
offset += 4 // padding
104+
105+
return nil
106+
}
107+
108+
func (obj *LiquidityPoolAccount) String() string {
109+
return fmt.Sprintf(
110+
"LiquidityPool{authority=%s,currency=%s,target_mint=%s,base_mint=%s,vault_target=%s,vault_base=%s,fee_target=%s,fee_base=%s,buy_fee=%d,sell_fee=%d,created_unix_time=%s,go_live_unix_time=%s,purchase_cap=%d,sale_cap=%d,curve=%s,bump=%d,currency_bump=%d,vault_target_bump=%d,vault_base_bump=%d}",
111+
base58.Encode(obj.Authority),
112+
base58.Encode(obj.Currency),
113+
base58.Encode(obj.TargetMint),
114+
base58.Encode(obj.BaseMint),
115+
base58.Encode(obj.VaultTarget),
116+
base58.Encode(obj.VaultBase),
117+
base58.Encode(obj.FeeTarget),
118+
base58.Encode(obj.FeeBase),
119+
obj.BuyFee,
120+
obj.SellFee,
121+
time.Unix(obj.CreatedUnixTime, 0).UTC().String(),
122+
time.Unix(obj.GoLiveUnixTime, 0).UTC().String(),
123+
obj.PurchaseCap,
124+
obj.SaleCap,
125+
obj.Curve.String(),
126+
obj.Bump,
127+
obj.CurrencyBump,
128+
obj.VaultTargetBump,
129+
obj.VaultBaseBump,
130+
)
131+
}

pkg/solana/currencycreator/address.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package currencycreator
2+
3+
import (
4+
"crypto/ed25519"
5+
6+
"github.com/code-payments/code-server/pkg/solana"
7+
)
8+
9+
var (
10+
MintPrefix = []byte("mint")
11+
CurrencyPrefix = []byte("currency")
12+
PoolPrefix = []byte("pool")
13+
TreasuryPrefix = []byte("treasury")
14+
MetadataPrefix = []byte("metadata")
15+
)
16+
17+
type GetMintAddressArgs struct {
18+
Authority ed25519.PublicKey
19+
Name string
20+
Seed ed25519.PublicKey
21+
}
22+
23+
func GetMintAddress(args *GetMintAddressArgs) (ed25519.PublicKey, uint8, error) {
24+
return solana.FindProgramAddressAndBump(
25+
PROGRAM_ID,
26+
MintPrefix,
27+
args.Authority,
28+
[]byte(toFixedString(args.Name, MaxCurrencyConfigAccountNameLength)),
29+
args.Seed,
30+
)
31+
}
32+
33+
type GetCurrencyAddressArgs struct {
34+
Mint ed25519.PublicKey
35+
}
36+
37+
func GetCurrencyAddress(args *GetCurrencyAddressArgs) (ed25519.PublicKey, uint8, error) {
38+
return solana.FindProgramAddressAndBump(
39+
PROGRAM_ID,
40+
CurrencyPrefix,
41+
args.Mint,
42+
)
43+
}
44+
45+
type GetPoolAddressArgs struct {
46+
Currency ed25519.PublicKey
47+
}
48+
49+
func GetPoolAddress(args *GetPoolAddressArgs) (ed25519.PublicKey, uint8, error) {
50+
return solana.FindProgramAddressAndBump(
51+
PROGRAM_ID,
52+
PoolPrefix,
53+
args.Currency,
54+
)
55+
}
56+
57+
type GetVaultAddressArgs struct {
58+
Pool ed25519.PublicKey
59+
Mint ed25519.PublicKey
60+
}
61+
62+
func GetVaultAddress(args *GetVaultAddressArgs) (ed25519.PublicKey, uint8, error) {
63+
return solana.FindProgramAddressAndBump(
64+
PROGRAM_ID,
65+
TreasuryPrefix,
66+
args.Pool,
67+
args.Mint,
68+
)
69+
}
70+
71+
type GetMetadataAddressArgs struct {
72+
Mint ed25519.PublicKey
73+
}
74+
75+
func GetMetadataAddress(args *GetMetadataAddressArgs) (ed25519.PublicKey, uint8, error) {
76+
return solana.FindProgramAddressAndBump(
77+
METADATA_PROGRAM_ID,
78+
MetadataPrefix,
79+
METADATA_PROGRAM_ID,
80+
args.Mint,
81+
)
82+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package currencycreator
2+
3+
import (
4+
"math"
5+
"math/big"
6+
)
7+
8+
type EstimateCurrentPriceArgs struct {
9+
Curve *ExponentialCurve
10+
CurrentSupplyInQuarks uint64
11+
MintDecimals uint8
12+
}
13+
14+
func EstimateCurrentPrice(args *EstimateCurrentPriceArgs) *big.Float {
15+
scale := big.NewFloat(math.Pow10(int(args.MintDecimals))).SetPrec(defaultExponentialCurvePrec)
16+
unscaledCurrentSupply := big.NewFloat(float64(args.CurrentSupplyInQuarks)).SetPrec(defaultExponentialCurvePrec)
17+
scaledCurrentSupply := new(big.Float).Quo(unscaledCurrentSupply, scale)
18+
return args.Curve.SpotPriceAtSupply(scaledCurrentSupply)
19+
}
20+
21+
type EstimateBuyArgs struct {
22+
BuyAmountInQuarks uint64
23+
Curve *ExponentialCurve
24+
CurrentSupplyInQuarks uint64
25+
BuyFeeBps uint32
26+
TargetMintDecimals uint8
27+
BaseMintDecimals uint8
28+
}
29+
30+
func EstimateBuy(args *EstimateBuyArgs) (uint64, uint64) {
31+
scale := big.NewFloat(math.Pow10(int(args.BaseMintDecimals))).SetPrec(defaultExponentialCurvePrec)
32+
unscaledBuyAmount := big.NewFloat(float64(args.BuyAmountInQuarks)).SetPrec(defaultExponentialCurvePrec)
33+
scaledBuyAmount := new(big.Float).Quo(unscaledBuyAmount, scale)
34+
35+
scale = big.NewFloat(math.Pow10(int(args.TargetMintDecimals))).SetPrec(defaultExponentialCurvePrec)
36+
unscaledCurrentSupply := big.NewFloat(float64(args.CurrentSupplyInQuarks)).SetPrec(defaultExponentialCurvePrec)
37+
scaledCurrentSupply := new(big.Float).Quo(unscaledCurrentSupply, scale)
38+
39+
scale = big.NewFloat(math.Pow10(int(args.TargetMintDecimals))).SetPrec(defaultExponentialCurvePrec)
40+
scaledTotalValue := args.Curve.ValueToTokens(scaledCurrentSupply, scaledBuyAmount)
41+
unscaledTotalValue := new(big.Float).Mul(scaledTotalValue, scale)
42+
43+
feePctValue := new(big.Float).SetPrec(defaultExponentialCurvePrec).Quo(big.NewFloat(float64(args.BuyFeeBps)), big.NewFloat(10000))
44+
scaledFees := new(big.Float).Mul(scaledTotalValue, feePctValue)
45+
unscaledFees := new(big.Float).Mul(scaledFees, scale)
46+
47+
total, _ := unscaledTotalValue.Int64()
48+
fees, _ := unscaledFees.Int64()
49+
return uint64(total - fees), uint64(fees)
50+
}
51+
52+
type EstimateSaleArgs struct {
53+
SellAmountInQuarks uint64
54+
Curve *ExponentialCurve
55+
CurrentSupplyInQuarks uint64
56+
SellFeeBps uint32
57+
TargetMintDecimals uint8
58+
BaseMintDecimals uint8
59+
}
60+
61+
func EstimateSale(args *EstimateSaleArgs) (uint64, uint64) {
62+
scale := big.NewFloat(math.Pow10(int(args.TargetMintDecimals))).SetPrec(defaultExponentialCurvePrec)
63+
unscaledSellAmount := big.NewFloat(float64(args.SellAmountInQuarks)).SetPrec(defaultExponentialCurvePrec)
64+
scaledSellAmount := new(big.Float).Quo(unscaledSellAmount, scale)
65+
66+
scale = big.NewFloat(math.Pow10(int(args.TargetMintDecimals))).SetPrec(defaultExponentialCurvePrec)
67+
unscaledCurrentSupply := big.NewFloat(float64(args.CurrentSupplyInQuarks)).SetPrec(defaultExponentialCurvePrec)
68+
scaledCurrentSupply := new(big.Float).Quo(unscaledCurrentSupply, scale)
69+
70+
scale = big.NewFloat(math.Pow10(int(args.BaseMintDecimals))).SetPrec(defaultExponentialCurvePrec)
71+
scaledTotalValue := args.Curve.TokensToValue(scaledCurrentSupply, scaledSellAmount)
72+
unscaledTotalValue := new(big.Float).Mul(scaledTotalValue, scale)
73+
74+
feePctValue := new(big.Float).SetPrec(defaultExponentialCurvePrec).Quo(big.NewFloat(float64(args.SellFeeBps)), big.NewFloat(10000))
75+
scaledFees := new(big.Float).Mul(scaledTotalValue, feePctValue)
76+
unscaledFees := new(big.Float).Mul(scaledFees, scale)
77+
78+
total, _ := unscaledTotalValue.Int64()
79+
fees, _ := unscaledFees.Int64()
80+
return uint64(total - fees), uint64(fees)
81+
}

0 commit comments

Comments
 (0)