Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Keystone

Deterministic financial computation infrastructure for DeFi and fintech.

What is Keystone?

Keystone provides precision arithmetic and financial calculations that produce identical results across all platforms. Built in Rust with no_std support, it compiles to native code, WebAssembly, and ZK-proving targets.

Key Features

  • 128-bit decimal arithmetic with 28 digits of precision
  • 7 rounding modes including banker’s rounding (half-even)
  • Checked operations that explicitly handle overflow
  • Cross-platform determinism verified in CI
  • DeFi risk metrics for health factors, liquidation, and position analysis
  • Financial calculations for interest, time value of money, and percentages

Crates

CrateDescription
precision-coreDecimal type with deterministic arithmetic
financial-calcInterest, TVM, and percentage calculations
risk-metricsDeFi health factors and liquidation logic
wasm-bindingsJavaScript/TypeScript bindings

Quick Example

#![allow(unused)]
fn main() {
use precision_core::{Decimal, RoundingMode};

let price = Decimal::new(123456, 2);  // 1234.56
let quantity = Decimal::new(15, 1);   // 1.5
let total = price.try_mul(quantity)?; // 1851.84

// Round to cents using banker's rounding
let rounded = total.round(2, RoundingMode::HalfEven);
}

Performance

Operations complete in nanoseconds:

OperationTime
Addition~8 ns
Multiplication~8 ns
Division~36 ns
Health factor~23 ns

License

Dual-licensed under MIT and Apache 2.0.

Installation

Rust

Add to your Cargo.toml:

[dependencies]
precision-core = "0.1"
financial-calc = "0.1"  # optional
risk-metrics = "0.1"    # optional

For no_std environments:

[dependencies]
precision-core = { version = "0.1", default-features = false }

JavaScript / TypeScript

npm install @dijkstra-keystone/wasm

Or via CDN:

<script type="module">
  import init, * as keystone from 'https://unpkg.com/@dijkstra-keystone/wasm';
  await init();
  console.log(keystone.add("0.1", "0.2")); // "0.3"
</script>

Building from Source

git clone https://github.com/dijkstra-keystone/keystone
cd keystone
cargo build --release

For WASM:

cargo build --target wasm32-unknown-unknown --release -p wasm-bindings

Quick Start

Basic Arithmetic

#![allow(unused)]
fn main() {
use precision_core::Decimal;

// From integers
let a = Decimal::from(100i64);

// From mantissa and scale: value = mantissa * 10^(-scale)
let b = Decimal::new(12345, 2);  // 123.45

// From strings
let c: Decimal = "99.99".parse().unwrap();

// Arithmetic with checked operations
let sum = a.try_add(b)?;
let product = a.try_mul(c)?;
let quotient = a.try_div(b)?;
}

Rounding

#![allow(unused)]
fn main() {
use precision_core::{Decimal, RoundingMode};

let value = Decimal::new(12345, 3);  // 12.345

// Banker's rounding (half-even) - default
let rounded = value.round_dp(2);  // 12.34

// Other modes
let up = value.round(2, RoundingMode::Up);        // 12.35
let down = value.round(2, RoundingMode::Down);    // 12.34
let half_up = value.round(2, RoundingMode::HalfUp); // 12.35
}

Financial Calculations

#![allow(unused)]
fn main() {
use financial_calc::{compound_interest, future_value, Decimal};

let principal = Decimal::from(10000i64);
let rate = Decimal::new(5, 2);  // 5%

// Compound interest: monthly for 5 years
let interest = compound_interest(principal, rate, 12, 5)?;

// Future value
let fv = future_value(principal, rate, 10)?;
}

Risk Metrics

#![allow(unused)]
fn main() {
use risk_metrics::{health_factor, liquidation_price, Decimal};

let collateral = Decimal::from(10000i64);
let debt = Decimal::from(5000i64);
let threshold = Decimal::new(80, 2);  // 80%

// Health factor: (collateral * threshold) / debt
let hf = health_factor(collateral, debt, threshold)?;  // 1.6

// Liquidation price
let liq = liquidation_price(
    Decimal::from(5i64),  // 5 ETH collateral
    debt,
    threshold,
)?;  // $1,250 per ETH
}

Error Handling

#![allow(unused)]
fn main() {
use precision_core::{Decimal, ArithmeticError};

let result = Decimal::MAX.try_add(Decimal::ONE);
match result {
    Ok(value) => println!("Result: {}", value),
    Err(ArithmeticError::Overflow) => println!("Overflow"),
    Err(ArithmeticError::DivisionByZero) => println!("Division by zero"),
    Err(e) => println!("Error: {}", e),
}
}

Decimal Type

The Decimal type provides 128-bit decimal arithmetic with up to 28 significant digits.

Construction

#![allow(unused)]
fn main() {
use precision_core::Decimal;

// Constants
let zero = Decimal::ZERO;
let one = Decimal::ONE;
let hundred = Decimal::ONE_HUNDRED;

// From integers (infallible)
let a = Decimal::from(42i64);
let b = Decimal::from(1_000_000u64);

// From mantissa and scale
// Value = mantissa * 10^(-scale)
let c = Decimal::new(12345, 2);    // 123.45
let d = Decimal::new(-500, 3);     // -0.500

// From strings (fallible)
let e: Decimal = "123.456".parse()?;
let f: Decimal = "-0.001".parse()?;

// From 128-bit integers (infallible)
let g = Decimal::from(i128::MAX);
}

Checked Arithmetic

All arithmetic operations have checked variants that return Option:

#![allow(unused)]
fn main() {
let a = Decimal::from(100i64);
let b = Decimal::from(3i64);

// Returns None on overflow/underflow/division-by-zero
let sum = a.checked_add(b);      // Some(103)
let diff = a.checked_sub(b);     // Some(97)
let prod = a.checked_mul(b);     // Some(300)
let quot = a.checked_div(b);     // Some(33.333...)
let rem = a.checked_rem(b);      // Some(1)

// Overflow example
let overflow = Decimal::MAX.checked_add(Decimal::ONE);  // None
}

Try Arithmetic

For explicit error handling:

#![allow(unused)]
fn main() {
use precision_core::{Decimal, ArithmeticError};

let a = Decimal::from(100i64);
let b = Decimal::ZERO;

match a.try_div(b) {
    Ok(result) => println!("{}", result),
    Err(ArithmeticError::DivisionByZero) => println!("Cannot divide by zero"),
    Err(ArithmeticError::Overflow) => println!("Result too large"),
    Err(e) => println!("Error: {}", e),
}
}

Saturating Arithmetic

Operations that clamp to MAX or MIN on overflow:

#![allow(unused)]
fn main() {
let max = Decimal::MAX;
let result = max.saturating_add(Decimal::ONE);  // MAX (no panic)
}

Properties

#![allow(unused)]
fn main() {
let a = Decimal::new(-12345, 3);  // -12.345

a.is_zero();        // false
a.is_negative();    // true
a.is_positive();    // false
a.scale();          // 3
a.abs();            // 12.345
a.signum();         // -1
}

Comparison

#![allow(unused)]
fn main() {
let a = Decimal::from(100i64);
let b = Decimal::from(200i64);

a < b;              // true
a.min(b);           // 100
a.max(b);           // 200
a.clamp(Decimal::ZERO, Decimal::from(150i64));  // 100
}

Normalization

Remove trailing zeros:

#![allow(unused)]
fn main() {
let a = Decimal::new(1000, 2);  // 10.00
let normalized = a.normalize();  // 10 (scale = 0)
}

