UNPKG

@wuwei-labs/srsly

Version:
174 lines 8.24 kB
"use strict"; /** * @purpose Simplified reserveRental instruction wrapper * * Thin convenience wrapper around Codama-generated reserveRental instruction. * Reserves a rental contract using ATLAS for a GBM-style auction. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.reserveRental = reserveRental; const kit_1 = require("@solana/kit"); const instructions_1 = require("../generated/codama/instructions"); const config_1 = require("../utils/config"); const signer_1 = require("../utils/signer"); const amount_1 = require("../params/amount"); const duration_1 = require("../params/duration"); const contract_1 = require("../accounts/contract"); const config_2 = require("../accounts/config"); const fleet_1 = require("../accounts/fleet"); const borrower_1 = require("../accounts/borrower"); const rental_1 = require("../accounts/rental"); const types_1 = require("../generated/codama/types"); const sage_1 = require("../pda/sage"); const srsly_1 = require("../pda/srsly"); const token_1 = require("../pda/token"); const thread_1 = require("../pda/thread"); const instructions_2 = require("../utils/instructions"); const bidMath_1 = require("../utils/bidMath"); /** * Bid on a contract via the GBM-style reservation auction. The winner * gets an exclusive window to accept once the current rental ends. * * @param params - Reservation parameters * @param config - Optional SDK configuration overrides * @returns Array of instructions (Kit format or web3.js if PublicKey in config) * * @example * ```typescript * // Reserve with 5 ATLAS for a 7-day rental * const ixs = await reserveRental({ * challenger: wallet, * contract: "CONTRACT_ADDRESS", * amount: 5, * duration: { days: 7 }, * computeUnits: 200_000 * }); * ``` */ async function reserveRental(params, config) { // Merge config const finalConfig = (0, config_1.mergeConfig)(config); // Validate required params if (!params.challenger) { throw new Error('challenger is required'); } if (!params.contract) { throw new Error('contract is required'); } if (!params.challengerProfile) { throw new Error('challengerProfile is required for reserveRental'); } if (!params.duration) { throw new Error('duration is required'); } const bidPoints = params.bidPoints ?? 0; let bidAtlasValue = params.bidAtlas !== undefined ? (0, amount_1.convertAmount)(params.bidAtlas) : 0n; const userProvidedBid = params.bidPoints !== undefined || params.bidAtlas !== undefined; if (bidPoints > 0 && bidAtlasValue > 0n) { throw new Error('bidPoints and bidAtlas are mutually exclusive'); } // Auto-fill the ATLAS bid when the caller omitted both bid params AND // there is an existing defender to outbid. First reservations allow // zero-bid, so we leave those untouched. const autoMinBid = params.autoMinBid ?? true; if (autoMinBid && !userProvidedBid) { const floor = await (0, bidMath_1.getMinimumBid)({ contractAddress: params.contract, rpcUrl: config?.rpcUrl, }); if (floor.defenderBidStardust > 0n) { bidAtlasValue = floor.minBidStardust; } } // Convert challenger to transaction signer const challengerSigner = (0, signer_1.toTransactionSigner)(params.challenger); // Get network-specific addresses const addresses = (0, config_1.getAddresses)(config); const programAddress = (0, kit_1.address)(addresses.srsly); const sageProgram = (0, kit_1.address)(addresses.sage); // Fetch config for mint const configAccount = await (0, config_2.fetchConfig)(finalConfig.rpcUrl); const atlasMint = configAccount.data.atlasMint; // Fetch contract to get contractTokenAccount const contractAccount = await (0, contract_1.fetchContract)(params.contract, finalConfig.rpcUrl); // Derive rental PDAs const activeRental = await (0, srsly_1.deriveActiveRental)(params.contract); const rentalState = await (0, srsly_1.deriveQueuedRental)(params.contract); // Derive rental authority const rentalAuthority = await (0, srsly_1.deriveRentalAuthority)(); // Derive contract thread and close_rental fiber (fiber index 1) const contractThread = await (0, thread_1.deriveContractThread)(params.contract, rentalAuthority); const closeRentalFiber = await (0, thread_1.deriveFiber)(contractThread, 1); // Derive contract token account (ATA of contract PDA) const contractAddress = (0, kit_1.address)(params.contract); const contractTokenAccount = await (0, token_1.deriveAssociatedTokenAccount)(contractAddress, atlasMint); // Derive challenger's token account const challengerTokenAccount = await (0, token_1.deriveAssociatedTokenAccount)(challengerSigner.address, atlasMint); // Convert duration to seconds const durationSeconds = (0, duration_1.convertDuration)(params.duration); const referrer = params.referrer ? (0, kit_1.address)(params.referrer) : undefined; // Build instruction input const instructionInput = { challenger: challengerSigner, contract: contractAddress, contractThread, closeRentalFiber, contractTokenAccount, activeRental, challengerTokenAccount, rentalState, referrer, sageProgram, bidPoints, bidAtlas: bidAtlasValue, duration: BigInt(durationSeconds), }; // If there's a current queued reservation, provide defender accounts // Fetch queued rental to check if there's an existing reservation holder try { const queuedRental = await (0, rental_1.fetchRental)(rentalState, finalConfig.rpcUrl); if (queuedRental.data.status === types_1.RentalStatus.Queued) { const defenderBorrowerState = queuedRental.data.borrowerState; const challengerBorrowerState = await (0, srsly_1.deriveBorrowerState)(challengerSigner); if (defenderBorrowerState === challengerBorrowerState) { // Self-contest: challenger is the current defender. // Program reuses challenger_state, but we still need defender_managed_token_account // for the ATLAS refund path. const challengerAccount = await (0, borrower_1.fetchBorrower)(challengerBorrowerState, finalConfig.rpcUrl); instructionInput.defenderManagedTokenAccount = challengerAccount.data.managedTokenAccount; } else { instructionInput.defenderState = defenderBorrowerState; const defenderAccount = await (0, borrower_1.fetchBorrower)(defenderBorrowerState, finalConfig.rpcUrl); instructionInput.defenderManagedTokenAccount = defenderAccount.data.managedTokenAccount; } } } catch { // Queued rental doesn't exist or is Available — no defender } // Derive SAGE accounts for remaining_accounts const challengerProfileAddr = (0, kit_1.address)(params.challengerProfile); const fleetState = await (0, fleet_1.fetchFleet)(contractAccount.data.fleet, finalConfig.rpcUrl); const gameAccounts = await (0, sage_1.deriveGameAccounts)(challengerProfileAddr, fleetState.faction, contractAccount.data.gameId); // Call Codama-generated instruction const kitInstruction = await (0, instructions_1.getReserveRentalInstructionAsync)(instructionInput, { programAddress, }); // Append SAGE accounts as remaining_accounts const sageRemainingAccounts = [ { address: challengerProfileAddr, role: kit_1.AccountRole.READONLY }, { address: gameAccounts.profileFaction, role: kit_1.AccountRole.READONLY }, { address: gameAccounts.starbase, role: kit_1.AccountRole.READONLY }, { address: gameAccounts.starbasePlayer, role: kit_1.AccountRole.READONLY }, ]; kitInstruction.accounts.push(...sageRemainingAccounts); // Prepare instructions with optional compute budget return (0, instructions_2.prepareInstructions)(kitInstruction, { computeUnits: params.computeUnits, PublicKey: finalConfig.PublicKey, }); } //# sourceMappingURL=reserveRental.js.map