diff --git a/packages/js/src/plugins/auctionHouseModule/AuctionHouseBuildersClient.ts b/packages/js/src/plugins/auctionHouseModule/AuctionHouseBuildersClient.ts index 357d384d7..cff641e2c 100644 --- a/packages/js/src/plugins/auctionHouseModule/AuctionHouseBuildersClient.ts +++ b/packages/js/src/plugins/auctionHouseModule/AuctionHouseBuildersClient.ts @@ -11,6 +11,8 @@ import { DirectSellBuilderParams, withdrawFromBuyerAccountBuilder, WithdrawFromBuyerAccountBuilderParams, + withdrawFromFeeAccountBuilder, + WithdrawFromFeeAccountBuilderParams, } from './operations'; import { createAuctionHouseBuilder, @@ -120,4 +122,12 @@ export class AuctionHouseBuildersClient { ) { return withdrawFromBuyerAccountBuilder(this.metaplex, input, options); } + + /** {@inheritDoc withdrawFromFeeAccountBuilder} */ + withdrawFromFeeAccount( + input: WithdrawFromFeeAccountBuilderParams, + options?: TransactionBuilderOptions + ) { + return withdrawFromFeeAccountBuilder(this.metaplex, input, options); + } } diff --git a/packages/js/src/plugins/auctionHouseModule/AuctionHouseClient.ts b/packages/js/src/plugins/auctionHouseModule/AuctionHouseClient.ts index 34f36696b..05c32266c 100644 --- a/packages/js/src/plugins/auctionHouseModule/AuctionHouseClient.ts +++ b/packages/js/src/plugins/auctionHouseModule/AuctionHouseClient.ts @@ -22,6 +22,8 @@ import { findAuctionHouseByAddressOperation, FindAuctionHouseByCreatorAndMintInput, findAuctionHouseByCreatorAndMintOperation, + FindAuctionHousesByAddressListInput, + findAuctionHousesByAddressListOperation, FindBidByReceiptInput, findBidByReceiptOperation, FindBidByTradeStateInput, @@ -188,6 +190,16 @@ export class AuctionHouseClient { .execute(findAuctionHouseByCreatorAndMintOperation(input), options); } + /** {@inheritDoc findAuctionHousesByAddressListOperation} */ + findByAddressList( + input: FindAuctionHousesByAddressListInput, + options?: OperationOptions + ) { + return this.metaplex + .operations() + .execute(findAuctionHousesByAddressListOperation(input), options); + } + /** {@inheritDoc findBidByReceiptOperation} */ findBidByReceipt(input: FindBidByReceiptInput, options?: OperationOptions) { return this.metaplex diff --git a/packages/js/src/plugins/auctionHouseModule/operations/findAuctionHouseByAddress.ts b/packages/js/src/plugins/auctionHouseModule/operations/findAuctionHouseByAddress.ts index ac51cec7a..6933a571d 100644 --- a/packages/js/src/plugins/auctionHouseModule/operations/findAuctionHouseByAddress.ts +++ b/packages/js/src/plugins/auctionHouseModule/operations/findAuctionHouseByAddress.ts @@ -1,6 +1,5 @@ import type { PublicKey } from '@solana/web3.js'; import { toAuctioneerAccount, toAuctionHouseAccount } from '../accounts'; -import { AuctioneerAuthorityRequiredError } from '../errors'; import { AuctionHouse, toAuctionHouse } from '../models/AuctionHouse'; import { Operation, @@ -51,7 +50,7 @@ export type FindAuctionHouseByAddressInput = { /** * The Auctioneer authority key. - * It is required when Auction House has Auctioneer enabled. + * It is automatically loaded when not specified and Auction House has Auctioneer enabled. * * @defaultValue No default value. */ @@ -101,7 +100,9 @@ export const findAuctionHouseByAddressOperationHandler: OperationHandler(Key); + +/** + * @group Operations + * @category Types + */ +export type FindAuctionHousesByAddressListOperation = Operation< + typeof Key, + FindAuctionHousesByAddressListInput, + FindAuctionHousesByAddressListOutput +>; + +/** + * @group Operations + * @category Inputs + */ +export type FindAuctionHousesByAddressListInput = { + /** The addresses of the Auction Houses. */ + addresses: PublicKey[]; +}; + +/** + * @group Operations + * @category Outputs + */ +export type FindAuctionHousesByAddressListOutput = AuctionHouse[]; + +const fetchUniqueAccountMap: ( + metaplex: Metaplex, + accounts: PublicKey[], + mapper: (account: UnparsedMaybeAccount) => T, + accountMap?: Map +) => Promise> = async ( + metaplex, + accounts, + mapper, // = (account) => account, + accountMap = new Map() +) => { + const uniqueAccounts: Set = new Set( + accounts.filter((key): key is PublicKey => key !== null) + ); + + const accountInfos = await Promise.all( + chunk(Array.from(uniqueAccounts), 100).map((chunk) => + metaplex.rpc().getMultipleAccounts(chunk) + ) + ).then((infos) => infos.flat()); + + for (const account of accountInfos) { + accountMap.set(account.publicKey, mapper(account)); + } + + return accountMap; +}; + +/** + * @group Operations + * @category Handlers + */ +export const findAuctionHousesByAddressListOperationHandler: OperationHandler = + { + handle: async ( + operation: FindAuctionHousesByAddressListOperation, + metaplex: Metaplex, + scope: OperationScope + ): Promise => { + const { commitment } = scope; + const { addresses } = operation.input; + + const auctionHouseUnparsedAccounts = await Promise.all( + chunk(addresses, 100).map((c) => + metaplex.rpc().getMultipleAccounts(c, commitment) + ) + ); + scope.throwIfCanceled(); + + const auctionHouseAccounts = auctionHouseUnparsedAccounts + .flat() + .map((account) => toAuctionHouseAccount(account)); + + const mintAddresses = auctionHouseAccounts.map( + (auctionHouseAccount) => auctionHouseAccount.data.treasuryMint + ); + const auctioneerAddresses = auctionHouseAccounts + .map(({ data: { hasAuctioneer, auctioneerAddress } }) => + hasAuctioneer ? auctioneerAddress : null + ) + .filter((key): key is PublicKey => key !== null); + + const mintAndAuctioneerAccounts = await fetchUniqueAccountMap( + metaplex, + mintAddresses.concat(auctioneerAddresses), + (a) => a + ); + scope.throwIfCanceled(); + + return auctionHouseAccounts.map((auctionHouseAccount) => { + const mintModel = toMint( + toMintAccount( + mintAndAuctioneerAccounts.get( + auctionHouseAccount.data.treasuryMint + )! + ) + ); + + if (!auctionHouseAccount.data.hasAuctioneer) + return toAuctionHouse(auctionHouseAccount, mintModel); + + return toAuctionHouse( + auctionHouseAccount, + mintModel, + toAuctioneerAccount( + mintAndAuctioneerAccounts.get( + auctionHouseAccount.data.auctioneerAddress + )! + ) + ); + }); + }, + }; diff --git a/packages/js/src/plugins/auctionHouseModule/operations/findListings.ts b/packages/js/src/plugins/auctionHouseModule/operations/findListings.ts index f215c2a56..d6855ab16 100644 --- a/packages/js/src/plugins/auctionHouseModule/operations/findListings.ts +++ b/packages/js/src/plugins/auctionHouseModule/operations/findListings.ts @@ -53,7 +53,7 @@ export type FindListingsOperation = Operation< */ export type FindListingsInput = { /** A model of the Auction House related to these listings. */ - auctionHouse: AuctionHouse; + auctionHouse?: AuctionHouse; /** The address of a seller to search by. */ seller?: PublicKey; @@ -92,9 +92,11 @@ export const findListingsOperationHandler: OperationHandler - toLazyListing(toListingReceiptAccount(account), auctionHouse) + const listingReceiptAccounts = await listingQuery.getAndMap((account) => + toListingReceiptAccount(account) + ); + + // return early if auctionHouse is specified + if (auctionHouse) { + return listingReceiptAccounts.map((listing) => + toLazyListing(listing, auctionHouse) + ); + } + + const uniqueAuctionHouseAddresses = Array.from( + new Set( + listingReceiptAccounts.map( + (auctionHouseAccount) => auctionHouseAccount.data.auctionHouse + ) + ) + ); + + const auctionHouses = await metaplex + .auctionHouse() + .findByAddressList({ addresses: uniqueAuctionHouseAddresses }); + + scope.throwIfCanceled(); + + const auctionHouseMap: Map = new Map( + auctionHouses.map((auctionHouse) => [ + auctionHouse.address, + auctionHouse, + ]) + ); + + return listingReceiptAccounts.map((listing) => + toLazyListing(listing, auctionHouseMap.get(listing.data.auctionHouse)!) ); }, }; diff --git a/packages/js/src/plugins/auctionHouseModule/operations/index.ts b/packages/js/src/plugins/auctionHouseModule/operations/index.ts index f5f607451..153a51b55 100644 --- a/packages/js/src/plugins/auctionHouseModule/operations/index.ts +++ b/packages/js/src/plugins/auctionHouseModule/operations/index.ts @@ -9,6 +9,7 @@ export * from './directSell'; export * from './executeSale'; export * from './findAuctionHouseByAddress'; export * from './findAuctionHouseByCreatorAndMint'; +export * from './findAuctionHousesByAddressList'; export * from './findBidByReceipt'; export * from './findBidByTradeState'; export * from './findBids'; diff --git a/packages/js/src/plugins/auctionHouseModule/plugin.ts b/packages/js/src/plugins/auctionHouseModule/plugin.ts index 9bc26fb0b..3f1109edf 100644 --- a/packages/js/src/plugins/auctionHouseModule/plugin.ts +++ b/packages/js/src/plugins/auctionHouseModule/plugin.ts @@ -24,6 +24,8 @@ import { findAuctionHouseByAddressOperationHandler, findAuctionHouseByCreatorAndMintOperation, findAuctionHouseByCreatorAndMintOperationHandler, + findAuctionHousesByAddressListOperation, + findAuctionHousesByAddressListOperationHandler, findBidByReceiptOperation, findBidByReceiptOperationHandler, findBidByTradeStateOperation, @@ -104,6 +106,10 @@ export const auctionHouseModule = (): MetaplexPlugin => ({ findAuctionHouseByCreatorAndMintOperation, findAuctionHouseByCreatorAndMintOperationHandler ); + op.register( + findAuctionHousesByAddressListOperation, + findAuctionHousesByAddressListOperationHandler + ); op.register(findBidByReceiptOperation, findBidByReceiptOperationHandler); op.register( findBidByTradeStateOperation, diff --git a/packages/js/test/plugins/auctionHouseModule/createAuctionHouse.test.ts b/packages/js/test/plugins/auctionHouseModule/createAuctionHouse.test.ts index fc43fc64b..ef037feb7 100644 --- a/packages/js/test/plugins/auctionHouseModule/createAuctionHouse.test.ts +++ b/packages/js/test/plugins/auctionHouseModule/createAuctionHouse.test.ts @@ -176,6 +176,19 @@ test('[auctionHouseModule] create new Auctioneer Auction House', async (t: Test) // And the Auctioneer PDA for that Auction House was created. const ahAuctioneerAccount = await mx.rpc().getAccount(ahAuctioneerPda); t.ok(ahAuctioneerAccount.exists); + + // Try fetching auctioneer auction house with only auctionHouse key + const retrievedAuctionHouse = await mx + .auctionHouse() + .findByAddress({ address: auctionHouse.address }); + spok(t, retrievedAuctionHouse, { + hasAuctioneer: true, + scopes: AUCTIONEER_ALL_SCOPES, + auctioneer: { + address: spokSamePubkey(ahAuctioneerPda), + authority: spokSamePubkey(auctioneerAuthority.publicKey), + }, + }); }); test('[auctionHouseModule] create new Auctioneer Auction House with separate authority', async (t: Test) => {