Internal Representation

Access the underlying components:

#![allow(unused)]
fn main() {
let a = Decimal::new(12345, 3);
let (mantissa, scale) = a.to_parts();  // (12345, 3)

// Access rust_decimal directly if needed
let inner = a.into_inner();
}

Rounding Modes

Keystone supports 7 rounding modes for precise control over decimal rounding.

Available Modes

ModeDescription2.5 →3.5 →-2.5 →
HalfEvenBanker’s rounding (ties to even)24-2
HalfUpTies round away from zero34-3
HalfDownTies round toward zero23-2
UpAlways round toward +∞34-2
DownAlways round toward -∞23-3
TowardZeroTruncate (round toward zero)23-2
AwayFromZeroRound away from zero34-3

Usage

#![allow(unused)]
fn main() {
use precision_core::{Decimal, RoundingMode};

let value = Decimal::new(12345, 3);  // 12.345

// Round to 2 decimal places
value.round(2, RoundingMode::HalfEven);   // 12.34
value.round(2, RoundingMode::HalfUp);     // 12.35
value.round(2, RoundingMode::Down);       // 12.34
value.round(2, RoundingMode::Up);         // 12.35

// Convenience methods
value.round_dp(2);  // 12.34 (uses HalfEven)
value.trunc(2);     // 12.34 (uses TowardZero)
value.floor();      // 12 (round toward -∞)
value.ceil();       // 13 (round toward +∞)
}

Banker’s Rounding (HalfEven)

The default mode. Ties round to the nearest even digit. This minimizes cumulative rounding error over many operations.

#![allow(unused)]
fn main() {
let a = Decimal::new(25, 1);  // 2.5
let b = Decimal::new(35, 1);  // 3.5

a.round(0, RoundingMode::HalfEven);  // 2 (rounds to even)
b.round(0, RoundingMode::HalfEven);  // 4 (rounds to even)
}

Financial Applications

Use CaseRecommended Mode
Invoice totalsHalfUp
Tax calculationsHalfUp or Up (toward government)
Interest accumulationHalfEven
Currency displayHalfUp
Truncation (e.g., satoshis)TowardZero

WASM

import { round } from '@dijkstra-keystone/wasm';

round("2.345", 2, "half_even");  // "2.34"
round("2.345", 2, "half_up");    // "2.35"
round("2.345", 2, "truncate");   // "2.34"

Available mode strings: "down", "up", "toward_zero", "truncate", "away_from_zero", "half_even", "bankers", "half_up", "half_down".

Error Handling

Keystone uses explicit error types for all fallible operations.

Arithmetic Errors

#![allow(unused)]
fn main() {
use precision_core::ArithmeticError;

pub enum ArithmeticError {
    Overflow,       // Result exceeds MAX
    Underflow,      // Result below MIN
    DivisionByZero, // Division by zero
    ScaleExceeded,  // Scale > 28
}
}

Parse Errors

#![allow(unused)]
fn main() {
use precision_core::ParseError;

pub enum ParseError {
    Empty,                  // Empty string
    InvalidCharacter,       // Non-numeric character
    MultipleDecimalPoints,  // "1.2.3"
    OutOfRange,            // Value too large
}
}

Handling Patterns

Option-based (checked operations)

#![allow(unused)]
fn main() {
let a = Decimal::from(100i64);
let b = Decimal::ZERO;

match a.checked_div(b) {
    Some(result) => use_result(result),
    None => handle_error(),
}
}

Result-based (try operations)

#![allow(unused)]
fn main() {
let result = a.try_div(b);
match result {
    Ok(value) => use_value(value),
    Err(ArithmeticError::DivisionByZero) => handle_div_zero(),
    Err(e) => handle_other(e),
}
}

With the ? operator

#![allow(unused)]
fn main() {
fn calculate(a: Decimal, b: Decimal) -> Result<Decimal, ArithmeticError> {
    let sum = a.try_add(b)?;
    let product = sum.try_mul(Decimal::from(2i64))?;
    Ok(product)
}
}

Panicking Operations

Standard operators panic on error. Use only when errors are impossible:

#![allow(unused)]
fn main() {
// These panic on overflow or division by zero
let sum = a + b;
let diff = a - b;
let prod = a * b;
let quot = a / b;  // panics if b == 0
}

WASM Error Handling

JavaScript functions throw on error:

try {
  const result = keystone.divide("1", "0");
} catch (e) {
  console.error(e.message);  // "division by zero"
}

Or use optional chaining:

const result = (() => {
  try { return keystone.divide(a, b); }
  catch { return null; }
})();

Tolerance Comparisons

Financial calculations often require approximate equality checks due to rounding. Keystone provides tolerance-based comparison functions.

Absolute Tolerance

Check if two values differ by at most a fixed amount:

#![allow(unused)]
fn main() {
use precision_core::{Decimal, approx_eq};

let a = Decimal::new(10000, 2);  // 100.00
let b = Decimal::new(10001, 2);  // 100.01
let tolerance = Decimal::new(1, 2);  // 0.01

approx_eq(a, b, tolerance);  // true (difference <= 0.01)
}

Relative Tolerance

Check if two values differ by at most a percentage of their magnitude:

#![allow(unused)]
fn main() {
use precision_core::{Decimal, approx_eq_relative};

let a = Decimal::from(1_000_000i64);
let b = Decimal::from(1_000_100i64);
let tolerance = Decimal::new(1, 3);  // 0.1%

approx_eq_relative(a, b, tolerance);  // true
}

Combined Tolerance

Use both absolute and relative tolerances (passes if either succeeds):

#![allow(unused)]
fn main() {
use precision_core::{Decimal, approx_eq_ulps};

let a = Decimal::from(100i64);
let b = Decimal::new(10001, 2);  // 100.01

approx_eq_ulps(
    a, b,
    Decimal::new(1, 2),   // absolute: 0.01
    Decimal::new(1, 3),   // relative: 0.1%
);
}

Percentage Tolerance

Check if values are within a percentage of each other:

#![allow(unused)]
fn main() {
use precision_core::{Decimal, within_percentage};

let actual = Decimal::from(102i64);
let expected = Decimal::from(100i64);

within_percentage(actual, expected, Decimal::from(5i64));  // true (within 5%)
within_percentage(actual, expected, Decimal::from(1i64));  // false (not within 1%)
}

Basis Points Tolerance

For financial applications using basis points (1 bp = 0.01%):

#![allow(unused)]
fn main() {
use precision_core::{Decimal, within_basis_points};

let a = Decimal::new(10010, 2);  // 100.10
let b = Decimal::from(100i64);

within_basis_points(a, b, Decimal::from(100i64));  // true (within 100 bps = 1%)
within_basis_points(a, b, Decimal::from(5i64));    // false (not within 5 bps)
}

Use Cases

ScenarioRecommended Function
Comparing priceswithin_basis_points
Verifying calculationsapprox_eq with small absolute tolerance
Comparing large valuesapprox_eq_relative
Test assertionsapprox_eq_ulps (handles edge cases)
Rate comparisonswithin_percentage

Interest Calculations

The financial-calc crate provides precise interest calculation functions.

Simple Interest

Interest calculated only on the principal:

#![allow(unused)]
fn main() {
use financial_calc::{simple_interest, Decimal};

let principal = Decimal::from(10000i64);
let rate = Decimal::new(5, 2);  // 5% annual rate
let years = Decimal::from(3i64);

let interest = simple_interest(principal, rate, years)?;
// interest = 10000 * 0.05 * 3 = 1500
}

