Holy cow, now we have editions too
This commit is contained in:
parent
e7abac452f
commit
ad23dbfb4f
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -593,6 +593,16 @@ pub fn process_puff_metadata_account(
|
||||||
|
|
||||||
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(())
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue