import { isValidPublicKeyAddress } from '@metaplex-foundation/amman-client' import { PublicKey } from '@solana/web3.js' import { spawnSync } from 'child_process' import path from 'path' import { logError, logInfo } from '../utils' import { canAccess, ensureDirSync } from '../utils/fs' import { Account } from '../validator/types' export async function getExecutableAddress(programId: string): Promise<string> { const programPubkey = new PublicKey(programId) const [executableAddress] = await PublicKey.findProgramAddress( [programPubkey.toBytes()], new PublicKey('BPFLoaderUpgradeab1e11111111111111111111111') ) return executableAddress.toString() } export async function saveAccount( accountId: string, endpoint: string, accountsFolder: string, executable = false ) { const makeRemainingArgs = (id: string) => [ '-u', endpoint, '-o', `${accountsFolder}/${id}.json`, '--output', 'json', ] logInfo(`Saving account ${accountId} from cluster ${endpoint}`) spawnSync('solana', ['account', accountId, ...makeRemainingArgs(accountId)]) if (executable) { logInfo(`Saving executable data for ${accountId} from cluster ${endpoint}`) const executableId = await getExecutableAddress(accountId) spawnSync('solana', [ 'account', executableId, ...makeRemainingArgs(executableId), ]) } } export async function handleFetchAccounts( accountsCluster: string, accounts: Account[], accountsFolder: string, force = false ) { if (accounts.length > 0) { ensureDirSync(accountsFolder) for (const { accountId, cluster, executable } of accounts) { if (accountId == null || !isValidPublicKeyAddress(accountId)) { throw new Error( `Account ID ${accountId} in accounts array from validator config is invalid` ) } if ( force || !(await canAccess(path.join(accountsFolder, `${accountId}.json`))) ) { const endpoint = cluster ?? accountsCluster try { await saveAccount(accountId, endpoint, accountsFolder, executable) } catch (err) { logError(`Failed to load ${accountId} from cluster ${endpoint}`) throw err } } } } }