Formula: I = P × r × t

Compound Interest

Interest calculated on principal plus accumulated interest:

#![allow(unused)]
fn main() {
use financial_calc::{compound_interest, Decimal};

let principal = Decimal::from(10000i64);
let rate = Decimal::new(5, 2);  // 5% annual rate
let periods_per_year = 12;       // monthly compounding
let years = 5;

let total_interest = compound_interest(principal, rate, periods_per_year, years)?;
// Returns total interest earned over the period
}

Formula: A = P(1 + r/n)^(nt) where interest = A - P

Compounding Frequencies

FrequencyPeriods per Year
Annual1
Semi-annual2
Quarterly4
Monthly12
Daily365

Effective Annual Rate (EAR)

Convert a nominal rate to its effective annual equivalent:

#![allow(unused)]
fn main() {
use financial_calc::{effective_annual_rate, Decimal};

let nominal_rate = Decimal::new(5, 2);  // 5% nominal
let periods = 12;  // monthly compounding

let ear = effective_annual_rate(nominal_rate, periods)?;
// ear ≈ 5.116% (effective annual rate)
}

Formula: EAR = (1 + r/n)^n - 1

Continuous Compounding

For theoretical continuous compounding, use a high number of periods:

#![allow(unused)]
fn main() {
let rate = Decimal::new(5, 2);
let periods = 365 * 24;  // hourly approximation

let ear = effective_annual_rate(rate, periods)?;
// Approaches e^r - 1
}

Error Handling

All interest functions return Result<Decimal, ArithmeticError>:

#![allow(unused)]
fn main() {
use financial_calc::{compound_interest, Decimal};
use precision_core::ArithmeticError;

let result = compound_interest(
    Decimal::MAX,
    Decimal::from(100i64),
    12,
    100,
);

match result {
    Ok(interest) => println!("Interest: {}", interest),
    Err(ArithmeticError::Overflow) => println!("Calculation overflow"),
    Err(e) => println!("Error: {:?}", e),
}
}

Practical Examples

Savings Account

#![allow(unused)]
fn main() {
let deposit = Decimal::from(5000i64);
let apy = Decimal::new(425, 4);  // 4.25% APY
let months = 18;

// APY is already effective rate, so use simple calculation
let years = Decimal::new(months as i64, 0)
    .checked_div(Decimal::from(12i64))
    .unwrap();
let earnings = simple_interest(deposit, apy, years)?;
}

Loan Interest

#![allow(unused)]
fn main() {
let loan = Decimal::from(250000i64);
let apr = Decimal::new(675, 4);  // 6.75% APR
let years = 30;

// Total interest over loan lifetime (monthly compounding)
let total_interest = compound_interest(loan, apr, 12, years)?;
}

Time Value of Money

Functions for present and future value calculations.

Future Value

Calculate the future value of a present amount:

#![allow(unused)]
fn main() {
use financial_calc::{future_value, Decimal};

let present = Decimal::from(10000i64);
let rate = Decimal::new(7, 2);  // 7% annual return
let periods = 10;

let fv = future_value(present, rate, periods)?;
// fv ≈ 19,671.51
}

Formula: FV = PV × (1 + r)^n

Present Value

Calculate the present value of a future amount:

#![allow(unused)]
fn main() {
use financial_calc::{present_value, Decimal};

let future = Decimal::from(100000i64);
let rate = Decimal::new(5, 2);  // 5% discount rate
let periods = 20;

let pv = present_value(future, rate, periods)?;
// pv ≈ 37,688.95
}

Formula: PV = FV / (1 + r)^n

Net Present Value (NPV)

Evaluate investment profitability by discounting future cash flows:

#![allow(unused)]
fn main() {
use financial_calc::{net_present_value, Decimal};

let rate = Decimal::new(10, 2);  // 10% discount rate
let cash_flows = [
    Decimal::from(-100000i64),  // Initial investment (negative)
    Decimal::from(30000i64),    // Year 1
    Decimal::from(40000i64),    // Year 2
    Decimal::from(50000i64),    // Year 3
    Decimal::from(60000i64),    // Year 4
];

let npv = net_present_value(rate, &cash_flows)?;
// npv > 0 indicates profitable investment
}

NPV Decision Rule

NPVDecision
> 0Accept (creates value)
= 0Indifferent
< 0Reject (destroys value)

Practical Examples

Retirement Planning

#![allow(unused)]
fn main() {
// How much do I need to save monthly to reach $1M in 30 years?
let target = Decimal::from(1_000_000i64);
let annual_return = Decimal::new(7, 2);
let years = 30;

// First, find present value
let pv_target = present_value(target, annual_return, years)?;
// pv_target ≈ $131,367

// Then calculate monthly savings needed (simplified)
let months = years * 12;
let monthly = pv_target.checked_div(Decimal::from(months as i64)).unwrap();
}

Investment Comparison

#![allow(unused)]
fn main() {
// Compare two investments with different cash flow patterns

// Investment A: $50K now, returns $80K in 5 years
let rate = Decimal::new(8, 2);
let fv_a = future_value(Decimal::from(50000i64), rate, 5)?;
let profit_a = Decimal::from(80000i64).checked_sub(fv_a);

// Investment B: $50K now, returns $15K annually for 5 years
let cash_flows_b = [
    Decimal::from(-50000i64),
    Decimal::from(15000i64),
    Decimal::from(15000i64),
    Decimal::from(15000i64),
    Decimal::from(15000i64),
    Decimal::from(15000i64),
];
let npv_b = net_present_value(rate, &cash_flows_b)?;
}

Inflation Adjustment

#![allow(unused)]
fn main() {
// What will $100 be worth in 10 years with 3% inflation?
let amount = Decimal::from(100i64);
let inflation = Decimal::new(3, 2);
let years = 10;

// Future purchasing power (inverse of future value)
let future_purchasing_power = present_value(amount, inflation, years)?;
// ≈ $74.41 in today's dollars
}

Error Conditions

  • Overflow: Large values or many periods can overflow
  • DivisionByZero: Rate of exactly -100% in present_value
#![allow(unused)]
fn main() {
// Handle potential errors
match future_value(principal, rate, periods) {
    Ok(fv) => use_value(fv),
    Err(ArithmeticError::Overflow) => handle_overflow(),
    Err(e) => handle_error(e),
}
}

Percentage Operations

Precise percentage calculations for financial applications.

Basic Percentage

Calculate a percentage of a value:

#![allow(unused)]
fn main() {
use financial_calc::{percentage_of, Decimal};

let total = Decimal::from(1500i64);
let rate = Decimal::new(15, 2);  // 15%

let amount = percentage_of(total, rate)?;
// amount = 225 (15% of 1500)
}

Percentage Change

Calculate the percentage change between two values:

#![allow(unused)]
fn main() {
use financial_calc::{percentage_change, Decimal};

let old_price = Decimal::from(100i64);
let new_price = Decimal::from(125i64);

let change = percentage_change(old_price, new_price)?;
// change = 25% (0.25)
}

Formula: (new - old) / old × 100

Add Percentage

Add a percentage to a base value:

#![allow(unused)]
fn main() {
use financial_calc::{add_percentage, Decimal};

let price = Decimal::from(100i64);
let tax_rate = Decimal::new(825, 4);  // 8.25%

let total = add_percentage(price, tax_rate)?;
// total = 108.25
}

