Documentation Index
Fetch the complete documentation index at: https://docs.kontor.network/llms.txt
Use this file to discover all available pages before exploring further.
Sigil provides arbitrary precision number types—Integer and Decimal—for financial calculations that require exact arithmetic without overflow or rounding errors.
Integer Type
The Integer type provides 256-bit signed arbitrary precision integers.
Structure
pub struct Integer {
r0: u64,
r1: u64,
r2: u64,
r3: u64,
sign: Sign, // Plus or Minus
}
Maximum value: 2^256 - 1 (roughly 115 quattuorvigintillion)
Creating Integers
// From literals
let a: Integer = 42.into();
let b: Integer = "1_000_000_000_000_000_000".into();
// From u64
let c = Integer::from(12345u64);
// Default is zero
let zero = Integer::default();
Arithmetic Operations
Unchecked operations (panic on overflow):
let sum = a + b; // Panics on overflow
let diff = a - b; // Panics on underflow
let product = a * b; // Panics on overflow
let quotient = a / b; // Panics on division by zero
let remainder = a % b;
Checked operations (return Result):
let sum = a.add(b)?; // Returns Result<Integer, Error>
let diff = a.sub(b)?;
let product = a.mul(b)?;
let quotient = a.div(b)?; // Returns Error::DivByZero on zero
let sqrt = a.sqrt()?;
Comparisons
if a > b { }
if a == b { }
if a <= b { }
// Find maximum
let max = if a > b { a } else { b };
Conversions
// To string
let s = a.to_string();
// From string
let n: Integer = "12345".into();
// To Decimal
let d = Decimal::from(a);
When to Use Integer
Use Integer for:
- Token balances
- Large financial calculations
- Vote counts
- Pool liquidity amounts
- Any value that might exceed u64 (18.4 quintillion)
- Exact arithmetic requirements
Use u64 for:
- Small counters
- Block heights
- Array indices
- When you know the value fits in 64 bits
Decimal Type
The Decimal type provides arbitrary precision decimals for calculations requiring fractional values.
Structure
pub struct Decimal {
r0: u64,
r1: u64,
r2: u64,
r3: u64,
sign: Sign,
}
Creating Decimals
// From Integer
let i = Integer::from(100);
let d = Decimal::from(i);
// From u64
let d = Decimal::from(42);
Operations
// Logarithm (base 10)
let d = Decimal::from(100);
let log = d.log10()?; // Result: "2.0"
// Conversion to Integer (truncates)
let i = d.to_integer();
When to Use Decimal
Use Decimal for:
- Price calculations
- Percentage calculations
- Logarithmic operations
- Scientific calculations
Most contracts use Integer for exact arithmetic and avoid decimals entirely.
Example: Token Contract
use stdlib::*;
contract!(name = "token");
#[derive(Clone, Default, StorageRoot)]
struct TokenStorage {
pub ledger: Map<String, Integer>,
}
impl Guest for Token {
fn mint(ctx: &ProcContext, n: Integer) {
let to = ctx.signer().to_string();
let ledger = ctx.model().ledger();
let balance = ledger.get(&to).unwrap_or_default();
// Unchecked: simple addition
ledger.set(to, balance + n);
}
fn mint_checked(ctx: &ProcContext, n: Integer) -> Result<(), Error> {
let to = ctx.signer().to_string();
let ledger = ctx.model().ledger();
let balance = ledger.get(&to).unwrap_or_default();
// Checked: returns overflow error
ledger.set(to, balance.add(n)?);
Ok(())
}
fn transfer(ctx: &ProcContext, to: String, n: Integer) -> Result<(), Error> {
let from = ctx.signer().to_string();
let ledger = ctx.model().ledger();
let from_balance = ledger.get(&from).unwrap_or_default();
let to_balance = ledger.get(&to).unwrap_or_default();
if from_balance < n {
return Err(Error::Message("insufficient funds".to_string()));
}
// Unchecked: subtraction safe after validation
ledger.set(from, from_balance - n);
ledger.set(to, to_balance + n);
Ok(())
}
fn balance_log10(ctx: &ViewContext, acc: String) -> Result<Option<Decimal>, Error> {
ctx.model()
.ledger()
.get(acc)
.map(|i| Decimal::from(i).log10())
.transpose()
}
}
Example: AMM Pool Math
fn create_pool(
ctx: &ProcContext,
amount_a: Integer,
amount_b: Integer,
) -> Result<Integer, Error> {
// LP shares = sqrt(amount_a * amount_b)
let product = amount_a.mul(amount_b)?; // Checked multiplication
let lp_shares = product.sqrt()?; // Checked sqrt
// Store in pool
ctx.model().set_lp_total_supply(lp_shares.clone());
Ok(lp_shares)
}
fn calculate_swap(
reserve_in: Integer,
reserve_out: Integer,
amount_in: Integer,
fee_bps: Integer,
) -> Result<Integer, Error> {
// Constant product formula: x * y = k
// With fee: out = (amount_in * (10000 - fee_bps) * reserve_out) / (reserve_in * 10000 + amount_in * (10000 - fee_bps))
let fee_multiplier = Integer::from(10000) - fee_bps;
let amount_in_with_fee = amount_in.mul(fee_multiplier)?;
let numerator = amount_in_with_fee.mul(reserve_out)?;
let denominator = reserve_in.mul(Integer::from(10000))?.add(amount_in_with_fee)?;
numerator.div(denominator)
}
Choosing Between Checked and Unchecked
Use checked operations when:
- Working with user inputs
- Complex calculations where overflow is possible
- You want specific error messages for overflow
- Financial calculations requiring exact results
Use unchecked operations when:
- You’ve already validated the operation is safe
- Performance is critical
- The overflow would indicate a bug (panic is appropriate)
See the token contract example above for combining both approaches—validation with checked arithmetic, then unchecked operations after validation.
Common Patterns
Safe Division
fn calculate_ratio(numerator: Integer, denominator: Integer) -> Result<Integer, Error> {
if denominator == Integer::from(0) {
return Err(Error::DivByZero("Cannot divide by zero".to_string()));
}
numerator.div(denominator)
}
Square Root for LP Tokens
fn mint_liquidity(amount_a: Integer, amount_b: Integer) -> Result<Integer, Error> {
let product = amount_a.mul(amount_b)?;
product.sqrt() // Common pattern for initial LP shares
}
Percentage Calculations
fn apply_fee(amount: Integer, fee_bps: Integer) -> Result<Integer, Error> {
// fee_bps is basis points (1% = 100 bps)
let fee = amount.mul(fee_bps)?.div(Integer::from(10000))?;
amount.sub(fee)
}
Comparison Utilities
fn max(a: Integer, b: Integer) -> Integer {
if a > b { a } else { b }
}
fn min(a: Integer, b: Integer) -> Integer {
if a < b { a } else { b }
}
Quick Reference
Number Types and Ranges
Integer
- 256-bit signed arbitrary precision integers
- Range:
±115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457
- Maximum value: 2^256 - 1
Decimal
- Arbitrary precision decimals with up to 18 decimal places
- Range:
±(2^256 - 1) / 10^18
- Full range:
±115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457.584_007_913_129_639_936
Arithmetic Operations
Both types support basic arithmetic operations (add, sub, mul, div) and comparisons.
Unchecked (using operators):
let result = a + b; // Panics on overflow
let result = a - b; // Panics on underflow
let result = a * b; // Panics on overflow
let result = a / b; // Panics on division by zero
Checked (using methods):
let result = a.add(b)?; // Returns Result<Integer, Error>
let result = a.sub(b)?; // Error::Overflow on overflow
let result = a.mul(b)?; // Error::Overflow on overflow
let result = a.div(b)?; // Error::DivByZero on zero
Advanced Operations
Integer:
.sqrt() - Square root (returns Result<Integer, Error>)
Decimal:
.log10() - Base-10 logarithm (returns Result<Decimal, Error>)
- Additional operations to be expanded
Type Conversions
// Integer from literals
let i: Integer = 42.into();
let i: Integer = "1_000_000".into();
// Decimal from Integer
let d = Decimal::from(integer);
// Integer from Decimal (truncates)
let i = decimal.to_integer();