var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { createAuctioneerBuyInstruction, createAuctioneerPublicBuyInstruction, createBuyInstruction, createPrintBidReceiptInstruction, createPublicBuyInstruction, } from '@metaplex-foundation/mpl-auction-house';
import { SYSVAR_INSTRUCTIONS_PUBKEY } from '@solarti/web3.js';
import { AuctioneerAuthorityRequiredError } from '../errors';
import { TransactionBuilder } from '../../../utils';
import { amount, isSigner, lamports, makeConfirmOptionsFinalizedOnMainnet, now, token, toPublicKey, useOperation, } from '../../../types';
// -----------------
// Operation
// -----------------
const Key = 'CreateBidOperation';
/**
 * Creates a bid on a given asset.
 *
 * You can post a public bid on a non-listed NFT by skipping seller and tokenAccount properties.
 * Public bids are specific to the token itself and not to any specific auction.
 * This means that a bid can stay active beyond the end of an auction
 * and be resolved if it meets the criteria for subsequent auctions of that token.
 *
 *
 * ```ts
 * await metaplex
 *   .auctionHouse()
 *   .createBid({ auctionHouse, mintAccount, seller };
 * ```
 *
 * @group Operations
 * @category Constructors
 */
export const createBidOperation = useOperation(Key);
/**
 * @group Operations
 * @category Handlers
 */
export const createBidOperationHandler = {
    handle(operation, metaplex, scope) {
        return __awaiter(this, void 0, void 0, function* () {
            const { auctionHouse } = operation.input;
            const builder = yield createBidBuilder(metaplex, operation.input, scope);
            const confirmOptions = makeConfirmOptionsFinalizedOnMainnet(metaplex, scope.confirmOptions);
            const output = yield builder.sendAndConfirm(metaplex, confirmOptions);
            scope.throwIfCanceled();
            if (output.receipt) {
                const bid = yield metaplex
                    .auctionHouse()
                    .findBidByReceipt({ auctionHouse, receiptAddress: output.receipt }, scope);
                return Object.assign({ bid }, output);
            }
            scope.throwIfCanceled();
            const lazyBid = {
                model: 'bid',
                lazy: true,
                auctionHouse,
                tradeStateAddress: output.buyerTradeState,
                bookkeeperAddress: output.bookkeeper,
                tokenAddress: output.tokenAccount,
                buyerAddress: output.buyer,
                metadataAddress: output.metadata,
                receiptAddress: output.receipt,
                purchaseReceiptAddress: null,
                isPublic: Boolean(output.tokenAccount),
                price: output.price,
                tokens: output.tokens.basisPoints,
                createdAt: now(),
                canceledAt: null,
            };
            return Object.assign({ bid: yield metaplex.auctionHouse().loadBid({ lazyBid }, scope) }, output);
        });
    },
};
/**
 * Creates a bid on a given asset.
 *
 * You can post a public bid on a non-listed NFT by skipping seller and tokenAccount properties.
 * Public bids are specific to the token itself and not to any specific auction.
 * This means that a bid can stay active beyond the end of an auction
 * and be resolved if it meets the criteria for subsequent auctions of that token.
 *
 *
 * ```ts
 * const transactionBuilder = metaplex
 *   .auctionHouse()
 *   .builders()
 *   .createBid({ auctionHouse, mintAccount, seller })
 * ```
 *
 * @group Transaction Builders
 * @category Constructors
 */
