UNPKG

@wuwei-labs/srsly

Version:
120 lines 4.47 kB
/** * @purpose Amount parameter conversion utilities * * Converts user-friendly ATLAS amounts to stardust (smallest unit) for use with Solana programs. * 1 ATLAS = 100,000,000 stardust (8 decimal places) */ /** * Stardust per ATLAS (8 decimal places) */ const STARDUST_PER_ATLAS = 100_000_000; const MAX_U64 = 18446744073709551615n; /** * Convert amount parameter to stardust (smallest unit) * * @param amount - Amount in any supported format * @returns Amount in stardust (u64 for Solana program) * @throws Error if amount is invalid, negative, or non-integer stardust * * @example * ```typescript * convertAmount(10_000_000_000) // 10_000_000_000n stardust (100 ATLAS) * convertAmount({ atlas: 10 }) // 1_000_000_000n stardust (10 ATLAS) * convertAmount({ stardust: 1_000_000 }) // 1_000_000n stardust (0.01 ATLAS) * ``` */ export function convertAmount(amount) { if (typeof amount === 'number' || typeof amount === 'bigint') { return toStardustBigInt(amount, 'stardust'); } if (typeof amount !== 'object' || amount === null) { throw new Error(`Invalid amount type. Expected number, bigint, or object. Got: ${typeof amount}`); } if ('atlas' in amount && amount.atlas !== undefined) { if (amount.atlas < 0) { throw new Error(`ATLAS amount must be non-negative, got ${amount.atlas}`); } return toStardustBigInt(amount.atlas * STARDUST_PER_ATLAS, 'atlas'); } if ('stardust' in amount && amount.stardust !== undefined) { return toStardustBigInt(amount.stardust, 'stardust'); } throw new Error(`Invalid amount format. Expected number, bigint, { atlas: number }, or { stardust: number | bigint }. ` + `Got: ${JSON.stringify(amount)}`); } function toStardustBigInt(raw, unit) { if (typeof raw === 'bigint') { if (raw < 0n) throw new Error(`Amount must be non-negative, got ${raw}`); if (raw > MAX_U64) throw new Error(`Amount too large. Max u64: ${MAX_U64}, got: ${raw}`); return raw; } if (!Number.isFinite(raw)) { throw new Error(`Amount must be a finite number, got ${raw}`); } if (raw < 0) { throw new Error(`Amount must be non-negative, got ${raw}`); } // The `{ atlas: ... }` path arrives here already multiplied by 1e8 — round // to absorb floating-point drift like 1.5 * 1e8 → 150000000.00000003. const value = unit === 'atlas' ? Math.round(raw) : raw; if (!Number.isInteger(value)) { throw new Error(`Stardust must be a whole number, got ${raw}. ` + `For sub-stardust precision use \`{ atlas: ${raw / STARDUST_PER_ATLAS} }\` instead.`); } const big = BigInt(value); if (big > MAX_U64) throw new Error(`Amount too large. Max u64: ${MAX_U64}, got: ${big}`); return big; } /** * Convert stardust to ATLAS for display * * @param stardust - Amount in stardust * @returns Amount in ATLAS (decimal) * * @example * ```typescript * stardustToAtlas(100_000_000n) // 1.0 ATLAS * stardustToAtlas(150_000_000n) // 1.5 ATLAS * ``` */ export function stardustToAtlas(stardust) { const stardustNum = typeof stardust === 'bigint' ? Number(stardust) : stardust; return stardustNum / STARDUST_PER_ATLAS; } /** * Format amount for display * * @param stardust - Amount in stardust * @param decimals - Number of decimal places (default: 8 for full precision) * @returns Formatted ATLAS amount string * * @example * ```typescript * formatAmount(100_000_000n) // "1.00000000 ATLAS" * formatAmount(150_000_000n, 2) // "1.50 ATLAS" * ``` */ export function formatAmount(stardust, decimals = 8) { const atlas = stardustToAtlas(stardust); return `${atlas.toFixed(decimals)} ATLAS`; } /** * Validate amount is within reasonable bounds * * @param stardust - Amount in stardust * @param min - Minimum allowed amount in stardust (default: 0) * @param max - Maximum allowed amount in stardust (default: MAX_U64) * @throws Error if amount is out of bounds */ export function validateAmount(stardust, min = 0n, max = 18446744073709551615n) { if (stardust < min) { throw new Error(`Amount too small: ${formatAmount(stardust)}. ` + `Minimum: ${formatAmount(min)}`); } if (stardust > max) { throw new Error(`Amount too large: ${formatAmount(stardust)}. ` + `Maximum: ${formatAmount(max)}`); } } //# sourceMappingURL=amount.js.map