@j0nnyboi/amman
Version:
A modern mandatory toolbelt to help test solana SDK libraries and apps on a locally running validator.
308 lines • 11.1 kB
JavaScript
#!/usr/bin/env node
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const amman_client_1 = require("@j0nnyboi/amman-client");
const assert_1 = require("assert");
const child_process_1 = require("child_process");
const path_1 = __importDefault(require("path"));
const helpers_1 = require("yargs/helpers");
const yargs_1 = __importDefault(require("yargs/yargs"));
const storage_1 = require("../storage");
const utils_1 = require("../utils");
const http_1 = require("../utils/http");
const commands_1 = require("./commands");
const utils_2 = require("./utils");
const commands = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
// -----------------
// start
// -----------------
.command('start', 'Launches a solana-test-validator and the amman relay and/or mock storage if so configured', (args) => {
return args
.positional('config', {
describe: 'File containing config with `validator` property along with options for the relay and storage.',
type: 'string',
demandOption: false,
})
.option('forceClone', {
alias: 'f',
describe: 'Whether or not to force updating the programs from on chain',
type: 'boolean',
default: false,
})
.option('load', {
alias: 'l',
describe: 'Label of the snapshot to load from snapshots folder',
type: 'string',
})
.help('help', (0, commands_1.startHelp)());
})
// -----------------
// stop
// -----------------
.command('stop', 'Stops the relay and storage and kills the running solana test validator')
// -----------------
// logs
// -----------------
.command('logs', `Launches 'solana logs' and pipes them through a prettifier`)
// -----------------
// airdrop
// -----------------
.command('airdrop', 'Airdrops provided Sol to the payer', (args) => args
.positional('destination', {
describe: 'A base58 PublicKey string or the relative path to the Keypair file of the airdrop destination',
type: 'string',
})
.positional('amount', {
describe: 'The amount of Sol to airdrop',
type: 'number',
default: 1,
})
.option('label', {
alias: 'l',
describe: 'The label to give to the account being airdropped to',
type: 'string',
default: 'payer',
})
.option('commitment', {
alias: 'c',
describe: 'The commitment to use for the Airdrop transaction',
type: 'string',
choices: utils_1.commitments,
default: 'singleGossip',
})
.help('help', (0, commands_1.airdropHelp)()))
// -----------------
// label
// -----------------
.command('label', 'Adds labels for accounts or transactions to amman', (args) => args.help('help', (0, commands_1.labelHelp)()))
// -----------------
// account
// -----------------
.command('account', 'Retrieves account information for a PublicKey or a label or shows all labeled accounts', (args) => args
.positional('address', {
describe: 'A base58 PublicKey string or the label of the acount to retrieve.' +
' If it is not provided, all labeled accounts are shown.',
type: 'string',
demandOption: false,
})
.option('includeTx', {
alias: 't',
describe: 'If to include transactions in the shown labeled accounts when no label/address is provided',
type: 'boolean',
default: false,
})
.option('save', {
alias: 's',
describe: 'If set the account information is saved to a file inside ./.amman/accounts',
type: 'boolean',
default: false,
}))
// -----------------
// snapshot
// -----------------
.command('snapshot', 'Creates a snapshot of the current accounts known to amman', (args) => {
args.positional('label', {
describe: 'The label to give to the snapshot. Default label is the account address.',
type: 'string',
demandOption: false,
});
})
// -----------------
// run
// -----------------
.command('run', 'Executes the provided command after expanding all address labels', (args) => args
.option('label', {
alias: 'l',
describe: 'Used to label addresses found int the command output ',
type: 'string',
multiple: true,
demandOption: false,
})
.option('txOnly', {
alias: 't',
describe: 'Includes only transaction addresses when labeling.',
type: 'string',
demandOption: false,
default: false,
})
.option('accOnly', {
alias: 'a',
describe: 'Includes only account addresses when labeling.',
type: 'string',
demandOption: false,
default: false,
})
.help('help', (0, commands_1.runHelp)()));
async function main() {
var _a, _b;
setupGracefulShutdown();
const args = await commands.parse();
const { _: cs } = args;
if (cs.length === 0) {
commands.showHelp();
return;
}
const command = cs[0];
switch (command) {
// -----------------
// start
// -----------------
case 'start': {
const { needHelp } = await (0, commands_1.handleStartCommand)(args);
if (needHelp) {
(0, utils_1.logInfo)('Rerun `amman --help` for more information');
}
break;
}
// -----------------
// stop
// -----------------
case 'stop': {
await stopAmman();
break;
}
// -----------------
// logs
// -----------------
case 'logs': {
(0, commands_1.handleLogsCommand)();
break;
}
// -----------------
// airdrop
// -----------------
case 'airdrop': {
const { commitment, label } = args;
try {
const destination = cs[1];
const maybeAmount = cs[2];
const amount = maybeAmount == null
? 1
: typeof maybeAmount === 'string'
? parseInt(maybeAmount)
: maybeAmount;
(0, assert_1.strict)(typeof destination === 'string', 'public key string or keypair file is required');
(0, assert_1.strict)(destination != null, 'public key string or keypair file is required');
(0, utils_1.assertCommitment)(commitment);
const { connection } = await (0, commands_1.handleAirdropCommand)(destination, amount, label, commitment);
await (0, utils_2.closeConnection)(connection, true);
}
catch (err) {
(0, utils_1.logError)(err);
commands.showHelp();
}
break;
}
// -----------------
// label
// -----------------
case 'label': {
const labels = cs.slice(1);
(0, assert_1.strict)(labels.length > 0, 'At least one label is required');
for (const label of labels) {
(0, assert_1.strict)(typeof label == 'string', `All labels must be of type string 'label:publicKey' and ${label} is not`);
}
await (0, commands_1.handleLabelCommand)(labels);
break;
}
// -----------------
// account
// -----------------
case 'account': {
const address = cs[1];
const { includeTx, save } = args;
(0, assert_1.strict)(address == null || typeof address === 'string', 'provided public key or label needs to be a string');
(0, assert_1.strict)(!includeTx || address == null, '--includeTx can only be used when no address is provided');
(0, assert_1.strict)(!save || address != null, '--save requires an account address or label to be provided');
const { connection, rendered, savedAccountPath } = await (0, commands_1.handleAccountCommand)(address, includeTx, save);
console.log(rendered);
if (savedAccountPath != null) {
(0, utils_1.logInfo)(`Saved account to ./${path_1.default.relative(process.cwd(), savedAccountPath)}`);
}
if (connection != null) {
await (0, utils_2.closeConnection)(connection, true);
}
disconnectAmman();
break;
}
// -----------------
// snapshot
// -----------------
case 'snapshot': {
const label = (_a = cs[1]) === null || _a === void 0 ? void 0 : _a.toString();
const snapshotDir = await (0, commands_1.handleSnapshotCommand)(label);
(0, utils_1.logInfo)(`Saved snapshot to ./${path_1.default.relative(process.cwd(), snapshotDir)}`);
disconnectAmman();
break;
}
// -----------------
// run
// -----------------
case 'run': {
let labels = (_b = args.label) !== null && _b !== void 0 ? _b : [];
if (!Array.isArray(labels)) {
labels = [labels];
}
const { txOnly, accOnly } = args;
const cmdArgs = cs.slice(1);
(0, assert_1.strict)(cmdArgs.length > 0, 'At least one argument is required or did you mean to `amman start`?');
try {
const { stdout, stderr } = await (0, commands_1.handleRunCommand)(labels, cmdArgs, txOnly, accOnly);
console.error(stderr);
console.log(stdout);
}
catch (err) {
(0, utils_1.logError)(err.toString());
}
break;
}
default:
commands.showHelp();
}
}
async function disconnectAmman(connection) {
var _a, _b, _c;
try {
(_a = amman_client_1.Amman.existingInstance) === null || _a === void 0 ? void 0 : _a.disconnect();
(_b = amman_client_1.Amman.existingInstance) === null || _b === void 0 ? void 0 : _b.destroy();
}
catch (_) { }
try {
(_c = storage_1.MockStorageServer.existingInstance) === null || _c === void 0 ? void 0 : _c.stop();
}
catch (_) { }
if (connection != null) {
try {
await (0, utils_2.closeConnection)(connection, true);
}
catch (_) { }
}
}
async function stopAmman() {
try {
(0, child_process_1.execSync)('pkill -f solana-test-validator');
(0, utils_1.logInfo)('Killed currently running solana-test-validator');
}
catch (_) { }
try {
await (0, http_1.killRunningServer)(amman_client_1.AMMAN_RELAY_PORT);
}
catch (_) { }
try {
await (0, http_1.killRunningServer)(amman_client_1.AMMAN_STORAGE_PORT);
}
catch (_) { }
}
function setupGracefulShutdown() {
process.on('beforeExit', () => {
disconnectAmman();
});
}
main().catch((err) => {
(0, utils_1.logError)(err);
process.exit(1);
});
//# sourceMappingURL=amman.js.map