Subtract Percentage

Remove a percentage from a value:

#![allow(unused)]
fn main() {
use financial_calc::{subtract_percentage, Decimal};

let original = Decimal::from(100i64);
let discount = Decimal::new(20, 2);  // 20%

let final_price = subtract_percentage(original, discount)?;
// final_price = 80
}

Reverse Percentage

Find the original value before a percentage was added:

#![allow(unused)]
fn main() {
use financial_calc::{reverse_percentage, Decimal};

let total_with_tax = Decimal::new(10825, 2);  // 108.25
let tax_rate = Decimal::new(825, 4);  // 8.25%

let original = reverse_percentage(total_with_tax, tax_rate)?;
// original = 100.00
}

Practical Examples

Sales Tax Calculation

#![allow(unused)]
fn main() {
let subtotal = Decimal::new(15999, 2);  // $159.99
let state_tax = Decimal::new(6, 2);      // 6%
let local_tax = Decimal::new(225, 4);    // 2.25%

let state_amount = percentage_of(subtotal, state_tax)?;
let local_amount = percentage_of(subtotal, local_tax)?;
let total = subtotal
    .checked_add(state_amount)?
    .checked_add(local_amount)?;
}

Profit Margin

#![allow(unused)]
fn main() {
let revenue = Decimal::from(500000i64);
let costs = Decimal::from(350000i64);
let profit = revenue.checked_sub(costs)?;

// Profit margin = profit / revenue
let margin = profit
    .checked_mul(Decimal::ONE_HUNDRED)?
    .checked_div(revenue)?;
// margin = 30%
}

Compound Discounts

#![allow(unused)]
fn main() {
// Apply 20% off, then additional 10% off
let original = Decimal::from(100i64);
let first_discount = Decimal::new(20, 2);
let second_discount = Decimal::new(10, 2);

let after_first = subtract_percentage(original, first_discount)?;  // 80
let final_price = subtract_percentage(after_first, second_discount)?;  // 72

// Note: Not the same as 30% off (which would be 70)
}

Tip Calculator

#![allow(unused)]
fn main() {
let bill = Decimal::new(8567, 2);  // $85.67

let tip_15 = percentage_of(bill, Decimal::new(15, 2))?;  // $12.85
let tip_18 = percentage_of(bill, Decimal::new(18, 2))?;  // $15.42
let tip_20 = percentage_of(bill, Decimal::new(20, 2))?;  // $17.13
}

Basis Points

For financial applications, use basis points (1 bp = 0.01%):

#![allow(unused)]
fn main() {
use precision_core::{Decimal, within_basis_points};

let quoted_rate = Decimal::new(525, 4);   // 5.25%
let actual_rate = Decimal::new(5251, 5);  // 5.251%

// Check if within 5 basis points
within_basis_points(quoted_rate, actual_rate, Decimal::from(5i64));  // true
}
Basis PointsPercentage
1 bp0.01%
10 bp0.10%
25 bp0.25%
50 bp0.50%
100 bp1.00%

Health Factor

The health factor is a key metric for collateralized lending positions in DeFi protocols.

Definition

Health factor measures the safety of a collateralized position:

Health Factor = (Collateral Value × Liquidation Threshold) / Debt Value
Health FactorStatus
> 1.0Safe
= 1.0At liquidation threshold
< 1.0Liquidatable

Basic Usage

#![allow(unused)]
fn main() {
use risk_metrics::{health_factor, Decimal};

let collateral = Decimal::from(10000i64);  // $10,000 collateral
let debt = Decimal::from(5000i64);          // $5,000 debt
let threshold = Decimal::new(80, 2);        // 80% liquidation threshold

let hf = health_factor(collateral, debt, threshold)?;
// hf = (10000 × 0.80) / 5000 = 1.6
}

Health Check

Quick boolean check for position safety:

#![allow(unused)]
fn main() {
use risk_metrics::{is_healthy, Decimal};

let collateral = Decimal::from(10000i64);
let debt = Decimal::from(8500i64);
let threshold = Decimal::new(80, 2);

is_healthy(collateral, debt, threshold)?;  // false (HF < 1.0)
}

Collateral Ratio

Calculate the ratio of collateral to debt:

#![allow(unused)]
fn main() {
use risk_metrics::{collateral_ratio, Decimal};

let collateral = Decimal::from(15000i64);
let debt = Decimal::from(10000i64);

let ratio = collateral_ratio(collateral, debt)?;
// ratio = 1.5 (150% collateralization)
}

Practical Examples

Monitor Position Safety

#![allow(unused)]
fn main() {
use risk_metrics::{health_factor, Decimal};

let collateral = Decimal::from(50000i64);
let debt = Decimal::from(30000i64);
let threshold = Decimal::new(825, 3);  // 82.5%

let hf = health_factor(collateral, debt, threshold)?;

match hf {
    hf if hf >= Decimal::new(15, 1) => println!("Position is safe"),
    hf if hf >= Decimal::new(12, 1) => println!("Consider adding collateral"),
    hf if hf >= Decimal::ONE => println!("Warning: Near liquidation"),
    _ => println!("DANGER: Position liquidatable"),
}
}

Calculate Safe Borrow Amount

#![allow(unused)]
fn main() {
use risk_metrics::{health_factor, Decimal};

let collateral = Decimal::from(100000i64);
let threshold = Decimal::new(80, 2);
let target_hf = Decimal::new(15, 1);  // Target 1.5 health factor

// max_debt = (collateral × threshold) / target_hf
let max_safe_debt = collateral
    .checked_mul(threshold)?
    .checked_div(target_hf)?;
// max_safe_debt = $53,333.33
}

Multi-Asset Position

#![allow(unused)]
fn main() {
// Weighted average for multiple collateral types
let eth_value = Decimal::from(50000i64);
let eth_threshold = Decimal::new(80, 2);

let btc_value = Decimal::from(30000i64);
let btc_threshold = Decimal::new(75, 2);

let weighted_collateral = eth_value
    .checked_mul(eth_threshold)?
    .checked_add(btc_value.checked_mul(btc_threshold)?)?;

let debt = Decimal::from(40000i64);
let hf = weighted_collateral.checked_div(debt)?;
}

Protocol-Specific Thresholds

Different protocols use different liquidation thresholds:

ProtocolTypical Threshold
Aave (ETH)82.5%
Compound (ETH)82.5%
MakerDAO (ETH-A)83%
Aave (Stablecoins)90%

Edge Cases

#![allow(unused)]
fn main() {
use risk_metrics::{health_factor, Decimal};
use precision_core::ArithmeticError;

// Zero debt = infinite health (returns MAX)
let hf = health_factor(
    Decimal::from(1000i64),
    Decimal::ZERO,
    Decimal::new(80, 2),
)?;
// hf = Decimal::MAX

// Zero collateral with debt
let hf = health_factor(
    Decimal::ZERO,
    Decimal::from(1000i64),
    Decimal::new(80, 2),
)?;
// hf = 0
}

Liquidation Calculations

Functions for calculating liquidation thresholds and prices in DeFi lending.

Liquidation Price

Calculate the price at which a position becomes liquidatable:

#![allow(unused)]
fn main() {
use risk_metrics::{liquidation_price, Decimal};

let collateral_amount = Decimal::from(5i64);  // 5 ETH
let debt = Decimal::from(10000i64);            // $10,000 debt
let threshold = Decimal::new(80, 2);           // 80% threshold

let liq_price = liquidation_price(collateral_amount, debt, threshold)?;
// liq_price = $2,500 per ETH
}

