Unit Conversion

Learn how to convert between different unit denominations in Stacks. The blockchain uses microSTX as its base unit, where 1 STX = 1,000,000 microSTX. Proper unit conversion is essential for displaying amounts to users and processing transactions.

Basic conversions

Convert between STX and microSTX using simple conversion functions.

basic-conversions.ts
// Convert STX to microSTX
function stxToMicroStx(stx: number | string): bigint {
  const stxAmount = typeof stx === 'string' ? parseFloat(stx) : stx;
  return BigInt(Math.floor(stxAmount * 1_000_000));
}

// Convert microSTX to STX
function microStxToStx(microStx: number | bigint | string): string {
  const amount = BigInt(microStx);
  const stx = Number(amount) / 1_000_000;
  return stx.toFixed(6).replace(/\.?0+$/, '');
}

// Usage examples
const microStx = stxToMicroStx(1.5);      // 1500000n
const stx = microStxToStx(1500000);       // "1.5"

Precision-safe handling

Handle large numbers without precision loss using BigInt operations.

precision-safe.ts
class StxConverter {
  static readonly MICROSTX_PER_STX = 1_000_000n;
  
  static toMicroStx(amount: string | number, decimals = 6): bigint {
    const amountStr = amount.toString();
    const [whole, decimal = ''] = amountStr.split('.');
    const paddedDecimal = decimal.padEnd(decimals, '0').slice(0, decimals);
    return BigInt(whole + paddedDecimal);
  }
  
  static toStx(microStx: bigint | string | number): string {
    const amount = BigInt(microStx);
    const isNegative = amount < 0n;
    const absoluteAmount = isNegative ? -amount : amount;
    
    const str = absoluteAmount.toString().padStart(7, '0');
    const whole = str.slice(0, -6) || '0';
    const decimal = str.slice(-6);
    
    let result = `${whole}.${decimal}`.replace(/\.?0+$/, '');
    return isNegative ? `-${result}` : result;
  }
}

// Precise conversion examples
const precise1 = StxConverter.toMicroStx('123.456789'); // 123456789n
const precise2 = StxConverter.toStx(123456789n);        // "123.456789"

Token conversions

Handle tokens with different decimal places using a flexible converter class.

token-converter.ts
interface TokenInfo {
  decimals: number;
  symbol: string;
  name: string;
}

class TokenConverter {
  constructor(private tokenInfo: TokenInfo) {}
  
  toSmallestUnit(amount: string | number): bigint {
    if (typeof amount === 'string') {
      const [whole, decimal = ''] = amount.split('.');
      const paddedDecimal = decimal
        .padEnd(this.tokenInfo.decimals, '0')
        .slice(0, this.tokenInfo.decimals);
      return BigInt(whole + paddedDecimal);
    }
    
    const multiplier = 10n ** BigInt(this.tokenInfo.decimals);
    return BigInt(Math.floor(amount * Number(multiplier)));
  }
  
  fromSmallestUnit(amount: bigint | string): string {
    const value = BigInt(amount);
    const divisor = 10n ** BigInt(this.tokenInfo.decimals);
    const whole = value / divisor;
    const remainder = value % divisor;
    
    if (remainder === 0n) return whole.toString();
    
    const decimal = remainder
      .toString()
      .padStart(this.tokenInfo.decimals, '0')
      .replace(/0+$/, '');
    
    return `${whole}.${decimal}`;
  }
}

// Different token examples
const usdc = new TokenConverter({ decimals: 6, symbol: 'USDC', name: 'USD Coin' });
const btc = new TokenConverter({ decimals: 8, symbol: 'BTC', name: 'Bitcoin' });

const usdcAmount = usdc.toSmallestUnit('100.50');     // 100500000n
const btcAmount = btc.toSmallestUnit('0.00123456');   // 123456n

Display formatting

Format amounts for user interfaces with localization support.

stx-formatter.ts
class StxFormatter {
  static format(microStx: bigint, options?: {
    decimals?: number;
    locale?: string;
    symbol?: boolean;
  }): string {
    const stx = StxConverter.toStx(microStx);
    const number = parseFloat(stx);
    
    const formatted = new Intl.NumberFormat(options?.locale || 'en-US', {
      minimumFractionDigits: 0,
      maximumFractionDigits: options?.decimals ?? 6,
    }).format(number);
    
    return options?.symbol ? `${formatted} STX` : formatted;
  }
  
  static compact(microStx: bigint): string {
    const stx = Number(StxConverter.toStx(microStx));
    
    if (stx >= 1_000_000) {
      return `${(stx / 1_000_000).toFixed(2)}M STX`;
    } else if (stx >= 1_000) {
      return `${(stx / 1_000).toFixed(2)}K STX`;
    }
    
    return this.format(microStx, { decimals: 6, symbol: true });
  }
}

// Formatting examples
const formatted = StxFormatter.format(123456789n, {
  decimals: 2,
  symbol: true
}); // "123.46 STX"

const compact = StxFormatter.compact(1234567890000n); // "1.23K STX"

Input validation

Validate and sanitize user input for amount fields.

amount-input.ts
class AmountInput {
  static validate(input: string, options?: {
    decimals?: number;
    min?: string;
    max?: string;
  }): { valid: boolean; error?: string } {
    // Check format
    if (!/^\d*\.?\d*$/.test(input)) {
      return { valid: false, error: 'Invalid number format' };
    }
    
    // Check decimal places
    const parts = input.split('.');
    if (parts[1] && parts[1].length > (options?.decimals || 6)) {
      return { valid: false, error: `Maximum ${options?.decimals || 6} decimal places` };
    }
    
    // Check range
    if (options?.min) {
      const value = parseFloat(input);
      if (value < parseFloat(options.min)) {
        return { valid: false, error: `Minimum amount is ${options.min}` };
      }
    }
    
    return { valid: true };
  }
  
  static sanitize(input: string, decimals = 6): string {
    let sanitized = input.replace(/[^\d.]/g, '');
    const parts = sanitized.split('.');
    
    if (parts.length > 2) {
      sanitized = parts[0] + '.' + parts.slice(1).join('');
    }
    
    if (parts[1] && parts[1].length > decimals) {
      sanitized = parts[0] + '.' + parts[1].slice(0, decimals);
    }
    
    return sanitized;
  }
}

// Validation examples
const result1 = AmountInput.validate('123.456', { 
  decimals: 6, 
  min: '0.000001' 
}); // { valid: true }

const result2 = AmountInput.validate('123.4567890', { 
  decimals: 6 
}); // { valid: false, error: 'Maximum 6 decimal places' }

Was this helpful?