import { KeyedAccountInfo, PublicKey, SystemProgram } from '@solana/web3.js'; import { Account, AccountStatic, AsyncSigner, DecodedAccountData, InstructionReturn, decodeAccountWithRemaining, getAnchorEnum, staticImplements, } from '@staratlas/data-source'; import { KeyIndexInput } from './common'; import { CraftingFacilityAccount, CraftingIDL, CraftingIDLProgram, } from './constants'; export enum CraftingFacilityLocationType { Starbase, } export interface RegisterCraftingFacilityInput extends KeyIndexInput { locationType: CraftingFacilityLocationType; efficiency: number; maxConcurrentProcesses: number; } export interface UpdateCraftingFacilityInput extends KeyIndexInput { efficiency?: number; maxConcurrentProcesses?: number; } export interface RemoveCraftingFacilityRecipeCategoryInput extends KeyIndexInput { recipeCategoryIndex: number; } export interface WrappedRecipeCategory { id: PublicKey; } export const CRAFTING_FACILITY_MIN_DATA_SIZE = 8 + // discriminator 1 + // version 32 + // domain 32 + // location 1 + // locationType 4 + // maxConcurrentProcesses 4 + // numConcurrentProcesses 4 + // efficiency 4; // numRecipeCategories /** * Checks equality between 2 Crafting Facility Accounts * @param data1 - First Crafting Facility Account * @param data2 - Second Crafting Facility Account * @returns boolean */ export function craftingFacilityDataEquals( data1: CraftingFacilityAccount, data2: CraftingFacilityAccount, ): boolean { return ( data1.version === data2.version && data1.locationType === data2.locationType && data1.maxConcurrentProcesses === data2.maxConcurrentProcesses && data1.numConcurrentProcesses === data2.numConcurrentProcesses && data1.efficiency === data2.efficiency && data1.numRecipeCategories === data2.numRecipeCategories && data1.domain.equals(data2.domain) && data1.location.equals(data2.location) ); } @staticImplements>() export class CraftingFacility implements Account { static readonly ACCOUNT_NAME: NonNullable< CraftingIDL['accounts'] >[number]['name'] = 'craftingFacility'; static readonly MIN_DATA_SIZE: number = CRAFTING_FACILITY_MIN_DATA_SIZE; constructor( private _data: CraftingFacilityAccount, private _key: PublicKey, private _recipeCategories: PublicKey[], ) {} get data(): Readonly { return this._data; } get key(): PublicKey { return this._key; } get recipeCategories(): Readonly { return this._recipeCategories || []; } /** * Registers new Crafting Facility * @param program - Crafting IDL program * @param craftingFacility - The new Crafting Facility Account * @param key - Key authorized to use this instruction * @param profile - Profile Account with required Crafting Permissions * @param domain - Domain Account key * @param location - Location Address key * @param input - input params * @returns InstructionReturn */ static registerCraftingFacility( program: CraftingIDLProgram, craftingFacility: AsyncSigner, key: AsyncSigner, profile: PublicKey, domain: PublicKey, location: PublicKey, input: RegisterCraftingFacilityInput, ): InstructionReturn { return async (funder) => [ { instruction: await program.methods .registerCraftingFacility({ ...input, locationType: getAnchorEnum( CraftingFacilityLocationType, input.locationType, ), }) .accountsStrict({ key: key.publicKey(), profile, funder: funder.publicKey(), craftingFacility: craftingFacility.publicKey(), domain, location, systemProgram: SystemProgram.programId, }) .instruction(), signers: [key, funder, craftingFacility], }, ]; } /** * Updates existing Crafting Facility Account * @param program - Crafting IDL program * @param craftingFacility - Crafting Facility Account key * @param key - Key authorized to use this instruction * @param profile - Profile Account with the required Crafting Permissions * @param domain - Domain Account key * @param input - input params * @returns InstructionReturn */ static updateCraftingFacility( program: CraftingIDLProgram, craftingFacility: PublicKey, key: AsyncSigner, profile: PublicKey, domain: PublicKey, input: UpdateCraftingFacilityInput, ): InstructionReturn { const formattedInput = { ...input, efficiency: input.efficiency == null ? null : input.efficiency, maxConcurrentProcesses: input.maxConcurrentProcesses == null ? null : input.maxConcurrentProcesses, }; return async () => [ { instruction: await program.methods .updateCraftingFacility(formattedInput) .accountsStrict({ key: key.publicKey(), profile, craftingFacility, domain, }) .instruction(), signers: [key], }, ]; } /** * Updates a Crafting Facility's Recipe Category * @param program - Crafting IDL program * @param craftingFacility - Crafting Facility Account key * @param key - Key authorized to use this instruction * @param profile - Profile with required Crafting Permissions * @param domain - Domain Account key * @param recipeCategory - New Recipe Category Account key * @param input - Key index * @returns InstructionReturn */ static addCraftingFacilityRecipeCategory( program: CraftingIDLProgram, craftingFacility: PublicKey, key: AsyncSigner, profile: PublicKey, domain: PublicKey, recipeCategory: PublicKey, input: KeyIndexInput, ): InstructionReturn { return async (funder) => [ { instruction: await program.methods .addCraftingFacilityRecipeCategory(input) .accountsStrict({ key: key.publicKey(), profile, funder: funder.publicKey(), craftingFacility, recipeCategory, domain, systemProgram: SystemProgram.programId, }) .instruction(), signers: [key, funder], }, ]; } /** * Removes a Crafting Facility's Recipe Category * @param program - Crafting IDL program * @param craftingFacility - Crafting Facility key * @param key - Key authorized to use this instruction * @param profile - Profile with the required Crafting permissions * @param domain - Domain Account key * @param recipeCategory - Recipe Category to be removed * @param input - Input struct for removeCraftingFacilityRecipeCategory instruction * @returns InstructionReturn */ static removeCraftingFacilityRecipeCategory( program: CraftingIDLProgram, craftingFacility: PublicKey, key: AsyncSigner, profile: PublicKey, domain: PublicKey, recipeCategory: PublicKey, input: RemoveCraftingFacilityRecipeCategoryInput, ): InstructionReturn { return async (funder) => [ { instruction: await program.methods .removeCraftingFacilityRecipeCategory(input) .accountsStrict({ key: key.publicKey(), profile, funder: funder.publicKey(), craftingFacility, recipeCategory, domain, systemProgram: SystemProgram.programId, }) .instruction(), signers: [key, funder], }, ]; } /** * Deregisters and Closes a Crafting Facility * @param program - Crafting IDL program * @param craftingFacility - Crafting Facility to close * @param key - Key authorized to use this instruction * @param profile - Profile with required Crafting Permission * @param fundsTo - Account where rent fees are debited * @param domain - Domain Account * @param input - Key index * @returns InstructionReturn */ static deregisterCraftingFacility( program: CraftingIDLProgram, craftingFacility: PublicKey, key: AsyncSigner, profile: PublicKey, fundsTo: PublicKey | 'funder', domain: PublicKey, input: KeyIndexInput, ): InstructionReturn { return async (funder) => [ { instruction: await program.methods .deregisterCraftingFacility(input) .accountsStrict({ key: key.publicKey(), profile, fundsTo: fundsTo === 'funder' ? funder.publicKey() : fundsTo, craftingFacility, domain, }) .instruction(), signers: [key], }, ]; } static decodeData( account: KeyedAccountInfo, program: CraftingIDLProgram, ): DecodedAccountData { return decodeAccountWithRemaining( account, program, CraftingFacility, (remainingData, data) => Array(data.numRecipeCategories) .fill(0) .map( (_, index) => program.coder.types.decode( 'WrappedRecipeCategory', remainingData.subarray(32 * index).subarray(0, 32), ).id, ), ); } }