@wuwei-labs/srsly
Version:
TypeScript SDK for SRSLY
133 lines • 6.18 kB
JavaScript
/**
* @purpose Simplified closeRental instruction wrapper
*
* Thin convenience wrapper around Codama-generated closeRental instruction.
* Closes a completed rental and returns the fleet to the owner.
*/
import { address } from '@solana/kit';
import { isOption, unwrapOption } from '@solana/kit';
import { getCloseRentalInstructionAsync } from '../generated/codama/instructions';
import { mergeConfig, getAddresses } from '../utils/config';
import { toTransactionSigner } from '../utils/signer';
import { fetchConfig } from '../accounts/config';
import { fetchContract } from '../accounts/contract';
import { fetchFleet } from '../accounts/fleet';
import { fetchRental } from '../accounts/rental';
import { deriveGameAccounts } from '../pda/sage';
import { deriveActiveRental, deriveBorrowerState, deriveQueuedRental, deriveRentalAuthority, } from '../pda/srsly';
import { deriveContractThread, deriveFiber } from '../pda/thread';
import { deriveProfileFaction } from '../pda/sage';
import { deriveAssociatedTokenAccount } from '../pda/token';
import { prepareInstructions } from '../utils/instructions';
/**
* Finalize a completed rental and return the fleet to the owner.
*
* @param params - Rental closure parameters (payer, contract)
* @param config - Optional SDK configuration overrides
* @returns Kit instruction (or web3.js if PublicKey in config)
*
* @example
* ```typescript
* const ix = await closeRental({
* payer: ownerWallet,
* contract: contractAddress
* });
* ```
*/
export async function closeRental(params, config) {
// Merge config
const finalConfig = mergeConfig(config);
// Validate required params
if (!params.payer) {
throw new Error('payer is required');
}
if (!params.contract) {
throw new Error('contract is required');
}
// Fetch config and contract
const configAccount = await fetchConfig(finalConfig.rpcUrl);
const atlasMint = configAccount.data.atlasMint;
const contractAccount = await fetchContract(params.contract, finalConfig.rpcUrl);
const fleet = contractAccount.data.fleet;
const gameId = contractAccount.data.gameId;
// Check if contract has a queued rental waiting to activate
const SYSTEM_PROGRAM_ID = '11111111111111111111111111111111';
const hasQueuedRental = contractAccount.data.queuedRental !== SYSTEM_PROGRAM_ID;
let queuedRentalAddress;
let queuedBorrowerProfileAddress;
let queuedBorrowerProfileFactionAddress;
if (hasQueuedRental) {
queuedRentalAddress = await deriveQueuedRental(params.contract);
const queuedRentalState = await fetchRental(queuedRentalAddress, finalConfig.rpcUrl);
queuedBorrowerProfileAddress = queuedRentalState.data.borrowerProfile;
queuedBorrowerProfileFactionAddress = await deriveProfileFaction(queuedBorrowerProfileAddress);
}
// Thread accounts needed for inline activation restage
const hasThread = contractAccount.data.thread !== SYSTEM_PROGRAM_ID;
let contractThreadAddress;
let closeRentalFiberAddress;
if (hasQueuedRental && hasThread) {
const rentalAuthority = await deriveRentalAuthority();
contractThreadAddress = await deriveContractThread(params.contract, rentalAuthority);
closeRentalFiberAddress = await deriveFiber(contractThreadAddress, 1);
}
// Convert payer to transaction signer
const payerSigner = toTransactionSigner(params.payer);
// Derive active rental PDA (no more slot logic)
const rentalState = await deriveActiveRental(params.contract);
// Fetch rental state for borrower, referrer
let borrower;
let referrer;
const rental = await fetchRental(rentalState, finalConfig.rpcUrl);
borrower = params.borrower ? address(params.borrower) : rental.data.borrower;
referrer = isOption(rental.data.referrer)
? (unwrapOption(rental.data.referrer) ?? undefined)
: undefined;
// Derive borrower state PDA
const borrowerState = await deriveBorrowerState(borrower);
// Fetch fleet to get faction and derive SAGE accounts
// During an active rental, fleet.subProfile IS the current borrower's profile
const fleetState = await fetchFleet(fleet, finalConfig.rpcUrl);
const borrowerProfile = fleetState.subProfile.key;
const faction = fleetState.faction;
// Derive SAGE game accounts using borrower's profile
const gameAccounts = await deriveGameAccounts(borrowerProfile, faction, gameId);
// Get addresses
const addresses = getAddresses(config);
const sageProgram = address(addresses.sage);
const programAddress = address(addresses.srsly);
const contractAddress = address(params.contract);
// Derive fee flush accounts
const vaultAddress = address(configAccount.data.slyvault);
const vaultTokenAccount = await deriveAssociatedTokenAccount(vaultAddress, atlasMint);
const contractTokenAccount = await deriveAssociatedTokenAccount(contractAddress, atlasMint);
// Borrower's managed ATA for discount refund
const borrowerTokenAccount = await deriveAssociatedTokenAccount(borrowerState, atlasMint);
// Call Codama-generated instruction
const kitInstruction = await getCloseRentalInstructionAsync({
payer: payerSigner,
borrowerState,
contract: contractAddress,
rentalState,
fleet: address(fleet),
gameId: address(gameId),
starbase: gameAccounts.starbase,
starbasePlayer: gameAccounts.starbasePlayer,
contractTokenAccount,
vault: vaultAddress,
vaultTokenAccount,
referrer,
borrowerTokenAccount,
queuedRental: queuedRentalAddress ?? programAddress,
queuedBorrowerProfile: queuedBorrowerProfileAddress ?? programAddress,
queuedBorrowerProfileFaction: queuedBorrowerProfileFactionAddress ?? programAddress,
contractThread: contractThreadAddress,
closeRentalFiber: closeRentalFiberAddress,
sageProgram,
}, { programAddress });
return prepareInstructions(kitInstruction, {
computeUnits: params.computeUnits,
PublicKey: finalConfig.PublicKey,
});
}
//# sourceMappingURL=closeRental.js.map