Working on instant sale

This commit is contained in:
Jordan Prince 2021-08-13 20:09:34 -05:00
parent b4f5b4b29b
commit bf22676c2c
8 changed files with 246 additions and 69 deletions

View File

@ -373,10 +373,14 @@ export interface IPartialCreateAuctionArgs {
tickSize: BN | null; tickSize: BN | null;
gapTickSizePercentage: number | null; gapTickSizePercentage: number | null;
instantSalePrice: BN | null;
name: number[] | null;
} }
export class CreateAuctionArgs implements IPartialCreateAuctionArgs { export class CreateAuctionArgs implements IPartialCreateAuctionArgs {
instruction: number = 1; instruction: number = 7;
/// How many winners are allowed for this auction. See AuctionData. /// How many winners are allowed for this auction. See AuctionData.
winners: WinnerLimit; winners: WinnerLimit;
/// End time is the cut-off point that the auction is forced to end by. See AuctionData. /// 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; gapTickSizePercentage: number | null;
instantSalePrice: BN | null;
name: number[] | null;
constructor(args: { constructor(args: {
winners: WinnerLimit; winners: WinnerLimit;
endAuctionAt: BN | null; endAuctionAt: BN | null;
@ -406,6 +414,8 @@ export class CreateAuctionArgs implements IPartialCreateAuctionArgs {
priceFloor: PriceFloor; priceFloor: PriceFloor;
tickSize: BN | null; tickSize: BN | null;
gapTickSizePercentage: number | null; gapTickSizePercentage: number | null;
name: number[] | null;
instantSalePrice: BN | null;
}) { }) {
this.winners = args.winners; this.winners = args.winners;
this.endAuctionAt = args.endAuctionAt; this.endAuctionAt = args.endAuctionAt;
@ -416,6 +426,8 @@ export class CreateAuctionArgs implements IPartialCreateAuctionArgs {
this.priceFloor = args.priceFloor; this.priceFloor = args.priceFloor;
this.tickSize = args.tickSize; this.tickSize = args.tickSize;
this.gapTickSizePercentage = args.gapTickSizePercentage; this.gapTickSizePercentage = args.gapTickSizePercentage;
this.name = args.name;
this.instantSalePrice = args.instantSalePrice;
} }
} }
@ -468,6 +480,8 @@ export const AUCTION_SCHEMA = new Map<any, any>([
['priceFloor', PriceFloor], ['priceFloor', PriceFloor],
['tickSize', { kind: 'option', type: 'u64' }], ['tickSize', { kind: 'option', type: 'u64' }],
['gapTickSizePercentage', { kind: 'option', type: 'u8' }], ['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<any, any>([
['totalUncancelledBids', 'u64'], ['totalUncancelledBids', 'u64'],
['tickSize', { kind: 'option', type: 'u64' }], ['tickSize', { kind: 'option', type: 'u64' }],
['gapTickSizePercentage', { kind: 'option', type: 'u8' }], ['gapTickSizePercentage', { kind: 'option', type: 'u8' }],
['instantSalePrice', { kind: 'option', type: 'u64' }],
['name', { kind: 'option', type: [32] }],
], ],
}, },
], ],

View File

@ -237,7 +237,20 @@ export class Metadata {
} }
public async init() { 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.edition = edition;
this.masterEdition = edition; this.masterEdition = edition;
} }

View File

@ -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,
}),
);
}

View File

