Oh yeaj, it works and it's fast as sh*t...just get the metadata oyu ned

This commit is contained in:
Jordan Prince 2021-08-10 19:52:21 -05:00
parent bc97c0c678
commit 9170223fb0
5 changed files with 153 additions and 37 deletions

View File

@ -14,6 +14,9 @@ import { processVaultData } from './processVaultData';
import {
MAX_CREATOR_LEN,
MAX_CREATOR_LIMIT,
MAX_NAME_LENGTH,
MAX_SYMBOL_LENGTH,
MAX_URI_LENGTH,
Metadata,
ParsedAccount,
} from '../../../../common/dist/lib';
@ -56,22 +59,6 @@ export const loadAccounts = async (connection: Connection, all: boolean) => {
}
};
await connection
.getProgramAccounts(METAPLEX_ID, {
filters: [
{
dataSize: MAX_WHITELISTED_CREATOR_SIZE,
},
{
memcmp: {
offset: 0,
bytes: MetaplexKey.WhitelistedCreatorV1.toString(),
},
},
],
})
.then(forEach(processMetaplexAccounts));
const promises = [
connection.getProgramAccounts(VAULT_ID).then(forEach(processVaultData)),
connection.getProgramAccounts(AUCTION_ID).then(forEach(processAuctions)),
@ -79,27 +66,58 @@ export const loadAccounts = async (connection: Connection, all: boolean) => {
connection
.getProgramAccounts(METAPLEX_ID)
.then(forEach(processMetaplexAccounts)),
];
for (let i = 0; i < MAX_CREATOR_LIMIT; i++) {
promises.push(
connection
.getProgramAccounts(METADATA_PROGRAM_ID, {
filters: [
{
memcmp: {
offset: 0,
bytes: MetaplexKey.WhitelistedCreatorV1.toString(),
},
},
],
})
.then(forEach(processMetaData)),
);
}
connection
.getProgramAccounts(METAPLEX_ID, {
filters: [
{
dataSize: MAX_WHITELISTED_CREATOR_SIZE,
},
],
})
.then(async creators => {
await forEach(processMetaplexAccounts)(creators);
const whitelistedCreators = Object.values(
tempCache.whitelistedCreatorsByCreator,
);
for (let i = 0; i < MAX_CREATOR_LIMIT; i++) {
for (let j = 0; j < whitelistedCreators.length; j++) {
promises.push(
connection
.getProgramAccounts(METADATA_PROGRAM_ID, {
filters: [
{
memcmp: {
offset:
1 + // key
32 + // update auth
32 + // mint
4 + // name string length
MAX_NAME_LENGTH + // name
4 + // uri string length
MAX_URI_LENGTH + // uri
4 + // symbol string length
MAX_SYMBOL_LENGTH + // symbol
2 + // seller fee basis points
1 + // whether or not there is a creators vec
4 + // creators vec length
i * MAX_CREATOR_LEN,
bytes: whitelistedCreators[j].info.address.toBase58(),
},
},
],
})
.then(forEach(processMetaData)),
);
}
}
}),
];
await Promise.all(promises);
await postProcessMetadata(tempCache, all);
console.log('Metadata size', tempCache.metadata.length);
return tempCache;
};

View File

@ -131,7 +131,6 @@ export const processMetaplexAccounts: ProcessAccountsFunc = async (
if (isWhitelistedCreatorV1Account(account)) {
const whitelistedCreator = decodeWhitelistedCreator(account.data);
// TODO: figure out a way to avoid generating creator addresses during parsing
// should we store store id inside creator?
const creatorKeyIfCreatorWasPartOfThisStore = await getWhitelistedCreator(

View File

@ -238,6 +238,11 @@ pub enum MetadataInstruction {
/// 15. `[]` System program
/// 16. `[]` Rent info
MintNewEditionFromMasterEditionViaVaultProxy(MintNewEditionFromMasterEditionViaTokenArgs),
/// Puff a Metadata - make all of it's variable length fields (name/uri/symbol) a fixed length using a null character
/// so that it can be found using offset searches by the RPC to make client lookups cheaper.
/// 0. `[writable]` Metadata account
PuffMetadata,
}
/// Creates an CreateMetadataAccounts instruction
@ -308,6 +313,22 @@ pub fn update_metadata_accounts(
}
}
/// puff metadata account instruction
pub fn puff_metadata_account(
program_id: Pubkey,
metadata_account: Pubkey,
) -> Instruction {
Instruction {
program_id,
accounts: vec![
AccountMeta::new(metadata_account, false),
],
data: MetadataInstruction::PuffMetadata
.try_to_vec()
.unwrap(),
}
}
/// creates a update_primary_sale_happened_via_token instruction
#[allow(clippy::too_many_arguments)]
pub fn update_primary_sale_happened_via_token(

View File

@ -130,6 +130,13 @@ pub fn process_instruction<'a>(
args.edition,
)
}
MetadataInstruction::PuffMetadata => {
msg!("Instruction: Puff Metadata");
process_puff_metadata_account(
program_id,
accounts
)
}
}
}
@ -570,3 +577,22 @@ pub fn process_mint_new_edition_from_master_edition_via_vault_proxy<'a>(
process_mint_new_edition_from_master_edition_via_token_logic(program_id, args, edition, true)
}
/// Puff out the variable length fields to a fixed length on a metadata
/// account in a permissionless way.
pub fn process_puff_metadata_account(
program_id: &Pubkey,
accounts: &[AccountInfo],
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let metadata_account_info = next_account_info(account_info_iter)?;
let mut metadata = Metadata::from_account_info(metadata_account_info)?;
assert_owned_by(metadata_account_info, program_id)?;
puff_out_data_fields(&mut metadata);
metadata.serialize(&mut *metadata_account_info.data.borrow_mut())?;
Ok(())
}

