// SPDX-License-Identifier: Apache-2.0 import { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey, BpfLoader, Transaction, SystemProgram, BPF_LOADER_PROGRAM_ID, TransactionExpiredBlockheightExceededError } from '@solana/web3.js'; import { AnchorProvider, Program } from '@coral-xyz/anchor'; import fs from 'fs'; const endpoint: string = process.env.RPC_URL || "http://127.0.0.1:8899"; export async function loadContractAndCallConstructor(name: string, args: any[] = [], space: number = 8192): Promise<{ program: Program, payer: Keypair, provider: AnchorProvider, storage: Keypair, program_key: PublicKey }> { const { program, payer, provider, program_key } = await loadContract(name); const storage = Keypair.generate(); await create_account(storage, program_key, space); await program.methods.new(...args) .accounts({ dataAccount: storage.publicKey }) .rpc(); return { provider, program, payer, storage, program_key: program_key }; } export async function loadContract(name: string): Promise<{ program: Program, payer: Keypair, provider: AnchorProvider, program_key: PublicKey }> { const idl = JSON.parse(fs.readFileSync(`${name}.json`, 'utf8')); const payer = loadKey('payer.key'); process.env['ANCHOR_WALLET'] = 'payer.key'; const provider = AnchorProvider.local(endpoint); const program_key = loadKey(`${name}.key`); const program = new Program(idl, program_key.publicKey, provider) return { program, payer, provider, program_key: program_key.publicKey }; } export async function create_account(account: Keypair, programId: PublicKey, space: number) { const provider = AnchorProvider.local(endpoint); const lamports = await provider.connection.getMinimumBalanceForRentExemption(space); const transaction = new Transaction(); transaction.add( SystemProgram.createAccount({ fromPubkey: provider.wallet.publicKey, newAccountPubkey: account.publicKey, lamports, space, programId, })); await provider.sendAndConfirm(transaction, [account], { commitment: 'confirmed' }); } export function newConnectionAndPayer(): [Connection, Keypair] { const connection = newConnection(); const payerAccount = loadKey('payer.key'); return [connection, payerAccount]; } export async function loadContractWithProvider(provider: AnchorProvider, name: string, args: any[] = [], space: number = 8192): Promise<{ program: Program, storage: Keypair, program_key: PublicKey }> { const idl = JSON.parse(fs.readFileSync(`${name}.json`, 'utf8')); const storage = Keypair.generate(); const program_key = loadKey(`${name}.key`); await create_account(storage, program_key.publicKey, space); const program = new Program(idl, program_key.publicKey, provider); await program.methods.new(...args) .accounts({ dataAccount: storage.publicKey }) .rpc(); return { program, storage, program_key: program_key.publicKey }; } function loadKey(filename: string): Keypair { const contents = fs.readFileSync(filename).toString(); const bs = Uint8Array.from(JSON.parse(contents)); return Keypair.fromSecretKey(bs); } async function newAccountWithLamports(connection: Connection): Promise { const account = Keypair.generate(); console.log('Airdropping SOL to a new wallet ...'); let signature = await connection.requestAirdrop(account.publicKey, 100 * LAMPORTS_PER_SOL); const latestBlockHash = await connection.getLatestBlockhash(); await connection.confirmTransaction({ blockhash: latestBlockHash.blockhash, lastValidBlockHeight: latestBlockHash.lastValidBlockHeight, signature, }, 'confirmed'); return account; } async function setup() { let write_key = (file_name: string, key: Keypair) => { fs.writeFileSync(file_name, JSON.stringify(Array.from(key.secretKey))); }; let connection = newConnection(); const payer = await newAccountWithLamports(connection); write_key('payer.key', payer); let files = fs.readdirSync(__dirname); for (let index in files) { let file = files[index]; if (file.endsWith('.so')) { let name = file.slice(0, -3); let program; if (fs.existsSync(`${name}.key`)) { program = loadKey(`${name}.key`); } else { program = Keypair.generate(); } console.log(`Loading ${name} at ${program.publicKey}...`); const program_so = fs.readFileSync(file); for (let retries = 5; retries > 0; retries -= 1) { try { await BpfLoader.load(connection, payer, program, program_so, BPF_LOADER_PROGRAM_ID); break; } catch (e) { if (e instanceof TransactionExpiredBlockheightExceededError) { console.log(e); console.log('retrying...'); connection = newConnection(); } else { throw e; } } } console.log(`Done loading ${name} ...`); write_key(`${name}.key`, program); } } // If there was a TransactionExpiredBlockheightExceededError exception, then // setup.ts does not exit. I have no idea why process.exit(); } function newConnection(): Connection { return new Connection(endpoint, { commitment: "confirmed", confirmTransactionInitialTimeout: 1e6, }); } if (require.main === module) { (async () => { await setup(); })(); }