diff --git a/js/packages/common/src/actions/auction.ts b/js/packages/common/src/actions/auction.ts index 59b11b3..b0f6576 100644 --- a/js/packages/common/src/actions/auction.ts +++ b/js/packages/common/src/actions/auction.ts @@ -15,6 +15,7 @@ import { findProgramAddress } from '../utils'; export const AUCTION_PREFIX = 'auction'; export const METADATA = 'metadata'; export const EXTENDED = 'extended'; +export const MAX_AUCTION_DATA_EXTENDED_SIZE = 8 + 9 + 2 + 200; export enum AuctionState { Created = 0, @@ -91,6 +92,23 @@ export const decodeBidderPot = (buffer: Buffer) => { return deserializeUnchecked(AUCTION_SCHEMA, BidderPot, buffer) as BidderPot; }; +export const AuctionDataExtendedParser: AccountParser = ( + pubkey: PublicKey, + account: AccountInfo, +) => ({ + pubkey, + account, + info: decodeAuctionDataExtended(account.data), +}); + +export const decodeAuctionDataExtended = (buffer: Buffer) => { + return deserializeUnchecked( + AUCTION_SCHEMA, + AuctionDataExtended, + buffer, + ) as AuctionDataExtended; +}; + export const BidderMetadataParser: AccountParser = ( pubkey: PublicKey, account: AccountInfo, @@ -323,7 +341,24 @@ export class WinnerLimit { } } -class CreateAuctionArgs { +export interface IPartialCreateAuctionArgs { + /// 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. + endAuctionAt: BN | null; + /// Gap time is how much time after the previous bid where the auction ends. See AuctionData. + auctionGap: BN | null; + /// Token mint for the SPL token used for bidding. + tokenMint: PublicKey; + + priceFloor: PriceFloor; + + tickSize: BN | null; + + gapTickSizePercentage: number | null; +} + +export class CreateAuctionArgs implements IPartialCreateAuctionArgs { instruction: number = 1; /// How many winners are allowed for this auction. See AuctionData. winners: WinnerLimit; @@ -340,6 +375,10 @@ class CreateAuctionArgs { priceFloor: PriceFloor; + tickSize: BN | null; + + gapTickSizePercentage: number | null; + constructor(args: { winners: WinnerLimit; endAuctionAt: BN | null; @@ -348,6 +387,8 @@ class CreateAuctionArgs { authority: PublicKey; resource: PublicKey; priceFloor: PriceFloor; + tickSize: BN | null; + gapTickSizePercentage: number | null; }) { this.winners = args.winners; this.endAuctionAt = args.endAuctionAt; @@ -356,6 +397,8 @@ class CreateAuctionArgs { this.authority = args.authority; this.resource = args.resource; this.priceFloor = args.priceFloor; + this.tickSize = args.tickSize; + this.gapTickSizePercentage = args.gapTickSizePercentage; } } @@ -402,6 +445,8 @@ export const AUCTION_SCHEMA = new Map([ ['authority', 'pubkey'], ['resource', 'pubkey'], ['priceFloor', PriceFloor], + ['tickSize', { kind: 'option', type: 'u64' }], + ['gapTickSizePercentage', { kind: 'option', type: 'u8' }], ], }, ], @@ -541,39 +586,20 @@ export const decodeAuctionData = (buffer: Buffer) => { }; export async function createAuction( - winners: WinnerLimit, - resource: PublicKey, - endAuctionAt: BN | null, - auctionGap: BN | null, - priceFloor: PriceFloor, - tokenMint: PublicKey, - authority: PublicKey, + settings: CreateAuctionArgs, creator: PublicKey, instructions: TransactionInstruction[], ) { const auctionProgramId = programIds().auction; - const data = Buffer.from( - serialize( - AUCTION_SCHEMA, - new CreateAuctionArgs({ - winners, - resource, - endAuctionAt, - auctionGap, - tokenMint, - authority, - priceFloor, - }), - ), - ); + const data = Buffer.from(serialize(AUCTION_SCHEMA, settings)); const auctionKey: PublicKey = ( await findProgramAddress( [ Buffer.from(AUCTION_PREFIX), auctionProgramId.toBuffer(), - resource.toBuffer(), + settings.resource.toBuffer(), ], auctionProgramId, ) @@ -591,7 +617,10 @@ export async function createAuction( isWritable: true, }, { - pubkey: await getAuctionExtended({ auctionProgramId, resource }), + pubkey: await getAuctionExtended({ + auctionProgramId, + resource: settings.resource, + }), isSigner: false, isWritable: true, }, diff --git a/js/packages/common/src/actions/metadata.ts b/js/packages/common/src/actions/metadata.ts index 4a29a0f..f0240c2 100644 --- a/js/packages/common/src/actions/metadata.ts +++ b/js/packages/common/src/actions/metadata.ts @@ -154,17 +154,20 @@ export class ReservationList { /// What supply counter was on master_edition when this reservation was created. supplySnapshot: BN | null; reservations: Reservation[]; + totalReservationSpots: BN; constructor(args: { key: MetadataKey; masterEdition: PublicKey; supplySnapshot: BN | null; reservations: Reservation[]; + totalReservationSpots: BN; }) { this.key = MetadataKey.EditionV1; this.masterEdition = args.masterEdition; this.supplySnapshot = args.supplySnapshot; this.reservations = args.reservations; + this.totalReservationSpots = args.totalReservationSpots; } } @@ -408,6 +411,7 @@ export const METADATA_SCHEMA = new Map([ ['masterEdition', 'pubkey'], ['supplySnapshot', { kind: 'option', type: 'u64' }], ['reservations', [Reservation]], + ['totalReservationSpots', 'u64'], ], }, ], diff --git a/js/packages/web/src/actions/createAuctionManager.ts b/js/packages/web/src/actions/createAuctionManager.ts index 2eb1a61..57d7f07 100644 --- a/js/packages/web/src/actions/createAuctionManager.ts +++ b/js/packages/web/src/actions/createAuctionManager.ts @@ -8,7 +8,6 @@ import { actions, Metadata, ParsedAccount, - WinnerLimit, MasterEdition, SequenceType, sendTransactions, @@ -20,8 +19,8 @@ import { getSafetyDepositBoxAddress, createAssociatedTokenAccountInstruction, sendTransactionWithRetry, - PriceFloor, findProgramAddress, + IPartialCreateAuctionArgs, } from '@oyster/common'; import { AccountLayout, Token } from '@solana/spl-token'; @@ -100,13 +99,10 @@ export async function createAuctionManager( ParsedAccount >, settings: AuctionManagerSettings, - winnerLimit: WinnerLimit, - endAuctionAt: BN, - auctionGap: BN, + auctionSettings: IPartialCreateAuctionArgs, safetyDepositDrafts: SafetyDepositDraft[], participationSafetyDepositDraft: SafetyDepositDraft | undefined, paymentMint: PublicKey, - priceFloor: PriceFloor, ): Promise<{ vault: PublicKey; auction: PublicKey; @@ -136,15 +132,7 @@ export async function createAuctionManager( instructions: makeAuctionInstructions, signers: makeAuctionSigners, auction, - } = await makeAuction( - wallet, - winnerLimit, - vault, - endAuctionAt, - auctionGap, - paymentMint, - priceFloor, - ); + } = await makeAuction(wallet, vault, auctionSettings); let safetyDepositConfigsWithPotentiallyUnsetTokens = await buildSafetyDepositArray( diff --git a/js/packages/web/src/actions/makeAuction.ts b/js/packages/web/src/actions/makeAuction.ts index f38e261..0bcf968 100644 --- a/js/packages/web/src/actions/makeAuction.ts +++ b/js/packages/web/src/actions/makeAuction.ts @@ -2,24 +2,19 @@ import { Keypair, PublicKey, TransactionInstruction } from '@solana/web3.js'; import { utils, actions, - WinnerLimit, - PriceFloor, findProgramAddress, + IPartialCreateAuctionArgs, + CreateAuctionArgs, } from '@oyster/common'; -import BN from 'bn.js'; import { METAPLEX_PREFIX } from '../models/metaplex'; const { AUCTION_PREFIX, createAuction } = actions; // This command makes an auction export async function makeAuction( wallet: any, - winnerLimit: WinnerLimit, vault: PublicKey, - endAuctionAt: BN, - auctionGap: BN, - paymentMint: PublicKey, - priceFloor: PriceFloor, + auctionSettings: IPartialCreateAuctionArgs, ): Promise<{ auction: PublicKey; instructions: TransactionInstruction[]; @@ -47,17 +42,13 @@ export async function makeAuction( ) )[0]; - createAuction( - winnerLimit, - vault, - endAuctionAt, - auctionGap, - priceFloor, - paymentMint, - auctionManagerKey, - wallet.publicKey, - instructions, - ); + const fullSettings = new CreateAuctionArgs({ + ...auctionSettings, + authority: auctionManagerKey, + resource: vault, + }); + + createAuction(fullSettings, wallet.publicKey, instructions); return { instructions, signers, auction: auctionKey }; } diff --git a/js/packages/web/src/components/AuctionCard/index.tsx b/js/packages/web/src/components/AuctionCard/index.tsx index 28291ba..7b96f5e 100644 --- a/js/packages/web/src/components/AuctionCard/index.tsx +++ b/js/packages/web/src/components/AuctionCard/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { Col, Button, InputNumber, Spin } from 'antd'; import { MemoryRouter, Route, Redirect, Link } from 'react-router-dom'; @@ -13,6 +13,10 @@ import { formatTokenAmount, useMint, PriceFloorType, + AuctionDataExtended, + ParsedAccount, + getAuctionExtended, + programIds, } from '@oyster/common'; import { AuctionView, useUserBalance } from '../../hooks'; import { sendPlaceBid } from '../../actions/sendPlaceBid'; @@ -26,9 +30,78 @@ import BN from 'bn.js'; import { Confetti } from '../Confetti'; import { QUOTE_MINT } from '../../constants'; import { LAMPORTS_PER_SOL } from '@solana/web3.js'; +import { useMeta } from '../../contexts'; +import moment from 'moment'; const { useWallet } = contexts.Wallet; +function useGapTickCheck( + value: number | undefined, + gapTick: number | null, + gapTime: number, + auctionView: AuctionView, +): boolean { + return !!useMemo(() => { + if (gapTick && value && gapTime && !auctionView.auction.info.ended()) { + // so we have a gap tick percentage, and a gap tick time, and a value, and we're not ended - are we within gap time? + const now = moment().unix(); + const endedAt = auctionView.auction.info.endedAt; + if (endedAt) { + const ended = endedAt.toNumber(); + if (now > ended) { + const toLamportVal = value * LAMPORTS_PER_SOL; + // Ok, we are in gap time, since now is greater than ended and we're not actually an ended auction yt. + // Check that the bid is at least gapTick % bigger than the next biggest one in the stack. + for ( + let i = auctionView.auction.info.bidState.bids.length - 1; + i > -1; + i-- + ) { + const bid = auctionView.auction.info.bidState.bids[i]; + const expected = bid.amount.toNumber(); + if (expected < toLamportVal) { + const higherExpectedAmount = expected * ((100 + gapTick) / 100); + + return higherExpectedAmount > toLamportVal; + } else if (expected == toLamportVal) { + // If gap tick is set, no way you can bid in this case - you must bid higher. + return true; + } + } + return false; + } else { + return false; + } + } + return false; + } + }, [value, gapTick, gapTime, auctionView]); +} + +function useAuctionExtended( + auctionView: AuctionView, +): ParsedAccount | undefined { + const [auctionExtended, setAuctionExtended] = + useState>(); + const { auctionDataExtended } = useMeta(); + + useMemo(() => { + const fn = async () => { + if (!auctionExtended) { + const PROGRAM_IDS = programIds(); + const extendedKey = await getAuctionExtended({ + auctionProgramId: PROGRAM_IDS.auction, + resource: auctionView.vault.pubkey, + }); + const extendedValue = auctionDataExtended[extendedKey.toBase58()]; + if (extendedValue) setAuctionExtended(extendedValue); + } + }; + fn(); + }, [auctionDataExtended, auctionExtended, setAuctionExtended]); + + return auctionExtended; +} export const AuctionCard = ({ auctionView, style, @@ -74,9 +147,21 @@ export const AuctionCard = ({ winnerIndex, auctionView, ); + const auctionExtended = useAuctionExtended(auctionView); const eligibleForAnything = winnerIndex !== null || eligibleForOpenEdition; const gapTime = (auctionView.auction.info.auctionGap?.toNumber() || 0) / 60; + const gapTick = auctionExtended + ? auctionExtended.info.gapTickSizePercentage + : 0; + const tickSize = auctionExtended ? auctionExtended.info.tickSize : 0; + const tickSizeInvalid = !!( + tickSize && + value && + (value * LAMPORTS_PER_SOL) % tickSize.toNumber() != 0 + ); + + const gapBidInvalid = useGapTickCheck(value, gapTick, gapTime, auctionView); return (
@@ -270,13 +355,32 @@ export const AuctionCard = ({ }} > Bids placed in the last {gapTime} minutes will extend - bidding for another {gapTime} minutes. + bidding for another {gapTime} minutes beyond the point in + time that bid was made.{' '} + {gapTick && ( + + Additionally, once the official auction end time has + passed, only bids {gapTick}% larger than an existing + bid will be accepted. + + )}
)}

+ {tickSizeInvalid && tickSize && ( + + Tick size is ◎{tickSize.toNumber() / LAMPORTS_PER_SOL}. + + )} + {gapBidInvalid && ( + + Your bid needs to be at least {gapTick}% larger than an + existing bid during gap periods to be eligible. + + )}
>; auctionManagersByAuction: Record>; auctions: Record>; + auctionDataExtended: Record>; vaults: Record>; store: ParsedAccount | null; bidderMetadataByAuctionAndBidder: Record< @@ -87,29 +91,38 @@ interface MetaState { const { MetadataKey } = actions; -type UpdateStateValueFunc = (prop: keyof MetaState, key: string, value: any) => void; +type UpdateStateValueFunc = ( + prop: keyof MetaState, + key: string, + value: any, +) => void; export interface MetaContextState extends MetaState { isLoading: boolean; } -const isMetadataPartOfStore = (m: ParsedAccount , store: ParsedAccount | null, whitelistedCreatorsByCreator: Record< - string, - ParsedAccount ->) => { - if(!m?.info?.data?.creators) { +const isMetadataPartOfStore = ( + m: ParsedAccount, + store: ParsedAccount | null, + whitelistedCreatorsByCreator: Record< + string, + ParsedAccount + >, +) => { + if (!m?.info?.data?.creators) { return false; } - return m.info.data.creators.findIndex( + return ( + m.info.data.creators.findIndex( c => c.verified && store && store.info && (store.info.public || - whitelistedCreatorsByCreator[c.address.toBase58()]?.info - ?.activated), - ) >= 0; -} + whitelistedCreatorsByCreator[c.address.toBase58()]?.info?.activated), + ) >= 0 + ); +}; const MetaContext = React.createContext({ metadata: [], @@ -121,6 +134,7 @@ const MetaContext = React.createContext({ editions: {}, auctionManagersByAuction: {}, auctions: {}, + auctionDataExtended: {}, vaults: {}, store: null, isLoading: false, @@ -140,13 +154,20 @@ export function MetaProvider({ children = null as any }) { metadata: [] as Array>, metadataByMint: {} as Record>, masterEditions: {} as Record>, - masterEditionsByPrintingMint: {} as Record>, - masterEditionsByOneTimeAuthMint: {} as Record>, + masterEditionsByPrintingMint: {} as Record< + string, + ParsedAccount + >, + masterEditionsByOneTimeAuthMint: {} as Record< + string, + ParsedAccount + >, metadataByMasterEdition: {} as any, editions: {}, auctionManagersByAuction: {}, bidRedemptions: {}, auctions: {}, + auctionDataExtended: {}, vaults: {}, payoutTickets: {}, store: null as ParsedAccount | null, @@ -154,7 +175,7 @@ export function MetaProvider({ children = null as any }) { bidderMetadataByAuctionAndBidder: {}, bidderPotsByAuctionAndBidder: {}, safetyDepositBoxesByVaultAndIndex: {}, - }) + }); const [isLoading, setIsLoading] = useState(true); @@ -162,11 +183,11 @@ export function MetaProvider({ children = null as any }) { async metadataByMint => { try { const m = await queryExtendedMetadata(connection, metadataByMint); - setState((current) => ({ + setState(current => ({ ...current, metadata: m.metadata, metadataByMint: m.mintToMetadata, - })) + })); } catch (er) { console.error(er); } @@ -202,6 +223,7 @@ export function MetaProvider({ children = null as any }) { auctionManagersByAuction: {}, bidRedemptions: {}, auctions: {}, + auctionDataExtended: {}, vaults: {}, payoutTickets: {}, store: null, @@ -214,11 +236,11 @@ export function MetaProvider({ children = null as any }) { const updateTemp = (prop: keyof MetaState, key: string, value: any) => { if (prop === 'store') { tempCache[prop] = value; - } else if(tempCache[prop]) { + } else if (tempCache[prop]) { const bucket = tempCache[prop] as any; bucket[key] = value as any; } - } + }; for (let i = 0; i < accounts.length; i++) { let account = accounts[i]; @@ -226,18 +248,25 @@ export function MetaProvider({ children = null as any }) { processAuctions(account, updateTemp); processMetaData(account, updateTemp); - await processMetaplexAccounts( - account, - updateTemp, - ); + await processMetaplexAccounts(account, updateTemp); } - const values = Object.values(tempCache.metadataByMint) as ParsedAccount[]; + const values = Object.values( + tempCache.metadataByMint, + ) as ParsedAccount[]; for (let i = 0; i < values.length; i++) { const metadata = values[i]; - if(isMetadataPartOfStore(metadata, tempCache.store, tempCache.whitelistedCreatorsByCreator)) { + if ( + isMetadataPartOfStore( + metadata, + tempCache.store, + tempCache.whitelistedCreatorsByCreator, + ) + ) { await metadata.info.init(); - tempCache.metadataByMasterEdition[metadata.info?.masterEdition?.toBase58() || ''] = metadata; + tempCache.metadataByMasterEdition[ + metadata.info?.masterEdition?.toBase58() || '' + ] = metadata; } else { delete tempCache.metadataByMint[metadata.info.mint.toBase58() || '']; } @@ -247,7 +276,7 @@ export function MetaProvider({ children = null as any }) { tempCache.metadata = values; setState({ ...tempCache, - }) + }); setIsLoading(false); console.log('------->set finished'); @@ -258,36 +287,34 @@ export function MetaProvider({ children = null as any }) { return () => { dispose(); }; - }, [ - connection, - setState, - updateMints, - env, - ]); + }, [connection, setState, updateMints, env]); - const updateStateValue = useMemo(() => (prop: keyof MetaState, key: string, value: any) => { - setState((current) => { - if (prop === 'store') { - return { - ...current, - [prop]: value, + const updateStateValue = useMemo( + () => (prop: keyof MetaState, key: string, value: any) => { + setState(current => { + if (prop === 'store') { + return { + ...current, + [prop]: value, + }; + } else { + return { + ...current, + [prop]: { + ...current[prop], + [key]: value, + }, + }; } - } else { - return ({ - ...current, - [prop]: { - ...current[prop], - [key]: value - } - }); - } - }); - }, [setState]); + }); + }, + [setState], + ); const store = state.store; const whitelistedCreatorsByCreator = state.whitelistedCreatorsByCreator; useEffect(() => { - if(isLoading) { + if (isLoading) { return; } @@ -340,7 +367,10 @@ export function MetaProvider({ children = null as any }) { updateStateValue, ); - if(result && isMetadataPartOfStore(result, store, whitelistedCreatorsByCreator)) { + if ( + result && + isMetadataPartOfStore(result, store, whitelistedCreatorsByCreator) + ) { await result.info.init(); setState((data) => ({ ...data, @@ -370,7 +400,7 @@ export function MetaProvider({ children = null as any }) { pubkey, account: info.accountInfo, }, - updateStateValue + updateStateValue, ); }, ); @@ -430,9 +460,12 @@ export function MetaProvider({ children = null as any }) { masterEditions: state.masterEditions, auctionManagersByAuction: state.auctionManagersByAuction, auctions: state.auctions, + auctionDataExtended: state.auctionDataExtended, metadataByMint: state.metadataByMint, - safetyDepositBoxesByVaultAndIndex: state.safetyDepositBoxesByVaultAndIndex, - bidderMetadataByAuctionAndBidder: state.bidderMetadataByAuctionAndBidder, + safetyDepositBoxesByVaultAndIndex: + state.safetyDepositBoxesByVaultAndIndex, + bidderMetadataByAuctionAndBidder: + state.bidderMetadataByAuctionAndBidder, bidderPotsByAuctionAndBidder: state.bidderPotsByAuctionAndBidder, vaults: state.vaults, bidRedemptions: state.bidRedemptions, @@ -525,6 +558,21 @@ const processAuctions = ( // ignore errors // add type as first byte for easier deserialization } + + try { + if (a.account.data.length === MAX_AUCTION_DATA_EXTENDED_SIZE) { + const account = cache.add( + a.pubkey, + a.account, + AuctionDataExtendedParser, + false, + ) as ParsedAccount; + setter('auctionDataExtended', a.pubkey.toBase58(), account); + } + } catch { + // ignore errors + // add type as first byte for easier deserialization + } try { if (a.account.data.length === BIDDER_METADATA_LEN) { const account = cache.add( @@ -536,9 +584,10 @@ const processAuctions = ( setter( 'bidderMetadataByAuctionAndBidder', account.info.auctionPubkey.toBase58() + - '-' + - account.info.bidderPubkey.toBase58(), - account); + '-' + + account.info.bidderPubkey.toBase58(), + account, + ); } } catch { // ignore errors @@ -555,9 +604,10 @@ const processAuctions = ( setter( 'bidderPotsByAuctionAndBidder', account.info.auctionAct.toBase58() + - '-' + - account.info.bidderAct.toBase58(), - account); + '-' + + account.info.bidderAct.toBase58(), + account, + ); } } catch { // ignore errors @@ -591,7 +641,11 @@ const processMetaplexAccounts = async ( account: a.account, info: auctionManager, }; - setter('auctionManagersByAuction', auctionManager.auction.toBase58(), account); + setter( + 'auctionManagersByAuction', + auctionManager.auction.toBase58(), + account, + ); } } } else if (a.account.data[0] === MetaplexKey.BidRedemptionTicketV1) { @@ -645,7 +699,11 @@ const processMetaplexAccounts = async ( account.info.image = nameInfo.image; account.info.twitter = nameInfo.twitter; } - setter('whitelistedCreatorsByCreator', whitelistedCreator.address.toBase58(), account); + setter( + 'whitelistedCreatorsByCreator', + whitelistedCreator.address.toBase58(), + account, + ); } } } catch { @@ -658,7 +716,8 @@ const processMetaData = ( meta: PublicKeyAndAccount, setter: UpdateStateValueFunc, ) => { - if (meta.account.owner.toBase58() !== programIds().metadata.toBase58()) return; + if (meta.account.owner.toBase58() !== programIds().metadata.toBase58()) + return; try { if (meta.account.data[0] === MetadataKey.MetadataV1) { @@ -692,8 +751,16 @@ const processMetaData = ( info: masterEdition, }; setter('masterEditions', meta.pubkey.toBase58(), account); - setter('masterEditionsByPrintingMint', masterEdition.printingMint.toBase58(), account); - setter('masterEditionsByOneTimeAuthMint', masterEdition.oneTimePrintingAuthorizationMint.toBase58(), account); + setter( + 'masterEditionsByPrintingMint', + masterEdition.printingMint.toBase58(), + account, + ); + setter( + 'masterEditionsByOneTimeAuthMint', + masterEdition.oneTimePrintingAuthorizationMint.toBase58(), + account, + ); } } catch { // ignore errors @@ -717,7 +784,8 @@ const processVaultData = ( setter( 'safetyDepositBoxesByVaultAndIndex', safetyDeposit.vault.toBase58() + '-' + safetyDeposit.order, - account); + account, + ); } else if (a.account.data[0] === VaultKey.VaultV1) { const vault = decodeVault(a.account.data); const account: ParsedAccount = { @@ -726,10 +794,7 @@ const processVaultData = ( info: vault, }; - setter( - 'vaults', - a.pubkey.toBase58(), - account); + setter('vaults', a.pubkey.toBase58(), account); } } catch { // ignore errors diff --git a/js/packages/web/src/views/auctionCreate/index.tsx b/js/packages/web/src/views/auctionCreate/index.tsx index 993f0e7..4e87d21 100644 --- a/js/packages/web/src/views/auctionCreate/index.tsx +++ b/js/packages/web/src/views/auctionCreate/index.tsx @@ -30,6 +30,7 @@ import { Creator, PriceFloor, PriceFloorType, + IPartialCreateAuctionArgs, } from '@oyster/common'; import { Connection, @@ -305,14 +306,29 @@ export const AuctionCreateView = () => { console.log('Tiered settings', settings); } + const auctionSettings: IPartialCreateAuctionArgs = { + winners: winnerLimit, + endAuctionAt: new BN((attributes.auctionDuration || 0) * 60), // endAuctionAt is actually auction duration, poorly named, in seconds + auctionGap: new BN((attributes.gapTime || 0) * 60), + priceFloor: new PriceFloor({ + type: attributes.priceFloor + ? PriceFloorType.Minimum + : PriceFloorType.None, + minPrice: new BN((attributes.priceFloor || 0) * LAMPORTS_PER_SOL), + }), + tokenMint: QUOTE_MINT, + gapTickSizePercentage: attributes.tickSizeEndingPhase || null, + tickSize: attributes.priceTick + ? new BN(attributes.priceTick * LAMPORTS_PER_SOL) + : null, + }; + const _auctionObj = await createAuctionManager( connection, wallet, whitelistedCreatorsByCreator, settings, - winnerLimit, - new BN((attributes.auctionDuration || 0) * 60), // endAuctionAt is actually auction duration, poorly named, in seconds - new BN((attributes.gapTime || 0) * 60), + auctionSettings, attributes.category === AuctionCategory.Open ? [] : attributes.category !== AuctionCategory.Tiered @@ -322,12 +338,6 @@ export const AuctionCreateView = () => { ? attributes.items[0] : attributes.participationNFT, QUOTE_MINT, - new PriceFloor({ - type: attributes.priceFloor - ? PriceFloorType.Minimum - : PriceFloorType.None, - minPrice: new BN((attributes.priceFloor || 0) * LAMPORTS_PER_SOL), - }), ); setAuctionObj(_auctionObj); }; diff --git a/rust/Cargo.lock b/rust/Cargo.lock index fc5f079..85f28dd 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -40,6 +40,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "anyhow" version = "1.0.40" @@ -113,6 +122,18 @@ dependencies = [ "serde", ] +[[package]] +name = "base32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "base64" version = "0.12.3" @@ -282,6 +303,12 @@ dependencies = [ "iovec", ] +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + [[package]] name = "bytes" version = "1.0.1" @@ -311,12 +338,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.49" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e450b8da92aa6f274e7c6437692f9f2ce6d701fb73bacfcf87897b3f89a4c20e" +checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" dependencies = [ "jobserver", - "num_cpus", ] [[package]] @@ -347,13 +373,28 @@ dependencies = [ [[package]] name = "chrono-humanize" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8164ae3089baf04ff71f32aeb70213283dcd236dce8bc976d00b17a458f5f71c" +checksum = "2eddc119501d583fd930cb92144e605f44e0252c38dd89d9247fffa1993375cb" dependencies = [ "chrono", ] +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -376,12 +417,60 @@ dependencies = [ "unreachable", ] +[[package]] +name = "console" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c0994e656bba7b922d8dd1245db90672ffb701e684e45be58f20719d69abc5a" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "regex", + "terminal_size", + "termios", + "unicode-width", + "winapi 0.3.9", + "winapi-util", +] + +[[package]] +name = "console" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "regex", + "terminal_size", + "unicode-width", + "winapi 0.3.9", +] + [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + [[package]] name = "cpufeatures" version = "0.1.4" @@ -605,6 +694,17 @@ dependencies = [ "syn 1.0.72", ] +[[package]] +name = "dialoguer" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aa86af7b19b40ef9cbef761ed411a49f0afa06b7b6dcd3dfe2f96a3c546138" +dependencies = [ + "console 0.11.3", + "lazy_static", + "tempfile", +] + [[package]] name = "digest" version = "0.8.1" @@ -632,6 +732,33 @@ dependencies = [ "walkdir", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + [[package]] name = "ed25519" version = "1.1.1" @@ -688,6 +815,12 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.28" @@ -787,6 +920,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.0.1" @@ -797,12 +945,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" - [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -1046,6 +1188,17 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hidapi" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e07da7e8614133e88b3a93b7352eb3729e3ccd82d5ab661adf23bef1761bf8" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "hmac" version = "0.7.1" @@ -1056,6 +1209,16 @@ dependencies = [ "digest 0.8.1", ] +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + [[package]] name = "hmac" version = "0.9.0" @@ -1144,7 +1307,7 @@ dependencies = [ "httpdate", "itoa", "pin-project", - "socket2", + "socket2 0.4.0", "tokio 1.6.1", "tower-service", "tracing", @@ -1187,6 +1350,27 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indicatif" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" +dependencies = [ + "console 0.14.1", + "lazy_static", + "number_prefix", + "regex", +] + +[[package]] +name = "input_buffer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" +dependencies = [ + "bytes 0.5.6", +] + [[package]] name = "instant" version = "0.1.9" @@ -1226,38 +1410,6 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" -[[package]] -name = "jemalloc-ctl" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c502a5ff9dd2924f1ed32ba96e3b65735d837b4bfd978d3161b1702e66aca4b7" -dependencies = [ - "jemalloc-sys", - "libc", - "paste", -] - -[[package]] -name = "jemalloc-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45" -dependencies = [ - "cc", - "fs_extra", - "libc", -] - -[[package]] -name = "jemallocator" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43ae63fcfc45e99ab3d1b29a46782ad679e98436c3169d15a167a1108a724b69" -dependencies = [ - "jemalloc-sys", - "libc", -] - [[package]] name = "jobserver" version = "0.1.22" @@ -1276,6 +1428,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpc-core" +version = "17.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4467ab6dfa369b69e52bd0692e480c4d117410538526a57a304a0f2250fd95e" +dependencies = [ + "futures 0.3.15", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "keccak" version = "0.1.0" @@ -1333,6 +1500,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + [[package]] name = "lock_api" version = "0.3.4" @@ -1485,6 +1658,24 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "native-tls" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "net2" version = "0.2.37" @@ -1496,6 +1687,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "nix" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -1578,6 +1781,12 @@ dependencies = [ "syn 1.0.72", ] +[[package]] +name = "number_prefix" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a" + [[package]] name = "object" version = "0.23.0" @@ -1589,6 +1798,9 @@ name = "once_cell" version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +dependencies = [ + "parking_lot 0.11.1", +] [[package]] name = "opaque-debug" @@ -1602,6 +1814,39 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549430950c79ae24e6d02e0b7404534ecf311d94cc9f861e9e4020187d13d885" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" + +[[package]] +name = "openssl-sys" +version = "0.9.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a7907e3bfa08bb85105209cdfcb6c63d109f8f6c1ed6ca318fff5c1853fbc1d" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ouroboros" version = "0.5.1" @@ -1635,6 +1880,16 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "parking_lot" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +dependencies = [ + "lock_api 0.3.4", + "parking_lot_core 0.7.2", +] + [[package]] name = "parking_lot" version = "0.11.1" @@ -1661,6 +1916,20 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "parking_lot_core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "libc", + "redox_syscall 0.1.57", + "smallvec 1.6.1", + "winapi 0.3.9", +] + [[package]] name = "parking_lot_core" version = "0.8.3" @@ -1676,22 +1945,12 @@ dependencies = [ ] [[package]] -name = "paste" -version = "0.1.18" +name = "pbkdf2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" dependencies = [ - "paste-impl", - "proc-macro-hack", -] - -[[package]] -name = "paste-impl" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" -dependencies = [ - "proc-macro-hack", + "crypto-mac 0.8.0", ] [[package]] @@ -1709,6 +1968,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + [[package]] name = "pin-project" version = "1.0.7" @@ -1956,6 +2224,16 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom 0.2.3", + "redox_syscall 0.2.8", +] + [[package]] name = "regex" version = "1.5.4" @@ -2020,32 +2298,48 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.12" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", - "lazy_static", "libc", + "once_cell", "spin", "untrusted", "web-sys", "winapi 0.3.9", ] +[[package]] +name = "rpassword" +version = "4.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99371657d3c8e4d816fb6221db98fa408242b0b53bac08f8676a41f8554fe99f" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "rustc-demangle" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "410f7acf3cb3a44527c5d9546bad4bf4e6c460915d5f9f2fc524498bfe8f70ce" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", ] [[package]] @@ -2082,6 +2376,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi 0.3.9", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -2118,13 +2422,45 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4effb91b4b8b6fb7732e670b6cee160278ff8e6bf485c7805d9e319d76e284" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.2", ] [[package]] @@ -2133,6 +2469,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.126" @@ -2185,6 +2530,30 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" +dependencies = [ + "dtoa", + "linked-hash-map", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + [[package]] name = "sha2" version = "0.8.2" @@ -2258,6 +2627,17 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "winapi 0.3.9", +] + [[package]] name = "socket2" version = "0.4.0" @@ -2269,10 +2649,33 @@ dependencies = [ ] [[package]] -name = "solana-banks-client" -version = "1.6.11" +name = "solana-account-decoder" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9977706df3b1d69e91eb4d0c53d60bd262f96de4116b606afea8728d33e8072e" +checksum = "e49c9e97fbaa91220924064f23ce367c02af1a3bc8cc514f84168b47cbb075c7" +dependencies = [ + "Inflector", + "base64 0.12.3", + "bincode", + "bs58", + "bv", + "lazy_static", + "serde", + "serde_derive", + "serde_json", + "solana-config-program", + "solana-sdk", + "solana-vote-program", + "spl-token", + "thiserror", + "zstd", +] + +[[package]] +name = "solana-banks-client" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "570ed9c8e78fabbd2f93140f731c9361b7aa6292341e03811e5b6d3ed2470c5a" dependencies = [ "bincode", "borsh", @@ -2289,9 +2692,9 @@ dependencies = [ [[package]] name = "solana-banks-interface" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0066bd7c2655141b095cfe25afc1aa76879254e0fe02ac212b858449cbddf275" +checksum = "f3b253e4d53eef373ab186829988a9ad4f7bfce886cf0e1fade1ad866c294df2" dependencies = [ "mio 0.7.11", "serde", @@ -2301,9 +2704,9 @@ dependencies = [ [[package]] name = "solana-banks-server" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "950c7a4061aaf4f79293caf06c12f639a4a55cfd5f3ec9f5323be869e4426ef9" +checksum = "a350a47e84f4e28a28e985e96150a222559b0e23f71a3b34760b7463091d0877" dependencies = [ "bincode", "futures 0.3.15", @@ -2321,9 +2724,9 @@ dependencies = [ [[package]] name = "solana-bpf-loader-program" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba7fcf2994963a758c97b6c9d205036ea9b1baf84227398f9bbc47d25877e00" +checksum = "7305c923737fc515d88454dd991b8c21c5e9270f72db3cd52da888d4d233fe07" dependencies = [ "bincode", "byteorder", @@ -2340,10 +2743,75 @@ dependencies = [ ] [[package]] -name = "solana-config-program" -version = "1.6.11" +name = "solana-clap-utils" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9341b8d8c32b970b6aa3e4a58795f5120154b3896b7231b830a5a9a13bc8083" +checksum = "29dca103fa63d4fa564c719d2f043feb4a116722362cbcbf027d4404fc65e7b3" +dependencies = [ + "chrono", + "clap", + "rpassword", + "solana-remote-wallet", + "solana-sdk", + "thiserror", + "tiny-bip39", + "uriparse", + "url", +] + +[[package]] +name = "solana-cli-config" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f22f4acf359df9e859878fc456bb98cfd3d008ee239a6300259c439a2356d1c9" +dependencies = [ + "dirs-next", + "lazy_static", + "serde", + "serde_derive", + "serde_yaml", + "url", +] + +[[package]] +name = "solana-client" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a149653548051cdcfdfb3494ad46a9810e647cbf004b0f65dccafd92eec152" +dependencies = [ + "base64 0.13.0", + "bincode", + "bs58", + "clap", + "indicatif", + "jsonrpc-core", + "log", + "net2", + "rayon", + "reqwest", + "semver 0.11.0", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-clap-utils", + "solana-faucet", + "solana-net-utils", + "solana-sdk", + "solana-transaction-status", + "solana-version", + "solana-vote-program", + "thiserror", + "tokio 1.6.1", + "tungstenite", + "url", +] + +[[package]] +name = "solana-config-program" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "056a9300b5e2665d0f6cf1dfbc5f9778ec616485adbe35af01fec234e1f9b288" dependencies = [ "bincode", "chrono", @@ -2356,9 +2824,9 @@ dependencies = [ [[package]] name = "solana-crate-features" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d62c8e8a526b62e3cefe51f079cc48585a5026d1d5a86a99ce38d782273027f" +checksum = "887ff45393b195ab7d93cdc94511c7cc2319a379b659e8008de77028c2326846" dependencies = [ "backtrace", "bytes 0.4.12", @@ -2371,6 +2839,7 @@ dependencies = [ "rand_chacha 0.2.2", "regex-syntax", "reqwest", + "ring", "serde", "syn 0.15.44", "syn 1.0.72", @@ -2379,10 +2848,33 @@ dependencies = [ ] [[package]] -name = "solana-frozen-abi" -version = "1.6.11" +name = "solana-faucet" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439fb97ca6bf4098973413e806071116a0d87dcb197c0c2ae01f71a1588d6dcc" +checksum = "981359d3f0f420dfa6d5eee4d33357d619817425a64ad090f2f88e521c9a24bd" +dependencies = [ + "bincode", + "byteorder", + "clap", + "log", + "serde", + "serde_derive", + "solana-clap-utils", + "solana-cli-config", + "solana-logger", + "solana-metrics", + "solana-sdk", + "solana-version", + "spl-memo", + "thiserror", + "tokio 1.6.1", +] + +[[package]] +name = "solana-frozen-abi" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc00a9f7c3eb2fb8687d34ce6d8672fbf7bd8f67002a5f75ccd6f6c4e8cd8a91" dependencies = [ "bs58", "bv", @@ -2400,11 +2892,10 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4b1844deb909101f83735504f41f7ce539fe0c448fa52bda1a7d2233aaefc3" +checksum = "bc381a29ab68515e69dcfad633ab78dd98d83c0b959c2cae9a9a98df6e265acf" dependencies = [ - "lazy_static", "proc-macro2 1.0.27", "quote 1.0.9", "rustc_version", @@ -2413,9 +2904,9 @@ dependencies = [ [[package]] name = "solana-logger" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01545ecd73ca356dd3d31db303e0ecc1989b67a1561dce0c8d8ae2d4c536562d" +checksum = "62f8e4921602f61681d8d29d2606d4f8e1c848d4f6b9964813bfc1b457dfd7ce" dependencies = [ "env_logger", "lazy_static", @@ -2424,12 +2915,10 @@ dependencies = [ [[package]] name = "solana-measure" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ddf22ce1f47ebc723bceec387100a52a27ed6a807092ac90ecc70189bb10ce2" +checksum = "958b8dab77246d9c71c78a4e9c5c31a1092dafb70294ce0334acf572442b9d84" dependencies = [ - "jemalloc-ctl", - "jemallocator", "log", "solana-metrics", "solana-sdk", @@ -2437,9 +2926,9 @@ dependencies = [ [[package]] name = "solana-metrics" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc2aa796e88a5fc8628474a55929cc3d8c5dd2d10900cd591917eac22de86a5a" +checksum = "77968c10909ef49d7a3cccc3ca8de9415e8750411ad917889ef8ba9ae8692d94" dependencies = [ "env_logger", "gethostname", @@ -2450,10 +2939,32 @@ dependencies = [ ] [[package]] -name = "solana-program" -version = "1.6.11" +name = "solana-net-utils" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ac6cafe3bb91070644839600e65b185292e285abc31ac626ddab6a79c0cc18e" +checksum = "9de765ffc78c8068b9efce0ff08b399a94105b004588c90a4b0835995dfc4712" +dependencies = [ + "bincode", + "clap", + "log", + "nix", + "rand 0.7.3", + "serde", + "serde_derive", + "socket2 0.3.19", + "solana-clap-utils", + "solana-logger", + "solana-sdk", + "solana-version", + "tokio 1.6.1", + "url", +] + +[[package]] +name = "solana-program" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f9c454274436aac77286369e35835fafa1f79d1da1c4b7a1c662b2c41705f77" dependencies = [ "bincode", "blake3", @@ -2485,9 +2996,9 @@ dependencies = [ [[package]] name = "solana-program-test" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5c6637e910327d7dbcd23b121437eb7bc64f98bc1080bc50db944f015f21d43" +checksum = "4cd5922e4a9a7437a19ff5d79f60bac64ea2636a365586cb3511f4d562a20cf7" dependencies = [ "async-trait", "base64 0.12.3", @@ -2511,19 +3022,40 @@ dependencies = [ [[package]] name = "solana-rayon-threadlimit" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e0a2dbe5f36a32c5f4c366bde65ac10f516693f0342da36dd6b3daf012ba50f" +checksum = "d837161f598afd30c445a7f9c39d640cd4cdf362c30062ed0831b71daaf63a45" dependencies = [ "lazy_static", "num_cpus", ] [[package]] -name = "solana-runtime" -version = "1.6.11" +name = "solana-remote-wallet" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4ca322a5fbcc3b647de773d8b8f30659b0b956f0226ec1a8fadc08159cc8be" +checksum = "24087b3048534e154db68c4cdd8233f3a35dd6f860d97e5ad1008c44e0d311f5" +dependencies = [ + "base32", + "console 0.14.1", + "dialoguer", + "hidapi", + "log", + "num-derive", + "num-traits", + "parking_lot 0.10.2", + "qstring", + "semver 0.9.0", + "solana-sdk", + "thiserror", + "uriparse", +] + +[[package]] +name = "solana-runtime" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224e6ef5ea772d9636493dd4fd196cf3b167ff73b7d54e7f591b2f2d452c70d6" dependencies = [ "arrayref", "bincode", @@ -2572,9 +3104,9 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a978bfd23136d5af402c5b932ffc6cb5a9a047d3dddc2522b729b270b8df546" +checksum = "339cc1a9d437ae2f5f05d2d678c8f6d19ea6cf57cbdf719b7726156d294487bd" dependencies = [ "assert_matches", "bincode", @@ -2596,7 +3128,7 @@ dependencies = [ "memmap2", "num-derive", "num-traits", - "pbkdf2", + "pbkdf2 0.6.0", "qstring", "rand 0.7.3", "rand_chacha 0.2.2", @@ -2621,9 +3153,9 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9205b47aa8e2474d014033375dea8734f14038fc860feed2ef654ee17c73353" +checksum = "85ee9c0af66098ec40bf9012b7910c8cdb1ce8b95fc9fad90e6a0cbe692a48fe" dependencies = [ "bs58", "proc-macro2 1.0.27", @@ -2634,9 +3166,9 @@ dependencies = [ [[package]] name = "solana-secp256k1-program" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eade5b09385c7139a8aab6420d76087a4ef6e47a03343cb70e77a50d6567eb62" +checksum = "2666ffed520bff7d0eb6747da156435def7f97341634e72af39fefc0d27496f1" dependencies = [ "bincode", "digest 0.9.0", @@ -2649,9 +3181,9 @@ dependencies = [ [[package]] name = "solana-stake-program" -version = "1.6.11" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a42ec32e879eb261fa847f659cd5631111c7a4b824bc29d90f4d9d497b93774" +checksum = "42f9e0309f95b3160c4961f11a3940d98126a86a7410b04bf88ff79448984257" dependencies = [ "bincode", "log", @@ -2670,10 +3202,50 @@ dependencies = [ ] [[package]] -name = "solana-vote-program" -version = "1.6.11" +name = "solana-transaction-status" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c69608668f8738cbaa3a25d15943dd202448b6eafe5d480326a91bd87f0eac" +checksum = "952083e52e835be5c0f10707d9019df84f3be5f0c524391848d4c8c7e05151da" +dependencies = [ + "Inflector", + "base64 0.12.3", + "bincode", + "bs58", + "lazy_static", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-runtime", + "solana-sdk", + "solana-vote-program", + "spl-associated-token-account", + "spl-memo", + "spl-token", + "thiserror", +] + +[[package]] +name = "solana-version" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "797f12524e5ea25d18f345cd4c34a4839dea83e979a9ccb43a02b486ba9d913c" +dependencies = [ + "log", + "rustc_version", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-logger", + "solana-sdk", +] + +[[package]] +name = "solana-vote-program" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8583f429404acb3ab8eb255cd9cb1c65496dda7a8be3d73058d63b7d26cc1c3d" dependencies = [ "bincode", "log", @@ -2692,9 +3264,9 @@ dependencies = [ [[package]] name = "solana_rbpf" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debbc13545a1d972955a4fd3014e7c9d6d81da16c3626ee5f64bf3aa619548f8" +checksum = "7c1c5bdfa63c68d848d95024c7f4335bae4b1917f7df2e48e2d945f4664a8b45" dependencies = [ "byteorder", "combine", @@ -2703,6 +3275,7 @@ dependencies = [ "libc", "log", "rand 0.7.3", + "rustc-demangle", "scroll", "thiserror", "time", @@ -2714,6 +3287,16 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spl-associated-token-account" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4adc47eebe5d2b662cbaaba1843719c28a67e5ec5d0460bc3ca60900a51f74e2" +dependencies = [ + "solana-program", + "spl-token", +] + [[package]] name = "spl-auction" version = "0.0.1" @@ -2728,6 +3311,32 @@ dependencies = [ "thiserror", ] +[[package]] +name = "spl-auction-test-client" +version = "0.1.0" +dependencies = [ + "bincode", + "borsh", + "clap", + "rand 0.8.3", + "solana-clap-utils", + "solana-cli-config", + "solana-client", + "solana-program", + "solana-sdk", + "spl-auction", + "spl-token", +] + +[[package]] +name = "spl-memo" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" +dependencies = [ + "solana-program", +] + [[package]] name = "spl-metaplex" version = "0.0.1" @@ -2744,6 +3353,29 @@ dependencies = [ "thiserror", ] +[[package]] +name = "spl-metaplex-test-client" +version = "0.1.0" +dependencies = [ + "arrayref", + "bincode", + "borsh", + "clap", + "serde", + "serde_derive", + "serde_json", + "solana-clap-utils", + "solana-cli-config", + "solana-client", + "solana-program", + "solana-sdk", + "spl-auction", + "spl-metaplex", + "spl-token", + "spl-token-metadata", + "spl-token-vault", +] + [[package]] name = "spl-token" version = "3.1.1" @@ -2770,6 +3402,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "spl-token-metadata-test-client" +version = "0.1.0" +dependencies = [ + "bincode", + "borsh", + "clap", + "solana-clap-utils", + "solana-cli-config", + "solana-client", + "solana-program", + "solana-sdk", + "spl-token", + "spl-token-metadata", +] + [[package]] name = "spl-token-vault" version = "0.0.1" @@ -2782,6 +3430,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "spl-token-vault-test-client" +version = "0.1.0" +dependencies = [ + "bincode", + "borsh", + "clap", + "solana-clap-utils", + "solana-cli-config", + "solana-client", + "solana-program", + "solana-sdk", + "spl-token", + "spl-token-vault", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -2794,6 +3458,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "subtle" version = "1.0.0" @@ -2912,6 +3582,34 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.25" @@ -2943,6 +3641,24 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "tiny-bip39" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e44c4759bae7f1032e286a7ef990bd9ed23fe831b7eeba0beb97484c2e59b8" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.5", + "thiserror", + "unicode-normalization", + "zeroize", +] + [[package]] name = "tinyvec" version = "1.2.0" @@ -3264,12 +3980,38 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "tungstenite" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfea31758bf674f990918962e8e5f07071a3161bd7c4138ed23e416e1ac4264e" +dependencies = [ + "base64 0.11.0", + "byteorder", + "bytes 0.5.6", + "http", + "httparse", + "input_buffer", + "log", + "native-tls", + "rand 0.7.3", + "sha-1", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "unicode-bidi" version = "0.3.5" @@ -3288,6 +4030,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "unicode-xid" version = "0.1.0" @@ -3337,6 +4085,24 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.3" @@ -3550,6 +4316,15 @@ dependencies = [ "libc", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "zeroize" version = "1.3.0" diff --git a/rust/auction/program/src/errors.rs b/rust/auction/program/src/errors.rs index c4cb3ad..9152ccf 100644 --- a/rust/auction/program/src/errors.rs +++ b/rust/auction/program/src/errors.rs @@ -122,6 +122,18 @@ pub enum AuctionError { /// Data type mismatch #[error("Data type mismatch")] DataTypeMismatch, + + /// Bid must be multiple of tick size + #[error("Bid must be multiple of tick size")] + BidMustBeMultipleOfTickSize, + + /// During the gap window, gap between next lowest bid must be of a certain percentage + #[error("During the gap window, gap between next lowest bid must be of a certain percentage")] + GapBetweenBidsTooSmall, + + /// Gap tick size percentage must be between 0 and 100 + #[error("Gap tick size percentage must be between 0 and 100")] + InvalidGapTickSizePercentage, } impl PrintProgramError for AuctionError { diff --git a/rust/auction/program/src/processor.rs b/rust/auction/program/src/processor.rs index c756b36..f873762 100644 --- a/rust/auction/program/src/processor.rs +++ b/rust/auction/program/src/processor.rs @@ -134,10 +134,10 @@ impl AuctionData { (Some(end), Some(gap)) => { // Check if the bid is within the gap between the last bidder. if let Some(last) = self.last_bid { - let next_bid_time = match last.checked_add(gap) { - Some(val) => val, - None => return Err(AuctionError::NumericalOverflowError.into()), - }; + let next_bid_time = last + .checked_add(gap) + .ok_or(AuctionError::NumericalOverflowError)?; + Ok(now > end && now > next_bid_time) } else { Ok(now > end) @@ -179,6 +179,28 @@ impl AuctionData { }; self.bid_state.winner_at(idx, minimum) } + + pub fn place_bid( + &mut self, + bid: Bid, + tick_size: Option, + gap_tick_size_percentage: Option, + now: UnixTimestamp, + ) -> Result<(), ProgramError> { + let gap_val = match self.ended_at { + Some(end) => { + // We use the actual gap tick size perc if we're in gap window, + // otherwise we pass in none so the logic isnt used + if now > end { + gap_tick_size_percentage + } else { + None + } + } + None => None, + }; + self.bid_state.place_bid(bid, tick_size, gap_val) + } } /// Define valid auction state transitions. @@ -259,59 +281,120 @@ impl BidState { real_max } + fn assert_valid_tick_size_bid(bid: &Bid, tick_size: Option) -> ProgramResult { + if let Some(tick) = tick_size { + if bid.1.checked_rem(tick) != Some(0) { + msg!( + "This bid {:?} is not a multiple of tick size {:?}, throw it out.", + bid.1, + tick_size + ); + return Err(AuctionError::BidMustBeMultipleOfTickSize.into()); + } + } else { + msg!("No tick size on this auction") + } + + Ok(()) + } + + fn assert_valid_gap_insertion( + gap_tick: u8, + beaten_bid: &Bid, + beating_bid: &Bid, + ) -> ProgramResult { + // Use u128 to avoid potential overflow due to temporary mult of 100x since + // we haven't divided yet. + let mut minimum_bid_amount: u128 = (beaten_bid.1 as u128) + .checked_mul((100 + gap_tick) as u128) + .ok_or(AuctionError::NumericalOverflowError)?; + minimum_bid_amount = minimum_bid_amount + .checked_div(100u128) + .ok_or(AuctionError::NumericalOverflowError)?; + + if minimum_bid_amount > beating_bid.1 as u128 { + msg!("Rejecting inserting this bid due to gap tick size of {:?} which causes min bid of {:?} from {:?} which is the bid it is trying to beat", gap_tick, minimum_bid_amount.to_string(), beaten_bid.1); + return Err(AuctionError::GapBetweenBidsTooSmall.into()); + } + + Ok(()) + } + /// Push a new bid into the state, this succeeds only if the bid is larger than the current top /// winner stored. Crappy list information to start with. - pub fn place_bid(&mut self, bid: Bid) -> Result<(), ProgramError> { + pub fn place_bid( + &mut self, + bid: Bid, + tick_size: Option, + gap_tick_size_percentage: Option, + ) -> Result<(), ProgramError> { + msg!("Placing bid {:?}", &bid.1.to_string()); + BidState::assert_valid_tick_size_bid(&bid, tick_size)?; + match self { // In a capped auction, track the limited number of winners. - BidState::EnglishAuction { ref mut bids, max } => match bids.last() { - Some(top) => { - msg!("Looking to go over the loop"); - for i in (0..bids.len()).rev() { - msg!("Comparison of {:?} and {:?} for {:?}", bids[i].1, bid.1, i); - if bids[i].1 < bid.1 { - msg!("Ok we can do an insert"); - if i + 1 < bids.len() { - msg!("Doing a normal insert"); - bids.insert(i + 1, bid); - } else { - msg!("Doing an on the end insert"); - bids.push(bid) - } - break; - } else if bids[i].1 == bid.1 { - msg!("Ok we can do an equivalent insert"); - if i == 0 { - msg!("Doing a normal insert"); + BidState::EnglishAuction { ref mut bids, max } => { + match bids.last() { + Some(top) => { + msg!("Looking to go over the loop, but check tick size first"); + + for i in (0..bids.len()).rev() { + msg!("Comparison of {:?} and {:?} for {:?}", bids[i].1, bid.1, i); + if bids[i].1 < bid.1 { + if let Some(gap_tick) = gap_tick_size_percentage { + BidState::assert_valid_gap_insertion(gap_tick, &bids[i], &bid)? + } + + msg!("Ok we can do an insert"); + if i + 1 < bids.len() { + msg!("Doing a normal insert"); + bids.insert(i + 1, bid); + } else { + msg!("Doing an on the end insert"); + bids.push(bid) + } + break; + } else if bids[i].1 == bid.1 { + if let Some(gap_tick) = gap_tick_size_percentage { + if gap_tick > 0 { + msg!("Rejecting same-bid insert due to gap tick size of {:?}", gap_tick); + return Err(AuctionError::GapBetweenBidsTooSmall.into()); + } + } + + msg!("Ok we can do an equivalent insert"); + if i == 0 { + msg!("Doing a normal insert"); + bids.insert(0, bid); + break; + } else { + if bids[i - 1].1 != bids[i].1 { + msg!("Doing an insert just before"); + bids.insert(i, bid); + break; + } + msg!("More duplicates ahead...") + } + } else if i == 0 { + msg!("Inserting at 0"); bids.insert(0, bid); break; - } else { - if bids[i - 1].1 != bids[i].1 { - msg!("Doing an insert just before"); - bids.insert(i, bid); - break; - } - msg!("More duplicates ahead...") } - } else if i == 0 { - msg!("Inserting at 0"); - bids.insert(0, bid); - break; } - } - let max_size = BidState::max_array_size_for(*max); + let max_size = BidState::max_array_size_for(*max); - if bids.len() > max_size { - bids.remove(0); + if bids.len() > max_size { + bids.remove(0); + } + Ok(()) + } + _ => { + msg!("Pushing bid onto stack"); + bids.push(bid); + Ok(()) } - Ok(()) } - _ => { - msg!("Pushing bid onto stack"); - bids.push(bid); - Ok(()) - } - }, + } // In an open auction, bidding simply succeeds. BidState::OpenEdition { bids, max } => Ok(()), diff --git a/rust/auction/program/src/processor/create_auction.rs b/rust/auction/program/src/processor/create_auction.rs index 60ac4ac..7841e7a 100644 --- a/rust/auction/program/src/processor/create_auction.rs +++ b/rust/auction/program/src/processor/create_auction.rs @@ -40,6 +40,10 @@ pub struct CreateAuctionArgs { pub resource: Pubkey, /// Set a price floor. pub price_floor: PriceFloor, + /// Add a tick size increment + pub tick_size: Option, + /// Add a minimum percentage increase each bid must meet. + pub gap_tick_size_percentage: Option, } struct Accounts<'a, 'b: 'a> { @@ -98,6 +102,12 @@ pub fn create_auction( WinnerLimit::Unlimited(_) => BidState::new_open_edition(), }; + if let Some(gap_tick) = args.gap_tick_size_percentage { + if gap_tick > 100 { + return Err(AuctionError::InvalidGapTickSizePercentage.into()); + } + } + // Create auction account with enough space for a winner tracking. create_or_allocate_account_raw( *program_id, @@ -141,6 +151,14 @@ pub fn create_auction( ], )?; + // Configure extended + AuctionDataExtended { + total_uncancelled_bids: 0, + tick_size: args.tick_size, + gap_tick_size_percentage: args.gap_tick_size_percentage, + } + .serialize(&mut *accounts.auction_extended.data.borrow_mut())?; + // Configure Auction. AuctionData { authority: args.authority, diff --git a/rust/auction/program/src/processor/place_bid.rs b/rust/auction/program/src/processor/place_bid.rs index cdff99f..f184f3c 100644 --- a/rust/auction/program/src/processor/place_bid.rs +++ b/rust/auction/program/src/processor/place_bid.rs @@ -316,9 +316,12 @@ pub fn place_bid<'r, 'b: 'r>( // Serialize new Auction State auction.last_bid = Some(clock.unix_timestamp); - auction - .bid_state - .place_bid(Bid(*accounts.bidder.key, args.amount))?; + auction.place_bid( + Bid(*accounts.bidder.key, args.amount), + auction_extended.tick_size, + auction_extended.gap_tick_size_percentage, + clock.unix_timestamp, + )?; auction.serialize(&mut *accounts.auction.data.borrow_mut())?; // Update latest metadata with results from the bid. diff --git a/rust/metaplex/program/src/processor/redeem_bid.rs b/rust/metaplex/program/src/processor/redeem_bid.rs index 6ba90ae..832e56b 100644 --- a/rust/metaplex/program/src/processor/redeem_bid.rs +++ b/rust/metaplex/program/src/processor/redeem_bid.rs @@ -21,12 +21,60 @@ use { }, }; +fn set_reservation_list_wrapper<'a>( + program_id: &'a Pubkey, + master_edition_info: &AccountInfo<'a>, + reservation_list_info: &AccountInfo<'a>, + auction_manager_info: &AccountInfo<'a>, + signer_seeds: &[&[u8]], + reservations: Vec, + total_reservation_spots: Option, + offset: u64, + total_spot_offset: u64, +) -> ProgramResult { + invoke_signed( + &set_reservation_list( + *program_id, + *master_edition_info.key, + *reservation_list_info.key, + *auction_manager_info.key, + reservations, + total_reservation_spots, + offset, + total_spot_offset, + ), + &[ + master_edition_info.clone(), + reservation_list_info.clone(), + auction_manager_info.clone(), + ], + &[&signer_seeds], + )?; + + Ok(()) +} + +pub fn calc_spots( + winning_config_item: &WinningConfigItem, + auction_manager: &AuctionManager, + n: usize, +) -> u64 { + auction_manager.settings.winning_configs[n] + .items + .iter() + .filter(|i| i.safety_deposit_box_index == winning_config_item.safety_deposit_box_index) + .map(|i| i.amount as u64) + .sum() +} + #[allow(clippy::too_many_arguments)] pub fn reserve_list_if_needed<'a>( program_id: &'a Pubkey, auction_manager: &AuctionManager, auction: &AuctionData, winning_config_item: &WinningConfigItem, + winning_index: usize, + bidder_info: &AccountInfo<'a>, master_edition_info: &AccountInfo<'a>, reservation_list_info: &AccountInfo<'a>, auction_manager_info: &AccountInfo<'a>, @@ -34,57 +82,56 @@ pub fn reserve_list_if_needed<'a>( ) -> ProgramResult { let reservation_list = get_reservation_list(reservation_list_info)?; - if reservation_list.supply_snapshot().is_none() { - let mut reservations: Vec = vec![]; + let total_reservation_spot_opt: Option; - // Auction specifically does not expose internal state workings as it may change someday, - // but it does expose a point get-winner-at-index method. Right now this is just array access - // but may be invocation someday. It's inefficient style but better for the interface maintenance - // in the long run if we move to better storage solutions (so that this action doesnt need to change if - // storage does.) + // Auction specifically does not expose internal state workings as it may change someday, + // but it does expose a point get-winner-at-index method. Right now this is just array access + // but may be invocation someday. It's inefficient style but better for the interface maintenance + // in the long run if we move to better storage solutions (so that this action doesnt need to change if + // storage does.) - for n in 0..auction_manager.settings.winning_configs.len() { - match auction.winner_at(n) { - Some(address) => { - let spots: u64 = auction_manager.settings.winning_configs[n] - .items - .iter() - .filter(|i| { - i.safety_deposit_box_index - == winning_config_item.safety_deposit_box_index - }) - .map(|i| i.amount as u64) - .sum(); - reservations.push(Reservation { - address, - // Select all items in a winning config matching the same safety deposit box - // as the one being redeemed here (likely only one) - // and then sum them to get the total spots to reserve for this winner - spots_remaining: spots, - total_spots: spots, - }) + let mut total_reservation_spots: u64 = 0; + let mut total_spot_offset: u64 = 0; + for n in 0..auction_manager.settings.winning_configs.len() { + match auction.winner_at(n) { + Some(_) => { + let spots: u64 = calc_spots(winning_config_item, auction_manager, n); + total_reservation_spots = total_reservation_spots + .checked_add(spots) + .ok_or(MetaplexError::NumericalOverflowError)?; + if n < winning_index { + total_spot_offset = total_spot_offset + .checked_add(spots) + .ok_or(MetaplexError::NumericalOverflowError)?; } - None => break, } + None => break, } - - invoke_signed( - &set_reservation_list( - *program_id, - *master_edition_info.key, - *reservation_list_info.key, - *auction_manager_info.key, - reservations, - ), - &[ - master_edition_info.clone(), - reservation_list_info.clone(), - auction_manager_info.clone(), - ], - &[&signer_seeds], - )?; } + if reservation_list.supply_snapshot().is_none() { + total_reservation_spot_opt = Some(total_reservation_spots) + } else { + total_reservation_spot_opt = None + } + + let my_spots: u64 = calc_spots(winning_config_item, auction_manager, winning_index); + set_reservation_list_wrapper( + program_id, + master_edition_info, + reservation_list_info, + auction_manager_info, + signer_seeds, + vec![Reservation { + address: *bidder_info.key, + spots_remaining: my_spots, + total_spots: my_spots, + }], + total_reservation_spot_opt, + winning_index as u64, + total_spot_offset, + )?; + Ok(()) } pub fn process_redeem_bid<'a>( @@ -186,6 +233,8 @@ pub fn process_redeem_bid<'a>( &auction_manager, &auction, &winning_config_item, + winning_index, + bidder_info, master_edition_info, reservation_list_info, auction_manager_info, diff --git a/rust/token-metadata/program/src/error.rs b/rust/token-metadata/program/src/error.rs index 936cabb..84fb44a 100644 --- a/rust/token-metadata/program/src/error.rs +++ b/rust/token-metadata/program/src/error.rs @@ -266,6 +266,18 @@ pub enum MetadataError { /// Data type mismatch #[error("Data type mismatch")] DataTypeMismatch, + + /// Beyond alotted address size in reservation! + #[error("Beyond alotted address size in reservation!")] + BeyondAlottedAddressSize, + + /// The reservation has only been partially alotted + #[error("The reservation has only been partially alotted")] + ReservationNotComplete, + + /// You cannot splice over an existing reservation! + #[error("You cannot splice over an existing reservation!")] + TriedToReplaceAnExistingReservation, } impl PrintProgramError for MetadataError { diff --git a/rust/token-metadata/program/src/instruction.rs b/rust/token-metadata/program/src/instruction.rs index 8a831ce..3405365 100644 --- a/rust/token-metadata/program/src/instruction.rs +++ b/rust/token-metadata/program/src/instruction.rs @@ -45,6 +45,15 @@ pub struct MintPrintingTokensViaTokenArgs { pub struct SetReservationListArgs { /// If set, means that no more than this number of editions can ever be minted. This is immutable. pub reservations: Vec, + /// should only be present on the very first call to set reservation list. + pub total_reservation_spots: Option, + /// Where in the reservation list you want to insert this slice of reservations + pub offset: u64, + /// What the total spot offset is in the reservation list from the beginning to your slice of reservations. + /// So if is going to be 4 total editions eventually reserved between your slice and the beginning of the array, + /// split between 2 reservation entries, the offset variable above would be "2" since you start at entry 2 in 0 indexed array + /// (first 2 taking 0 and 1) and because they each have 2 spots taken, this variable would be 4. + pub total_spot_offset: u64, } /// Instructions supported by the Metadata program. @@ -124,9 +133,13 @@ pub enum MetadataInstruction { /// with the pda that was created by that first bidder - the token metadata can then cross reference /// these people with the list and see that bidder A gets edition #2, so on and so forth. /// + /// NOTE: If you have more than 20 addresses in a reservation list, this may be called multiple times to build up the list, + /// otherwise, it simply wont fit in one transaction. Only provide a total_reservation argument on the first call, which will + /// allocate the edition space, and in follow up calls this will specifically be unnecessary (and indeed will error.) + /// /// 0. `[writable]` Master Edition key (pda of ['metadata', program id, mint id, 'edition']) /// 1. `[writable]` PDA for ReservationList of ['metadata', program id, master edition key, 'reservation', resource-key] - /// 3. `[signer]` The resource you tied the reservation list too + /// 2. `[signer]` The resource you tied the reservation list too SetReservationList(SetReservationListArgs), /// Create an empty reservation list for a resource who can come back later as a signer and fill the reservation list @@ -363,6 +376,9 @@ pub fn set_reservation_list( reservation_list: Pubkey, resource: Pubkey, reservations: Vec, + total_reservation_spots: Option, + offset: u64, + total_spot_offset: u64, ) -> Instruction { Instruction { program_id, @@ -371,9 +387,14 @@ pub fn set_reservation_list( AccountMeta::new(reservation_list, false), AccountMeta::new_readonly(resource, true), ], - data: MetadataInstruction::SetReservationList(SetReservationListArgs { reservations }) - .try_to_vec() - .unwrap(), + data: MetadataInstruction::SetReservationList(SetReservationListArgs { + reservations, + total_reservation_spots, + offset, + total_spot_offset, + }) + .try_to_vec() + .unwrap(), } } diff --git a/rust/token-metadata/program/src/processor.rs b/rust/token-metadata/program/src/processor.rs index 7cc3cb0..6379559 100644 --- a/rust/token-metadata/program/src/processor.rs +++ b/rust/token-metadata/program/src/processor.rs @@ -70,7 +70,14 @@ pub fn process_instruction( } MetadataInstruction::SetReservationList(args) => { msg!("Instruction: Set Reservation List"); - process_set_reservation_list(program_id, accounts, args.reservations) + process_set_reservation_list( + program_id, + accounts, + args.reservations, + args.total_reservation_spots, + args.offset, + args.total_spot_offset, + ) } MetadataInstruction::CreateReservationList => { msg!("Instruction: Create Reservation List"); @@ -541,6 +548,9 @@ pub fn process_set_reservation_list( program_id: &Pubkey, accounts: &[AccountInfo], reservations: Vec, + total_reservation_spots: Option, + offset: u64, + total_spot_offset: u64, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); @@ -558,10 +568,6 @@ pub fn process_set_reservation_list( return Err(MetadataError::ReservationDoesNotExist.into()); } - if reservations.len() > MAX_RESERVATIONS { - return Err(MetadataError::BeyondMaxAddressSize.into()); - } - assert_derivation( program_id, reservation_list_info, @@ -576,12 +582,12 @@ pub fn process_set_reservation_list( let mut reservation_list = get_reservation_list(reservation_list_info)?; - if reservation_list.supply_snapshot().is_some() { + if reservation_list.supply_snapshot().is_some() && total_reservation_spots.is_some() { return Err(MetadataError::ReservationAlreadyMade.into()); } - let mut total_len: u64 = 0; - let mut total_len_check: u64 = 0; + let mut total_len: u64 = reservation_list.current_reservation_spots(); + let mut total_len_check: u64 = reservation_list.current_reservation_spots(); for reservation in &reservations { total_len = total_len @@ -596,28 +602,39 @@ pub fn process_set_reservation_list( ); } } + reservation_list.set_current_reservation_spots(total_len); + + reservation_list.add_reservations(reservations, offset, total_spot_offset)?; + + if let Some(total) = total_reservation_spots { + reservation_list.set_supply_snapshot(Some(master_edition.supply)); + reservation_list.set_total_reservation_spots(total); + master_edition.supply = master_edition + .supply + .checked_add(total as u64) + .ok_or(MetadataError::NumericalOverflowError)?; + + if let Some(max_supply) = master_edition.max_supply { + if master_edition.supply > max_supply { + return Err(MetadataError::ReservationBreachesMaximumSupply.into()); + } + } + master_edition.serialize(&mut *master_edition_info.data.borrow_mut())?; + } if total_len_check != total_len { return Err(MetadataError::SpotMismatch.into()); } - reservation_list.set_supply_snapshot(Some(master_edition.supply)); - reservation_list.set_reservations(reservations); - msg!("Master edition {:?}", master_edition); - msg!("Total new spots {:?}", total_len); - master_edition.supply = master_edition - .supply - .checked_add(total_len as u64) - .ok_or(MetadataError::NumericalOverflowError)?; + if total_len > reservation_list.total_reservation_spots() { + return Err(MetadataError::BeyondAlottedAddressSize.into()); + }; - if let Some(max_supply) = master_edition.max_supply { - if master_edition.supply > max_supply { - return Err(MetadataError::ReservationBreachesMaximumSupply.into()); - } + if reservation_list.reservations().len() > MAX_RESERVATIONS { + return Err(MetadataError::BeyondMaxAddressSize.into()); } reservation_list.save(reservation_list_info)?; - master_edition.serialize(&mut *master_edition_info.data.borrow_mut())?; Ok(()) } diff --git a/rust/token-metadata/program/src/state.rs b/rust/token-metadata/program/src/state.rs index a5515db..94a01c2 100644 --- a/rust/token-metadata/program/src/state.rs +++ b/rust/token-metadata/program/src/state.rs @@ -46,7 +46,7 @@ pub const MAX_RESERVATIONS: usize = 200; pub const MAX_RESERVATION_LIST_V1_SIZE: usize = 1 + 32 + 8 + 8 + MAX_RESERVATIONS * 34 + 100; // can hold up to 200 keys per reservation, note: the extra 8 is for number of elements in the vec -pub const MAX_RESERVATION_LIST_SIZE: usize = 1 + 32 + 8 + 8 + MAX_RESERVATIONS * 48 + 100; +pub const MAX_RESERVATION_LIST_SIZE: usize = 1 + 32 + 8 + 8 + MAX_RESERVATIONS * 48 + 8 + 8 + 84; #[repr(C)] #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)] @@ -170,9 +170,19 @@ pub trait ReservationList { fn master_edition(&self) -> Pubkey; fn supply_snapshot(&self) -> Option; fn reservations(&self) -> Vec; + fn total_reservation_spots(&self) -> u64; + fn current_reservation_spots(&self) -> u64; fn set_master_edition(&mut self, key: Pubkey); fn set_supply_snapshot(&mut self, supply: Option); - fn set_reservations(&mut self, reservations: Vec); + fn set_reservations(&mut self, reservations: Vec) -> ProgramResult; + fn add_reservations( + &mut self, + reservations: Vec, + offset: u64, + total_spot_offset: u64, + ) -> ProgramResult; + fn set_total_reservation_spots(&mut self, total_reservation_spots: u64); + fn set_current_reservation_spots(&mut self, current_reservation_spots: u64); fn save(&self, account: &AccountInfo) -> ProgramResult; } @@ -199,6 +209,10 @@ pub struct ReservationListV2 { /// What supply counter was on master_edition when this reservation was created. pub supply_snapshot: Option, pub reservations: Vec, + /// How many reservations there are going to be, given on first set_reservation call + pub total_reservation_spots: u64, + /// Cached count of reservation spots in the reservation vec to save on CPU. + pub current_reservation_spots: u64, } impl ReservationList for ReservationListV2 { @@ -222,14 +236,74 @@ impl ReservationList for ReservationListV2 { self.supply_snapshot = supply; } - fn set_reservations(&mut self, reservations: Vec) { - self.reservations = reservations + fn add_reservations( + &mut self, + mut reservations: Vec, + offset: u64, + total_spot_offset: u64, + ) -> ProgramResult { + let usize_offset = offset as usize; + while self.reservations.len() < usize_offset { + self.reservations.push(Reservation { + address: solana_program::system_program::id(), + spots_remaining: 0, + total_spots: 0, + }) + } + if self.reservations.len() > usize_offset { + let removed_elements: Vec = self + .reservations + .splice( + usize_offset..usize_offset + reservations.len(), + reservations, + ) + .collect(); + let existing_res = removed_elements + .iter() + .find(|r| r.address != solana_program::system_program::id()); + if existing_res.is_some() { + return Err(MetadataError::TriedToReplaceAnExistingReservation.into()); + } + } else { + self.reservations.append(&mut reservations) + } + + if usize_offset != 0 + && self.reservations[usize_offset - 1].address == solana_program::system_program::id() + { + // This becomes an anchor then for calculations... put total spot offset in here. + self.reservations[usize_offset - 1].spots_remaining = total_spot_offset; + self.reservations[usize_offset - 1].total_spots = total_spot_offset; + } + + Ok(()) + } + + fn set_reservations(&mut self, reservations: Vec) -> ProgramResult { + self.reservations = reservations; + Ok(()) } fn save(&self, account: &AccountInfo) -> ProgramResult { self.serialize(&mut *account.data.borrow_mut())?; Ok(()) } + + fn total_reservation_spots(&self) -> u64 { + self.total_reservation_spots + } + + fn set_total_reservation_spots(&mut self, total_reservation_spots: u64) { + self.total_reservation_spots = total_reservation_spots; + } + + fn current_reservation_spots(&self) -> u64 { + self.current_reservation_spots + } + + fn set_current_reservation_spots(&mut self, current_reservation_spots: u64) { + self.current_reservation_spots = current_reservation_spots; + } } impl ReservationListV2 { @@ -293,7 +367,12 @@ impl ReservationList for ReservationListV1 { self.supply_snapshot = supply; } - fn set_reservations(&mut self, reservations: Vec) { + fn add_reservations( + &mut self, + reservations: Vec, + _: u64, + _: u64, + ) -> ProgramResult { self.reservations = reservations .iter() .map(|r| ReservationV1 { @@ -302,12 +381,31 @@ impl ReservationList for ReservationListV1 { total_spots: r.total_spots as u8, }) .collect(); + + Ok(()) + } + + fn set_reservations(&mut self, reservations: Vec) -> ProgramResult { + self.add_reservations(reservations, 0, 0)?; + Ok(()) } fn save(&self, account: &AccountInfo) -> ProgramResult { self.serialize(&mut *account.data.borrow_mut())?; Ok(()) } + + fn total_reservation_spots(&self) -> u64 { + self.reservations.iter().map(|r| r.total_spots as u64).sum() + } + + fn set_total_reservation_spots(&mut self, _: u64) {} + + fn current_reservation_spots(&self) -> u64 { + self.reservations.iter().map(|r| r.total_spots as u64).sum() + } + + fn set_current_reservation_spots(&mut self, _: u64) {} } impl ReservationListV1 { diff --git a/rust/token-metadata/program/src/utils.rs b/rust/token-metadata/program/src/utils.rs index 0b555b9..6a36f30 100644 --- a/rust/token-metadata/program/src/utils.rs +++ b/rust/token-metadata/program/src/utils.rs @@ -424,6 +424,7 @@ pub fn mint_limited_edition<'a>( let mut reservations = reservation_list.reservations(); for i in 0..reservations.len() { let mut reservation = &mut reservations[i]; + if reservation.address == *mint_authority_info.key { offset = Some( prev_total_offsets @@ -436,13 +437,21 @@ pub fn mint_limited_edition<'a>( .checked_sub(1) .ok_or(MetadataError::NumericalOverflowError)?; - reservation_list.set_reservations(reservations); + reservation_list.set_reservations(reservations)?; reservation_list.save(account)?; break; } - prev_total_offsets = prev_total_offsets - .checked_add(reservation.total_spots) - .ok_or(MetadataError::NumericalOverflowError)?; + + if reservation.address == solana_program::system_program::id() { + // This is an anchor point in the array...it means we reset our math to + // this offset because we may be missing information in between this point and + // the points before it. + prev_total_offsets = reservation.total_spots; + } else { + prev_total_offsets = prev_total_offsets + .checked_add(reservation.total_spots) + .ok_or(MetadataError::NumericalOverflowError)?; + } } match offset { diff --git a/rust/token-metadata/test/src/main.rs b/rust/token-metadata/test/src/main.rs index 23224e8..3e23425 100644 --- a/rust/token-metadata/test/src/main.rs +++ b/rust/token-metadata/test/src/main.rs @@ -5,7 +5,9 @@ use { input_validators::{is_url, is_valid_pubkey, is_valid_signer}, }, solana_client::rpc_client::RpcClient, - solana_program::{borsh::try_from_slice_unchecked, program_pack::Pack}, + solana_program::{ + account_info::AccountInfo, borsh::try_from_slice_unchecked, program_pack::Pack, + }, solana_sdk::{ pubkey::Pubkey, signature::{read_keypair_file, Keypair, Signer}, @@ -22,7 +24,9 @@ use { mint_new_edition_from_master_edition_via_token, mint_printing_tokens, update_metadata_accounts, }, - state::{Data, Edition, Key, MasterEdition, Metadata, EDITION, PREFIX}, + state::{ + get_reservation_list, Data, Edition, Key, MasterEdition, Metadata, EDITION, PREFIX, + }, }, std::str::FromStr, }; @@ -79,6 +83,30 @@ fn mint_coins(app_matches: &ArgMatches, payer: Keypair, client: RpcClient) { println!("Minted {:?} tokens to {:?}.", amount, destination_key); } +fn show_reservation_list(app_matches: &ArgMatches, _payer: Keypair, client: RpcClient) { + let key = pubkey_of(app_matches, "key").unwrap(); + let mut res_data = client.get_account(&key).unwrap(); + let mut lamports = 0; + let account_info = AccountInfo::new( + &key, + false, + false, + &mut lamports, + &mut res_data.data, + &res_data.owner, + false, + 0, + ); + + let res_list = get_reservation_list(&account_info).unwrap(); + println!("Res list {:?}", res_list.reservations()); + println!( + "current res spots: {:?}", + res_list.current_reservation_spots() + ); + println!("total res spots: {:?}", res_list.total_reservation_spots()); + println!("supply snapshot: {:?}", res_list.supply_snapshot()); +} fn show(app_matches: &ArgMatches, _payer: Keypair, client: RpcClient) { let program_key = spl_token_metadata::id(); @@ -735,6 +763,18 @@ fn main() { .takes_value(true) .help("Metadata mint"), ) + ).subcommand( + SubCommand::with_name("show_reservation_list") + .about("Show Reservation List") + .arg( + Arg::with_name("key") + .long("key") + .value_name("KEY") + .required(true) + .validator(is_valid_pubkey) + .takes_value(true) + .help("Account key of reservation list"), + ) ) .subcommand( SubCommand::with_name("create_master_edition") @@ -843,6 +883,9 @@ fn main() { ("show", Some(arg_matches)) => { show(arg_matches, payer, client); } + ("show_reservation_list", Some(arg_matches)) => { + show_reservation_list(arg_matches, payer, client); + } ("mint_coins", Some(arg_matches)) => { mint_coins(arg_matches, payer, client); }