Merge pull request #101 from metaplex-foundation/feature/performance

Feature/performance
This commit is contained in:
B 2021-06-30 00:29:33 -05:00 committed by GitHub
commit ad27a9ecf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 208 additions and 153 deletions

View File

@ -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,

View File

@ -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

View File

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

View File

@ -214,7 +214,7 @@ export async function sendRedeemBid(
}
if (
wallet.publicKey.toBase58() ==
wallet.publicKey.toBase58() ===
auctionView.auctionManager.info.authority.toBase58()
) {
await claimUnusedPrizes(

View File

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

View File

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

View File

@ -27,6 +27,7 @@ export const PreSaleBanner = ({ auction }: IPreSaleBanner) => {
<ArtContent
pubkey={id}
className="artwork-image"
allowMeshRender={true}
/>
</Col>
<Col md={12} className="presale-info">

View File

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

View File

@ -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: {

View File

@ -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(

View File

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

View File

@ -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;

View File

@ -1,5 +1,3 @@
import { Creator, MetadataCategory } from '@oyster/common';
export interface Auction {
name: string;
auctionerName: string;

View File

@ -31,5 +31,5 @@
.royalties {
font-weight: 600;
font-size: 2rem;
font-size: 1.3rem;
}

View File

@ -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,85 +73,96 @@ 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>
<div className="creators">
{(art.creators || [])
.map(creator => {
return (
<div
className="info-content"
style={{ display: 'flex', alignItems: 'center' }}
>
<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);
}}
<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
style={{ display: 'flex', alignItems: 'center', marginBottom: 5 }}
>
Mark as Sold
</Button> */}
<div style={{ marginLeft: 10 }}>
{!creator.verified &&
(creator.address === pubkey ? (
<Button
onClick={async () => {
try {
await sendSignMetadata(
connection,
wallet,
new PublicKey(id),
);
} catch (e) {
console.error(e);
return false;
}
return true;
}}
>
Approve
</Button>
) : (
tag
))}
<MetaAvatar creators={[creator]} size={64} />
<div>
<span className="creator-name">
{creator.name || shortenAddress(creator.address || '')}
</span>
<div style={{ marginLeft: 10 }}>
{!creator.verified &&
(creator.address === pubkey ? (
<Button
onClick={async () => {
try {
await sendSignMetadata(
connection,
wallet,
new PublicKey(id),
);
} catch (e) {
console.error(e);
return false;
}
return true;
}}
>
Approve
</Button>
) : (
tag
))}
</div>
</div>
</div>
</div>
</div>
);
})}
</div>
);
})}
</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>

View File

@ -58,15 +58,13 @@ export const AuctionItem = ({
height: 300,
};
return (
<div>
<ArtContent
pubkey={id}
className="artwork-image stack-item"
style={style}
active={active}
allowMeshRender={true}
/>
</div>
<ArtContent
pubkey={id}
className="artwork-image stack-item"
style={style}
active={active}
allowMeshRender={true}
/>
);
};
@ -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>