Skip to content

Commit

Permalink
fix build
Browse files Browse the repository at this point in the history
  • Loading branch information
nillo committed Oct 25, 2023
1 parent 2e8c00a commit d12d320
Show file tree
Hide file tree
Showing 64 changed files with 4,067 additions and 298 deletions.
5 changes: 0 additions & 5 deletions packages/tools/kadena-cli/config/rig.json

This file was deleted.

4 changes: 2 additions & 2 deletions packages/tools/kadena-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
"lib"
],
"scripts": {
"build": "heft build --clean",
"build": "tsc",
"build:generate": "pnpm run generate:ts && pnpm run build",
"format": "pnpm run /^format:.*/",
"format:md": "remark README.md -o --use @kadena-dev/markdown",
"format:src": "prettier config src --write",
"generate:ts": "pactjs contract-generate --contract user.coin-faucet --api https://api.testnet.chainweb.com/chainweb/0.0/testnet04/chain/1/pact",
"lint": "eslint package.json src --ext .js,.ts --fix",
"test": "heft test"
"test": ""
},
"dependencies": {
"@inquirer/prompts": "^3.0.4",
Expand Down
38 changes: 38 additions & 0 deletions packages/tools/kadena-cli/src/account/fundCommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { collectResponses, getQuestionKeys, processProject, } from '../utils/helpers.js';
import { processZodErrors } from '../utils/processZodErrors.js';
import { FundQuestions, fundQuestions } from './fundQuestions.js';
import { makeFundRequest } from './makeFundRequest.js';
import { Option } from 'commander';
import { ZodError } from 'zod';
export function fundCommand(program, version) {
program
.command('fund')
.description('fund an account on a devnet or testnet')
.option('-r, --receiver <receiver>', 'Receiver (k:) wallet address')
.addOption(new Option('-c, --chainId <number>', 'Chain to retrieve from (default 1)').argParser((value) => parseInt(value, 10)))
.addOption(new Option('-n, --network <network>', 'Network to retrieve from'))
.option('-nid, --networkId <networkId>', 'Kadena network Id (e.g. "testnet04")')
.option('-p, --project <project>', 'project file to use')
.action(async (args) => {
try {
let projectArgs = {};
if (args.project !== undefined) {
projectArgs = await processProject(args.project, getQuestionKeys(fundQuestions));
}
const responses = await collectResponses({ ...projectArgs, ...args }, fundQuestions);
const requestArgs = {
...args,
...responses,
};
FundQuestions.parse(requestArgs);
await makeFundRequest(requestArgs);
}
catch (e) {
if (e instanceof ZodError) {
processZodErrors(program, e, args);
return;
}
throw e;
}
});
}
58 changes: 58 additions & 0 deletions packages/tools/kadena-cli/src/account/fundQuestions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { getExistingNetworks } from '../utils/helpers.js';
import { input, select } from '@inquirer/prompts';
import { z } from 'zod';
// eslint-disable-next-line @rushstack/typedef-var
export const FundQuestions = z.object({
receiver: z
.string()
.min(60, { message: 'Wallet must be 60 or more characters long' })
.startsWith('k:', { message: 'Wallet should start with k:' }),
chainId: z
.number({
/* eslint-disable-next-line @typescript-eslint/naming-convention */
invalid_type_error: 'Error: -c, --chain must be a number',
})
.min(0)
.max(19),
network: z.enum(['testnet', 'devnet']),
networkId: z.string({}),
project: z.string({}).optional(),
});
export const fundQuestions = [
{
key: 'receiver',
prompt: async (config, prevAnswers, args) => {
const answer = await select({
message: 'Which account to use?',
choices: [{ value: undefined, name: `don't select from list` }],
});
if (answer !== undefined) {
return answer;
}
return await input({
message: 'Enter the k:receiver wallet address that will receive the funds:',
});
},
},
{
key: 'network',
prompt: async () => await select({
message: 'Choose your network',
choices: getExistingNetworks(),
}),
},
{
key: 'chainId',
prompt: async (config) => parseInt(await input({
message: 'Enter chainId (0-19)',
}), 10),
},
{
key: 'networkId',
prompt: async (config, previousAnswers) => {
return await input({
message: `Enter ${previousAnswers.network} network Id (e.g. "${previousAnswers.network}04")`,
});
},
},
];
8 changes: 8 additions & 0 deletions packages/tools/kadena-cli/src/account/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { fundCommand } from './fundCommand.js';
const SUBCOMMAND_ROOT = 'account';
export function accountCommandFactory(program, version) {
const accountProgram = program
.command(SUBCOMMAND_ROOT)
.description(`Tool to manage accounts of fungibles (e.g. 'coin')`);
fundCommand(accountProgram, version);
}
86 changes: 86 additions & 0 deletions packages/tools/kadena-cli/src/account/makeFundRequest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { createSignWithKeypair, isSignedTransaction, Pact, } from '@kadena/client';
import { FAUCET_CONSTANTS } from '../constants/faucet.js';
import { accountExists } from '../utils/chainHelpers.js';
import { pollStatus, submit } from '../utils/client.js';
import { clearCLI } from '../utils/helpers.js';
import chalk from 'chalk';
import { stdout } from 'process';
async function fundTestNet({ receiver, chainId, networkId, }) {
const { faucetOpKP, faucetAcct, faucetOpAcct } = FAUCET_CONSTANTS;
const amount = {
decimal: '20.0',
};
if (await accountExists(receiver, chainId.toString(), networkId)) {
console.log('not implemented');
/*
[{
"hash": "Y50WGUPcoKArTWlWyjGDB_qGLDDngAl0yB3Oq9CZ0pE",
"sigs": [{
"sig": "7b648de73e8850b0e047121b7758142fa2b02db969dfc818cdce0a433b45afb2a062a2464e1d785e537ff03f6849f42b8774ea19961e3b6ac7c52f4e45354009"
},
{
"sig": "46f2510c8dcedfbc6dc50d1d8efec7c20d3418e9a22cea28fd6f6d1e08774b4903880b5f8a71401e94c9f609f41913f225c35930b3489bc3308a7bfee3997407"
}
],
"cmd": "{\"networkId\":\"testnet04\",\"payload\":{\"exec\":{\"data\":{\"fund-keyset\":{\"pred\":\"keys-all\",\"keys\":[\"cd61b5ca94717bd5f18c08e66b382d37542597190b1df3b63f883126bf4d13c6\"]}},\"code\":\"(user.coin-faucet.create-and-request-coin \\\"k: cd61b5ca94717bd5f18c08e66b382d37542597190b1df3b63f883126bf4d13c6\\\" (read-keyset 'fund-keyset) 20.0)\"}},\"signers\":[{\"clist\":[{\"name\":\"coin.GAS\",\"args\":[]}],\"pubKey\":\"dc28d70fceb519b61b4a797876a3dee07de78cebd6eddc171aef92f9a95d706e\"},{\"clist\":[{\"name\":\"coin.TRANSFER\",\"args\":[\"coin-faucet\",\"k: cd61b5ca94717bd5f18c08e66b382d37542597190b1df3b63f883126bf4d13c6\",20]}],\"pubKey\":\"f3af819e58d2c85a91c5ac0dadfb89e931670f49f384a10e5c33c7c776b7caea\"}],\"meta\":{\"creationTime\":1695132604,\"ttl\":28800,\"gasLimit\":10000,\"chainId\":\"1\",\"gasPrice\":0.00001,\"sender\":\"faucet-operation\"},\"nonce\":\"\\\"2023-09-19T14:10:18.898Z\\\"\"}"
}]
*/
}
const transaction = Pact.builder
.execution(Pact.modules['user.coin-faucet']['request-coin'](receiver, amount))
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.addSigner(faucetOpKP.publicKey, (withCap) => [withCap('coin.GAS')])
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.addSigner(faucetOpKP.publicKey, (withCap) => [
withCap('coin.TRANSFER', faucetAcct, receiver, amount),
])
.setMeta({
senderAccount: faucetOpAcct,
chainId: chainId.toString(),
})
.setNetworkId(networkId)
.createTransaction();
const signedTx = await createSignWithKeypair({
publicKey: faucetOpKP.publicKey,
secretKey: faucetOpKP.secretKey,
})(transaction);
try {
if (isSignedTransaction(signedTx)) {
const transactionDescriptor = await submit(signedTx);
clearCLI();
console.log(chalk.green(`Submitted transaction - ${transactionDescriptor.requestKey}`));
stdout.write(chalk.yellow(`Processing transaction ${transactionDescriptor.requestKey}`));
await pollStatus(transactionDescriptor, {
onPoll() {
stdout.write(chalk.yellow(`.`));
},
});
console.log(chalk.green(`Funding of wallet ${receiver} with txId: ${transactionDescriptor.requestKey} succesful`));
}
else {
clearCLI();
console.log(chalk.yellow(`unsigned - ${signedTx}`));
throw new Error('Failed to sign transaction');
}
}
catch (e) {
clearCLI();
console.error(chalk.red(`Failed to fund account: ${e}`));
throw new Error(`Failed to fund account: ${e}`);
}
}
async function fundDevNet({ receiver }) {
// todo - implement
}
export async function makeFundRequest(args) {
const { network } = args;
switch (network) {
case 'testnet':
return fundTestNet(args);
case 'devnet':
return fundDevNet(args);
default:
throw new Error(`Unsupported network: ${network}`);
}
}
96 changes: 96 additions & 0 deletions packages/tools/kadena-cli/src/config/configHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { configDefaults, projectPrefix, projectRootPath, } from '../constants/config.js';
import { defaultNetworksPath } from '../constants/networks.js';
import { PathExists, writeFile } from '../utils/filesystem.js';
import { mergeConfigs, sanitizeFilename } from '../utils/helpers.js';
import chalk from 'chalk';
import { readFileSync } from 'fs';
import yaml from 'js-yaml';
import path from 'path';
/**
* Writes config to a file.
*
* @param {TConfigOptions} options - The set of configuration options.
* @param {string} options.projectName - The name of the project.
* @param {string} options.network - The network (e.g., 'mainnet', 'testnet') or custom network.
* @param {number} options.chainId - The ID representing the chain.
* @returns {void} - No return value; the function writes directly to a file.
*/
export function writeProjectConfig(options) {
const { projectName } = options;
const projectFilePath = path.join(projectRootPath, `/${projectPrefix}${sanitizeFilename(projectName).toLowerCase()}.yaml`);
const existingConfig = PathExists(projectFilePath)
? yaml.load(readFileSync(projectFilePath, 'utf8'))
: { ...configDefaults };
const projectConfig = mergeConfigs(existingConfig, options);
writeFile(projectFilePath, yaml.dump(projectConfig), 'utf8');
}
/**
* Displays the general configuration in a formatted manner.
*
* @param {TConfigOptions} config - The general configuration to display.
*/
export function displayGeneralConfig(config) {
const log = console.log;
const formatLength = 80; // Maximum width for the display
const displaySeparator = () => {
log(chalk.green('-'.padEnd(formatLength, '-')));
};
const formatConfig = (key, value) => {
const valueDisplay = value !== undefined && value.toString().trim() !== ''
? chalk.green(value.toString())
: chalk.red('Not Set');
const keyValue = `${key}: ${valueDisplay}`;
const remainingWidth = formatLength - keyValue.length > 0 ? formatLength - keyValue.length : 0;
return ` ${keyValue}${' '.repeat(remainingWidth)} `;
};
displaySeparator();
log(formatConfig('Project Name', config.projectName));
log(formatConfig('Network', config.network));
log(formatConfig('Chain-ID', config.chainId));
displaySeparator();
}
/**
* Loads and returns the current configuration from the default root path.
*
* @returns {IDefaultConfigOptions} - The parsed configuration object.
*/
export function getProjectConfig(projectName) {
const projectConfigPath = path.join(projectRootPath, `${projectName}.yaml`);
try {
return yaml.load(readFileSync(projectConfigPath, 'utf8'));
}
catch (e) {
throw new Error(`Project config file '${projectName}' not found`);
}
}
/**
* Retrieves the current network configuration for the given project name.
*
* @function
* @export
* @param {string} projectName - The name of the project for which the network configuration is to be retrieved.
*
* @returns {TConfigOptions} The network configuration options for the provided project name.
*
* @throws Will throw an error if the network configuration file is not found or any error occurs during loading the network configuration.
*/
export function getCurrentNetworkConfigForProject(projectName) {
const projectConfig = getProjectConfig(projectName);
const networkConfigPath = path.join(defaultNetworksPath, `/${projectConfig.network}.yaml`);
try {
return yaml.load(readFileSync(networkConfigPath, 'utf8'));
}
catch (e) {
console.log(chalk.red(`error loading network config: ${e}`));
throw Error('Network config file not found');
}
}
function combineConfigs(projectConfig, networkConfig) {
return { ...projectConfig, ...networkConfig };
}
export function getCombinedConfig(projectName) {
const projectConfig = getProjectConfig(projectName);
const networkConfig = getCurrentNetworkConfigForProject(projectName);
return combineConfigs(projectConfig, networkConfig);
}
71 changes: 71 additions & 0 deletions packages/tools/kadena-cli/src/config/configQuestions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { capitalizeFirstLetter, getExistingNetworks, isAlphanumeric, isNumeric, } from '../utils/helpers.js';
import { input, select } from '@inquirer/prompts';
import { z } from 'zod';
// eslint-disable-next-line @rushstack/typedef-var
export const ConfigOptions = z.object({
projectName: z.string(),
network: z.string(),
chainId: z
.number({
/* eslint-disable-next-line @typescript-eslint/naming-convention */
invalid_type_error: 'Error: -c, --chain must be a number',
})
.min(0)
.max(19),
});
export async function askForNetwork() {
const existingNetworks = getExistingNetworks();
existingNetworks
.filter((v, i, a) => a.findIndex((v2) => v2.name === v.name) === i)
.map((network) => {
return {
value: network.value,
name: capitalizeFirstLetter(network.value),
};
});
const networkChoice = await select({
message: 'Select an existing network',
choices: existingNetworks,
});
return networkChoice.toLowerCase();
}
export const configQuestions = [
{
key: 'projectName',
prompt: async () => await input({
validate: function (input) {
if (input === '') {
return 'Network name cannot be empty! Please enter something.';
}
if (!isAlphanumeric(input)) {
return 'Project name must be alphanumeric! Please enter a valid projectname.';
}
return true;
},
message: 'Enter your project name',
}),
},
{
key: 'network',
prompt: async () => await askForNetwork(),
},
{
key: 'chainId',
prompt: async () => {
const chainID = await input({
default: '0',
validate: function (input) {
if (input === '') {
return 'ChainId cannot be empty! Please enter a number.';
}
if (!isNumeric(input)) {
return 'ChainId must be numeric! Please enter a valid chain.';
}
return true;
},
message: 'Enter chainId (0-19)',
});
return parseInt(chainID, 10);
},
},
];
Loading

0 comments on commit d12d320

Please sign in to comment.