UNPKG

@wuwei-labs/srsly

Version:
117 lines 5.02 kB
/** * @purpose SOL + ATLAS cost estimators for SRSLY protocol usage. * * Three quotes: * - Owner setup (contract_create + contract_thread) — recoverable rents + infra + exec budget. * - Borrower setup (borrower_create) — BorrowerState + managed ATA. * - Per-rental (rental_accept) — SOL tx fee + ATLAS escrow + service fee. * * All functions use `rpc.getMinimumBalanceForRentExemption` so quotes reflect the * target cluster's rent rate rather than hardcoded lamports. */ import { fetchContract } from '../accounts/contract'; import { fetchConfig } from '../accounts/config'; import { getRpcUrl } from '../utils/config'; import { createRpc } from '../utils/rpc'; import { convertSol } from '../params/sol'; import { CONTRACT_STATE_SIZE, RENTAL_STATE_SIZE, BORROWER_STATE_SIZE } from './sizes'; import { MINIMUM_THREAD_FUNDING, DEFAULT_EXECUTION_BUDGET, TX_BASE_FEE_LAMPORTS, PER_TICK_LAMPORTS, SECONDS_PER_DAY, SPL_ATA_SIZE, } from './constants'; async function minBalance(rpcUrl, size) { const rpc = createRpc(rpcUrl ?? getRpcUrl()); const response = await rpc.getMinimumBalanceForRentExemption(BigInt(size)).send(); return BigInt(response); } export async function estimateOwnerSetupCost(opts = {}) { const executionBudget = opts.executionBudget === undefined ? DEFAULT_EXECUTION_BUDGET : convertSol(opts.executionBudget); const [contractState, rentalState, ataRent] = await Promise.all([ minBalance(opts.rpcUrl, CONTRACT_STATE_SIZE), minBalance(opts.rpcUrl, RENTAL_STATE_SIZE), minBalance(opts.rpcUrl, SPL_ATA_SIZE), ]); const ownerAta = opts.ownerAtaExists ? 0n : ataRent; const rentLamports = contractState + rentalState + rentalState + ataRent + ownerAta; const threadLamports = MINIMUM_THREAD_FUNDING + executionBudget; const txFees = TX_BASE_FEE_LAMPORTS * 2n; // contract_create + contract_thread return { rentLamports, threadLamports, txFees, total: rentLamports + threadLamports + txFees, breakdown: { contractState, rentalStateActive: rentalState, rentalStateQueued: rentalState, contractAta: ataRent, ownerAta, threadInfraBase: MINIMUM_THREAD_FUNDING, executionBudget, }, }; } export async function estimateBorrowerSetupCost(rpcUrl) { const [borrowerState, managedAta] = await Promise.all([ minBalance(rpcUrl, BORROWER_STATE_SIZE), minBalance(rpcUrl, SPL_ATA_SIZE), ]); const rentLamports = borrowerState + managedAta; const txFees = TX_BASE_FEE_LAMPORTS; return { rentLamports, txFees, total: rentLamports + txFees, breakdown: { borrowerState, managedAta }, }; } /** * Quote the ATLAS cost a borrower will pay for a given rate + duration. * Pure helper — does not build a transaction. */ export async function estimateRentalCost(params) { const [{ data: contract }, { data: config }] = await Promise.all([ fetchContract(params.contract, params.rpcUrl), fetchConfig(params.rpcUrl), ]); const duration = BigInt(params.durationSeconds); const stardustToAtlas = BigInt(config.stardustToAtlas); const rate = BigInt(contract.rate); // Mirror on-chain scaling: rate <1 ATLAS is treated as ATLAS-denominated. const rateStardust = rate < stardustToAtlas ? saturatingMul(rate, stardustToAtlas) : rate; const atlasEscrow = saturatingDiv(saturatingMul(rateStardust, duration), SECONDS_PER_DAY); const feeBps = BigInt(config.serviceFeeBps); const baseBps = BigInt(config.baseBps); const atlasServiceFee = baseBps === 0n ? 0n : saturatingDiv(saturatingMul(atlasEscrow, feeBps), baseBps); const atlasBid = params.bidStardust ?? 0n; return { solTxFees: TX_BASE_FEE_LAMPORTS, atlasEscrow, atlasServiceFee, atlasBid, atlasTotal: atlasEscrow + atlasBid, serviceFeeBps: Number(feeBps), }; } /* -------------------------------------------------------------------------- */ /* Thread burn */ /* -------------------------------------------------------------------------- */ /** * Lamports burned by a given number of cron ticks at `PER_TICK_LAMPORTS` each. * Useful for sizing a custom `executionBudget` before calling * `estimateOwnerSetupCost`. */ export function tickBudget(ticks) { return BigInt(ticks) * PER_TICK_LAMPORTS; } /* -------------------------------------------------------------------------- */ /* u64 helpers */ /* -------------------------------------------------------------------------- */ const MAX_U64 = 18446744073709551615n; function saturatingMul(a, b) { const product = a * b; return product > MAX_U64 ? MAX_U64 : product; } function saturatingDiv(a, b) { return b === 0n ? 0n : a / b; } //# sourceMappingURL=estimate.js.map