You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/blockchain-development-tutorials/forte/fixed-point-128-bit
+39-43Lines changed: 39 additions & 43 deletions
Original file line number
Diff line number
Diff line change
@@ -17,7 +17,7 @@ sidebar_label: DeFi Math Utils
17
17
18
18
# High-Precision Fixed-Point 128 Bit Math
19
19
20
-
`DeFiActionsMathUtils` provides a standardized library for high-precision mathematical operations in DeFi applications on Flow. The contract extends Cadence's native 8-decimal precision (`UFix64`) to 24 decimals using `UInt128` for intermediate calculations, ensuring accuracy in complex financial computations while maintaining deterministic results across the network.
20
+
Dealing with decimals is a notorious issue for most developers on other chains, especially when working with DeFi. Blockchains are deterministic systems and floating-point arithmetic is non-deterministic across different compilers and architectures, this is why blockchains use fixed-point arithmetic via integers (scaling numbers by a fixed factor). The issue with this is that these fixed-point integers tend to be very imprecise when using various mathematical operations on them. The more operations you apply to these numbers, the more imprecise these numbers become. However [`DeFiActionsMathUtils`] provides a standardized library for high-precision mathematical operations in DeFi applications on Flow. The contract extends Cadence's native 8-decimal precision (`UFix64`) to 24 decimals using `UInt128` for intermediate calculations, ensuring accuracy in complex financial computations while maintaining deterministic results across the network.
21
21
22
22
Through integration of this math utility library, developers can ensure that their DeFi protocols perform precise calculations for liquidity pools, yield farming, token swaps, and other financial operations without accumulating rounding errors.
23
23
@@ -50,11 +50,11 @@ let output = afterFee * price // More precision lost
50
50
let finalAmount = output / someRatio // Even more precision lost
51
51
```
52
52
53
-
After 3-4 sequential operations, the cumulative rounding error can be significant, especially when dealing with large amounts.
53
+
After 3-4 sequential operations, the cumulative rounding error can be significant, especially when dealing with large amounts. Assuming a rounding error with 8 decimals (1.234567885 rounds up to 1.23456789, causing a rounding error of 0.000000005), then after 100 operations with this error and dealing with 1 million dollars USDF, the protocol is losing $0.5 in revenue from this lack of precision. This might not seem like a lot, but if we consider the TVL of Aave, which is around 40 billion USD, then that loss results in $20,000 USD!
54
54
55
55
## The Solution: 24-Decimal Precision
56
56
57
-
`DeFiActionsMathUtils` solves this by using `UInt128` to represent fixed-point numbers with 24 decimal places (scaling factor of 10^24). This provides 16 additional decimal places for intermediate calculations, dramatically reducing precision loss.
57
+
[`DeFiActionsMathUtils`] solves this by using `UInt128` to represent fixed-point numbers with 24 decimal places (scaling factor of 10^24). This provides 16 additional decimal places for intermediate calculations, dramatically reducing precision loss.
58
58
59
59
:::Warning
60
60
@@ -100,48 +100,50 @@ These constants ensure consistent scaling across all operations.
100
100
101
101
## Rounding Modes
102
102
103
-
Financial applications require different rounding strategies depending on the context. `DeFiActionsMathUtils` provides four rounding modes:
103
+
Smart rounding is the strategic selection of rounding strategies based on the financial context of your calculation. After performing high-precision calculations at 24 decimals, the final results must be converted back to `UFix64` (8 decimals), and how you handle this conversion can protect your protocol from losses, ensure fairness to users, and reduce systematic bias.
104
+
105
+
[`DeFiActionsMathUtils`] provides four rounding modes, each optimized for specific financial scenarios:
104
106
105
107
```cadence
106
108
access(all) enum RoundingMode: UInt8 {
107
109
/// Rounds down (floor) - use for payouts
108
110
access(all) case RoundDown
109
-
111
+
110
112
/// Rounds up (ceiling) - use for fees/liabilities
111
113
access(all) case RoundUp
112
-
114
+
113
115
/// Standard rounding: < 0.5 down, >= 0.5 up
114
116
access(all) case RoundHalfUp
115
-
117
+
116
118
/// Banker's rounding: ties round to even number
117
119
access(all) case RoundEven
118
120
}
119
121
```
120
122
121
123
### When to Use Each Mode
122
124
123
-
**RoundDown** - Conservative for user payouts:
125
+
**RoundDown** - Choose this when calculating user payouts, withdrawals, or rewards. By rounding down, your protocol retains any fractional amounts, protecting against losses from accumulated rounding errors. This is the conservative choice when funds leave your protocol.
124
126
125
127
```cadence
126
128
// When calculating how much to pay out to users
127
129
let userReward = DeFiActionsMathUtils.toUFix64RoundDown(calculatedReward)
128
130
```
129
131
130
-
**RoundUp** - Conservative for protocol fees:
132
+
**RoundUp** - Use this for protocol fees, transaction costs, or amounts owed to your protocol. Rounding up ensures your protocol collects slightly more, compensating for precision loss and preventing systematic under-collection of fees over many transactions.
131
133
132
134
```cadence
133
135
// When calculating fees the protocol collects
134
136
let protocolFee = DeFiActionsMathUtils.toUFix64RoundUp(calculatedFee)
135
137
```
136
138
137
-
**RoundHalfUp** - Standard financial rounding:
139
+
**RoundHalfUp** - Apply this for general-purpose calculations, display values, or when presenting prices to users. This is the familiar rounding method (values 0.5 and above round up, below 0.5 round down) that users expect in traditional finance.
138
140
139
141
```cadence
140
142
// For display values or general calculations
141
143
let displayValue = DeFiActionsMathUtils.toUFix64Round(calculatedValue)
142
144
```
143
145
144
-
**RoundEven** - Reduces cumulative rounding bias:
146
+
**RoundEven** - Select this for scenarios involving many repeated calculations where you want to minimize systematic bias. Also known as "[banker's rounding]", this mode rounds ties (exactly 0.5) to the nearest even number, which statistically balances out over many operations, making it ideal for large-scale distributions or statistical calculations.
145
147
146
148
```cadence
147
149
// For repeated operations where bias matters
@@ -216,8 +218,12 @@ let result = DeFiActionsMathUtils.toUFix64Round(totalValue)
216
218
// result = 1500.0
217
219
```
218
220
221
+
:::info
222
+
219
223
**Important:** The multiplication uses `UInt256` internally to prevent overflow:
Different financial scenarios require different rounding approaches:
459
-
460
-
```cadence
461
-
// Conservative for payouts (protect protocol)
462
-
let payout = DeFiActionsMathUtils.toUFix64RoundDown(calculatedPayout)
463
-
464
-
// Conservative for fees (protect protocol)
465
-
let fee = DeFiActionsMathUtils.toUFix64RoundUp(calculatedFee)
466
-
467
-
// Fair for display
468
-
let display = DeFiActionsMathUtils.toUFix64Round(calculatedValue)
469
-
```
470
-
471
-
### Gas Efficiency
472
-
473
-
Integer arithmetic is computationally cheaper than floating-point emulation:
474
-
475
-
- Predictable gas costs
476
-
- Fast execution
477
-
- No complex floating-point library needed
478
-
479
462
## Best Practices
480
463
481
464
Always Use High Precision for Intermediate Calculations
482
465
483
-
**❌ Don't do this:**
466
+
**❌ Low precision (loses ~$0.50 per 1M USDC):**
484
467
485
468
```cadence
486
469
let fee: UFix64 = amount * 0.003
487
470
let afterFee: UFix64 = amount - fee
488
471
let output: UFix64 = afterFee * price
489
472
```
490
473
491
-
**✅ Do this instead:**
474
+
**✅ High precision (safe and accurate):**
492
475
493
476
```cadence
477
+
// Convert once at the start
494
478
let amountHP = DeFiActionsMathUtils.toUInt128(amount)
495
479
let feeRate = DeFiActionsMathUtils.toUInt128(0.003)
496
480
let priceHP = DeFiActionsMathUtils.toUInt128(price)
497
481
498
-
let one = DeFiActionsMathUtils.toUInt128(1.0)
499
-
let afterFeeMultiplier = one - feeRate
500
-
let afterFeeHP = DeFiActionsMathUtils.mul(amountHP, afterFeeMultiplier)
482
+
// Perform all calculations at high precision
483
+
let afterFeeHP = DeFiActionsMathUtils.mul(amountHP, DeFiActionsMathUtils.toUInt128(1.0) - feeRate)
501
484
let outputHP = DeFiActionsMathUtils.mul(afterFeeHP, priceHP)
502
485
486
+
// Convert once at the end with smart rounding
503
487
let output = DeFiActionsMathUtils.toUFix64RoundDown(outputHP)
504
488
```
505
489
490
+
The pattern is simple: **convert → calculate → convert back**. The extra lines give you production-grade precision that protects your protocol from financial losses.
491
+
506
492
Always validate that inputs are within acceptable ranges:
507
493
508
494
```cadence
@@ -519,9 +505,9 @@ access(all) fun swap(inputAmount: UFix64) {
519
505
520
506
## More Resources
521
507
522
-
- [View the DeFiActionsMathUtils source code](https://github.com/onflow/FlowActions/blob/main/cadence/contracts/utils/DeFiActionsMathUtils.cdc)
@@ -533,4 +519,14 @@ access(all) fun swap(inputAmount: UFix64) {
533
519
534
520
## Conclusion
535
521
536
-
`DeFiActionsMathUtils` provides the mathematical foundation for building accurate and secure DeFi applications on Flow. By using 24-decimal fixed-point arithmetic with `UInt128`, you can perform complex financial calculations without the precision loss that would occur with native `UFix64` operations alone.
522
+
[`DeFiActionsMathUtils`] gives Flow developers a significant advantage in building DeFi applications. With 24-decimal precision, it is orders of magnitude more accurate than typical blockchain implementations (which use 6-18 decimals). The standardized library eliminates the need to build custom math implementations.
523
+
524
+
The simple **convert → calculate → convert back** pattern, combined with strategic rounding modes and built-in overflow protection, means you can focus on your protocol's business logic instead of low-level precision handling. At scale, this protection prevents thousands of dollars in losses from accumulated rounding errors.
0 commit comments