Formula: Liquidation Price = Debt / (Collateral Amount × Threshold)

When ETH drops to $2,500, the position reaches HF = 1.0 and becomes liquidatable.

Liquidation Threshold

Determine the threshold at which liquidation occurs:

#![allow(unused)]
fn main() {
use risk_metrics::{liquidation_threshold, Decimal};

let collateral_value = Decimal::from(10000i64);
let debt = Decimal::from(8000i64);
let current_hf = Decimal::new(125, 2);  // 1.25

let threshold = liquidation_threshold(collateral_value, debt, current_hf)?;
// Calculates the effective liquidation threshold
}

Maximum Borrowable

Calculate maximum debt for a given health factor target:

#![allow(unused)]
fn main() {
use risk_metrics::{max_borrowable, Decimal};

let collateral = Decimal::from(100000i64);
let threshold = Decimal::new(80, 2);
let min_health_factor = Decimal::new(15, 1);  // Target HF 1.5

let max_debt = max_borrowable(collateral, threshold, min_health_factor)?;
// max_debt = $53,333.33
}

Formula: Max Debt = (Collateral × Threshold) / Min Health Factor

Practical Examples

Position Monitoring Dashboard

#![allow(unused)]
fn main() {
use risk_metrics::{liquidation_price, health_factor, Decimal};

struct Position {
    eth_amount: Decimal,
    eth_price: Decimal,
    debt_usd: Decimal,
    threshold: Decimal,
}

fn analyze_position(pos: &Position) -> Result<(), ArithmeticError> {
    let collateral_usd = pos.eth_amount.checked_mul(pos.eth_price)?;

    let hf = health_factor(collateral_usd, pos.debt_usd, pos.threshold)?;
    let liq_price = liquidation_price(pos.eth_amount, pos.debt_usd, pos.threshold)?;

    let buffer = pos.eth_price
        .checked_sub(liq_price)?
        .checked_div(pos.eth_price)?
        .checked_mul(Decimal::ONE_HUNDRED)?;

    println!("Health Factor: {}", hf);
    println!("Liquidation Price: ${}", liq_price);
    println!("Price Buffer: {}%", buffer);

    Ok(())
}
}

Liquidation Alert System

#![allow(unused)]
fn main() {
use risk_metrics::{liquidation_price, Decimal};

fn check_liquidation_risk(
    collateral_amount: Decimal,
    debt: Decimal,
    threshold: Decimal,
    current_price: Decimal,
) -> Result<RiskLevel, ArithmeticError> {
    let liq_price = liquidation_price(collateral_amount, debt, threshold)?;

    let distance = current_price
        .checked_sub(liq_price)?
        .checked_div(current_price)?;

    Ok(match distance {
        d if d >= Decimal::new(30, 2) => RiskLevel::Safe,
        d if d >= Decimal::new(15, 2) => RiskLevel::Moderate,
        d if d >= Decimal::new(5, 2) => RiskLevel::High,
        _ => RiskLevel::Critical,
    })
}

enum RiskLevel {
    Safe,
    Moderate,
    High,
    Critical,
}
}

Multi-Collateral Liquidation

#![allow(unused)]
fn main() {
// Calculate weighted liquidation threshold for multiple assets
fn weighted_liquidation_threshold(
    assets: &[(Decimal, Decimal)],  // (value, threshold) pairs
) -> Result<Decimal, ArithmeticError> {
    let mut weighted_sum = Decimal::ZERO;
    let mut total_value = Decimal::ZERO;

    for (value, threshold) in assets {
        weighted_sum = weighted_sum
            .checked_add(value.checked_mul(*threshold)?)?;
        total_value = total_value.checked_add(*value)?;
    }

    weighted_sum.checked_div(total_value)
}
}

Liquidation Mechanics

Typical Liquidation Process

  1. Health factor drops below 1.0
  2. Liquidator repays portion of debt
  3. Liquidator receives collateral + bonus
  4. Position health factor improves

Liquidation Bonus

#![allow(unused)]
fn main() {
// Calculate liquidator profit
let debt_to_repay = Decimal::from(1000i64);
let liquidation_bonus = Decimal::new(5, 2);  // 5% bonus

let collateral_received = debt_to_repay
    .checked_mul(Decimal::ONE.checked_add(liquidation_bonus)?)?;
// Liquidator receives $1,050 worth of collateral for repaying $1,000
}

Edge Cases

#![allow(unused)]
fn main() {
// Zero collateral amount
let result = liquidation_price(
    Decimal::ZERO,
    Decimal::from(1000i64),
    Decimal::new(80, 2),
);
// Returns error or MAX (no liquidation price exists)

// Zero debt
let result = liquidation_price(
    Decimal::from(10i64),
    Decimal::ZERO,
    Decimal::new(80, 2),
);
// Returns ZERO (no liquidation risk)
}

Position Management

Functions for managing and analyzing trading and lending positions.

Position Size

Calculate appropriate position size based on risk parameters:

#![allow(unused)]
fn main() {
use risk_metrics::{position_size, Decimal};

let account_balance = Decimal::from(100000i64);
let risk_per_trade = Decimal::new(2, 2);  // 2% risk
let stop_loss_pct = Decimal::new(5, 2);   // 5% stop loss

let size = position_size(account_balance, risk_per_trade, stop_loss_pct)?;
// size = $40,000 (2% risk / 5% stop = 40% of account)
}

Leverage Calculation

Determine effective leverage of a position:

#![allow(unused)]
fn main() {
use risk_metrics::{effective_leverage, Decimal};

let position_value = Decimal::from(50000i64);
let collateral = Decimal::from(10000i64);

let leverage = effective_leverage(position_value, collateral)?;
// leverage = 5x
}

Margin Requirements

Calculate required margin for a leveraged position:

#![allow(unused)]
fn main() {
use risk_metrics::{required_margin, Decimal};

let position_size = Decimal::from(100000i64);
let leverage = Decimal::from(10i64);

let margin = required_margin(position_size, leverage)?;
// margin = $10,000
}

Profit and Loss

Unrealized PnL

#![allow(unused)]
fn main() {
use risk_metrics::{unrealized_pnl, Decimal};

let entry_price = Decimal::from(2000i64);
let current_price = Decimal::from(2150i64);
let position_size = Decimal::from(5i64);  // 5 ETH
let is_long = true;

let pnl = unrealized_pnl(entry_price, current_price, position_size, is_long)?;
// pnl = +$750
}

Return on Investment

#![allow(unused)]
fn main() {
use risk_metrics::{position_roi, Decimal};

let entry_value = Decimal::from(10000i64);
let current_value = Decimal::from(12500i64);

let roi = position_roi(entry_value, current_value)?;
// roi = 25%
}

Risk-Adjusted Returns

Sharpe Ratio Components

#![allow(unused)]
fn main() {
use risk_metrics::{excess_return, Decimal};

let portfolio_return = Decimal::new(12, 2);  // 12%
let risk_free_rate = Decimal::new(4, 2);     // 4%

let excess = excess_return(portfolio_return, risk_free_rate)?;
// excess = 8%
}

Practical Examples

Position Sizing with Kelly Criterion

