@renec-foundation/metaplex-js
Version:
Metaplex JavaScript API
201 lines (180 loc) • 6.96 kB
text/typescript
import BN from 'bn.js';
import { PublicKey, TransactionSignature } from '@solana/web3.js';
import { Wallet } from '../wallet';
import { Connection } from '../Connection';
import { sendTransaction } from './transactions';
import { TransactionsBatch } from '../utils/transactions-batch';
import { createApproveTxs, createWrappedAccountTxs, prepareTokenAccountAndMintTxs } from './shared';
import { getBidRedemptionPDA } from './redeemFullRightsTransferBid';
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
NATIVE_MINT,
Token,
TOKEN_PROGRAM_ID,
} from '@solana/spl-token';
import { Auction, AuctionExtended, BidderMetadata } from '@metaplex-foundation/mpl-auction';
import { Vault } from '@metaplex-foundation/mpl-token-vault';
import {
AuctionManager,
NonWinningConstraint,
ParticipationConfigV2,
PrizeTrackingTicket,
RedeemParticipationBidV3,
SafetyDepositConfig,
WinningConstraint,
} from '@metaplex-foundation/mpl-metaplex';
import {
Edition,
EditionMarker,
MasterEdition,
Metadata,
UpdatePrimarySaleHappenedViaToken,
} from '@renec-foundation/mpl-token-metadata';
export interface RedeemParticipationBidV3Params {
connection: Connection;
wallet: Wallet;
auction: PublicKey;
store: PublicKey;
}
export interface RedeemParticipationBidV3Response {
txIds: TransactionSignature[];
}
export const redeemParticipationBidV3 = async ({
connection,
wallet,
store,
auction,
}: RedeemParticipationBidV3Params): Promise<RedeemParticipationBidV3Response> => {
const txInitBatch = new TransactionsBatch({ transactions: [] });
const txMainBatch = new TransactionsBatch({ transactions: [] });
const bidder = wallet.publicKey;
const {
data: { bidState, tokenMint: auctionTokenMint },
} = await Auction.load(connection, auction);
const auctionManagerPDA = await AuctionManager.getPDA(auction);
const manager = await AuctionManager.load(connection, auctionManagerPDA);
const vault = await Vault.load(connection, manager.data.vault);
const auctionExtendedPDA = await AuctionExtended.getPDA(vault.pubkey);
const [safetyDepositBox] = await vault.getSafetyDepositBoxes(connection);
const originalMint = new PublicKey(safetyDepositBox.data.tokenMint);
const safetyDepositTokenStore = new PublicKey(safetyDepositBox.data.store);
const bidderMetaPDA = await BidderMetadata.getPDA(auction, bidder);
const bidRedemptionPDA = await getBidRedemptionPDA(auction, bidderMetaPDA);
const safetyDepositConfigPDA = await SafetyDepositConfig.getPDA(
auctionManagerPDA,
safetyDepositBox.pubkey,
);
const {
data: {
participationConfig: { fixedPrice },
},
} = await SafetyDepositConfig.load(connection, safetyDepositConfigPDA);
const acceptPaymentAccount = new PublicKey(manager.data.acceptPayment);
const { mint, createMintTx, createAssociatedTokenAccountTx, mintToTx, recipient } =
await prepareTokenAccountAndMintTxs(connection, wallet.publicKey);
txInitBatch.addSigner(mint);
txInitBatch.addTransaction(createMintTx);
txInitBatch.addTransaction(createAssociatedTokenAccountTx);
txInitBatch.addTransaction(mintToTx);
const newMint = mint.publicKey;
const newMetadataPDA = await Metadata.getPDA(newMint);
const newEditionPDA = await Edition.getPDA(newMint);
const metadataPDA = await Metadata.getPDA(originalMint);
const masterEditionPDA = await MasterEdition.getPDA(originalMint);
const masterEdition = await MasterEdition.load(connection, masterEditionPDA);
const prizeTrackingTicketPDA = await PrizeTrackingTicket.getPDA(auctionManagerPDA, originalMint);
const winIndex = bidState.getWinnerIndex(bidder.toBase58());
const desiredEdition = masterEdition.data.supply.add(new BN(1));
const editionMarkerPDA = await EditionMarker.getPDA(originalMint, desiredEdition);
let tokenPaymentAccount: PublicKey;
if (auctionTokenMint === NATIVE_MINT.toBase58()) {
const { account, createTokenAccountTx, closeTokenAccountTx } = await createWrappedAccountTxs(
connection,
bidder,
fixedPrice.toNumber(),
);
tokenPaymentAccount = account.publicKey;
txInitBatch.addTransaction(createTokenAccountTx);
txInitBatch.addSigner(account);
txMainBatch.addAfterTransaction(closeTokenAccountTx);
} else {
// TODO: find out what will happen if currency is not WSOL
tokenPaymentAccount = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
new PublicKey(auctionTokenMint),
bidder,
);
}
const { authority, createApproveTx, createRevokeTx } = createApproveTxs({
account: tokenPaymentAccount,
owner: bidder,
amount: fixedPrice.toNumber(),
});
txMainBatch.addTransaction(createApproveTx);
txMainBatch.addAfterTransaction(createRevokeTx);
txMainBatch.addSigner(authority);
const redeemParticipationBidV3Tx = new RedeemParticipationBidV3(
{ feePayer: bidder },
{
store,
vault: vault.pubkey,
auction,
auctionManager: auctionManagerPDA,
bidRedemption: bidRedemptionPDA,
bidMetadata: bidderMetaPDA,
safetyDepositTokenStore,
destination: recipient,
safetyDeposit: safetyDepositBox.pubkey,
bidder,
safetyDepositConfig: safetyDepositConfigPDA,
auctionExtended: auctionExtendedPDA,
newMint,
newEdition: newEditionPDA,
newMetadata: newMetadataPDA,
metadata: metadataPDA,
masterEdition: masterEditionPDA,
editionMark: editionMarkerPDA,
prizeTrackingTicket: prizeTrackingTicketPDA,
winIndex: winIndex !== null ? new BN(winIndex) : null,
transferAuthority: authority.publicKey,
tokenPaymentAccount,
acceptPaymentAccount,
},
);
txMainBatch.addTransaction(redeemParticipationBidV3Tx);
const updatePrimarySaleHappenedViaTokenTx = new UpdatePrimarySaleHappenedViaToken(
{ feePayer: bidder },
{
metadata: newMetadataPDA,
owner: bidder,
tokenAccount: recipient,
},
);
txMainBatch.addTransaction(updatePrimarySaleHappenedViaTokenTx);
const initTxId = await sendTransaction({
connection,
wallet,
txs: txInitBatch.toTransactions(),
signers: txInitBatch.signers,
});
// wait for all accounts to be created
await connection.confirmTransaction(initTxId, 'finalized');
const mainTxId = await sendTransaction({
connection,
wallet,
txs: txMainBatch.toTransactions(),
signers: txMainBatch.signers,
});
return { txIds: [initTxId, mainTxId] };
};
export function isEligibleForParticipationPrize(
winIndex: number,
{ nonWinningConstraint, winnerConstraint }: ParticipationConfigV2 = {} as ParticipationConfigV2,
) {
const noWinnerConstraints = winnerConstraint !== WinningConstraint.NoParticipationPrize;
const noNonWinnerConstraints = nonWinningConstraint !== NonWinningConstraint.NoParticipationPrize;
return (
(winIndex === null && noNonWinnerConstraints) || (winIndex !== null && noWinnerConstraints)
);
}