diff --git a/js/packages/common/src/actions/auction.ts b/js/packages/common/src/actions/auction.ts index 5c350af..8f90632 100644 --- a/js/packages/common/src/actions/auction.ts +++ b/js/packages/common/src/actions/auction.ts @@ -373,10 +373,14 @@ export interface IPartialCreateAuctionArgs { tickSize: BN | null; gapTickSizePercentage: number | null; + + instantSalePrice: BN | null; + + name: number[] | null; } export class CreateAuctionArgs implements IPartialCreateAuctionArgs { - instruction: number = 1; + instruction: number = 7; /// How many winners are allowed for this auction. See AuctionData. winners: WinnerLimit; /// End time is the cut-off point that the auction is forced to end by. See AuctionData. @@ -396,6 +400,10 @@ export class CreateAuctionArgs implements IPartialCreateAuctionArgs { gapTickSizePercentage: number | null; + instantSalePrice: BN | null; + + name: number[] | null; + constructor(args: { winners: WinnerLimit; endAuctionAt: BN | null; @@ -406,6 +414,8 @@ export class CreateAuctionArgs implements IPartialCreateAuctionArgs { priceFloor: PriceFloor; tickSize: BN | null; gapTickSizePercentage: number | null; + name: number[] | null; + instantSalePrice: BN | null; }) { this.winners = args.winners; this.endAuctionAt = args.endAuctionAt; @@ -416,6 +426,8 @@ export class CreateAuctionArgs implements IPartialCreateAuctionArgs { this.priceFloor = args.priceFloor; this.tickSize = args.tickSize; this.gapTickSizePercentage = args.gapTickSizePercentage; + this.name = args.name; + this.instantSalePrice = args.instantSalePrice; } } @@ -468,6 +480,8 @@ export const AUCTION_SCHEMA = new Map([ ['priceFloor', PriceFloor], ['tickSize', { kind: 'option', type: 'u64' }], ['gapTickSizePercentage', { kind: 'option', type: 'u8' }], + ['instantSalePrice', { kind: 'option', type: 'u64' }], + ['name', { kind: 'option', type: [32] }], ], }, ], @@ -545,6 +559,8 @@ export const AUCTION_SCHEMA = new Map([ ['totalUncancelledBids', 'u64'], ['tickSize', { kind: 'option', type: 'u64' }], ['gapTickSizePercentage', { kind: 'option', type: 'u8' }], + ['instantSalePrice', { kind: 'option', type: 'u64' }], + ['name', { kind: 'option', type: [32] }], ], }, ], diff --git a/js/packages/common/src/actions/metadata.ts b/js/packages/common/src/actions/metadata.ts index 9070d2c..d40893b 100644 --- a/js/packages/common/src/actions/metadata.ts +++ b/js/packages/common/src/actions/metadata.ts @@ -237,7 +237,20 @@ export class Metadata { } public async init() { - const edition = await getEdition(this.mint); + const edition: PublicKey; + if (this.info.editionNonce != null) { + edition = await PublicKey.createProgramAddress( + [ + Buffer.from(METADATA_PREFIX), + METADATA_PROGRAM_ID.toBuffer(), + tempCache.metadata[i].info.mint.toBuffer(), + new Uint8Array([tempCache.metadata[i].info.editionNonce || 0]), + ], + METADATA_PROGRAM_ID, + ); + } else { + edition = await getEdition(tempCache.metadata[i].info.mint); + } this.edition = edition; this.masterEdition = edition; } diff --git a/js/packages/web/src/models/metaplex/endAuction.ts b/js/packages/web/src/models/metaplex/endAuction.ts new file mode 100644 index 0000000..2304e9c --- /dev/null +++ b/js/packages/web/src/models/metaplex/endAuction.ts @@ -0,0 +1,75 @@ +import { getAuctionExtended, programIds } from '@oyster/common'; +import { + PublicKey, + SYSVAR_CLOCK_PUBKEY, + TransactionInstruction, +} from '@solana/web3.js'; +import { serialize } from 'borsh'; + +import { EndAuctionArgs, getAuctionKeys, SCHEMA } from '.'; + +export async function endAuction( + vault: PublicKey, + auctionManagerAuthority: PublicKey, + instructions: TransactionInstruction[], +) { + const PROGRAM_IDS = programIds(); + const store = PROGRAM_IDS.store; + if (!store) { + throw new Error('Store not initialized'); + } + + const { auctionKey, auctionManagerKey } = await getAuctionKeys(vault); + const auctionExtended = await getAuctionExtended({ + auctionProgramId: PROGRAM_IDS.auction, + resource: vault, + }); + const value = new EndAuctionArgs({ reveal: null }); + const data = Buffer.from(serialize(SCHEMA, value)); + + const keys = [ + { + pubkey: auctionManagerKey, + isSigner: false, + isWritable: true, + }, + { + pubkey: auctionKey, + isSigner: false, + isWritable: true, + }, + { + pubkey: auctionExtended, + isSigner: false, + isWritable: false, + }, + { + pubkey: auctionManagerAuthority, + isSigner: true, + isWritable: false, + }, + { + pubkey: store, + isSigner: false, + isWritable: false, + }, + { + pubkey: PROGRAM_IDS.auction, + isSigner: false, + isWritable: false, + }, + { + pubkey: SYSVAR_CLOCK_PUBKEY, + isSigner: false, + isWritable: false, + }, + ]; + + instructions.push( + new TransactionInstruction({ + keys, + programId: PROGRAM_IDS.metaplex, + data, + }), + ); +} diff --git a/js/packages/web/src/models/metaplex/index.ts b/js/packages/web/src/models/metaplex/index.ts index 5869259..7190725 100644 --- a/js/packages/web/src/models/metaplex/index.ts +++ b/js/packages/web/src/models/metaplex/index.ts @@ -320,6 +320,15 @@ export class RedeemFullRightsTransferBidArgs { export class StartAuctionArgs { instruction = 5; } + +export class EndAuctionArgs { + instruction = 21; + reveal: BN[] | null; + constructor(args: { reveal: BN[] | null }) { + this.reveal = args.reveal; + } +} + export class ClaimBidArgs { instruction = 6; } @@ -953,6 +962,16 @@ export const SCHEMA = new Map([ fields: [['instruction', 'u8']], }, ], + [ + EndAuctionArgs, + { + kind: 'struct', + fields: [ + ['instruction', 'u8'], + ['reveal', { kind: 'option', type: [BN] }], + ], + }, + ], [ ClaimBidArgs, { diff --git a/js/packages/web/src/models/metaplex/redeemBid.ts b/js/packages/web/src/models/metaplex/redeemBid.ts index 2a2952a..b48fd6e 100644 --- a/js/packages/web/src/models/metaplex/redeemBid.ts +++ b/js/packages/web/src/models/metaplex/redeemBid.ts @@ -1,4 +1,9 @@ -import { findProgramAddress, programIds, VAULT_PREFIX } from '@oyster/common'; +import { + findProgramAddress, + getAuctionExtended, + programIds, + VAULT_PREFIX, +} from '@oyster/common'; import { PublicKey, SystemProgram, @@ -63,6 +68,11 @@ export async function redeemBid( safetyDeposit, ); + const auctionExtended = await getAuctionExtended({ + auctionProgramId: PROGRAM_IDS.auction, + resource: vault, + }); + const value = auctioneerReclaimIndex !== undefined ? new RedeemUnusedWinningConfigItemsAsAuctioneerArgs({ @@ -167,6 +177,11 @@ export async function redeemBid( isSigner: false, isWritable: false, }, + { + pubkey: auctionExtended, + isSigner: false, + isWritable: false, + }, ]; if (isPrintingType && masterEdition && reservationList) { diff --git a/js/packages/web/src/models/metaplex/redeemFullRightsTransferBid.ts b/js/packages/web/src/models/metaplex/redeemFullRightsTransferBid.ts index 15248f1..8dde607 100644 --- a/js/packages/web/src/models/metaplex/redeemFullRightsTransferBid.ts +++ b/js/packages/web/src/models/metaplex/redeemFullRightsTransferBid.ts @@ -1,4 +1,9 @@ -import { programIds, VAULT_PREFIX, findProgramAddress } from '@oyster/common'; +import { + programIds, + VAULT_PREFIX, + findProgramAddress, + getAuctionExtended, +} from '@oyster/common'; import { PublicKey, SystemProgram, @@ -62,6 +67,11 @@ export async function redeemFullRightsTransferBid( safetyDeposit, ); + const auctionExtended = await getAuctionExtended({ + auctionProgramId: PROGRAM_IDS.auction, + resource: vault, + }); + const value = auctioneerReclaimIndex !== undefined ? new RedeemUnusedWinningConfigItemsAsAuctioneerArgs({ @@ -176,6 +186,12 @@ export async function redeemFullRightsTransferBid( isSigner: false, isWritable: false, }, + + { + pubkey: auctionExtended, + isSigner: false, + isWritable: false, + }, ]; instructions.push( diff --git a/js/packages/web/src/views/auctionCreate/index.tsx b/js/packages/web/src/views/auctionCreate/index.tsx index b3f0db2..9e73d29 100644 --- a/js/packages/web/src/views/auctionCreate/index.tsx +++ b/js/packages/web/src/views/auctionCreate/index.tsx @@ -36,14 +36,7 @@ import { Connection, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; import { MintLayout } from '@solana/spl-token'; import { useHistory, useParams } from 'react-router-dom'; import { capitalize } from 'lodash'; -import { - WinningConfigType, - NonWinningConstraint, - WinningConstraint, - ParticipationConfigV2, - SafetyDepositConfig, - AmountRange, -} from '../../models/metaplex'; +import { WinningConfigType, AmountRange } from '../../models/metaplex'; import moment from 'moment'; import { createAuctionManager, @@ -124,6 +117,8 @@ export interface AuctionState { tiers?: Array; winnersCount: number; + + instantSalePrice?: number; } export const AuctionCreateView = () => { @@ -413,6 +408,10 @@ export const AuctionCreateView = () => { tickSize: attributes.priceTick ? new BN(attributes.priceTick * LAMPORTS_PER_SOL) : null, + instantSalePrice: attributes.instantSalePrice + ? new BN((attributes.instantSalePrice || 0) * LAMPORTS_PER_SOL) + : null, + name: null, }; const _auctionObj = await createAuctionManager( @@ -856,6 +855,22 @@ const SaleTypeStep = (props: { Allow bidding on your NFT(s). + + props.setAttributes({ + ...props.attributes, + saleType: info.target.value, + }) + } + > + + Instant Sale + +
+ Instant purchase and redemption of your NFT. +
+
@@ -898,13 +913,13 @@ const PriceSale = (props: { <>

Price

-

Set the price for your auction.

+

Set the fixed price for your instant sale.