Holy cow, now we have editions too

This commit is contained in:
Jordan Prince 2021-08-11 17:31:10 -05:00
parent e7abac452f
commit ad23dbfb4f
7 changed files with 142 additions and 34 deletions

View File

@ -213,6 +213,7 @@ export class Metadata {
data: Data; data: Data;
primarySaleHappened: boolean; primarySaleHappened: boolean;
isMutable: boolean; isMutable: boolean;
editionNonce: number | null;
// set lazy // set lazy
masterEdition?: PublicKey; masterEdition?: PublicKey;
@ -224,6 +225,7 @@ export class Metadata {
data: Data; data: Data;
primarySaleHappened: boolean; primarySaleHappened: boolean;
isMutable: boolean; isMutable: boolean;
editionNonce: number | null;
}) { }) {
this.key = MetadataKey.MetadataV1; this.key = MetadataKey.MetadataV1;
this.updateAuthority = args.updateAuthority; this.updateAuthority = args.updateAuthority;
@ -231,6 +233,7 @@ export class Metadata {
this.data = args.data; this.data = args.data;
this.primarySaleHappened = args.primarySaleHappened; this.primarySaleHappened = args.primarySaleHappened;
this.isMutable = args.isMutable; this.isMutable = args.isMutable;
this.editionNonce = args.editionNonce;
} }
public async init() { public async init() {

View File

@ -4,7 +4,7 @@ import {
METAPLEX_ID, METAPLEX_ID,
VAULT_ID, VAULT_ID,
} from '@oyster/common/dist/lib/utils/ids'; } from '@oyster/common/dist/lib/utils/ids';
import { Connection } from '@solana/web3.js'; import { 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';
import { processAuctions } from './processAuctions'; import { processAuctions } from './processAuctions';
@ -12,12 +12,16 @@ import { processMetaplexAccounts } from './processMetaplexAccounts';
import { processMetaData } from './processMetaData'; import { processMetaData } from './processMetaData';
import { processVaultData } from './processVaultData'; import { processVaultData } from './processVaultData';
import { import {
findProgramAddress,
getEdition,
getMultipleAccounts,
MAX_CREATOR_LEN, MAX_CREATOR_LEN,
MAX_CREATOR_LIMIT, MAX_CREATOR_LIMIT,
MAX_NAME_LENGTH, MAX_NAME_LENGTH,
MAX_SYMBOL_LENGTH, MAX_SYMBOL_LENGTH,
MAX_URI_LENGTH, MAX_URI_LENGTH,
Metadata, Metadata,
METADATA_PREFIX,
ParsedAccount, ParsedAccount,
} from '../../../../common/dist/lib'; } from '../../../../common/dist/lib';
import { import {
@ -83,35 +87,46 @@ export const loadAccounts = async (connection: Connection, all: boolean) => {
tempCache.whitelistedCreatorsByCreator, tempCache.whitelistedCreatorsByCreator,
); );
for (let i = 0; i < MAX_CREATOR_LIMIT; i++) { if (whitelistedCreators.length > 3) {
for (let j = 0; j < whitelistedCreators.length; j++) { console.log(' too many creators, pulling all nfts in one go');
additionalPromises.push( additionalPromises.push(
connection connection
.getProgramAccounts(METADATA_PROGRAM_ID, { .getProgramAccounts(METADATA_PROGRAM_ID)
filters: [ .then(forEach(processMetaData)),
{ );
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 connection
4 + // uri string length .getProgramAccounts(METADATA_PROGRAM_ID, {
MAX_URI_LENGTH + // uri filters: [
4 + // symbol string length {
MAX_SYMBOL_LENGTH + // symbol memcmp: {
2 + // seller fee basis points offset:
1 + // whether or not there is a creators vec 1 + // key
4 + // creators vec length 32 + // update auth
i * MAX_CREATOR_LEN, 32 + // mint
bytes: whitelistedCreators[j].info.address.toBase58(), 4 + // name string length
MAX_NAME_LENGTH + // name
4 + // uri string length
MAX_URI_LENGTH + // uri
4 + // symbol string length
MAX_SYMBOL_LENGTH + // symbol
2 + // seller fee basis points
1 + // whether or not there is a creators vec
4 + // creators vec length
i * MAX_CREATOR_LEN,
bytes: whitelistedCreators[j].info.address.toBase58(),
},
}, },
}, ],
], })
}) .then(forEach(processMetaData)),
.then(forEach(processMetaData)), );
); }
} }
} }
}), }),
@ -122,6 +137,63 @@ export const loadAccounts = async (connection: Connection, all: boolean) => {
await postProcessMetadata(tempCache, all); await postProcessMetadata(tempCache, all);
console.log('Metadata size', tempCache.metadata.length); console.log('Metadata size', tempCache.metadata.length);
if (additionalPromises.length > 0) {
console.log('Pulling editions for optimized metadata');
let setOf100MetadataEditionKeys: string[] = [];
const editionPromises = [];
for (let i = 0; i < tempCache.metadata.length; i++) {
let edition: PublicKey;
if (tempCache.metadata[i].info.editionNonce != null) {
edition = await PublicKey.createProgramAddress(
[
Buffer.from(METADATA_PREFIX),
METADATA_PROGRAM_ID.toBuffer(),
tempCache.metadata[i].info.mint.toBuffer(),
new Uint8Array([tempCache.metadata[i].info.editionNonce || 0]),
],
METADATA_PROGRAM_ID,
);
} else {
edition = await getEdition(tempCache.metadata[i].info.mint);
}
setOf100MetadataEditionKeys.push(edition.toBase58());
if (setOf100MetadataEditionKeys.length >= 100) {
console.log('push');
editionPromises.push(
getMultipleAccounts(
connection,
setOf100MetadataEditionKeys,
'recent',
),
);
setOf100MetadataEditionKeys = [];
}
}
const responses = await Promise.all(editionPromises);
for (let i = 0; i < responses.length; i++) {
const returnedAccounts = responses[i];
for (let j = 0; j < returnedAccounts.array.length; j++) {
processMetaData(
{
pubkey: new PublicKey(returnedAccounts.keys[j]),
account: returnedAccounts.array[j],
},
updateTemp,
all,
);
}
}
console.log(
'Edition size',
Object.keys(tempCache.editions).length,
Object.keys(tempCache.masterEditions).length,
);
}
return tempCache; return tempCache;
}; };

