UNPKG

@wuwei-labs/srsly

Version:
143 lines 6.54 kB
/** * @purpose Simplified createContract instruction wrapper * * Thin convenience wrapper around Codama-generated createContract instruction. * Handles parameter conversion and optional web3.js compatibility. */ import { address } from '@solana/kit'; import { getCreateContractInstructionAsync, getCreateContractThreadInstructionAsync, } from '../generated/codama/instructions'; import { mergeConfig, getAddresses } from '../utils/config'; import { toTransactionSigner, getSignerAddress } from '../utils/signer'; import { convertAmount, stardustToAtlas } from '../params/amount'; import { convertDuration } from '../params/duration'; import { normalizeSchedule } from '../params/schedule'; import { convertSol } from '../params/sol'; import { fetchFleet } from '../accounts/fleet'; import { fetchConfig } from '../accounts/config'; import { prepareInstructions } from '../utils/instructions'; import { deriveContract, deriveRentalAuthority } from '../pda/srsly'; import { deriveContractThread, deriveFiber } from '../pda/thread'; import { deriveAssociatedTokenAccount } from '../pda/token'; /** * List a Star Atlas fleet for rent. Sets the per-period rate, allowed * duration window, fee, and (optionally) automation parameters. * * @param params - Contract creation parameters * @param config - Optional SDK configuration overrides * @returns Kit instruction (or web3.js if PublicKey in config) * * @example * ```typescript * // With global config - auto-fetch ownerProfile and gameId from fleet * setSdkConfig({ network: 'atlasnet' }); * const ix = await createContract({ * owner: wallet, * fleet: fleetAddress, * rate: 100, * durationMax: { days: 30 } * }); * ``` */ export async function createContract(params, config) { // Merge config const finalConfig = mergeConfig(config); // Validate required params if (!params.owner) { throw new Error('owner is required'); } if (!params.fleet) { throw new Error('fleet is required'); } if (!params.rate) { throw new Error('rate is required'); } if (!params.durationMax) { throw new Error('durationMax is required'); } // Convert owner to transaction signer const ownerSigner = toTransactionSigner(params.owner); // Fetch fleet data (source of truth for ownerProfile and gameId) const fleetState = await fetchFleet(params.fleet, finalConfig.rpcUrl); const ownerProfile = fleetState.ownerProfile; const gameId = fleetState.gameId; // Convert parameters const rateStardust = convertAmount(params.rate); // Validate minimum rate (1 ATLAS = 100_000_000 stardust) const MIN_RATE_STARDUST = 100000000n; if (rateStardust < MIN_RATE_STARDUST) { throw new Error(`Rate too low. Minimum rate is 1 ATLAS (100,000,000 stardust). ` + `Got: ${rateStardust} stardust (${stardustToAtlas(rateStardust)} ATLAS)`); } const durationMinSeconds = params.durationMin ? convertDuration(params.durationMin) : 3600; // Default 1 hour const durationMaxSeconds = convertDuration(params.durationMax); // Validate duration logic if (durationMaxSeconds < durationMinSeconds) { throw new Error(`durationMax (${durationMaxSeconds}s) must be >= durationMin (${durationMinSeconds}s)`); } // Get atlasMint from on-chain config const configAccount = await fetchConfig(finalConfig.rpcUrl); const atlasMint = configAccount.data.atlasMint; const addresses = getAddresses(config); const sageProgram = address(addresses.sage); const programAddress = address(addresses.srsly); // Convert cancelDelayMin duration to seconds. Default mirrors // `durationMin` so a borrower can never cancel out of a rental // earlier than the contract's own minimum lease window — the owner // is guaranteed at least one full minimum-duration of revenue. const cancelDelayMinSeconds = params.cancelDelayMin !== undefined ? BigInt(convertDuration(params.cancelDelayMin)) : BigInt(durationMinSeconds); // Call Codama-generated instruction const kitInstruction = await getCreateContractInstructionAsync({ owner: ownerSigner, fleet: address(params.fleet), ownerProfile: address(ownerProfile), gameId: address(gameId), mint: atlasMint, sageProgram, rate: rateStardust, durationMin: BigInt(durationMinSeconds), durationMax: BigInt(durationMaxSeconds), ownerKeyIndex: params.ownerKeyIndex ?? 0, cancelDelayMin: cancelDelayMinSeconds, reservationsDisabled: params.reservationsDisabled ?? true, contestedThreshold: params.contestedThreshold ?? 1, }, { programAddress }); // Default: bundle thread creation for automation if (params.automated !== false) { const paymentsFreq = normalizeSchedule(params.paymentsFreq ?? '@daily'); // Derive PDAs locally (contract doesn't exist on-chain yet) const contractAddress = await deriveContract(params.fleet); const contractTokenAccount = await deriveAssociatedTokenAccount(contractAddress, atlasMint); const rentalAuthority = await deriveRentalAuthority(); const contractThread = await deriveContractThread(contractAddress, rentalAuthority); const fiber0 = await deriveFiber(contractThread, 0); const closeRentalFiber = await deriveFiber(contractThread, 1); const ownerAddress = getSignerAddress(params.owner); const MINIMUM_THREAD_FUNDING = 40000000n; const DEFAULT_EXECUTION_BUDGET = 2190000n; // 1 year daily @ 0.000006 SOL/tick const threadIx = await getCreateContractThreadInstructionAsync({ payer: ownerSigner, mint: atlasMint, contract: contractAddress, contractThread, fiber0, closeRentalFiber, owner: ownerAddress, contractTokenAccount, sageProgram, lamports: MINIMUM_THREAD_FUNDING + (params.threadLamports ? convertSol(params.threadLamports) : DEFAULT_EXECUTION_BUDGET), paymentsFreq, }, { programAddress }); return prepareInstructions([kitInstruction, threadIx], { computeUnits: params.computeUnits, PublicKey: finalConfig.PublicKey, }); } return prepareInstructions(kitInstruction, { computeUnits: params.computeUnits, PublicKey: finalConfig.PublicKey, }); } //# sourceMappingURL=createContract.js.map