@wuwei-labs/srsly
Version:
TypeScript SDK for SRSLY
145 lines • 7.93 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
vitest_1.vi.mock('../accounts/contract', () => ({
fetchContract: vitest_1.vi.fn(),
}));
vitest_1.vi.mock('../accounts/config', () => ({
fetchConfig: vitest_1.vi.fn(),
}));
vitest_1.vi.mock('../utils/rpc', () => ({
createRpc: vitest_1.vi.fn(() => ({
getMinimumBalanceForRentExemption: (size) => ({
// Solana rent formula constants: ACCOUNT_STORAGE_OVERHEAD=128,
// lamports_per_byte_year=3_480, exemption_threshold=2 years.
send: async () => {
const bytes = Number(size) + 128;
return BigInt(bytes * 3_480 * 2);
},
}),
})),
}));
vitest_1.vi.mock('../utils/config', () => ({
getRpcUrl: () => 'http://localhost:8899',
}));
const contract_1 = require("../accounts/contract");
const config_1 = require("../accounts/config");
const estimate_1 = require("./estimate");
const constants_1 = require("./constants");
const sizes_1 = require("./sizes");
const constants_2 = require("./constants");
// Rent formula used by our mock — matches Solana mainnet rent-exemption math.
const rentFor = (size) => BigInt((size + 128) * 3_480 * 2);
(0, vitest_1.describe)('estimateOwnerSetupCost', () => {
(0, vitest_1.it)('baseline matches COSTS.md breakdown', async () => {
const quote = await (0, estimate_1.estimateOwnerSetupCost)();
const contractStateRent = rentFor(sizes_1.CONTRACT_STATE_SIZE);
const rentalStateRent = rentFor(sizes_1.RENTAL_STATE_SIZE);
const ataRent = rentFor(constants_2.SPL_ATA_SIZE);
(0, vitest_1.expect)(quote.breakdown.contractState).toBe(contractStateRent);
(0, vitest_1.expect)(quote.breakdown.rentalStateActive).toBe(rentalStateRent);
(0, vitest_1.expect)(quote.breakdown.rentalStateQueued).toBe(rentalStateRent);
(0, vitest_1.expect)(quote.breakdown.contractAta).toBe(ataRent);
(0, vitest_1.expect)(quote.breakdown.ownerAta).toBe(ataRent);
(0, vitest_1.expect)(quote.breakdown.threadInfraBase).toBe(constants_1.MINIMUM_THREAD_FUNDING);
(0, vitest_1.expect)(quote.breakdown.executionBudget).toBe(constants_1.DEFAULT_EXECUTION_BUDGET);
(0, vitest_1.expect)(quote.rentLamports).toBe(contractStateRent + rentalStateRent * 2n + ataRent + ataRent);
(0, vitest_1.expect)(quote.threadLamports).toBe(constants_1.MINIMUM_THREAD_FUNDING + constants_1.DEFAULT_EXECUTION_BUDGET);
(0, vitest_1.expect)(quote.txFees).toBe(constants_1.TX_BASE_FEE_LAMPORTS * 2n);
(0, vitest_1.expect)(quote.total).toBe(quote.rentLamports + quote.threadLamports + quote.txFees);
});
(0, vitest_1.it)('ownerAtaExists skips owner ATA rent', async () => {
const with_ = await (0, estimate_1.estimateOwnerSetupCost)();
const without = await (0, estimate_1.estimateOwnerSetupCost)({ ownerAtaExists: true });
const ataRent = rentFor(constants_2.SPL_ATA_SIZE);
(0, vitest_1.expect)(with_.total - without.total).toBe(ataRent);
(0, vitest_1.expect)(without.breakdown.ownerAta).toBe(0n);
});
(0, vitest_1.it)('custom executionBudget flows through to thread funding', async () => {
const quote = await (0, estimate_1.estimateOwnerSetupCost)({ executionBudget: { sol: 0.01 } });
(0, vitest_1.expect)(quote.breakdown.executionBudget).toBe(10000000n);
(0, vitest_1.expect)(quote.threadLamports).toBe(constants_1.MINIMUM_THREAD_FUNDING + 10000000n);
});
});
(0, vitest_1.describe)('estimateBorrowerSetupCost', () => {
(0, vitest_1.it)('sums BorrowerState + managed ATA rent + tx fee', async () => {
const quote = await (0, estimate_1.estimateBorrowerSetupCost)();
const borrowerStateRent = rentFor(sizes_1.BORROWER_STATE_SIZE);
const ataRent = rentFor(constants_2.SPL_ATA_SIZE);
(0, vitest_1.expect)(quote.breakdown.borrowerState).toBe(borrowerStateRent);
(0, vitest_1.expect)(quote.breakdown.managedAta).toBe(ataRent);
(0, vitest_1.expect)(quote.rentLamports).toBe(borrowerStateRent + ataRent);
(0, vitest_1.expect)(quote.txFees).toBe(constants_1.TX_BASE_FEE_LAMPORTS);
(0, vitest_1.expect)(quote.total).toBe(borrowerStateRent + ataRent + constants_1.TX_BASE_FEE_LAMPORTS);
});
});
(0, vitest_1.describe)('estimateRentalCost', () => {
const mockContract = (rate) => ({
data: { rate },
address: 'Contract11111111111111111111111111111111111',
});
const mockConfig = (overrides = {}) => ({
data: {
stardustToAtlas: 100000000n,
serviceFeeBps: 1000n,
baseBps: 10_000,
...overrides,
},
address: 'Config1111111111111111111111111111111111111',
});
(0, vitest_1.it)('1 ATLAS/day × 86400s yields 100M Stardust escrow, 10M service fee', async () => {
vitest_1.vi.mocked(contract_1.fetchContract).mockResolvedValue(mockContract(1n));
vitest_1.vi.mocked(config_1.fetchConfig).mockResolvedValue(mockConfig());
const quote = await (0, estimate_1.estimateRentalCost)({
contract: 'AnyContract',
durationSeconds: 86400n,
});
(0, vitest_1.expect)(quote.atlasEscrow).toBe(100000000n);
(0, vitest_1.expect)(quote.atlasServiceFee).toBe(10000000n);
(0, vitest_1.expect)(quote.atlasBid).toBe(0n);
(0, vitest_1.expect)(quote.atlasTotal).toBe(100000000n);
(0, vitest_1.expect)(quote.serviceFeeBps).toBe(1_000);
(0, vitest_1.expect)(quote.solTxFees).toBe(constants_1.TX_BASE_FEE_LAMPORTS);
});
(0, vitest_1.it)('rate in Stardust units (rate >= stardustToAtlas) is not rescaled', async () => {
// 5 ATLAS/day already in Stardust = 500M.
vitest_1.vi.mocked(contract_1.fetchContract).mockResolvedValue(mockContract(500000000n));
vitest_1.vi.mocked(config_1.fetchConfig).mockResolvedValue(mockConfig());
const quote = await (0, estimate_1.estimateRentalCost)({
contract: 'AnyContract',
durationSeconds: 86400n * 3n, // 3 days
});
(0, vitest_1.expect)(quote.atlasEscrow).toBe(1500000000n);
});
(0, vitest_1.it)('pro-rates sub-day durations via integer division', async () => {
vitest_1.vi.mocked(contract_1.fetchContract).mockResolvedValue(mockContract(1n));
vitest_1.vi.mocked(config_1.fetchConfig).mockResolvedValue(mockConfig());
// Half a day -> 50M Stardust.
const quote = await (0, estimate_1.estimateRentalCost)({
contract: 'AnyContract',
durationSeconds: 43200n,
});
(0, vitest_1.expect)(quote.atlasEscrow).toBe(50000000n);
(0, vitest_1.expect)(quote.atlasServiceFee).toBe(5000000n);
});
(0, vitest_1.it)('bidStardust flows into atlasTotal but not into atlasEscrow', async () => {
vitest_1.vi.mocked(contract_1.fetchContract).mockResolvedValue(mockContract(1n));
vitest_1.vi.mocked(config_1.fetchConfig).mockResolvedValue(mockConfig());
const quote = await (0, estimate_1.estimateRentalCost)({
contract: 'AnyContract',
durationSeconds: 86400n,
bidStardust: 25000000n,
});
(0, vitest_1.expect)(quote.atlasEscrow).toBe(100000000n);
(0, vitest_1.expect)(quote.atlasBid).toBe(25000000n);
(0, vitest_1.expect)(quote.atlasTotal).toBe(125000000n);
});
});
(0, vitest_1.describe)('tickBudget', () => {
(0, vitest_1.it)('scales by PER_TICK_LAMPORTS', () => {
(0, vitest_1.expect)((0, estimate_1.tickBudget)(365)).toBe(365n * constants_1.PER_TICK_LAMPORTS);
(0, vitest_1.expect)((0, estimate_1.tickBudget)(0)).toBe(0n);
(0, vitest_1.expect)((0, estimate_1.tickBudget)(1000000n)).toBe(1000000n * constants_1.PER_TICK_LAMPORTS);
});
});
//# sourceMappingURL=estimate.test.js.map