@renec-foundation/metaplex-js
Version:
Metaplex JavaScript API
239 lines (212 loc) • 6.93 kB
text/typescript
import BN from 'bn.js';
import { PublicKey } from '@solana/web3.js';
import { Wallet } from '../wallet';
import { Connection } from '../Connection';
import { sendTransaction } from './transactions';
import { Auction, AuctionExtended, BidderMetadata } from '@metaplex-foundation/mpl-auction';
import { TransactionsBatch } from '../utils/transactions-batch';
import {
AuctionManager,
PrizeTrackingTicket,
SafetyDepositConfig,
} from '@metaplex-foundation/mpl-metaplex';
import { Vault } from '@metaplex-foundation/mpl-token-vault';
import {
Edition,
EditionMarker,
MasterEdition,
Metadata,
UpdatePrimarySaleHappenedViaToken,
} from '@renec-foundation/mpl-token-metadata';
import { RedeemPrintingV2Bid } from '@metaplex-foundation/mpl-metaplex';
import { prepareTokenAccountAndMintTxs } from './shared';
import { getBidRedemptionPDA } from './redeemFullRightsTransferBid';
export interface RedeemBidParams {
connection: Connection;
wallet: Wallet;
auction: PublicKey;
store: PublicKey;
}
export interface RedeemBidResponse {
txId: string;
}
export interface RedeemBidTransactionsParams {
bidder: PublicKey;
bidderPotToken?: PublicKey;
bidderMeta: PublicKey;
auction: PublicKey;
auctionExtended: PublicKey;
destination: PublicKey;
vault: PublicKey;
store: PublicKey;
auctionManager: PublicKey;
bidRedemption: PublicKey;
safetyDepositTokenStore: PublicKey;
safetyDeposit: PublicKey;
safetyDepositConfig: PublicKey;
metadata: PublicKey;
newMint: PublicKey;
newMetadata: PublicKey;
newEdition: PublicKey;
masterEdition: PublicKey;
editionMarker: PublicKey;
prizeTrackingTicket: PublicKey;
winIndex: BN;
editionOffset: BN;
}
export const redeemPrintingV2Bid = async ({
connection,
wallet,
store,
auction,
}: RedeemBidParams): Promise<RedeemBidResponse> => {
const bidder = wallet.publicKey;
const {
data: { bidState },
} = 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 { mint, createMintTx, createAssociatedTokenAccountTx, mintToTx, recipient } =
await prepareTokenAccountAndMintTxs(connection, wallet.publicKey);
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);
let prizeTrackingTicket: PrizeTrackingTicket;
// this account doesn't exist when we do redeem for the first time
try {
prizeTrackingTicket = await PrizeTrackingTicket.load(connection, prizeTrackingTicketPDA);
} catch (e) {
prizeTrackingTicket = null;
}
const winIndex = bidState.getWinnerIndex(bidder.toBase58()) || 0;
const editionOffset = getEditionOffset(winIndex);
const editionBase = prizeTrackingTicket?.data.supplySnapshot || masterEdition.data.supply;
const desiredEdition = editionBase.add(editionOffset);
const editionMarkerPDA = await EditionMarker.getPDA(originalMint, desiredEdition);
// checking if edition marker is taken
try {
const editionMarker = await EditionMarker.load(connection, editionMarkerPDA);
const isEditionTaken = editionMarker.data.editionTaken(desiredEdition.toNumber());
if (isEditionTaken) {
throw new Error('The edition is already taken');
}
} catch (e) {
// it's not. continue
}
const txBatch = await getRedeemPrintingV2BidTransactions({
bidder,
bidderMeta: bidderMetaPDA,
store,
vault: vault.pubkey,
destination: recipient,
auction,
auctionExtended: auctionExtendedPDA,
auctionManager: auctionManagerPDA,
safetyDepositTokenStore,
safetyDeposit: safetyDepositBox.pubkey,
bidRedemption: bidRedemptionPDA,
safetyDepositConfig: safetyDepositConfigPDA,
metadata: metadataPDA,
newMint,
newMetadata: newMetadataPDA,
newEdition: newEditionPDA,
masterEdition: masterEditionPDA,
editionMarker: editionMarkerPDA,
prizeTrackingTicket: prizeTrackingTicketPDA,
editionOffset,
winIndex: new BN(winIndex),
});
txBatch.addSigner(mint);
txBatch.addBeforeTransaction(createMintTx);
txBatch.addBeforeTransaction(createAssociatedTokenAccountTx);
txBatch.addBeforeTransaction(mintToTx);
const txId = await sendTransaction({
connection,
wallet,
txs: txBatch.toTransactions(),
signers: txBatch.signers,
});
return { txId };
};
export const getRedeemPrintingV2BidTransactions = async ({
bidder,
destination,
store,
vault,
auction,
auctionManager,
auctionExtended,
bidRedemption,
bidderMeta: bidMetadata,
safetyDepositTokenStore,
safetyDeposit,
safetyDepositConfig,
metadata,
newMint,
newMetadata,
newEdition,
masterEdition,
editionMarker: editionMark,
prizeTrackingTicket,
winIndex,
editionOffset,
}: RedeemBidTransactionsParams) => {
const txBatch = new TransactionsBatch({ transactions: [] });
const redeemPrintingV2BidTx = new RedeemPrintingV2Bid(
{ feePayer: bidder },
{
store,
vault,
auction,
auctionManager,
bidRedemption,
bidMetadata,
safetyDepositTokenStore,
destination,
safetyDeposit,
bidder,
safetyDepositConfig,
auctionExtended,
newMint,
newEdition,
newMetadata,
metadata,
masterEdition,
editionMark,
prizeTrackingTicket,
winIndex,
editionOffset,
},
);
txBatch.addTransaction(redeemPrintingV2BidTx);
const updatePrimarySaleHappenedViaTokenTx = new UpdatePrimarySaleHappenedViaToken(
{ feePayer: bidder },
{
metadata: newMetadata,
owner: bidder,
tokenAccount: destination,
},
);
txBatch.addTransaction(updatePrimarySaleHappenedViaTokenTx);
return txBatch;
};
export function getEditionOffset(winIndex: number) {
const offset = new BN(1);
// NOTE: not sure if this the right way to calculate it
return offset.add(new BN(winIndex));
}