#![allow(unused)]
fn main() {
use risk_metrics::Decimal;

fn kelly_fraction(
    win_probability: Decimal,
    win_loss_ratio: Decimal,
) -> Result<Decimal, ArithmeticError> {
    // Kelly % = W - (1-W)/R
    // where W = win probability, R = win/loss ratio
    let loss_probability = Decimal::ONE.checked_sub(win_probability)?;
    let second_term = loss_probability.checked_div(win_loss_ratio)?;
    win_probability.checked_sub(second_term)
}

let win_rate = Decimal::new(55, 2);    // 55% win rate
let win_loss = Decimal::new(15, 1);    // 1.5:1 win/loss ratio

let kelly = kelly_fraction(win_rate, win_loss)?;
// Use half-Kelly for safety: kelly / 2
}

Portfolio Position Limits

#![allow(unused)]
fn main() {
fn validate_position(
    new_position: Decimal,
    portfolio_value: Decimal,
    max_concentration: Decimal,  // e.g., 10%
) -> bool {
    let concentration = new_position
        .checked_div(portfolio_value)
        .unwrap_or(Decimal::MAX);

    concentration <= max_concentration
}
}

Liquidation-Safe Position

#![allow(unused)]
fn main() {
use risk_metrics::{max_borrowable, health_factor, Decimal};

fn safe_position_size(
    available_collateral: Decimal,
    threshold: Decimal,
    target_hf: Decimal,
    price_buffer: Decimal,  // e.g., 20% buffer for volatility
) -> Result<Decimal, ArithmeticError> {
    // Reduce effective collateral by price buffer
    let buffered_collateral = available_collateral
        .checked_mul(Decimal::ONE.checked_sub(price_buffer)?)?;

    max_borrowable(buffered_collateral, threshold, target_hf)
}
}

Break-Even Analysis

#![allow(unused)]
fn main() {
fn break_even_price(
    entry_price: Decimal,
    position_size: Decimal,
    fees: Decimal,
    is_long: bool,
) -> Result<Decimal, ArithmeticError> {
    let fee_per_unit = fees.checked_div(position_size)?;

    if is_long {
        entry_price.checked_add(fee_per_unit)
    } else {
        entry_price.checked_sub(fee_per_unit)
    }
}
}

Position Tracking

#![allow(unused)]
fn main() {
struct Position {
    asset: String,
    entry_price: Decimal,
    size: Decimal,
    is_long: bool,
    stop_loss: Option<Decimal>,
    take_profit: Option<Decimal>,
}

impl Position {
    fn value_at(&self, price: Decimal) -> Result<Decimal, ArithmeticError> {
        self.size.checked_mul(price)
    }

    fn pnl_at(&self, price: Decimal) -> Result<Decimal, ArithmeticError> {
        let diff = if self.is_long {
            price.checked_sub(self.entry_price)?
        } else {
            self.entry_price.checked_sub(price)?
        };
        diff.checked_mul(self.size)
    }

    fn should_close(&self, price: Decimal) -> bool {
        if let Some(sl) = self.stop_loss {
            if (self.is_long && price <= sl) || (!self.is_long && price >= sl) {
                return true;
            }
        }
        if let Some(tp) = self.take_profit {
            if (self.is_long && price >= tp) || (!self.is_long && price <= tp) {
                return true;
            }
        }
        false
    }
}
}

Browser Integration

Using Keystone in web applications via WebAssembly.

Installation

npm install @dijkstra-keystone/wasm
# or
yarn add @dijkstra-keystone/wasm

Setup

ES Modules

import init, * as keystone from '@dijkstra-keystone/wasm';

async function setup() {
  await init();
  // Now ready to use
  const result = keystone.add("100.50", "49.50");
  console.log(result); // "150"
}

Bundlers (Webpack, Vite, etc.)

Most bundlers handle WASM automatically:

// vite.config.js
export default {
  optimizeDeps: {
    exclude: ['@dijkstra-keystone/wasm']
  }
}
// In your code
import * as keystone from '@dijkstra-keystone/wasm';

const result = keystone.multiply("99.99", "1.0825");

Script Tag

<script type="module">
  import init, * as keystone from './keystone_wasm.js';

  init().then(() => {
    document.getElementById('result').textContent =
      keystone.add("1.1", "2.2");
  });
</script>

Basic Usage

All functions accept string representations of decimals:

import * as keystone from '@dijkstra-keystone/wasm';

// Arithmetic
keystone.add("100", "50");           // "150"
keystone.subtract("100", "30");      // "70"
keystone.multiply("25", "4");        // "100"
keystone.divide("100", "3");         // "33.333..."

// Comparison
keystone.compare("100", "200");      // -1 (less than)
keystone.compare("200", "200");      // 0 (equal)
keystone.compare("300", "200");      // 1 (greater than)

// Properties
keystone.abs("-42");                 // "42"
keystone.is_zero("0.00");            // true
keystone.is_negative("-5");          // true

Rounding

import { round } from '@dijkstra-keystone/wasm';

// Round to 2 decimal places
round("123.456", 2, "half_even");   // "123.46" (banker's)
round("123.456", 2, "half_up");     // "123.46"
round("123.455", 2, "half_up");     // "123.46"
round("123.454", 2, "half_up");     // "123.45"

// Available modes
round("2.5", 0, "half_even");       // "2" (ties to even)
round("2.5", 0, "half_up");         // "3" (ties away from zero)
round("2.5", 0, "down");            // "2" (toward -infinity)
round("2.5", 0, "up");              // "3" (toward +infinity)
round("2.5", 0, "toward_zero");     // "2" (truncate)
round("2.5", 0, "away_from_zero");  // "3"

Financial Functions

import * as keystone from '@dijkstra-keystone/wasm';

// Simple interest
keystone.simple_interest("10000", "0.05", "3");
// 10000 * 0.05 * 3 = "1500"

// Compound interest (principal, rate, periods_per_year, years)
keystone.compound_interest("10000", "0.05", 12, 5);
// Returns total interest earned

// Future value
keystone.future_value("10000", "0.07", 10);
// 10000 * (1.07)^10

// Present value
keystone.present_value("19672", "0.07", 10);
// Discounts future value to present

Risk Metrics

import * as keystone from '@dijkstra-keystone/wasm';

// Health factor
keystone.health_factor("10000", "5000", "0.80");
// (10000 * 0.80) / 5000 = "1.6"

// Is position healthy?
keystone.is_healthy("10000", "5000", "0.80");  // true

// Liquidation price
keystone.liquidation_price("5", "10000", "0.80");
// Price per unit at which position becomes liquidatable

Error Handling

Functions throw on error:

import * as keystone from '@dijkstra-keystone/wasm';

try {
  const result = keystone.divide("100", "0");
} catch (e) {
  console.error(e.message);  // "division by zero"
}

// Or use optional chaining pattern
function safeDivide(a, b) {
  try {
    return keystone.divide(a, b);
  } catch {
    return null;
  }
}

React Integration

import { useState, useEffect } from 'react';
import init, * as keystone from '@dijkstra-keystone/wasm';

function Calculator() {
  const [ready, setReady] = useState(false);
  const [result, setResult] = useState('');

  useEffect(() => {
    init().then(() => setReady(true));
  }, []);

  const calculate = (a, b) => {
    if (!ready) return;
    try {
      setResult(keystone.add(a, b));
    } catch (e) {
      setResult(`Error: ${e.message}`);
    }
  };

  return (
    <div>
      {ready ? (
        <button onClick={() => calculate("100.50", "49.50")}>
          Add
        </button>
      ) : (
        <span>Loading...</span>
      )}
      <div>Result: {result}</div>
    </div>
  );
}

