Math in JavaScript and DeFi

January 19, 2025

Mathematics in JavaScript can be tricky, especially when dealing with financial applications like DeFi. Precision errors, floating-point quirks, and handling large numbers are all challenges developers must address. In this post, we'll cover key math issues in JavaScript, best practices for handling numbers in DeFi, and tools like bignumber.js, BigInt, and viem utilities.

Math in JavaScript and DeFi

The Basics: Floating-Point Math in JavaScript

JavaScript uses IEEE 754 floating-point arithmetic, which introduces quirks when performing arithmetic operations. This can lead to unexpected results:

console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.3 - 0.1); // 0.19999999999999998

Why Is This a Problem in DeFi?

DeFi applications require precise calculations, especially when dealing with token balances, staking rewards, and interest rates. Small precision errors can lead to significant discrepancies in financial transactions.

Handling Precision: Decimals in DeFi

Most ERC-20 tokens and DeFi protocols use fixed decimal places instead of floating-point numbers. Token balances are usually represented in their smallest unit (e.g., wei for ETH, satoshis for BTC) and later formatted for human readability.

Example: Converting Token Amounts

const tokenDecimals = 18;
const rawBalance = BigInt("1000000000000000000"); // 1 ETH in wei
const formattedBalance = Number(rawBalance) / 10 ** tokenDecimals;
console.log(formattedBalance); // 1.0

While using JavaScript's Number type for such conversions works, it is prone to floating-point precision errors. A better approach is to use a Big Number library like bignumber.js, which ensures robustness, consistency, and improved readability when dealing with precise financial calculations.

Why bignumber.js?

  • Arbitrary precision: Prevents floating-point errors.
  • Performance optimized: Faster than alternatives like decimal.js.
  • Compact: Smaller than other libraries.
  • Well-maintained: Reliable and actively supported.

When to Consider Alternatives:

  • If only basic decimal math is needed, big.js is smaller and faster.
  • For advanced math functions, decimal.js offers more capabilities.
  • If working with large integers only, native BigInt is a viable alternative.

Example: Safe Arithmetic with bignumber.js

import BigNumber from "bignumber.js";
 
const a = new BigNumber("0.1");
const b = new BigNumber("0.2");
const sum = a.plus(b);
 
console.log(sum.toString()); // "0.3"

Using bignumber.js ensures predictable behavior and avoids floating-point inaccuracies.

Math on EVM: Using Viem's Utilities

When working with Ethereum Virtual Machine (EVM) transactions, Viem provides utilities for handling numeric conversions: