Code fixes for multiple editions

This commit is contained in:
Jordan Prince 2021-05-02 16:52:10 -05:00
parent 9db1b71a9e
commit 593a229950
3 changed files with 148 additions and 76 deletions

View File

@ -4,13 +4,28 @@ import {
PublicKey,
TransactionInstruction,
} from '@solana/web3.js';
import { utils, actions, models } from '@oyster/common';
import {
utils,
actions,
models,
MasterEdition,
Metadata,
ParsedAccount,
} from '@oyster/common';
import { AccountLayout } from '@solana/spl-token';
import BN from 'bn.js';
import { SafetyDepositDraft } from './createAuctionManager';
const { createTokenAccount, addTokenToInactiveVault, VAULT_PREFIX } = actions;
const { approve } = models;
export interface SafetyDepositInstructionConfig {
tokenAccount: PublicKey;
tokenMint: PublicKey;
amount: BN;
draft: SafetyDepositDraft;
}
const BATCH_SIZE = 4;
// This command batches out adding tokens to a vault using a prefilled payer account, and then activates and combines
// the vault for use. It issues a series of transaction instructions and signers for the sendTransactions batch.
@ -18,7 +33,7 @@ export async function addTokensToVault(
connection: Connection,
wallet: any,
vault: PublicKey,
nfts: { tokenAccount: PublicKey; tokenMint: PublicKey; amount: BN }[],
nfts: SafetyDepositInstructionConfig[],
): Promise<{
instructions: Array<TransactionInstruction[]>;
signers: Array<Account[]>;

View File

@ -28,10 +28,14 @@ import {
initAuctionManager,
startAuction,
validateSafetyDepositBox,
WinningConfig,
} from '../models/metaplex';
import { createVault } from './createVault';
import { closeVault } from './closeVault';
import { addTokensToVault } from './addTokensToVault';
import {
addTokensToVault,
SafetyDepositInstructionConfig,
} from './addTokensToVault';
import { makeAuction } from './makeAuction';
import { createExternalPriceAccount } from './createExternalPriceAccount';
const { createTokenAccount } = actions;
@ -75,7 +79,8 @@ export async function createAuctionManager(
winnerLimit: WinnerLimit,
endAuctionAt: BN,
auctionGap: BN,
safetyDeposits: SafetyDepositDraft[],
safetyDepositDrafts: SafetyDepositDraft[],
openEditionSafetyDepositDraft: SafetyDepositDraft | undefined,
paymentMint: PublicKey,
): Promise<{
vault: PublicKey;
@ -111,37 +116,11 @@ export async function createAuctionManager(
paymentMint,
);
let nftConfigs = safetyDeposits.map((w, i) => {
let winningConfig = settings.winningConfigs.find(
ow => ow.safetyDepositBoxIndex == i,
);
let mintToUse = w.metadata.info.mint;
let holdingToUse = w.holding;
// When selling a Limited Edition with intention to print copies of it, we use it's master mint
// as token type because we are actually selling authorization tokens, not the limited edition itself..
if (
winningConfig &&
winningConfig.editionType == EditionType.LimitedEdition &&
w.masterEdition?.info.masterMint &&
w.masterMintHolding
) {
mintToUse = w.masterEdition?.info.masterMint;
holdingToUse = w.masterMintHolding;
}
return {
tokenAccount: holdingToUse,
tokenMint: mintToUse,
amount: new BN(winningConfig?.amount || 1),
};
});
let openEditionSafetyDeposit = undefined;
if (
settings.openEditionConfig != null &&
settings.openEditionConfig != undefined
) {
openEditionSafetyDeposit = safetyDeposits[settings.openEditionConfig];
}
let safetyDepositConfigs = buildSafetyDepositArray(
safetyDepositDrafts,
openEditionSafetyDepositDraft,
settings.winningConfigs,
);
const {
instructions: auctionManagerInstructions,
@ -153,14 +132,14 @@ export async function createAuctionManager(
vault,
paymentMint,
settings,
openEditionSafetyDeposit,
openEditionSafetyDepositDraft,
);
const {
instructions: addTokenInstructions,
signers: addTokenSigners,
stores,
} = await addTokensToVault(connection, wallet, vault, nftConfigs);
} = await addTokensToVault(connection, wallet, vault, safetyDepositConfigs);
let lookup: byType = {
externalPriceAccount: {
@ -194,8 +173,13 @@ export async function createAuctionManager(
validateBoxes: await validateBoxes(
wallet,
vault,
// No need to validate open edition, it's already been during init
safetyDeposits.filter((_, i) => i != settings.openEditionConfig),
// No need to validate open edition, it's already been during init, or if not present, let all in
safetyDepositConfigs.filter(
c =>
!openEditionSafetyDepositDraft ||
c.draft.metadata.pubkey.toBase58() !=
openEditionSafetyDepositDraft.metadata.pubkey.toBase58(),
),
stores,
settings,
),
@ -238,13 +222,75 @@ export async function createAuctionManager(
return { vault, auction, auctionManager };
}
function buildSafetyDepositArray(
safetyDeposits: SafetyDepositDraft[],
openEditionSafetyDepositDraft: SafetyDepositDraft | undefined,
winningConfigs: WinningConfig[],
): SafetyDepositInstructionConfig[] {
let safetyDepositConfig: SafetyDepositInstructionConfig[] = [];
safetyDeposits.forEach((w, i) => {
let winningConfigsThatShareThisBox = winningConfigs.filter(
ow => ow.safetyDepositBoxIndex == i,
);
// Configs where we are selling this safety deposit as a master edition or single nft
let nonLimitedEditionConfigs = winningConfigsThatShareThisBox.filter(
ow => ow.editionType != EditionType.LimitedEdition,
);
// we may also have an auction where we are selling prints of the master too as secondary prizes
let limitedEditionConfigs = winningConfigsThatShareThisBox.filter(
ow => ow.editionType == EditionType.LimitedEdition,
);
const nonLimitedEditionTotal = nonLimitedEditionConfigs
.map(ow => ow.amount)
.reduce((sum, acc) => (sum += acc), 0);
const limitedEditionTotal = limitedEditionConfigs
.map(ow => ow.amount)
.reduce((sum, acc) => (sum += acc), 0);
if (nonLimitedEditionTotal > 0) {
safetyDepositConfig.push({
tokenAccount: w.holding,
tokenMint: w.metadata.info.mint,
amount: new BN(nonLimitedEditionTotal),
draft: w,
});
}
if (
limitedEditionTotal > 0 &&
w.masterEdition?.info.masterMint &&
w.masterMintHolding
) {
safetyDepositConfig.push({
tokenAccount: w.masterMintHolding,
tokenMint: w.masterEdition?.info.masterMint,
amount: new BN(limitedEditionTotal),
draft: w,
});
}
});
if (openEditionSafetyDepositDraft) {
safetyDepositConfig.push({
tokenAccount: openEditionSafetyDepositDraft.holding,
tokenMint: openEditionSafetyDepositDraft.metadata.info.mint,
amount: new BN(1),
draft: openEditionSafetyDepositDraft,
});
}
return safetyDepositConfig;
}
async function setupAuctionManagerInstructions(
connection: Connection,
wallet: any,
vault: PublicKey,
paymentMint: PublicKey,
settings: AuctionManagerSettings,
openEditionSafetyDeposit?: SafetyDepositDraft,
openEditionSafetyDepositDraft?: SafetyDepositDraft,
): Promise<{
instructions: TransactionInstruction[];
signers: Account[];
@ -269,12 +315,12 @@ async function setupAuctionManagerInstructions(
await initAuctionManager(
vault,
openEditionSafetyDeposit?.metadata.pubkey,
openEditionSafetyDeposit?.nameSymbol?.pubkey,
openEditionSafetyDepositDraft?.metadata.pubkey,
openEditionSafetyDepositDraft?.nameSymbol?.pubkey,
wallet.publicKey,
openEditionSafetyDeposit?.masterEdition?.pubkey,
openEditionSafetyDeposit?.metadata.info.mint,
openEditionSafetyDeposit?.masterEdition?.info.masterMint,
openEditionSafetyDepositDraft?.masterEdition?.pubkey,
openEditionSafetyDepositDraft?.metadata.info.mint,
openEditionSafetyDepositDraft?.masterEdition?.info.masterMint,
wallet.publicKey,
wallet.publicKey,
wallet.publicKey,
@ -304,7 +350,7 @@ async function setupStartAuction(
async function validateBoxes(
wallet: any,
vault: PublicKey,
safetyDeposits: SafetyDepositDraft[],
safetyDeposits: SafetyDepositInstructionConfig[],
stores: PublicKey[],
settings: AuctionManagerSettings,
): Promise<{
@ -327,40 +373,40 @@ async function validateBoxes(
if (winningConfig) {
if (
winningConfig.editionType == EditionType.LimitedEdition &&
safetyDeposits[i].masterEdition &&
safetyDeposits[i].masterEdition?.info.masterMint
safetyDeposits[i].draft.masterEdition &&
safetyDeposits[i].draft.masterEdition?.info.masterMint
)
safetyDepositBox = await getSafetyDepositBox(
vault,
//@ts-ignore
safetyDeposits[i].masterEdition.info.masterMint,
safetyDeposits[i].draft.masterEdition.info.masterMint,
);
else
safetyDepositBox = await getSafetyDepositBox(
vault,
safetyDeposits[i].metadata.info.mint,
safetyDeposits[i].draft.metadata.info.mint,
);
const edition: PublicKey = await getEdition(
safetyDeposits[i].metadata.info.mint,
safetyDeposits[i].draft.metadata.info.mint,
);
await validateSafetyDepositBox(
vault,
safetyDeposits[i].metadata.pubkey,
safetyDeposits[i].nameSymbol?.pubkey,
safetyDeposits[i].draft.metadata.pubkey,
safetyDeposits[i].draft.nameSymbol?.pubkey,
safetyDepositBox,
stores[i],
//@ts-ignore
winningConfig.editionType == EditionType.LimitedEdition
? safetyDeposits[i].masterEdition?.info.masterMint
: safetyDeposits[i].metadata.info.mint,
? safetyDeposits[i].draft.masterEdition?.info.masterMint
: safetyDeposits[i].draft.metadata.info.mint,
wallet.publicKey,
wallet.publicKey,
wallet.publicKey,
tokenInstructions,
edition,
safetyDeposits[i].masterEdition?.info.masterMint,
safetyDeposits[i].masterEdition ? wallet.publicKey : undefined,
safetyDeposits[i].draft.masterEdition?.info.masterMint,
safetyDeposits[i].draft.masterEdition ? wallet.publicKey : undefined,
);
}
signers.push(tokenSigners);

View File

@ -175,6 +175,31 @@ export const AuctionCreateView = () => {
attributes.category == AuctionCategory.Limited ||
attributes.category == AuctionCategory.Single
) {
// In these cases there is only ever one item in the array.
let winningConfigs: WinningConfig[];
if (attributes.category == AuctionCategory.Single)
winningConfigs = [
new WinningConfig({
safetyDepositBoxIndex: 0,
amount: 1,
editionType: attributes.items[0].masterEdition
? EditionType.MasterEdition
: EditionType.NA,
}),
];
else {
winningConfigs = [];
for (let i = 0; i < (attributes.editions || 1); i++) {
winningConfigs.push(
new WinningConfig({
safetyDepositBoxIndex: 0,
amount: 1,
editionType: EditionType.LimitedEdition,
}),
);
}
}
settings = new AuctionManagerSettings({
openEditionWinnerConstraint: attributes.participationNFT
? WinningConstraint.OpenEditionGiven
@ -182,20 +207,7 @@ export const AuctionCreateView = () => {
openEditionNonWinningConstraint: attributes.participationNFT
? NonWinningConstraint.GivenForFixedPrice
: NonWinningConstraint.NoOpenEdition,
winningConfigs: attributes.items.map(
(item, index) =>
new WinningConfig({
// TODO: check index
safetyDepositBoxIndex: index,
amount: 1,
editionType:
attributes.category == AuctionCategory.Limited
? EditionType.LimitedEdition
: item.masterEdition
? EditionType.MasterEdition
: EditionType.NA,
}),
),
winningConfigs,
openEditionConfig: attributes.participationNFT
? attributes.items.length
: null,
@ -222,10 +234,8 @@ export const AuctionCreateView = () => {
winnerLimit,
new BN(endAuctionAt),
new BN((attributes.gapTime || 0) * 60),
[
...attributes.items,
...(attributes.participationNFT ? [attributes.participationNFT] : []),
],
attributes.items,
attributes.participationNFT,
// TODO: move to config
new PublicKey('4XEUcBjLyBHuMDKTARycf4VXqpsAsDcThMbhWgFuDGsC'),
);
@ -527,7 +537,7 @@ const CopiesStep = (props: {
>
Select NFT
</ArtSelector>
{props.attributes.category !== AuctionCategory.Open && (
{props.attributes.category == AuctionCategory.Limited && (
<label className="action-field">
<span className="field-title">
How many copies do you want to create?
@ -543,6 +553,7 @@ const CopiesStep = (props: {
onChange={info =>
props.setAttributes({
...props.attributes,
editions: parseInt(info.target.value),
})
}
/>