mirror of https://github.com/certusone/oyster.git
Code fixes for multiple editions
This commit is contained in:
parent
9db1b71a9e
commit
593a229950
|
@ -4,13 +4,28 @@ import {
|
||||||
PublicKey,
|
PublicKey,
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from '@solana/web3.js';
|
} 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 { AccountLayout } from '@solana/spl-token';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
|
import { SafetyDepositDraft } from './createAuctionManager';
|
||||||
const { createTokenAccount, addTokenToInactiveVault, VAULT_PREFIX } = actions;
|
const { createTokenAccount, addTokenToInactiveVault, VAULT_PREFIX } = actions;
|
||||||
const { approve } = models;
|
const { approve } = models;
|
||||||
|
|
||||||
|
export interface SafetyDepositInstructionConfig {
|
||||||
|
tokenAccount: PublicKey;
|
||||||
|
tokenMint: PublicKey;
|
||||||
|
amount: BN;
|
||||||
|
draft: SafetyDepositDraft;
|
||||||
|
}
|
||||||
|
|
||||||
const BATCH_SIZE = 4;
|
const BATCH_SIZE = 4;
|
||||||
// This command batches out adding tokens to a vault using a prefilled payer account, and then activates and combines
|
// 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.
|
// 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,
|
connection: Connection,
|
||||||
wallet: any,
|
wallet: any,
|
||||||
vault: PublicKey,
|
vault: PublicKey,
|
||||||
nfts: { tokenAccount: PublicKey; tokenMint: PublicKey; amount: BN }[],
|
nfts: SafetyDepositInstructionConfig[],
|
||||||
): Promise<{
|
): Promise<{
|
||||||
instructions: Array<TransactionInstruction[]>;
|
instructions: Array<TransactionInstruction[]>;
|
||||||
signers: Array<Account[]>;
|
signers: Array<Account[]>;
|
||||||
|
|
|
@ -28,10 +28,14 @@ import {
|
||||||
initAuctionManager,
|
initAuctionManager,
|
||||||
startAuction,
|
startAuction,
|
||||||
validateSafetyDepositBox,
|
validateSafetyDepositBox,
|
||||||
|
WinningConfig,
|
||||||
} from '../models/metaplex';
|
} from '../models/metaplex';
|
||||||
import { createVault } from './createVault';
|
import { createVault } from './createVault';
|
||||||
import { closeVault } from './closeVault';
|
import { closeVault } from './closeVault';
|
||||||
import { addTokensToVault } from './addTokensToVault';
|
import {
|
||||||
|
addTokensToVault,
|
||||||
|
SafetyDepositInstructionConfig,
|
||||||
|
} from './addTokensToVault';
|
||||||
import { makeAuction } from './makeAuction';
|
import { makeAuction } from './makeAuction';
|
||||||
import { createExternalPriceAccount } from './createExternalPriceAccount';
|
import { createExternalPriceAccount } from './createExternalPriceAccount';
|
||||||
const { createTokenAccount } = actions;
|
const { createTokenAccount } = actions;
|
||||||
|
@ -75,7 +79,8 @@ export async function createAuctionManager(
|
||||||
winnerLimit: WinnerLimit,
|
winnerLimit: WinnerLimit,
|
||||||
endAuctionAt: BN,
|
endAuctionAt: BN,
|
||||||
auctionGap: BN,
|
auctionGap: BN,
|
||||||
safetyDeposits: SafetyDepositDraft[],
|
safetyDepositDrafts: SafetyDepositDraft[],
|
||||||
|
openEditionSafetyDepositDraft: SafetyDepositDraft | undefined,
|
||||||
paymentMint: PublicKey,
|
paymentMint: PublicKey,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
vault: PublicKey;
|
vault: PublicKey;
|
||||||
|
@ -111,37 +116,11 @@ export async function createAuctionManager(
|
||||||
paymentMint,
|
paymentMint,
|
||||||
);
|
);
|
||||||
|
|
||||||
let nftConfigs = safetyDeposits.map((w, i) => {
|
let safetyDepositConfigs = buildSafetyDepositArray(
|
||||||
let winningConfig = settings.winningConfigs.find(
|
safetyDepositDrafts,
|
||||||
ow => ow.safetyDepositBoxIndex == i,
|
openEditionSafetyDepositDraft,
|
||||||
|
settings.winningConfigs,
|
||||||
);
|
);
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
instructions: auctionManagerInstructions,
|
instructions: auctionManagerInstructions,
|
||||||
|
@ -153,14 +132,14 @@ export async function createAuctionManager(
|
||||||
vault,
|
vault,
|
||||||
paymentMint,
|
paymentMint,
|
||||||
settings,
|
settings,
|
||||||
openEditionSafetyDeposit,
|
openEditionSafetyDepositDraft,
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
instructions: addTokenInstructions,
|
instructions: addTokenInstructions,
|
||||||
signers: addTokenSigners,
|
signers: addTokenSigners,
|
||||||
stores,
|
stores,
|
||||||
} = await addTokensToVault(connection, wallet, vault, nftConfigs);
|
} = await addTokensToVault(connection, wallet, vault, safetyDepositConfigs);
|
||||||
|
|
||||||
let lookup: byType = {
|
let lookup: byType = {
|
||||||
externalPriceAccount: {
|
externalPriceAccount: {
|
||||||
|
@ -194,8 +173,13 @@ export async function createAuctionManager(
|
||||||
validateBoxes: await validateBoxes(
|
validateBoxes: await validateBoxes(
|
||||||
wallet,
|
wallet,
|
||||||
vault,
|
vault,
|
||||||
// No need to validate open edition, it's already been during init
|
// No need to validate open edition, it's already been during init, or if not present, let all in
|
||||||
safetyDeposits.filter((_, i) => i != settings.openEditionConfig),
|
safetyDepositConfigs.filter(
|
||||||
|
c =>
|
||||||
|
!openEditionSafetyDepositDraft ||
|
||||||
|
c.draft.metadata.pubkey.toBase58() !=
|
||||||
|
openEditionSafetyDepositDraft.metadata.pubkey.toBase58(),
|
||||||
|
),
|
||||||
stores,
|
stores,
|
||||||
settings,
|
settings,
|
||||||
),
|
),
|
||||||
|
@ -238,13 +222,75 @@ export async function createAuctionManager(
|
||||||
return { vault, auction, auctionManager };
|
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(
|
async function setupAuctionManagerInstructions(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
wallet: any,
|
wallet: any,
|
||||||
vault: PublicKey,
|
vault: PublicKey,
|
||||||
paymentMint: PublicKey,
|
paymentMint: PublicKey,
|
||||||
settings: AuctionManagerSettings,
|
settings: AuctionManagerSettings,
|
||||||
openEditionSafetyDeposit?: SafetyDepositDraft,
|
openEditionSafetyDepositDraft?: SafetyDepositDraft,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
instructions: TransactionInstruction[];
|
instructions: TransactionInstruction[];
|
||||||
signers: Account[];
|
signers: Account[];
|
||||||
|
@ -269,12 +315,12 @@ async function setupAuctionManagerInstructions(
|
||||||
|
|
||||||
await initAuctionManager(
|
await initAuctionManager(
|
||||||
vault,
|
vault,
|
||||||
openEditionSafetyDeposit?.metadata.pubkey,
|
openEditionSafetyDepositDraft?.metadata.pubkey,
|
||||||
openEditionSafetyDeposit?.nameSymbol?.pubkey,
|
openEditionSafetyDepositDraft?.nameSymbol?.pubkey,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
openEditionSafetyDeposit?.masterEdition?.pubkey,
|
openEditionSafetyDepositDraft?.masterEdition?.pubkey,
|
||||||
openEditionSafetyDeposit?.metadata.info.mint,
|
openEditionSafetyDepositDraft?.metadata.info.mint,
|
||||||
openEditionSafetyDeposit?.masterEdition?.info.masterMint,
|
openEditionSafetyDepositDraft?.masterEdition?.info.masterMint,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
|
@ -304,7 +350,7 @@ async function setupStartAuction(
|
||||||
async function validateBoxes(
|
async function validateBoxes(
|
||||||
wallet: any,
|
wallet: any,
|
||||||
vault: PublicKey,
|
vault: PublicKey,
|
||||||
safetyDeposits: SafetyDepositDraft[],
|
safetyDeposits: SafetyDepositInstructionConfig[],
|
||||||
stores: PublicKey[],
|
stores: PublicKey[],
|
||||||
settings: AuctionManagerSettings,
|
settings: AuctionManagerSettings,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
|
@ -327,40 +373,40 @@ async function validateBoxes(
|
||||||
if (winningConfig) {
|
if (winningConfig) {
|
||||||
if (
|
if (
|
||||||
winningConfig.editionType == EditionType.LimitedEdition &&
|
winningConfig.editionType == EditionType.LimitedEdition &&
|
||||||
safetyDeposits[i].masterEdition &&
|
safetyDeposits[i].draft.masterEdition &&
|
||||||
safetyDeposits[i].masterEdition?.info.masterMint
|
safetyDeposits[i].draft.masterEdition?.info.masterMint
|
||||||
)
|
)
|
||||||
safetyDepositBox = await getSafetyDepositBox(
|
safetyDepositBox = await getSafetyDepositBox(
|
||||||
vault,
|
vault,
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
safetyDeposits[i].masterEdition.info.masterMint,
|
safetyDeposits[i].draft.masterEdition.info.masterMint,
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
safetyDepositBox = await getSafetyDepositBox(
|
safetyDepositBox = await getSafetyDepositBox(
|
||||||
vault,
|
vault,
|
||||||
safetyDeposits[i].metadata.info.mint,
|
safetyDeposits[i].draft.metadata.info.mint,
|
||||||
);
|
);
|
||||||
const edition: PublicKey = await getEdition(
|
const edition: PublicKey = await getEdition(
|
||||||
safetyDeposits[i].metadata.info.mint,
|
safetyDeposits[i].draft.metadata.info.mint,
|
||||||
);
|
);
|
||||||
|
|
||||||
await validateSafetyDepositBox(
|
await validateSafetyDepositBox(
|
||||||
vault,
|
vault,
|
||||||
safetyDeposits[i].metadata.pubkey,
|
safetyDeposits[i].draft.metadata.pubkey,
|
||||||
safetyDeposits[i].nameSymbol?.pubkey,
|
safetyDeposits[i].draft.nameSymbol?.pubkey,
|
||||||
safetyDepositBox,
|
safetyDepositBox,
|
||||||
stores[i],
|
stores[i],
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
winningConfig.editionType == EditionType.LimitedEdition
|
winningConfig.editionType == EditionType.LimitedEdition
|
||||||
? safetyDeposits[i].masterEdition?.info.masterMint
|
? safetyDeposits[i].draft.masterEdition?.info.masterMint
|
||||||
: safetyDeposits[i].metadata.info.mint,
|
: safetyDeposits[i].draft.metadata.info.mint,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
tokenInstructions,
|
tokenInstructions,
|
||||||
edition,
|
edition,
|
||||||
safetyDeposits[i].masterEdition?.info.masterMint,
|
safetyDeposits[i].draft.masterEdition?.info.masterMint,
|
||||||
safetyDeposits[i].masterEdition ? wallet.publicKey : undefined,
|
safetyDeposits[i].draft.masterEdition ? wallet.publicKey : undefined,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
signers.push(tokenSigners);
|
signers.push(tokenSigners);
|
||||||
|
|
|
@ -175,6 +175,31 @@ export const AuctionCreateView = () => {
|
||||||
attributes.category == AuctionCategory.Limited ||
|
attributes.category == AuctionCategory.Limited ||
|
||||||
attributes.category == AuctionCategory.Single
|
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({
|
settings = new AuctionManagerSettings({
|
||||||
openEditionWinnerConstraint: attributes.participationNFT
|
openEditionWinnerConstraint: attributes.participationNFT
|
||||||
? WinningConstraint.OpenEditionGiven
|
? WinningConstraint.OpenEditionGiven
|
||||||
|
@ -182,20 +207,7 @@ export const AuctionCreateView = () => {
|
||||||
openEditionNonWinningConstraint: attributes.participationNFT
|
openEditionNonWinningConstraint: attributes.participationNFT
|
||||||
? NonWinningConstraint.GivenForFixedPrice
|
? NonWinningConstraint.GivenForFixedPrice
|
||||||
: NonWinningConstraint.NoOpenEdition,
|
: NonWinningConstraint.NoOpenEdition,
|
||||||
winningConfigs: attributes.items.map(
|
winningConfigs,
|
||||||
(item, index) =>
|
|
||||||
new WinningConfig({
|
|
||||||
// TODO: check index
|
|
||||||
safetyDepositBoxIndex: index,
|
|
||||||
amount: 1,
|
|
||||||
editionType:
|
|
||||||
attributes.category == AuctionCategory.Limited
|
|
||||||
? EditionType.LimitedEdition
|
|
||||||
: item.masterEdition
|
|
||||||
? EditionType.MasterEdition
|
|
||||||
: EditionType.NA,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
openEditionConfig: attributes.participationNFT
|
openEditionConfig: attributes.participationNFT
|
||||||
? attributes.items.length
|
? attributes.items.length
|
||||||
: null,
|
: null,
|
||||||
|
@ -222,10 +234,8 @@ export const AuctionCreateView = () => {
|
||||||
winnerLimit,
|
winnerLimit,
|
||||||
new BN(endAuctionAt),
|
new BN(endAuctionAt),
|
||||||
new BN((attributes.gapTime || 0) * 60),
|
new BN((attributes.gapTime || 0) * 60),
|
||||||
[
|
attributes.items,
|
||||||
...attributes.items,
|
attributes.participationNFT,
|
||||||
...(attributes.participationNFT ? [attributes.participationNFT] : []),
|
|
||||||
],
|
|
||||||
// TODO: move to config
|
// TODO: move to config
|
||||||
new PublicKey('4XEUcBjLyBHuMDKTARycf4VXqpsAsDcThMbhWgFuDGsC'),
|
new PublicKey('4XEUcBjLyBHuMDKTARycf4VXqpsAsDcThMbhWgFuDGsC'),
|
||||||
);
|
);
|
||||||
|
@ -527,7 +537,7 @@ const CopiesStep = (props: {
|
||||||
>
|
>
|
||||||
Select NFT
|
Select NFT
|
||||||
</ArtSelector>
|
</ArtSelector>
|
||||||
{props.attributes.category !== AuctionCategory.Open && (
|
{props.attributes.category == AuctionCategory.Limited && (
|
||||||
<label className="action-field">
|
<label className="action-field">
|
||||||
<span className="field-title">
|
<span className="field-title">
|
||||||
How many copies do you want to create?
|
How many copies do you want to create?
|
||||||
|
@ -543,6 +553,7 @@ const CopiesStep = (props: {
|
||||||
onChange={info =>
|
onChange={info =>
|
||||||
props.setAttributes({
|
props.setAttributes({
|
||||||
...props.attributes,
|
...props.attributes,
|
||||||
|
editions: parseInt(info.target.value),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in New Issue