Vue Integration

<script setup>
import { ref, onMounted } from 'vue';
import init, * as keystone from '@dijkstra-keystone/wasm';

const ready = ref(false);
const result = ref('');

onMounted(async () => {
  await init();
  ready.value = true;
});

function calculate() {
  result.value = keystone.multiply("99.99", "1.0825");
}
</script>

<template>
  <button v-if="ready" @click="calculate">Calculate</button>
  <span v-else>Loading...</span>
  <div>{{ result }}</div>
</template>

Performance Tips

  1. Batch operations: Minimize JS-WASM boundary crossings
  2. Reuse results: Store intermediate values in JS when possible
  3. Async init: Initialize WASM during app startup, not on-demand
// Pre-compute values
const TAX_RATE = "0.0825";
const DISCOUNT = "0.15";

function calculateTotal(items) {
  let subtotal = "0";
  for (const item of items) {
    subtotal = keystone.add(subtotal, item.price);
  }

  const discount = keystone.multiply(subtotal, DISCOUNT);
  const afterDiscount = keystone.subtract(subtotal, discount);
  const tax = keystone.multiply(afterDiscount, TAX_RATE);

  return keystone.add(afterDiscount, tax);
}

WASM API Reference

Complete API reference for the WebAssembly bindings.

Arithmetic Operations

add(a, b)

Add two decimal values.

add("100.50", "49.50")  // "150"
add("-10", "5")         // "-5"

Parameters:

  • a: string - First operand
  • b: string - Second operand

Returns: string - Sum

Throws: On overflow or invalid input


subtract(a, b)

Subtract b from a.

subtract("100", "30")   // "70"
subtract("10", "25")    // "-15"

Parameters:

  • a: string - Minuend
  • b: string - Subtrahend

Returns: string - Difference


multiply(a, b)

Multiply two decimal values.

multiply("25", "4")     // "100"
multiply("0.1", "0.1")  // "0.01"

Parameters:

  • a: string - First factor
  • b: string - Second factor

Returns: string - Product

Throws: On overflow


divide(a, b)

Divide a by b.

divide("100", "4")      // "25"
divide("10", "3")       // "3.333..."

Parameters:

  • a: string - Dividend
  • b: string - Divisor

Returns: string - Quotient

Throws: On division by zero


remainder(a, b)

Calculate remainder of a divided by b.

remainder("10", "3")    // "1"
remainder("7.5", "2")   // "1.5"

Comparison Operations

compare(a, b)

Compare two decimal values.

compare("100", "200")   // -1
compare("200", "200")   // 0
compare("300", "200")   // 1

Returns: number - -1 if a < b, 0 if equal, 1 if a > b


min(a, b) / max(a, b)

Return minimum or maximum of two values.

min("100", "200")       // "100"
max("100", "200")       // "200"

Rounding Operations

round(value, dp, mode)

Round to specified decimal places.

round("123.456", 2, "half_up")     // "123.46"
round("123.445", 2, "half_even")   // "123.44"

Parameters:

  • value: string - Value to round
  • dp: number - Decimal places (0-28)
  • mode: string - Rounding mode

Rounding Modes:

ModeDescription
"half_even" or "bankers"Ties round to nearest even
"half_up"Ties round away from zero
"half_down"Ties round toward zero
"up"Always toward +infinity
"down"Always toward -infinity
"toward_zero" or "truncate"Truncate
"away_from_zero"Away from zero

floor(value) / ceil(value) / trunc(value)

Convenience rounding functions.

floor("2.7")    // "2"
floor("-2.7")   // "-3"
ceil("2.3")     // "3"
ceil("-2.3")    // "-2"
trunc("2.9")    // "2"
trunc("-2.9")   // "-2"

Properties

abs(value)

Absolute value.

abs("-42")      // "42"
abs("42")       // "42"

is_zero(value) / is_positive(value) / is_negative(value)

Boolean checks.

is_zero("0.00")        // true
is_positive("5")       // true
is_negative("-5")      // true

scale(value)

Get the scale (decimal places).

scale("123.45")        // 2
scale("100")           // 0
scale("1.000")         // 3

normalize(value)

Remove trailing zeros.

normalize("100.00")    // "100"
normalize("1.50")      // "1.5"

Financial Functions

simple_interest(principal, rate, time)

Calculate simple interest.

simple_interest("10000", "0.05", "3")  // "1500"

compound_interest(principal, rate, periods_per_year, years)

Calculate compound interest.

compound_interest("10000", "0.05", 12, 5)  // Total interest

future_value(present_value, rate, periods)

Calculate future value.

future_value("10000", "0.07", 10)  // ~"19671.51"

present_value(future_value, rate, periods)

Calculate present value.

present_value("19672", "0.07", 10)  // ~"10000"

percentage_of(value, percent)

Calculate percentage of a value.

percentage_of("200", "0.15")  // "30" (15% of 200)

percentage_change(old_value, new_value)

Calculate percentage change.

percentage_change("100", "125")  // "0.25" (25% increase)

Risk Functions

health_factor(collateral, debt, threshold)

Calculate lending position health factor.

health_factor("10000", "5000", "0.80")  // "1.6"

is_healthy(collateral, debt, threshold)

Check if position is healthy (HF >= 1).

is_healthy("10000", "5000", "0.80")  // true

liquidation_price(collateral_amount, debt, threshold)

Calculate liquidation price.

liquidation_price("5", "10000", "0.80")  // "2500"

max_borrowable(collateral, threshold, min_health_factor)

Calculate maximum safe debt.

max_borrowable("100000", "0.80", "1.5")  // "53333.33..."

Tolerance Functions

approx_eq(a, b, tolerance)

Check if values are approximately equal.

approx_eq("100.00", "100.01", "0.02")  // true

within_percentage(a, b, percent)

Check if values are within percentage of each other.

within_percentage("102", "100", "5")  // true (within 5%)

within_basis_points(a, b, bps)

Check if values are within basis points.

within_basis_points("1.0010", "1.0000", "15")  // true (within 15 bps)

Constants

ZERO        // "0"
ONE         // "1"
ONE_HUNDRED // "100"
MAX         // Maximum representable value
MIN         // Minimum representable value

Error Types

All functions throw JavaScript errors on failure:

try {
  divide("1", "0");
} catch (e) {
  // e.message contains error description
  switch (true) {
    case e.message.includes("division by zero"):
      // Handle division by zero
      break;
    case e.message.includes("overflow"):
      // Handle overflow
      break;
    case e.message.includes("invalid"):
      // Handle parse error
      break;
  }
}

Benchmarks

Performance characteristics of Keystone operations measured with Criterion.

Arithmetic Operations

OperationTime (ns)Throughput
Addition~8125M ops/sec
Subtraction~8125M ops/sec
Multiplication~8125M ops/sec
Division~3628M ops/sec
Remainder~4025M ops/sec

Rounding Operations

OperationTime (ns)
round_dp (2 places)~15
floor~12
ceil~12
trunc~10

Financial Calculations

OperationTime (ns)
simple_interest~25
compound_interest (12 periods, 5 years)~850
future_value (10 periods)~200
present_value (10 periods)~220
effective_annual_rate~180

Risk Metrics

OperationTime (ns)
health_factor~45
liquidation_price~50
collateral_ratio~40
max_borrowable~55

Running Benchmarks

cd keystone
cargo bench

Specific Benchmark