View File

@ -84,8 +84,12 @@ export const processMetaData: ProcessAccountsFunc = (
} }
}; };
const isMetadataAccount = (account: AccountInfo<Buffer>) => const isMetadataAccount = (account: AccountInfo<Buffer>) => {
account.owner.equals(METADATA_PROGRAM_ID); return account.owner.equals
? account.owner.equals(METADATA_PROGRAM_ID)
: //@ts-ignore
account.owner === METADATA_PROGRAM_ID.toBase58();
};
const isMetadataV1Account = (account: AccountInfo<Buffer>) => const isMetadataV1Account = (account: AccountInfo<Buffer>) =>
account.data[0] === MetadataKey.MetadataV1; account.data[0] === MetadataKey.MetadataV1;

View File

@ -592,6 +592,16 @@ pub fn process_puff_metadata_account(
assert_owned_by(metadata_account_info, program_id)?; assert_owned_by(metadata_account_info, program_id)?;
puff_out_data_fields(&mut metadata); puff_out_data_fields(&mut metadata);
let edition_seeds = &[
PREFIX.as_bytes(),
program_id.as_ref(),
metadata.mint.as_ref(),
EDITION.as_bytes()
];
let (_, edition_bump_seed) =
Pubkey::find_program_address(edition_seeds, program_id);
metadata.edition_nonce = Some(edition_bump_seed);
metadata.serialize(&mut *metadata_account_info.data.borrow_mut())?; metadata.serialize(&mut *metadata_account_info.data.borrow_mut())?;
Ok(()) Ok(())

View File

@ -23,14 +23,20 @@ pub const MAX_URI_LENGTH: usize = 200;
pub const MAX_METADATA_LEN: usize = 1 pub const MAX_METADATA_LEN: usize = 1
+ 32 + 32
+ 32 + 32
+ 4
+ MAX_NAME_LENGTH + MAX_NAME_LENGTH
+ 4
+ MAX_SYMBOL_LENGTH + MAX_SYMBOL_LENGTH
+ 4
+ MAX_URI_LENGTH + MAX_URI_LENGTH
+ MAX_CREATOR_LIMIT * MAX_CREATOR_LEN
+ 2 + 2
+ 1 + 1
+ 4
+ MAX_CREATOR_LIMIT * MAX_CREATOR_LEN
+ 1 + 1
+ 198; + 1
+ 9
+ 172;
pub const MAX_EDITION_LEN: usize = 1 + 32 + 8 + 200; pub const MAX_EDITION_LEN: usize = 1 + 32 + 8 + 200;
@ -93,6 +99,8 @@ pub struct Metadata {
pub primary_sale_happened: bool, pub primary_sale_happened: bool,
// Whether or not the data struct is mutable, default is not // Whether or not the data struct is mutable, default is not
pub is_mutable: bool, pub is_mutable: bool,
/// nonce for easy calculation of editions, if present
pub edition_nonce: Option<u8>
} }
impl Metadata { impl Metadata {

View File

@ -851,6 +851,17 @@ pub fn process_create_metadata_accounts_logic(
metadata.update_authority = *update_authority_info.key; metadata.update_authority = *update_authority_info.key;
puff_out_data_fields(&mut metadata); puff_out_data_fields(&mut metadata);
let edition_seeds = &[
PREFIX.as_bytes(),
program_id.as_ref(),
metadata.mint.as_ref(),
EDITION.as_bytes()
];
let (_, edition_bump_seed) =
Pubkey::find_program_address(edition_seeds, program_id);
metadata.edition_nonce = Some(edition_bump_seed);
metadata.serialize(&mut *metadata_account_info.data.borrow_mut())?; metadata.serialize(&mut *metadata_account_info.data.borrow_mut())?;
Ok(()) Ok(())

View File

@ -40,7 +40,7 @@ fn puff_unpuffed_metadata(_app_matches: &ArgMatches, payer: Keypair, client: Rpc
for acct in metadata_accounts { for acct in metadata_accounts {
if acct.1.data[0] == Key::MetadataV1 as u8 { if acct.1.data[0] == Key::MetadataV1 as u8 {
let account: Metadata = try_from_slice_unchecked(&acct.1.data).unwrap(); let account: Metadata = try_from_slice_unchecked(&acct.1.data).unwrap();
if account.data.name.len() < MAX_NAME_LENGTH || account.data.uri.len() < MAX_URI_LENGTH || account.data.symbol.len() < MAX_SYMBOL_LENGTH { if account.data.name.len() < MAX_NAME_LENGTH || account.data.uri.len() < MAX_URI_LENGTH || account.data.symbol.len() < MAX_SYMBOL_LENGTH || account.edition_nonce.is_none() {
needing_puffing.push(acct.0); needing_puffing.push(acct.0);
} }
} }