Smart Contract

FixesBondingCurve

A.d2abb5dbf5e08666.FixesBondingCurve

Valid From

86,129,099

Deployed

3d ago
Feb 24, 2026, 11:54:28 PM UTC

Dependents

0 imports
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