View File

@ -23,17 +23,63 @@ use {
spl_token_metadata::{
instruction::{
create_master_edition, create_metadata_accounts,
mint_new_edition_from_master_edition_via_token, update_metadata_accounts,
mint_new_edition_from_master_edition_via_token, update_metadata_accounts,puff_metadata_account
},
state::{
get_reservation_list, Data, Edition, Key, MasterEditionV1, MasterEditionV2, Metadata,
EDITION, PREFIX,
EDITION, PREFIX,MAX_NAME_LENGTH, MAX_URI_LENGTH, MAX_SYMBOL_LENGTH
},
},
std::str::FromStr,
};
const TOKEN_PROGRAM_PUBKEY: &str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
fn puff_unpuffed_metadata(_app_matches: &ArgMatches, payer: Keypair, client: RpcClient) {
let metadata_accounts = client.get_program_accounts(&spl_token_metadata::id()).unwrap();
let mut needing_puffing = vec![];
for acct in metadata_accounts {
if acct.1.data[0] == Key::MetadataV1 as u8 {
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 {
needing_puffing.push(acct.0);
}
}
}
println!("Found {} accounts needing puffing", needing_puffing.len());
let mut instructions = vec![];
let mut i = 0;
while i < needing_puffing.len() {
let pubkey = needing_puffing[i];
instructions.push(puff_metadata_account(spl_token_metadata::id(), pubkey));
if instructions.len() == 20 {
let mut transaction = Transaction::new_with_payer(&instructions, Some(&payer.pubkey()));
let recent_blockhash = client.get_recent_blockhash().unwrap().0;
transaction.sign(&[&payer], recent_blockhash);
match client.send_and_confirm_transaction(&transaction) {
Ok(_) => {
println!("Another 20 down. At {} / {}", i, needing_puffing.len());
instructions = vec![];
i += 1;
},
Err(_) => {
println!("Txn failed. Retry.");
std::thread::sleep(std::time::Duration::from_millis(1000));
},
}
} else {
i += 1;
}
}
if instructions.len() > 0 {
let mut transaction = Transaction::new_with_payer(&instructions, Some(&payer.pubkey()));
let recent_blockhash = client.get_recent_blockhash().unwrap().0;
transaction.sign(&[&payer], recent_blockhash);
client.send_and_confirm_transaction(&transaction).unwrap();
}
}
fn mint_coins(app_matches: &ArgMatches, payer: Keypair, client: RpcClient) {
let token_key = Pubkey::from_str(TOKEN_PROGRAM_PUBKEY).unwrap();
@ -780,7 +826,10 @@ fn main() {
.takes_value(true)
.help("Account's authority, defaults to you"),
)
).get_matches();
).subcommand(
SubCommand::with_name("puff_unpuffed_metadata")
.about("Take metadata that still have variable length name, symbol, and uri fields and stretch them out with null symbols so they can be searched more easily by RPC.")).get_matches();
let client = RpcClient::new(
app_matches
@ -831,6 +880,9 @@ fn main() {
("mint_coins", Some(arg_matches)) => {
mint_coins(arg_matches, payer, client);
}
("puff_unpuffed_metadata", Some(arg_matches)) => {
puff_unpuffed_metadata(arg_matches, payer, client);
}
_ => unreachable!(),
}
}