import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID, } from '@solana/spl-token'; import { KeyedAccountInfo, PublicKey, SystemProgram } from '@solana/web3.js'; import { Account, AccountStatic, AsyncSigner, decodeAccount, DecodedAccountData, InstructionReturn, staticImplements, transferToTokenAccount, } from '@staratlas/data-source'; import { isEqual } from 'lodash'; import { CraftableItemAccount, CraftingIDL, CraftingIDLProgram, DrainCraftableItemBankIxInput, RegisterCraftableItemIxInput, } from './constants'; /** * Checks equality between two Craftable Item Accounts * @param data1 - First Craftable Item Account * @param data2 - Second Craftable Item Account * @returns boolean */ export function craftableItemDataEquals( data1: CraftableItemAccount, data2: CraftableItemAccount, ): boolean { return ( data1.version === data2.version && data1.bump === data2.bump && isEqual(data1.namespace, data2.namespace) && data1.domain.equals(data2.domain) && data1.mint.equals(data2.mint) ); } @staticImplements>() export class CraftableItem implements Account { static readonly ACCOUNT_NAME: NonNullable< CraftingIDL['accounts'] >[number]['name'] = 'craftableItem'; static readonly MIN_DATA_SIZE: number = 8 + // discriminator 1 + // version 32 + // domain 32 + // mint 32 + // creator 32 + // name 1; // bump constructor(private _data: CraftableItemAccount, private _key: PublicKey) {} get data(): Readonly { return this._data; } get key(): PublicKey { return this._key; } /** * Finds PDA for Craftable Item Account * @param program - Crafting IDL program * @param domain - Domain Account key * @param mint - Mint key * @returns PDA key & bump */ static findAddress( program: CraftingIDLProgram, domain: PublicKey, mint: PublicKey, ): [PublicKey, number] { return PublicKey.findProgramAddressSync( [Buffer.from('CraftableItem'), domain.toBuffer(), mint.toBuffer()], program.programId, ); } /** * Register a new Craftable Item Account * @param program - Crafting IDL program * @param key - Key authorized for this instruction * @param profile - Profile Account key with the required Crafting Permissions * @param domain - Domain Account key * @param mint - Mint key * @param input - Input struct for registerCraftableItem instruction * @returns InstructionReturn */ static registerCraftableItem( program: CraftingIDLProgram, key: AsyncSigner, profile: PublicKey, domain: PublicKey, mint: PublicKey, input: RegisterCraftableItemIxInput, ): InstructionReturn { return async (funder) => [ { instruction: await program.methods .registerCraftableItem(input) .accountsStrict({ key: key.publicKey(), profile, funder: funder.publicKey(), craftableItem: this.findAddress(program, domain, mint)[0], domain, mint, systemProgram: SystemProgram.programId, }) .instruction(), signers: [key, funder], }, ]; } /** * Funds Token Account owned by Craftable Item Account * @param craftableItem - Craftable Item Account key * @param mint - Mint key, * @param tokenFromOwner - Owner of Token Account being credited * @param tokenFrom - Token Account being credited * @param amount - Amount of token being debited to Craftable Item Token Account * @returns InstructionReturn */ static fundCraftableItemBank( craftableItem: PublicKey, mint: PublicKey, tokenFromOwner: AsyncSigner, tokenFrom: PublicKey, amount: number, ): InstructionReturn { const tokenTo = getAssociatedTokenAddressSync(mint, craftableItem, true); return transferToTokenAccount(tokenFromOwner, tokenFrom, tokenTo, amount); } /** * Drains the CraftableItem token bank and closes it * @param program - Crafting IDL program * @param craftableItem - Craftable Item Account key * @param key - Key authorized to use this instruction * @param profile - Profile Account with required Crafting Permissions * @param fundsTo - Account to which the rent fees are debited * @param domain - Domain Account key * @param tokenFrom - The craftable item token bank to drain * @param tokenTo - Where to send tokens from the bank * @param input - Input struct for deregisterCraftableItem instruction * @returns InstructionReturn */ static drainCraftableItemBank( program: CraftingIDLProgram, craftableItem: PublicKey, key: AsyncSigner, profile: PublicKey, fundsTo: PublicKey | 'funder', domain: PublicKey, tokenFrom: PublicKey, tokenTo: PublicKey, input: DrainCraftableItemBankIxInput, ): InstructionReturn { return async (funder) => [ { instruction: await program.methods .drainCraftableItemBank(input) .accountsStrict({ key: key.publicKey(), profile, fundsTo: fundsTo === 'funder' ? funder.publicKey() : fundsTo, craftableItem, domain, tokenFrom, tokenTo, tokenProgram: TOKEN_PROGRAM_ID, }) .instruction(), signers: [key], }, ]; } static decodeData( account: KeyedAccountInfo, program: CraftingIDLProgram, ): DecodedAccountData { return decodeAccount(account, program, CraftableItem); } }