Skip to main content

Stroops and USDC Precision

USDC on Stellar uses 7 decimal places. The smallest unit is called a stroop:
1 USDC = 10,000,000 stroops
All on-chain amounts are stored and transmitted as stroops represented by bigint values. The SDK provides two helper functions to convert between human-readable strings and stroops.
Why stroops? Floating-point arithmetic introduces rounding errors that are unacceptable for financial transactions. By using integer math with bigint, every calculation is exact - no precision loss, no rounding surprises.

toStroops

Converts a human-readable amount string to a bigint in stroops.
import { toStroops } from "vowena";

const amount = toStroops("9.99");
// → 99900000n
Signature:
function toStroops(amount: string): bigint;
ParameterTypeDescription
amountstringHuman-readable decimal amount (e.g., "9.99", "100", "0.0000001")
Returns: bigint - The amount in stroops.

fromStroops

Converts a bigint in stroops back to a human-readable string.
import { fromStroops } from "vowena";

const display = fromStroops(99900000n);
// → "9.99"
Signature:
function fromStroops(stroops: bigint): string;
ParameterTypeDescription
stroopsbigintAmount in stroops
Returns: string - Human-readable decimal string.

Conversion Table

Human-ReadableStroops (bigint)Usage
"0.01"100000nMinimum practical charge
"0.50"5000000nMicro-subscription
"1.00"10000000n1 USDC
"4.99"49900000nStarter plan
"9.99"99900000nStandard monthly plan
"14.99"149900000nPremium plan
"29.99"299900000nProfessional plan
"99.99"999900000nEnterprise plan
"0.0000001"1nOne stroop (smallest unit)

Constants

The SDK exports several constants for common values:
import {
  USDC_DECIMALS,
  SECONDS_PER_DAY,
  SECONDS_PER_MONTH,
  SECONDS_PER_YEAR,
} from "vowena";
ConstantValueDescription
USDC_DECIMALS7Number of decimal places for USDC on Stellar
SECONDS_PER_DAY86400Seconds in one day
SECONDS_PER_MONTH2592000Seconds in 30 days
SECONDS_PER_YEAR31536000Seconds in 365 days
These time constants are the same values used by the Vowena smart contract. Use them when setting period and gracePeriod in buildCreatePlan() to stay consistent with on-chain conventions.

Examples

Creating a Plan with Correct Amounts

import { VowenaClient, NETWORKS, toStroops, SECONDS_PER_MONTH } from "vowena";

const client = new VowenaClient({
  contractId: NETWORKS.testnet.contractId,
  rpcUrl: NETWORKS.testnet.rpcUrl,
  networkPassphrase: NETWORKS.testnet.networkPassphrase,
});

const xdr = await client.buildCreatePlan({
  merchant: "GMERCHANT...ADDR",
  token: NETWORKS.testnet.usdcAddress,
  amount: toStroops("9.99"),          // 99900000n
  period: SECONDS_PER_MONTH,          // 2592000
  priceCeiling: toStroops("14.99"),   // 149900000n
  gracePeriod: SECONDS_PER_DAY * 3,   // 259200 (3 days)
});

Displaying a Subscription Price

import { fromStroops } from "vowena";

const plan = await client.getPlan(42, callerAddress);
const priceDisplay = `$${fromStroops(plan.amount)} USDC / month`;
// → "$9.99 USDC / month"