Merge pull request #101 from metaplex-foundation/feature/performance
Feature/performance
This commit is contained in:
commit
ad27a9ecf0
|
@ -4,13 +4,11 @@ import {
|
|||
PublicKey,
|
||||
TransactionInstruction,
|
||||
} from '@solana/web3.js';
|
||||
import { utils, actions, models, findProgramAddress } from '@oyster/common';
|
||||
import { actions, models } from '@oyster/common';
|
||||
|
||||
import { AccountLayout } from '@solana/spl-token';
|
||||
import BN from 'bn.js';
|
||||
import { METAPLEX_PREFIX } from '../models/metaplex';
|
||||
const { createTokenAccount, activateVault, combineVault, AUCTION_PREFIX } =
|
||||
actions;
|
||||
const { createTokenAccount, activateVault, combineVault } = actions;
|
||||
const { approve } = models;
|
||||
|
||||
// This command "closes" the vault, by activating & combining it in one go, handing it over to the auction manager
|
||||
|
@ -28,25 +26,12 @@ export async function closeVault(
|
|||
instructions: TransactionInstruction[];
|
||||
signers: Keypair[];
|
||||
}> {
|
||||
const PROGRAM_IDS = utils.programIds();
|
||||
|
||||
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
|
||||
AccountLayout.span,
|
||||
);
|
||||
let signers: Keypair[] = [];
|
||||
let instructions: TransactionInstruction[] = [];
|
||||
|
||||
const auctionKey: PublicKey = (
|
||||
await findProgramAddress(
|
||||
[
|
||||
Buffer.from(AUCTION_PREFIX),
|
||||
PROGRAM_IDS.auction.toBuffer(),
|
||||
vault.toBuffer(),
|
||||
],
|
||||
PROGRAM_IDS.auction,
|
||||
)
|
||||
)[0];
|
||||
|
||||
await activateVault(
|
||||
new BN(0),
|
||||
vault,
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
CreateAuctionArgs,
|
||||
} from '@oyster/common';
|
||||
|
||||
import { METAPLEX_PREFIX } from '../models/metaplex';
|
||||
const { AUCTION_PREFIX, createAuction } = actions;
|
||||
|
||||
// This command makes an auction
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
Data,
|
||||
Creator,
|
||||
findProgramAddress,
|
||||
MetadataCategory,
|
||||
} from '@oyster/common';
|
||||
import React from 'react';
|
||||
import { MintLayout, Token } from '@solana/spl-token';
|
||||
|
@ -337,7 +336,7 @@ export const mintNFT = async (
|
|||
notify({
|
||||
message: 'Art created on Solana',
|
||||
description: (
|
||||
<a href={arweaveLink} target="_blank">
|
||||
<a href={arweaveLink} target="_blank" rel="noopener noreferrer">
|
||||
Arweave Link
|
||||
</a>
|
||||
),
|
||||
|
|
|
@ -214,7 +214,7 @@ export async function sendRedeemBid(
|
|||
}
|
||||
|
||||
if (
|
||||
wallet.publicKey.toBase58() ==
|
||||
wallet.publicKey.toBase58() ===
|
||||
auctionView.auctionManager.info.authority.toBase58()
|
||||
) {
|
||||
await claimUnusedPrizes(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Ref, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import React, { Ref, useCallback, useEffect, useState } from 'react';
|
||||
import { Image } from 'antd';
|
||||
import { MetadataCategory, MetadataFile } from '@oyster/common';
|
||||
import { MeshViewer } from '../MeshViewer';
|
||||
|
@ -47,7 +47,7 @@ const CachedImageContent = ({
|
|||
style?: React.CSSProperties;
|
||||
}) => {
|
||||
const [loaded, setLoaded] = useState<boolean>(false);
|
||||
const { cachedBlob, isLoading } = useCachedImage(uri || '');
|
||||
const { cachedBlob } = useCachedImage(uri || '');
|
||||
|
||||
return <Image
|
||||
src={cachedBlob}
|
||||
|
@ -212,5 +212,5 @@ export const ArtContent = ({
|
|||
style={style}/>
|
||||
);
|
||||
|
||||
return <div ref={ref as any}>{content}</div>;
|
||||
return <div ref={ref as any} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{content}</div>;
|
||||
};
|
||||
|
|
|
@ -63,7 +63,7 @@ function useGapTickCheck(
|
|||
const higherExpectedAmount = expected * ((100 + gapTick) / 100);
|
||||
|
||||
return higherExpectedAmount > toLamportVal;
|
||||
} else if (expected == toLamportVal) {
|
||||
} else if (expected === toLamportVal) {
|
||||
// If gap tick is set, no way you can bid in this case - you must bid higher.
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ export const PreSaleBanner = ({ auction }: IPreSaleBanner) => {
|
|||
<ArtContent
|
||||
pubkey={id}
|
||||
className="artwork-image"
|
||||
allowMeshRender={true}
|
||||
/>
|
||||
</Col>
|
||||
<Col md={12} className="presale-info">
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import React from 'react';
|
||||
import { Col, Button } from 'antd';
|
||||
import {
|
||||
useArt,
|
||||
} from '../../hooks';
|
||||
import {
|
||||
useConnectionConfig,
|
||||
} from '@oyster/common';
|
||||
|
||||
export const ViewOn = ({ id }: { id: string }) => {
|
||||
const { env } = useConnectionConfig();
|
||||
const art = useArt(id);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Col>
|
||||
<h6>View on</h6>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<Button
|
||||
className="tag"
|
||||
onClick={() => window.open(art.uri || '', '_blank')}
|
||||
>
|
||||
Arweave
|
||||
</Button>
|
||||
<Button
|
||||
className="tag"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
`https://explorer.solana.com/account/${art?.mint || ''}${
|
||||
env.indexOf('main') >= 0 ? '' : `?cluster=${env}`
|
||||
}`,
|
||||
'_blank',
|
||||
)
|
||||
}
|
||||
>
|
||||
Solana
|
||||
</Button>
|
||||
</div>
|
||||
</Col>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -33,7 +33,6 @@ import {
|
|||
} from '@oyster/common';
|
||||
import { MintInfo } from '@solana/spl-token';
|
||||
import { Connection, PublicKey, PublicKeyAndAccount } from '@solana/web3.js';
|
||||
import BN from 'bn.js';
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
|
@ -43,7 +42,6 @@ import React, {
|
|||
} from 'react';
|
||||
import {
|
||||
AuctionManager,
|
||||
AuctionManagerStatus,
|
||||
BidRedemptionTicket,
|
||||
decodeAuctionManager,
|
||||
decodeBidRedemptionTicket,
|
||||
|
@ -254,6 +252,8 @@ export function MetaProvider({ children = null as any }) {
|
|||
const values = Object.values(
|
||||
tempCache.metadataByMint,
|
||||
) as ParsedAccount<Metadata>[];
|
||||
|
||||
tempCache.metadata = new Array(values.length);
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
const metadata = values[i];
|
||||
if (
|
||||
|
@ -267,13 +267,16 @@ export function MetaProvider({ children = null as any }) {
|
|||
tempCache.metadataByMasterEdition[
|
||||
metadata.info?.masterEdition?.toBase58() || ''
|
||||
] = metadata;
|
||||
|
||||
tempCache.metadata.push(metadata);
|
||||
} else {
|
||||
|
||||
delete tempCache.metadataByMint[metadata.info.mint.toBase58() || ''];
|
||||
}
|
||||
}
|
||||
|
||||
console.log('------->init finished');
|
||||
tempCache.metadata = values;
|
||||
tempCache.metadata = tempCache.metadata.filter(m => m);
|
||||
setState({
|
||||
...tempCache,
|
||||
});
|
||||
|
@ -375,7 +378,7 @@ export function MetaProvider({ children = null as any }) {
|
|||
setState(data => ({
|
||||
...data,
|
||||
metadata: [
|
||||
...data.metadata.filter(m => m.pubkey.equals(pubkey)),
|
||||
...data.metadata.filter(m => !m.pubkey.equals(pubkey)),
|
||||
result,
|
||||
],
|
||||
metadataByMasterEdition: {
|
||||
|
|
|
@ -101,9 +101,7 @@ export function useCachedRedemptionKeysByWallet() {
|
|||
}
|
||||
|
||||
export const useAuctions = (state?: AuctionViewState) => {
|
||||
const [auctionViews, setAuctionViews] = useState<
|
||||
Record<string, AuctionView | undefined>
|
||||
>({});
|
||||
const [auctionViews, setAuctionViews] = useState<AuctionView[]>([]);
|
||||
const { wallet } = useWallet();
|
||||
|
||||
const pubkey = wallet?.publicKey;
|
||||
|
@ -124,9 +122,8 @@ export const useAuctions = (state?: AuctionViewState) => {
|
|||
} = useMeta();
|
||||
|
||||
useEffect(() => {
|
||||
Object.keys(auctions).forEach(a => {
|
||||
const map = Object.keys(auctions).reduce((agg, a) => {
|
||||
const auction = auctions[a];
|
||||
const existingAuctionView = auctionViews[a];
|
||||
const nextAuctionView = processAccountsIntoAuctionView(
|
||||
pubkey,
|
||||
auction,
|
||||
|
@ -142,10 +139,20 @@ export const useAuctions = (state?: AuctionViewState) => {
|
|||
metadataByMasterEdition,
|
||||
cachedRedemptionKeys,
|
||||
state,
|
||||
existingAuctionView,
|
||||
);
|
||||
setAuctionViews(nA => ({ ...nA, [a]: nextAuctionView }));
|
||||
});
|
||||
agg[a] = nextAuctionView;
|
||||
return agg;
|
||||
}, {} as Record<string, AuctionView | undefined>);
|
||||
|
||||
setAuctionViews(
|
||||
(Object.values(map).filter(v => v) as AuctionView[]).sort((a, b) => {
|
||||
return (
|
||||
b?.auction.info.endedAt
|
||||
?.sub(a?.auction.info.endedAt || new BN(0))
|
||||
.toNumber() || 0
|
||||
);
|
||||
}),
|
||||
);
|
||||
}, [
|
||||
state,
|
||||
auctions,
|
||||
|
@ -161,17 +168,10 @@ export const useAuctions = (state?: AuctionViewState) => {
|
|||
metadataByMasterEdition,
|
||||
pubkey,
|
||||
cachedRedemptionKeys,
|
||||
setAuctionViews,
|
||||
]);
|
||||
|
||||
return (Object.values(auctionViews).filter(v => v) as AuctionView[]).sort(
|
||||
(a, b) => {
|
||||
return (
|
||||
b?.auction.info.endedAt
|
||||
?.sub(a?.auction.info.endedAt || new BN(0))
|
||||
.toNumber() || 0
|
||||
);
|
||||
},
|
||||
);
|
||||
return auctionViews;
|
||||
};
|
||||
|
||||
export function processAccountsIntoAuctionView(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import {
|
||||
BidderMetadata,
|
||||
|
@ -21,12 +21,34 @@ export const useBidsForAuction = (auctionPubkey: PublicKey | string) => {
|
|||
const id = useMemo(
|
||||
() =>
|
||||
typeof auctionPubkey === 'string'
|
||||
? auctionPubkey
|
||||
: auctionPubkey.toBase58(),
|
||||
? auctionPubkey !== ''
|
||||
? new PublicKey(auctionPubkey)
|
||||
: undefined
|
||||
: auctionPubkey,
|
||||
[auctionPubkey],
|
||||
);
|
||||
|
||||
const bids = cache
|
||||
const [bids, setBids] = useState<ParsedAccount<BidderMetadata>[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const dispose = cache.emitter.onCache(args => {
|
||||
if (args.parser === BidderMetadataParser) {
|
||||
setBids(getBids(id));
|
||||
}
|
||||
});
|
||||
|
||||
setBids(getBids(id));
|
||||
|
||||
return () => {
|
||||
dispose();
|
||||
};
|
||||
}, [id]);
|
||||
|
||||
return bids;
|
||||
};
|
||||
|
||||
const getBids = (id?: PublicKey) => {
|
||||
return cache
|
||||
.byParser(BidderMetadataParser)
|
||||
.filter(key => {
|
||||
const bidder = cache.get(key) as ParsedAccount<BidderMetadata>;
|
||||
|
@ -34,7 +56,7 @@ export const useBidsForAuction = (auctionPubkey: PublicKey | string) => {
|
|||
return false;
|
||||
}
|
||||
|
||||
return bidder.info.auctionPubkey.toBase58() === id;
|
||||
return id?.equals(bidder.info.auctionPubkey);
|
||||
})
|
||||
.map(key => {
|
||||
const bidder = cache.get(key) as ParsedAccount<BidderMetadata>;
|
||||
|
@ -49,10 +71,6 @@ export const useBidsForAuction = (auctionPubkey: PublicKey | string) => {
|
|||
return lastBidDiff;
|
||||
})
|
||||
.map(item => {
|
||||
return {
|
||||
...item,
|
||||
};
|
||||
return item;
|
||||
});
|
||||
|
||||
return bids;
|
||||
};
|
||||
|
|
|
@ -342,6 +342,14 @@ export class Store {
|
|||
}
|
||||
}
|
||||
|
||||
export enum AuctionManagerStatus {
|
||||
Initialized,
|
||||
Validated,
|
||||
Running,
|
||||
Disbursing,
|
||||
Finished,
|
||||
}
|
||||
|
||||
export class AuctionManagerState {
|
||||
status: AuctionManagerStatus = AuctionManagerStatus.Initialized;
|
||||
winningConfigItemsValidated: number = 0;
|
||||
|
@ -355,14 +363,6 @@ export class AuctionManagerState {
|
|||
}
|
||||
}
|
||||
|
||||
export enum AuctionManagerStatus {
|
||||
Initialized,
|
||||
Validated,
|
||||
Running,
|
||||
Disbursing,
|
||||
Finished,
|
||||
}
|
||||
|
||||
export class BidRedemptionTicket {
|
||||
key: MetaplexKey = MetaplexKey.BidRedemptionTicketV1;
|
||||
participationRedeemed: boolean = false;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { Creator, MetadataCategory } from '@oyster/common';
|
||||
|
||||
export interface Auction {
|
||||
name: string;
|
||||
auctionerName: string;
|
||||
|
|
|
@ -31,5 +31,5 @@
|
|||
|
||||
.royalties {
|
||||
font-weight: 600;
|
||||
font-size: 2rem;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import React from 'react';
|
||||
import { Row, Col, Divider, Layout, Tag, Button } from 'antd';
|
||||
import { Row, Col, Divider, Layout, Tag, Button, Skeleton } from 'antd';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useArt, useExtendedArt } from './../../hooks';
|
||||
|
||||
import './index.less';
|
||||
import { ArtContent } from '../../components/ArtContent';
|
||||
import { shortenAddress, TokenAccount, useConnection, useUserAccounts, useWallet } from '@oyster/common';
|
||||
import { shortenAddress, useConnection, useWallet } from '@oyster/common';
|
||||
import { MetaAvatar } from '../../components/MetaAvatar';
|
||||
import { sendSignMetadata } from '../../actions/sendSignMetadata';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { ViewOn } from './../../components/ViewOn';
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
|
@ -72,47 +73,35 @@ export const ArtView = () => {
|
|||
md={{ span: 12 }}
|
||||
style={{ textAlign: 'left', fontSize: '1.4rem' }}
|
||||
>
|
||||
<div style={{ fontWeight: 700, fontSize: '4rem' }}>{art.title}</div>
|
||||
<br />
|
||||
<div className="info-header">CREATED BY</div>
|
||||
<Row>
|
||||
<div style={{ fontWeight: 700, fontSize: '4rem' }}>{art.title || <Skeleton paragraph={{ rows: 0 }} />}</div>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<h6>Royalties</h6>
|
||||
<div className="royalties">
|
||||
{((art.seller_fee_basis_points || 0) / 100).toFixed(2)}%
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<ViewOn id={id} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<h6 style={{ marginTop: 5 }}>Created By</h6>
|
||||
<div className="creators">
|
||||
{(art.creators || [])
|
||||
.map(creator => {
|
||||
return (
|
||||
<div
|
||||
className="info-content"
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
style={{ display: 'flex', alignItems: 'center', marginBottom: 5 }}
|
||||
>
|
||||
<MetaAvatar creators={[creator]} size={64} />
|
||||
<div>
|
||||
<span className="creator-name">
|
||||
{creator.name || shortenAddress(creator.address || '')}
|
||||
</span>
|
||||
{/* <Button
|
||||
onClick={async () => {
|
||||
if(!art.mint) {
|
||||
return;
|
||||
}
|
||||
const mint = new PublicKey(art.mint);
|
||||
|
||||
const account = accountByMint.get(art.mint);
|
||||
if(!account) {
|
||||
return;
|
||||
}
|
||||
|
||||
const owner = wallet?.publicKey;
|
||||
|
||||
if(!owner) {
|
||||
return;
|
||||
}
|
||||
const instructions: any[] = [];
|
||||
await updateMetadata(undefined, undefined, true, mint, owner, instructions)
|
||||
|
||||
sendTransaction(connection, wallet, instructions, [], true);
|
||||
}}
|
||||
>
|
||||
Mark as Sold
|
||||
</Button> */}
|
||||
<div style={{ marginLeft: 10 }}>
|
||||
{!creator.verified &&
|
||||
(creator.address === pubkey ? (
|
||||
|
@ -143,14 +132,37 @@ export const ArtView = () => {
|
|||
})}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* <Button
|
||||
onClick={async () => {
|
||||
if(!art.mint) {
|
||||
return;
|
||||
}
|
||||
const mint = new PublicKey(art.mint);
|
||||
|
||||
const account = accountByMint.get(art.mint);
|
||||
if(!account) {
|
||||
return;
|
||||
}
|
||||
|
||||
const owner = wallet?.publicKey;
|
||||
|
||||
if(!owner) {
|
||||
return;
|
||||
}
|
||||
const instructions: any[] = [];
|
||||
await updateMetadata(undefined, undefined, true, mint, owner, instructions)
|
||||
|
||||
sendTransaction(connection, wallet, instructions, [], true);
|
||||
}}
|
||||
>
|
||||
Mark as Sold
|
||||
</Button> */}
|
||||
</Col>
|
||||
<Col span="24">
|
||||
<Divider />
|
||||
<br />
|
||||
{art.creators?.find(c => !c.verified) && unverified}
|
||||
<div className="info-header">CREATOR ROYALTIES</div>
|
||||
<div className="royalties">
|
||||
{((art.seller_fee_basis_points || 0) / 100).toFixed(2)}%
|
||||
</div>
|
||||
<br />
|
||||
<div className="info-header">ABOUT THE CREATION</div>
|
||||
<div className="info-content">{description}</div>
|
||||
|
|
|
@ -58,7 +58,6 @@ export const AuctionItem = ({
|
|||
height: 300,
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<ArtContent
|
||||
pubkey={id}
|
||||
className="artwork-image stack-item"
|
||||
|
@ -66,7 +65,6 @@ export const AuctionItem = ({
|
|||
active={active}
|
||||
allowMeshRender={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -124,9 +122,9 @@ export const AuctionView = () => {
|
|||
{items}
|
||||
</Carousel>
|
||||
</div>
|
||||
<h6>NUMBER OF WINNERS</h6>
|
||||
<h6>Number Of Winners</h6>
|
||||
<h1>{winnerCount === undefined ? <Skeleton paragraph={{ rows: 0 }} /> : winnerCount}</h1>
|
||||
<h6>NUMBER OF NFTs</h6>
|
||||
<h6>Number Of NFTs</h6>
|
||||
<h1>{nftCount === undefined ? <Skeleton paragraph={{ rows: 0 }} /> : nftCount}</h1>
|
||||
<h6>About this {nftCount === 1 ? 'NFT' : 'Collection'}</h6>
|
||||
<p>
|
||||
|
|
Loading…
Reference in New Issue