UNPKG

@deserialize/auction-sdk

Version:

An SDK for Auction on SVMs

215 lines (179 loc) 10.2 kB
import { PublicKey, SystemProgram, Transaction, TransactionSignature } from "@solana/web3.js"; import * as anchor from "@coral-xyz/anchor"; import ProgramLoader from "./ProgramLoader"; import { ApplicationState as ApplicationStateType } from "../types/application-state"; import Listing from "./Listing"; import BidReceipt from "./BidReciept"; import { ListingState } from "../types/listing"; import { ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddress, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { checkEnumState } from "../helper/check-enum-status.helper"; /** * ApplicationState * * @description A class for managing the application state * - get the application state by pubkey * - update application state * - execute sale ? */ export default class ApplicationState { private listingProgram: Listing; private bidReceiptProgram: BidReceipt; public constructor(protected readonly programLoader: ProgramLoader) { this.listingProgram = new Listing(programLoader); this.bidReceiptProgram = new BidReceipt(programLoader); } public async create(feeAccount: string): Promise<ApplicationStateType> { try { const seeds1 = [anchor.utils.bytes.utf8.encode("deserialize"), Uint8Array.from(this.programLoader.wallet.publicKey.toBuffer())] const [applicationState, _] = PublicKey.findProgramAddressSync(seeds1, this.programLoader.program.programId); console.log("[*] NEW APPLICATION STATE:- " + applicationState); let txSignature = await this.programLoader.program.methods .initialize() .accounts({ applicationState, user: this.programLoader.wallet.publicKey, feeAccount, systemProgram: SystemProgram.programId }) .signers([this.programLoader.wallet]) .rpc(); console.log("[*] Transaction signature:- ", txSignature); let appState = await this.programLoader.program.account.applicationState.fetch(applicationState); return appState as unknown as ApplicationStateType; } catch (e: any) { console.error(e); throw new Error(`Failed to create application state: ${e.message}`); } } /** * @description Get all application states * @returns {ApplicationStateType[]} The application states */ public async getAll(): Promise<ApplicationStateType[]> { try { const applicationStates = await this.programLoader.program.account.applicationState.all(); console.log("[*] Application states: ", applicationStates); return applicationStates as unknown as ApplicationStateType[]; } catch (e: any) { console.error(e); throw new Error(`Failed to get all application states: ${e.message}`); } } /** * @description Get the application state * * @param {PublicKey} appKey - The application state pubkey * @returns {ApplicationStateType} The application state */ public async get(appKey: PublicKey): Promise<ApplicationStateType> { try { const applicationState = await this.programLoader.program.account.applicationState.fetch(appKey); return applicationState as any; } catch (e: any) { console.error(e); throw new Error(`Failed to get application state: ${e.message}`); } } /** * @description Update the application state * * @param {PublicKey} appKey - The application state pubkey * @param {PublicKey} feeAccount - The fee account pubkey * @returns {ApplicationStateType} The application state */ public async update(appKey: PublicKey, feeAccount: PublicKey): Promise<ApplicationStateType> { try { await this.programLoader.program.methods .updateApplicationState(feeAccount) .accounts({ applicationState: appKey, user: this.programLoader.wallet.publicKey, }) .signers([this.programLoader.wallet]) .rpc(); let state = await this.get(appKey); return state; } catch (e: any) { console.error(e); throw new Error(`Failed to update application state: ${e.message}`); } } /** * @description Execute a sale * @param listingPubkey * @param executor * @returns */ public async executeSale(listingPubkey: PublicKey, executor: PublicKey, tokenMintProgramId: PublicKey, nftMintProgramId: PublicKey): Promise<Transaction> { try { const listingData = await this.listingProgram.get(listingPubkey); console.log("[*] Listing data: ", JSON.stringify(listingData, null, 4)); // const seeds1 = [anchor.utils.bytes.utf8.encode("deserialize"), Uint8Array.from(this.programLoader.wallet.publicKey.toBuffer())] // const [applicationState, applicationStateBump] = PublicKey.findProgramAddressSync(seeds1, this.programLoader.program.programId); let appState = await this.programLoader.program.account.applicationState.fetch(this.programLoader.applicationState); console.log("[*] Application state: ", JSON.stringify(appState, null, 4)); // if(!executor.equals(listingData.authority) || !executor.equals(appState.listingAuthority)) throw new Error('The user has no access to execute sale'); const seeds2 = [anchor.utils.bytes.utf8.encode("auction_manager"), Uint8Array.from(listingData.nft.toBuffer()), Uint8Array.from(listingData.authority.toBuffer())]; const [auctionManager, auctionManagerBump] = PublicKey.findProgramAddressSync(seeds2, this.programLoader.program.programId); var auctionManagerState = await this.programLoader.program.account.auctionManager.fetch(auctionManager); console.log("[*] Auction manager state: ", JSON.stringify(auctionManagerState, null, 4)); const seeds3 = [anchor.utils.bytes.utf8.encode("listing"), Uint8Array.from(listingData.nft.toBuffer()), Uint8Array.from(listingData.authority.toBuffer()), Uint8Array.from(auctionManager.toBuffer()), Uint8Array.from(listingData.tokenMint.toBuffer())]; const [listing, listingBump] = PublicKey.findProgramAddressSync(seeds3, this.programLoader.program.programId); var listingState = await this.programLoader.program.account.listing.fetch(listing); console.log("[*] Listing: ", JSON.stringify(listingState, null, 4)); if (!listingPubkey.equals(listing)) throw new Error("Invalid Listing"); console.log("[*] Listing state: ", listingData.state); if (!checkEnumState(listingData.state as any, ListingState.Active)) throw new Error("Listing is not active"); let highestBidInfo = await this.listingProgram.getHighestBidInfo(listingPubkey); console.log("[*] Highest bid info: ", JSON.stringify(highestBidInfo, null, 4)); let bidsInfoToRefund = await this.listingProgram.getBidsInfoToRefund(listingPubkey); console.log("[*] Bids info to refund: ", JSON.stringify(bidsInfoToRefund, null, 4)); let txn: Transaction = new Transaction(); //create refund bid transactions for await (let bid of bidsInfoToRefund) { let inx = await this.bidReceiptProgram.refund(bid.receipt, bid.bidder, tokenMintProgramId); txn.add(inx); } const nftEscrow = await getAssociatedTokenAddress(listingData.nft, auctionManager, true); // const nftOwner = await getAssociatedTokenAddress(listingData.nft, listingData.authority, true); const winnerNftAccount = await getAssociatedTokenAddress(listingData.nft, highestBidInfo.bidder, true, nftMintProgramId); const tokenEscrow = await getAssociatedTokenAddress(listingData.tokenMint, auctionManager, true, tokenMintProgramId); const feeTokenAccount = await getAssociatedTokenAddress(listingData.tokenMint, appState.feeAccount, true, tokenMintProgramId); const seeds = [anchor.utils.bytes.utf8.encode("user_account"), Uint8Array.from(highestBidInfo.bidder.toBuffer()), Uint8Array.from(appState.listingAuthority.toBuffer()), anchor.utils.bytes.utf8.encode("deserialize")]; const [userdata, _] = PublicKey.findProgramAddressSync(seeds, this.programLoader.program.programId); console.log("[*] WINNER NFT account: ", winnerNftAccount); console.log("[*] AUCTION MANAGER Token escrow: ", tokenEscrow); //create transaction to execute sale const submittedAt = Math.floor(Date.now() / 1000); const inx = await this.programLoader.program.methods .executeSale(new anchor.BN(submittedAt)) .accounts({ applicationState: this.programLoader.applicationState, userdata, bidReceipt: highestBidInfo.receipt, user: executor, listing, nft: listingData.nft, auctionManager, nftEscrow, winnerNftAccount, proceedsWallet: listingData.auctionProceedsWallet, tokenEscrow, tokenMint: listingData.tokenMint, tokenProgram: tokenMintProgramId, nftTokenProgram: nftMintProgramId, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, feeTokenAccount }) .instruction(); txn.add(inx); txn.recentBlockhash = (await this.programLoader.getRecentBlockHash()).blockhash; txn.feePayer = executor; return txn; } catch (e: any) { console.error(e); throw new Error(`Failed to execute sale: ${e.message}`); } } }