@wuwei-labs/srsly
Version:
TypeScript SDK for SRSLY
95 lines • 4.64 kB
JavaScript
/**
* @purpose Simplified createContractThread instruction wrapper
*
* Thin convenience wrapper around Codama-generated createContractThread instruction.
* Creates a per-contract automation thread with fiber-based dispatch.
*/
import { AccountRole, address } from '@solana/kit';
import { getCreateContractThreadInstructionAsync } from '../generated/codama/instructions';
import { mergeConfig, getAddresses } from '../utils/config';
import { toTransactionSigner, getSignerAddress } from '../utils/signer';
import { prepareInstructions } from '../utils/instructions';
import { fetchConfig } from '../accounts/config';
import { fetchContract } from '../accounts/contract';
import { fetchFleet } from '../accounts/fleet';
import { fetchRental } from '../accounts/rental';
import { RentalStatus } from '../generated/codama/types';
import { deriveGameAccounts } from '../pda/sage';
import { deriveActiveRental } from '../pda/srsly';
import { deriveContractThread, deriveFiber } from '../pda/thread';
import { deriveRentalAuthority } from '../pda/srsly';
import { normalizeSchedule } from '../params/schedule';
import { convertSol } from '../params/sol';
import { MINIMUM_THREAD_FUNDING, DEFAULT_EXECUTION_BUDGET } from '../cost/constants';
/**
* Set up the per-contract automation thread that processes payments and
* lifecycle ticks. Thread starts paused; the rental_close and
* rental_activate fibers spin up lazily on first use.
*
* @param params - Thread creation parameters
* @param config - Optional SDK configuration overrides
* @returns Kit instruction (or web3.js if PublicKey in config)
*/
export async function createContractThread(params, config) {
const finalConfig = mergeConfig(config);
if (!params.owner) {
throw new Error('owner is required');
}
if (!params.contract) {
throw new Error('contract is required');
}
const ownerSigner = toTransactionSigner(params.owner);
const ownerAddress = getSignerAddress(params.owner);
// Get atlasMint and sage program from config
const configAccount = await fetchConfig(finalConfig.rpcUrl);
const atlasMint = configAccount.data.atlasMint;
const addresses = getAddresses(config);
const programAddress = address(addresses.srsly);
// Fetch contract to get managed_token_account
const contractAccount = await fetchContract(params.contract, finalConfig.rpcUrl);
const contractAddress = address(params.contract);
// Derive thread and fiber PDAs
const rentalAuthority = await deriveRentalAuthority();
const contractThread = await deriveContractThread(contractAddress, rentalAuthority);
const fiber0 = await deriveFiber(contractThread, 0);
const closeRentalFiber = await deriveFiber(contractThread, 1);
// Normalize schedule to cron string
const paymentsFreq = normalizeSchedule(params.paymentsFreq);
const kitInstruction = await getCreateContractThreadInstructionAsync({
payer: ownerSigner,
mint: atlasMint,
contract: contractAddress,
contractThread,
fiber0,
closeRentalFiber,
owner: ownerAddress,
contractTokenAccount: contractAccount.data.managedTokenAccount,
sageProgram: address(addresses.sage),
lamports: MINIMUM_THREAD_FUNDING +
(params.lamports ? convertSol(params.lamports) : DEFAULT_EXECUTION_BUDGET),
paymentsFreq,
}, { programAddress });
// If active rental exists, append starbase + starbasePlayer as remaining_accounts
// (Rust reads them from remaining_accounts when building fiber 1 close instruction)
try {
const activeRentalPda = await deriveActiveRental(contractAddress);
const activeRental = await fetchRental(activeRentalPda, finalConfig.rpcUrl);
if (activeRental.data.status === RentalStatus.Active) {
const fleetState = await fetchFleet(contractAccount.data.fleet, finalConfig.rpcUrl);
const gameAccounts = await deriveGameAccounts(fleetState.subProfile.key, fleetState.faction, contractAccount.data.gameId);
const sageRemainingAccounts = [
{ address: gameAccounts.starbase, role: AccountRole.READONLY },
{ address: gameAccounts.starbasePlayer, role: AccountRole.READONLY },
];
kitInstruction.accounts.push(...sageRemainingAccounts);
}
}
catch {
// No active rental — remaining_accounts not needed
}
return prepareInstructions(kitInstruction, {
computeUnits: params.computeUnits,
PublicKey: finalConfig.PublicKey,
});
}
//# sourceMappingURL=createContractThread.js.map