feat(instant sale): add the instant sale with copies flow

This commit is contained in:
shotgunofdeath 2021-09-12 20:17:45 +03:00
parent 5c338a156e
commit 4c5f1e7642
6 changed files with 102 additions and 53 deletions

View File

@ -20,6 +20,7 @@ import {
MAX_METADATA_LEN, MAX_METADATA_LEN,
MAX_EDITION_LEN, MAX_EDITION_LEN,
useWalletModal, useWalletModal,
VaultState,
} from '@oyster/common'; } from '@oyster/common';
import { useWallet } from '@solana/wallet-adapter-react'; import { useWallet } from '@solana/wallet-adapter-react';
import { AuctionView, useBidsForAuction, useUserBalance } from '../../hooks'; import { AuctionView, useBidsForAuction, useUserBalance } from '../../hooks';
@ -183,7 +184,7 @@ export const AuctionCard = ({
action?: JSX.Element; action?: JSX.Element;
}) => { }) => {
const connection = useConnection(); const connection = useConnection();
const {update} = useMeta(); const { update } = useMeta();
const wallet = useWallet(); const wallet = useWallet();
const { setVisible } = useWalletModal(); const { setVisible } = useWalletModal();
@ -253,8 +254,14 @@ export const AuctionCard = ({
auctionView.auction.info.state === AuctionState.Created; auctionView.auction.info.state === AuctionState.Created;
//if instant sale auction bid and claimed hide buttons //if instant sale auction bid and claimed hide buttons
if (auctionView.isInstantSale && auctionView.myBidderPot?.info.emptied) { if (
return <></> (auctionView.isInstantSale &&
Number(auctionView.myBidderPot?.info.emptied) !== 0 &&
isAuctionManagerAuthorityNotWalletOwner &&
auctionView.auction.info.bidState.max.toNumber() === bids.length) ||
auctionView.vault.info.state === VaultState.Deactivated
) {
return <></>;
} }
return ( return (
@ -387,7 +394,9 @@ export const AuctionCard = ({
{loading ? ( {loading ? (
<Spin /> <Spin />
) : auctionView.isInstantSale ? ( ) : auctionView.isInstantSale ? (
bids.length ? ( !isAuctionManagerAuthorityNotWalletOwner ? (
'Claim master'
) : auctionView.myBidderPot ? (
'Claim Purchase' 'Claim Purchase'
) : ( ) : (
'Buy Now' 'Buy Now'
@ -509,7 +518,8 @@ export const AuctionCard = ({
// Placing a "bid" of the full amount results in a purchase to redeem. // Placing a "bid" of the full amount results in a purchase to redeem.
if ( if (
myPayingAccount && myPayingAccount &&
bids.length === 0 && !auctionView.myBidderPot &&
isAuctionManagerAuthorityNotWalletOwner &&
auctionView.auctionDataExtended?.info.instantSalePrice auctionView.auctionDataExtended?.info.instantSalePrice
) { ) {
try { try {
@ -523,15 +533,20 @@ export const AuctionCard = ({
); );
setLastBid(bid); setLastBid(bid);
} catch (e) { } catch (e) {
console.error('sendPlaceBid', e) console.error('sendPlaceBid', e);
setShowBidModal(false); setShowBidModal(false);
setLoading(false); setLoading(false);
return; return;
} }
} }
await update(); const newAuctionState = await update(
auctionView.auction.pubkey,
wallet.publicKey,
);
auctionView.auction = newAuctionState[0];
auctionView.myBidderPot = newAuctionState[1];
auctionView.myBidderMetadata = newAuctionState[2];
// Claim the purchase // Claim the purchase
try { try {
@ -684,7 +699,8 @@ export const AuctionCard = ({
{loading || !accountByMint.get(QUOTE_MINT.toBase58()) ? ( {loading || !accountByMint.get(QUOTE_MINT.toBase58()) ? (
<Spin /> <Spin />
) : auctionView.isInstantSale ? ( ) : auctionView.isInstantSale ? (
bids.length ? ( auctionView.myBidderPot ||
!isAuctionManagerAuthorityNotWalletOwner ? (
'Claim' 'Claim'
) : ( ) : (
'Purchase' 'Purchase'

View File

@ -7,6 +7,9 @@ import {
METADATA_PROGRAM_ID, METADATA_PROGRAM_ID,
toPublicKey, toPublicKey,
useQuerySearch, useQuerySearch,
AuctionData,
BidderPot,
BidderMetadata,
} from '@oyster/common'; } from '@oyster/common';
import React, { import React, {
useCallback, useCallback,
@ -52,9 +55,11 @@ const MetaContext = React.createContext<MetaContextState>({
payoutTickets: {}, payoutTickets: {},
prizeTrackingTickets: {}, prizeTrackingTickets: {},
stores: {}, stores: {},
update: () => {}, // @ts-ignore
update: () => [AuctionData, BidderPot, BidderMetadata],
}); });
// eslint-disable-next-line react/prop-types
export function MetaProvider({ children = null as any }) { export function MetaProvider({ children = null as any }) {
const connection = useConnection(); const connection = useConnection();
const { isReady, storeAddress } = useStore(); const { isReady, storeAddress } = useStore();
@ -109,7 +114,7 @@ export function MetaProvider({ children = null as any }) {
[setState], [setState],
); );
async function update () { async function update(auctionAddress?, bidderAddress?) {
if (!storeAddress) { if (!storeAddress) {
if (isReady) { if (isReady) {
setIsLoading(false); setIsLoading(false);
@ -122,7 +127,7 @@ export function MetaProvider({ children = null as any }) {
console.log('-----> Query started'); console.log('-----> Query started');
const nextState = await loadAccounts(connection, all); const nextState = await loadAccounts(connection, all);
console.log('loadAccounts',nextState) console.log('loadAccounts', nextState);
console.log('------->Query finished'); console.log('------->Query finished');
setState(nextState); setState(nextState);
@ -130,9 +135,17 @@ export function MetaProvider({ children = null as any }) {
setIsLoading(false); setIsLoading(false);
console.log('------->set finished'); console.log('------->set finished');
updateMints(nextState.metadataByMint); await updateMints(nextState.metadataByMint);
}
if (auctionAddress && bidderAddress) {
const auctionBidderKey = auctionAddress + '-' + bidderAddress;
return [
nextState.auctions[auctionAddress],
nextState.bidderPotsByAuctionAndBidder[auctionBidderKey],
nextState.bidderMetadataByAuctionAndBidder[auctionBidderKey],
];
}
}
useEffect(() => { useEffect(() => {
update(); update();
@ -232,7 +245,8 @@ export function MetaProvider({ children = null as any }) {
value={{ value={{
...state, ...state,
isLoading, isLoading,
update // @ts-ignore
update,
}} }}
> >
{children} {children}

View File

@ -76,7 +76,14 @@ export interface MetaState {
export interface MetaContextState extends MetaState { export interface MetaContextState extends MetaState {
isLoading: boolean; isLoading: boolean;
update: () => void; update: (
auctionAddress?: any,
bidderAddress?: any,
) => [
ParsedAccount<AuctionData>,
ParsedAccount<BidderPot>,
ParsedAccount<BidderMetadata>,
];
} }
export type AccountAndPubkey = { export type AccountAndPubkey = {

View File

@ -5,6 +5,7 @@ import {
programIds, programIds,
StringPublicKey, StringPublicKey,
toPublicKey, toPublicKey,
getAuctionExtended,
} from '@oyster/common'; } from '@oyster/common';
import { import {
SystemProgram, SystemProgram,
@ -69,6 +70,10 @@ export async function redeemPrintingV2Bid(
const value = new RedeemPrintingV2BidArgs({ editionOffset, winIndex }); const value = new RedeemPrintingV2BidArgs({ editionOffset, winIndex });
const data = Buffer.from(serialize(SCHEMA, value)); const data = Buffer.from(serialize(SCHEMA, value));
const extended = await getAuctionExtended({
auctionProgramId: PROGRAM_IDS.auction,
resource: vault,
});
const keys = [ const keys = [
{ {
pubkey: toPublicKey(auctionManagerKey), pubkey: toPublicKey(auctionManagerKey),
@ -199,6 +204,11 @@ export async function redeemPrintingV2Bid(
isSigner: false, isSigner: false,
isWritable: false, isWritable: false,
}, },
{
pubkey: toPublicKey(extended),
isSigner: false,
isWritable: false,
},
]; ];
instructions.push( instructions.push(

View File

@ -172,11 +172,13 @@ export const AuctionCreateView = () => {
if (attributes.category === AuctionCategory.InstantSale) { if (attributes.category === AuctionCategory.InstantSale) {
if (attributes.items.length > 0) { if (attributes.items.length > 0) {
const item = attributes.items[0]; const item = attributes.items[0];
item.winningConfigType = if (!attributes.editions) {
item.metadata.info.updateAuthority === item.winningConfigType =
(wallet?.publicKey || SystemProgram.programId).toBase58() item.metadata.info.updateAuthority ===
? WinningConfigType.FullRightsTransfer (wallet?.publicKey || SystemProgram.programId).toBase58()
: WinningConfigType.TokenOnlyTransfer; ? WinningConfigType.FullRightsTransfer
: WinningConfigType.TokenOnlyTransfer;
}
item.amountRanges = [ item.amountRanges = [
new AmountRange({ new AmountRange({
amount: new BN(1), amount: new BN(1),
@ -770,37 +772,37 @@ const InstantSaleStep = (props: {
Select NFT Select NFT
</ArtSelector> </ArtSelector>
{/*<label className="action-field">*/} <label className="action-field">
{/* <Checkbox*/} <Checkbox
{/* defaultChecked={false}*/} defaultChecked={false}
{/* checked={copiesChecked}*/} checked={copiesChecked}
{/* disabled={!copiesEnabled}*/} disabled={!copiesEnabled}
{/* onChange={e => setCopiesChecked(e.target.checked)}*/} onChange={e => setCopiesChecked(e.target.checked)}
{/* >*/} >
{/* <span className="field-title">*/} <span className="field-title">
{/* Create copies of a Master Edition NFT?*/} Create copies of a Master Edition NFT?
{/* </span>*/} </span>
{/* </Checkbox>*/} </Checkbox>
{/* {copiesChecked && copiesEnabled && (*/} {copiesChecked && copiesEnabled && (
{/* <>*/} <>
{/* <span className="field-info">*/} <span className="field-info">
{/* Each copy will be given unique edition number e.g. 1 of 30*/} Each copy will be given unique edition number e.g. 1 of 30
{/* </span>*/} </span>
{/* <Input*/} <Input
{/* autoFocus*/} autoFocus
{/* className="input"*/} className="input"
{/* placeholder="Enter number of copies sold"*/} placeholder="Enter number of copies sold"
{/* allowClear*/} allowClear
{/* onChange={info =>*/} onChange={info =>
{/* props.setAttributes({*/} props.setAttributes({
{/* ...props.attributes,*/} ...props.attributes,
{/* editions: parseInt(info.target.value),*/} editions: parseInt(info.target.value),
{/* })*/} })
{/* }*/} }
{/* />*/} />
{/* </>*/} </>
{/* )}*/} )}
{/*</label>*/} </label>
<label className="action-field"> <label className="action-field">
<span className="field-title">Price</span> <span className="field-title">Price</span>

View File

@ -25,7 +25,7 @@ export const AuctionListView = () => {
const auctions = useAuctions(AuctionViewState.Live); const auctions = useAuctions(AuctionViewState.Live);
const auctionsEnded = [ const auctionsEnded = [
...useAuctions(AuctionViewState.Ended), ...useAuctions(AuctionViewState.Ended),
...useAuctions(AuctionViewState.BuyNow).filter((auction) => Number(auction.myBidderPot?.info.emptied) === 0) ...useAuctions(AuctionViewState.BuyNow)
]; ];
const [activeKey, setActiveKey] = useState(LiveAuctionViewState.All); const [activeKey, setActiveKey] = useState(LiveAuctionViewState.All);
const { isLoading } = useMeta(); const { isLoading } = useMeta();