Smart Contract
FixesBondingCurve
A.d2abb5dbf5e08666.FixesBondingCurve
1/**
2
3> Author: Fixes Lab <https://github.com/fixes-world/>
4
5# FixesBondingCurve
6
7This is a bonding curve contract that uses a Quadratic curve to calculate the price of a token based on the token's supply.
8
9*/
10// Third-party dependencies
11import SwapConfig from 0xb78ef7afa52ff906
12
13/// The bonding curve contract.
14///
15access(all) contract FixesBondingCurve {
16
17 /// -------- Resources and Interfaces --------
18
19 /// The curve interface.
20 ///
21 access(all) struct interface CurveInterface {
22 access(all)
23 view fun getFreeAmount(): UFix64 { return 0.0 }
24 access(all)
25 view fun calculatePrice(supply: UFix64, amount: UFix64): UFix64
26 access(all)
27 view fun calculateAmount(supply: UFix64, cost: UFix64): UFix64
28 /// Calculate the unit price of the token
29 access(all)
30 view fun calculateUnitPrice(supply: UFix64): UFix64 {
31 let freeAmount = self.getFreeAmount()
32 let restFreeAmount = freeAmount.saturatingSubtract(supply)
33 return self.calculatePrice(supply: supply, amount: restFreeAmount + 1.0)
34 }
35 }
36
37 /// The Quadratic curve implementation.
38 /// This formula is used to calculate the price of a token based on the token's supply.
39 /// Inspired by FriendTech: https://basescan.org/address/0xcf205808ed36593aa40a44f10c7f7c2f67d4a4d4#code
40 /// Formula:
41 /// - Price = ((CurrentSupply - FreeAmount) * Coefficient)^2
42 /// -> y = x^2
43 /// -> SumY = 1^2 + 2^2 + 3^2 + ... + x^2 = x * (x + 1) * (2 * x + 1) / 6
44 /// - Sum1 -> CurrentSupply < FreeAmount ? 0 : SumY(CurrentSupply - FreeAmount)
45 /// - Sum2 -> CurrentSupply + Amount <= FreeAmount ? 0 : SumY(CurrentSupply - FreeAmount + Amount)
46 /// - BuyPrice = (sum2 - sum1) * Coefficient^2
47 /// Coefficient = Sqrt(1 / (Sqrt(maxSupply) + maxSupply/2))
48 /// -> priceCoefficient = 1 / Coefficient^2 = maxSupply / 2 * Sqrt(maxSupply)
49 ///
50 access(all) struct Quadratic: CurveInterface {
51 access(all)
52 let freeScaledAmount: UInt256
53 access(all)
54 let priceCoefficient: UInt256
55
56 init(
57 freeAmount: UFix64?,
58 maxSupply: UFix64?
59 ) {
60 self.freeScaledAmount = SwapConfig.UFix64ToScaledUInt256(freeAmount ?? 0.0)
61 let max = SwapConfig.UFix64ToScaledUInt256(maxSupply ?? UFix64.max)
62 assert(
63 max > 0,
64 message: "Max supply must be greater than 0"
65 )
66 self.priceCoefficient = max / 2 / SwapConfig.scaleFactor * SwapConfig.sqrt(max) / SwapConfig.sqrt(SwapConfig.scaleFactor)
67 }
68
69 /// Get the free amount
70 ///
71 access(all)
72 view fun getFreeAmount(): UFix64 { return SwapConfig.ScaledUInt256ToUFix64(self.freeScaledAmount) }
73
74 /// Get the price of the token based on the supply and amount
75 ///
76 access(all)
77 view fun calculatePrice(supply: UFix64, amount: UFix64): UFix64 {
78 // avoid useless calculation
79 if amount == 0.0 {
80 return 0.0
81 }
82
83 let scaledX = SwapConfig.UFix64ToScaledUInt256(supply)
84 let scaledDeltaX = SwapConfig.UFix64ToScaledUInt256(amount)
85 let ufix64Max = SwapConfig.UFix64ToScaledUInt256(UFix64.max)
86 // calculate the sum of squares
87 let x0 = scaledX.saturatingSubtract(self.freeScaledAmount)
88 let sum0: UInt256 = scaledX < self.freeScaledAmount
89 ? 0
90 : x0 * (x0 + 1) / SwapConfig.scaleFactor * (2 * x0 + 1) / 6 / SwapConfig.scaleFactor
91 // calculate the sum of squares after adding the amount
92 let x1 = scaledX.saturatingAdd(scaledDeltaX).saturatingSubtract(self.freeScaledAmount)
93 let sum1: UInt256 = scaledX + scaledDeltaX <= self.freeScaledAmount
94 ? 0
95 : x1 * (x1 + 1) / SwapConfig.scaleFactor * (2 * x1 + 1) / 6 / SwapConfig.scaleFactor
96 let summation = sum1 - sum0
97 // free tokens
98 if summation == 0 {
99 return 0.0
100 }
101 let fixedPriceCoefficient = self.priceCoefficient / 250000 * self.priceCoefficient
102 let price = summation / fixedPriceCoefficient
103 if price > ufix64Max {
104 return UFix64.max
105 }
106 let ret = SwapConfig.ScaledUInt256ToUFix64(price)
107 // set the minimum price for none-free tokens
108 if ret == 0.0 {
109 return 0.00000001
110 }
111 return ret
112 }
113
114 /// Calculate the amount of tokens that can be bought with the given cost
115 ///
116 access(all)
117 view fun calculateAmount(supply: UFix64, cost: UFix64): UFix64 {
118 // avoid useless calculation
119 if cost == 0.0 {
120 return 0.0
121 }
122
123 let freeAmount = self.getFreeAmount()
124 let restFreeAmount = freeAmount.saturatingSubtract(supply)
125 var supplyOnePrice = self.calculatePrice(supply: supply, amount: restFreeAmount + 1.0)
126 if supplyOnePrice == 0.0 {
127 supplyOnePrice = 0.00000001
128 }
129
130 let minH = 0.01
131 var low = restFreeAmount
132 var high = restFreeAmount + cost / supplyOnePrice
133 var finalAmount = low.saturatingAdd(high).saturatingMultiply(0.5)
134
135 var calcCost = self.calculatePrice(supply: supply, amount: finalAmount)
136 // binary search
137 while calcCost > cost.saturatingAdd(minH) || calcCost < cost.saturatingSubtract(minH) {
138 if calcCost > cost {
139 high = finalAmount
140 } else {
141 low = finalAmount
142 }
143 finalAmount = low.saturatingAdd(high) * 0.5
144 calcCost = self.calculatePrice(supply: supply, amount: finalAmount)
145 }
146 return finalAmount
147 }
148 }
149}
150