@ -320,6 +320,15 @@ export class RedeemFullRightsTransferBidArgs {
export class StartAuctionArgs { export class StartAuctionArgs {
instruction = 5; instruction = 5;
} }
export class EndAuctionArgs {
instruction = 21;
reveal: BN[] | null;
constructor(args: { reveal: BN[] | null }) {
this.reveal = args.reveal;
}
}
export class ClaimBidArgs { export class ClaimBidArgs {
instruction = 6; instruction = 6;
} }
@ -953,6 +962,16 @@ export const SCHEMA = new Map<any, any>([
fields: [['instruction', 'u8']], fields: [['instruction', 'u8']],
}, },
], ],
[
EndAuctionArgs,
{
kind: 'struct',
fields: [
['instruction', 'u8'],
['reveal', { kind: 'option', type: [BN] }],
],
},
],
[ [
ClaimBidArgs, ClaimBidArgs,
{ {

View File

@ -1,4 +1,9 @@
import { findProgramAddress, programIds, VAULT_PREFIX } from '@oyster/common'; import {
findProgramAddress,
getAuctionExtended,
programIds,
VAULT_PREFIX,
} from '@oyster/common';
import { import {
PublicKey, PublicKey,
SystemProgram, SystemProgram,
@ -63,6 +68,11 @@ export async function redeemBid(
safetyDeposit, safetyDeposit,
); );
const auctionExtended = await getAuctionExtended({
auctionProgramId: PROGRAM_IDS.auction,
resource: vault,
});
const value = const value =
auctioneerReclaimIndex !== undefined auctioneerReclaimIndex !== undefined
? new RedeemUnusedWinningConfigItemsAsAuctioneerArgs({ ? new RedeemUnusedWinningConfigItemsAsAuctioneerArgs({
@ -167,6 +177,11 @@ export async function redeemBid(
isSigner: false, isSigner: false,
isWritable: false, isWritable: false,
}, },
{
pubkey: auctionExtended,
isSigner: false,
isWritable: false,
},
]; ];
if (isPrintingType && masterEdition && reservationList) { if (isPrintingType && masterEdition && reservationList) {

View File

@ -1,4 +1,9 @@
import { programIds, VAULT_PREFIX, findProgramAddress } from '@oyster/common'; import {
programIds,
VAULT_PREFIX,
findProgramAddress,
getAuctionExtended,
} from '@oyster/common';
import { import {
PublicKey, PublicKey,
SystemProgram, SystemProgram,
@ -62,6 +67,11 @@ export async function redeemFullRightsTransferBid(
safetyDeposit, safetyDeposit,
); );
const auctionExtended = await getAuctionExtended({
auctionProgramId: PROGRAM_IDS.auction,
resource: vault,
});
const value = const value =
auctioneerReclaimIndex !== undefined auctioneerReclaimIndex !== undefined
? new RedeemUnusedWinningConfigItemsAsAuctioneerArgs({ ? new RedeemUnusedWinningConfigItemsAsAuctioneerArgs({
@ -176,6 +186,12 @@ export async function redeemFullRightsTransferBid(
isSigner: false, isSigner: false,
isWritable: false, isWritable: false,
}, },
{
pubkey: auctionExtended,
isSigner: false,
isWritable: false,
},
]; ];
instructions.push( instructions.push(

View File

@ -36,14 +36,7 @@ import { Connection, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
import { MintLayout } from '@solana/spl-token'; import { MintLayout } from '@solana/spl-token';
import { useHistory, useParams } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom';
import { capitalize } from 'lodash'; import { capitalize } from 'lodash';
import { import { WinningConfigType, AmountRange } from '../../models/metaplex';
WinningConfigType,
NonWinningConstraint,
WinningConstraint,
ParticipationConfigV2,
SafetyDepositConfig,
AmountRange,
} from '../../models/metaplex';
import moment from 'moment'; import moment from 'moment';
import { import {
createAuctionManager, createAuctionManager,
@ -124,6 +117,8 @@ export interface AuctionState {
tiers?: Array<Tier>; tiers?: Array<Tier>;
winnersCount: number; winnersCount: number;
instantSalePrice?: number;
} }
export const AuctionCreateView = () => { export const AuctionCreateView = () => {
@ -413,6 +408,10 @@ export const AuctionCreateView = () => {
tickSize: attributes.priceTick tickSize: attributes.priceTick
? new BN(attributes.priceTick * LAMPORTS_PER_SOL) ? new BN(attributes.priceTick * LAMPORTS_PER_SOL)
: null, : null,
instantSalePrice: attributes.instantSalePrice
? new BN((attributes.instantSalePrice || 0) * LAMPORTS_PER_SOL)
: null,
name: null,
}; };
const _auctionObj = await createAuctionManager( const _auctionObj = await createAuctionManager(
@ -856,6 +855,22 @@ const SaleTypeStep = (props: {
Allow bidding on your NFT(s). Allow bidding on your NFT(s).
</div> </div>
</Radio.Group> </Radio.Group>
<Radio.Group
defaultValue={props.attributes.saleType}
onChange={info =>
props.setAttributes({
...props.attributes,
saleType: info.target.value,
})
}
>
<Radio className="radio-field" value="auction">
Instant Sale
</Radio>
<div className="radio-subtitle">
Instant purchase and redemption of your NFT.
</div>
</Radio.Group>
</label> </label>
</Col> </Col>
</Row> </Row>
@ -898,13 +913,13 @@ const PriceSale = (props: {
<> <>
<Row className="call-to-action"> <Row className="call-to-action">
<h2>Price</h2> <h2>Price</h2>
<p>Set the price for your auction.</p> <p>Set the fixed price for your instant sale.</p>
</Row> </Row>
<Row className="content-action"> <Row className="content-action">
<label className="action-field"> <label className="action-field">
<span className="field-title">Sale price</span> <span className="field-title">Sale price</span>
<span className="field-info"> <span className="field-info">
This is the starting bid price for your auction. This is the price of purchasing the item(s).
</span> </span>
<Input <Input
type="number" type="number"
@ -917,7 +932,8 @@ const PriceSale = (props: {
onChange={info => onChange={info =>
props.setAttributes({ props.setAttributes({
...props.attributes, ...props.attributes,
price: parseFloat(info.target.value) || undefined, priceFloor: parseFloat(info.target.value),
instantSalePrice: parseFloat(info.target.value),
}) })
} }
/> />
@ -1228,7 +1244,10 @@ const EndingPhaseAuction = (props: {
<Row className="content-action"> <Row className="content-action">
<Col className="section" xl={24}> <Col className="section" xl={24}>
<div className="action-field"> <div className="action-field">
<span className="field-title">Auction Duration</span> <span className="field-title">
{props.attributes.saleType == 'auction' ? 'Auction' : 'Sale'}{' '}
Duration
</span>
<span className="field-info"> <span className="field-info">
This is how long the auction will last for. This is how long the auction will last for.
</span> </span>
@ -1261,12 +1280,14 @@ const EndingPhaseAuction = (props: {
/> />
</div> </div>
{props.attributes.saleType == 'auction' && (
<>
<div className="action-field"> <div className="action-field">
<span className="field-title">Gap Time</span> <span className="field-title">Gap Time</span>
<span className="field-info"> <span className="field-info">
The final phase of the auction will begin when there is this much The final phase of the auction will begin when there is this
time left on the countdown. Any bids placed during the final phase much time left on the countdown. Any bids placed during the
will extend the end time by this same duration. final phase will extend the end time by this same duration.
</span> </span>
<Input <Input
addonAfter={ addonAfter={
@ -1299,9 +1320,9 @@ const EndingPhaseAuction = (props: {
<label className="action-field"> <label className="action-field">
<span className="field-title">Tick Size for Ending Phase</span> <span className="field-title">Tick Size for Ending Phase</span>
<span className="field-info"> <span className="field-info">
In order for winners to move up in the auction, they must place a In order for winners to move up in the auction, they must
bid thats at least this percentage higher than the next highest place a bid thats at least this percentage higher than the
bid. next highest bid.
</span> </span>
<Input <Input
type="number" type="number"
@ -1316,6 +1337,8 @@ const EndingPhaseAuction = (props: {
} }
/> />
</label> </label>
</>
)}
</Col> </Col>
</Row> </Row>
<Row> <Row>

View File

@ -45,10 +45,10 @@ pub struct CreateAuctionArgsV2 {
pub tick_size: Option<u64>, pub tick_size: Option<u64>,
/// Add a minimum percentage increase each bid must meet. /// Add a minimum percentage increase each bid must meet.
pub gap_tick_size_percentage: Option<u8>, pub gap_tick_size_percentage: Option<u8>,
/// Auction name
pub name: AuctionName,
/// Add a instant sale price. /// Add a instant sale price.
pub instant_sale_price: Option<u64>, pub instant_sale_price: Option<u64>,
/// Auction name
pub name: Option<AuctionName>,
} }
struct Accounts<'a, 'b: 'a> { struct Accounts<'a, 'b: 'a> {
@ -94,6 +94,6 @@ pub fn create_auction_v2(
gap_tick_size_percentage: args.gap_tick_size_percentage, gap_tick_size_percentage: args.gap_tick_size_percentage,
}, },
args.instant_sale_price, args.instant_sale_price,
Some(args.name), args.name,
) )
} }