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,61 +1280,65 @@ const EndingPhaseAuction = (props: {
/> />
</div> </div>
<div className="action-field"> {props.attributes.saleType == 'auction' && (
<span className="field-title">Gap Time</span> <>
<span className="field-info"> <div className="action-field">
The final phase of the auction will begin when there is this much <span className="field-title">Gap Time</span>
time left on the countdown. Any bids placed during the final phase <span className="field-info">
will extend the end time by this same duration. The final phase of the auction will begin when there is this
</span> much time left on the countdown. Any bids placed during the
<Input final phase will extend the end time by this same duration.
addonAfter={ </span>
<Select <Input
defaultValue={props.attributes.gapTimeType} addonAfter={
onChange={value => <Select
defaultValue={props.attributes.gapTimeType}
onChange={value =>
props.setAttributes({
...props.attributes,
gapTimeType: value,
})
}
>
<Option value="minutes">Minutes</Option>
<Option value="hours">Hours</Option>
<Option value="days">Days</Option>
</Select>
}
type="number"
className="input"
placeholder="Set the gap time"
onChange={info =>
props.setAttributes({ props.setAttributes({
...props.attributes, ...props.attributes,
gapTimeType: value, gapTime: parseInt(info.target.value),
}) })
} }
> />
<Option value="minutes">Minutes</Option> </div>
<Option value="hours">Hours</Option>
<Option value="days">Days</Option>
</Select>
}
type="number"
className="input"
placeholder="Set the gap time"
onChange={info =>
props.setAttributes({
...props.attributes,
gapTime: parseInt(info.target.value),
})
}
/>
</div>
<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"
className="input" className="input"
placeholder="Percentage" placeholder="Percentage"
suffix="%" suffix="%"
onChange={info => onChange={info =>
props.setAttributes({ props.setAttributes({
...props.attributes, ...props.attributes,
tickSizeEndingPhase: parseInt(info.target.value), tickSizeEndingPhase: parseInt(info.target.value),
}) })
} }
/> />
</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,
) )
} }