diff --git a/js/packages/common/src/actions/account.ts b/js/packages/common/src/actions/account.ts index 4ce7069..133222d 100644 --- a/js/packages/common/src/actions/account.ts +++ b/js/packages/common/src/actions/account.ts @@ -13,7 +13,8 @@ import { } from '../utils/ids'; import { programIds } from '../utils/programIds'; import { TokenAccount } from '../models/account'; -import { cache, TokenAccountParser } from '../contexts/accounts'; +import { cache } from '../contexts/accounts/cache'; +import { TokenAccountParser } from '../contexts/accounts/parsesrs'; export function ensureSplAccount( instructions: TransactionInstruction[], diff --git a/js/packages/common/src/actions/auction.ts b/js/packages/common/src/actions/auction.ts index c01a12c..793529a 100644 --- a/js/packages/common/src/actions/auction.ts +++ b/js/packages/common/src/actions/auction.ts @@ -8,7 +8,7 @@ import { import { programIds } from '../utils/programIds'; import { deserializeUnchecked, serialize } from 'borsh'; import BN from 'bn.js'; -import { AccountParser } from '../contexts'; +import { AccountParser } from '../contexts/accounts/types'; import moment from 'moment'; import { findProgramAddress, StringPublicKey, toPublicKey } from '../utils'; export const AUCTION_PREFIX = 'auction'; @@ -672,7 +672,7 @@ export async function createAuction( ); } -export async function startAuction( +export async function startAuctionWithResource( resource: StringPublicKey, creator: StringPublicKey, instructions: TransactionInstruction[], diff --git a/js/packages/common/src/contexts/accounts.tsx b/js/packages/common/src/contexts/accounts.tsx deleted file mode 100644 index b6b08a7..0000000 --- a/js/packages/common/src/contexts/accounts.tsx +++ /dev/null @@ -1,664 +0,0 @@ -import React, { - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from 'react'; -import { useConnection } from '../contexts/connection'; -import { useWallet } from '@solana/wallet-adapter-react'; -import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'; -import { AccountLayout, MintInfo, MintLayout, u64 } from '@solana/spl-token'; -import { TokenAccount } from '../models'; -import { chunks } from '../utils/utils'; -import { EventEmitter } from '../utils/eventEmitter'; -import { StringPublicKey, WRAPPED_SOL_MINT } from '../utils/ids'; -import { programIds } from '../utils/programIds'; - -const AccountsContext = React.createContext(null); - -const pendingCalls = new Map>(); -const genericCache = new Map(); -const pendingMintCalls = new Map>(); -const mintCache = new Map(); - -export interface ParsedAccountBase { - pubkey: StringPublicKey; - account: AccountInfo; - info: any; // TODO: change to unknown -} - -export type AccountParser = ( - pubkey: StringPublicKey, - data: AccountInfo, -) => ParsedAccountBase | undefined; - -export interface ParsedAccount extends ParsedAccountBase { - info: T; -} - -const getMintInfo = async (connection: Connection, pubKey: PublicKey) => { - const info = await connection.getAccountInfo(pubKey); - if (info === null) { - throw new Error('Failed to find mint account'); - } - - const data = Buffer.from(info.data); - - return deserializeMint(data); -}; - -export const MintParser = (pubKey: string, info: AccountInfo) => { - const buffer = Buffer.from(info.data); - - const data = deserializeMint(buffer); - - const details = { - pubkey: pubKey, - account: { - ...info, - }, - info: data, - } as ParsedAccountBase; - - return details; -}; - -export const TokenAccountParser = ( - pubKey: string, - info: AccountInfo, -) => { - // Sometimes a wrapped sol account gets closed, goes to 0 length, - // triggers an update over wss which triggers this guy to get called - // since your UI already logged that pubkey as a token account. Check for length. - if (info.data.length > 0) { - const buffer = Buffer.from(info.data); - const data = deserializeAccount(buffer); - - const details = { - pubkey: pubKey, - account: { - ...info, - }, - info: data, - } as TokenAccount; - - return details; - } -}; - -export const GenericAccountParser = ( - pubKey: string, - info: AccountInfo, -) => { - const buffer = Buffer.from(info.data); - - const details = { - pubkey: pubKey, - account: { - ...info, - }, - info: buffer, - } as ParsedAccountBase; - - return details; -}; - -export const keyToAccountParser = new Map(); - -export const cache = { - emitter: new EventEmitter(), - query: async ( - connection: Connection, - pubKey: string | PublicKey, - parser?: AccountParser, - ) => { - let id: PublicKey; - if (typeof pubKey === 'string') { - id = new PublicKey(pubKey); - } else { - id = pubKey; - } - - const address = id.toBase58(); - - let account = genericCache.get(address); - if (account) { - return account; - } - - let query = pendingCalls.get(address); - if (query) { - return query; - } - - // TODO: refactor to use multiple accounts query with flush like behavior - query = connection.getAccountInfo(id).then(data => { - if (!data) { - throw new Error('Account not found'); - } - - return cache.add(id, data, parser); - }) as Promise; - pendingCalls.set(address, query as any); - - return query; - }, - add: ( - id: PublicKey | string, - obj: AccountInfo, - parser?: AccountParser, - isActive?: boolean | undefined | ((parsed: any) => boolean), - ) => { - const address = typeof id === 'string' ? id : id?.toBase58(); - const deserialize = parser ? parser : keyToAccountParser.get(address); - if (!deserialize) { - throw new Error( - 'Deserializer needs to be registered or passed as a parameter', - ); - } - - cache.registerParser(id, deserialize); - pendingCalls.delete(address); - const account = deserialize(address, obj); - if (!account) { - return; - } - - if (isActive === undefined) isActive = true; - else if (isActive instanceof Function) isActive = isActive(account); - - const isNew = !genericCache.has(address); - - genericCache.set(address, account); - cache.emitter.raiseCacheUpdated(address, isNew, deserialize, isActive); - return account; - }, - get: (pubKey: string | PublicKey) => { - let key: string; - if (typeof pubKey !== 'string') { - key = pubKey.toBase58(); - } else { - key = pubKey; - } - - return genericCache.get(key); - }, - delete: (pubKey: string | PublicKey) => { - let key: string; - if (typeof pubKey !== 'string') { - key = pubKey.toBase58(); - } else { - key = pubKey; - } - - if (genericCache.get(key)) { - genericCache.delete(key); - cache.emitter.raiseCacheDeleted(key); - return true; - } - return false; - }, - - byParser: (parser: AccountParser) => { - const result: string[] = []; - for (const id of keyToAccountParser.keys()) { - if (keyToAccountParser.get(id) === parser) { - result.push(id); - } - } - - return result; - }, - registerParser: (pubkey: PublicKey | string, parser: AccountParser) => { - if (pubkey) { - const address = typeof pubkey === 'string' ? pubkey : pubkey?.toBase58(); - keyToAccountParser.set(address, parser); - } - - return pubkey; - }, - queryMint: async (connection: Connection, pubKey: string | PublicKey) => { - let id: PublicKey; - if (typeof pubKey === 'string') { - id = new PublicKey(pubKey); - } else { - id = pubKey; - } - - const address = id.toBase58(); - let mint = mintCache.get(address); - if (mint) { - return mint; - } - - let query = pendingMintCalls.get(address); - if (query) { - return query; - } - - query = getMintInfo(connection, id).then(data => { - pendingMintCalls.delete(address); - - mintCache.set(address, data); - return data; - }) as Promise; - pendingMintCalls.set(address, query as any); - - return query; - }, - getMint: (pubKey: string | PublicKey) => { - let key: string; - if (typeof pubKey !== 'string') { - key = pubKey.toBase58(); - } else { - key = pubKey; - } - - return mintCache.get(key); - }, - addMint: (pubKey: PublicKey, obj: AccountInfo) => { - const mint = deserializeMint(obj.data); - const id = pubKey.toBase58(); - mintCache.set(id, mint); - return mint; - }, -}; - -export const useAccountsContext = () => { - const context = useContext(AccountsContext); - - return context; -}; - -function wrapNativeAccount( - pubkey: string, - account?: AccountInfo, -): TokenAccount | undefined { - if (!account) { - return undefined; - } - - const key = new PublicKey(pubkey); - - return { - pubkey: pubkey, - account, - info: { - address: key, - mint: WRAPPED_SOL_MINT, - owner: key, - amount: new u64(account.lamports), - delegate: null, - delegatedAmount: new u64(0), - isInitialized: true, - isFrozen: false, - isNative: true, - rentExemptReserve: null, - closeAuthority: null, - }, - }; -} - -export const getCachedAccount = ( - predicate: (account: TokenAccount) => boolean, -) => { - for (const account of genericCache.values()) { - if (predicate(account)) { - return account as TokenAccount; - } - } -}; - -const UseNativeAccount = () => { - const connection = useConnection(); - const { publicKey } = useWallet(); - - const [nativeAccount, setNativeAccount] = useState>(); - - const updateCache = useCallback( - account => { - if (publicKey) { - const wrapped = wrapNativeAccount(publicKey.toBase58(), account); - if (wrapped !== undefined) { - const id = publicKey.toBase58(); - cache.registerParser(id, TokenAccountParser); - genericCache.set(id, wrapped as TokenAccount); - cache.emitter.raiseCacheUpdated(id, false, TokenAccountParser, true); - } - } - }, - [publicKey], - ); - - useEffect(() => { - let subId = 0; - const updateAccount = (account: AccountInfo | null) => { - if (account) { - updateCache(account); - setNativeAccount(account); - } - }; - - (async () => { - if (!connection || !publicKey) { - return; - } - - const account = await connection.getAccountInfo(publicKey); - updateAccount(account); - - subId = connection.onAccountChange(publicKey, updateAccount); - })(); - - return () => { - if (subId) { - connection.removeAccountChangeListener(subId); - } - }; - }, [setNativeAccount, publicKey, connection, updateCache]); - - return { nativeAccount }; -}; - -const PRECACHED_OWNERS = new Set(); -const precacheUserTokenAccounts = async ( - connection: Connection, - owner?: PublicKey, -) => { - if (!owner) { - return; - } - - // used for filtering account updates over websocket - PRECACHED_OWNERS.add(owner.toBase58()); - - // user accounts are updated via ws subscription - const accounts = await connection.getTokenAccountsByOwner(owner, { - programId: programIds().token, - }); - - accounts.value.forEach(info => { - cache.add(info.pubkey.toBase58(), info.account, TokenAccountParser); - }); -}; - -export function AccountsProvider({ children = null as any }) { - const connection = useConnection(); - const { publicKey } = useWallet(); - const [tokenAccounts, setTokenAccounts] = useState([]); - const [userAccounts, setUserAccounts] = useState([]); - const { nativeAccount } = UseNativeAccount(); - const walletKey = publicKey?.toBase58(); - - const selectUserAccounts = useCallback(() => { - return cache - .byParser(TokenAccountParser) - .map(id => cache.get(id)) - .filter(a => a && a.info.owner.toBase58() === walletKey) - .map(a => a as TokenAccount); - }, [walletKey]); - - useEffect(() => { - const accounts = selectUserAccounts().filter( - a => a !== undefined, - ) as TokenAccount[]; - setUserAccounts(accounts); - }, [nativeAccount, tokenAccounts, selectUserAccounts]); - - useEffect(() => { - const subs: number[] = []; - cache.emitter.onCache(args => { - if (args.isNew && args.isActive) { - let id = args.id; - let deserialize = args.parser; - connection.onAccountChange(new PublicKey(id), info => { - cache.add(id, info, deserialize); - }); - } - }); - - return () => { - subs.forEach(id => connection.removeAccountChangeListener(id)); - }; - }, [connection]); - - useEffect(() => { - if (!connection || !publicKey) { - setTokenAccounts([]); - } else { - precacheUserTokenAccounts(connection, publicKey).then(() => { - setTokenAccounts(selectUserAccounts()); - }); - - // This can return different types of accounts: token-account, mint, multisig - // TODO: web3.js expose ability to filter. - // this should use only filter syntax to only get accounts that are owned by user - const tokenSubID = connection.onProgramAccountChange( - programIds().token, - info => { - // TODO: fix type in web3.js - const id = info.accountId as unknown as string; - // TODO: do we need a better way to identify layout (maybe a enum identifing type?) - if (info.accountInfo.data.length === AccountLayout.span) { - const data = deserializeAccount(info.accountInfo.data); - - if (PRECACHED_OWNERS.has(data.owner.toBase58())) { - cache.add(id, info.accountInfo, TokenAccountParser); - setTokenAccounts(selectUserAccounts()); - } - } - }, - 'singleGossip', - ); - - return () => { - connection.removeProgramAccountChangeListener(tokenSubID); - }; - } - }, [connection, publicKey, selectUserAccounts]); - - return ( - - {children} - - ); -} - -export function useNativeAccount() { - const context = useContext(AccountsContext); - return { - account: context.nativeAccount as AccountInfo, - }; -} - -export const getMultipleAccounts = async ( - connection: any, - keys: string[], - commitment: string, -) => { - const result = await Promise.all( - chunks(keys, 99).map(chunk => - getMultipleAccountsCore(connection, chunk, commitment), - ), - ); - - const array = result - .map( - a => - a.array.map(acc => { - if (!acc) { - return undefined; - } - - const { data, ...rest } = acc; - const obj = { - ...rest, - data: Buffer.from(data[0], 'base64'), - } as AccountInfo; - return obj; - }) as AccountInfo[], - ) - .flat(); - return { keys, array }; -}; - -const getMultipleAccountsCore = async ( - connection: any, - keys: string[], - commitment: string, -) => { - const args = connection._buildArgs([keys], commitment, 'base64'); - - const unsafeRes = await connection._rpcRequest('getMultipleAccounts', args); - if (unsafeRes.error) { - throw new Error( - 'failed to get info about account ' + unsafeRes.error.message, - ); - } - - if (unsafeRes.result.value) { - const array = unsafeRes.result.value as AccountInfo[]; - return { keys, array }; - } - - // TODO: fix - throw new Error(); -}; - -export function useMint(key?: string | PublicKey) { - const connection = useConnection(); - const [mint, setMint] = useState(); - - const id = typeof key === 'string' ? key : key?.toBase58(); - - useEffect(() => { - if (!id) { - return; - } - - cache - .query(connection, id, MintParser) - .then(acc => setMint(acc.info as any)) - .catch(err => console.log(err)); - - const dispose = cache.emitter.onCache(e => { - const event = e; - if (event.id === id) { - cache - .query(connection, id, MintParser) - .then(mint => setMint(mint.info as any)); - } - }); - return () => { - dispose(); - }; - }, [connection, id]); - - return mint; -} - -export function useAccount(pubKey?: PublicKey) { - const connection = useConnection(); - const [account, setAccount] = useState(); - - const key = pubKey?.toBase58(); - useEffect(() => { - const query = async () => { - try { - if (!key) { - return; - } - - const acc = await cache - .query(connection, key, TokenAccountParser) - .catch(err => console.log(err)); - if (acc) { - setAccount(acc); - } - } catch (err) { - console.error(err); - } - }; - - query(); - - const dispose = cache.emitter.onCache(e => { - const event = e; - if (event.id === key) { - query(); - } - }); - return () => { - dispose(); - }; - }, [connection, key]); - - return account; -} - -// TODO: expose in spl package -export const deserializeAccount = (data: Buffer) => { - const accountInfo = AccountLayout.decode(data); - accountInfo.mint = new PublicKey(accountInfo.mint); - accountInfo.owner = new PublicKey(accountInfo.owner); - accountInfo.amount = u64.fromBuffer(accountInfo.amount); - - if (accountInfo.delegateOption === 0) { - accountInfo.delegate = null; - accountInfo.delegatedAmount = new u64(0); - } else { - accountInfo.delegate = new PublicKey(accountInfo.delegate); - accountInfo.delegatedAmount = u64.fromBuffer(accountInfo.delegatedAmount); - } - - accountInfo.isInitialized = accountInfo.state !== 0; - accountInfo.isFrozen = accountInfo.state === 2; - - if (accountInfo.isNativeOption === 1) { - accountInfo.rentExemptReserve = u64.fromBuffer(accountInfo.isNative); - accountInfo.isNative = true; - } else { - accountInfo.rentExemptReserve = null; - accountInfo.isNative = false; - } - - if (accountInfo.closeAuthorityOption === 0) { - accountInfo.closeAuthority = null; - } else { - accountInfo.closeAuthority = new PublicKey(accountInfo.closeAuthority); - } - - return accountInfo; -}; - -// TODO: expose in spl package -export const deserializeMint = (data: Buffer) => { - if (data.length !== MintLayout.span) { - throw new Error('Not a valid Mint'); - } - - const mintInfo = MintLayout.decode(data); - - if (mintInfo.mintAuthorityOption === 0) { - mintInfo.mintAuthority = null; - } else { - mintInfo.mintAuthority = new PublicKey(mintInfo.mintAuthority); - } - - mintInfo.supply = u64.fromBuffer(mintInfo.supply); - mintInfo.isInitialized = mintInfo.isInitialized !== 0; - - if (mintInfo.freezeAuthorityOption === 0) { - mintInfo.freezeAuthority = null; - } else { - mintInfo.freezeAuthority = new PublicKey(mintInfo.freezeAuthority); - } - - return mintInfo as MintInfo; -}; diff --git a/js/packages/common/src/contexts/accounts/accounts.tsx b/js/packages/common/src/contexts/accounts/accounts.tsx new file mode 100644 index 0000000..656f85d --- /dev/null +++ b/js/packages/common/src/contexts/accounts/accounts.tsx @@ -0,0 +1,286 @@ +import React, { useCallback, useContext, useEffect, useState } from 'react'; +import { useWallet } from '@solana/wallet-adapter-react'; +import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'; +import { AccountLayout, MintInfo, u64 } from '@solana/spl-token'; +import { useConnection } from '../../contexts/connection'; +import { TokenAccount } from '../../models'; +import { StringPublicKey, WRAPPED_SOL_MINT } from '../../utils/ids'; +import { programIds } from '../../utils/programIds'; +import { genericCache, cache } from './cache'; +import { deserializeAccount } from './deserialize'; +import { TokenAccountParser, MintParser } from './parsesrs'; + +const AccountsContext = React.createContext(null); + +export const useAccountsContext = () => { + const context = useContext(AccountsContext); + + return context; +}; + +function wrapNativeAccount( + pubkey: StringPublicKey, + account?: AccountInfo, +): TokenAccount | undefined { + if (!account) { + return undefined; + } + + const key = new PublicKey(pubkey); + + return { + pubkey: pubkey, + account, + info: { + address: key, + mint: WRAPPED_SOL_MINT, + owner: key, + amount: new u64(account.lamports), + delegate: null, + delegatedAmount: new u64(0), + isInitialized: true, + isFrozen: false, + isNative: true, + rentExemptReserve: null, + closeAuthority: null, + }, + }; +} + +const UseNativeAccount = () => { + const connection = useConnection(); + const { publicKey } = useWallet(); + + const [nativeAccount, setNativeAccount] = useState>(); + + const updateCache = useCallback( + account => { + if (publicKey) { + const wrapped = wrapNativeAccount(publicKey.toBase58(), account); + if (wrapped !== undefined) { + const id = publicKey.toBase58(); + cache.registerParser(id, TokenAccountParser); + genericCache.set(id, wrapped as TokenAccount); + cache.emitter.raiseCacheUpdated(id, false, TokenAccountParser, true); + } + } + }, + [publicKey], + ); + + useEffect(() => { + let subId = 0; + const updateAccount = (account: AccountInfo | null) => { + if (account) { + updateCache(account); + setNativeAccount(account); + } + }; + + (async () => { + if (!connection || !publicKey) { + return; + } + + const account = await connection.getAccountInfo(publicKey); + updateAccount(account); + + subId = connection.onAccountChange(publicKey, updateAccount); + })(); + + return () => { + if (subId) { + connection.removeAccountChangeListener(subId); + } + }; + }, [setNativeAccount, publicKey, connection, updateCache]); + + return { nativeAccount }; +}; + +const PRECACHED_OWNERS = new Set(); +const precacheUserTokenAccounts = async ( + connection: Connection, + owner?: PublicKey, +) => { + if (!owner) { + return; + } + + // used for filtering account updates over websocket + PRECACHED_OWNERS.add(owner.toBase58()); + + // user accounts are updated via ws subscription + const accounts = await connection.getTokenAccountsByOwner(owner, { + programId: programIds().token, + }); + + accounts.value.forEach(info => { + cache.add(info.pubkey.toBase58(), info.account, TokenAccountParser); + }); +}; + +export function AccountsProvider({ children = null as any }) { + const connection = useConnection(); + const { publicKey } = useWallet(); + const [tokenAccounts, setTokenAccounts] = useState([]); + const [userAccounts, setUserAccounts] = useState([]); + const { nativeAccount } = UseNativeAccount(); + const walletKey = publicKey?.toBase58(); + + const selectUserAccounts = useCallback(() => { + return cache + .byParser(TokenAccountParser) + .map(id => cache.get(id)) + .filter(a => a && a.info.owner.toBase58() === walletKey) + .map(a => a as TokenAccount); + }, [walletKey]); + + useEffect(() => { + const accounts = selectUserAccounts().filter( + a => a !== undefined, + ) as TokenAccount[]; + setUserAccounts(accounts); + }, [nativeAccount, tokenAccounts, selectUserAccounts]); + + useEffect(() => { + const subs: number[] = []; + cache.emitter.onCache(args => { + if (args.isNew && args.isActive) { + let id = args.id; + let deserialize = args.parser; + connection.onAccountChange(new PublicKey(id), info => { + cache.add(id, info, deserialize); + }); + } + }); + + return () => { + subs.forEach(id => connection.removeAccountChangeListener(id)); + }; + }, [connection]); + + useEffect(() => { + if (!connection || !publicKey) { + setTokenAccounts([]); + } else { + precacheUserTokenAccounts(connection, publicKey).then(() => { + setTokenAccounts(selectUserAccounts()); + }); + + // This can return different types of accounts: token-account, mint, multisig + // TODO: web3.js expose ability to filter. + // this should use only filter syntax to only get accounts that are owned by user + const tokenSubID = connection.onProgramAccountChange( + programIds().token, + info => { + // TODO: fix type in web3.js + const id = info.accountId as unknown as string; + // TODO: do we need a better way to identify layout (maybe a enum identifing type?) + if (info.accountInfo.data.length === AccountLayout.span) { + const data = deserializeAccount(info.accountInfo.data); + + if (PRECACHED_OWNERS.has(data.owner.toBase58())) { + cache.add(id, info.accountInfo, TokenAccountParser); + setTokenAccounts(selectUserAccounts()); + } + } + }, + 'singleGossip', + ); + + return () => { + connection.removeProgramAccountChangeListener(tokenSubID); + }; + } + }, [connection, publicKey, selectUserAccounts]); + + return ( + + {children} + + ); +} + +export function useNativeAccount() { + const context = useContext(AccountsContext); + return { + account: context.nativeAccount as AccountInfo, + }; +} + +export function useMint(key?: string | PublicKey) { + const connection = useConnection(); + const [mint, setMint] = useState(); + + const id = typeof key === 'string' ? key : key?.toBase58(); + + useEffect(() => { + if (!id) { + return; + } + + cache + .query(connection, id, MintParser) + .then(acc => setMint(acc.info as any)) + .catch(err => console.log(err)); + + const dispose = cache.emitter.onCache(e => { + const event = e; + if (event.id === id) { + cache + .query(connection, id, MintParser) + .then(mint => setMint(mint.info as any)); + } + }); + return () => { + dispose(); + }; + }, [connection, id]); + + return mint; +} + +export function useAccount(pubKey?: PublicKey) { + const connection = useConnection(); + const [account, setAccount] = useState(); + + const key = pubKey?.toBase58(); + useEffect(() => { + const query = async () => { + try { + if (!key) { + return; + } + + const acc = await cache + .query(connection, key, TokenAccountParser) + .catch(err => console.log(err)); + if (acc) { + setAccount(acc); + } + } catch (err) { + console.error(err); + } + }; + + query(); + + const dispose = cache.emitter.onCache(e => { + const event = e; + if (event.id === key) { + query(); + } + }); + return () => { + dispose(); + }; + }, [connection, key]); + + return account; +} diff --git a/js/packages/common/src/contexts/accounts/cache.ts b/js/packages/common/src/contexts/accounts/cache.ts new file mode 100644 index 0000000..5e88ffe --- /dev/null +++ b/js/packages/common/src/contexts/accounts/cache.ts @@ -0,0 +1,193 @@ +import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'; +import { MintInfo } from '@solana/spl-token'; +import { TokenAccount } from '../../models'; +import { EventEmitter } from '../../utils/eventEmitter'; +import { ParsedAccountBase, AccountParser } from './types'; +import { deserializeMint } from './deserialize'; + +export const genericCache = new Map(); +const mintCache = new Map(); +const pendingCalls = new Map>(); +const pendingMintCalls = new Map>(); + +const keyToAccountParser = new Map(); + +const getMintInfo = async (connection: Connection, pubKey: PublicKey) => { + const info = await connection.getAccountInfo(pubKey); + if (info === null) { + throw new Error('Failed to find mint account'); + } + + const data = Buffer.from(info.data); + + return deserializeMint(data); +}; + +export const cache = { + emitter: new EventEmitter(), + query: async ( + connection: Connection, + pubKey: string | PublicKey, + parser?: AccountParser, + ) => { + let id: PublicKey; + if (typeof pubKey === 'string') { + id = new PublicKey(pubKey); + } else { + id = pubKey; + } + + const address = id.toBase58(); + + const account = genericCache.get(address); + if (account) { + return account; + } + + let query = pendingCalls.get(address); + if (query) { + return query; + } + + // TODO: refactor to use multiple accounts query with flush like behavior + query = connection.getAccountInfo(id).then(data => { + if (!data) { + throw new Error('Account not found'); + } + + return cache.add(id, data, parser); + }) as Promise; + pendingCalls.set(address, query as any); + + return query; + }, + add: ( + id: PublicKey | string, + obj: AccountInfo, + parser?: AccountParser, + isActive?: boolean | undefined | ((parsed: any) => boolean), + ) => { + const address = typeof id === 'string' ? id : id?.toBase58(); + const deserialize = parser ? parser : keyToAccountParser.get(address); + if (!deserialize) { + throw new Error( + 'Deserializer needs to be registered or passed as a parameter', + ); + } + + cache.registerParser(id, deserialize); + pendingCalls.delete(address); + const account = deserialize(address, obj); + if (!account) { + return; + } + + if (isActive === undefined) isActive = true; + else if (isActive instanceof Function) isActive = isActive(account); + + const isNew = !genericCache.has(address); + + genericCache.set(address, account); + cache.emitter.raiseCacheUpdated(address, isNew, deserialize, isActive); + return account; + }, + get: (pubKey: string | PublicKey) => { + let key: string; + if (typeof pubKey !== 'string') { + key = pubKey.toBase58(); + } else { + key = pubKey; + } + + return genericCache.get(key); + }, + delete: (pubKey: string | PublicKey) => { + let key: string; + if (typeof pubKey !== 'string') { + key = pubKey.toBase58(); + } else { + key = pubKey; + } + + if (genericCache.get(key)) { + genericCache.delete(key); + cache.emitter.raiseCacheDeleted(key); + return true; + } + return false; + }, + + byParser: (parser: AccountParser) => { + const result: string[] = []; + for (const id of keyToAccountParser.keys()) { + if (keyToAccountParser.get(id) === parser) { + result.push(id); + } + } + + return result; + }, + registerParser: (pubkey: PublicKey | string, parser: AccountParser) => { + if (pubkey) { + const address = typeof pubkey === 'string' ? pubkey : pubkey?.toBase58(); + keyToAccountParser.set(address, parser); + } + + return pubkey; + }, + queryMint: async (connection: Connection, pubKey: string | PublicKey) => { + let id: PublicKey; + if (typeof pubKey === 'string') { + id = new PublicKey(pubKey); + } else { + id = pubKey; + } + + const address = id.toBase58(); + const mint = mintCache.get(address); + if (mint) { + return mint; + } + + let query = pendingMintCalls.get(address); + if (query) { + return query; + } + + query = getMintInfo(connection, id).then(data => { + pendingMintCalls.delete(address); + + mintCache.set(address, data); + return data; + }) as Promise; + pendingMintCalls.set(address, query as any); + + return query; + }, + getMint: (pubKey: string | PublicKey) => { + let key: string; + if (typeof pubKey !== 'string') { + key = pubKey.toBase58(); + } else { + key = pubKey; + } + + return mintCache.get(key); + }, + addMint: (pubKey: PublicKey, obj: AccountInfo) => { + const mint = deserializeMint(obj.data); + const id = pubKey.toBase58(); + mintCache.set(id, mint); + return mint; + }, +}; + +export const getCachedAccount = ( + predicate: (account: TokenAccount) => boolean, +) => { + for (const account of genericCache.values()) { + if (predicate(account)) { + return account as TokenAccount; + } + } +}; diff --git a/js/packages/common/src/contexts/accounts/deserialize.ts b/js/packages/common/src/contexts/accounts/deserialize.ts new file mode 100644 index 0000000..a5ef201 --- /dev/null +++ b/js/packages/common/src/contexts/accounts/deserialize.ts @@ -0,0 +1,63 @@ +import { PublicKey } from '@solana/web3.js'; +import { AccountLayout, MintInfo, MintLayout, u64 } from '@solana/spl-token'; + +// TODO: expose in spl package +export const deserializeAccount = (data: Buffer) => { + const accountInfo = AccountLayout.decode(data); + accountInfo.mint = new PublicKey(accountInfo.mint); + accountInfo.owner = new PublicKey(accountInfo.owner); + accountInfo.amount = u64.fromBuffer(accountInfo.amount); + + if (accountInfo.delegateOption === 0) { + accountInfo.delegate = null; + accountInfo.delegatedAmount = new u64(0); + } else { + accountInfo.delegate = new PublicKey(accountInfo.delegate); + accountInfo.delegatedAmount = u64.fromBuffer(accountInfo.delegatedAmount); + } + + accountInfo.isInitialized = accountInfo.state !== 0; + accountInfo.isFrozen = accountInfo.state === 2; + + if (accountInfo.isNativeOption === 1) { + accountInfo.rentExemptReserve = u64.fromBuffer(accountInfo.isNative); + accountInfo.isNative = true; + } else { + accountInfo.rentExemptReserve = null; + accountInfo.isNative = false; + } + + if (accountInfo.closeAuthorityOption === 0) { + accountInfo.closeAuthority = null; + } else { + accountInfo.closeAuthority = new PublicKey(accountInfo.closeAuthority); + } + + return accountInfo; +}; + +// TODO: expose in spl package +export const deserializeMint = (data: Buffer) => { + if (data.length !== MintLayout.span) { + throw new Error('Not a valid Mint'); + } + + const mintInfo = MintLayout.decode(data); + + if (mintInfo.mintAuthorityOption === 0) { + mintInfo.mintAuthority = null; + } else { + mintInfo.mintAuthority = new PublicKey(mintInfo.mintAuthority); + } + + mintInfo.supply = u64.fromBuffer(mintInfo.supply); + mintInfo.isInitialized = mintInfo.isInitialized !== 0; + + if (mintInfo.freezeAuthorityOption === 0) { + mintInfo.freezeAuthority = null; + } else { + mintInfo.freezeAuthority = new PublicKey(mintInfo.freezeAuthority); + } + + return mintInfo as MintInfo; +}; diff --git a/js/packages/common/src/contexts/accounts/getMultipleAccounts.ts b/js/packages/common/src/contexts/accounts/getMultipleAccounts.ts new file mode 100644 index 0000000..d0611c4 --- /dev/null +++ b/js/packages/common/src/contexts/accounts/getMultipleAccounts.ts @@ -0,0 +1,56 @@ +import { AccountInfo } from '@solana/web3.js'; +import { chunks } from '../../utils/utils'; + +export const getMultipleAccounts = async ( + connection: any, + keys: string[], + commitment: string, +) => { + const result = await Promise.all( + chunks(keys, 99).map(chunk => + getMultipleAccountsCore(connection, chunk, commitment), + ), + ); + + const array = result + .map( + a => + a.array.map(acc => { + if (!acc) { + return undefined; + } + + const { data, ...rest } = acc; + const obj = { + ...rest, + data: Buffer.from(data[0], 'base64'), + } as AccountInfo; + return obj; + }) as AccountInfo[], + ) + .flat(); + return { keys, array }; +}; + +const getMultipleAccountsCore = async ( + connection: any, + keys: string[], + commitment: string, +) => { + const args = connection._buildArgs([keys], commitment, 'base64'); + + const unsafeRes = await connection._rpcRequest('getMultipleAccounts', args); + if (unsafeRes.error) { + throw new Error( + 'failed to get info about account ' + unsafeRes.error.message, + ); + } + + if (unsafeRes.result.value) { + const array = unsafeRes.result.value as AccountInfo[]; + return { keys, array }; + } + + // TODO: fix + throw new Error(); +}; diff --git a/js/packages/common/src/contexts/accounts/index.ts b/js/packages/common/src/contexts/accounts/index.ts new file mode 100644 index 0000000..8c602f0 --- /dev/null +++ b/js/packages/common/src/contexts/accounts/index.ts @@ -0,0 +1,6 @@ +export * from './accounts'; +export * from './cache'; +export * from './getMultipleAccounts'; +export * from './parsesrs'; +export * from './deserialize'; +export * from './types'; diff --git a/js/packages/common/src/contexts/accounts/parsesrs.ts b/js/packages/common/src/contexts/accounts/parsesrs.ts new file mode 100644 index 0000000..174ab9b --- /dev/null +++ b/js/packages/common/src/contexts/accounts/parsesrs.ts @@ -0,0 +1,64 @@ +import { AccountInfo } from '@solana/web3.js'; +import { TokenAccount } from '../../models'; +import { ParsedAccountBase } from './types'; +import { deserializeMint, deserializeAccount } from './deserialize'; +import { StringPublicKey } from '../../utils'; + +export const MintParser = ( + pubKey: StringPublicKey, + info: AccountInfo, +) => { + const buffer = Buffer.from(info.data); + + const data = deserializeMint(buffer); + + const details = { + pubkey: pubKey, + account: { + ...info, + }, + info: data, + } as ParsedAccountBase; + + return details; +}; + +export const TokenAccountParser = ( + pubKey: StringPublicKey, + info: AccountInfo, +) => { + // Sometimes a wrapped sol account gets closed, goes to 0 length, + // triggers an update over wss which triggers this guy to get called + // since your UI already logged that pubkey as a token account. Check for length. + if (info.data.length > 0) { + const buffer = Buffer.from(info.data); + const data = deserializeAccount(buffer); + + const details = { + pubkey: pubKey, + account: { + ...info, + }, + info: data, + } as TokenAccount; + + return details; + } +}; + +export const GenericAccountParser = ( + pubKey: StringPublicKey, + info: AccountInfo, +) => { + const buffer = Buffer.from(info.data); + + const details = { + pubkey: pubKey, + account: { + ...info, + }, + info: buffer, + } as ParsedAccountBase; + + return details; +}; diff --git a/js/packages/common/src/contexts/accounts/types.ts b/js/packages/common/src/contexts/accounts/types.ts new file mode 100644 index 0000000..a44a3ca --- /dev/null +++ b/js/packages/common/src/contexts/accounts/types.ts @@ -0,0 +1,17 @@ +import { AccountInfo } from '@solana/web3.js'; +import { StringPublicKey } from '../../utils'; + +export interface ParsedAccountBase { + pubkey: StringPublicKey; + account: AccountInfo; + info: any; // TODO: change to unknown +} + +export type AccountParser = ( + pubkey: StringPublicKey, + data: AccountInfo, +) => ParsedAccountBase | undefined; + +export interface ParsedAccount extends ParsedAccountBase { + info: T; +} diff --git a/js/packages/common/src/contexts/index.tsx b/js/packages/common/src/contexts/index.tsx index 05fbc46..55bff1c 100644 --- a/js/packages/common/src/contexts/index.tsx +++ b/js/packages/common/src/contexts/index.tsx @@ -4,5 +4,5 @@ export * as Connection from './connection'; export * from './connection'; export * as Wallet from './wallet'; export * from './wallet'; -export * as Store from './store'; export * from './store'; +export * from './meta'; diff --git a/js/packages/common/src/contexts/meta/getEmptyMetaState.ts b/js/packages/common/src/contexts/meta/getEmptyMetaState.ts new file mode 100644 index 0000000..bd6a1db --- /dev/null +++ b/js/packages/common/src/contexts/meta/getEmptyMetaState.ts @@ -0,0 +1,27 @@ +import { MetaState } from './types'; + +export const getEmptyMetaState = (): MetaState => ({ + metadata: [], + metadataByMint: {}, + masterEditions: {}, + masterEditionsByPrintingMint: {}, + masterEditionsByOneTimeAuthMint: {}, + metadataByMasterEdition: {}, + editions: {}, + auctionManagersByAuction: {}, + bidRedemptions: {}, + auctions: {}, + auctionDataExtended: {}, + vaults: {}, + payoutTickets: {}, + store: null, + whitelistedCreatorsByCreator: {}, + bidderMetadataByAuctionAndBidder: {}, + bidderPotsByAuctionAndBidder: {}, + safetyDepositBoxesByVaultAndIndex: {}, + prizeTrackingTickets: {}, + safetyDepositConfigsByAuctionManagerAndIndex: {}, + bidRedemptionV2sByAuctionManagerAndWinningIndex: {}, + stores: {}, + creators: {}, +}); diff --git a/js/packages/common/src/contexts/meta/index.ts b/js/packages/common/src/contexts/meta/index.ts new file mode 100644 index 0000000..cdf9404 --- /dev/null +++ b/js/packages/common/src/contexts/meta/index.ts @@ -0,0 +1,11 @@ +export * from './meta'; +export * from './isMetadataPartOfStore'; +export * from './loadAccounts'; +export * from './onChangeAccount'; +export * from './subscribeAccountsChange'; +export * from './processAuctions'; +export * from './processMetaData'; +export * from './processMetaplexAccounts'; +export * from './processVaultData'; +export * from './queryExtendedMetadata'; +export * from './types'; diff --git a/js/packages/web/src/contexts/meta/isMetadataPartOfStore.ts b/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts similarity index 81% rename from js/packages/web/src/contexts/meta/isMetadataPartOfStore.ts rename to js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts index ca7dabb..2bcf158 100644 --- a/js/packages/web/src/contexts/meta/isMetadataPartOfStore.ts +++ b/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts @@ -1,5 +1,6 @@ -import { Metadata, ParsedAccount } from '@oyster/common'; +import { Metadata } from '../../actions'; import { Store, WhitelistedCreator } from '../../models/metaplex'; +import { ParsedAccount } from '../accounts/types'; export const isMetadataPartOfStore = ( m: ParsedAccount, @@ -8,11 +9,7 @@ export const isMetadataPartOfStore = ( string, ParsedAccount >, - useAll: boolean, ) => { - if (useAll) { - return true; - } if (!m?.info?.data?.creators || !store?.info) { return false; } diff --git a/js/packages/web/src/contexts/meta/loadAccounts.ts b/js/packages/common/src/contexts/meta/loadAccounts.ts similarity index 68% rename from js/packages/web/src/contexts/meta/loadAccounts.ts rename to js/packages/common/src/contexts/meta/loadAccounts.ts index f3551ab..f79ac02 100644 --- a/js/packages/web/src/contexts/meta/loadAccounts.ts +++ b/js/packages/common/src/contexts/meta/loadAccounts.ts @@ -5,7 +5,18 @@ import { StringPublicKey, toPublicKey, VAULT_ID, -} from '@oyster/common/dist/lib/utils/ids'; +} from '../../utils/ids'; +import { MAX_WHITELISTED_CREATOR_SIZE } from '../../models'; +import { + getEdition, + Metadata, + MAX_CREATOR_LEN, + MAX_CREATOR_LIMIT, + MAX_NAME_LENGTH, + MAX_SYMBOL_LENGTH, + MAX_URI_LENGTH, + METADATA_PREFIX, +} from '../../actions'; import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'; import { AccountAndPubkey, MetaState, ProcessAccountsFunc } from './types'; import { isMetadataPartOfStore } from './isMetadataPartOfStore'; @@ -13,19 +24,9 @@ import { processAuctions } from './processAuctions'; import { processMetaplexAccounts } from './processMetaplexAccounts'; import { processMetaData } from './processMetaData'; import { processVaultData } from './processVaultData'; -import { - getEdition, - getMultipleAccounts, - MAX_CREATOR_LEN, - MAX_CREATOR_LIMIT, - MAX_NAME_LENGTH, - MAX_SYMBOL_LENGTH, - MAX_URI_LENGTH, - Metadata, - METADATA_PREFIX, - ParsedAccount, -} from '@oyster/common'; -import { MAX_WHITELISTED_CREATOR_SIZE } from '../../models/metaplex'; +import { ParsedAccount } from '../accounts/types'; +import { getEmptyMetaState } from './getEmptyMetaState'; +import { getMultipleAccounts } from '../accounts/getMultipleAccounts'; async function getProgramAccounts( connection: Connection, @@ -82,30 +83,7 @@ async function getProgramAccounts( } export const loadAccounts = async (connection: Connection, all: boolean) => { - const tempCache: MetaState = { - metadata: [], - metadataByMint: {}, - masterEditions: {}, - masterEditionsByPrintingMint: {}, - masterEditionsByOneTimeAuthMint: {}, - metadataByMasterEdition: {}, - editions: {}, - auctionManagersByAuction: {}, - bidRedemptions: {}, - auctions: {}, - auctionDataExtended: {}, - vaults: {}, - payoutTickets: {}, - store: null, - whitelistedCreatorsByCreator: {}, - bidderMetadataByAuctionAndBidder: {}, - bidderPotsByAuctionAndBidder: {}, - safetyDepositBoxesByVaultAndIndex: {}, - prizeTrackingTickets: {}, - safetyDepositConfigsByAuctionManagerAndIndex: {}, - bidRedemptionV2sByAuctionManagerAndWinningIndex: {}, - stores: {}, - }; + const tempCache: MetaState = getEmptyMetaState(); const updateTemp = makeSetter(tempCache); const forEach = @@ -115,89 +93,58 @@ export const loadAccounts = async (connection: Connection, all: boolean) => { } }; - const additionalPromises: Promise[] = []; + let isSelectivePullMetadata = false; + const pullMetadata = async (creators: AccountAndPubkey[]) => { + await forEach(processMetaplexAccounts)(creators); - const IS_BIG_STORE = - process.env.NEXT_PUBLIC_BIG_STORE?.toLowerCase() === 'true'; - console.log(`Is big store: ${IS_BIG_STORE}`); + const whitelistedCreators = Object.values( + tempCache.whitelistedCreatorsByCreator, + ); - const promises = [ - getProgramAccounts(connection, VAULT_ID).then(forEach(processVaultData)), - getProgramAccounts(connection, AUCTION_ID).then(forEach(processAuctions)), - getProgramAccounts(connection, METAPLEX_ID).then( - forEach(processMetaplexAccounts), - ), - IS_BIG_STORE - ? getProgramAccounts(connection, METADATA_PROGRAM_ID).then( + if (whitelistedCreators.length > 3) { + console.log(' too many creators, pulling all nfts in one go'); + additionalPromises.push( + getProgramAccounts(connection, METADATA_PROGRAM_ID).then( forEach(processMetaData), - ) - : undefined, - getProgramAccounts(connection, METAPLEX_ID, { - filters: [ - { - dataSize: MAX_WHITELISTED_CREATOR_SIZE, - }, - ], - }).then(async creators => { - const result = await forEach(processMetaplexAccounts)(creators); - - if (IS_BIG_STORE) { - return result; - } - - const whitelistedCreators = Object.values( - tempCache.whitelistedCreatorsByCreator, + ), ); + } else { + console.log('pulling optimized nfts'); + isSelectivePullMetadata = true; - if (whitelistedCreators.length > 3) { - console.log(' too many creators, pulling all nfts in one go'); - additionalPromises.push( - getProgramAccounts(connection, METADATA_PROGRAM_ID).then( - forEach(processMetaData), - ), - ); - } else { - console.log('pulling optimized nfts'); - - for (let i = 0; i < MAX_CREATOR_LIMIT; i++) { - for (let j = 0; j < whitelistedCreators.length; j++) { - additionalPromises.push( - getProgramAccounts(connection, METADATA_PROGRAM_ID, { - filters: [ - { - memcmp: { - offset: - 1 + // key - 32 + // update auth - 32 + // mint - 4 + // name string length - MAX_NAME_LENGTH + // name - 4 + // uri string length - MAX_URI_LENGTH + // uri - 4 + // symbol string length - MAX_SYMBOL_LENGTH + // symbol - 2 + // seller fee basis points - 1 + // whether or not there is a creators vec - 4 + // creators vec length - i * MAX_CREATOR_LEN, - bytes: whitelistedCreators[j].info.address, - }, + for (let i = 0; i < MAX_CREATOR_LIMIT; i++) { + for (let j = 0; j < whitelistedCreators.length; j++) { + additionalPromises.push( + getProgramAccounts(connection, METADATA_PROGRAM_ID, { + filters: [ + { + memcmp: { + offset: + 1 + // key + 32 + // update auth + 32 + // mint + 4 + // name string length + MAX_NAME_LENGTH + // name + 4 + // uri string length + MAX_URI_LENGTH + // uri + 4 + // symbol string length + MAX_SYMBOL_LENGTH + // symbol + 2 + // seller fee basis points + 1 + // whether or not there is a creators vec + 4 + // creators vec length + i * MAX_CREATOR_LEN, + bytes: whitelistedCreators[j].info.address, }, - ], - }).then(forEach(processMetaData)), - ); - } + }, + ], + }).then(forEach(processMetaData)), + ); } } - }), - ]; - await Promise.all(promises); - await Promise.all(additionalPromises); + } + }; - await postProcessMetadata(tempCache, all); - console.log('Metadata size', tempCache.metadata.length); - - if (additionalPromises.length > 0) { + const pullEditions = async () => { console.log('Pulling editions for optimized metadata'); let setOf100MetadataEditionKeys: string[] = []; const editionPromises: Promise<{ @@ -263,6 +210,39 @@ export const loadAccounts = async (connection: Connection, all: boolean) => { Object.keys(tempCache.editions).length, Object.keys(tempCache.masterEditions).length, ); + }; + + const IS_BIG_STORE = + all || process.env.NEXT_PUBLIC_BIG_STORE?.toLowerCase() === 'true'; + console.log(`Is big store: ${IS_BIG_STORE}`); + + const additionalPromises: Promise[] = []; + const basePromises = [ + getProgramAccounts(connection, VAULT_ID).then(forEach(processVaultData)), + getProgramAccounts(connection, AUCTION_ID).then(forEach(processAuctions)), + getProgramAccounts(connection, METAPLEX_ID).then( + forEach(processMetaplexAccounts), + ), + IS_BIG_STORE + ? getProgramAccounts(connection, METADATA_PROGRAM_ID).then( + forEach(processMetaData), + ) + : getProgramAccounts(connection, METAPLEX_ID, { + filters: [ + { + dataSize: MAX_WHITELISTED_CREATOR_SIZE, + }, + ], + }).then(pullMetadata), + ]; + await Promise.all(basePromises); + await Promise.all(additionalPromises); + + await postProcessMetadata(tempCache, all); + console.log('Metadata size', tempCache.metadata.length); + + if (isSelectivePullMetadata) { + await pullEditions(); } return tempCache; @@ -294,11 +274,11 @@ export const metadataByMintUpdater = async ( ) => { const key = metadata.info.mint; if ( + all || isMetadataPartOfStore( metadata, state.store, state.whitelistedCreatorsByCreator, - all, ) ) { await metadata.info.init(); diff --git a/js/packages/common/src/contexts/meta/meta.tsx b/js/packages/common/src/contexts/meta/meta.tsx new file mode 100644 index 0000000..facd1da --- /dev/null +++ b/js/packages/common/src/contexts/meta/meta.tsx @@ -0,0 +1,124 @@ +import React, { useCallback, useContext, useEffect, useState } from 'react'; +import { queryExtendedMetadata } from './queryExtendedMetadata'; +import { subscribeAccountsChange } from './subscribeAccountsChange'; +import { getEmptyMetaState } from './getEmptyMetaState'; +import { loadAccounts } from './loadAccounts'; +import { MetaContextState, MetaState } from './types'; +import { useConnection } from '../connection'; +import { useStore } from '../store'; +import { useQuerySearch } from '../../hooks'; + +const MetaContext = React.createContext({ + ...getEmptyMetaState(), + isLoading: false, +}); + +export function MetaProvider({ children = null as any }) { + const connection = useConnection(); + const { isReady, storeAddress } = useStore(); + const searchParams = useQuerySearch(); + const all = searchParams.get('all') == 'true'; + + const [state, setState] = useState(getEmptyMetaState()); + + const [isLoading, setIsLoading] = useState(true); + + const updateMints = useCallback( + async metadataByMint => { + try { + if (!all) { + const { metadata, mintToMetadata } = await queryExtendedMetadata( + connection, + metadataByMint, + ); + setState(current => ({ + ...current, + metadata, + metadataByMint: mintToMetadata, + })); + } + } catch (er) { + console.error(er); + } + }, + [setState], + ); + + useEffect(() => { + (async () => { + if (!storeAddress) { + if (isReady) { + setIsLoading(false); + } + return; + } else if (!state.store) { + setIsLoading(true); + } + + console.log('-----> Query started'); + + const nextState = await loadAccounts(connection, all); + + console.log('------->Query finished'); + + setState(nextState); + + setIsLoading(false); + console.log('------->set finished'); + + updateMints(nextState.metadataByMint); + })(); + }, [connection, setState, updateMints, storeAddress, isReady]); + + useEffect(() => { + if (isLoading) { + return; + } + + return subscribeAccountsChange(connection, all, () => state, setState); + }, [connection, setState, isLoading]); + + // TODO: fetch names dynamically + // TODO: get names for creators + // useEffect(() => { + // (async () => { + // const twitterHandles = await connection.getProgramAccounts(NAME_PROGRAM_ID, { + // filters: [ + // { + // dataSize: TWITTER_ACCOUNT_LENGTH, + // }, + // { + // memcmp: { + // offset: VERIFICATION_AUTHORITY_OFFSET, + // bytes: TWITTER_VERIFICATION_AUTHORITY.toBase58() + // } + // } + // ] + // }); + + // const handles = twitterHandles.map(t => { + // const owner = new PublicKey(t.account.data.slice(32, 64)); + // const name = t.account.data.slice(96, 114).toString(); + // }); + + // console.log(handles); + + // })(); + // }, [whitelistedCreatorsByCreator]); + + return ( + + {children} + + ); +} + +export const useMeta = () => { + const context = useContext(MetaContext); + return context; +}; diff --git a/js/packages/web/src/contexts/meta/onChangeAccount.ts b/js/packages/common/src/contexts/meta/onChangeAccount.ts similarity index 55% rename from js/packages/web/src/contexts/meta/onChangeAccount.ts rename to js/packages/common/src/contexts/meta/onChangeAccount.ts index c9a01bc..273204a 100644 --- a/js/packages/web/src/contexts/meta/onChangeAccount.ts +++ b/js/packages/common/src/contexts/meta/onChangeAccount.ts @@ -1,7 +1,5 @@ -import { - KeyedAccountInfo, - ProgramAccountChangeCallback, -} from '@solana/web3.js'; +import { ProgramAccountChangeCallback } from '@solana/web3.js'; +import { pubkeyToString } from '../../utils'; import { ProcessAccountsFunc, UpdateStateValueFunc } from './types'; export const onChangeAccount = @@ -11,7 +9,7 @@ export const onChangeAccount = all: boolean, ): ProgramAccountChangeCallback => async info => { - const pubkey = pubkeyByAccountInfo(info); + const pubkey = pubkeyToString(info.accountId); await process( { pubkey, @@ -21,9 +19,3 @@ export const onChangeAccount = all, ); }; - -const pubkeyByAccountInfo = (info: KeyedAccountInfo) => { - return typeof info.accountId === 'string' - ? info.accountId - : info.accountId.toBase58(); -}; diff --git a/js/packages/web/src/contexts/meta/processAuctions.ts b/js/packages/common/src/contexts/meta/processAuctions.ts similarity index 94% rename from js/packages/web/src/contexts/meta/processAuctions.ts rename to js/packages/common/src/contexts/meta/processAuctions.ts index 8bbb308..b1f4105 100644 --- a/js/packages/web/src/contexts/meta/processAuctions.ts +++ b/js/packages/common/src/contexts/meta/processAuctions.ts @@ -1,19 +1,19 @@ import { - AuctionParser, - cache, - ParsedAccount, AuctionData, + AuctionDataExtended, + AuctionDataExtendedParser, + AuctionParser, BidderMetadata, BidderMetadataParser, BidderPot, BidderPotParser, BIDDER_METADATA_LEN, BIDDER_POT_LEN, - AuctionDataExtended, MAX_AUCTION_DATA_EXTENDED_SIZE, - AuctionDataExtendedParser, - AUCTION_ID, -} from '@oyster/common'; +} from '../../actions'; +import { AUCTION_ID } from '../../utils'; +import { ParsedAccount } from '../accounts/types'; +import { cache } from '../accounts/cache'; import { CheckAccountFunc, ProcessAccountsFunc } from './types'; export const processAuctions: ProcessAccountsFunc = ( diff --git a/js/packages/web/src/contexts/meta/processMetaData.ts b/js/packages/common/src/contexts/meta/processMetaData.ts similarity index 95% rename from js/packages/web/src/contexts/meta/processMetaData.ts rename to js/packages/common/src/contexts/meta/processMetaData.ts index 4bce00b..03020bc 100644 --- a/js/packages/web/src/contexts/meta/processMetaData.ts +++ b/js/packages/common/src/contexts/meta/processMetaData.ts @@ -1,18 +1,18 @@ -import { - decodeMetadata, - decodeEdition, - decodeMasterEdition, - Metadata, - ParsedAccount, - Edition, - MasterEditionV1, - MasterEditionV2, - MetadataKey, - METADATA_PROGRAM_ID, -} from '@oyster/common'; import { AccountInfo } from '@solana/web3.js'; import { ProcessAccountsFunc } from './types'; import { isValidHttpUrl } from '../../utils/isValidHttpUrl'; +import { + decodeEdition, + decodeMasterEdition, + decodeMetadata, + Edition, + MasterEditionV1, + MasterEditionV2, + Metadata, + MetadataKey, +} from '../../actions'; +import { ParsedAccount } from '../accounts/types'; +import { METADATA_PROGRAM_ID } from '../../utils'; export const processMetaData: ProcessAccountsFunc = ( { account, pubkey }, diff --git a/js/packages/web/src/contexts/meta/processMetaplexAccounts.ts b/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts similarity index 84% rename from js/packages/web/src/contexts/meta/processMetaplexAccounts.ts rename to js/packages/common/src/contexts/meta/processMetaplexAccounts.ts index 119521a..da324f1 100644 --- a/js/packages/web/src/contexts/meta/processMetaplexAccounts.ts +++ b/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts @@ -1,13 +1,12 @@ -import { programIds, cache, ParsedAccount, METAPLEX_ID } from '@oyster/common'; import { AccountInfo, PublicKey } from '@solana/web3.js'; import { + AuctionManagerV1, AuctionManagerV2, BidRedemptionTicket, decodeAuctionManager, decodeBidRedemptionTicket, decodeStore, - decodeWhitelistedCreator, - getWhitelistedCreator, + isCreatorPartOfTheStore, MetaplexKey, Store, WhitelistedCreator, @@ -20,9 +19,10 @@ import { decodeSafetyDepositConfig, SafetyDepositConfig, } from '../../models/metaplex'; -import { AuctionManagerV1 } from '../../models/metaplex/deprecatedStates'; -import names from '../../config/userNames.json'; import { ProcessAccountsFunc } from './types'; +import { METAPLEX_ID, programIds } from '../../utils'; +import { ParsedAccount } from '../accounts/types'; +import { cache } from '../accounts/cache'; export const processMetaplexAccounts: ProcessAccountsFunc = async ( { account, pubkey }, @@ -130,30 +130,33 @@ export const processMetaplexAccounts: ProcessAccountsFunc = async ( } if (isWhitelistedCreatorV1Account(account)) { - const whitelistedCreator = decodeWhitelistedCreator(account.data); + const parsedAccount = cache.add( + pubkey, + account, + WhitelistedCreatorParser, + false, + ) as ParsedAccount; + // TODO: figure out a way to avoid generating creator addresses during parsing // should we store store id inside creator? - const creatorKeyIfCreatorWasPartOfThisStore = await getWhitelistedCreator( - whitelistedCreator.address, - ); - - if (creatorKeyIfCreatorWasPartOfThisStore === pubkey) { - const parsedAccount = cache.add( + if (STORE_ID) { + const isWhitelistedCreator = await isCreatorPartOfTheStore( + parsedAccount.info.address, pubkey, - account, - WhitelistedCreatorParser, - false, - ) as ParsedAccount; - - const nameInfo = (names as any)[parsedAccount.info.address]; - - if (nameInfo) { - parsedAccount.info = { ...parsedAccount.info, ...nameInfo }; + ); + if (isWhitelistedCreator) { + setter( + 'whitelistedCreatorsByCreator', + parsedAccount.info.address, + parsedAccount, + ); } + } + if (useAll) { setter( - 'whitelistedCreatorsByCreator', - whitelistedCreator.address, + 'creators', + parsedAccount.info.address + '-' + pubkey, parsedAccount, ); } diff --git a/js/packages/web/src/contexts/meta/processVaultData.ts b/js/packages/common/src/contexts/meta/processVaultData.ts similarity index 92% rename from js/packages/web/src/contexts/meta/processVaultData.ts rename to js/packages/common/src/contexts/meta/processVaultData.ts index 0a7f4cf..1ab148b 100644 --- a/js/packages/web/src/contexts/meta/processVaultData.ts +++ b/js/packages/common/src/contexts/meta/processVaultData.ts @@ -1,13 +1,13 @@ +import { AccountInfo } from '@solana/web3.js'; import { - ParsedAccount, - SafetyDepositBox, - VaultKey, decodeSafetyDeposit, decodeVault, + SafetyDepositBox, Vault, -} from '@oyster/common'; -import { VAULT_ID } from '@oyster/common/dist/lib/utils/ids'; -import { AccountInfo } from '@solana/web3.js'; + VaultKey, +} from '../../actions'; +import { VAULT_ID } from '../../utils'; +import { ParsedAccount } from '../accounts/types'; import { ProcessAccountsFunc } from './types'; export const processVaultData: ProcessAccountsFunc = ( diff --git a/js/packages/web/src/contexts/meta/queryExtendedMetadata.ts b/js/packages/common/src/contexts/meta/queryExtendedMetadata.ts similarity index 80% rename from js/packages/web/src/contexts/meta/queryExtendedMetadata.ts rename to js/packages/common/src/contexts/meta/queryExtendedMetadata.ts index 4c1b7f3..f265168 100644 --- a/js/packages/web/src/contexts/meta/queryExtendedMetadata.ts +++ b/js/packages/common/src/contexts/meta/queryExtendedMetadata.ts @@ -1,12 +1,10 @@ -import { - Metadata, - getMultipleAccounts, - cache, - MintParser, - ParsedAccount, -} from '@oyster/common'; import { MintInfo } from '@solana/spl-token'; import { Connection } from '@solana/web3.js'; +import { Metadata } from '../../actions'; +import { ParsedAccount } from '../accounts/types'; +import { cache } from '../accounts/cache'; +import { getMultipleAccounts } from '../accounts/getMultipleAccounts'; +import { MintParser } from '../accounts/parsesrs'; export const queryExtendedMetadata = async ( connection: Connection, diff --git a/js/packages/common/src/contexts/meta/subscribeAccountsChange.ts b/js/packages/common/src/contexts/meta/subscribeAccountsChange.ts new file mode 100644 index 0000000..d1c32cb --- /dev/null +++ b/js/packages/common/src/contexts/meta/subscribeAccountsChange.ts @@ -0,0 +1,76 @@ +import { Connection } from '@solana/web3.js'; +import { + AUCTION_ID, + METADATA_PROGRAM_ID, + METAPLEX_ID, + toPublicKey, + VAULT_ID, +} from '../../utils'; +import { makeSetter, metadataByMintUpdater } from './loadAccounts'; +import { onChangeAccount } from './onChangeAccount'; +import { processAuctions } from './processAuctions'; +import { processMetaData } from './processMetaData'; +import { processMetaplexAccounts } from './processMetaplexAccounts'; +import { processVaultData } from './processVaultData'; +import { MetaState, UpdateStateValueFunc } from './types'; + +export const subscribeAccountsChange = ( + connection: Connection, + all: boolean, + getState: () => MetaState, + setState: (v: MetaState) => void, +) => { + const subscriptions: number[] = []; + + const updateStateValue: UpdateStateValueFunc = (prop, key, value) => { + const state = getState(); + const nextState = makeSetter({ ...state })(prop, key, value); + setState(nextState); + }; + + subscriptions.push( + connection.onProgramAccountChange( + toPublicKey(VAULT_ID), + onChangeAccount(processVaultData, updateStateValue, all), + ), + ); + + subscriptions.push( + connection.onProgramAccountChange( + toPublicKey(AUCTION_ID), + onChangeAccount(processAuctions, updateStateValue, all), + ), + ); + + subscriptions.push( + connection.onProgramAccountChange( + toPublicKey(METAPLEX_ID), + onChangeAccount(processMetaplexAccounts, updateStateValue, all), + ), + ); + + subscriptions.push( + connection.onProgramAccountChange( + toPublicKey(METADATA_PROGRAM_ID), + onChangeAccount( + processMetaData, + async (prop, key, value) => { + if (prop === 'metadataByMint') { + const state = getState(); + const nextState = await metadataByMintUpdater(value, state, all); + setState(nextState); + } else { + updateStateValue(prop, key, value); + } + }, + all, + ), + ), + ); + + return () => { + subscriptions.forEach(subscriptionId => { + connection.removeProgramAccountChangeListener(subscriptionId); + }); + }; +}; diff --git a/js/packages/web/src/contexts/meta/types.ts b/js/packages/common/src/contexts/meta/types.ts similarity index 92% rename from js/packages/web/src/contexts/meta/types.ts rename to js/packages/common/src/contexts/meta/types.ts index a4f9b25..e41f7f0 100644 --- a/js/packages/web/src/contexts/meta/types.ts +++ b/js/packages/common/src/contexts/meta/types.ts @@ -1,29 +1,29 @@ -import { - Metadata, - ParsedAccount, - Edition, - AuctionData, - SafetyDepositBox, - BidderMetadata, - BidderPot, - Vault, - AuctionDataExtended, - MasterEditionV1, - MasterEditionV2, - PublicKeyStringAndAccount, -} from '@oyster/common'; import { AccountInfo } from '@solana/web3.js'; import { + AuctionData, + AuctionDataExtended, + BidderMetadata, + BidderPot, + Edition, + MasterEditionV1, + MasterEditionV2, + Metadata, + SafetyDepositBox, + Vault, +} from '../../actions'; +import { + AuctionManagerV1, + AuctionManagerV2, BidRedemptionTicket, - Store, - WhitelistedCreator, + BidRedemptionTicketV2, PayoutTicket, PrizeTrackingTicket, - AuctionManagerV2, SafetyDepositConfig, - BidRedemptionTicketV2, + Store, + WhitelistedCreator, } from '../../models/metaplex'; -import { AuctionManagerV1 } from '../../models/metaplex/deprecatedStates'; +import { PublicKeyStringAndAccount } from '../../utils'; +import { ParsedAccount } from '../accounts/types'; export interface MetaState { metadata: ParsedAccount[]; @@ -72,6 +72,7 @@ export interface MetaState { >; payoutTickets: Record>; stores: Record>; + creators: Record>; } export interface MetaContextState extends MetaState { diff --git a/js/packages/common/src/models/index.ts b/js/packages/common/src/models/index.ts index 362a768..5f3bcc6 100644 --- a/js/packages/common/src/models/index.ts +++ b/js/packages/common/src/models/index.ts @@ -1 +1,2 @@ export * from './account'; +export * from './metaplex'; diff --git a/js/packages/web/src/models/metaplex/claimBid.ts b/js/packages/common/src/models/metaplex/claimBid.ts similarity index 94% rename from js/packages/web/src/models/metaplex/claimBid.ts rename to js/packages/common/src/models/metaplex/claimBid.ts index d9f9141..a36c045 100644 --- a/js/packages/web/src/models/metaplex/claimBid.ts +++ b/js/packages/common/src/models/metaplex/claimBid.ts @@ -1,13 +1,9 @@ -import { - getBidderPotKey, - programIds, - StringPublicKey, - toPublicKey, -} from '@oyster/common'; import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; import { getAuctionKeys, ClaimBidArgs, SCHEMA } from '.'; +import { getBidderPotKey } from '../../actions'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function claimBid( acceptPayment: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/decommissionAuctionManager.ts b/js/packages/common/src/models/metaplex/decommissionAuctionManager.ts similarity index 95% rename from js/packages/web/src/models/metaplex/decommissionAuctionManager.ts rename to js/packages/common/src/models/metaplex/decommissionAuctionManager.ts index 1279bb7..4c48bc8 100644 --- a/js/packages/web/src/models/metaplex/decommissionAuctionManager.ts +++ b/js/packages/common/src/models/metaplex/decommissionAuctionManager.ts @@ -1,8 +1,8 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; import { DecommissionAuctionManagerArgs, SCHEMA } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function decommissionAuctionManager( auctionManager: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/deprecatedInitAuctionManagerV1.ts b/js/packages/common/src/models/metaplex/deprecatedInitAuctionManagerV1.ts similarity index 96% rename from js/packages/web/src/models/metaplex/deprecatedInitAuctionManagerV1.ts rename to js/packages/common/src/models/metaplex/deprecatedInitAuctionManagerV1.ts index af2cb55..cd65f3e 100644 --- a/js/packages/web/src/models/metaplex/deprecatedInitAuctionManagerV1.ts +++ b/js/packages/common/src/models/metaplex/deprecatedInitAuctionManagerV1.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -7,6 +6,7 @@ import { import { serialize } from 'borsh'; import { getAuctionKeys, SCHEMA } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; import { AuctionManagerSettingsV1, DeprecatedInitAuctionManagerV1Args, diff --git a/js/packages/web/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts b/js/packages/common/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts similarity index 97% rename from js/packages/web/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts rename to js/packages/common/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts index e6a9401..17b5387 100644 --- a/js/packages/web/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts +++ b/js/packages/common/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts @@ -1,15 +1,13 @@ -import { - programIds, - VAULT_PREFIX, - getAuctionExtended, - findProgramAddress, - StringPublicKey, - toPublicKey, -} from '@oyster/common'; import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; - import { SCHEMA } from '.'; +import { getAuctionExtended, VAULT_PREFIX } from '../../actions'; +import { + findProgramAddress, + programIds, + StringPublicKey, + toPublicKey, +} from '../../utils'; import { DeprecatedPopulateParticipationPrintingAccountArgs } from './deprecatedStates'; export async function deprecatedPopulateParticipationPrintingAccount( diff --git a/js/packages/web/src/models/metaplex/deprecatedRedeemParticipationBid.ts b/js/packages/common/src/models/metaplex/deprecatedRedeemParticipationBid.ts similarity index 97% rename from js/packages/web/src/models/metaplex/deprecatedRedeemParticipationBid.ts rename to js/packages/common/src/models/metaplex/deprecatedRedeemParticipationBid.ts index 69fb542..0ab4d95 100644 --- a/js/packages/web/src/models/metaplex/deprecatedRedeemParticipationBid.ts +++ b/js/packages/common/src/models/metaplex/deprecatedRedeemParticipationBid.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -12,6 +11,7 @@ import { SCHEMA, getSafetyDepositConfig, } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; import { DeprecatedRedeemParticipationBidArgs } from './deprecatedStates'; export async function deprecatedRedeemParticipationBid( diff --git a/js/packages/web/src/models/metaplex/deprecatedStates.ts b/js/packages/common/src/models/metaplex/deprecatedStates.ts similarity index 98% rename from js/packages/web/src/models/metaplex/deprecatedStates.ts rename to js/packages/common/src/models/metaplex/deprecatedStates.ts index 37041ba..fecf8d7 100644 --- a/js/packages/web/src/models/metaplex/deprecatedStates.ts +++ b/js/packages/common/src/models/metaplex/deprecatedStates.ts @@ -1,4 +1,3 @@ -import { programIds, findProgramAddress, toPublicKey } from '@oyster/common'; import BN from 'bn.js'; import { AuctionManagerStatus, @@ -9,6 +8,7 @@ import { WinningConfigType, WinningConstraint, } from '.'; +import { findProgramAddress, programIds, toPublicKey } from '../../utils'; export const MAX_BID_REDEMPTION_TICKET_V1_SIZE = 3; diff --git a/js/packages/web/src/models/metaplex/deprecatedValidateParticipation.ts b/js/packages/common/src/models/metaplex/deprecatedValidateParticipation.ts similarity index 96% rename from js/packages/web/src/models/metaplex/deprecatedValidateParticipation.ts rename to js/packages/common/src/models/metaplex/deprecatedValidateParticipation.ts index bcd332f..9f8c77f 100644 --- a/js/packages/web/src/models/metaplex/deprecatedValidateParticipation.ts +++ b/js/packages/common/src/models/metaplex/deprecatedValidateParticipation.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -7,6 +6,7 @@ import { import { serialize } from 'borsh'; import { SCHEMA } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; import { DeprecatedValidateParticipationArgs } from './deprecatedStates'; export async function deprecatedValidateParticipation( diff --git a/js/packages/web/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts b/js/packages/common/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts similarity index 97% rename from js/packages/web/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts rename to js/packages/common/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts index 8b204a9..f985be4 100644 --- a/js/packages/web/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts +++ b/js/packages/common/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -7,6 +6,7 @@ import { import { serialize } from 'borsh'; import { getAuctionKeys, getOriginalAuthority, SCHEMA } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; import { getSafetyDepositBoxValidationTicket, diff --git a/js/packages/web/src/models/metaplex/emptyPaymentAccount.ts b/js/packages/common/src/models/metaplex/emptyPaymentAccount.ts similarity index 97% rename from js/packages/web/src/models/metaplex/emptyPaymentAccount.ts rename to js/packages/common/src/models/metaplex/emptyPaymentAccount.ts index c86b379..aac9a8a 100644 --- a/js/packages/web/src/models/metaplex/emptyPaymentAccount.ts +++ b/js/packages/common/src/models/metaplex/emptyPaymentAccount.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -13,6 +12,7 @@ import { getSafetyDepositConfig, SCHEMA, } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function emptyPaymentAccount( acceptPayment: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/index.ts b/js/packages/common/src/models/metaplex/index.ts similarity index 97% rename from js/packages/web/src/models/metaplex/index.ts rename to js/packages/common/src/models/metaplex/index.ts index bfb62a2..237afb5 100644 --- a/js/packages/web/src/models/metaplex/index.ts +++ b/js/packages/common/src/models/metaplex/index.ts @@ -1,24 +1,24 @@ -import { - AUCTION_PREFIX, - programIds, - METADATA, - AccountParser, - findProgramAddress, - AuctionData, - ParsedAccount, - Vault, - Metadata, - MasterEditionV1, - SafetyDepositBox, - MasterEditionV2, - toPublicKey, - StringPublicKey, -} from '@oyster/common'; import { AccountInfo, SystemProgram } from '@solana/web3.js'; import BN from 'bn.js'; -import { deserializeUnchecked } from 'borsh'; import bs58 from 'bs58'; -import { AuctionViewItem } from '../../hooks'; +import { deserializeUnchecked } from 'borsh'; +import { + AuctionData, + AUCTION_PREFIX, + MasterEditionV1, + MasterEditionV2, + METADATA, + Metadata, + SafetyDepositBox, + Vault, +} from '../../actions'; +import { AccountParser, ParsedAccount } from '../../contexts'; +import { + findProgramAddress, + programIds, + toPublicKey, + StringPublicKey, +} from '../../utils'; import { AuctionManagerV1, BidRedemptionTicketV1, @@ -35,6 +35,7 @@ export * from './deprecatedValidateSafetyDepositBoxV1'; export * from './redeemParticipationBidV3'; export * from './redeemPrintingV2Bid'; export * from './withdrawMasterEdition'; +export * from './deprecatedStates'; export const METAPLEX_PREFIX = 'metaplex'; export const TOTALS = 'totals'; @@ -537,6 +538,14 @@ export interface BidRedemptionTicket { getBidRedeemed(order: number): boolean; } + +export interface AuctionViewItem { + winningConfigType: WinningConfigType; + amount: BN; + metadata: ParsedAccount; + safetyDeposit: ParsedAccount; + masterEdition?: ParsedAccount; +} export class BidRedemptionTicketV2 implements BidRedemptionTicket { key: MetaplexKey = MetaplexKey.BidRedemptionTicketV2; winnerIndex: BN | null; @@ -1087,9 +1096,22 @@ export async function getOriginalAuthority( )[0]; } -export async function getWhitelistedCreator(creator: string) { +export const isCreatorPartOfTheStore = async ( + creatorAddress: StringPublicKey, + pubkey: StringPublicKey, + store?: StringPublicKey, +) => { + const creatorKeyInStore = await getWhitelistedCreator(creatorAddress, store); + + return creatorKeyInStore === pubkey; +}; + +export async function getWhitelistedCreator( + creator: StringPublicKey, + storeId?: StringPublicKey, +) { const PROGRAM_IDS = programIds(); - const store = PROGRAM_IDS.store; + const store = storeId || PROGRAM_IDS.store; if (!store) { throw new Error('Store not initialized'); } diff --git a/js/packages/web/src/models/metaplex/initAuctionManagerV2.ts b/js/packages/common/src/models/metaplex/initAuctionManagerV2.ts similarity index 96% rename from js/packages/web/src/models/metaplex/initAuctionManagerV2.ts rename to js/packages/common/src/models/metaplex/initAuctionManagerV2.ts index 11a8543..9e76393 100644 --- a/js/packages/web/src/models/metaplex/initAuctionManagerV2.ts +++ b/js/packages/common/src/models/metaplex/initAuctionManagerV2.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -14,6 +13,7 @@ import { SCHEMA, TupleNumericType, } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function initAuctionManagerV2( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/redeemBid.ts b/js/packages/common/src/models/metaplex/redeemBid.ts similarity index 98% rename from js/packages/web/src/models/metaplex/redeemBid.ts rename to js/packages/common/src/models/metaplex/redeemBid.ts index f5b4287..9f1ad94 100644 --- a/js/packages/web/src/models/metaplex/redeemBid.ts +++ b/js/packages/common/src/models/metaplex/redeemBid.ts @@ -1,10 +1,3 @@ -import { - findProgramAddress, - programIds, - StringPublicKey, - toPublicKey, - VAULT_PREFIX, -} from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -21,6 +14,13 @@ import { RedeemUnusedWinningConfigItemsAsAuctioneerArgs, SCHEMA, } from '.'; +import { VAULT_PREFIX } from '../../actions'; +import { + findProgramAddress, + programIds, + StringPublicKey, + toPublicKey, +} from '../../utils'; export async function redeemBid( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/redeemFullRightsTransferBid.ts b/js/packages/common/src/models/metaplex/redeemFullRightsTransferBid.ts similarity index 98% rename from js/packages/web/src/models/metaplex/redeemFullRightsTransferBid.ts rename to js/packages/common/src/models/metaplex/redeemFullRightsTransferBid.ts index 29ae2c9..64377f9 100644 --- a/js/packages/web/src/models/metaplex/redeemFullRightsTransferBid.ts +++ b/js/packages/common/src/models/metaplex/redeemFullRightsTransferBid.ts @@ -1,10 +1,3 @@ -import { - programIds, - VAULT_PREFIX, - findProgramAddress, - StringPublicKey, - toPublicKey, -} from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -21,6 +14,13 @@ import { RedeemUnusedWinningConfigItemsAsAuctioneerArgs, SCHEMA, } from '.'; +import { VAULT_PREFIX } from '../../actions'; +import { + findProgramAddress, + programIds, + StringPublicKey, + toPublicKey, +} from '../../utils'; export async function redeemFullRightsTransferBid( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/redeemParticipationBidV3.ts b/js/packages/common/src/models/metaplex/redeemParticipationBidV3.ts similarity index 98% rename from js/packages/web/src/models/metaplex/redeemParticipationBidV3.ts rename to js/packages/common/src/models/metaplex/redeemParticipationBidV3.ts index 4922d0a..b46cdfc 100644 --- a/js/packages/web/src/models/metaplex/redeemParticipationBidV3.ts +++ b/js/packages/common/src/models/metaplex/redeemParticipationBidV3.ts @@ -1,12 +1,3 @@ -import { - getEdition, - programIds, - getMetadata, - getEditionMarkPda, - getAuctionExtended, - StringPublicKey, - toPublicKey, -} from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -23,6 +14,13 @@ import { getPrizeTrackingTicket, getSafetyDepositConfig, } from '.'; +import { + getAuctionExtended, + getEdition, + getEditionMarkPda, + getMetadata, +} from '../../actions'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function redeemParticipationBidV3( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/redeemPrintingV2Bid.ts b/js/packages/common/src/models/metaplex/redeemPrintingV2Bid.ts similarity index 96% rename from js/packages/web/src/models/metaplex/redeemPrintingV2Bid.ts rename to js/packages/common/src/models/metaplex/redeemPrintingV2Bid.ts index 8384982..3ed4c15 100644 --- a/js/packages/web/src/models/metaplex/redeemPrintingV2Bid.ts +++ b/js/packages/common/src/models/metaplex/redeemPrintingV2Bid.ts @@ -1,11 +1,3 @@ -import { - getEdition, - getEditionMarkPda, - getMetadata, - programIds, - StringPublicKey, - toPublicKey, -} from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -22,6 +14,8 @@ import { SCHEMA, getSafetyDepositConfig, } from '.'; +import { getEdition, getEditionMarkPda, getMetadata } from '../../actions'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function redeemPrintingV2Bid( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/setStore.ts b/js/packages/common/src/models/metaplex/setStore.ts similarity index 95% rename from js/packages/web/src/models/metaplex/setStore.ts rename to js/packages/common/src/models/metaplex/setStore.ts index de5defa..b381683 100644 --- a/js/packages/web/src/models/metaplex/setStore.ts +++ b/js/packages/common/src/models/metaplex/setStore.ts @@ -1,8 +1,8 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; import { SCHEMA, SetStoreArgs } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function setStore( isPublic: boolean, diff --git a/js/packages/web/src/models/metaplex/setWhitelistedCreator.ts b/js/packages/common/src/models/metaplex/setWhitelistedCreator.ts similarity index 95% rename from js/packages/web/src/models/metaplex/setWhitelistedCreator.ts rename to js/packages/common/src/models/metaplex/setWhitelistedCreator.ts index 8d56c2f..1f9768d 100644 --- a/js/packages/web/src/models/metaplex/setWhitelistedCreator.ts +++ b/js/packages/common/src/models/metaplex/setWhitelistedCreator.ts @@ -1,8 +1,8 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; import { getWhitelistedCreator, SCHEMA, SetWhitelistedCreatorArgs } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function setWhitelistedCreator( creator: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/startAuction.ts b/js/packages/common/src/models/metaplex/startAuction.ts similarity index 94% rename from js/packages/web/src/models/metaplex/startAuction.ts rename to js/packages/common/src/models/metaplex/startAuction.ts index d5ad758..9e75802 100644 --- a/js/packages/web/src/models/metaplex/startAuction.ts +++ b/js/packages/common/src/models/metaplex/startAuction.ts @@ -1,8 +1,8 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; import { getAuctionKeys, SCHEMA, StartAuctionArgs } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function startAuction( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/validateSafetyDepositBoxV2.ts b/js/packages/common/src/models/metaplex/validateSafetyDepositBoxV2.ts similarity index 97% rename from js/packages/web/src/models/metaplex/validateSafetyDepositBoxV2.ts rename to js/packages/common/src/models/metaplex/validateSafetyDepositBoxV2.ts index 79aa063..d671435 100644 --- a/js/packages/web/src/models/metaplex/validateSafetyDepositBoxV2.ts +++ b/js/packages/common/src/models/metaplex/validateSafetyDepositBoxV2.ts @@ -1,4 +1,3 @@ -import { programIds, toPublicKey, StringPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -15,6 +14,7 @@ import { SCHEMA, ValidateSafetyDepositBoxV2Args, } from '.'; +import { programIds, toPublicKey, StringPublicKey } from '../../utils'; export async function validateSafetyDepositBoxV2( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/withdrawMasterEdition.ts b/js/packages/common/src/models/metaplex/withdrawMasterEdition.ts similarity index 97% rename from js/packages/web/src/models/metaplex/withdrawMasterEdition.ts rename to js/packages/common/src/models/metaplex/withdrawMasterEdition.ts index 76f652c..9c6053e 100644 --- a/js/packages/web/src/models/metaplex/withdrawMasterEdition.ts +++ b/js/packages/common/src/models/metaplex/withdrawMasterEdition.ts @@ -1,12 +1,3 @@ -import { - AUCTION_PREFIX, - EXTENDED, - findProgramAddress, - programIds, - StringPublicKey, - toPublicKey, - VAULT_PREFIX, -} from '@oyster/common'; import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; @@ -17,6 +8,13 @@ import { getPrizeTrackingTicket, getSafetyDepositConfig, } from '.'; +import { AUCTION_PREFIX, EXTENDED, VAULT_PREFIX } from '../../actions'; +import { + findProgramAddress, + programIds, + toPublicKey, + StringPublicKey, +} from '../../utils'; export async function withdrawMasterEdition( vault: StringPublicKey, diff --git a/js/packages/common/src/utils/ids.ts b/js/packages/common/src/utils/ids.ts index 6816fff..6f53b00 100644 --- a/js/packages/common/src/utils/ids.ts +++ b/js/packages/common/src/utils/ids.ts @@ -36,6 +36,10 @@ export const toPublicKey = (key: string | PublicKey) => { return result; }; +export const pubkeyToString = (key: PublicKey | null | string = '') => { + return typeof key === 'string' ? key : key?.toBase58() || ''; +}; + export interface PublicKeyStringAndAccount { pubkey: string; account: AccountInfo; diff --git a/js/packages/common/src/utils/index.tsx b/js/packages/common/src/utils/index.tsx index 82a9c93..e27e980 100644 --- a/js/packages/common/src/utils/index.tsx +++ b/js/packages/common/src/utils/index.tsx @@ -4,6 +4,8 @@ export * from './programIds'; export * as Layout from './layout'; export * from './notifications'; export * from './utils'; +export * from './useLocalStorage'; export * from './strings'; export * as shortvec from './shortvec'; +export * from './isValidHttpUrl'; export * from './borsh'; diff --git a/js/packages/web/src/utils/isValidHttpUrl.ts b/js/packages/common/src/utils/isValidHttpUrl.ts similarity index 100% rename from js/packages/web/src/utils/isValidHttpUrl.ts rename to js/packages/common/src/utils/isValidHttpUrl.ts diff --git a/js/packages/common/src/utils/useLocalStorage.ts b/js/packages/common/src/utils/useLocalStorage.ts new file mode 100644 index 0000000..e030fce --- /dev/null +++ b/js/packages/common/src/utils/useLocalStorage.ts @@ -0,0 +1,32 @@ +type UseStorageReturnValue = { + getItem: (key: string) => string; + setItem: (key: string, value: string) => boolean; + removeItem: (key: string) => void; +}; + +export const useLocalStorage = (): UseStorageReturnValue => { + const isBrowser: boolean = ((): boolean => typeof window !== 'undefined')(); + + const getItem = (key: string): string => { + return isBrowser ? window.localStorage[key] : ''; + }; + + const setItem = (key: string, value: string): boolean => { + if (isBrowser) { + window.localStorage.setItem(key, value); + return true; + } + + return false; + }; + + const removeItem = (key: string): void => { + window.localStorage.removeItem(key); + }; + + return { + getItem, + setItem, + removeItem, + }; +}; diff --git a/js/packages/common/src/utils/utils.ts b/js/packages/common/src/utils/utils.ts index 9fa7b30..e03c887 100644 --- a/js/packages/common/src/utils/utils.ts +++ b/js/packages/common/src/utils/utils.ts @@ -6,6 +6,7 @@ import { PublicKey } from '@solana/web3.js'; import BN from 'bn.js'; import { WAD, ZERO } from '../constants'; import { TokenInfo } from '@solana/spl-token-registry'; +import { useLocalStorage } from './useLocalStorage'; export type KnownTokenMap = Map; @@ -16,6 +17,7 @@ export const formatPriceNumber = new Intl.NumberFormat('en-US', { }); export function useLocalStorageState(key: string, defaultState?: string) { + const localStorage = useLocalStorage(); const [state, setState] = useState(() => { // NOTE: Not sure if this is ok const storedState = localStorage.getItem(key); @@ -52,6 +54,7 @@ export const findProgramAddress = async ( seeds: (Buffer | Uint8Array)[], programId: PublicKey, ) => { + const localStorage = useLocalStorage(); const key = 'pda-' + seeds.reduce((agg, item) => agg + item.toString('hex'), '') + diff --git a/js/packages/web/src/actions/addTokensToVault.ts b/js/packages/web/src/actions/addTokensToVault.ts index 73162fb..d83cedc 100644 --- a/js/packages/web/src/actions/addTokensToVault.ts +++ b/js/packages/web/src/actions/addTokensToVault.ts @@ -1,22 +1,24 @@ import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js'; import { utils, - actions, - models, findProgramAddress, MetadataKey, StringPublicKey, toPublicKey, WalletSigner, } from '@oyster/common'; +import { SafetyDepositConfig } from '@oyster/common/dist/lib/models/metaplex/index'; +import { approve } from '@oyster/common/dist/lib/models/account'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; +import { + addTokenToInactiveVault, + VAULT_PREFIX, +} from '@oyster/common/dist/lib/actions/vault'; import { AccountLayout } from '@solana/spl-token'; import BN from 'bn.js'; import { SafetyDepositDraft } from './createAuctionManager'; -import { SafetyDepositConfig } from '../models/metaplex'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; -const { createTokenAccount, addTokenToInactiveVault, VAULT_PREFIX } = actions; -const { approve } = models; export interface SafetyDepositInstructionTemplate { box: { diff --git a/js/packages/web/src/actions/cancelBid.ts b/js/packages/web/src/actions/cancelBid.ts index 20ab562..c33f13d 100644 --- a/js/packages/web/src/actions/cancelBid.ts +++ b/js/packages/web/src/actions/cancelBid.ts @@ -16,7 +16,10 @@ import { import { AccountLayout } from '@solana/spl-token'; import { TransactionInstruction, Keypair, Connection } from '@solana/web3.js'; import { AuctionView } from '../hooks'; -import { BidRedemptionTicket, PrizeTrackingTicket } from '../models/metaplex'; +import { + BidRedemptionTicket, + PrizeTrackingTicket, +} from '@oyster/common/dist/lib/models/metaplex/index'; import { claimUnusedPrizes } from './claimUnusedPrizes'; import { setupPlaceBid } from './sendPlaceBid'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; diff --git a/js/packages/web/src/actions/claimUnusedPrizes.ts b/js/packages/web/src/actions/claimUnusedPrizes.ts index 0f74ebf..d01804e 100644 --- a/js/packages/web/src/actions/claimUnusedPrizes.ts +++ b/js/packages/web/src/actions/claimUnusedPrizes.ts @@ -1,6 +1,5 @@ import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js'; import { - actions, ParsedAccount, TokenAccount, SafetyDepositBox, @@ -16,7 +15,7 @@ import { } from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; import { AccountLayout, MintLayout } from '@solana/spl-token'; -import { AuctionView, AuctionViewItem } from '../hooks'; +import { AuctionView } from '../hooks'; import { WinningConfigType, redeemBid, @@ -25,13 +24,14 @@ import { BidRedemptionTicket, getBidRedemption, PrizeTrackingTicket, -} from '../models/metaplex'; + AuctionViewItem, +} from '@oyster/common/dist/lib/models/metaplex/index'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; import { eligibleForParticipationPrizeGivenWinningIndex, setupRedeemParticipationInstructions, setupRedeemPrintingV2Instructions, } from './sendRedeemBid'; -const { createTokenAccount } = actions; export async function findEligibleParticipationBidsForRedemption( auctionView: AuctionView, diff --git a/js/packages/web/src/actions/closeVault.ts b/js/packages/web/src/actions/closeVault.ts index eb5ac8e..2eb2e44 100644 --- a/js/packages/web/src/actions/closeVault.ts +++ b/js/packages/web/src/actions/closeVault.ts @@ -1,16 +1,15 @@ import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js'; -import { - actions, - models, - StringPublicKey, - toPublicKey, - WalletSigner, -} from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; +import { StringPublicKey, toPublicKey, WalletSigner } from '@oyster/common'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; +import { + activateVault, + combineVault, +} from '@oyster/common/dist/lib/actions/vault'; +import { approve } from '@oyster/common/dist/lib/models/account'; + import { AccountLayout } from '@solana/spl-token'; import BN from 'bn.js'; -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 // authority (that may or may not exist yet.) diff --git a/js/packages/web/src/actions/createAuctionManager.ts b/js/packages/web/src/actions/createAuctionManager.ts index 910aa87..e44cc28 100644 --- a/js/packages/web/src/actions/createAuctionManager.ts +++ b/js/packages/web/src/actions/createAuctionManager.ts @@ -5,7 +5,6 @@ import { SystemProgram, } from '@solana/web3.js'; import { - actions, Metadata, ParsedAccount, MasterEditionV1, @@ -41,7 +40,8 @@ import { TupleNumericType, SafetyDepositConfig, ParticipationStateV2, -} from '../models/metaplex'; +} from '@oyster/common/dist/lib/models/metaplex/index'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; import { createVault } from './createVault'; import { closeVault } from './closeVault'; import { @@ -50,15 +50,13 @@ import { } from './addTokensToVault'; import { makeAuction } from './makeAuction'; import { createExternalPriceAccount } from './createExternalPriceAccount'; -import { deprecatedValidateParticipation } from '../models/metaplex/deprecatedValidateParticipation'; +import { deprecatedValidateParticipation } from '@oyster/common/dist/lib/models/metaplex/deprecatedValidateParticipation'; import { deprecatedCreateReservationListForTokens } from './deprecatedCreateReservationListsForTokens'; import { deprecatedPopulatePrintingTokens } from './deprecatedPopulatePrintingTokens'; import { setVaultAndAuctionAuthorities } from './setVaultAndAuctionAuthorities'; import { markItemsThatArentMineAsSold } from './markItemsThatArentMineAsSold'; -import { validateSafetyDepositBoxV2 } from '../models/metaplex/validateSafetyDepositBoxV2'; -import { initAuctionManagerV2 } from '../models/metaplex/initAuctionManagerV2'; - -const { createTokenAccount } = actions; +import { validateSafetyDepositBoxV2 } from '@oyster/common/dist/lib/models/metaplex/validateSafetyDepositBoxV2'; +import { initAuctionManagerV2 } from '@oyster/common/dist/lib/models/metaplex/initAuctionManagerV2'; interface normalPattern { instructions: TransactionInstruction[]; diff --git a/js/packages/web/src/actions/createExternalPriceAccount.ts b/js/packages/web/src/actions/createExternalPriceAccount.ts index 7dc1e01..f1b47b5 100644 --- a/js/packages/web/src/actions/createExternalPriceAccount.ts +++ b/js/packages/web/src/actions/createExternalPriceAccount.ts @@ -4,22 +4,22 @@ import { SystemProgram, TransactionInstruction, } from '@solana/web3.js'; +import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; + import { utils, - actions, StringPublicKey, toPublicKey, WalletSigner, } from '@oyster/common'; -import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; -import BN from 'bn.js'; -import { QUOTE_MINT } from '../constants'; - -const { +import { updateExternalPriceAccount, ExternalPriceAccount, MAX_EXTERNAL_ACCOUNT_SIZE, -} = actions; +} from '@oyster/common/dist/lib/actions/vault'; + +import BN from 'bn.js'; +import { QUOTE_MINT } from '../constants'; // This command creates the external pricing oracle export async function createExternalPriceAccount( diff --git a/js/packages/web/src/actions/createVault.ts b/js/packages/web/src/actions/createVault.ts index 0ba20f5..6268a26 100644 --- a/js/packages/web/src/actions/createVault.ts +++ b/js/packages/web/src/actions/createVault.ts @@ -6,19 +6,22 @@ import { } from '@solana/web3.js'; import { utils, - actions, createMint, findProgramAddress, StringPublicKey, toPublicKey, WalletSigner, } from '@oyster/common'; +import { + initVault, + MAX_VAULT_SIZE, + VAULT_PREFIX, +} from '@oyster/common/dist/lib/actions/vault'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; import { AccountLayout, MintLayout } from '@solana/spl-token'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; -const { createTokenAccount, initVault, MAX_VAULT_SIZE, VAULT_PREFIX } = actions; - // This command creates the external pricing oracle a vault // This gets the vault ready for adding the tokens. export async function createVault( diff --git a/js/packages/web/src/actions/decommAuctionManagerAndReturnPrizes.ts b/js/packages/web/src/actions/decommAuctionManagerAndReturnPrizes.ts index 084b7ca..cc02298 100644 --- a/js/packages/web/src/actions/decommAuctionManagerAndReturnPrizes.ts +++ b/js/packages/web/src/actions/decommAuctionManagerAndReturnPrizes.ts @@ -9,8 +9,8 @@ import { } from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; import { AuctionView } from '../hooks'; -import { AuctionManagerStatus } from '../models/metaplex'; -import { decommissionAuctionManager } from '../models/metaplex/decommissionAuctionManager'; +import { AuctionManagerStatus } from '@oyster/common/dist/lib/models/metaplex/index'; +import { decommissionAuctionManager } from '@oyster/common/dist/lib/models/metaplex/decommissionAuctionManager'; import { unwindVault } from './unwindVault'; export async function decommAuctionManagerAndReturnPrizes( diff --git a/js/packages/web/src/actions/deprecatedCreateReservationListsForTokens.ts b/js/packages/web/src/actions/deprecatedCreateReservationListsForTokens.ts index 63372f5..5a59f3e 100644 --- a/js/packages/web/src/actions/deprecatedCreateReservationListsForTokens.ts +++ b/js/packages/web/src/actions/deprecatedCreateReservationListsForTokens.ts @@ -6,7 +6,7 @@ import { } from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; import { SafetyDepositInstructionTemplate } from './addTokensToVault'; -import { WinningConfigType } from '../models/metaplex'; +import { WinningConfigType } from '@oyster/common/dist/lib/models/metaplex/index'; const BATCH_SIZE = 10; // This command batches out creating reservation lists for those tokens who are being sold in PrintingV1 mode. diff --git a/js/packages/web/src/actions/makeAuction.ts b/js/packages/web/src/actions/makeAuction.ts index 1e71385..c38d22f 100644 --- a/js/packages/web/src/actions/makeAuction.ts +++ b/js/packages/web/src/actions/makeAuction.ts @@ -1,7 +1,6 @@ import { Keypair, TransactionInstruction } from '@solana/web3.js'; import { utils, - actions, findProgramAddress, IPartialCreateAuctionArgs, CreateAuctionArgs, @@ -9,10 +8,12 @@ import { toPublicKey, WalletSigner, } from '@oyster/common'; +import { + AUCTION_PREFIX, + createAuction, +} from '@oyster/common/dist/lib/actions/auction'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; -const { AUCTION_PREFIX, createAuction } = actions; - // This command makes an auction export async function makeAuction( wallet: WalletSigner, diff --git a/js/packages/web/src/actions/saveAdmin.ts b/js/packages/web/src/actions/saveAdmin.ts index 95cf681..930cc93 100644 --- a/js/packages/web/src/actions/saveAdmin.ts +++ b/js/packages/web/src/actions/saveAdmin.ts @@ -5,10 +5,10 @@ import { sendTransactionWithRetry, WalletSigner, } from '@oyster/common'; +import { WhitelistedCreator } from '@oyster/common/dist/lib/models/metaplex/index'; +import { setStore } from '@oyster/common/dist/lib/models/metaplex/setStore'; +import { setWhitelistedCreator } from '@oyster/common/dist/lib/models/metaplex/setWhitelistedCreator'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; -import { WhitelistedCreator } from '../models/metaplex'; -import { setStore } from '../models/metaplex/setStore'; -import { setWhitelistedCreator } from '../models/metaplex/setWhitelistedCreator'; // TODO if this becomes very slow move to batching txns like we do with settle.ts // but given how little this should be used keep it simple diff --git a/js/packages/web/src/actions/sendPlaceBid.ts b/js/packages/web/src/actions/sendPlaceBid.ts index e2d6028..784b6be 100644 --- a/js/packages/web/src/actions/sendPlaceBid.ts +++ b/js/packages/web/src/actions/sendPlaceBid.ts @@ -1,11 +1,8 @@ import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js'; import { - actions, sendTransactionWithRetry, placeBid, - models, cache, - TokenAccount, ensureWrappedAccount, toLamports, ParsedAccount, @@ -13,15 +10,16 @@ import { WalletSigner, } from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; +import { approve } from '@oyster/common/dist/lib/models/account'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; +import { TokenAccount } from '@oyster/common/dist/lib/models/account'; + import { AccountLayout, MintInfo } from '@solana/spl-token'; import { AuctionView } from '../hooks'; import BN from 'bn.js'; import { setupCancelBid } from './cancelBid'; import { QUOTE_MINT } from '../constants'; -const { createTokenAccount } = actions; -const { approve } = models; - export async function sendPlaceBid( connection: Connection, wallet: WalletSigner, diff --git a/js/packages/web/src/actions/sendRedeemBid.ts b/js/packages/web/src/actions/sendRedeemBid.ts index d2ccd93..5983bfe 100644 --- a/js/packages/web/src/actions/sendRedeemBid.ts +++ b/js/packages/web/src/actions/sendRedeemBid.ts @@ -1,9 +1,7 @@ import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js'; import { - actions, ParsedAccount, programIds, - models, TokenAccount, createMint, SafetyDepositBox, @@ -28,8 +26,10 @@ import { } from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; import { AccountLayout, MintLayout, Token } from '@solana/spl-token'; -import { AuctionView, AuctionViewItem } from '../hooks'; +import { AuctionView } from '../hooks'; import { + AuctionManagerV1, + ParticipationStateV1, WinningConfigType, NonWinningConstraint, redeemBid, @@ -41,21 +41,18 @@ import { PrizeTrackingTicket, getPrizeTrackingTicket, BidRedemptionTicket, -} from '../models/metaplex'; -import { claimBid } from '../models/metaplex/claimBid'; + AuctionViewItem, +} from '@oyster/common/dist/lib/models/metaplex/index'; +import { claimBid } from '@oyster/common/dist/lib/models/metaplex/claimBid'; +import { approve } from '@oyster/common/dist/lib/models/account'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; import { setupCancelBid } from './cancelBid'; -import { deprecatedPopulateParticipationPrintingAccount } from '../models/metaplex/deprecatedPopulateParticipationPrintingAccount'; +import { deprecatedPopulateParticipationPrintingAccount } from '@oyster/common/dist/lib/models/metaplex/deprecatedPopulateParticipationPrintingAccount'; import { setupPlaceBid } from './sendPlaceBid'; import { claimUnusedPrizes } from './claimUnusedPrizes'; import { createMintAndAccountWithOne } from './createMintAndAccountWithOne'; import { BN } from 'bn.js'; import { QUOTE_MINT } from '../constants'; -import { - AuctionManagerV1, - ParticipationStateV1, -} from '../models/metaplex/deprecatedStates'; -const { createTokenAccount } = actions; -const { approve } = models; export function eligibleForParticipationPrizeGivenWinningIndex( winnerIndex: number | null, diff --git a/js/packages/web/src/actions/settle.ts b/js/packages/web/src/actions/settle.ts index b165d5b..a8684e4 100644 --- a/js/packages/web/src/actions/settle.ts +++ b/js/packages/web/src/actions/settle.ts @@ -16,8 +16,8 @@ import { import { AuctionView } from '../hooks'; -import { claimBid } from '../models/metaplex/claimBid'; -import { emptyPaymentAccount } from '../models/metaplex/emptyPaymentAccount'; +import { claimBid } from '@oyster/common/dist/lib/models/metaplex/claimBid'; +import { emptyPaymentAccount } from '@oyster/common/dist/lib/models/metaplex/emptyPaymentAccount'; import { QUOTE_MINT } from '../constants'; import { setupPlaceBid } from './sendPlaceBid'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; diff --git a/js/packages/web/src/actions/startAuctionManually.ts b/js/packages/web/src/actions/startAuctionManually.ts index ac59450..0315792 100644 --- a/js/packages/web/src/actions/startAuctionManually.ts +++ b/js/packages/web/src/actions/startAuctionManually.ts @@ -1,5 +1,5 @@ import { Connection, Keypair, TransactionInstruction } from '@solana/web3.js'; -import { startAuction } from '../models/metaplex'; +import { startAuction } from '@oyster/common/dist/lib/models/metaplex/index'; import { notify, sendTransactionWithRetry, WalletSigner } from '@oyster/common'; import { AuctionView } from '../hooks'; diff --git a/js/packages/web/src/components/ArtContent/index.tsx b/js/packages/web/src/components/ArtContent/index.tsx index bc338f2..e38f92b 100644 --- a/js/packages/web/src/components/ArtContent/index.tsx +++ b/js/packages/web/src/components/ArtContent/index.tsx @@ -1,13 +1,12 @@ import React, { Ref, useCallback, useEffect, useState } from 'react'; import { Image } from 'antd'; -import { MetadataCategory, MetadataFile } from '@oyster/common'; +import { MetadataCategory, MetadataFile, pubkeyToString } from '@oyster/common'; import { MeshViewer } from '../MeshViewer'; import { ThreeDots } from '../MyLoader'; import { useCachedImage, useExtendedArt } from '../../hooks'; import { Stream, StreamPlayerApi } from '@cloudflare/stream-react'; import { PublicKey } from '@solana/web3.js'; import { getLast } from '../../utils/utils'; -import { pubkeyToString } from '../../utils/pubkeyToString'; const MeshArtContent = ({ uri, diff --git a/js/packages/web/src/components/AuctionCard/index.tsx b/js/packages/web/src/components/AuctionCard/index.tsx index e752b29..b698564 100644 --- a/js/packages/web/src/components/AuctionCard/index.tsx +++ b/js/packages/web/src/components/AuctionCard/index.tsx @@ -43,7 +43,7 @@ import { findEligibleParticipationBidsForRedemption } from '../../actions/claimU import { BidRedemptionTicket, MAX_PRIZE_TRACKING_TICKET_SIZE, -} from '../../models/metaplex'; +} from '@oyster/common/dist/lib/models/metaplex/index'; async function calculateTotalCostOfRedeemingOtherPeoplesBids( connection: Connection, diff --git a/js/packages/web/src/contexts/index.tsx b/js/packages/web/src/contexts/index.tsx index 59ec44d..0241456 100644 --- a/js/packages/web/src/contexts/index.tsx +++ b/js/packages/web/src/contexts/index.tsx @@ -1,2 +1,2 @@ -export * from './meta'; +export * from '@oyster/common/dist/lib/contexts/meta/meta'; export * from './coingecko'; diff --git a/js/packages/web/src/contexts/meta/index.ts b/js/packages/web/src/contexts/meta/index.ts deleted file mode 100644 index c9942f4..0000000 --- a/js/packages/web/src/contexts/meta/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './meta'; diff --git a/js/packages/web/src/contexts/meta/meta.tsx b/js/packages/web/src/contexts/meta/meta.tsx deleted file mode 100644 index fe98208..0000000 --- a/js/packages/web/src/contexts/meta/meta.tsx +++ /dev/null @@ -1,241 +0,0 @@ -import { - useConnection, - useStore, - AUCTION_ID, - METAPLEX_ID, - VAULT_ID, - METADATA_PROGRAM_ID, - toPublicKey, - useQuerySearch, -} from '@oyster/common'; -import React, { - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from 'react'; -import { MetaState, MetaContextState, UpdateStateValueFunc } from './types'; -import { queryExtendedMetadata } from './queryExtendedMetadata'; -import { processAuctions } from './processAuctions'; -import { processMetaplexAccounts } from './processMetaplexAccounts'; -import { processMetaData } from './processMetaData'; -import { processVaultData } from './processVaultData'; -import { - loadAccounts, - makeSetter, - metadataByMintUpdater, -} from './loadAccounts'; -import { onChangeAccount } from './onChangeAccount'; - -const MetaContext = React.createContext({ - metadata: [], - metadataByMint: {}, - masterEditions: {}, - masterEditionsByPrintingMint: {}, - masterEditionsByOneTimeAuthMint: {}, - metadataByMasterEdition: {}, - editions: {}, - auctionManagersByAuction: {}, - auctions: {}, - auctionDataExtended: {}, - vaults: {}, - store: null, - isLoading: false, - bidderMetadataByAuctionAndBidder: {}, - safetyDepositBoxesByVaultAndIndex: {}, - safetyDepositConfigsByAuctionManagerAndIndex: {}, - bidRedemptionV2sByAuctionManagerAndWinningIndex: {}, - bidderPotsByAuctionAndBidder: {}, - bidRedemptions: {}, - whitelistedCreatorsByCreator: {}, - payoutTickets: {}, - prizeTrackingTickets: {}, - stores: {}, -}); - -export function MetaProvider({ children = null as any }) { - const connection = useConnection(); - const { isReady, storeAddress } = useStore(); - const searchParams = useQuerySearch(); - const all = searchParams.get('all') == 'true'; - - const [state, setState] = useState({ - metadata: [], - metadataByMint: {}, - masterEditions: {}, - masterEditionsByPrintingMint: {}, - masterEditionsByOneTimeAuthMint: {}, - metadataByMasterEdition: {}, - editions: {}, - auctionManagersByAuction: {}, - bidRedemptions: {}, - auctions: {}, - auctionDataExtended: {}, - vaults: {}, - payoutTickets: {}, - store: null, - whitelistedCreatorsByCreator: {}, - bidderMetadataByAuctionAndBidder: {}, - bidderPotsByAuctionAndBidder: {}, - safetyDepositBoxesByVaultAndIndex: {}, - prizeTrackingTickets: {}, - safetyDepositConfigsByAuctionManagerAndIndex: {}, - bidRedemptionV2sByAuctionManagerAndWinningIndex: {}, - stores: {}, - }); - - const [isLoading, setIsLoading] = useState(true); - - const updateMints = useCallback( - async metadataByMint => { - try { - if (!all) { - const { metadata, mintToMetadata } = await queryExtendedMetadata( - connection, - metadataByMint, - ); - setState(current => ({ - ...current, - metadata, - metadataByMint: mintToMetadata, - })); - } - } catch (er) { - console.error(er); - } - }, - [setState], - ); - - useEffect(() => { - (async () => { - if (!storeAddress) { - if (isReady) { - setIsLoading(false); - } - return; - } else if (!state.store) { - setIsLoading(true); - } - - console.log('-----> Query started'); - - const nextState = await loadAccounts(connection, all); - - console.log('------->Query finished'); - - setState(nextState); - - setIsLoading(false); - console.log('------->set finished'); - - updateMints(nextState.metadataByMint); - })(); - }, [connection, setState, updateMints, storeAddress, isReady]); - - const updateStateValue = useMemo( - () => (prop, key, value) => { - setState(current => makeSetter({ ...current })(prop, key, value)); - }, - [setState], - ); - - const store = state.store; - const whitelistedCreatorsByCreator = state.whitelistedCreatorsByCreator; - - useEffect(() => { - if (isLoading) { - return; - } - - const vaultSubId = connection.onProgramAccountChange( - toPublicKey(VAULT_ID), - onChangeAccount(processVaultData, updateStateValue, all), - ); - - const auctionSubId = connection.onProgramAccountChange( - toPublicKey(AUCTION_ID), - onChangeAccount(processAuctions, updateStateValue, all), - ); - - const metaplexSubId = connection.onProgramAccountChange( - toPublicKey(METAPLEX_ID), - onChangeAccount(processMetaplexAccounts, updateStateValue, all), - ); - - const metaSubId = connection.onProgramAccountChange( - toPublicKey(METADATA_PROGRAM_ID), - onChangeAccount( - processMetaData, - async (prop, key, value) => { - if (prop === 'metadataByMint') { - const nextState = await metadataByMintUpdater(value, state, all); - setState(nextState); - } else { - updateStateValue(prop, key, value); - } - }, - all, - ), - ); - - return () => { - connection.removeProgramAccountChangeListener(vaultSubId); - connection.removeProgramAccountChangeListener(metaplexSubId); - connection.removeProgramAccountChangeListener(metaSubId); - connection.removeProgramAccountChangeListener(auctionSubId); - }; - }, [ - connection, - updateStateValue, - setState, - store, - whitelistedCreatorsByCreator, - isLoading, - ]); - - // TODO: fetch names dynamically - // TODO: get names for creators - // useEffect(() => { - // (async () => { - // const twitterHandles = await connection.getProgramAccounts(NAME_PROGRAM_ID, { - // filters: [ - // { - // dataSize: TWITTER_ACCOUNT_LENGTH, - // }, - // { - // memcmp: { - // offset: VERIFICATION_AUTHORITY_OFFSET, - // bytes: TWITTER_VERIFICATION_AUTHORITY.toBase58() - // } - // } - // ] - // }); - - // const handles = twitterHandles.map(t => { - // const owner = new PublicKey(t.account.data.slice(32, 64)); - // const name = t.account.data.slice(96, 114).toString(); - // }); - - // console.log(handles); - - // })(); - // }, [whitelistedCreatorsByCreator]); - - return ( - - {children} - - ); -} - -export const useMeta = () => { - const context = useContext(MetaContext); - return context; -}; diff --git a/js/packages/web/src/hooks/useArt.ts b/js/packages/web/src/hooks/useArt.ts index d676382..35961a7 100644 --- a/js/packages/web/src/hooks/useArt.ts +++ b/js/packages/web/src/hooks/useArt.ts @@ -9,11 +9,12 @@ import { Metadata, ParsedAccount, StringPublicKey, + useLocalStorage, + pubkeyToString, } from '@oyster/common'; -import { WhitelistedCreator } from '../models/metaplex'; +import { WhitelistedCreator } from '@oyster/common/dist/lib/models/metaplex/index'; import { Cache } from 'three'; import { useInView } from 'react-intersection-observer'; -import { pubkeyToString } from '../utils/pubkeyToString'; const metadataToArt = ( info: Metadata | undefined, @@ -159,6 +160,7 @@ export const useExtendedArt = (id?: StringPublicKey) => { const [data, setData] = useState(); const { ref, inView } = useInView(); + const localStorage = useLocalStorage(); const key = pubkeyToString(id); diff --git a/js/packages/web/src/hooks/useAuctions.ts b/js/packages/web/src/hooks/useAuctions.ts index 299ebf1..4402f7e 100644 --- a/js/packages/web/src/hooks/useAuctions.ts +++ b/js/packages/web/src/hooks/useAuctions.ts @@ -18,14 +18,15 @@ import { useMeta } from '../contexts'; import { AuctionManager, AuctionManagerStatus, + AuctionManagerV1, AuctionManagerV2, BidRedemptionTicket, BidRedemptionTicketV2, getBidderKeys, SafetyDepositConfig, WinningConfigType, -} from '../models/metaplex'; -import { AuctionManagerV1 } from '../models/metaplex/deprecatedStates'; + AuctionViewItem, +} from '@oyster/common/dist/lib/models/metaplex/index'; export enum AuctionViewState { Live = '0', @@ -35,14 +36,6 @@ export enum AuctionViewState { Defective = '-1', } -export interface AuctionViewItem { - winningConfigType: WinningConfigType; - amount: BN; - metadata: ParsedAccount; - safetyDeposit: ParsedAccount; - masterEdition?: ParsedAccount; -} - // Flattened surface item for easy display export interface AuctionView { // items 1:1 with winning configs FOR NOW diff --git a/js/packages/web/src/hooks/useCreator.ts b/js/packages/web/src/hooks/useCreator.ts index 5344405..d996166 100644 --- a/js/packages/web/src/hooks/useCreator.ts +++ b/js/packages/web/src/hooks/useCreator.ts @@ -1,6 +1,5 @@ -import { StringPublicKey } from '@oyster/common'; +import { StringPublicKey, pubkeyToString } from '@oyster/common'; import { useMeta } from '../contexts'; -import { pubkeyToString } from '../utils/pubkeyToString'; export const useCreator = (id?: StringPublicKey) => { const { whitelistedCreatorsByCreator } = useMeta(); diff --git a/js/packages/web/src/hooks/useUserArts.ts b/js/packages/web/src/hooks/useUserArts.ts index 58593cc..b86902a 100644 --- a/js/packages/web/src/hooks/useUserArts.ts +++ b/js/packages/web/src/hooks/useUserArts.ts @@ -12,7 +12,7 @@ import { ParticipationConfigV2, WinningConfigType, WinningConstraint, -} from '../models/metaplex'; +} from '@oyster/common/dist/lib/models/metaplex/index'; import { useMeta } from './../contexts'; export const useUserArts = (): SafetyDepositDraft[] => { diff --git a/js/packages/web/src/providers.tsx b/js/packages/web/src/providers.tsx index 63b9e3b..e9e2cce 100644 --- a/js/packages/web/src/providers.tsx +++ b/js/packages/web/src/providers.tsx @@ -3,11 +3,11 @@ import { ConnectionProvider, StoreProvider, WalletProvider, + MetaProvider, } from '@oyster/common'; import { FC } from 'react'; import { ConfettiProvider } from './components/Confetti'; import { AppLayout } from './components/Layout'; -import { MetaProvider } from './contexts/meta'; import { CoingeckoProvider } from './contexts/coingecko'; export const Providers: FC = ({ children }) => { diff --git a/js/packages/web/src/utils/assets.ts b/js/packages/web/src/utils/assets.ts index 6d191f0..5e20f5d 100644 --- a/js/packages/web/src/utils/assets.ts +++ b/js/packages/web/src/utils/assets.ts @@ -1,3 +1,4 @@ +import { useLocalStorage } from '@oyster/common'; import { TokenInfo } from '@solana/spl-token-registry'; export const LAMPORT_MULTIPLIER = 10 ** 9; @@ -8,6 +9,7 @@ export const filterModalSolTokens = (tokens: TokenInfo[]) => { }; export async function getAssetCostToStore(files: File[]) { + const localStorage = useLocalStorage(); const totalBytes = files.reduce((sum, f) => (sum += f.size), 0); console.log('Total bytes', totalBytes); const txnFeeInWinstons = parseInt( diff --git a/js/packages/web/src/utils/pubkeyToString.ts b/js/packages/web/src/utils/pubkeyToString.ts deleted file mode 100644 index 4c1f5eb..0000000 --- a/js/packages/web/src/utils/pubkeyToString.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { PublicKey } from '@solana/web3.js'; - -export const pubkeyToString = (key: PublicKey | string = '') => { - return typeof key === 'string' ? key : key?.toBase58() || ''; -}; diff --git a/js/packages/web/src/views/admin/index.tsx b/js/packages/web/src/views/admin/index.tsx index 5cd136b..f619d5f 100644 --- a/js/packages/web/src/views/admin/index.tsx +++ b/js/packages/web/src/views/admin/index.tsx @@ -12,7 +12,10 @@ import { Divider, } from 'antd'; import { useMeta } from '../../contexts'; -import { Store, WhitelistedCreator } from '../../models/metaplex'; +import { + Store, + WhitelistedCreator, +} from '@oyster/common/dist/lib/models/metaplex/index'; import { MasterEditionV1, notify, diff --git a/js/packages/web/src/views/analytics/index.tsx b/js/packages/web/src/views/analytics/index.tsx index f93e083..90429c5 100644 --- a/js/packages/web/src/views/analytics/index.tsx +++ b/js/packages/web/src/views/analytics/index.tsx @@ -1,7 +1,11 @@ import React, { Dispatch, SetStateAction, useState } from 'react'; import { Layout, Button, Col, Spin } from 'antd'; import { useMeta } from '../../contexts'; -import { AuctionManagerV2, WinningConfigType } from '../../models/metaplex'; +import { + AuctionManagerV1, + AuctionManagerV2, + WinningConfigType, +} from '@oyster/common/dist/lib/models/metaplex/index'; import { Pie, Bar } from 'react-chartjs-2'; import { AuctionDataExtended, @@ -16,7 +20,6 @@ import { import { AuctionView, useAuctions } from '../../hooks'; import { QUOTE_MINT } from '../../constants'; import { MintInfo } from '@solana/spl-token'; -import { AuctionManagerV1 } from '../../models/metaplex/deprecatedStates'; const { Content } = Layout; export const AnalyticsView = () => { diff --git a/js/packages/web/src/views/auction/billing.tsx b/js/packages/web/src/views/auction/billing.tsx index f03cef9..40e7499 100644 --- a/js/packages/web/src/views/auction/billing.tsx +++ b/js/packages/web/src/views/auction/billing.tsx @@ -33,7 +33,7 @@ import { NonWinningConstraint, PayoutTicket, WinningConstraint, -} from '../../models/metaplex'; +} from '@oyster/common/dist/lib/models/metaplex/index'; import { Connection } from '@solana/web3.js'; import { settle } from '../../actions/settle'; import { MintInfo } from '@solana/spl-token'; diff --git a/js/packages/web/src/views/auction/index.tsx b/js/packages/web/src/views/auction/index.tsx index c4f6310..376f169 100644 --- a/js/packages/web/src/views/auction/index.tsx +++ b/js/packages/web/src/views/auction/index.tsx @@ -3,9 +3,9 @@ import { useParams } from 'react-router-dom'; import { Row, Col, Button, Skeleton, Carousel, List, Card } from 'antd'; import { AuctionCard } from '../../components/AuctionCard'; import { Connection } from '@solana/web3.js'; +import { AuctionViewItem } from '@oyster/common/dist/lib/models/metaplex/index'; import { AuctionView as Auction, - AuctionViewItem, useArt, useAuction, useBidsForAuction, diff --git a/js/packages/web/src/views/auctionCreate/index.tsx b/js/packages/web/src/views/auctionCreate/index.tsx index 00fc36b..6117ce1 100644 --- a/js/packages/web/src/views/auctionCreate/index.tsx +++ b/js/packages/web/src/views/auctionCreate/index.tsx @@ -37,7 +37,10 @@ import { useWallet } from '@solana/wallet-adapter-react'; import { MintLayout } from '@solana/spl-token'; import { useHistory, useParams } from 'react-router-dom'; import { capitalize } from 'lodash'; -import { WinningConfigType, AmountRange } from '../../models/metaplex'; +import { + WinningConfigType, + AmountRange, +} from '@oyster/common/dist/lib/models/metaplex/index'; import moment from 'moment'; import { createAuctionManager, diff --git a/js/packages/web/src/views/home/setup.tsx b/js/packages/web/src/views/home/setup.tsx index 358a38d..6228c71 100644 --- a/js/packages/web/src/views/home/setup.tsx +++ b/js/packages/web/src/views/home/setup.tsx @@ -2,7 +2,7 @@ import { useConnection, useStore, useWalletModal, - WalletSigner, + WhitelistedCreator, } from '@oyster/common'; import { useWallet } from '@solana/wallet-adapter-react'; import { Button } from 'antd'; @@ -10,9 +10,7 @@ import { useCallback, useEffect, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { saveAdmin } from '../../actions/saveAdmin'; import { useMeta } from '../../contexts'; -import { WhitelistedCreator } from '../../models/metaplex'; import { SetupVariables } from '../../components/SetupVariables'; -import { WalletAdapter } from '@solana/wallet-adapter-base'; export const SetupView = () => { const [isInitalizingStore, setIsInitalizingStore] = useState(false);