export const createBidBuilder = (metaplex, params, options = {}) => __awaiter(void 0, void 0, void 0, function* () {
    var _a, _b, _c, _d, _e, _f, _g, _h;
    // Data.
    const { programs, payer = metaplex.rpc().getDefaultFeePayer() } = options;
    const { auctionHouse } = params;
    const tokens = (_a = params.tokens) !== null && _a !== void 0 ? _a : token(1);
    const priceBasisPoint = (_c = (_b = params.price) === null || _b === void 0 ? void 0 : _b.basisPoints) !== null && _c !== void 0 ? _c : 0;
    const price = auctionHouse.isNative
        ? lamports(priceBasisPoint)
        : amount(priceBasisPoint, auctionHouse.treasuryMint.currency);
    if (auctionHouse.hasAuctioneer && !params.auctioneerAuthority) {
        throw new AuctioneerAuthorityRequiredError();
    }
    // Accounts.
    const buyer = (_d = params.buyer) !== null && _d !== void 0 ? _d : metaplex.identity();
    const authority = (_e = params.authority) !== null && _e !== void 0 ? _e : auctionHouse.authorityAddress;
    const metadata = metaplex.nfts().pdas().metadata({
        mint: params.mintAccount,
        programs,
    });
    const paymentAccount = auctionHouse.isNative
        ? toPublicKey(buyer)
        : metaplex
            .tokens()
            .pdas()
            .associatedTokenAccount({
            mint: auctionHouse.treasuryMint.address,
            owner: toPublicKey(buyer),
            programs,
        });
    const escrowPayment = metaplex
        .auctionHouse()
        .pdas()
        .buyerEscrow({
        auctionHouse: auctionHouse.address,
        buyer: toPublicKey(buyer),
        programs,
    });
    const tokenAccount = (_f = params.tokenAccount) !== null && _f !== void 0 ? _f : (params.seller
        ? metaplex.tokens().pdas().associatedTokenAccount({
            mint: params.mintAccount,
            owner: params.seller,
            programs,
        })
        : null);
    const buyerTokenAccount = metaplex
        .tokens()
        .pdas()
        .associatedTokenAccount({
        mint: params.mintAccount,
        owner: toPublicKey(buyer),
        programs,
    });
    const buyerTradeState = metaplex
        .auctionHouse()
        .pdas()
        .tradeState({
        auctionHouse: auctionHouse.address,
        wallet: toPublicKey(buyer),
        treasuryMint: auctionHouse.treasuryMint.address,
        tokenMint: params.mintAccount,
        price: price.basisPoints,
        tokenSize: tokens.basisPoints,
        tokenAccount,
        programs,
    });
    const accounts = {
        wallet: toPublicKey(buyer),
        paymentAccount,
        transferAuthority: toPublicKey(buyer),
        treasuryMint: auctionHouse.treasuryMint.address,
        metadata,
        escrowPaymentAccount: escrowPayment,
        authority: toPublicKey(authority),
        auctionHouse: auctionHouse.address,
        auctionHouseFeeAccount: auctionHouse.feeAccountAddress,
        buyerTradeState,
    };
    // Args.
    const args = {
        tradeStateBump: buyerTradeState.bump,
        escrowPaymentBump: escrowPayment.bump,
        buyerPrice: price.basisPoints,
        tokenSize: tokens.basisPoints,
    };
    // Sell Instruction.
    let buyInstruction = tokenAccount
        ? createBuyInstruction(Object.assign(Object.assign({}, accounts), { tokenAccount }), args)
        : createPublicBuyInstruction(Object.assign(Object.assign({}, accounts), { tokenAccount: buyerTokenAccount }), args);
    if (params.auctioneerAuthority) {
        const ahAuctioneerPda = metaplex.auctionHouse().pdas().auctioneer({
            auctionHouse: auctionHouse.address,
            auctioneerAuthority: params.auctioneerAuthority.publicKey,
            programs,
        });
        const accountsWithAuctioneer = Object.assign(Object.assign({}, accounts), { auctioneerAuthority: params.auctioneerAuthority.publicKey, ahAuctioneerPda });
        buyInstruction = tokenAccount
            ? createAuctioneerBuyInstruction(Object.assign(Object.assign({}, accountsWithAuctioneer), { tokenAccount }), args)
            : createAuctioneerPublicBuyInstruction(Object.assign(Object.assign({}, accountsWithAuctioneer), { tokenAccount: buyerTokenAccount }), args);
    }
    // Signers.
    const buySigners = [buyer, authority, params.auctioneerAuthority].filter(isSigner);
    // Update the accounts to be signers since it's not covered properly by MPL due to its dynamic nature.
    buySigners.forEach((signer) => {
        const signerKeyIndex = buyInstruction.keys.findIndex(({ pubkey }) => pubkey.equals(signer.publicKey));
        buyInstruction.keys[signerKeyIndex].isSigner = true;
    });
    // Receipt.
    // Since createPrintBidReceiptInstruction can't deserialize createAuctioneerBuyInstruction due to a bug
    // Don't print Auctioneer Bid receipt for the time being.
    const shouldPrintReceipt = ((_g = params.printReceipt) !== null && _g !== void 0 ? _g : true) && !params.auctioneerAuthority;
    const bookkeeper = (_h = params.bookkeeper) !== null && _h !== void 0 ? _h : metaplex.identity();
    const receipt = metaplex.auctionHouse().pdas().bidReceipt({
        tradeState: buyerTradeState,
        programs,
    });
    const builder = TransactionBuilder.make()
        .setFeePayer(payer)
        .setContext({
        buyerTradeState,
        tokenAccount,
        metadata,
        buyer: toPublicKey(buyer),
        receipt: shouldPrintReceipt ? receipt : null,
        bookkeeper: shouldPrintReceipt ? bookkeeper.publicKey : null,
        price,
        tokens,
    });
    // Create a TA for public bid if it doesn't exist
    if (!tokenAccount) {
        const account = yield metaplex.rpc().getAccount(buyerTokenAccount);
        if (!account.exists) {
            builder.add(yield metaplex
                .tokens()
                .builders()
                .createToken({
                mint: params.mintAccount,
                owner: toPublicKey(buyer),
            }));
        }
    }
    return (builder
        // Create bid.
        .add({
        instruction: buyInstruction,
        signers: buySigners,
        key: 'buy',
    })
        // Print the Bid Receipt.
        .when(shouldPrintReceipt, (builder) => builder.add({
        instruction: createPrintBidReceiptInstruction({
            receipt,
            bookkeeper: bookkeeper.publicKey,
            instruction: SYSVAR_INSTRUCTIONS_PUBKEY,
        }, { receiptBump: receipt.bump }),
        signers: [bookkeeper],
        key: 'printBidReceipt',
    })));
});
//# sourceMappingURL=createBid.js.map