Skip to content

Commit 8a90c64

Browse files
fix: implemented changes
1 parent dfbfb19 commit 8a90c64

File tree

1 file changed

+39
-43
lines changed

1 file changed

+39
-43
lines changed

docs/blockchain-development-tutorials/forte/fixed-point-128-bit

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ sidebar_label: DeFi Math Utils
1717

1818
# High-Precision Fixed-Point 128 Bit Math
1919

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.
2121

2222
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.
2323

@@ -50,11 +50,11 @@ let output = afterFee * price // More precision lost
5050
let finalAmount = output / someRatio // Even more precision lost
5151
```
5252

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!
5454

5555
## The Solution: 24-Decimal Precision
5656

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.
5858

5959
:::Warning
6060

@@ -100,48 +100,50 @@ These constants ensure consistent scaling across all operations.
100100

101101
## Rounding Modes
102102

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:
104106

105107
```cadence
106108
access(all) enum RoundingMode: UInt8 {
107109
/// Rounds down (floor) - use for payouts
108110
access(all) case RoundDown
109-
111+
110112
/// Rounds up (ceiling) - use for fees/liabilities
111113
access(all) case RoundUp
112-
114+
113115
/// Standard rounding: < 0.5 down, >= 0.5 up
114116
access(all) case RoundHalfUp
115-
117+
116118
/// Banker's rounding: ties round to even number
117119
access(all) case RoundEven
118120
}
119121
```
120122

121123
### When to Use Each Mode
122124

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.
124126

125127
```cadence
126128
// When calculating how much to pay out to users
127129
let userReward = DeFiActionsMathUtils.toUFix64RoundDown(calculatedReward)
128130
```
129131

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.
131133

132134
```cadence
133135
// When calculating fees the protocol collects
134136
let protocolFee = DeFiActionsMathUtils.toUFix64RoundUp(calculatedFee)
135137
```
136138

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.
138140

139141
```cadence
140142
// For display values or general calculations
141143
let displayValue = DeFiActionsMathUtils.toUFix64Round(calculatedValue)
142144
```
143145

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.
145147

146148
```cadence
147149
// For repeated operations where bias matters
@@ -216,8 +218,12 @@ let result = DeFiActionsMathUtils.toUFix64Round(totalValue)
216218
// result = 1500.0
217219
```
218220

221+
:::info
222+
219223
**Important:** The multiplication uses `UInt256` internally to prevent overflow:
220224

225+
:::
226+
221227
```cadence
222228
// Internal implementation prevents overflow
223229
return UInt128(UInt256(x) * UInt256(y) / UInt256(e24))
@@ -453,56 +459,36 @@ access(all) view fun assertWithinUFix64Bounds(_ value: UInt128) {
453459
}
454460
```
455461

456-
### Flexible Rounding Strategies
457-
458-
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-
479462
## Best Practices
480463

481464
Always Use High Precision for Intermediate Calculations
482465

483-
**❌ Don't do this:**
466+
**❌ Low precision (loses ~$0.50 per 1M USDC):**
484467

485468
```cadence
486469
let fee: UFix64 = amount * 0.003
487470
let afterFee: UFix64 = amount - fee
488471
let output: UFix64 = afterFee * price
489472
```
490473

491-
**✅ Do this instead:**
474+
**✅ High precision (safe and accurate):**
492475

493476
```cadence
477+
// Convert once at the start
494478
let amountHP = DeFiActionsMathUtils.toUInt128(amount)
495479
let feeRate = DeFiActionsMathUtils.toUInt128(0.003)
496480
let priceHP = DeFiActionsMathUtils.toUInt128(price)
497481

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)
501484
let outputHP = DeFiActionsMathUtils.mul(afterFeeHP, priceHP)
502485

486+
// Convert once at the end with smart rounding
503487
let output = DeFiActionsMathUtils.toUFix64RoundDown(outputHP)
504488
```
505489

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+
506492
Always validate that inputs are within acceptable ranges:
507493

508494
```cadence
@@ -519,9 +505,9 @@ access(all) fun swap(inputAmount: UFix64) {
519505

520506
## More Resources
521507

522-
- [View the DeFiActionsMathUtils source code](https://github.com/onflow/FlowActions/blob/main/cadence/contracts/utils/DeFiActionsMathUtils.cdc)
523-
- [Flow DeFi Actions Documentation](https://developers.flow.com/blockchain-development-tutorials/forte/flow-actions)
524-
- [Cadence Fixed-Point Numbers](https://cadence-lang.org/docs/language/values-and-types/fixed-point-nums-ints)
508+
- [View the DeFiActionsMathUtils source code]
509+
- [Flow DeFi Actions Documentation]
510+
- [Cadence Fixed-Point Numbers]
525511

526512
## Key takeaways
527513

@@ -533,4 +519,14 @@ access(all) fun swap(inputAmount: UFix64) {
533519

534520
## Conclusion
535521

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.
525+
526+
<!-- Relative links, will not render on page -->
527+
528+
[`DeFiActionsMathUtils`]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/utils/DeFiActionsMathUtils.cdc
529+
[banker's rounding]: https://learn.microsoft.com/en-us/openspecs/microsoft_general_purpose_programming_languages/ms-vbal/98152b5a-4d86-4acb-b875-66cb1f49433e
530+
[View the DeFiActionsMathUtils source code]: https://github.com/onflow/FlowActions/blob/main/cadence/contracts/utils/DeFiActionsMathUtils.cdc
531+
[Flow DeFi Actions Documentation]: https://developers.flow.com/blockchain-development-tutorials/forte/flow-actions
532+
[Cadence Fixed-Point Numbers]: https://cadence-lang.org/docs/language/values-and-types/fixed-point-nums-ints

0 commit comments

Comments
 (0)