@btc-vision/btc-runtime
Version:
Bitcoin L1 Smart Contract Runtime for OP_NET. Build decentralized applications on Bitcoin using AssemblyScript and WebAssembly. Fully audited.
404 lines (287 loc) • 9.12 kB
Markdown
# SafeMath
SafeMath provides overflow-safe arithmetic operations for `u256` and other numeric types. It's essential for all smart contract arithmetic to prevent silent overflow/underflow bugs.
## Overview
```typescript
import { SafeMath } from '@btc-vision/btc-runtime/runtime';
import { u256 } from '@btc-vision/as-bignum/assembly';
const a = u256.fromU64(100);
const b = u256.fromU64(50);
// Safe operations
const sum = SafeMath.add(a, b); // 150
const diff = SafeMath.sub(a, b); // 50
const product = SafeMath.mul(a, b); // 5000
const quotient = SafeMath.div(a, b); // 2
```
## Why SafeMath?
### The Problem
Native arithmetic can overflow silently:
```typescript
// DANGEROUS: Silent overflow
const max = u256.Max;
const result = max + u256.One; // Wraps to 0!
// DANGEROUS: Silent underflow
const zero = u256.Zero;
const result = zero - u256.One; // Wraps to u256.Max!
```
### The Solution
SafeMath reverts on overflow/underflow:
```typescript
// SAFE: Reverts on overflow
const max = u256.Max;
const result = SafeMath.add(max, u256.One); // Reverts!
// SAFE: Reverts on underflow
const zero = u256.Zero;
const result = SafeMath.sub(zero, u256.One); // Reverts!
```
## Basic Operations
### Addition
```typescript
const sum = SafeMath.add(a, b);
// Reverts if a + b > u256.Max
// Equivalent to Solidity: a + b (with checked arithmetic)
```
### Subtraction
```typescript
const diff = SafeMath.sub(a, b);
// Reverts if b > a (would underflow)
// Equivalent to Solidity: a - b (with checked arithmetic)
```
### Multiplication
```typescript
const product = SafeMath.mul(a, b);
// Reverts if a * b > u256.Max
// Equivalent to Solidity: a * b (with checked arithmetic)
```
### Division
```typescript
const quotient = SafeMath.div(a, b);
// Reverts if b == 0
// Equivalent to Solidity: a / b
```
### Modulo
```typescript
const remainder = SafeMath.mod(a, b);
// Reverts if b == 0
// Equivalent to Solidity: a % b
```
## Advanced Operations
### Power (Exponentiation)
```typescript
const base = u256.fromU64(2);
const exp = u256.fromU64(10);
const result = SafeMath.pow(base, exp); // 2^10 = 1024
// Reverts on overflow
// Uses efficient exponentiation by squaring (binary exponentiation)
```
### Power of 10
```typescript
// Optimized function for 10^n
const million = SafeMath.pow10(6); // 10^6 = 1,000,000
const ether = SafeMath.pow10(18); // 10^18
// Maximum safe exponent is 77 (10^78 > u256.Max)
// Reverts if exponent > 77
```
### Square Root
```typescript
const value = u256.fromU64(144);
const sqrt = SafeMath.sqrt(value); // 12
// Uses Newton-Raphson method
// Returns floor of square root
```
### Increment and Decrement
```typescript
const value = u256.fromU64(100);
// Safe increment (reverts at u256.Max)
const incremented = SafeMath.inc(value); // 101
// Safe decrement (reverts at 0)
const decremented = SafeMath.dec(value); // 99
```
## Shift Operations
### Left Shift
```typescript
const shifted = SafeMath.shl(value, 10); // value << 10
// WARNING: Unlike other SafeMath operations, bit shifts do NOT throw on overflow!
// Bits shifted beyond the type width are SILENTLY LOST
// Shifts >= 256 return 0
// Negative shifts return 0 (defensive behavior)
```
### Right Shift
```typescript
const shifted = SafeMath.shr(value, 5); // value >> 5
// Right shift is always safe (can't overflow)
// Equivalent to division by 2^bits
```
### Bitwise Operations
```typescript
// AND operation
const result1 = SafeMath.and(a, b); // a & b
// OR operation
const result2 = SafeMath.or(a, b); // a | b
// XOR operation
const result3 = SafeMath.xor(a, b); // a ^ b
// Check if even
const isEven = SafeMath.isEven(value); // (value & 1) == 0
```
## Comparison Operations
```typescript
// Use u256 built-in comparison methods (not SafeMath)
if (u256.lt(a, b)) { } // Less than
if (u256.le(a, b)) { } // Less than or equal
if (u256.gt(a, b)) { } // Greater than
if (u256.ge(a, b)) { } // Greater than or equal
if (u256.eq(a, b)) { } // Equal
if (u256.ne(a, b)) { } // Not equal
// SafeMath provides min/max operations
const smaller = SafeMath.min(a, b);
const larger = SafeMath.max(a, b);
```
## Specialized Functions
### Modular Multiplication
```typescript
// (a * b) % n without intermediate overflow
const result = SafeMath.mulmod(a, b, n);
// Useful for cryptographic operations
```
### Modular Inverse
```typescript
// Find x such that (a * x) % n == 1
const inverse = SafeMath.modInverse(a, n);
// Used in elliptic curve operations
// Reverts if inverse doesn't exist
```
### Logarithm Operations
```typescript
// Approximate log2 (returns floor of log2(x))
// Fast O(1) using bit length calculation
const log2 = SafeMath.approximateLog2(value); // Returns u256
// Precise natural log (ln) scaled by 10^6 for fixed-point precision
// Higher accuracy but more gas intensive
const lnScaled = SafeMath.preciseLog(value); // ln(x) * 1e6
// Approximate natural log scaled by 10^6
// Uses bit length * ln(2) approximation
const approxLn = SafeMath.approxLog(value); // Approximate ln(x) * 1e6
// Bit length (number of bits needed to represent value)
const bits = SafeMath.bitLength256(value); // Returns u32
```
## Other Integer Sizes
SafeMath also provides u128 and u64 variants. See [SafeMath API Reference](../api-reference/safe-math.md#u128-operations) for the complete list.
## SafeMathI128
For signed 128-bit arithmetic:
```typescript
import { SafeMathI128 } from '@btc-vision/btc-runtime/runtime';
import { i128 } from '@btc-vision/as-bignum/assembly';
const a = i128.from(-100);
const b = i128.from(50);
const sum = SafeMathI128.add(a, b); // -50
const diff = SafeMathI128.sub(a, b); // -150
// Handles signed overflow correctly
```
## Common Patterns
### Percentage Calculation
```typescript
// Calculate percentage: (amount * percentage) / 100
public calculatePercentage(amount: u256, percentage: u256): u256 {
const numerator = SafeMath.mul(amount, percentage);
return SafeMath.div(numerator, u256.fromU64(100));
}
// Example: 10% of 1000
const result = calculatePercentage(u256.fromU64(1000), u256.fromU64(10)); // 100
```
### Fee Deduction
```typescript
// Deduct fee and return remaining
public deductFee(amount: u256, feePercent: u256): u256 {
const fee = SafeMath.div(SafeMath.mul(amount, feePercent), u256.fromU64(100));
return SafeMath.sub(amount, fee);
}
```
### Balance Updates
```typescript
// Safe balance increase
private addToBalance(addr: Address, amount: u256): void {
const current = this.balances.get(addr);
const newBalance = SafeMath.add(current, amount);
this.balances.set(addr, newBalance);
}
// Safe balance decrease
private subFromBalance(addr: Address, amount: u256): void {
const current = this.balances.get(addr);
if (current < amount) {
throw new Revert('Insufficient balance');
}
const newBalance = SafeMath.sub(current, amount);
this.balances.set(addr, newBalance);
}
```
### Supply Cap Enforcement
```typescript
public mint(to: Address, amount: u256): void {
const currentSupply = this.totalSupply.value;
const maxSupply = this.maxSupply.value;
// Check supply cap
const newSupply = SafeMath.add(currentSupply, amount);
if (newSupply > maxSupply) {
throw new Revert('Exceeds max supply');
}
this.totalSupply.value = newSupply;
this.addToBalance(to, amount);
}
```
## Edge Cases
### Division by Zero
```typescript
// Always reverts
SafeMath.div(u256.fromU64(100), u256.Zero); // Reverts!
SafeMath.mod(u256.fromU64(100), u256.Zero); // Reverts!
```
### Power Edge Cases
```typescript
// 0^0 = 1 (mathematical convention)
SafeMath.pow(u256.Zero, u256.Zero); // Returns 1
// n^0 = 1
SafeMath.pow(u256.fromU64(5), u256.Zero); // Returns 1
// 0^n = 0 (for n > 0)
SafeMath.pow(u256.Zero, u256.fromU64(5)); // Returns 0
// 1^n = 1
SafeMath.pow(u256.One, u256.fromU64(1000)); // Returns 1
```
### Maximum Values
```typescript
// Near max value operations
const nearMax = SafeMath.sub(u256.Max, u256.fromU64(10));
SafeMath.add(nearMax, u256.fromU64(5)); // OK: 5 from max
SafeMath.add(nearMax, u256.fromU64(15)); // Reverts: would overflow
```
## Best Practices
### 1. Always Use SafeMath
```typescript
// WRONG
const sum = a + b;
const diff = a - b;
// CORRECT
const sum = SafeMath.add(a, b);
const diff = SafeMath.sub(a, b);
```
### 2. Validate Before Division
```typescript
// Explicit zero check for better error messages
public divide(a: u256, b: u256): u256 {
if (b.isZero()) {
throw new Revert('Division by zero');
}
return SafeMath.div(a, b);
}
```
### 3. Order Operations to Minimize Overflow
```typescript
// RISKY: Large intermediate value
const result = SafeMath.div(SafeMath.mul(largeA, largeB), c);
// BETTER: Divide first if possible
const result = SafeMath.mul(SafeMath.div(largeA, c), largeB);
// BEST: Use muldiv for multiplication then division
const result = u128.muldiv(largeA, largeB, c);
```
---
**Navigation:**
- Previous: [Address](./address.md)
- Next: [Calldata](./calldata.md)