Merge pull request #313 from exromany/feat/api-server-2
API-2: preparing to api
This commit is contained in:
commit
f3366f3ab0
|
@ -13,7 +13,8 @@ import {
|
||||||
} from '../utils/ids';
|
} from '../utils/ids';
|
||||||
import { programIds } from '../utils/programIds';
|
import { programIds } from '../utils/programIds';
|
||||||
import { TokenAccount } from '../models/account';
|
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(
|
export function ensureSplAccount(
|
||||||
instructions: TransactionInstruction[],
|
instructions: TransactionInstruction[],
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
import { programIds } from '../utils/programIds';
|
import { programIds } from '../utils/programIds';
|
||||||
import { deserializeUnchecked, serialize } from 'borsh';
|
import { deserializeUnchecked, serialize } from 'borsh';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { AccountParser } from '../contexts';
|
import { AccountParser } from '../contexts/accounts/types';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { findProgramAddress, StringPublicKey, toPublicKey } from '../utils';
|
import { findProgramAddress, StringPublicKey, toPublicKey } from '../utils';
|
||||||
export const AUCTION_PREFIX = 'auction';
|
export const AUCTION_PREFIX = 'auction';
|
||||||
|
@ -672,7 +672,7 @@ export async function createAuction(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startAuction(
|
export async function startAuctionWithResource(
|
||||||
resource: StringPublicKey,
|
resource: StringPublicKey,
|
||||||
creator: StringPublicKey,
|
creator: StringPublicKey,
|
||||||
instructions: TransactionInstruction[],
|
instructions: TransactionInstruction[],
|
||||||
|
|
|
@ -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<any>(null);
|
|
||||||
|
|
||||||
const pendingCalls = new Map<string, Promise<ParsedAccountBase>>();
|
|
||||||
const genericCache = new Map<string, ParsedAccountBase>();
|
|
||||||
const pendingMintCalls = new Map<string, Promise<MintInfo>>();
|
|
||||||
const mintCache = new Map<string, MintInfo>();
|
|
||||||
|
|
||||||
export interface ParsedAccountBase {
|
|
||||||
pubkey: StringPublicKey;
|
|
||||||
account: AccountInfo<Buffer>;
|
|
||||||
info: any; // TODO: change to unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AccountParser = (
|
|
||||||
pubkey: StringPublicKey,
|
|
||||||
data: AccountInfo<Buffer>,
|
|
||||||
) => ParsedAccountBase | undefined;
|
|
||||||
|
|
||||||
export interface ParsedAccount<T> 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<Buffer>) => {
|
|
||||||
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<Buffer>,
|
|
||||||
) => {
|
|
||||||
// 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<Buffer>,
|
|
||||||
) => {
|
|
||||||
const buffer = Buffer.from(info.data);
|
|
||||||
|
|
||||||
const details = {
|
|
||||||
pubkey: pubKey,
|
|
||||||
account: {
|
|
||||||
...info,
|
|
||||||
},
|
|
||||||
info: buffer,
|
|
||||||
} as ParsedAccountBase;
|
|
||||||
|
|
||||||
return details;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const keyToAccountParser = new Map<string, AccountParser>();
|
|
||||||
|
|
||||||
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<TokenAccount>;
|
|
||||||
pendingCalls.set(address, query as any);
|
|
||||||
|
|
||||||
return query;
|
|
||||||
},
|
|
||||||
add: (
|
|
||||||
id: PublicKey | string,
|
|
||||||
obj: AccountInfo<Buffer>,
|
|
||||||
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<MintInfo>;
|
|
||||||
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<Buffer>) => {
|
|
||||||
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<Buffer>,
|
|
||||||
): 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<AccountInfo<Buffer>>();
|
|
||||||
|
|
||||||
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<Buffer> | 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<string>();
|
|
||||||
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<TokenAccount[]>([]);
|
|
||||||
const [userAccounts, setUserAccounts] = useState<TokenAccount[]>([]);
|
|
||||||
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 (
|
|
||||||
<AccountsContext.Provider
|
|
||||||
value={{
|
|
||||||
userAccounts,
|
|
||||||
nativeAccount,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</AccountsContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useNativeAccount() {
|
|
||||||
const context = useContext(AccountsContext);
|
|
||||||
return {
|
|
||||||
account: context.nativeAccount as AccountInfo<Buffer>,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Buffer>;
|
|
||||||
return obj;
|
|
||||||
}) as AccountInfo<Buffer>[],
|
|
||||||
)
|
|
||||||
.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<string[]>[];
|
|
||||||
return { keys, array };
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: fix
|
|
||||||
throw new Error();
|
|
||||||
};
|
|
||||||
|
|
||||||
export function useMint(key?: string | PublicKey) {
|
|
||||||
const connection = useConnection();
|
|
||||||
const [mint, setMint] = useState<MintInfo>();
|
|
||||||
|
|
||||||
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<TokenAccount>();
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
|
@ -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<any>(null);
|
||||||
|
|
||||||
|
export const useAccountsContext = () => {
|
||||||
|
const context = useContext(AccountsContext);
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
function wrapNativeAccount(
|
||||||
|
pubkey: StringPublicKey,
|
||||||
|
account?: AccountInfo<Buffer>,
|
||||||
|
): 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<AccountInfo<Buffer>>();
|
||||||
|
|
||||||
|
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<Buffer> | 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<string>();
|
||||||
|
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<TokenAccount[]>([]);
|
||||||
|
const [userAccounts, setUserAccounts] = useState<TokenAccount[]>([]);
|
||||||
|
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 (
|
||||||
|
<AccountsContext.Provider
|
||||||
|
value={{
|
||||||
|
userAccounts,
|
||||||
|
nativeAccount,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AccountsContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useNativeAccount() {
|
||||||
|
const context = useContext(AccountsContext);
|
||||||
|
return {
|
||||||
|
account: context.nativeAccount as AccountInfo<Buffer>,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMint(key?: string | PublicKey) {
|
||||||
|
const connection = useConnection();
|
||||||
|
const [mint, setMint] = useState<MintInfo>();
|
||||||
|
|
||||||
|
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<TokenAccount>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -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<string, ParsedAccountBase>();
|
||||||
|
const mintCache = new Map<string, MintInfo>();
|
||||||
|
const pendingCalls = new Map<string, Promise<ParsedAccountBase>>();
|
||||||
|
const pendingMintCalls = new Map<string, Promise<MintInfo>>();
|
||||||
|
|
||||||
|
const keyToAccountParser = new Map<string, AccountParser>();
|
||||||
|
|
||||||
|
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<TokenAccount>;
|
||||||
|
pendingCalls.set(address, query as any);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
},
|
||||||
|
add: (
|
||||||
|
id: PublicKey | string,
|
||||||
|
obj: AccountInfo<Buffer>,
|
||||||
|
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<MintInfo>;
|
||||||
|
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<Buffer>) => {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -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;
|
||||||
|
};
|
|
@ -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<Buffer>;
|
||||||
|
return obj;
|
||||||
|
}) as AccountInfo<Buffer>[],
|
||||||
|
)
|
||||||
|
.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<string[]>[];
|
||||||
|
return { keys, array };
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: fix
|
||||||
|
throw new Error();
|
||||||
|
};
|
|
@ -0,0 +1,6 @@
|
||||||
|
export * from './accounts';
|
||||||
|
export * from './cache';
|
||||||
|
export * from './getMultipleAccounts';
|
||||||
|
export * from './parsesrs';
|
||||||
|
export * from './deserialize';
|
||||||
|
export * from './types';
|
|
@ -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<Buffer>,
|
||||||
|
) => {
|
||||||
|
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<Buffer>,
|
||||||
|
) => {
|
||||||
|
// 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<Buffer>,
|
||||||
|
) => {
|
||||||
|
const buffer = Buffer.from(info.data);
|
||||||
|
|
||||||
|
const details = {
|
||||||
|
pubkey: pubKey,
|
||||||
|
account: {
|
||||||
|
...info,
|
||||||
|
},
|
||||||
|
info: buffer,
|
||||||
|
} as ParsedAccountBase;
|
||||||
|
|
||||||
|
return details;
|
||||||
|
};
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { AccountInfo } from '@solana/web3.js';
|
||||||
|
import { StringPublicKey } from '../../utils';
|
||||||
|
|
||||||
|
export interface ParsedAccountBase {
|
||||||
|
pubkey: StringPublicKey;
|
||||||
|
account: AccountInfo<Buffer>;
|
||||||
|
info: any; // TODO: change to unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AccountParser = (
|
||||||
|
pubkey: StringPublicKey,
|
||||||
|
data: AccountInfo<Buffer>,
|
||||||
|
) => ParsedAccountBase | undefined;
|
||||||
|
|
||||||
|
export interface ParsedAccount<T> extends ParsedAccountBase {
|
||||||
|
info: T;
|
||||||
|
}
|
|
@ -4,5 +4,5 @@ export * as Connection from './connection';
|
||||||
export * from './connection';
|
export * from './connection';
|
||||||
export * as Wallet from './wallet';
|
export * as Wallet from './wallet';
|
||||||
export * from './wallet';
|
export * from './wallet';
|
||||||
export * as Store from './store';
|
|
||||||
export * from './store';
|
export * from './store';
|
||||||
|
export * from './meta';
|
||||||
|
|
|
@ -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: {},
|
||||||
|
});
|
|
@ -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';
|
|
@ -1,5 +1,6 @@
|
||||||
import { Metadata, ParsedAccount } from '@oyster/common';
|
import { Metadata } from '../../actions';
|
||||||
import { Store, WhitelistedCreator } from '../../models/metaplex';
|
import { Store, WhitelistedCreator } from '../../models/metaplex';
|
||||||
|
import { ParsedAccount } from '../accounts/types';
|
||||||
|
|
||||||
export const isMetadataPartOfStore = (
|
export const isMetadataPartOfStore = (
|
||||||
m: ParsedAccount<Metadata>,
|
m: ParsedAccount<Metadata>,
|
||||||
|
@ -8,11 +9,7 @@ export const isMetadataPartOfStore = (
|
||||||
string,
|
string,
|
||||||
ParsedAccount<WhitelistedCreator>
|
ParsedAccount<WhitelistedCreator>
|
||||||
>,
|
>,
|
||||||
useAll: boolean,
|
|
||||||
) => {
|
) => {
|
||||||
if (useAll) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!m?.info?.data?.creators || !store?.info) {
|
if (!m?.info?.data?.creators || !store?.info) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
|
@ -5,7 +5,18 @@ import {
|
||||||
StringPublicKey,
|
StringPublicKey,
|
||||||
toPublicKey,
|
toPublicKey,
|
||||||
VAULT_ID,
|
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 { AccountInfo, Connection, PublicKey } from '@solana/web3.js';
|
||||||
import { AccountAndPubkey, MetaState, ProcessAccountsFunc } from './types';
|
import { AccountAndPubkey, MetaState, ProcessAccountsFunc } from './types';
|
||||||
import { isMetadataPartOfStore } from './isMetadataPartOfStore';
|
import { isMetadataPartOfStore } from './isMetadataPartOfStore';
|
||||||
|
@ -13,19 +24,9 @@ import { processAuctions } from './processAuctions';
|
||||||
import { processMetaplexAccounts } from './processMetaplexAccounts';
|
import { processMetaplexAccounts } from './processMetaplexAccounts';
|
||||||
import { processMetaData } from './processMetaData';
|
import { processMetaData } from './processMetaData';
|
||||||
import { processVaultData } from './processVaultData';
|
import { processVaultData } from './processVaultData';
|
||||||
import {
|
import { ParsedAccount } from '../accounts/types';
|
||||||
getEdition,
|
import { getEmptyMetaState } from './getEmptyMetaState';
|
||||||
getMultipleAccounts,
|
import { getMultipleAccounts } from '../accounts/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';
|
|
||||||
|
|
||||||
async function getProgramAccounts(
|
async function getProgramAccounts(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
|
@ -82,30 +83,7 @@ async function getProgramAccounts(
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadAccounts = async (connection: Connection, all: boolean) => {
|
export const loadAccounts = async (connection: Connection, all: boolean) => {
|
||||||
const tempCache: MetaState = {
|
const tempCache: MetaState = getEmptyMetaState();
|
||||||
metadata: [],
|
|
||||||
metadataByMint: {},
|
|
||||||
masterEditions: {},
|
|
||||||
masterEditionsByPrintingMint: {},
|
|
||||||
masterEditionsByOneTimeAuthMint: {},
|
|
||||||
metadataByMasterEdition: {},
|
|
||||||
editions: {},
|
|
||||||
auctionManagersByAuction: {},
|
|
||||||
bidRedemptions: {},
|
|
||||||
auctions: {},
|
|
||||||
auctionDataExtended: {},
|
|
||||||
vaults: {},
|
|
||||||
payoutTickets: {},
|
|
||||||
store: null,
|
|
||||||
whitelistedCreatorsByCreator: {},
|
|
||||||
bidderMetadataByAuctionAndBidder: {},
|
|
||||||
bidderPotsByAuctionAndBidder: {},
|
|
||||||
safetyDepositBoxesByVaultAndIndex: {},
|
|
||||||
prizeTrackingTickets: {},
|
|
||||||
safetyDepositConfigsByAuctionManagerAndIndex: {},
|
|
||||||
bidRedemptionV2sByAuctionManagerAndWinningIndex: {},
|
|
||||||
stores: {},
|
|
||||||
};
|
|
||||||
const updateTemp = makeSetter(tempCache);
|
const updateTemp = makeSetter(tempCache);
|
||||||
|
|
||||||
const forEach =
|
const forEach =
|
||||||
|
@ -115,89 +93,58 @@ export const loadAccounts = async (connection: Connection, all: boolean) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const additionalPromises: Promise<void>[] = [];
|
let isSelectivePullMetadata = false;
|
||||||
|
const pullMetadata = async (creators: AccountAndPubkey[]) => {
|
||||||
|
await forEach(processMetaplexAccounts)(creators);
|
||||||
|
|
||||||
const IS_BIG_STORE =
|
const whitelistedCreators = Object.values(
|
||||||
process.env.NEXT_PUBLIC_BIG_STORE?.toLowerCase() === 'true';
|
tempCache.whitelistedCreatorsByCreator,
|
||||||
console.log(`Is big store: ${IS_BIG_STORE}`);
|
);
|
||||||
|
|
||||||
const promises = [
|
if (whitelistedCreators.length > 3) {
|
||||||
getProgramAccounts(connection, VAULT_ID).then(forEach(processVaultData)),
|
console.log(' too many creators, pulling all nfts in one go');
|
||||||
getProgramAccounts(connection, AUCTION_ID).then(forEach(processAuctions)),
|
additionalPromises.push(
|
||||||
getProgramAccounts(connection, METAPLEX_ID).then(
|
getProgramAccounts(connection, METADATA_PROGRAM_ID).then(
|
||||||
forEach(processMetaplexAccounts),
|
|
||||||
),
|
|
||||||
IS_BIG_STORE
|
|
||||||
? getProgramAccounts(connection, METADATA_PROGRAM_ID).then(
|
|
||||||
forEach(processMetaData),
|
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) {
|
for (let i = 0; i < MAX_CREATOR_LIMIT; i++) {
|
||||||
console.log(' too many creators, pulling all nfts in one go');
|
for (let j = 0; j < whitelistedCreators.length; j++) {
|
||||||
additionalPromises.push(
|
additionalPromises.push(
|
||||||
getProgramAccounts(connection, METADATA_PROGRAM_ID).then(
|
getProgramAccounts(connection, METADATA_PROGRAM_ID, {
|
||||||
forEach(processMetaData),
|
filters: [
|
||||||
),
|
{
|
||||||
);
|
memcmp: {
|
||||||
} else {
|
offset:
|
||||||
console.log('pulling optimized nfts');
|
1 + // key
|
||||||
|
32 + // update auth
|
||||||
for (let i = 0; i < MAX_CREATOR_LIMIT; i++) {
|
32 + // mint
|
||||||
for (let j = 0; j < whitelistedCreators.length; j++) {
|
4 + // name string length
|
||||||
additionalPromises.push(
|
MAX_NAME_LENGTH + // name
|
||||||
getProgramAccounts(connection, METADATA_PROGRAM_ID, {
|
4 + // uri string length
|
||||||
filters: [
|
MAX_URI_LENGTH + // uri
|
||||||
{
|
4 + // symbol string length
|
||||||
memcmp: {
|
MAX_SYMBOL_LENGTH + // symbol
|
||||||
offset:
|
2 + // seller fee basis points
|
||||||
1 + // key
|
1 + // whether or not there is a creators vec
|
||||||
32 + // update auth
|
4 + // creators vec length
|
||||||
32 + // mint
|
i * MAX_CREATOR_LEN,
|
||||||
4 + // name string length
|
bytes: whitelistedCreators[j].info.address,
|
||||||
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);
|
const pullEditions = async () => {
|
||||||
console.log('Metadata size', tempCache.metadata.length);
|
|
||||||
|
|
||||||
if (additionalPromises.length > 0) {
|
|
||||||
console.log('Pulling editions for optimized metadata');
|
console.log('Pulling editions for optimized metadata');
|
||||||
let setOf100MetadataEditionKeys: string[] = [];
|
let setOf100MetadataEditionKeys: string[] = [];
|
||||||
const editionPromises: Promise<{
|
const editionPromises: Promise<{
|
||||||
|
@ -263,6 +210,39 @@ export const loadAccounts = async (connection: Connection, all: boolean) => {
|
||||||
Object.keys(tempCache.editions).length,
|
Object.keys(tempCache.editions).length,
|
||||||
Object.keys(tempCache.masterEditions).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<void>[] = [];
|
||||||
|
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;
|
return tempCache;
|
||||||
|
@ -294,11 +274,11 @@ export const metadataByMintUpdater = async (
|
||||||
) => {
|
) => {
|
||||||
const key = metadata.info.mint;
|
const key = metadata.info.mint;
|
||||||
if (
|
if (
|
||||||
|
all ||
|
||||||
isMetadataPartOfStore(
|
isMetadataPartOfStore(
|
||||||
metadata,
|
metadata,
|
||||||
state.store,
|
state.store,
|
||||||
state.whitelistedCreatorsByCreator,
|
state.whitelistedCreatorsByCreator,
|
||||||
all,
|
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
await metadata.info.init();
|
await metadata.info.init();
|
|
@ -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<MetaContextState>({
|
||||||
|
...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<MetaState>(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 (
|
||||||
|
<MetaContext.Provider
|
||||||
|
value={{
|
||||||
|
...state,
|
||||||
|
isLoading,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</MetaContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useMeta = () => {
|
||||||
|
const context = useContext(MetaContext);
|
||||||
|
return context;
|
||||||
|
};
|
|
@ -1,7 +1,5 @@
|
||||||
import {
|
import { ProgramAccountChangeCallback } from '@solana/web3.js';
|
||||||
KeyedAccountInfo,
|
import { pubkeyToString } from '../../utils';
|
||||||
ProgramAccountChangeCallback,
|
|
||||||
} from '@solana/web3.js';
|
|
||||||
import { ProcessAccountsFunc, UpdateStateValueFunc } from './types';
|
import { ProcessAccountsFunc, UpdateStateValueFunc } from './types';
|
||||||
|
|
||||||
export const onChangeAccount =
|
export const onChangeAccount =
|
||||||
|
@ -11,7 +9,7 @@ export const onChangeAccount =
|
||||||
all: boolean,
|
all: boolean,
|
||||||
): ProgramAccountChangeCallback =>
|
): ProgramAccountChangeCallback =>
|
||||||
async info => {
|
async info => {
|
||||||
const pubkey = pubkeyByAccountInfo(info);
|
const pubkey = pubkeyToString(info.accountId);
|
||||||
await process(
|
await process(
|
||||||
{
|
{
|
||||||
pubkey,
|
pubkey,
|
||||||
|
@ -21,9 +19,3 @@ export const onChangeAccount =
|
||||||
all,
|
all,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const pubkeyByAccountInfo = (info: KeyedAccountInfo) => {
|
|
||||||
return typeof info.accountId === 'string'
|
|
||||||
? info.accountId
|
|
||||||
: info.accountId.toBase58();
|
|
||||||
};
|
|
|
@ -1,19 +1,19 @@
|
||||||
import {
|
import {
|
||||||
AuctionParser,
|
|
||||||
cache,
|
|
||||||
ParsedAccount,
|
|
||||||
AuctionData,
|
AuctionData,
|
||||||
|
AuctionDataExtended,
|
||||||
|
AuctionDataExtendedParser,
|
||||||
|
AuctionParser,
|
||||||
BidderMetadata,
|
BidderMetadata,
|
||||||
BidderMetadataParser,
|
BidderMetadataParser,
|
||||||
BidderPot,
|
BidderPot,
|
||||||
BidderPotParser,
|
BidderPotParser,
|
||||||
BIDDER_METADATA_LEN,
|
BIDDER_METADATA_LEN,
|
||||||
BIDDER_POT_LEN,
|
BIDDER_POT_LEN,
|
||||||
AuctionDataExtended,
|
|
||||||
MAX_AUCTION_DATA_EXTENDED_SIZE,
|
MAX_AUCTION_DATA_EXTENDED_SIZE,
|
||||||
AuctionDataExtendedParser,
|
} from '../../actions';
|
||||||
AUCTION_ID,
|
import { AUCTION_ID } from '../../utils';
|
||||||
} from '@oyster/common';
|
import { ParsedAccount } from '../accounts/types';
|
||||||
|
import { cache } from '../accounts/cache';
|
||||||
import { CheckAccountFunc, ProcessAccountsFunc } from './types';
|
import { CheckAccountFunc, ProcessAccountsFunc } from './types';
|
||||||
|
|
||||||
export const processAuctions: ProcessAccountsFunc = (
|
export const processAuctions: ProcessAccountsFunc = (
|
|
@ -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 { AccountInfo } from '@solana/web3.js';
|
||||||
import { ProcessAccountsFunc } from './types';
|
import { ProcessAccountsFunc } from './types';
|
||||||
import { isValidHttpUrl } from '../../utils/isValidHttpUrl';
|
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 = (
|
export const processMetaData: ProcessAccountsFunc = (
|
||||||
{ account, pubkey },
|
{ account, pubkey },
|
|
@ -1,13 +1,12 @@
|
||||||
import { programIds, cache, ParsedAccount, METAPLEX_ID } from '@oyster/common';
|
|
||||||
import { AccountInfo, PublicKey } from '@solana/web3.js';
|
import { AccountInfo, PublicKey } from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
|
AuctionManagerV1,
|
||||||
AuctionManagerV2,
|
AuctionManagerV2,
|
||||||
BidRedemptionTicket,
|
BidRedemptionTicket,
|
||||||
decodeAuctionManager,
|
decodeAuctionManager,
|
||||||
decodeBidRedemptionTicket,
|
decodeBidRedemptionTicket,
|
||||||
decodeStore,
|
decodeStore,
|
||||||
decodeWhitelistedCreator,
|
isCreatorPartOfTheStore,
|
||||||
getWhitelistedCreator,
|
|
||||||
MetaplexKey,
|
MetaplexKey,
|
||||||
Store,
|
Store,
|
||||||
WhitelistedCreator,
|
WhitelistedCreator,
|
||||||
|
@ -20,9 +19,10 @@ import {
|
||||||
decodeSafetyDepositConfig,
|
decodeSafetyDepositConfig,
|
||||||
SafetyDepositConfig,
|
SafetyDepositConfig,
|
||||||
} from '../../models/metaplex';
|
} from '../../models/metaplex';
|
||||||
import { AuctionManagerV1 } from '../../models/metaplex/deprecatedStates';
|
|
||||||
import names from '../../config/userNames.json';
|
|
||||||
import { ProcessAccountsFunc } from './types';
|
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 (
|
export const processMetaplexAccounts: ProcessAccountsFunc = async (
|
||||||
{ account, pubkey },
|
{ account, pubkey },
|
||||||
|
@ -130,30 +130,33 @@ export const processMetaplexAccounts: ProcessAccountsFunc = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWhitelistedCreatorV1Account(account)) {
|
if (isWhitelistedCreatorV1Account(account)) {
|
||||||
const whitelistedCreator = decodeWhitelistedCreator(account.data);
|
const parsedAccount = cache.add(
|
||||||
|
pubkey,
|
||||||
|
account,
|
||||||
|
WhitelistedCreatorParser,
|
||||||
|
false,
|
||||||
|
) as ParsedAccount<WhitelistedCreator>;
|
||||||
|
|
||||||
// TODO: figure out a way to avoid generating creator addresses during parsing
|
// TODO: figure out a way to avoid generating creator addresses during parsing
|
||||||
// should we store store id inside creator?
|
// should we store store id inside creator?
|
||||||
const creatorKeyIfCreatorWasPartOfThisStore = await getWhitelistedCreator(
|
if (STORE_ID) {
|
||||||
whitelistedCreator.address,
|
const isWhitelistedCreator = await isCreatorPartOfTheStore(
|
||||||
);
|
parsedAccount.info.address,
|
||||||
|
|
||||||
if (creatorKeyIfCreatorWasPartOfThisStore === pubkey) {
|
|
||||||
const parsedAccount = cache.add(
|
|
||||||
pubkey,
|
pubkey,
|
||||||
account,
|
);
|
||||||
WhitelistedCreatorParser,
|
if (isWhitelistedCreator) {
|
||||||
false,
|
setter(
|
||||||
) as ParsedAccount<WhitelistedCreator>;
|
'whitelistedCreatorsByCreator',
|
||||||
|
parsedAccount.info.address,
|
||||||
const nameInfo = (names as any)[parsedAccount.info.address];
|
parsedAccount,
|
||||||
|
);
|
||||||
if (nameInfo) {
|
|
||||||
parsedAccount.info = { ...parsedAccount.info, ...nameInfo };
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useAll) {
|
||||||
setter(
|
setter(
|
||||||
'whitelistedCreatorsByCreator',
|
'creators',
|
||||||
whitelistedCreator.address,
|
parsedAccount.info.address + '-' + pubkey,
|
||||||
parsedAccount,
|
parsedAccount,
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
|
import { AccountInfo } from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
ParsedAccount,
|
|
||||||
SafetyDepositBox,
|
|
||||||
VaultKey,
|
|
||||||
decodeSafetyDeposit,
|
decodeSafetyDeposit,
|
||||||
decodeVault,
|
decodeVault,
|
||||||
|
SafetyDepositBox,
|
||||||
Vault,
|
Vault,
|
||||||
} from '@oyster/common';
|
VaultKey,
|
||||||
import { VAULT_ID } from '@oyster/common/dist/lib/utils/ids';
|
} from '../../actions';
|
||||||
import { AccountInfo } from '@solana/web3.js';
|
import { VAULT_ID } from '../../utils';
|
||||||
|
import { ParsedAccount } from '../accounts/types';
|
||||||
import { ProcessAccountsFunc } from './types';
|
import { ProcessAccountsFunc } from './types';
|
||||||
|
|
||||||
export const processVaultData: ProcessAccountsFunc = (
|
export const processVaultData: ProcessAccountsFunc = (
|
|
@ -1,12 +1,10 @@
|
||||||
import {
|
|
||||||
Metadata,
|
|
||||||
getMultipleAccounts,
|
|
||||||
cache,
|
|
||||||
MintParser,
|
|
||||||
ParsedAccount,
|
|
||||||
} from '@oyster/common';
|
|
||||||
import { MintInfo } from '@solana/spl-token';
|
import { MintInfo } from '@solana/spl-token';
|
||||||
import { Connection } from '@solana/web3.js';
|
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 (
|
export const queryExtendedMetadata = async (
|
||||||
connection: Connection,
|
connection: Connection,
|
|
@ -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);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
|
@ -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 { AccountInfo } from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
|
AuctionData,
|
||||||
|
AuctionDataExtended,
|
||||||
|
BidderMetadata,
|
||||||
|
BidderPot,
|
||||||
|
Edition,
|
||||||
|
MasterEditionV1,
|
||||||
|
MasterEditionV2,
|
||||||
|
Metadata,
|
||||||
|
SafetyDepositBox,
|
||||||
|
Vault,
|
||||||
|
} from '../../actions';
|
||||||
|
import {
|
||||||
|
AuctionManagerV1,
|
||||||
|
AuctionManagerV2,
|
||||||
BidRedemptionTicket,
|
BidRedemptionTicket,
|
||||||
Store,
|
BidRedemptionTicketV2,
|
||||||
WhitelistedCreator,
|
|
||||||
PayoutTicket,
|
PayoutTicket,
|
||||||
PrizeTrackingTicket,
|
PrizeTrackingTicket,
|
||||||
AuctionManagerV2,
|
|
||||||
SafetyDepositConfig,
|
SafetyDepositConfig,
|
||||||
BidRedemptionTicketV2,
|
Store,
|
||||||
|
WhitelistedCreator,
|
||||||
} from '../../models/metaplex';
|
} from '../../models/metaplex';
|
||||||
import { AuctionManagerV1 } from '../../models/metaplex/deprecatedStates';
|
import { PublicKeyStringAndAccount } from '../../utils';
|
||||||
|
import { ParsedAccount } from '../accounts/types';
|
||||||
|
|
||||||
export interface MetaState {
|
export interface MetaState {
|
||||||
metadata: ParsedAccount<Metadata>[];
|
metadata: ParsedAccount<Metadata>[];
|
||||||
|
@ -72,6 +72,7 @@ export interface MetaState {
|
||||||
>;
|
>;
|
||||||
payoutTickets: Record<string, ParsedAccount<PayoutTicket>>;
|
payoutTickets: Record<string, ParsedAccount<PayoutTicket>>;
|
||||||
stores: Record<string, ParsedAccount<Store>>;
|
stores: Record<string, ParsedAccount<Store>>;
|
||||||
|
creators: Record<string, ParsedAccount<WhitelistedCreator>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MetaContextState extends MetaState {
|
export interface MetaContextState extends MetaState {
|
|
@ -1 +1,2 @@
|
||||||
export * from './account';
|
export * from './account';
|
||||||
|
export * from './metaplex';
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
import {
|
|
||||||
getBidderPotKey,
|
|
||||||
programIds,
|
|
||||||
StringPublicKey,
|
|
||||||
toPublicKey,
|
|
||||||
} from '@oyster/common';
|
|
||||||
import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
|
|
||||||
import { getAuctionKeys, ClaimBidArgs, SCHEMA } from '.';
|
import { getAuctionKeys, ClaimBidArgs, SCHEMA } from '.';
|
||||||
|
import { getBidderPotKey } from '../../actions';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
|
|
||||||
export async function claimBid(
|
export async function claimBid(
|
||||||
acceptPayment: StringPublicKey,
|
acceptPayment: StringPublicKey,
|
|
@ -1,8 +1,8 @@
|
||||||
import { programIds, StringPublicKey, toPublicKey } from '@oyster/common';
|
|
||||||
import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
|
|
||||||
import { DecommissionAuctionManagerArgs, SCHEMA } from '.';
|
import { DecommissionAuctionManagerArgs, SCHEMA } from '.';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
|
|
||||||
export async function decommissionAuctionManager(
|
export async function decommissionAuctionManager(
|
||||||
auctionManager: StringPublicKey,
|
auctionManager: StringPublicKey,
|
|
@ -1,4 +1,3 @@
|
||||||
import { programIds, StringPublicKey, toPublicKey } from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -7,6 +6,7 @@ import {
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
|
|
||||||
import { getAuctionKeys, SCHEMA } from '.';
|
import { getAuctionKeys, SCHEMA } from '.';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
import {
|
import {
|
||||||
AuctionManagerSettingsV1,
|
AuctionManagerSettingsV1,
|
||||||
DeprecatedInitAuctionManagerV1Args,
|
DeprecatedInitAuctionManagerV1Args,
|
|
@ -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 { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
|
|
||||||
import { SCHEMA } from '.';
|
import { SCHEMA } from '.';
|
||||||
|
import { getAuctionExtended, VAULT_PREFIX } from '../../actions';
|
||||||
|
import {
|
||||||
|
findProgramAddress,
|
||||||
|
programIds,
|
||||||
|
StringPublicKey,
|
||||||
|
toPublicKey,
|
||||||
|
} from '../../utils';
|
||||||
import { DeprecatedPopulateParticipationPrintingAccountArgs } from './deprecatedStates';
|
import { DeprecatedPopulateParticipationPrintingAccountArgs } from './deprecatedStates';
|
||||||
|
|
||||||
export async function deprecatedPopulateParticipationPrintingAccount(
|
export async function deprecatedPopulateParticipationPrintingAccount(
|
|
@ -1,4 +1,3 @@
|
||||||
import { programIds, StringPublicKey, toPublicKey } from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -12,6 +11,7 @@ import {
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
getSafetyDepositConfig,
|
getSafetyDepositConfig,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
import { DeprecatedRedeemParticipationBidArgs } from './deprecatedStates';
|
import { DeprecatedRedeemParticipationBidArgs } from './deprecatedStates';
|
||||||
|
|
||||||
export async function deprecatedRedeemParticipationBid(
|
export async function deprecatedRedeemParticipationBid(
|
|
@ -1,4 +1,3 @@
|
||||||
import { programIds, findProgramAddress, toPublicKey } from '@oyster/common';
|
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import {
|
import {
|
||||||
AuctionManagerStatus,
|
AuctionManagerStatus,
|
||||||
|
@ -9,6 +8,7 @@ import {
|
||||||
WinningConfigType,
|
WinningConfigType,
|
||||||
WinningConstraint,
|
WinningConstraint,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
import { findProgramAddress, programIds, toPublicKey } from '../../utils';
|
||||||
|
|
||||||
export const MAX_BID_REDEMPTION_TICKET_V1_SIZE = 3;
|
export const MAX_BID_REDEMPTION_TICKET_V1_SIZE = 3;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { programIds, StringPublicKey, toPublicKey } from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -7,6 +6,7 @@ import {
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
|
|
||||||
import { SCHEMA } from '.';
|
import { SCHEMA } from '.';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
import { DeprecatedValidateParticipationArgs } from './deprecatedStates';
|
import { DeprecatedValidateParticipationArgs } from './deprecatedStates';
|
||||||
|
|
||||||
export async function deprecatedValidateParticipation(
|
export async function deprecatedValidateParticipation(
|
|
@ -1,4 +1,3 @@
|
||||||
import { programIds, StringPublicKey, toPublicKey } from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -7,6 +6,7 @@ import {
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
|
|
||||||
import { getAuctionKeys, getOriginalAuthority, SCHEMA } from '.';
|
import { getAuctionKeys, getOriginalAuthority, SCHEMA } from '.';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getSafetyDepositBoxValidationTicket,
|
getSafetyDepositBoxValidationTicket,
|
|
@ -1,4 +1,3 @@
|
||||||
import { programIds, StringPublicKey, toPublicKey } from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -13,6 +12,7 @@ import {
|
||||||
getSafetyDepositConfig,
|
getSafetyDepositConfig,
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
|
|
||||||
export async function emptyPaymentAccount(
|
export async function emptyPaymentAccount(
|
||||||
acceptPayment: StringPublicKey,
|
acceptPayment: StringPublicKey,
|
|
@ -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 { AccountInfo, SystemProgram } from '@solana/web3.js';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { deserializeUnchecked } from 'borsh';
|
|
||||||
import bs58 from 'bs58';
|
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 {
|
import {
|
||||||
AuctionManagerV1,
|
AuctionManagerV1,
|
||||||
BidRedemptionTicketV1,
|
BidRedemptionTicketV1,
|
||||||
|
@ -35,6 +35,7 @@ export * from './deprecatedValidateSafetyDepositBoxV1';
|
||||||
export * from './redeemParticipationBidV3';
|
export * from './redeemParticipationBidV3';
|
||||||
export * from './redeemPrintingV2Bid';
|
export * from './redeemPrintingV2Bid';
|
||||||
export * from './withdrawMasterEdition';
|
export * from './withdrawMasterEdition';
|
||||||
|
export * from './deprecatedStates';
|
||||||
|
|
||||||
export const METAPLEX_PREFIX = 'metaplex';
|
export const METAPLEX_PREFIX = 'metaplex';
|
||||||
export const TOTALS = 'totals';
|
export const TOTALS = 'totals';
|
||||||
|
@ -537,6 +538,14 @@ export interface BidRedemptionTicket {
|
||||||
|
|
||||||
getBidRedeemed(order: number): boolean;
|
getBidRedeemed(order: number): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AuctionViewItem {
|
||||||
|
winningConfigType: WinningConfigType;
|
||||||
|
amount: BN;
|
||||||
|
metadata: ParsedAccount<Metadata>;
|
||||||
|
safetyDeposit: ParsedAccount<SafetyDepositBox>;
|
||||||
|
masterEdition?: ParsedAccount<MasterEditionV1 | MasterEditionV2>;
|
||||||
|
}
|
||||||
export class BidRedemptionTicketV2 implements BidRedemptionTicket {
|
export class BidRedemptionTicketV2 implements BidRedemptionTicket {
|
||||||
key: MetaplexKey = MetaplexKey.BidRedemptionTicketV2;
|
key: MetaplexKey = MetaplexKey.BidRedemptionTicketV2;
|
||||||
winnerIndex: BN | null;
|
winnerIndex: BN | null;
|
||||||
|
@ -1087,9 +1096,22 @@ export async function getOriginalAuthority(
|
||||||
)[0];
|
)[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 PROGRAM_IDS = programIds();
|
||||||
const store = PROGRAM_IDS.store;
|
const store = storeId || PROGRAM_IDS.store;
|
||||||
if (!store) {
|
if (!store) {
|
||||||
throw new Error('Store not initialized');
|
throw new Error('Store not initialized');
|
||||||
}
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
import { programIds, StringPublicKey, toPublicKey } from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -14,6 +13,7 @@ import {
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
TupleNumericType,
|
TupleNumericType,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
|
|
||||||
export async function initAuctionManagerV2(
|
export async function initAuctionManagerV2(
|
||||||
vault: StringPublicKey,
|
vault: StringPublicKey,
|
|
@ -1,10 +1,3 @@
|
||||||
import {
|
|
||||||
findProgramAddress,
|
|
||||||
programIds,
|
|
||||||
StringPublicKey,
|
|
||||||
toPublicKey,
|
|
||||||
VAULT_PREFIX,
|
|
||||||
} from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -21,6 +14,13 @@ import {
|
||||||
RedeemUnusedWinningConfigItemsAsAuctioneerArgs,
|
RedeemUnusedWinningConfigItemsAsAuctioneerArgs,
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
import { VAULT_PREFIX } from '../../actions';
|
||||||
|
import {
|
||||||
|
findProgramAddress,
|
||||||
|
programIds,
|
||||||
|
StringPublicKey,
|
||||||
|
toPublicKey,
|
||||||
|
} from '../../utils';
|
||||||
|
|
||||||
export async function redeemBid(
|
export async function redeemBid(
|
||||||
vault: StringPublicKey,
|
vault: StringPublicKey,
|
|
@ -1,10 +1,3 @@
|
||||||
import {
|
|
||||||
programIds,
|
|
||||||
VAULT_PREFIX,
|
|
||||||
findProgramAddress,
|
|
||||||
StringPublicKey,
|
|
||||||
toPublicKey,
|
|
||||||
} from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -21,6 +14,13 @@ import {
|
||||||
RedeemUnusedWinningConfigItemsAsAuctioneerArgs,
|
RedeemUnusedWinningConfigItemsAsAuctioneerArgs,
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
import { VAULT_PREFIX } from '../../actions';
|
||||||
|
import {
|
||||||
|
findProgramAddress,
|
||||||
|
programIds,
|
||||||
|
StringPublicKey,
|
||||||
|
toPublicKey,
|
||||||
|
} from '../../utils';
|
||||||
|
|
||||||
export async function redeemFullRightsTransferBid(
|
export async function redeemFullRightsTransferBid(
|
||||||
vault: StringPublicKey,
|
vault: StringPublicKey,
|
|
@ -1,12 +1,3 @@
|
||||||
import {
|
|
||||||
getEdition,
|
|
||||||
programIds,
|
|
||||||
getMetadata,
|
|
||||||
getEditionMarkPda,
|
|
||||||
getAuctionExtended,
|
|
||||||
StringPublicKey,
|
|
||||||
toPublicKey,
|
|
||||||
} from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -23,6 +14,13 @@ import {
|
||||||
getPrizeTrackingTicket,
|
getPrizeTrackingTicket,
|
||||||
getSafetyDepositConfig,
|
getSafetyDepositConfig,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
import {
|
||||||
|
getAuctionExtended,
|
||||||
|
getEdition,
|
||||||
|
getEditionMarkPda,
|
||||||
|
getMetadata,
|
||||||
|
} from '../../actions';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
|
|
||||||
export async function redeemParticipationBidV3(
|
export async function redeemParticipationBidV3(
|
||||||
vault: StringPublicKey,
|
vault: StringPublicKey,
|
|
@ -1,11 +1,3 @@
|
||||||
import {
|
|
||||||
getEdition,
|
|
||||||
getEditionMarkPda,
|
|
||||||
getMetadata,
|
|
||||||
programIds,
|
|
||||||
StringPublicKey,
|
|
||||||
toPublicKey,
|
|
||||||
} from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -22,6 +14,8 @@ import {
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
getSafetyDepositConfig,
|
getSafetyDepositConfig,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
import { getEdition, getEditionMarkPda, getMetadata } from '../../actions';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
|
|
||||||
export async function redeemPrintingV2Bid(
|
export async function redeemPrintingV2Bid(
|
||||||
vault: StringPublicKey,
|
vault: StringPublicKey,
|
|
@ -1,8 +1,8 @@
|
||||||
import { programIds, StringPublicKey, toPublicKey } from '@oyster/common';
|
|
||||||
import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
|
|
||||||
import { SCHEMA, SetStoreArgs } from '.';
|
import { SCHEMA, SetStoreArgs } from '.';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
|
|
||||||
export async function setStore(
|
export async function setStore(
|
||||||
isPublic: boolean,
|
isPublic: boolean,
|
|
@ -1,8 +1,8 @@
|
||||||
import { programIds, StringPublicKey, toPublicKey } from '@oyster/common';
|
|
||||||
import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
|
|
||||||
import { getWhitelistedCreator, SCHEMA, SetWhitelistedCreatorArgs } from '.';
|
import { getWhitelistedCreator, SCHEMA, SetWhitelistedCreatorArgs } from '.';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
|
|
||||||
export async function setWhitelistedCreator(
|
export async function setWhitelistedCreator(
|
||||||
creator: StringPublicKey,
|
creator: StringPublicKey,
|
|
@ -1,8 +1,8 @@
|
||||||
import { programIds, StringPublicKey, toPublicKey } from '@oyster/common';
|
|
||||||
import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
|
|
||||||
import { getAuctionKeys, SCHEMA, StartAuctionArgs } from '.';
|
import { getAuctionKeys, SCHEMA, StartAuctionArgs } from '.';
|
||||||
|
import { programIds, StringPublicKey, toPublicKey } from '../../utils';
|
||||||
|
|
||||||
export async function startAuction(
|
export async function startAuction(
|
||||||
vault: StringPublicKey,
|
vault: StringPublicKey,
|
|
@ -1,4 +1,3 @@
|
||||||
import { programIds, toPublicKey, StringPublicKey } from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -15,6 +14,7 @@ import {
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
ValidateSafetyDepositBoxV2Args,
|
ValidateSafetyDepositBoxV2Args,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
import { programIds, toPublicKey, StringPublicKey } from '../../utils';
|
||||||
|
|
||||||
export async function validateSafetyDepositBoxV2(
|
export async function validateSafetyDepositBoxV2(
|
||||||
vault: StringPublicKey,
|
vault: StringPublicKey,
|
|
@ -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 { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js';
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
|
|
||||||
|
@ -17,6 +8,13 @@ import {
|
||||||
getPrizeTrackingTicket,
|
getPrizeTrackingTicket,
|
||||||
getSafetyDepositConfig,
|
getSafetyDepositConfig,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
import { AUCTION_PREFIX, EXTENDED, VAULT_PREFIX } from '../../actions';
|
||||||
|
import {
|
||||||
|
findProgramAddress,
|
||||||
|
programIds,
|
||||||
|
toPublicKey,
|
||||||
|
StringPublicKey,
|
||||||
|
} from '../../utils';
|
||||||
|
|
||||||
export async function withdrawMasterEdition(
|
export async function withdrawMasterEdition(
|
||||||
vault: StringPublicKey,
|
vault: StringPublicKey,
|
|
@ -36,6 +36,10 @@ export const toPublicKey = (key: string | PublicKey) => {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const pubkeyToString = (key: PublicKey | null | string = '') => {
|
||||||
|
return typeof key === 'string' ? key : key?.toBase58() || '';
|
||||||
|
};
|
||||||
|
|
||||||
export interface PublicKeyStringAndAccount<T> {
|
export interface PublicKeyStringAndAccount<T> {
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
account: AccountInfo<T>;
|
account: AccountInfo<T>;
|
||||||
|
|
|
@ -4,6 +4,8 @@ export * from './programIds';
|
||||||
export * as Layout from './layout';
|
export * as Layout from './layout';
|
||||||
export * from './notifications';
|
export * from './notifications';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
export * from './useLocalStorage';
|
||||||
export * from './strings';
|
export * from './strings';
|
||||||
export * as shortvec from './shortvec';
|
export * as shortvec from './shortvec';
|
||||||
|
export * from './isValidHttpUrl';
|
||||||
export * from './borsh';
|
export * from './borsh';
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
||||||
|
};
|
|
@ -6,6 +6,7 @@ import { PublicKey } from '@solana/web3.js';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { WAD, ZERO } from '../constants';
|
import { WAD, ZERO } from '../constants';
|
||||||
import { TokenInfo } from '@solana/spl-token-registry';
|
import { TokenInfo } from '@solana/spl-token-registry';
|
||||||
|
import { useLocalStorage } from './useLocalStorage';
|
||||||
|
|
||||||
export type KnownTokenMap = Map<string, TokenInfo>;
|
export type KnownTokenMap = Map<string, TokenInfo>;
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ export const formatPriceNumber = new Intl.NumberFormat('en-US', {
|
||||||
});
|
});
|
||||||
|
|
||||||
export function useLocalStorageState(key: string, defaultState?: string) {
|
export function useLocalStorageState(key: string, defaultState?: string) {
|
||||||
|
const localStorage = useLocalStorage();
|
||||||
const [state, setState] = useState(() => {
|
const [state, setState] = useState(() => {
|
||||||
// NOTE: Not sure if this is ok
|
// NOTE: Not sure if this is ok
|
||||||
const storedState = localStorage.getItem(key);
|
const storedState = localStorage.getItem(key);
|
||||||
|
@ -52,6 +54,7 @@ export const findProgramAddress = async (
|
||||||
seeds: (Buffer | Uint8Array)[],
|
seeds: (Buffer | Uint8Array)[],
|
||||||
programId: PublicKey,
|
programId: PublicKey,
|
||||||
) => {
|
) => {
|
||||||
|
const localStorage = useLocalStorage();
|
||||||
const key =
|
const key =
|
||||||
'pda-' +
|
'pda-' +
|
||||||
seeds.reduce((agg, item) => agg + item.toString('hex'), '') +
|
seeds.reduce((agg, item) => agg + item.toString('hex'), '') +
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js';
|
import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
utils,
|
utils,
|
||||||
actions,
|
|
||||||
models,
|
|
||||||
findProgramAddress,
|
findProgramAddress,
|
||||||
MetadataKey,
|
MetadataKey,
|
||||||
StringPublicKey,
|
StringPublicKey,
|
||||||
toPublicKey,
|
toPublicKey,
|
||||||
WalletSigner,
|
WalletSigner,
|
||||||
} from '@oyster/common';
|
} 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 { AccountLayout } from '@solana/spl-token';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { SafetyDepositDraft } from './createAuctionManager';
|
import { SafetyDepositDraft } from './createAuctionManager';
|
||||||
import { SafetyDepositConfig } from '../models/metaplex';
|
|
||||||
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
||||||
const { createTokenAccount, addTokenToInactiveVault, VAULT_PREFIX } = actions;
|
|
||||||
const { approve } = models;
|
|
||||||
|
|
||||||
export interface SafetyDepositInstructionTemplate {
|
export interface SafetyDepositInstructionTemplate {
|
||||||
box: {
|
box: {
|
||||||
|
|
|
@ -16,7 +16,10 @@ import {
|
||||||
import { AccountLayout } from '@solana/spl-token';
|
import { AccountLayout } from '@solana/spl-token';
|
||||||
import { TransactionInstruction, Keypair, Connection } from '@solana/web3.js';
|
import { TransactionInstruction, Keypair, Connection } from '@solana/web3.js';
|
||||||
import { AuctionView } from '../hooks';
|
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 { claimUnusedPrizes } from './claimUnusedPrizes';
|
||||||
import { setupPlaceBid } from './sendPlaceBid';
|
import { setupPlaceBid } from './sendPlaceBid';
|
||||||
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js';
|
import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
actions,
|
|
||||||
ParsedAccount,
|
ParsedAccount,
|
||||||
TokenAccount,
|
TokenAccount,
|
||||||
SafetyDepositBox,
|
SafetyDepositBox,
|
||||||
|
@ -16,7 +15,7 @@ import {
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
||||||
import { AccountLayout, MintLayout } from '@solana/spl-token';
|
import { AccountLayout, MintLayout } from '@solana/spl-token';
|
||||||
import { AuctionView, AuctionViewItem } from '../hooks';
|
import { AuctionView } from '../hooks';
|
||||||
import {
|
import {
|
||||||
WinningConfigType,
|
WinningConfigType,
|
||||||
redeemBid,
|
redeemBid,
|
||||||
|
@ -25,13 +24,14 @@ import {
|
||||||
BidRedemptionTicket,
|
BidRedemptionTicket,
|
||||||
getBidRedemption,
|
getBidRedemption,
|
||||||
PrizeTrackingTicket,
|
PrizeTrackingTicket,
|
||||||
} from '../models/metaplex';
|
AuctionViewItem,
|
||||||
|
} from '@oyster/common/dist/lib/models/metaplex/index';
|
||||||
|
import { createTokenAccount } from '@oyster/common/dist/lib/actions/account';
|
||||||
import {
|
import {
|
||||||
eligibleForParticipationPrizeGivenWinningIndex,
|
eligibleForParticipationPrizeGivenWinningIndex,
|
||||||
setupRedeemParticipationInstructions,
|
setupRedeemParticipationInstructions,
|
||||||
setupRedeemPrintingV2Instructions,
|
setupRedeemPrintingV2Instructions,
|
||||||
} from './sendRedeemBid';
|
} from './sendRedeemBid';
|
||||||
const { createTokenAccount } = actions;
|
|
||||||
|
|
||||||
export async function findEligibleParticipationBidsForRedemption(
|
export async function findEligibleParticipationBidsForRedemption(
|
||||||
auctionView: AuctionView,
|
auctionView: AuctionView,
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js';
|
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 { 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 { AccountLayout } from '@solana/spl-token';
|
||||||
import BN from 'bn.js';
|
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
|
// 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.)
|
// authority (that may or may not exist yet.)
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
} from '@solana/web3.js';
|
} from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
actions,
|
|
||||||
Metadata,
|
Metadata,
|
||||||
ParsedAccount,
|
ParsedAccount,
|
||||||
MasterEditionV1,
|
MasterEditionV1,
|
||||||
|
@ -41,7 +40,8 @@ import {
|
||||||
TupleNumericType,
|
TupleNumericType,
|
||||||
SafetyDepositConfig,
|
SafetyDepositConfig,
|
||||||
ParticipationStateV2,
|
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 { createVault } from './createVault';
|
||||||
import { closeVault } from './closeVault';
|
import { closeVault } from './closeVault';
|
||||||
import {
|
import {
|
||||||
|
@ -50,15 +50,13 @@ import {
|
||||||
} from './addTokensToVault';
|
} from './addTokensToVault';
|
||||||
import { makeAuction } from './makeAuction';
|
import { makeAuction } from './makeAuction';
|
||||||
import { createExternalPriceAccount } from './createExternalPriceAccount';
|
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 { deprecatedCreateReservationListForTokens } from './deprecatedCreateReservationListsForTokens';
|
||||||
import { deprecatedPopulatePrintingTokens } from './deprecatedPopulatePrintingTokens';
|
import { deprecatedPopulatePrintingTokens } from './deprecatedPopulatePrintingTokens';
|
||||||
import { setVaultAndAuctionAuthorities } from './setVaultAndAuctionAuthorities';
|
import { setVaultAndAuctionAuthorities } from './setVaultAndAuctionAuthorities';
|
||||||
import { markItemsThatArentMineAsSold } from './markItemsThatArentMineAsSold';
|
import { markItemsThatArentMineAsSold } from './markItemsThatArentMineAsSold';
|
||||||
import { validateSafetyDepositBoxV2 } from '../models/metaplex/validateSafetyDepositBoxV2';
|
import { validateSafetyDepositBoxV2 } from '@oyster/common/dist/lib/models/metaplex/validateSafetyDepositBoxV2';
|
||||||
import { initAuctionManagerV2 } from '../models/metaplex/initAuctionManagerV2';
|
import { initAuctionManagerV2 } from '@oyster/common/dist/lib/models/metaplex/initAuctionManagerV2';
|
||||||
|
|
||||||
const { createTokenAccount } = actions;
|
|
||||||
|
|
||||||
interface normalPattern {
|
interface normalPattern {
|
||||||
instructions: TransactionInstruction[];
|
instructions: TransactionInstruction[];
|
||||||
|
|
|
@ -4,22 +4,22 @@ import {
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from '@solana/web3.js';
|
} from '@solana/web3.js';
|
||||||
|
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
utils,
|
utils,
|
||||||
actions,
|
|
||||||
StringPublicKey,
|
StringPublicKey,
|
||||||
toPublicKey,
|
toPublicKey,
|
||||||
WalletSigner,
|
WalletSigner,
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
import {
|
||||||
import BN from 'bn.js';
|
|
||||||
import { QUOTE_MINT } from '../constants';
|
|
||||||
|
|
||||||
const {
|
|
||||||
updateExternalPriceAccount,
|
updateExternalPriceAccount,
|
||||||
ExternalPriceAccount,
|
ExternalPriceAccount,
|
||||||
MAX_EXTERNAL_ACCOUNT_SIZE,
|
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
|
// This command creates the external pricing oracle
|
||||||
export async function createExternalPriceAccount(
|
export async function createExternalPriceAccount(
|
||||||
|
|
|
@ -6,19 +6,22 @@ import {
|
||||||
} from '@solana/web3.js';
|
} from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
utils,
|
utils,
|
||||||
actions,
|
|
||||||
createMint,
|
createMint,
|
||||||
findProgramAddress,
|
findProgramAddress,
|
||||||
StringPublicKey,
|
StringPublicKey,
|
||||||
toPublicKey,
|
toPublicKey,
|
||||||
WalletSigner,
|
WalletSigner,
|
||||||
} from '@oyster/common';
|
} 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 { AccountLayout, MintLayout } from '@solana/spl-token';
|
||||||
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
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 command creates the external pricing oracle a vault
|
||||||
// This gets the vault ready for adding the tokens.
|
// This gets the vault ready for adding the tokens.
|
||||||
export async function createVault(
|
export async function createVault(
|
||||||
|
|
|
@ -9,8 +9,8 @@ import {
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
||||||
import { AuctionView } from '../hooks';
|
import { AuctionView } from '../hooks';
|
||||||
import { AuctionManagerStatus } from '../models/metaplex';
|
import { AuctionManagerStatus } from '@oyster/common/dist/lib/models/metaplex/index';
|
||||||
import { decommissionAuctionManager } from '../models/metaplex/decommissionAuctionManager';
|
import { decommissionAuctionManager } from '@oyster/common/dist/lib/models/metaplex/decommissionAuctionManager';
|
||||||
import { unwindVault } from './unwindVault';
|
import { unwindVault } from './unwindVault';
|
||||||
|
|
||||||
export async function decommAuctionManagerAndReturnPrizes(
|
export async function decommAuctionManagerAndReturnPrizes(
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
||||||
import { SafetyDepositInstructionTemplate } from './addTokensToVault';
|
import { SafetyDepositInstructionTemplate } from './addTokensToVault';
|
||||||
import { WinningConfigType } from '../models/metaplex';
|
import { WinningConfigType } from '@oyster/common/dist/lib/models/metaplex/index';
|
||||||
|
|
||||||
const BATCH_SIZE = 10;
|
const BATCH_SIZE = 10;
|
||||||
// This command batches out creating reservation lists for those tokens who are being sold in PrintingV1 mode.
|
// This command batches out creating reservation lists for those tokens who are being sold in PrintingV1 mode.
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Keypair, TransactionInstruction } from '@solana/web3.js';
|
import { Keypair, TransactionInstruction } from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
utils,
|
utils,
|
||||||
actions,
|
|
||||||
findProgramAddress,
|
findProgramAddress,
|
||||||
IPartialCreateAuctionArgs,
|
IPartialCreateAuctionArgs,
|
||||||
CreateAuctionArgs,
|
CreateAuctionArgs,
|
||||||
|
@ -9,10 +8,12 @@ import {
|
||||||
toPublicKey,
|
toPublicKey,
|
||||||
WalletSigner,
|
WalletSigner,
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
|
import {
|
||||||
|
AUCTION_PREFIX,
|
||||||
|
createAuction,
|
||||||
|
} from '@oyster/common/dist/lib/actions/auction';
|
||||||
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
||||||
|
|
||||||
const { AUCTION_PREFIX, createAuction } = actions;
|
|
||||||
|
|
||||||
// This command makes an auction
|
// This command makes an auction
|
||||||
export async function makeAuction(
|
export async function makeAuction(
|
||||||
wallet: WalletSigner,
|
wallet: WalletSigner,
|
||||||
|
|
|
@ -5,10 +5,10 @@ import {
|
||||||
sendTransactionWithRetry,
|
sendTransactionWithRetry,
|
||||||
WalletSigner,
|
WalletSigner,
|
||||||
} from '@oyster/common';
|
} 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 { 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
|
// 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
|
// but given how little this should be used keep it simple
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js';
|
import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
actions,
|
|
||||||
sendTransactionWithRetry,
|
sendTransactionWithRetry,
|
||||||
placeBid,
|
placeBid,
|
||||||
models,
|
|
||||||
cache,
|
cache,
|
||||||
TokenAccount,
|
|
||||||
ensureWrappedAccount,
|
ensureWrappedAccount,
|
||||||
toLamports,
|
toLamports,
|
||||||
ParsedAccount,
|
ParsedAccount,
|
||||||
|
@ -13,15 +10,16 @@ import {
|
||||||
WalletSigner,
|
WalletSigner,
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
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 { AccountLayout, MintInfo } from '@solana/spl-token';
|
||||||
import { AuctionView } from '../hooks';
|
import { AuctionView } from '../hooks';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { setupCancelBid } from './cancelBid';
|
import { setupCancelBid } from './cancelBid';
|
||||||
import { QUOTE_MINT } from '../constants';
|
import { QUOTE_MINT } from '../constants';
|
||||||
|
|
||||||
const { createTokenAccount } = actions;
|
|
||||||
const { approve } = models;
|
|
||||||
|
|
||||||
export async function sendPlaceBid(
|
export async function sendPlaceBid(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
wallet: WalletSigner,
|
wallet: WalletSigner,
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js';
|
import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
actions,
|
|
||||||
ParsedAccount,
|
ParsedAccount,
|
||||||
programIds,
|
programIds,
|
||||||
models,
|
|
||||||
TokenAccount,
|
TokenAccount,
|
||||||
createMint,
|
createMint,
|
||||||
SafetyDepositBox,
|
SafetyDepositBox,
|
||||||
|
@ -28,8 +26,10 @@ import {
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
||||||
import { AccountLayout, MintLayout, Token } from '@solana/spl-token';
|
import { AccountLayout, MintLayout, Token } from '@solana/spl-token';
|
||||||
import { AuctionView, AuctionViewItem } from '../hooks';
|
import { AuctionView } from '../hooks';
|
||||||
import {
|
import {
|
||||||
|
AuctionManagerV1,
|
||||||
|
ParticipationStateV1,
|
||||||
WinningConfigType,
|
WinningConfigType,
|
||||||
NonWinningConstraint,
|
NonWinningConstraint,
|
||||||
redeemBid,
|
redeemBid,
|
||||||
|
@ -41,21 +41,18 @@ import {
|
||||||
PrizeTrackingTicket,
|
PrizeTrackingTicket,
|
||||||
getPrizeTrackingTicket,
|
getPrizeTrackingTicket,
|
||||||
BidRedemptionTicket,
|
BidRedemptionTicket,
|
||||||
} from '../models/metaplex';
|
AuctionViewItem,
|
||||||
import { claimBid } from '../models/metaplex/claimBid';
|
} 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 { setupCancelBid } from './cancelBid';
|
||||||
import { deprecatedPopulateParticipationPrintingAccount } from '../models/metaplex/deprecatedPopulateParticipationPrintingAccount';
|
import { deprecatedPopulateParticipationPrintingAccount } from '@oyster/common/dist/lib/models/metaplex/deprecatedPopulateParticipationPrintingAccount';
|
||||||
import { setupPlaceBid } from './sendPlaceBid';
|
import { setupPlaceBid } from './sendPlaceBid';
|
||||||
import { claimUnusedPrizes } from './claimUnusedPrizes';
|
import { claimUnusedPrizes } from './claimUnusedPrizes';
|
||||||
import { createMintAndAccountWithOne } from './createMintAndAccountWithOne';
|
import { createMintAndAccountWithOne } from './createMintAndAccountWithOne';
|
||||||
import { BN } from 'bn.js';
|
import { BN } from 'bn.js';
|
||||||
import { QUOTE_MINT } from '../constants';
|
import { QUOTE_MINT } from '../constants';
|
||||||
import {
|
|
||||||
AuctionManagerV1,
|
|
||||||
ParticipationStateV1,
|
|
||||||
} from '../models/metaplex/deprecatedStates';
|
|
||||||
const { createTokenAccount } = actions;
|
|
||||||
const { approve } = models;
|
|
||||||
|
|
||||||
export function eligibleForParticipationPrizeGivenWinningIndex(
|
export function eligibleForParticipationPrizeGivenWinningIndex(
|
||||||
winnerIndex: number | null,
|
winnerIndex: number | null,
|
||||||
|
|
|
@ -16,8 +16,8 @@ import {
|
||||||
|
|
||||||
import { AuctionView } from '../hooks';
|
import { AuctionView } from '../hooks';
|
||||||
|
|
||||||
import { claimBid } from '../models/metaplex/claimBid';
|
import { claimBid } from '@oyster/common/dist/lib/models/metaplex/claimBid';
|
||||||
import { emptyPaymentAccount } from '../models/metaplex/emptyPaymentAccount';
|
import { emptyPaymentAccount } from '@oyster/common/dist/lib/models/metaplex/emptyPaymentAccount';
|
||||||
import { QUOTE_MINT } from '../constants';
|
import { QUOTE_MINT } from '../constants';
|
||||||
import { setupPlaceBid } from './sendPlaceBid';
|
import { setupPlaceBid } from './sendPlaceBid';
|
||||||
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Connection, Keypair, TransactionInstruction } from '@solana/web3.js';
|
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 { notify, sendTransactionWithRetry, WalletSigner } from '@oyster/common';
|
||||||
import { AuctionView } from '../hooks';
|
import { AuctionView } from '../hooks';
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import React, { Ref, useCallback, useEffect, useState } from 'react';
|
import React, { Ref, useCallback, useEffect, useState } from 'react';
|
||||||
import { Image } from 'antd';
|
import { Image } from 'antd';
|
||||||
import { MetadataCategory, MetadataFile } from '@oyster/common';
|
import { MetadataCategory, MetadataFile, pubkeyToString } from '@oyster/common';
|
||||||
import { MeshViewer } from '../MeshViewer';
|
import { MeshViewer } from '../MeshViewer';
|
||||||
import { ThreeDots } from '../MyLoader';
|
import { ThreeDots } from '../MyLoader';
|
||||||
import { useCachedImage, useExtendedArt } from '../../hooks';
|
import { useCachedImage, useExtendedArt } from '../../hooks';
|
||||||
import { Stream, StreamPlayerApi } from '@cloudflare/stream-react';
|
import { Stream, StreamPlayerApi } from '@cloudflare/stream-react';
|
||||||
import { PublicKey } from '@solana/web3.js';
|
import { PublicKey } from '@solana/web3.js';
|
||||||
import { getLast } from '../../utils/utils';
|
import { getLast } from '../../utils/utils';
|
||||||
import { pubkeyToString } from '../../utils/pubkeyToString';
|
|
||||||
|
|
||||||
const MeshArtContent = ({
|
const MeshArtContent = ({
|
||||||
uri,
|
uri,
|
||||||
|
|
|
@ -43,7 +43,7 @@ import { findEligibleParticipationBidsForRedemption } from '../../actions/claimU
|
||||||
import {
|
import {
|
||||||
BidRedemptionTicket,
|
BidRedemptionTicket,
|
||||||
MAX_PRIZE_TRACKING_TICKET_SIZE,
|
MAX_PRIZE_TRACKING_TICKET_SIZE,
|
||||||
} from '../../models/metaplex';
|
} from '@oyster/common/dist/lib/models/metaplex/index';
|
||||||
|
|
||||||
async function calculateTotalCostOfRedeemingOtherPeoplesBids(
|
async function calculateTotalCostOfRedeemingOtherPeoplesBids(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
export * from './meta';
|
export * from '@oyster/common/dist/lib/contexts/meta/meta';
|
||||||
export * from './coingecko';
|
export * from './coingecko';
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export * from './meta';
|
|
|
@ -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<MetaContextState>({
|
|
||||||
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<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 [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<UpdateStateValueFunc>(
|
|
||||||
() => (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 (
|
|
||||||
<MetaContext.Provider
|
|
||||||
value={{
|
|
||||||
...state,
|
|
||||||
isLoading,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</MetaContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useMeta = () => {
|
|
||||||
const context = useContext(MetaContext);
|
|
||||||
return context;
|
|
||||||
};
|
|
|
@ -9,11 +9,12 @@ import {
|
||||||
Metadata,
|
Metadata,
|
||||||
ParsedAccount,
|
ParsedAccount,
|
||||||
StringPublicKey,
|
StringPublicKey,
|
||||||
|
useLocalStorage,
|
||||||
|
pubkeyToString,
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { WhitelistedCreator } from '../models/metaplex';
|
import { WhitelistedCreator } from '@oyster/common/dist/lib/models/metaplex/index';
|
||||||
import { Cache } from 'three';
|
import { Cache } from 'three';
|
||||||
import { useInView } from 'react-intersection-observer';
|
import { useInView } from 'react-intersection-observer';
|
||||||
import { pubkeyToString } from '../utils/pubkeyToString';
|
|
||||||
|
|
||||||
const metadataToArt = (
|
const metadataToArt = (
|
||||||
info: Metadata | undefined,
|
info: Metadata | undefined,
|
||||||
|
@ -159,6 +160,7 @@ export const useExtendedArt = (id?: StringPublicKey) => {
|
||||||
|
|
||||||
const [data, setData] = useState<IMetadataExtension>();
|
const [data, setData] = useState<IMetadataExtension>();
|
||||||
const { ref, inView } = useInView();
|
const { ref, inView } = useInView();
|
||||||
|
const localStorage = useLocalStorage();
|
||||||
|
|
||||||
const key = pubkeyToString(id);
|
const key = pubkeyToString(id);
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,15 @@ import { useMeta } from '../contexts';
|
||||||
import {
|
import {
|
||||||
AuctionManager,
|
AuctionManager,
|
||||||
AuctionManagerStatus,
|
AuctionManagerStatus,
|
||||||
|
AuctionManagerV1,
|
||||||
AuctionManagerV2,
|
AuctionManagerV2,
|
||||||
BidRedemptionTicket,
|
BidRedemptionTicket,
|
||||||
BidRedemptionTicketV2,
|
BidRedemptionTicketV2,
|
||||||
getBidderKeys,
|
getBidderKeys,
|
||||||
SafetyDepositConfig,
|
SafetyDepositConfig,
|
||||||
WinningConfigType,
|
WinningConfigType,
|
||||||
} from '../models/metaplex';
|
AuctionViewItem,
|
||||||
import { AuctionManagerV1 } from '../models/metaplex/deprecatedStates';
|
} from '@oyster/common/dist/lib/models/metaplex/index';
|
||||||
|
|
||||||
export enum AuctionViewState {
|
export enum AuctionViewState {
|
||||||
Live = '0',
|
Live = '0',
|
||||||
|
@ -35,14 +36,6 @@ export enum AuctionViewState {
|
||||||
Defective = '-1',
|
Defective = '-1',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuctionViewItem {
|
|
||||||
winningConfigType: WinningConfigType;
|
|
||||||
amount: BN;
|
|
||||||
metadata: ParsedAccount<Metadata>;
|
|
||||||
safetyDeposit: ParsedAccount<SafetyDepositBox>;
|
|
||||||
masterEdition?: ParsedAccount<MasterEditionV1 | MasterEditionV2>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flattened surface item for easy display
|
// Flattened surface item for easy display
|
||||||
export interface AuctionView {
|
export interface AuctionView {
|
||||||
// items 1:1 with winning configs FOR NOW
|
// items 1:1 with winning configs FOR NOW
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { StringPublicKey } from '@oyster/common';
|
import { StringPublicKey, pubkeyToString } from '@oyster/common';
|
||||||
import { useMeta } from '../contexts';
|
import { useMeta } from '../contexts';
|
||||||
import { pubkeyToString } from '../utils/pubkeyToString';
|
|
||||||
|
|
||||||
export const useCreator = (id?: StringPublicKey) => {
|
export const useCreator = (id?: StringPublicKey) => {
|
||||||
const { whitelistedCreatorsByCreator } = useMeta();
|
const { whitelistedCreatorsByCreator } = useMeta();
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
ParticipationConfigV2,
|
ParticipationConfigV2,
|
||||||
WinningConfigType,
|
WinningConfigType,
|
||||||
WinningConstraint,
|
WinningConstraint,
|
||||||
} from '../models/metaplex';
|
} from '@oyster/common/dist/lib/models/metaplex/index';
|
||||||
import { useMeta } from './../contexts';
|
import { useMeta } from './../contexts';
|
||||||
|
|
||||||
export const useUserArts = (): SafetyDepositDraft[] => {
|
export const useUserArts = (): SafetyDepositDraft[] => {
|
||||||
|
|
|
@ -3,11 +3,11 @@ import {
|
||||||
ConnectionProvider,
|
ConnectionProvider,
|
||||||
StoreProvider,
|
StoreProvider,
|
||||||
WalletProvider,
|
WalletProvider,
|
||||||
|
MetaProvider,
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { ConfettiProvider } from './components/Confetti';
|
import { ConfettiProvider } from './components/Confetti';
|
||||||
import { AppLayout } from './components/Layout';
|
import { AppLayout } from './components/Layout';
|
||||||
import { MetaProvider } from './contexts/meta';
|
|
||||||
import { CoingeckoProvider } from './contexts/coingecko';
|
import { CoingeckoProvider } from './contexts/coingecko';
|
||||||
|
|
||||||
export const Providers: FC = ({ children }) => {
|
export const Providers: FC = ({ children }) => {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useLocalStorage } from '@oyster/common';
|
||||||
import { TokenInfo } from '@solana/spl-token-registry';
|
import { TokenInfo } from '@solana/spl-token-registry';
|
||||||
|
|
||||||
export const LAMPORT_MULTIPLIER = 10 ** 9;
|
export const LAMPORT_MULTIPLIER = 10 ** 9;
|
||||||
|
@ -8,6 +9,7 @@ export const filterModalSolTokens = (tokens: TokenInfo[]) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getAssetCostToStore(files: File[]) {
|
export async function getAssetCostToStore(files: File[]) {
|
||||||
|
const localStorage = useLocalStorage();
|
||||||
const totalBytes = files.reduce((sum, f) => (sum += f.size), 0);
|
const totalBytes = files.reduce((sum, f) => (sum += f.size), 0);
|
||||||
console.log('Total bytes', totalBytes);
|
console.log('Total bytes', totalBytes);
|
||||||
const txnFeeInWinstons = parseInt(
|
const txnFeeInWinstons = parseInt(
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import { PublicKey } from '@solana/web3.js';
|
|
||||||
|
|
||||||
export const pubkeyToString = (key: PublicKey | string = '') => {
|
|
||||||
return typeof key === 'string' ? key : key?.toBase58() || '';
|
|
||||||
};
|
|
|
@ -12,7 +12,10 @@ import {
|
||||||
Divider,
|
Divider,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { useMeta } from '../../contexts';
|
import { useMeta } from '../../contexts';
|
||||||
import { Store, WhitelistedCreator } from '../../models/metaplex';
|
import {
|
||||||
|
Store,
|
||||||
|
WhitelistedCreator,
|
||||||
|
} from '@oyster/common/dist/lib/models/metaplex/index';
|
||||||
import {
|
import {
|
||||||
MasterEditionV1,
|
MasterEditionV1,
|
||||||
notify,
|
notify,
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import React, { Dispatch, SetStateAction, useState } from 'react';
|
import React, { Dispatch, SetStateAction, useState } from 'react';
|
||||||
import { Layout, Button, Col, Spin } from 'antd';
|
import { Layout, Button, Col, Spin } from 'antd';
|
||||||
import { useMeta } from '../../contexts';
|
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 { Pie, Bar } from 'react-chartjs-2';
|
||||||
import {
|
import {
|
||||||
AuctionDataExtended,
|
AuctionDataExtended,
|
||||||
|
@ -16,7 +20,6 @@ import {
|
||||||
import { AuctionView, useAuctions } from '../../hooks';
|
import { AuctionView, useAuctions } from '../../hooks';
|
||||||
import { QUOTE_MINT } from '../../constants';
|
import { QUOTE_MINT } from '../../constants';
|
||||||
import { MintInfo } from '@solana/spl-token';
|
import { MintInfo } from '@solana/spl-token';
|
||||||
import { AuctionManagerV1 } from '../../models/metaplex/deprecatedStates';
|
|
||||||
|
|
||||||
const { Content } = Layout;
|
const { Content } = Layout;
|
||||||
export const AnalyticsView = () => {
|
export const AnalyticsView = () => {
|
||||||
|
|
|
@ -33,7 +33,7 @@ import {
|
||||||
NonWinningConstraint,
|
NonWinningConstraint,
|
||||||
PayoutTicket,
|
PayoutTicket,
|
||||||
WinningConstraint,
|
WinningConstraint,
|
||||||
} from '../../models/metaplex';
|
} from '@oyster/common/dist/lib/models/metaplex/index';
|
||||||
import { Connection } from '@solana/web3.js';
|
import { Connection } from '@solana/web3.js';
|
||||||
import { settle } from '../../actions/settle';
|
import { settle } from '../../actions/settle';
|
||||||
import { MintInfo } from '@solana/spl-token';
|
import { MintInfo } from '@solana/spl-token';
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { useParams } from 'react-router-dom';
|
||||||
import { Row, Col, Button, Skeleton, Carousel, List, Card } from 'antd';
|
import { Row, Col, Button, Skeleton, Carousel, List, Card } from 'antd';
|
||||||
import { AuctionCard } from '../../components/AuctionCard';
|
import { AuctionCard } from '../../components/AuctionCard';
|
||||||
import { Connection } from '@solana/web3.js';
|
import { Connection } from '@solana/web3.js';
|
||||||
|
import { AuctionViewItem } from '@oyster/common/dist/lib/models/metaplex/index';
|
||||||
import {
|
import {
|
||||||
AuctionView as Auction,
|
AuctionView as Auction,
|
||||||
AuctionViewItem,
|
|
||||||
useArt,
|
useArt,
|
||||||
useAuction,
|
useAuction,
|
||||||
useBidsForAuction,
|
useBidsForAuction,
|
||||||
|
|
|
@ -37,7 +37,10 @@ import { useWallet } from '@solana/wallet-adapter-react';
|
||||||
import { MintLayout } from '@solana/spl-token';
|
import { MintLayout } from '@solana/spl-token';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { capitalize } from 'lodash';
|
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 moment from 'moment';
|
||||||
import {
|
import {
|
||||||
createAuctionManager,
|
createAuctionManager,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {
|
||||||
useConnection,
|
useConnection,
|
||||||
useStore,
|
useStore,
|
||||||
useWalletModal,
|
useWalletModal,
|
||||||
WalletSigner,
|
WhitelistedCreator,
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { useWallet } from '@solana/wallet-adapter-react';
|
import { useWallet } from '@solana/wallet-adapter-react';
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
|
@ -10,9 +10,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { saveAdmin } from '../../actions/saveAdmin';
|
import { saveAdmin } from '../../actions/saveAdmin';
|
||||||
import { useMeta } from '../../contexts';
|
import { useMeta } from '../../contexts';
|
||||||
import { WhitelistedCreator } from '../../models/metaplex';
|
|
||||||
import { SetupVariables } from '../../components/SetupVariables';
|
import { SetupVariables } from '../../components/SetupVariables';
|
||||||
import { WalletAdapter } from '@solana/wallet-adapter-base';
|
|
||||||
|
|
||||||
export const SetupView = () => {
|
export const SetupView = () => {
|
||||||
const [isInitalizingStore, setIsInitalizingStore] = useState(false);
|
const [isInitalizingStore, setIsInitalizingStore] = useState(false);
|
||||||
|
|
Loading…
Reference in New Issue