@wuwei-labs/srsly
Version:
TypeScript SDK for SRSLY
120 lines • 4.47 kB
JavaScript
/**
* @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