@mountainpass/hooked-cli
Version:
A tool for runnable scripts
140 lines (139 loc) • 7.59 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/* eslint-disable no-template-curly-in-string */
import fs from 'fs/promises';
import path from 'path';
import * as ethers from 'ethers';
import { isString } from '../types.js';
import logger from '../utils/logger.js';
import defaults from '../defaults.js';
const searchJsonFiles = (directory) => __awaiter(void 0, void 0, void 0, function* () {
const foundFiles = [];
const search = (currentDirectory_1, ...args_1) => __awaiter(void 0, [currentDirectory_1, ...args_1], void 0, function* (currentDirectory, depth = 0) {
if (depth > 5) {
// logger.debug(`ABI: Max depth reached in ${currentDirectory}`)
return;
}
const files = yield fs.readdir(currentDirectory);
for (const file of files) {
const filePath = path.join(currentDirectory, file);
const stats = yield fs.stat(filePath);
if (stats.isDirectory()) {
// Recursively search subdirectories
yield search(filePath, depth + 1);
}
else if (stats.isFile() && path.extname(filePath) === '.json') {
// Process JSON file
try {
const jsonContent = yield processJsonFile(filePath);
if (typeof jsonContent.address === 'string' && typeof jsonContent.abi === 'object') {
foundFiles.push({ filePath: path.relative(directory, filePath), jsonContent });
}
}
catch (err) {
logger.debug(`Error processing ${filePath}: ${err === null || err === void 0 ? void 0 : err.message}`);
}
}
}
});
yield search(directory);
return foundFiles;
});
const processJsonFile = (filePath) => __awaiter(void 0, void 0, void 0, function* () {
const data = yield fs.readFile(filePath, 'utf8');
const json = JSON.parse(data);
return json;
});
// eslint-disable-next-line max-len
const getProviderAndWallet = (env) => __awaiter(void 0, void 0, void 0, function* () {
const provider = new ethers.JsonRpcProvider(env.PROVIDER_URL);
let blockNumber = isString(env.BLOCK_NUMBER) ? env.BLOCK_NUMBER : 'latest';
if (!isNaN(Number(blockNumber))) {
blockNumber = Number(blockNumber);
}
if (typeof env.PRIVATE_KEY === 'string') {
logger.debug(`ABI: Using block number: ${blockNumber}, Using private key: ***`);
return { provider, blockNumber, wallet: new ethers.Wallet(env.PRIVATE_KEY, provider) };
}
else {
logger.debug(`ABI: Using block number: ${blockNumber}`);
return { provider, blockNumber };
}
});
export const generateAbiScripts = () => __awaiter(void 0, void 0, void 0, function* () {
logger.debug('ABI: enabled...');
const files = yield searchJsonFiles(defaults.getDefaults().HOOKED_DIR);
logger.debug(`ABI: Found files - ${(files).length}`);
if (files.length === 0) {
return {};
}
else {
return {
'🔑 abi': Object.assign({
// get the current block number
'getBlockNumber()': {
$internal: (_a) => __awaiter(void 0, [_a], void 0, function* ({ env }) {
const provider = new ethers.JsonRpcProvider(env.PROVIDER_URL);
logger.info(yield provider.getBlockNumber());
})
},
// show the address of the current wallet
'getAddress()': {
$internal: (_a) => __awaiter(void 0, [_a], void 0, function* ({ env }) {
const { wallet } = yield getProviderAndWallet(env);
if (typeof (wallet === null || wallet === void 0 ? void 0 : wallet.address) === 'string') {
logger.info(wallet.address);
}
else {
throw new Error('No wallet. You can define one by specifying a PRIVATE_KEY environment variable.');
}
})
},
// show the native balance of the current wallet
'getBalance()': {
$internal: (_a) => __awaiter(void 0, [_a], void 0, function* ({ env }) {
const { provider, blockNumber, wallet } = yield getProviderAndWallet(env);
if (typeof (wallet === null || wallet === void 0 ? void 0 : wallet.address) === 'string') {
logger.info(yield provider.getBalance(wallet.address, blockNumber));
}
else {
throw new Error('No wallet. You can define one by specifying a PRIVATE_KEY environment variable.');
}
})
} }, Object.fromEntries((files).map((file) => {
const fns = file.jsonContent.abi.filter((abi) => {
// choose only no args (->FREE) transactions for now
return abi.type === 'function'; // && // is a function
// abi.inputs.length === 0 && // has no inputs
// ['view', 'nonpayable'].includes(abi.stateMutability) // is free
});
// map each solidity file
return [file.filePath, Object.fromEntries(fns.map((fn) => {
const contractArgs = Object.fromEntries(fn.inputs.map((input) => {
return [input.name, { $ask: `Please enter a ${input.name} (type = ${input.type})?` }];
}));
// map each function to a script
return [`${fn.name}(${fn.inputs.map(i => i.name).join(', ')}) [${fn.stateMutability}]`, {
$env: Object.assign({ PROVIDER_URL: '${PROVIDER_URL}' }, contractArgs),
$internal: (_a) => __awaiter(void 0, [_a], void 0, function* ({ env }) {
const { provider, wallet, blockNumber } = yield getProviderAndWallet(env);
const contract = typeof wallet !== 'undefined'
? new ethers.Contract(file.jsonContent.address, file.jsonContent.abi, wallet)
: new ethers.Contract(file.jsonContent.address, file.jsonContent.abi, provider);
const params = fn.inputs.map(input => env[input.name]);
// logger.debug(`calling ${fn.name}(${params.join(', ')})`)
logger.info(yield contract[fn.name](...params, { blockTag: blockNumber }));
})
}];
}))];
})))
};
}
});