cargo bench --bench arithmetic
cargo bench --bench financial
cargo bench --bench risk

With Baseline Comparison

# Save baseline
cargo bench -- --save-baseline main

# Compare to baseline
cargo bench -- --baseline main

Benchmark Code

Located in crates/precision-core/benches/:

#![allow(unused)]
fn main() {
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use precision_core::Decimal;

fn bench_addition(c: &mut Criterion) {
    let a = Decimal::from(12345i64);
    let b = Decimal::from(67890i64);

    c.bench_function("decimal_add", |bencher| {
        bencher.iter(|| black_box(a).checked_add(black_box(b)))
    });
}

criterion_group!(benches, bench_addition);
criterion_main!(benches);
}

WASM Performance

WASM operations include JS-WASM boundary overhead:

OperationNative (ns)WASM (ns)Overhead
Addition8~50~6x
Division36~100~3x
compound_interest850~1200~1.4x

WASM Binary Size

BuildSize
Debug~450 KB
Release~97 KB
Release + wasm-opt~85 KB

Optimization Settings

From Cargo.toml:

[profile.release]
opt-level = "z"      # Optimize for size
lto = "fat"          # Full link-time optimization
codegen-units = 1    # Better optimization
panic = "abort"      # Smaller binary
strip = true         # Remove symbols
overflow-checks = true  # Keep overflow checking

Memory Usage

TypeSize
Decimal16 bytes
RoundingMode1 byte
ArithmeticError1 byte

Stack-only allocation for all operations.

Comparison with Alternatives

vs JavaScript Number

// JavaScript floating-point
0.1 + 0.2  // 0.30000000000000004

// Keystone
keystone.add("0.1", "0.2")  // "0.3"

Keystone is slower but guarantees correctness for financial calculations.

vs BigInt

JavaScript BigInt handles integers only. Keystone provides:

  • Decimal precision up to 28 digits
  • Native rounding modes
  • Financial functions

vs decimal.js

Similar precision, but Keystone provides:

  • Deterministic cross-platform results
  • Smaller WASM bundle
  • ZK-proof compatibility
  • Rust native performance

Profiling

CPU Profiling

cargo bench --bench arithmetic -- --profile-time 10

Memory Profiling

cargo bench --bench arithmetic -- --plotting-backend disabled

CI Benchmarks

Benchmarks run on every PR to detect regressions:

- name: Run benchmarks
  run: cargo bench -- --noplot

- name: Compare with main
  run: |
    cargo bench -- --save-baseline pr
    git checkout main
    cargo bench -- --baseline pr

Determinism

Keystone guarantees identical results across all platforms and execution environments.

Why Determinism Matters

Financial Applications

  • Audit trails require reproducible calculations
  • Reconciliation between systems
  • Regulatory compliance

Zero-Knowledge Proofs

  • Prover and verifier must compute identical results
  • Any divergence breaks proof validity
  • Cross-platform proof generation

Distributed Systems

  • Consensus requires identical state transitions
  • Smart contract execution
  • Multi-party computation

Guarantees

Bit-Exact Results

The same inputs always produce the same outputs, regardless of:

  • Operating system (Linux, macOS, Windows)
  • CPU architecture (x86_64, ARM64, WASM)
  • Compiler version
  • Optimization level

No Floating-Point

Keystone uses fixed-point decimal arithmetic:

#![allow(unused)]
fn main() {
// Floating-point: non-deterministic
let a: f64 = 0.1;
let b: f64 = 0.2;
let c = a + b;  // May vary by platform

// Keystone: deterministic
let a = Decimal::new(1, 1);  // 0.1
let b = Decimal::new(2, 1);  // 0.2
let c = a.checked_add(b);    // Always 0.3
}

no_std Core

The core library has no OS dependencies:

#![allow(unused)]
#![no_std]  // No standard library
#![forbid(unsafe_code)]  // No undefined behavior
fn main() {
}

Verification

Test Vectors

Pre-computed test vectors verify determinism:

#![allow(unused)]
fn main() {
#[test]
fn test_determinism_vectors() {
    let vectors = [
        ("100", "3", "div", "33.333333333333333333333333333"),
        ("0.1", "0.2", "add", "0.3"),
        ("999999999999999999999999999", "2", "mul",
         "1999999999999999999999999998"),
    ];

    for (a, b, op, expected) in vectors {
        let a: Decimal = a.parse().unwrap();
        let b: Decimal = b.parse().unwrap();
        let result = match op {
            "add" => a.checked_add(b),
            "mul" => a.checked_mul(b),
            "div" => a.checked_div(b),
            _ => panic!("unknown op"),
        };
        assert_eq!(result.unwrap().to_string(), expected);
    }
}
}

Cross-Platform CI

CI runs on multiple platforms:

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    target:
      - x86_64-unknown-linux-gnu
      - x86_64-pc-windows-msvc
      - x86_64-apple-darwin
      - aarch64-apple-darwin
      - wasm32-unknown-unknown

Property-Based Testing

Random inputs verify consistency:

#![allow(unused)]
fn main() {
use proptest::prelude::*;

proptest! {
    #[test]
    fn add_commutative(a: i64, b: i64) {
        let da = Decimal::from(a);
        let db = Decimal::from(b);

        let ab = da.checked_add(db);
        let ba = db.checked_add(da);

        assert_eq!(ab, ba);
    }
}
}

ZK Compatibility

SP1 Integration

Keystone is designed for use in SP1 zkVM:

// In SP1 program
#![no_main]
sp1_zkvm::entrypoint!(main);

use precision_core::Decimal;

pub fn main() {
    let a: Decimal = sp1_zkvm::io::read();
    let b: Decimal = sp1_zkvm::io::read();

    let result = a.checked_mul(b).expect("overflow");

    sp1_zkvm::io::commit(&result);
}

Proof Generation

#![allow(unused)]
fn main() {
// Generate proof
let (pk, vk) = client.setup(ELF);
let mut stdin = SP1Stdin::new();
stdin.write(&a);
stdin.write(&b);

let proof = client.prove(&pk, stdin)?;

// Verify anywhere
client.verify(&proof, &vk)?;
}

Implementation Details

Decimal Representation

128-bit fixed-point with explicit scale:

#![allow(unused)]
fn main() {
struct Decimal {
    // 96-bit mantissa + 1-bit sign
    mantissa: i128,
    // Scale: number of decimal places (0-28)
    scale: u32,
}
}

Normalization

Canonical form ensures comparison stability:

#![allow(unused)]
fn main() {
let a = Decimal::new(100, 2);   // 1.00
let b = Decimal::new(1, 0);     // 1

// Internal comparison normalizes
assert!(a == b);

// Explicit normalization
let normalized = a.normalize(); // 1 (scale = 0)
}

Rounding Specification

Each rounding mode has precise semantics:

ModeTie-Breaking Rule
HalfEvenTo nearest even digit
HalfUpAway from zero
HalfDownToward zero

Edge Cases

Maximum Precision

28 significant digits:

#![allow(unused)]
fn main() {
let max_precision = Decimal::new(
    9999999999999999999999999999i128,
    0
);
}

Minimum Value

#![allow(unused)]
fn main() {
let min_scale = Decimal::new(1, 28);
// 0.0000000000000000000000000001
}

Overflow Behavior

Explicit error handling, never silent wraparound:

#![allow(unused)]
fn main() {
let max = Decimal::MAX;
let result = max.checked_add(Decimal::ONE);
assert!(result.is_none());  // Never silently wraps
}