From a946dbce5940f07dc4969b28e92c93df87565e3b Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Mon, 18 May 2026 17:55:16 +0100 Subject: [PATCH] Migrate JS tests from local validator to LiteSVM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR replaces the local validator with [LiteSVM](https://github.com/LiteSVM/litesvm) for the JS test suite: swaps `solanaLocalRpc()` for `litesvm()` in `_setup.ts`, fabricates loader-v3 program/programData accounts directly via `client.svm.setAccount(...)` in `createDeployedProgram`, deletes the now-unused `test/loader-v3/` helpers and the `validator:*` scripts, and drops `solana: true` from the CI test job. Tests now run ~47× faster (~600ms vs ~28s) with no validator required. --- .github/workflows/main.yml | 2 - clients/js/README.md | 5 +- clients/js/package.json | 1 + clients/js/pnpm-lock.yaml | 105 +++++++ clients/js/test/_setup.ts | 139 +++++---- clients/js/test/loader-v3/deploy.ts | 287 ------------------ clients/js/test/loader-v3/initializeBuffer.ts | 139 --------- clients/js/test/loader-v3/shared.ts | 120 -------- clients/js/test/loader-v3/write.ts | 164 ---------- package.json | 3 - scripts/js/test.mjs | 3 - scripts/start-validator.mjs | 125 -------- scripts/stop-validator.mjs | 13 - 13 files changed, 191 insertions(+), 915 deletions(-) delete mode 100644 clients/js/test/loader-v3/deploy.ts delete mode 100644 clients/js/test/loader-v3/initializeBuffer.ts delete mode 100644 clients/js/test/loader-v3/shared.ts delete mode 100644 clients/js/test/loader-v3/write.ts delete mode 100644 scripts/start-validator.mjs delete mode 100644 scripts/stop-validator.mjs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 35a3e8a..c40b848 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -185,8 +185,6 @@ jobs: - name: Setup Environment uses: ./.github/actions/setup - with: - solana: true - name: Restore Client Artifacts uses: actions/cache/restore@v4 diff --git a/clients/js/README.md b/clients/js/README.md index bae7f38..af85f88 100644 --- a/clients/js/README.md +++ b/clients/js/README.md @@ -10,16 +10,15 @@ To build and test your JavaScript client from the root of the repository, you ma pnpm clients:js:test ``` -This will start a new local validator, if one is not already running, and run the tests for your JavaScript client. +This will build the program and run the tests for your JavaScript client. Tests run in-memory via [LiteSVM](https://github.com/LiteSVM/litesvm) — no local validator required. ## Available client scripts. Alternatively, you can go into the client directory and run the tests directly. ```sh -# Build your programs and start the validator. +# Build your programs so the compiled `.so` file is available for LiteSVM. pnpm programs:build -pnpm validator:restart # Go into the client directory and run the tests. cd clients/js diff --git a/clients/js/package.json b/clients/js/package.json index 51a7ff9..e902f9c 100644 --- a/clients/js/package.json +++ b/clients/js/package.json @@ -61,6 +61,7 @@ "devDependencies": { "@solana-config/eslint": "^0.2.3", "@solana/kit": "^6.9.0", + "@solana/kit-plugin-litesvm": "^0.10.0", "@solana/kit-plugin-rpc": "^0.11.1", "@solana/kit-plugin-signer": "^0.10.0", "@types/node": "^24", diff --git a/clients/js/pnpm-lock.yaml b/clients/js/pnpm-lock.yaml index fba021e..3fa4e00 100644 --- a/clients/js/pnpm-lock.yaml +++ b/clients/js/pnpm-lock.yaml @@ -36,6 +36,9 @@ importers: '@solana/kit': specifier: ^6.9.0 version: 6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/kit-plugin-litesvm': + specifier: ^0.10.0 + version: 0.10.0(@solana/kit@6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/kit-plugin-rpc': specifier: ^0.11.1 version: 0.11.1(@solana/kit@6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)) @@ -569,6 +572,11 @@ packages: peerDependencies: '@solana/kit': ^6.1.0 + '@solana-program/token@0.12.0': + resolution: {integrity: sha512-hnidRNuFhmqUdW5aWkKTJ+cdzuotVMNwLsTyAk0Nd8VjLDld+vQC0fugHWqm5GPrvYe0hCNAhtpJcZVnNp7rOA==} + peerDependencies: + '@solana/kit': ^6.1.0 + '@solana/accounts@6.9.0': resolution: {integrity: sha512-g36AJreJrgf9AAjOfbdFHEFUTymBgzbWHoEDElZ+fDKvqBINDiUVKzDApwc7C7kGPMFqQBaoEHnQRxf2IqfKZQ==} engines: {node: '>=20.18.0'} @@ -713,6 +721,11 @@ packages: peerDependencies: '@solana/kit': ^6.8.0 + '@solana/kit-plugin-litesvm@0.10.0': + resolution: {integrity: sha512-ks/sbFyYJOpa4BrdzEpp2karA66N4b1DFnWol3bui/UzJm+YJZ65xnOyeX9U8PC8uF/TPVkgf6oYvsxyLqrxIw==} + peerDependencies: + '@solana/kit': ^6.8.0 + '@solana/kit-plugin-rpc@0.11.1': resolution: {integrity: sha512-NpCKBY6y3lmOOZjzm5dvk/+iZsKOH+Wc9TQx5yKZ0RzNOXC6V9i0Inn+niVdkX5ECDJOmClfa64bLcImFJWLuQ==} peerDependencies: @@ -1698,6 +1711,46 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + litesvm-darwin-arm64@1.0.0: + resolution: {integrity: sha512-Fl1i0G+DbsMh4M0nLtFvJcbYcxVZyYOnJhvLTxzFy26CN5dtOHRTWcHTkQZpfEwdIQxCJAvjsGIHAg4K/ecycQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + litesvm-darwin-x64@1.0.0: + resolution: {integrity: sha512-4gSTuYdOCZ0SpjbGRhZ6g8ua8oSJtMgHwz7qpjTPvXw5Zc1K2bdWDlV0FNwg+NSTTQhCn7PzmpFKip2lI/CU3w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + litesvm-linux-arm64-gnu@1.0.0: + resolution: {integrity: sha512-i/NdIvl/D3AJE2PbvYOqORKAe5EYpKO6hlgqKEukzWDQd/yx7hmevg/yEI5rJuI0BfTDPZzRuK4Y5wMKdKu42A==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + litesvm-linux-arm64-musl@1.0.0: + resolution: {integrity: sha512-suv1yKYH4oBcR9dIf4JdoMOjdDPyi4QHsO6dKB7Kw/0/tGxnWy/ZcZCMqE283exEyGc64bZXqFTZQQcZxqJrRw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + litesvm-linux-x64-gnu@1.0.0: + resolution: {integrity: sha512-mM+0lSof8Kb45EXUOqM6E+uVNEZgfOn7Vy0PgEgHI9igDhoxID7S4Z44AAcw5k0hzfPT7qtHGH/uOeIFz6w5fw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + litesvm-linux-x64-musl@1.0.0: + resolution: {integrity: sha512-xBuFg4PYicqrkthgEgX7GkY2XftqwwBhqZoxEjMjLT9XiRAu82xZJsZTQ0yC749+phXeWT7fjp3W6uVEKvDjAQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + litesvm@1.0.0: + resolution: {integrity: sha512-+uM5Uqut++IKNoG4rw2QzMNspZhqqqRXZ5XDPxb68kJMBrOdvIJX9Ie7IAHTD26qdX074FqMDqlOqlJR9H3xIw==} + engines: {node: '>= 20'} + load-tsconfig@0.2.5: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2585,6 +2638,11 @@ snapshots: dependencies: '@solana/kit': 6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana-program/token@0.12.0(@solana/kit@6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))': + dependencies: + '@solana-program/system': 0.12.0(@solana/kit@6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)) + '@solana/kit': 6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/accounts@6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: '@solana/addresses': 6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -2718,6 +2776,17 @@ snapshots: dependencies: '@solana/kit': 6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/kit-plugin-litesvm@0.10.0(@solana/kit@6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + dependencies: + '@solana/kit': 6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/kit-plugin-instruction-plan': 0.10.0(@solana/kit@6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)) + litesvm: 1.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + '@solana/kit-plugin-rpc@0.11.1(@solana/kit@6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))': dependencies: '@solana/kit': 6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -3837,6 +3906,42 @@ snapshots: lines-and-columns@1.2.4: {} + litesvm-darwin-arm64@1.0.0: + optional: true + + litesvm-darwin-x64@1.0.0: + optional: true + + litesvm-linux-arm64-gnu@1.0.0: + optional: true + + litesvm-linux-arm64-musl@1.0.0: + optional: true + + litesvm-linux-x64-gnu@1.0.0: + optional: true + + litesvm-linux-x64-musl@1.0.0: + optional: true + + litesvm@1.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): + dependencies: + '@solana-program/system': 0.12.0(@solana/kit@6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)) + '@solana-program/token': 0.12.0(@solana/kit@6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)) + '@solana/kit': 6.9.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + optionalDependencies: + litesvm-darwin-arm64: 1.0.0 + litesvm-darwin-x64: 1.0.0 + litesvm-linux-arm64-gnu: 1.0.0 + litesvm-linux-arm64-musl: 1.0.0 + litesvm-linux-x64-gnu: 1.0.0 + litesvm-linux-x64-musl: 1.0.0 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + load-tsconfig@0.2.5: {} locate-path@6.0.0: diff --git a/clients/js/test/_setup.ts b/clients/js/test/_setup.ts index 1acfc2f..ba6866d 100644 --- a/clients/js/test/_setup.ts +++ b/clients/js/test/_setup.ts @@ -1,37 +1,57 @@ +import path from 'node:path'; + import { systemProgram } from '@solana-program/system'; import { Address, createClient, generateKeyPairSigner, - getBase64Encoder, + getAddressEncoder, + getOptionEncoder, + getStructEncoder, + getU32Encoder, + getU64Encoder, KeyPairSigner, + lamports, Lamports, sol, solToLamports, + some, } from '@solana/kit'; -import { solanaLocalRpc } from '@solana/kit-plugin-rpc'; +import { litesvm } from '@solana/kit-plugin-litesvm'; import { airdropSigner, generatedSigner } from '@solana/kit-plugin-signer'; import { getProgramDataPda as getLoaderV3ProgramDataPda, LOADER_V3_PROGRAM_ADDRESS, + PROGRAM_METADATA_PROGRAM_ADDRESS, programMetadataProgram, } from '../src'; -import { getDeployWithMaxDataLenInstruction as getLoaderV3DeployInstruction } from './loader-v3/deploy'; -import { getInitializeBufferInstruction as getLoaderV3InitializeBufferInstruction } from './loader-v3/initializeBuffer'; -import { getWriteInstruction as getLoaderV3WriteInstruction } from './loader-v3/write'; export const REALLOC_LIMIT = 10_240; -const SMALLER_VALID_PROGRAM_BINARY = - 'f0VMRgIBAQAAAAAAAAAAAAMA9wABAAAA6AAAAAAAAABAAAAAAAAAAMgBAAAAAAAAAAAAAEAAOAADAEAABgAFAAEAAAAFAAAA6AAAAAAAAADoAAAAAAAAAOgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAAQAAAAAAAAAQAAAAQAAABgAQAAAAAAAGABAAAAAAAAYAEAAAAAAAA8AAAAAAAAADwAAAAAAAAAABAAAAAAAAACAAAABgAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAHAAAAAAAAAAcAAAAAAAAAAIAAAAAAAAAJUAAAAAAAAAHgAAAAAAAAAEAAAAAAAAAAYAAAAAAAAAYAEAAAAAAAALAAAAAAAAABgAAAAAAAAABQAAAAAAAACQAQAAAAAAAAoAAAAAAAAADAAAAAAAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAQAAEA6AAAAAAAAAAAAAAAAAAAAABlbnRyeXBvaW50AAAudGV4dAAuZHluYW1pYwAuZHluc3ltAC5keW5zdHIALnNoc3RydGFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAABgAAAAAAAADoAAAAAAAAAOgAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAHAAAABgAAAAMAAAAAAAAA8AAAAAAAAADwAAAAAAAAAHAAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAEAAAAAsAAAACAAAAAAAAAGABAAAAAAAAYAEAAAAAAAAwAAAAAAAAAAQAAAABAAAACAAAAAAAAAAYAAAAAAAAABgAAAADAAAAAgAAAAAAAACQAQAAAAAAAJABAAAAAAAADAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAgAAAAAwAAAAAAAAAAAAAAAAAAAAAAAACcAQAAAAAAACoAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA'; +const PROGRAM_METADATA_BINARY_PATH = path.resolve( + __dirname, + '..', + '..', + '..', + 'target', + 'deploy', + 'spl_program_metadata.so', +); export const createTestClient = () => { return createClient() .use(generatedSigner()) - .use(solanaLocalRpc()) + .use(litesvm()) .use(airdropSigner(solToLamports(sol('1')))) .use(systemProgram()) + .use(client => { + // Load the program-metadata program into the LiteSVM instance from + // its compiled `.so` file. This must run after the `litesvm()` + // plugin so that `client.svm` is available. + client.svm.addProgramFromFile(PROGRAM_METADATA_PROGRAM_ADDRESS, PROGRAM_METADATA_BINARY_PATH); + return client; + }) .use(programMetadataProgram()); }; @@ -49,60 +69,67 @@ export const generateKeyPairSignerWithSol = async ( export const getBalance = async (client: TestClient, address: Address) => (await client.rpc.getBalance(address, { commitment: 'confirmed' }).send()).value; +/** + * Encoders mirroring the on-chain loader-v3 account layouts. See + * {@link getProgramAuthority} in `src/utils.ts` for the corresponding + * decoders. + */ +const getLoaderV3ProgramAccountEncoder = () => + getStructEncoder([ + ['discriminator', getU32Encoder()], + ['programData', getAddressEncoder()], + ]); + +const getLoaderV3ProgramDataAccountEncoder = () => + getStructEncoder([ + ['discriminator', getU32Encoder()], + ['slot', getU64Encoder()], + ['authority', getOptionEncoder(getAddressEncoder())], + ]); + +/** + * Fabricates a "deployed" loader-v3 program inside the LiteSVM instance by + * writing the `program` and `programData` accounts directly. The deployed + * program is never actually invoked — only its account data is read by the + * program-metadata program for the canonicity check. + */ export const createDeployedProgram = async ( client: TestClient, authority: KeyPairSigner, - payer?: KeyPairSigner, ): Promise<[Address, Address]> => { - // Prepare all inputs. - payer = payer ?? authority; - const data = getBase64Encoder().encode(SMALLER_VALID_PROGRAM_BINARY); - const dataSize = BigInt(37 + data.length); - const programSize = 36n; - const [buffer, program, dataRent, programRent] = await Promise.all([ - generateKeyPairSigner(), - generateKeyPairSigner(), - client.getMinimumBalance(Number(dataSize)), - client.getMinimumBalance(Number(programSize)), - ]); + const program = await generateKeyPairSigner(); const [programData] = await getLoaderV3ProgramDataPda(program.address); - // Create the buffer, write the program binary to it, create the program - // account and deploy the program from the buffer. - await client.sendTransactions([ - client.system.instructions.createAccount({ - payer, - newAccount: buffer, - lamports: dataRent, - space: dataSize, - programAddress: LOADER_V3_PROGRAM_ADDRESS, - }), - getLoaderV3InitializeBufferInstruction({ - sourceAccount: buffer.address, - bufferAuthority: authority.address, - }), - getLoaderV3WriteInstruction({ - bufferAccount: buffer.address, - bufferAuthority: authority, - offset: 0, - bytes: data, - }), - client.system.instructions.createAccount({ - payer, - newAccount: program, - lamports: programRent, - space: programSize, - programAddress: LOADER_V3_PROGRAM_ADDRESS, - }), - getLoaderV3DeployInstruction({ - payerAccount: payer, - programDataAccount: programData, - programAccount: program.address, - bufferAccount: buffer.address, - authority, - maxDataLen: data.length, - }), - ]); + const programAccountData = getLoaderV3ProgramAccountEncoder().encode({ + discriminator: 2, + programData, + }); + const programDataAccountData = getLoaderV3ProgramDataAccountEncoder().encode({ + discriminator: 3, + slot: 0n, + authority: some(authority.address), + }); + + const programSpace = BigInt(programAccountData.length); + const programDataSpace = BigInt(programDataAccountData.length); + + client.svm.setAccount({ + address: program.address, + data: programAccountData, + executable: true, + lamports: lamports(client.svm.minimumBalanceForRentExemption(programSpace)), + programAddress: LOADER_V3_PROGRAM_ADDRESS, + space: programSpace, + }); + + client.svm.setAccount({ + address: programData, + data: programDataAccountData, + executable: false, + lamports: lamports(client.svm.minimumBalanceForRentExemption(programDataSpace)), + programAddress: LOADER_V3_PROGRAM_ADDRESS, + space: programDataSpace, + }); return [program.address, programData]; }; diff --git a/clients/js/test/loader-v3/deploy.ts b/clients/js/test/loader-v3/deploy.ts deleted file mode 100644 index 2ac9de0..0000000 --- a/clients/js/test/loader-v3/deploy.ts +++ /dev/null @@ -1,287 +0,0 @@ -import { - combineCodec, - getStructDecoder, - getStructEncoder, - getU32Decoder, - getU32Encoder, - getU64Decoder, - getU64Encoder, - transformEncoder, - type Address, - type Codec, - type Decoder, - type Encoder, - type AccountMeta, - type AccountSignerMeta, - type Instruction, - type InstructionWithAccounts, - type InstructionWithData, - type ReadonlyAccount, - type ReadonlySignerAccount, - type TransactionSigner, - type WritableAccount, - type WritableSignerAccount, -} from '@solana/kit'; -import { getAccountMetaFactory, type ResolvedAccount } from './shared'; -import { LOADER_V3_PROGRAM_ADDRESS } from '../../src'; - -export const DEPLOY_WITH_MAX_DATA_LEN_DISCRIMINATOR = 2; - -export function getDeployWithMaxDataLenDiscriminatorBytes() { - return getU32Encoder().encode(DEPLOY_WITH_MAX_DATA_LEN_DISCRIMINATOR); -} - -export type DeployWithMaxDataLenInstruction< - TProgram extends string = typeof LOADER_V3_PROGRAM_ADDRESS, - TAccountPayerAccount extends string | AccountMeta = string, - TAccountProgramDataAccount extends string | AccountMeta = string, - TAccountProgramAccount extends string | AccountMeta = string, - TAccountBufferAccount extends string | AccountMeta = string, - TAccountRentSysvar extends string | AccountMeta = 'SysvarRent111111111111111111111111111111111', - TAccountClockSysvar extends string | AccountMeta = 'SysvarC1ock11111111111111111111111111111111', - TAccountSystemProgram extends string | AccountMeta = '11111111111111111111111111111111', - TAccountAuthority extends string | AccountMeta = string, - TRemainingAccounts extends readonly AccountMeta[] = [], -> = Instruction & - InstructionWithData & - InstructionWithAccounts< - [ - TAccountPayerAccount extends string - ? WritableSignerAccount & AccountSignerMeta - : TAccountPayerAccount, - TAccountProgramDataAccount extends string - ? WritableAccount - : TAccountProgramDataAccount, - TAccountProgramAccount extends string ? WritableAccount : TAccountProgramAccount, - TAccountBufferAccount extends string ? WritableAccount : TAccountBufferAccount, - TAccountRentSysvar extends string ? ReadonlyAccount : TAccountRentSysvar, - TAccountClockSysvar extends string ? ReadonlyAccount : TAccountClockSysvar, - TAccountSystemProgram extends string ? ReadonlyAccount : TAccountSystemProgram, - TAccountAuthority extends string - ? ReadonlySignerAccount & AccountSignerMeta - : TAccountAuthority, - ...TRemainingAccounts, - ] - >; - -export type DeployWithMaxDataLenInstructionData = { - discriminator: number; - maxDataLen: bigint; -}; - -export type DeployWithMaxDataLenInstructionDataArgs = { - maxDataLen: number | bigint; -}; - -export function getDeployWithMaxDataLenInstructionDataEncoder(): Encoder { - return transformEncoder( - getStructEncoder([ - ['discriminator', getU32Encoder()], - ['maxDataLen', getU64Encoder()], - ]), - value => ({ - ...value, - discriminator: DEPLOY_WITH_MAX_DATA_LEN_DISCRIMINATOR, - }), - ); -} - -export function getDeployWithMaxDataLenInstructionDataDecoder(): Decoder { - return getStructDecoder([ - ['discriminator', getU32Decoder()], - ['maxDataLen', getU64Decoder()], - ]); -} - -export function getDeployWithMaxDataLenInstructionDataCodec(): Codec< - DeployWithMaxDataLenInstructionDataArgs, - DeployWithMaxDataLenInstructionData -> { - return combineCodec( - getDeployWithMaxDataLenInstructionDataEncoder(), - getDeployWithMaxDataLenInstructionDataDecoder(), - ); -} - -export type DeployWithMaxDataLenInput< - TAccountPayerAccount extends string = string, - TAccountProgramDataAccount extends string = string, - TAccountProgramAccount extends string = string, - TAccountBufferAccount extends string = string, - TAccountRentSysvar extends string = string, - TAccountClockSysvar extends string = string, - TAccountSystemProgram extends string = string, - TAccountAuthority extends string = string, -> = { - /** Payer account that will pay to create the ProgramData account. */ - payerAccount: TransactionSigner; - /** ProgramData account (uninitialized). */ - programDataAccount: Address; - /** Program account (uninitialized). */ - programAccount: Address; - /** Buffer account where the program data has been written. */ - bufferAccount: Address; - /** Rent sysvar. */ - rentSysvar?: Address; - /** Clock sysvar. */ - clockSysvar?: Address; - /** System program. */ - systemProgram?: Address; - /** Authority. */ - authority: TransactionSigner; - maxDataLen: DeployWithMaxDataLenInstructionDataArgs['maxDataLen']; -}; - -export function getDeployWithMaxDataLenInstruction< - TAccountPayerAccount extends string, - TAccountProgramDataAccount extends string, - TAccountProgramAccount extends string, - TAccountBufferAccount extends string, - TAccountRentSysvar extends string, - TAccountClockSysvar extends string, - TAccountSystemProgram extends string, - TAccountAuthority extends string, ->( - input: DeployWithMaxDataLenInput< - TAccountPayerAccount, - TAccountProgramDataAccount, - TAccountProgramAccount, - TAccountBufferAccount, - TAccountRentSysvar, - TAccountClockSysvar, - TAccountSystemProgram, - TAccountAuthority - >, -): DeployWithMaxDataLenInstruction< - typeof LOADER_V3_PROGRAM_ADDRESS, - TAccountPayerAccount, - TAccountProgramDataAccount, - TAccountProgramAccount, - TAccountBufferAccount, - TAccountRentSysvar, - TAccountClockSysvar, - TAccountSystemProgram, - TAccountAuthority -> { - // Program address. - const programAddress = LOADER_V3_PROGRAM_ADDRESS; - - // Original accounts. - const originalAccounts = { - payerAccount: { value: input.payerAccount ?? null, isWritable: true }, - programDataAccount: { - value: input.programDataAccount ?? null, - isWritable: true, - }, - programAccount: { value: input.programAccount ?? null, isWritable: true }, - bufferAccount: { value: input.bufferAccount ?? null, isWritable: true }, - rentSysvar: { value: input.rentSysvar ?? null, isWritable: false }, - clockSysvar: { value: input.clockSysvar ?? null, isWritable: false }, - systemProgram: { value: input.systemProgram ?? null, isWritable: false }, - authority: { value: input.authority ?? null, isWritable: false }, - }; - const accounts = originalAccounts as Record; - - // Original args. - const args = { ...input }; - - // Resolve default values. - if (!accounts.rentSysvar.value) { - accounts.rentSysvar.value = - 'SysvarRent111111111111111111111111111111111' as Address<'SysvarRent111111111111111111111111111111111'>; - } - if (!accounts.clockSysvar.value) { - accounts.clockSysvar.value = - 'SysvarC1ock11111111111111111111111111111111' as Address<'SysvarC1ock11111111111111111111111111111111'>; - } - if (!accounts.systemProgram.value) { - accounts.systemProgram.value = - '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>; - } - - const getAccountMeta = getAccountMetaFactory(programAddress, 'programId'); - const instruction = { - accounts: [ - getAccountMeta(accounts.payerAccount), - getAccountMeta(accounts.programDataAccount), - getAccountMeta(accounts.programAccount), - getAccountMeta(accounts.bufferAccount), - getAccountMeta(accounts.rentSysvar), - getAccountMeta(accounts.clockSysvar), - getAccountMeta(accounts.systemProgram), - getAccountMeta(accounts.authority), - ], - programAddress, - data: getDeployWithMaxDataLenInstructionDataEncoder().encode(args as DeployWithMaxDataLenInstructionDataArgs), - } as DeployWithMaxDataLenInstruction< - typeof LOADER_V3_PROGRAM_ADDRESS, - TAccountPayerAccount, - TAccountProgramDataAccount, - TAccountProgramAccount, - TAccountBufferAccount, - TAccountRentSysvar, - TAccountClockSysvar, - TAccountSystemProgram, - TAccountAuthority - >; - - return instruction; -} - -export type ParsedDeployWithMaxDataLenInstruction< - TProgram extends string = typeof LOADER_V3_PROGRAM_ADDRESS, - TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[], -> = { - programAddress: Address; - accounts: { - /** Payer account that will pay to create the ProgramData account. */ - payerAccount: TAccountMetas[0]; - /** ProgramData account (uninitialized). */ - programDataAccount: TAccountMetas[1]; - /** Program account (uninitialized). */ - programAccount: TAccountMetas[2]; - /** Buffer account where the program data has been written. */ - bufferAccount: TAccountMetas[3]; - /** Rent sysvar. */ - rentSysvar: TAccountMetas[4]; - /** Clock sysvar. */ - clockSysvar: TAccountMetas[5]; - /** System program. */ - systemProgram: TAccountMetas[6]; - /** Authority. */ - authority: TAccountMetas[7]; - }; - data: DeployWithMaxDataLenInstructionData; -}; - -export function parseDeployWithMaxDataLenInstruction< - TProgram extends string, - TAccountMetas extends readonly AccountMeta[], ->( - instruction: Instruction & InstructionWithAccounts & InstructionWithData, -): ParsedDeployWithMaxDataLenInstruction { - if (instruction.accounts.length < 8) { - // TODO: Coded error. - throw new Error('Not enough accounts'); - } - let accountIndex = 0; - const getNextAccount = () => { - const accountMeta = instruction.accounts![accountIndex]!; - accountIndex += 1; - return accountMeta; - }; - return { - programAddress: instruction.programAddress, - accounts: { - payerAccount: getNextAccount(), - programDataAccount: getNextAccount(), - programAccount: getNextAccount(), - bufferAccount: getNextAccount(), - rentSysvar: getNextAccount(), - clockSysvar: getNextAccount(), - systemProgram: getNextAccount(), - authority: getNextAccount(), - }, - data: getDeployWithMaxDataLenInstructionDataDecoder().decode(instruction.data), - }; -} diff --git a/clients/js/test/loader-v3/initializeBuffer.ts b/clients/js/test/loader-v3/initializeBuffer.ts deleted file mode 100644 index 05eaa3e..0000000 --- a/clients/js/test/loader-v3/initializeBuffer.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { - combineCodec, - getStructDecoder, - getStructEncoder, - getU32Decoder, - getU32Encoder, - transformEncoder, - type Address, - type Codec, - type Decoder, - type Encoder, - type AccountMeta, - type Instruction, - type InstructionWithAccounts, - type InstructionWithData, - type ReadonlyAccount, - type WritableAccount, -} from '@solana/kit'; -import { getAccountMetaFactory, type ResolvedAccount } from './shared'; -import { LOADER_V3_PROGRAM_ADDRESS } from '../../src'; - -export const INITIALIZE_BUFFER_DISCRIMINATOR = 0; - -export function getInitializeBufferDiscriminatorBytes() { - return getU32Encoder().encode(INITIALIZE_BUFFER_DISCRIMINATOR); -} - -export type InitializeBufferInstruction< - TProgram extends string = typeof LOADER_V3_PROGRAM_ADDRESS, - TAccountSourceAccount extends string | AccountMeta = string, - TAccountBufferAuthority extends string | AccountMeta = string, - TRemainingAccounts extends readonly AccountMeta[] = [], -> = Instruction & - InstructionWithData & - InstructionWithAccounts< - [ - TAccountSourceAccount extends string ? WritableAccount : TAccountSourceAccount, - TAccountBufferAuthority extends string ? ReadonlyAccount : TAccountBufferAuthority, - ...TRemainingAccounts, - ] - >; - -export type InitializeBufferInstructionData = { discriminator: number }; - -export type InitializeBufferInstructionDataArgs = {}; - -export function getInitializeBufferInstructionDataEncoder(): Encoder { - return transformEncoder(getStructEncoder([['discriminator', getU32Encoder()]]), value => ({ - ...value, - discriminator: INITIALIZE_BUFFER_DISCRIMINATOR, - })); -} - -export function getInitializeBufferInstructionDataDecoder(): Decoder { - return getStructDecoder([['discriminator', getU32Decoder()]]); -} - -export function getInitializeBufferInstructionDataCodec(): Codec< - InitializeBufferInstructionDataArgs, - InitializeBufferInstructionData -> { - return combineCodec(getInitializeBufferInstructionDataEncoder(), getInitializeBufferInstructionDataDecoder()); -} - -export type InitializeBufferInput< - TAccountSourceAccount extends string = string, - TAccountBufferAuthority extends string = string, -> = { - /** Source account to initialize. */ - sourceAccount: Address; - /** Buffer authority. */ - bufferAuthority: Address; -}; - -export function getInitializeBufferInstruction< - TAccountSourceAccount extends string, - TAccountBufferAuthority extends string, ->( - input: InitializeBufferInput, -): InitializeBufferInstruction { - // Program address. - const programAddress = LOADER_V3_PROGRAM_ADDRESS; - - // Original accounts. - const originalAccounts = { - sourceAccount: { value: input.sourceAccount ?? null, isWritable: true }, - bufferAuthority: { - value: input.bufferAuthority ?? null, - isWritable: false, - }, - }; - const accounts = originalAccounts as Record; - - const getAccountMeta = getAccountMetaFactory(programAddress, 'programId'); - const instruction = { - accounts: [getAccountMeta(accounts.sourceAccount), getAccountMeta(accounts.bufferAuthority)], - programAddress, - data: getInitializeBufferInstructionDataEncoder().encode({}), - } as InitializeBufferInstruction; - - return instruction; -} - -export type ParsedInitializeBufferInstruction< - TProgram extends string = typeof LOADER_V3_PROGRAM_ADDRESS, - TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[], -> = { - programAddress: Address; - accounts: { - /** Source account to initialize. */ - sourceAccount: TAccountMetas[0]; - /** Buffer authority. */ - bufferAuthority: TAccountMetas[1]; - }; - data: InitializeBufferInstructionData; -}; - -export function parseInitializeBufferInstruction( - instruction: Instruction & InstructionWithAccounts & InstructionWithData, -): ParsedInitializeBufferInstruction { - if (instruction.accounts.length < 2) { - // TODO: Coded error. - throw new Error('Not enough accounts'); - } - let accountIndex = 0; - const getNextAccount = () => { - const accountMeta = instruction.accounts![accountIndex]!; - accountIndex += 1; - return accountMeta; - }; - return { - programAddress: instruction.programAddress, - accounts: { - sourceAccount: getNextAccount(), - bufferAuthority: getNextAccount(), - }, - data: getInitializeBufferInstructionDataDecoder().decode(instruction.data), - }; -} diff --git a/clients/js/test/loader-v3/shared.ts b/clients/js/test/loader-v3/shared.ts deleted file mode 100644 index 1df89e4..0000000 --- a/clients/js/test/loader-v3/shared.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { - AccountRole, - isProgramDerivedAddress, - isTransactionSigner as web3JsIsTransactionSigner, - type Address, - type AccountMeta, - type AccountSignerMeta, - type ProgramDerivedAddress, - type TransactionSigner, - upgradeRoleToSigner, -} from '@solana/kit'; - -/** - * Asserts that the given value is not null or undefined. - * @internal - */ -export function expectSome(value: T | null | undefined): T { - if (value == null) { - throw new Error('Expected a value but received null or undefined.'); - } - return value; -} - -/** - * Asserts that the given value is a PublicKey. - * @internal - */ -export function expectAddress( - value: Address | ProgramDerivedAddress | TransactionSigner | null | undefined, -): Address { - if (!value) { - throw new Error('Expected a Address.'); - } - if (typeof value === 'object' && 'address' in value) { - return value.address; - } - if (Array.isArray(value)) { - return value[0] as Address; - } - return value as Address; -} - -/** - * Asserts that the given value is a PDA. - * @internal - */ -export function expectProgramDerivedAddress( - value: Address | ProgramDerivedAddress | TransactionSigner | null | undefined, -): ProgramDerivedAddress { - if (!value || !Array.isArray(value) || !isProgramDerivedAddress(value)) { - throw new Error('Expected a ProgramDerivedAddress.'); - } - return value; -} - -/** - * Asserts that the given value is a TransactionSigner. - * @internal - */ -export function expectTransactionSigner( - value: Address | ProgramDerivedAddress | TransactionSigner | null | undefined, -): TransactionSigner { - if (!value || !isTransactionSigner(value)) { - throw new Error('Expected a TransactionSigner.'); - } - return value; -} - -/** - * Defines an instruction account to resolve. - * @internal - */ -export type ResolvedAccount< - T extends string = string, - U extends Address | ProgramDerivedAddress | TransactionSigner | null = - | Address - | ProgramDerivedAddress - | TransactionSigner - | null, -> = { - isWritable: boolean; - value: U; -}; - -/** - * Defines an instruction that stores additional bytes on-chain. - * @internal - */ -export type InstructionWithByteDelta = { - byteDelta: number; -}; - -/** - * Get account metas and signers from resolved accounts. - * @internal - */ -export function getAccountMetaFactory(programAddress: Address, optionalAccountStrategy: 'omitted' | 'programId') { - return (account: ResolvedAccount): AccountMeta | AccountSignerMeta | undefined => { - if (!account.value) { - if (optionalAccountStrategy === 'omitted') return; - return Object.freeze({ - address: programAddress, - role: AccountRole.READONLY, - }); - } - - const writableRole = account.isWritable ? AccountRole.WRITABLE : AccountRole.READONLY; - return Object.freeze({ - address: expectAddress(account.value), - role: isTransactionSigner(account.value) ? upgradeRoleToSigner(writableRole) : writableRole, - ...(isTransactionSigner(account.value) ? { signer: account.value } : {}), - }); - }; -} - -export function isTransactionSigner( - value: Address | ProgramDerivedAddress | TransactionSigner, -): value is TransactionSigner { - return !!value && typeof value === 'object' && 'address' in value && web3JsIsTransactionSigner(value); -} diff --git a/clients/js/test/loader-v3/write.ts b/clients/js/test/loader-v3/write.ts deleted file mode 100644 index fd1e0cf..0000000 --- a/clients/js/test/loader-v3/write.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { - addDecoderSizePrefix, - addEncoderSizePrefix, - combineCodec, - getBytesDecoder, - getBytesEncoder, - getStructDecoder, - getStructEncoder, - getU32Decoder, - getU32Encoder, - getU64Decoder, - getU64Encoder, - transformEncoder, - type Address, - type Codec, - type Decoder, - type Encoder, - type AccountMeta, - type AccountSignerMeta, - type Instruction, - type InstructionWithAccounts, - type InstructionWithData, - type ReadonlySignerAccount, - type ReadonlyUint8Array, - type TransactionSigner, - type WritableAccount, -} from '@solana/kit'; -import { getAccountMetaFactory, type ResolvedAccount } from './shared'; -import { LOADER_V3_PROGRAM_ADDRESS } from '../../src'; - -export const WRITE_DISCRIMINATOR = 1; - -export function getWriteDiscriminatorBytes() { - return getU32Encoder().encode(WRITE_DISCRIMINATOR); -} - -export type WriteInstruction< - TProgram extends string = typeof LOADER_V3_PROGRAM_ADDRESS, - TAccountBufferAccount extends string | AccountMeta = string, - TAccountBufferAuthority extends string | AccountMeta = string, - TRemainingAccounts extends readonly AccountMeta[] = [], -> = Instruction & - InstructionWithData & - InstructionWithAccounts< - [ - TAccountBufferAccount extends string ? WritableAccount : TAccountBufferAccount, - TAccountBufferAuthority extends string - ? ReadonlySignerAccount & AccountSignerMeta - : TAccountBufferAuthority, - ...TRemainingAccounts, - ] - >; - -export type WriteInstructionData = { - discriminator: number; - offset: number; - bytes: ReadonlyUint8Array; -}; - -export type WriteInstructionDataArgs = { - offset: number; - bytes: ReadonlyUint8Array; -}; - -export function getWriteInstructionDataEncoder(): Encoder { - return transformEncoder( - getStructEncoder([ - ['discriminator', getU32Encoder()], - ['offset', getU32Encoder()], - ['bytes', addEncoderSizePrefix(getBytesEncoder(), getU64Encoder())], - ]), - value => ({ ...value, discriminator: WRITE_DISCRIMINATOR }), - ); -} - -export function getWriteInstructionDataDecoder(): Decoder { - return getStructDecoder([ - ['discriminator', getU32Decoder()], - ['offset', getU32Decoder()], - ['bytes', addDecoderSizePrefix(getBytesDecoder(), getU64Decoder())], - ]); -} - -export function getWriteInstructionDataCodec(): Codec { - return combineCodec(getWriteInstructionDataEncoder(), getWriteInstructionDataDecoder()); -} - -export type WriteInput< - TAccountBufferAccount extends string = string, - TAccountBufferAuthority extends string = string, -> = { - /** Buffer account. */ - bufferAccount: Address; - /** Buffer authority. */ - bufferAuthority: TransactionSigner; - offset: WriteInstructionDataArgs['offset']; - bytes: WriteInstructionDataArgs['bytes']; -}; - -export function getWriteInstruction( - input: WriteInput, -): WriteInstruction { - // Program address. - const programAddress = LOADER_V3_PROGRAM_ADDRESS; - - // Original accounts. - const originalAccounts = { - bufferAccount: { value: input.bufferAccount ?? null, isWritable: true }, - bufferAuthority: { - value: input.bufferAuthority ?? null, - isWritable: false, - }, - }; - const accounts = originalAccounts as Record; - - // Original args. - const args = { ...input }; - - const getAccountMeta = getAccountMetaFactory(programAddress, 'programId'); - const instruction = { - accounts: [getAccountMeta(accounts.bufferAccount), getAccountMeta(accounts.bufferAuthority)], - programAddress, - data: getWriteInstructionDataEncoder().encode(args as WriteInstructionDataArgs), - } as WriteInstruction; - - return instruction; -} - -export type ParsedWriteInstruction< - TProgram extends string = typeof LOADER_V3_PROGRAM_ADDRESS, - TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[], -> = { - programAddress: Address; - accounts: { - /** Buffer account. */ - bufferAccount: TAccountMetas[0]; - /** Buffer authority. */ - bufferAuthority: TAccountMetas[1]; - }; - data: WriteInstructionData; -}; - -export function parseWriteInstruction( - instruction: Instruction & InstructionWithAccounts & InstructionWithData, -): ParsedWriteInstruction { - if (instruction.accounts.length < 2) { - // TODO: Coded error. - throw new Error('Not enough accounts'); - } - let accountIndex = 0; - const getNextAccount = () => { - const accountMeta = instruction.accounts![accountIndex]!; - accountIndex += 1; - return accountMeta; - }; - return { - programAddress: instruction.programAddress, - accounts: { - bufferAccount: getNextAccount(), - bufferAuthority: getNextAccount(), - }, - data: getWriteInstructionDataDecoder().decode(instruction.data), - }; -} diff --git a/package.json b/package.json index a9bed90..e14fb04 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,6 @@ "solana:link": "zx ./scripts/link-solana-version.mjs", "generate": "pnpm generate:clients", "generate:clients": "codama run --all", - "validator:start": "zx ./scripts/start-validator.mjs", - "validator:restart": "pnpm validator:start --restart", - "validator:stop": "zx ./scripts/stop-validator.mjs", "clients:js:format": "zx ./scripts/js/format.mjs", "clients:js:lint": "zx ./scripts/js/lint.mjs", "clients:js:publish": "zx ./scripts/js/publish.mjs", diff --git a/scripts/js/test.mjs b/scripts/js/test.mjs index 652df0e..12e1ca9 100644 --- a/scripts/js/test.mjs +++ b/scripts/js/test.mjs @@ -2,9 +2,6 @@ import 'zx/globals'; import { cliArguments, workingDirectory } from '../utils.mjs'; -// Start the local validator, or restart it if it is already running. -await $`pnpm validator:restart`; - // Build the client and run the tests. cd(path.join(workingDirectory, 'clients', 'js')); await $`pnpm install`; diff --git a/scripts/start-validator.mjs b/scripts/start-validator.mjs deleted file mode 100644 index 1adbcab..0000000 --- a/scripts/start-validator.mjs +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env zx -import { spawn } from 'node:child_process'; -import fs from 'node:fs'; -import 'zx/globals'; -import { - getCargo, - getExternalAccountAddresses, - getExternalProgramAddresses, - getExternalProgramOutputDir, - getProgramFolders, -} from './utils.mjs'; - -// Check Solana version. -await $`pnpm solana:check`; - -// Options and arguments. -const restart = argv['restart']; - -// Keep the validator running when not using the restart flag. -const isValidatorRunning = (await $`lsof -t -i:8899`.quiet().exitCode) === 0; -if (!restart && isValidatorRunning) { - echo(chalk.yellow('Local validator is already running.')); - process.exit(); -} - -// Initial message. -const verb = isValidatorRunning ? 'Restarting' : 'Starting'; - -// Get programs and accounts. -const programs = [...getPrograms(), ...getExternalPrograms()]; -const programPluralized = programs.length === 1 ? 'program' : 'programs'; -const accounts = [...getExternalAccounts()]; -const accountsPluralized = accounts.length === 1 ? 'account' : 'accounts'; - -echo( - `${verb} local validator with ${programs.length} custom ${programPluralized}` + - (accounts.length > 0 - ? ` and ${accounts.length} external ${accountsPluralized}...` - : `...`) -); - -// Kill the validator if it's already running. -if (isValidatorRunning) { - await $`pkill -f solana-test-validator`.quiet(); - await sleep(1000); -} - -// Global validator arguments. -const args = [/* Reset ledger */ '-r']; - -// Load programs. -programs.forEach(({ programId, deployPath }) => { - args.push(/* Load BPF program */ '--bpf-program', programId, deployPath); -}); - -// Load accounts. -accounts.forEach(({ account, deployPath }) => { - args.push(/* Load account */ '--account', account, deployPath); -}); - -// Start the validator in detached mode. -const cliLogs = path.join(os.tmpdir(), 'validator-cli.log'); -fs.writeFileSync(cliLogs, '', () => {}); -const out = fs.openSync(cliLogs, 'a'); -const err = fs.openSync(cliLogs, 'a'); -const validator = spawn('solana-test-validator', args, { - detached: true, - stdio: ['ignore', out, err], -}); -validator.unref(); - -// Wait for the validator to stabilize. -const waitForValidator = spinner( - 'Waiting for local validator to stabilize...', - () => - new Promise((resolve, reject) => { - setInterval(() => { - const logs = fs.readFileSync(cliLogs, 'utf8'); - if (validator.exitCode !== null) { - reject(logs); - } else if (logs.includes('Confirmed Slot: 1')) { - resolve(); - } - }, 1000); - }) -); - -try { - await waitForValidator; - echo(chalk.green('Local validator is up and running!')); -} catch (error) { - echo(error); - echo(chalk.red('Could not start local validator.')); -} finally { - fs.rmSync(cliLogs); - process.exit(); -} - -function getPrograms() { - const binaryDir = path.join(__dirname, '..', 'target', 'deploy'); - return getProgramFolders().map((folder) => { - const cargo = getCargo(folder); - const name = cargo.package.name.replace(/-/g, '_'); - return { - programId: cargo.package.metadata.solana['program-id'], - deployPath: path.join(binaryDir, `${name}.so`), - }; - }); -} - -function getExternalPrograms() { - const binaryDir = getExternalProgramOutputDir(); - return getExternalProgramAddresses().map((address) => ({ - programId: address, - deployPath: path.join(binaryDir, `${address}.so`), - })); -} - -function getExternalAccounts() { - const binaryDir = getExternalProgramOutputDir(); - return getExternalAccountAddresses().map((address) => ({ - account: address, - deployPath: path.join(binaryDir, `${address}.json`), - })); -} diff --git a/scripts/stop-validator.mjs b/scripts/stop-validator.mjs deleted file mode 100644 index ebbff8d..0000000 --- a/scripts/stop-validator.mjs +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; - -const isValidatorRunning = (await $`lsof -t -i:8899`.quiet().exitCode) === 0; - -if (isValidatorRunning) { - // Kill the validator if it's already running. - await $`pkill -f solana-test-validator`.quiet(); - await sleep(1000); - echo(chalk.green('Local validator terminated!')); -} else { - echo(chalk.yellow('Local validator is not running.')); -}