@completium/completium-cli
Version:
1,695 lines (1,477 loc) • 156 kB
JavaScript
/*!
* completium-cli <https://github.com/completium/completium-cli>
*
* Copyright (c) 2021-2025, edukera, SAS.
* Released under the MIT License.
*/
import fs from 'fs';
import execa from 'execa';
import path from 'path';
import glob from 'glob';
import * as taquito from '@taquito/taquito';
import * as taquitoUtils from '@taquito/utils';
import binderTs from '@completium/archetype-binder-ts';
import * as codec from '@taquito/michel-codec';
import * as encoder from '@taquito/michelson-encoder';
import bip39 from 'bip39';
import * as signer from '@taquito/signer';
import { BigNumber } from 'bignumber.js';
import { Fraction } from 'fractional';
import os from 'os';
import fetch from 'node-fetch';
import enquirer from 'enquirer';
const { Select } = enquirer;
import tmp from 'tmp'
import readline from 'readline'
import blakejs from 'blakejs'
import keccakLib from 'keccak'
import archetype from '@completium/archetype';
const version = '1.0.27'
const homedir = os.homedir();
const completium_dir = homedir + '/.completium'
const config_path = completium_dir + '/config.json'
const accounts_path = completium_dir + '/accounts.json'
const contracts_path = completium_dir + '/contracts.json'
const mockup_conf_path = completium_dir + '/mockup.conf.json'
const log_path = completium_dir + '/log.json'
const bin_dir = completium_dir + '/bin'
const contracts_dir = completium_dir + "/contracts"
const scripts_dir = completium_dir + "/scripts"
const sources_dir = completium_dir + "/sources"
// const docker_id = 'oxheadalpha/flextesa:latest'
// const flextesa_script = 'nairobibox'
var config = null;
const mockup_path = completium_dir + "/mockup";
const context_mockup_path = completium_dir + "/mockup/mockup/context.json";
const tezos_client_dir = homedir + '/.tezos-client'
// const default_mockup_protocol = 'ProxfordYmVfjWnRcgjWH36fW6PArwqykTFzotUxRs6gmTcZDuH'
const default_mockup_protocol = 'PsParisCZo7KAh1Z1smVd9ZMZ1HHn5gkzbM94V3PLCpknFWhUAi'
const import_endpoint = 'https://ghostnet.ecadinfra.com'; // used for import faucet
///////////
// TOOLS //
///////////
let settings_quiet = false
function print(msg) {
if (!settings_quiet)
console.log(msg);
}
function print_error(msg) {
if (!settings_quiet)
console.error(msg);
}
function loadJS(path) {
return JSON.parse(fs.readFileSync(path, 'utf8'));
}
export function getConfig() {
if (config == null)
config = loadJS(config_path);
return config;
}
function getMockupConfig() {
let mockup_config = {};
if (fs.existsSync(mockup_conf_path)) {
mockup_config = loadJS(mockup_conf_path);
}
return mockup_config;
}
function saveFile(path, c, callback) {
const content = JSON.stringify(c, null, 2);
fs.writeFileSync(path, content);
if (callback !== undefined) {
callback();
}
}
function saveConfig(config, callback) {
saveFile(config_path, config, callback);
}
function saveMockupConfig(config, callback) {
saveFile(mockup_conf_path, config, callback);
}
function getContracts() {
if (!fs.existsSync(contracts_path)) {
print(`Completium is not initialized, try 'completium-cli init'`);
return null;
}
const content = fs.readFileSync(contracts_path, 'utf8');
var res = JSON.parse(content);
return res;
}
function saveContract(c, callback) {
var obj = getContracts();
const name = c.name;
const a = obj.contracts.find(x => x.name === name);
if (isNull(a)) {
obj.contracts.push(c);
} else {
obj.contracts = obj.contracts.map(x => x.name === name ? c : x)
}
saveFile(contracts_path, obj, callback);
}
function getContract(name) {
var obj = getContracts();
var c = obj.contracts.find(x => x.name === name);
return c;
}
function getContractFromIdOrAddress(input) {
var obj = getContracts();
var c = obj.contracts.find(x => x.name === input || x.address === input);
return c;
}
function getAccounts() {
if (!fs.existsSync(accounts_path)) {
print(`Completium is not initialized, try 'completium-cli init'`);
return null;
}
var res = JSON.parse(fs.readFileSync(accounts_path, 'utf8'));
return res;
}
async function saveAccount(c, callback) {
var obj = getAccounts();
const name = c.name;
const a = obj.accounts.find(x => x.name === name);
if (isNull(a)) {
obj.accounts.push(c);
} else {
obj.accounts = obj.accounts.map(x => x.name === name ? c : x)
}
saveFile(accounts_path, obj, callback);
}
function renameAccountInternal(src, dst, callback) {
var obj = getAccounts();
obj.accounts = obj.accounts.map(x => x.name === src ? { ...x, name: dst } : x)
saveFile(accounts_path, obj, callback);
}
function removeAccountInternal(name, callback) {
var obj = getAccounts();
obj.accounts = obj.accounts.filter(x => { return (name !== x.name) });
saveFile(accounts_path, obj, callback);
}
function removeContractInternal(input, callback) {
var obj = getContracts();
obj.contracts = obj.contracts.filter(x => { return (input !== x.name && input !== x.address) });
saveFile(contracts_path, obj, callback);
}
function getAccount(name) {
var obj = getAccounts();
return obj.accounts.find(x => x.name === name);
}
function getAccountFromIdOrAddr(input) {
var obj = getAccounts();
return obj.accounts.find(x => x.name === input || x.pkh === input);
}
function getSigner(forceAccount) {
const config = getConfig();
const account = config.account;
if (isNull(forceAccount) && isNull(account)) {
print("Cannot execute this command, please generate an account first.");
return null;
}
var a = isNull(forceAccount) ? account : forceAccount;
var ac = getAccount(a);
if (isNull(ac)) {
print(`${account} is not found.`);
return null;
}
return {
signer: new signer.InMemorySigner(ac.key.value)
}
}
function getTezos(forceAccount) {
const config = getConfig();
const tezos_endpoint = config.tezos.endpoint;
const tezos = new taquito.TezosToolkit(tezos_endpoint);
var signer = getSigner(forceAccount);
tezos.setProvider(signer);
return tezos;
}
function isNull(str) {
return str === undefined || str === null || str === "";
}
function computeSettings(options, settings) {
const config = getConfig();
const metadata_storage = options.metadata_storage ? options.metadata_storage : (settings ? settings.metadata_storage : undefined);
const metadata_uri = options.metadata_uri ? options.metadata_uri : (settings ? settings.metadata_uri : undefined);
const otest = options.test || (settings !== undefined && settings.test_mode);
const compiler_json = options.compiler_json ? true : false;
return {
...settings,
"test_mode": otest,
"metadata_storage": metadata_storage,
"metadata_uri": metadata_uri,
"json": compiler_json
}
}
function computeArgsSettings(options, settings, path) {
const args = []
if (settings.version) {
args.push('--version')
} else {
if (settings.with_parameters) {
args.push('--with-parameters')
} else {
if (settings.target) {
args.push('--target');
args.push(settings.target);
}
if (settings.contract_interface) {
args.push('--show-contract-interface');
}
if (settings.contract_interface_michelson) {
args.push('--show-contract-interface-michelson');
}
if (settings.sci) {
args.push('--set-caller-init');
args.push(settings.sci);
}
if (settings.get_storage_values) {
args.push('--get-storage-values')
}
if (options.metadata_storage) {
args.push('--metadata-storage');
args.push(options.metadata_storage);
} else if (settings.metadata_storage) {
args.push('--metadata-storage');
args.push(settings.metadata_storage);
}
if (options.metadata_uri) {
args.push('--metadata-uri');
args.push(options.metadata_uri);
} else if (settings.metadata_uri) {
args.push('--metadata-uri');
args.push(settings.metadata_uri);
}
if (options.test || (settings !== undefined && settings.test_mode)) {
args.push('--test-mode');
}
if (options.no_js_header) {
args.push('--no-js-header');
}
if (settings.compiler_json) {
args.push('--json');
}
if (settings.sandbox_exec_address) {
args.push('--sandbox-exec-address');
args.push(settings.sandbox_exec_address);
}
}
args.push(path);
}
return args;
}
async function callArchetype(options, path, s) {
const verbose = options.verbose;
const config = getConfig();
// const isFrombin = config.archetype_from_bin ? config.archetype_from_bin : false;
const archetypeMode = config.mode.archetype;
switch (archetypeMode) {
case 'binary':
{
const bin = config.bin.archetype;
const args = computeArgsSettings(options, s, path);
if (verbose) {
print(args);
}
return new Promise(async (resolve, reject) => {
try {
const { stdout, stderr, failed } = await execa(bin, args, {});
if (failed) {
const msg = "Archetype compiler: " + stderr;
reject(msg);
} else {
resolve(stdout);
}
} catch (e) {
reject(e);
}
})
};
case 'js':
{
if (s.version) {
return archetype.version()
} else {
try {
const settings = computeSettings(options, s);
if (verbose) {
print(settings);
}
const a = await archetype.compile(path, settings);
return a;
} catch (error) {
if (error.message) {
const msg = "Archetype compiler: " + error.message;
throw msg;
} else {
throw error;
}
}
}
}
case 'docker':
{
const docker_bin = 'docker';
const cwd = process.cwd();
const args = ['run', '--platform=linux/amd64', '--rm', '-v', `${cwd}:${cwd}`, '-w', `${cwd}`, 'completium/archetype:latest'].concat(computeArgsSettings(options, s, path));
if (verbose) {
print(args);
}
return new Promise(async (resolve, reject) => {
try {
const { stdout, stderr, failed } = await execa(docker_bin, args, {});
if (failed) {
const msg = "Archetype compiler: " + stderr;
reject(msg);
} else {
resolve(stdout);
}
} catch (e) {
reject(e);
}
});
}
default:
throw 'Archetype compiler: unknown mode';
}
}
export function expr_micheline_to_json(input) {
return (new codec.Parser()).parseMichelineExpression(input.toString());
}
export function json_micheline_to_expr(input) {
return codec.emitMicheline(input);
}
function getAmount(raw) {
if (typeof raw !== "string") {
throw ('amount must be a string')
}
var v = raw.endsWith('utz') ? { str: raw.slice(0, -3), utz: true } : (raw.endsWith('tz') ? { str: raw.slice(0, -2), utz: false } : null);
if (isNull(v)) {
const msg = `'${raw}' is an invalid value; expecting for example, 1tz or 2utz.`;
throw msg;
}
let rat = new Fraction(v.str);
if (!v.utz) {
rat = rat.multiply(new Fraction(1000000, 1))
}
if (rat.denominator != 1) {
const msg = `'${raw}' is an invalid value.`;
throw msg;
}
return rat.numerator;
}
async function getArchetypeVersion() {
const v = await callArchetype({}, null, { version: true });
return v;
}
async function show_entries_internal(i, rjson) {
const config = getConfig();
const isFrombin = config.archetype_from_bin ? config.archetype_from_bin : false;
if (isFrombin) {
const bin = config.bin.archetype;
const tmpobj = tmp.fileSync({ postfix: '.json' });
const path = tmpobj.name;
fs.writeFileSync(path, i);
const args = []
args.push('-rj');
args.push('-j');
args.push('--show-entries');
args.push(path);
return new Promise(async (resolve, reject) => {
try {
const { stdout, stderr, failed } = await execa(bin, args, {});
if (failed) {
const msg = "Archetype compiler: " + stderr;
reject(msg);
} else {
resolve(stdout);
}
} catch (e) {
reject(e);
}
});
} else {
const res = archetype.show_entries(i, {
json: true,
rjson: rjson
});
return res;
}
}
function isMockupMode() {
var config = getConfig();
const tezos_endpoint = config.tezos.endpoint;
return tezos_endpoint === "mockup";
}
function isForceTezosClient() {
var config = getConfig();
const force_tezos_client = config.tezos.force_tezos_client;
return force_tezos_client !== undefined && force_tezos_client;
}
async function callTezosClient(args) {
let arguments_;
if (isMockupMode()) {
arguments_ = ["--mode", "mockup", "--base-dir", mockup_path].concat(args);
} else {
const tezos_endpoint = config.tezos.endpoint;
arguments_ = ["--endpoint", tezos_endpoint].concat(args);
}
try {
const bin = config.bin['tezos-client'];
const x = await execa(bin, arguments_, {});
return x;
} catch (e) {
return e;
}
}
export async function retrieveBalanceFor(addr) {
if (isMockupMode()) {
const args = ["rpc", "get", "/chains/main/blocks/head/context/contracts/" + addr + "/balance"];
const { stdout, stderr, failed } = await callTezosClient(args);
if (failed) {
return new Promise((resolve, reject) => { reject(stderr) });
} else {
const val = JSON.parse(stdout);
const res = new BigNumber(val);
return res;
}
} else {
const tezos = getTezos();
var balance = await tezos.tz.getBalance(addr);
return balance;
}
}
async function rpcGet(uri) {
if (isMockupMode()) {
const { stdout, stderr, failed } = await callTezosClient(["rpc", "get", uri]);
if (failed) {
throw new Error(stderr);
} else {
return JSON.parse(stdout);
}
} else {
const config = getConfig();
const tezos_endpoint = config.tezos.endpoint;
const url = tezos_endpoint + uri;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Error get status code: ${response.status}`);
}
return await response.json();
} catch (error) {
throw error;
}
}
}
async function rpcPost(uri, payload) {
if (isMockupMode()) {
const { stdout, stderr, failed } = await callTezosClient(["rpc", "post", uri, "with", JSON.stringify(payload)]);
if (failed) {
throw new Error(stderr);
} else {
return JSON.parse(stdout);
}
} else {
const config = getConfig();
const tezos_endpoint = config.tezos.endpoint;
const url = tezos_endpoint + uri;
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`Error get status code: ${response.status}`);
}
return await response.json();
} catch (error) {
throw error;
}
}
}
export async function getChainId() {
if (isMockupMode()) {
const input = loadJS(context_mockup_path);
return input.chain_id;
} else {
return new Promise((resolve, reject) => {
rpcGet("/chains/main/blocks/head/header")
.then(x => {
if (x && x.chain_id) {
resolve(x.chain_id)
}
})
.catch(err => {
reject(err)
})
});
}
}
//////////////
// COMMANDS //
//////////////
function help(options) {
print("usage: [command] [options]")
print("command:");
print(" init")
print(" help");
print(" version")
print(" archetype version")
print(" install archetype")
// print("")
// print(" start sandbox");
// print(" stop sandbox");
print("")
print(" mockup init [--protocol <VALUE>]");
print(" mockup set now <value>");
print("")
print(" show endpoint");
print(" switch endpoint");
print(" add endpoint (main|ghost|sandbox) <ENDPOINT_URL>");
print(" set endpoint <ENDPOINT_URL>");
print(" remove endpoint <ENDPOINT_URL>");
print("")
print(" set mode archetype (js|docker|binary)")
print(" switch mode archetype")
print(" show mode archetype")
print(" set binary path (archetype|tezos-client) <PATH>");
print(" show binary path (archetype|tezos-client)");
print("")
print(" generate account as <ACCOUNT_ALIAS> [--with-tezos-client] [--force]");
print(" import faucet <FAUCET_FILE> as <ACCOUNT_ALIAS> [--with-tezos-client] [--force]");
print(" import privatekey <PRIVATE_KEY> as <ACCOUNT_ALIAS> [--with-tezos-client] [--force]");
print(" import contract <CONTRACT_ADDRESS> as <CONTRACT_ALIAS> [--network <NETWORK>]");
print(" remove contracts from (mockup|sandbox)");
print("")
print(" show keys from <PRIVATE_KEY>");
print(" set account <ACCOUNT_ALIAS>");
print(" switch account");
print(" rename account <ACCOUNT_ALIAS|ACCOUNT_ADDRESS> by <ACCOUNT_ALIAS> [--force]");
print(" remove account <ACCOUNT_ALIAS>");
print("")
print(" set contract address <CONTRACT_NAME> <ADDRESS>");
print(" print contract <CONTRACT_NAME>");
print("")
print(" transfer <AMOUNT>(tz|utz) from <ACCOUNT_ALIAS|ACCOUNT_ADDRESS> to <ACCOUNT_ALIAS|ACCOUNT_ADDRESS> [--force]");
print(" deploy <FILE.arl> [--as <ACCOUNT_ALIAS>] [--named <CONTRACT_ALIAS>] [--amount <AMOUNT>(tz|utz)] [--fee <FEE>(tz|utz)] [--init <MICHELSON_DATA> | --parameters <PARAMETERS> | --parameters-micheline <PARAMETERS>] [--metadata-storage <PATH_TO_JSON> | --metadata-uri <VALUE_URI>] [--force] [--show-tezos-client-command]");
print(" originate <FILE.tz> [--as <ACCOUNT_ALIAS>] [--named <CONTRACT_ALIAS>] [--amount <AMOUNT>(tz|utz)] [--fee <FEE>(tz|utz)] [--force-tezos-client] [--force] [--show-tezos-client-command]");
print(" call <CONTRACT_ALIAS> [--as <ACCOUNT_ALIAS>] [--entry <ENTRYPOINT>] [--arg <ARGS> | --arg-michelson <MICHELSON_DATA>] [--amount <AMOUNT>(tz|utz)] [--fee <FEE>(tz|utz)] [--force] [--show-tezos-client-command]");
print(" run <FILE.arl> [--entry <ENTRYPOINT>] [--arg-michelson <MICHELSON_DATA>] [--amount <AMOUNT>(tz|utz)] [--trace] [--force]");
print(" run getter <GETTER_ID> on <CONTRACT_ALIAS|CONTRACT_ADDRESS> [--arg <MICHELSON_DATA>] [--as <CALLER_ADDRESS>]");
print(" run view <VIEW_ID> on <CONTRACT_ALIAS|CONTRACT_ADDRESS> [--arg-michelson <MICHELSON_DATA>] [--as <CALLER_ADDRESS>]");
print(" interp <FILE.[arl|tz]> [--entry <ENTRYPOINT>] [--arg-michelson <MICHELSON_DATA>] [--amount <AMOUNT>(tz|utz)] [--force]");
print(" register global constant <MICHELSON_DATA> [--as <CALLER_ADDRESS>] [--force]");
print(" generate michelson <FILE.arl|CONTRACT_ALIAS>");
print(" generate javascript <FILE.arl|CONTRACT_ALIAS>");
print(" generate whyml <FILE.arl|CONTRACT_ALIAS>");
print(" generate event-binding-js <FILE.arl|CONTRACT_ALIAS>");
print(" generate event-binding-ts <FILE.arl|CONTRACT_ALIAS>");
print(" generate binding-ts <FILE.arl|CONTRACT_ALIAS> [--input-path <PATH> --output-path <PATH>]");
print(" generate binding-dapp-ts <FILE.arl|CONTRACT_ALIAS> [--input-path <PATH> --output-path <PATH>] [--with-dapp-originate]");
print(" generate contract interface <FILE.arl|FILE.tz|CONTRACT_ALIAS>");
print("")
print(" show accounts");
print(" show account [--with-private-key] [--alias <ALIAS>]");
print(" show contracts");
print(" show contract <CONTRACT_ALIAS>");
print(" show entries <CONTRACT_ADDRESS>");
print(" rename contract <CONTRACT_ALIAS|CONTRACT_ADDRESS> by <CONTRACT_ALIAS> [--force]");
print(" remove contract <CONTRACT_ALIAS>");
print(" show url <CONTRACT_ALIAS>");
print(" show source <CONTRACT_ALIAS>");
print(" show address <CONTRACT_ALIAS|ACCOUNT_ALIAS>");
print(" show storage <CONTRACT_ALIAS|CONTRACT_ADDRESS> [--json]");
print(" show script <CONTRACT_ALIAS|CONTRACT_ADDRESS> [--json]");
print(" get balance for <ACCOUNT_NAME|ACCOUNT_ADDRESS>");
print("")
print(" log enable");
print(" log disable");
print(" log clear [--force]");
print(" log dump");
print("")
print(" create project <PROJECT_NAME>");
print(" get completium property <VALUE>");
}
async function initCompletium(options) {
const config = {
account: 'alice',
mode: {
archetype: 'js',
'tezos-client': 'binary'
},
bin: {
archetype: 'archetype',
'tezos-client': 'octez-client'
},
tezos: {
force_tezos_client: false,
network: 'ghost',
endpoint: 'https://ghostnet.ecadinfra.com',
list: [
{
network: 'main',
bcd_url: "https://better-call.dev/mainnet/${address}",
tzstat_url: "https://tzstats.com",
endpoints: [
'https://mainnet.api.tez.ie',
'https://mainnet.smartpy.io',
'https://mainnet.tezos.marigold.dev',
'https://mainnet-tezos.giganode.io',
'https://rpc.tzbeta.net'
],
sandbox_exec_address: "KT19wkxScvCZghXQbpZNaP2AwTJ63gBhdofE"
},
{
network: 'ghost',
bcd_url: "https://better-call.dev/ghostnet/${address}",
tzstat_url: "https://tzstats.com",
endpoints: [
'https://ghostnet.ecadinfra.com',
'https://ghostnet.smartpy.io',
'https://ghostnet.tezos.marigold.dev'
],
sandbox_exec_address: "KT1MS3bjqJHYkg4mEiRgVmfXGoGUHAdXUuLL"
},
{
"network": "nairobi",
"bcd_url": "https://better-call.dev/nairobinet/${address}",
"tzstat_url": "https://nairobi.tzstats.com",
"endpoints": [
"https://nairobinet.ecadinfra.com",
"https://nairobinet.smartpy.io",
"https://nairobinet.tezos.marigold.dev"
]
},
{
"network": "oxford",
"bcd_url": "https://better-call.dev/oxfordnet/${address}",
"tzstat_url": "https://oxford.tzstats.com",
"endpoints": [
"https://oxfordnet.ecadinfra.com",
"https://oxfordnet.smartpy.io",
"https://oxfordnet.tezos.marigold.dev"
]
},
{
network: "sandbox",
bcd_url: "https://localhost:8080/sandbox/${address}",
endpoints: [
"http://localhost:20000",
"http://localhost:8732"
]
},
{
network: "mockup",
bcd_url: "",
endpoints: [
"mockup"
]
}
]
}
};
const exists = fs.existsSync(config_path);
if (exists) {
const old_config = getConfig();
const old_account = old_config.account;
const old_bin = old_config.bin;
const old_mode = old_config.mode;
const new_config = { ...old_config, ...config };
if (old_account) {
new_config.account = old_account;
}
if (old_bin) {
new_config.bin = old_bin
}
if (old_mode) {
new_config.mode = old_mode
}
saveFile(config_path, new_config, (x => {
print("Completium updated successfully!")
}))
} else {
if (!fs.existsSync(bin_dir)) {
fs.mkdirSync(bin_dir, { recursive: true });
}
if (!fs.existsSync(contracts_dir)) {
fs.mkdirSync(contracts_dir, { recursive: true });
}
if (!fs.existsSync(scripts_dir)) {
fs.mkdirSync(scripts_dir, { recursive: true });
}
if (!fs.existsSync(sources_dir)) {
fs.mkdirSync(sources_dir, { recursive: true });
}
saveFile(config_path, config, (x => {
saveFile(accounts_path, {
accounts: [{
"name": "alice",
"pkh": "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb",
"pubk": "edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn",
"key": {
"kind": "private_key",
"value": "edsk3QoqBuvdamxouPhin7swCvkQNgq4jP5KZPbwWNnwdZpSpJiEbq"
}
},
{
"name": "bob",
"pkh": "tz1aSkwEot3L2kmUvcoxzjMomb9mvBNuzFK6",
"pubk": "edpkurPsQ8eUApnLUJ9ZPDvu98E8VNj4KtJa1aZr16Cr5ow5VHKnz4",
"key": {
"kind": "private_key",
"value": "edsk3RFfvaFaxbHx8BMtEW1rKQcPtDML3LXjNqMNLCzC3wLC1bWbAt"
}
},
{
"name": "carl",
"pubk": "edpkugep78JxqeTzJ6N2dvAUKBGdHrHVbytAzUHGLLHrfXweSzX2oG",
"pkh": "tz1aGDrJ58LbcnD47CkwSk3myfTxJxipYJyk",
"key": {
"kind": "private_key",
"value": "edskS8eMgJopZofUWiuzRTrQJPGRoR3mcYEhhp2BTpR91ZMjmvHMEdfoPFfGaiXSV9M1NG21r4zQcz5QYPY1BtqigMSrd8eVUv"
}
},
{
"name": "bootstrap1",
"pkh": "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx",
"pubk": "edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav",
"key": {
"kind": "private_key",
"value": "edsk3gUfUPyBSfrS9CCgmCiQsTCHGkviBDusMxDJstFtojtc1zcpsh"
}
},
{
"name": "bootstrap2",
"pkh": "tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN",
"pubk": "edpktzNbDAUjUk697W7gYg2CRuBQjyPxbEg8dLccYYwKSKvkPvjtV9",
"key": {
"kind": "private_key",
"value": "edsk39qAm1fiMjgmPkw1EgQYkMzkJezLNewd7PLNHTkr6w9XA2zdfo"
}
},
{
"name": "bootstrap3",
"pkh": "tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUzU",
"pubk": "edpkuTXkJDGcFd5nh6VvMz8phXxU3Bi7h6hqgywNFi1vZTfQNnS1RV",
"key": {
"kind": "private_key",
"value": "edsk4ArLQgBTLWG5FJmnGnT689VKoqhXwmDPBuGx3z4cvwU9MmrPZZ"
}
},
{
"name": "bootstrap4",
"pkh": "tz1b7tUupMgCNw2cCLpKTkSD1NZzB5TkP2sv",
"pubk": "edpkuFrRoDSEbJYgxRtLx2ps82UdaYc1WwfS9sE11yhauZt5DgCHbU",
"key": {
"kind": "private_key",
"value": "edsk2uqQB9AY4FvioK2YMdfmyMrer5R8mGFyuaLLFfSRo8EoyNdht3"
}
},
{
"name": "bootstrap5",
"pkh": "tz1ddb9NMYHZi5UzPdzTZMYQQZoMub195zgv",
"pubk": "edpkv8EUUH68jmo3f7Um5PezmfGrRF24gnfLpH3sVNwJnV5bVCxL2n",
"key": {
"kind": "private_key",
"value": "edsk4QLrcijEffxV31gGdN2HU7UpyJjA8drFoNcmnB28n89YjPNRFm"
}
}]
}, (y => {
saveFile(contracts_path, { contracts: [] }, (z => { print("Completium initialized successfully!") }));
}))
}));
}
}
async function doInstall(options) {
const bin = options.bin;
const verbose = options.verbose;
check_bin_archetype(bin);
try {
const { stdout } = await execa('docker', ['pull', `completium/archetype:latest`], {});
if (verbose) {
print(stdout);
}
print(stdout)
} catch (error) {
print(error);
throw error;
}
}
function print_flextesa_message() {
print('This feature is deprecated. Please use Flextesa externally. For more details, visit https://tezos.gitlab.io/flextesa/');
}
async function startSandbox(options) {
print_flextesa_message();
// const verbose = options.verbose;
// print('Waiting for sandbox to start ...');
// try {
// const { stdout } = await execa('docker', ['run', '--rm', '--name', 'my-sandbox', '--detach', '-p', '20000:20000', '--cpus', '1', '-e', 'block_time=10',
// '-e', "flextesa_node_cors_origin='*'", docker_id, flextesa_script, 'start'], {});
// if (verbose) {
// print(stdout);
// }
// print('sandbox is running');
// return stdout;
// } catch (error) {
// print(error);
// throw error;
// }
}
async function stopSandbox(options) {
print_flextesa_message();
// const verbose = options.verbose;
// try {
// const { stdout } = await execa('docker', ['kill', 'my-sandbox'], {});
// if (verbose) {
// print(stdout);
// }
// print('sandbox is stopped');
// return stdout;
// } catch (error) {
// print(error);
// throw error;
// }
}
function get_event_well_script() {
return `{ storage unit; parameter (bytes %event); code { UNPAIR; DROP; NIL operation; PAIR } }`
}
function get_sandbox_exec_script() {
return `{ storage unit; parameter (pair (list (ticket (pair nat (option bytes)))) (lambda (list (ticket (pair nat (option bytes)))) (list operation))); code { UNPAIR; UNPAIR; EXEC; PAIR} }`
}
export function get_sandbox_exec_address() {
const config = getConfig()
const network = config.tezos.list.find(x => x.network === config.tezos.network)
return fetch_sandbox_exec_address_from_network(network)
}
async function deploy_contract(contract_name, script) {
await callTezosClient(["originate", "contract", contract_name,
"transferring", "0", "from", "bootstrap1",
"running", script, "--init", "Unit",
"--burn-cap", "20", "--force", "--no-print-source"]);
const path_contracts = mockup_path + "/contracts";
const inputContracts = fs.readFileSync(path_contracts, 'utf8');
const cobj = JSON.parse(inputContracts);
const o = cobj.find(x => { return (x.name === contract_name) });
const contract_address = isNull(o) ? null : o.value;
return contract_address
}
export async function mockupInit(options) {
setEndpoint({ endpoint: "mockup" })
const protocol = options.protocol ? options.protocol : default_mockup_protocol
const config = getConfig();
fs.rmSync(mockup_path, { force: true, recursive: true });
const { stdout } = await execa(config.bin['tezos-client'], [
'--protocol', protocol,
'--base-dir', mockup_path,
'--mode', 'mockup',
'create', 'mockup'
], {});
print(stdout);
print("Importing account ...");
const importAccount = async (name, key) => {
print(`Importing ${name}`)
const uri = "unencrypted:" + key.value;
await callTezosClient(["import", "secret", "key", name, uri, "--force"]);
};
const transferAccount = async (name, pkh) => {
if (name !== "bootstrap1" && name !== "bootstrap2" && name !== "bootstrap3" && name !== "bootstrap4" && name !== "bootstrap5") {
print(`Transfer ${pkh}`)
await callTezosClient(["transfer", "10000", "from", "bootstrap1", "to", pkh, "--burn-cap", "0.06425"]);
}
};
const accounts = getAccounts().accounts;
for (const x of accounts) {
const name = x.name;
const pkh = x.pkh;
const key = x.key;
await importAccount(name, key);
await transferAccount(name, pkh);
}
const event_well_contract_name = 'event_well'
const event_well_script = get_event_well_script()
const sandbox_exec_contract_name = 'sandbox_exec'
const sandbox_exec_script = get_sandbox_exec_script()
// deploy contracts
const event_well_address = await deploy_contract(event_well_contract_name, event_well_script)
const sandbox_exec_address = await deploy_contract(sandbox_exec_contract_name, sandbox_exec_script)
const mockupConf = getMockupConfig();
saveMockupConfig({ ...mockupConf, event_well: event_well_address, sandbox_exec: sandbox_exec_address })
}
async function showVersion(options) {
print(version);
}
async function showArchetypeVersion(options) {
const vers = await getArchetypeVersion();
print(vers);
}
async function showEndpoint(options) {
var config = getConfig();
print("Current network: " + config.tezos.network);
print("Current endpoint: " + config.tezos.endpoint);
}
async function switchEndpoint(options) {
showEndpoint(options);
var config = getConfig();
var res = {
answers: [],
indexes: [],
networks: [],
endpoints: []
};
config.tezos.list.forEach(x => {
const network = x.network;
x.endpoints.forEach(y => {
res.answers.push(`${network.padEnd(10)} ${y}`);
res.indexes.push(`${network.padEnd(10)} ${y}`);
res.networks.push(network);
res.endpoints.push(y);
});
});
const prompt = new Select({
name: 'color',
message: 'Switch endpoint',
choices: res.answers,
});
prompt.run()
.then(answer => {
var i = res.indexes.indexOf(answer);
const config = getConfig();
config.tezos.network = res.networks[i];
config.tezos.endpoint = res.endpoints[i];
saveConfig(config, x => { print("endpoint updated") });
})
.catch(console.error);
}
async function addEndpoint(options) {
const network = options.network_;
const endpoint = options.endpoint;
const config = getConfig();
const cnetwork = config.tezos.list.find(x => x.network === network);
if (isNull(cnetwork)) {
const networks = config.tezos.list.map(x => x.network);
return print(`'${network}' is not found, expecting one of ${networks}.`)
}
if (cnetwork.endpoints.includes(endpoint)) {
return print(`'${endpoint}' is already registered.`)
}
cnetwork.endpoints.push(endpoint);
config.tezos.list = config.tezos.list.map(x => x.network == network ? cnetwork : x);
saveConfig(config, x => { print(`endpoint '${endpoint}' for network ${network} registered.`) });
}
export function setEndpoint(options) {
const endpoint = options.endpoint;
const config = getConfig();
const network = config.tezos.list.find(x => x.endpoints.includes(endpoint));
if (isNull(network)) {
throw new Error(`'${endpoint}' is not found.`);
}
config.tezos.network = network.network;
config.tezos.endpoint = endpoint;
saveConfig(config);
print(`endpoint '${endpoint}' for network ${network.network} set.`);
}
async function removeEndpoint(options) {
const endpoint = options.endpoint;
const config = getConfig();
const network = config.tezos.list.find(x => x.endpoints.includes(endpoint));
if (isNull(network)) {
return print(`'${endpoint}' is not found.`);
}
if (config.tezos.endpoint === endpoint) {
return print(`Cannot remove endpoint '${endpoint}' because it is currently set as the default endpoint. Switch to another endpoint before removing.`);
}
const l = config.tezos.list.map(x =>
({
...x,
endpoints: x.endpoints.filter(e => { return (e !== endpoint) })
})
);
config.tezos.list = l;
saveConfig(config, x => { print(`'${endpoint}' is removed, configuration file updated.`) });
}
function check_bin(bin) {
if (bin !== 'tezos-client' && bin !== 'archetype') {
const msg = `Invalid binary "${bin}", expecting 'tezos-client' or 'archetype'`;
throw msg;
}
}
function check_bin_archetype(bin) {
if (bin !== 'archetype') {
const msg = `Invalid binary "${bin}", expecting 'archetype'`;
throw msg;
}
}
function check_mode_value(mode) {
if (mode !== 'js' && mode !== 'docker' && mode !== 'binary') {
const msg = `Invalid mode ${mode}, expecting 'js' 'docker' or 'binary'`;
throw msg;
}
}
async function setMode(options) {
const bin = options.bin;
const mode = options.value;
check_bin_archetype(bin)
check_mode_value(mode)
const config = getConfig();
config.mode[bin] = mode;
saveConfig(config, x => { print(`${bin} mode set to ${mode}`) })
}
async function switchMode(options) {
const bin = options.bin;
check_bin_archetype(bin)
const config = getConfig();
const mode = config.mode[bin];
print(`Current ${bin} mode: ${mode}`);
const prompt = new Select({
name: 'color',
message: `Switch ${bin} mode`,
choices: ['js', 'docker', 'binary'],
});
prompt.run()
.then(mode => {
config.mode[bin] = mode;
saveConfig(config, x => { print(`${bin} mode set to ${mode}`) })
})
.catch(console.error);
}
async function showMode(options) {
const bin = options.bin;
check_bin(bin)
const config = getConfig();
const mode = config.mode[bin];
print(`${bin} mode: ${mode}`)
}
async function setBinPath(options) {
const bin = options.bin;
const path = options.value;
check_bin(bin)
const config = getConfig();
config.bin[bin] = path;
saveConfig(config, x => { print(`'${bin}' binary path set to ${path}`) })
}
async function showBinPath(options) {
const bin = options.bin;
check_bin(bin)
const config = getConfig();
const path = config.bin[bin];
print(`${bin} binary path: ${path}`)
}
async function confirmAccount(force, account) {
if (force || isNull(getAccount(account))) { return true }
const str = `${account} already exists, do you want to overwrite?`;
return new Promise(resolve => { askQuestionBool(str, answer => { resolve(answer); }) });
}
async function generateAccount(options) {
const alias = options.value;
const force = options.force;
var confirm = await confirmAccount(force, alias);
if (!confirm) {
return;
}
const mnemonic = bip39.generateMnemonic(256);
const seed = bip39.mnemonicToSeedSync(mnemonic);
const buffer = seed.slice(0, 32);
// const buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
const privateK = taquitoUtils.b58cencode(buffer, taquitoUtils.prefix.edsk2);
const signer_ = await signer.InMemorySigner.fromSecretKey(privateK);
const pubk = await signer_.publicKey();
const pkh = await signer_.publicKeyHash();
const prik = await signer_.secretKey();
saveAccountWithId(alias, pubk, pkh, prik);
}
async function saveAccountWithId(alias, pubk, pkh, prik) {
saveAccount({ name: alias, pubk: pubk, pkh: pkh, key: { kind: 'private_key', value: prik } },
x => { print(`Account ${pkh} is registered as '${alias}'.`) });
}
async function importAccount(kind, options) {
const value = options.value;
const alias = options.account;
const force = options.force;
const with_tezos_client = options.with_tezos_client;
var confirm = await confirmAccount(force, alias);
if (!confirm) {
return;
}
const config = getConfig();
const tezos = new taquito.TezosToolkit(import_endpoint);
switch (kind) {
case "faucet":
const faucet = loadJS(value);
print(`Importing key ...`);
const secret = faucet.secret ? faucet.secret : (faucet.activation_code ? faucet.activation_code : "");
await signer.importKey(tezos,
faucet.email,
faucet.password,
faucet.mnemonic.join(' '),
secret)
.catch(console.error);
break;
case "privatekey":
tezos.setProvider({
signer: new signer.InMemorySigner(value),
});
break;
default:
break;
}
const pubk = await tezos.signer.publicKey();
const pkh = await tezos.signer.publicKeyHash();
const sk = await tezos.signer.secretKey();
saveAccountWithId(alias, pubk, pkh, sk);
if (with_tezos_client || isMockupMode()) {
const args = ["import", "secret", "key", alias, ("unencrypted:" + sk)];
await callTezosClient(args);
if (isMockupMode()) {
await callTezosClient(["transfer", "10000", "from", "bootstrap1", "to", pkh, "--burn-cap", "0.06425"]);
}
}
}
async function importFaucet(options) {
importAccount("faucet", options);
}
async function importPrivatekey(options) {
importAccount("privatekey", options);
}
export async function getKeysFrom(sk) {
const signer = new signer.InMemorySigner(sk);
const pk = await tezos.signer.publicKey();
const pkh = await tezos.signer.publicKeyHash();
const sk2 = await tezos.signer.secretKey();
return {
pkh: pkh,
pk: pk,
sk: sk2,
}
}
async function showKeysFrom(options) {
const value = options.value;
const config = getConfig();
const tezos_endpoint = config.tezos.endpoint;
const tezos = new taquito.TezosToolkit(tezos_endpoint);
tezos.setProvider({
signer: new signer.InMemorySigner(value),
});
const pubk = await tezos.signer.publicKey();
const pkh = await tezos.signer.publicKeyHash();
const prik = await tezos.signer.secretKey();
showKeyInfo(pubk, pkh, prik);
}
async function showAccounts(options) {
const accounts = getAccounts();
accounts.accounts.forEach(x => {
print(`${x.name.padEnd(30)}\t\t${x.pkh}`);
});
}
async function showAccount(options) {
const config = getConfig();
const alias = options.alias;
let value = config.account;
const withPrivateKey = options.withPrivateKey;
if (!isNull(alias)) {
value = alias
}
if (isNull(value)) {
print("No account is set.");
} else {
const account = getAccount(value);
if (isNull(account)) {
return print(`'${value}' is not found.`);
}
print(`Current account:\t${account.name}`);
showKeyInfo(account.pubk, account.pkh, withPrivateKey ? account.key.value : null);
var balance = await retrieveBalanceFor(account.pkh);
print(`Balance on ${config.tezos.network}:\t${balance.toNumber() / 1000000} ꜩ`);
}
}
async function showKeyInfo(pubk, pkh, prik) {
print(`Public key hash:\t${pkh}`);
print(`Public key:\t\t${pubk}`);
if (prik) {
print(`Private key:\t\t${prik}`);
}
}
async function switchAccount(options) {
const config = getConfig();
if (!isNull(config.account)) {
print(`Current account: ${config.account}`);
}
const a = getAccounts();
var res = a.accounts.reduce(((accu, x) => ({
answers: accu.answers.concat(`${x.name.padEnd(60)} ${x.pkh}`),
indexes: accu.indexes.concat(`${x.name.padEnd(60)} ${x.pkh}`),
values: accu.values.concat(x.name)
})),
{
answers: [],
indexes: [],
values: []
});
const prompt = new Select({
name: 'color',
message: 'Switch account',
choices: res.answers,
});
prompt.run()
.then(answer => {
var i = res.indexes.indexOf(answer);
const value = res.values[i];
config.account = value;
saveConfig(config, x => { print("account updated") });
})
.catch(console.error);
}
export function setAccount(options) {
const value = options.account;
const account = getAccount(value);
if (isNull(account)) {
throw new Error(`'${value}' is not found.`);
}
const config = getConfig();
config.account = value;
saveConfig(config)
print(`'${value}' is set as current account.`);
}
async function renameAccount(options) {
const from = options.from;
const to = options.to;
const force = options.force;
const accountFrom = getAccountFromIdOrAddr(from);
if (isNull(accountFrom)) {
return print(`'${from}' is not found.`);
}
const config = getConfig();
if (config.account === from) {
return print(`Cannot rename account '${from}' because it is currently set as the default account. Switch to another account before renaming.`);
}
var confirm = await confirmAccount(force, to);
if (!confirm) {
return;
}
var f = function () { renameAccountInternal(from, to, x => { print(`account '${accountFrom.pkh}' has been renamed from '${accountFrom.name}' to '${to}'.`) }) };
const accountTo = getAccount(to);
if (!isNull(accountTo)) {
removeAccountInternal(to, y => { f() });
} else {
f();
}
}
async function removeAccount(options) {
const value = options.account;
const account = getAccount(value);
if (isNull(account)) {
return print(`'${value}' is not found.`);
}
const config = getConfig();
if (config.account === value) {
return print(`Cannot remove account '${value}' because it is currently set as the default account. Switch to another account before removing.`);
}
removeAccountInternal(value, x => { print(`'${value}' is removed.`) });
}
async function confirmTransfer(force, amount, from, to) {
if (force) { return true }
const config = getConfig();
const str = `Confirm transfer ${amount / 1000000} ꜩ from ${from.name} to ${to} on ${config.tezos.network}?`;
return new Promise(resolve => { askQuestionBool(str, answer => { resolve(answer); }) });
}
async function setContractAddress(options) {
const name = options.name;
const address = options.value;
if (isMockupMode()) {
const msg = `Cannot set contract in mockup mode.`;
return new Promise((resolve, reject) => { reject(msg) });
}
let field = undefined;
switch (name) {
case "sandbox_exec":
field = "sandbox_exec_address"
break;
default:
const msg = `Unknown contract ${name}.`;
return new Promise((resolve, reject) => { reject(msg) });
}
const config = getConfig();
let network = config.tezos.list.find(x => x.network === config.tezos.network);
network[field] = address;
saveConfig(config, x => { print(`${name} contract saved as ${address} for network ${config.tezos.network}`) });
}
async function printContract(options) {
const name = options.name;
let script = undefined;
switch (name) {
case "sandbox_exec":
script = get_sandbox_exec_script()
break;
default:
const msg = `Unknown contract ${name}.`;
return new Promise((resolve, reject) => { reject(msg) });
}
print(script)
}
export async function transfer(options) {
const amount_raw = options.vamount;
const from_raw = options.from;
const to_raw = options.to;
const force = options.force;
const amount = getAmount(amount_raw);
if (isNull(amount)) {
return;
}
const accountFrom = getAccountFromIdOrAddr(from_raw);
if (isNull(accountFrom)) {
print(`'${from_raw}' is not found.`);
return;
}
var accountTo = getAccountFromIdOrAddr(to_raw);
if (isNull(accountTo) && !to_raw.startsWith('tz')) {
print(`'${to_raw}' bad account or address.`);
return;
}
const to = isNull(accountTo) ? to_raw : accountTo.name;
var confirm = await confirmTransfer(force, amount, accountFrom, to);
if (!confirm) {
return;
}
const to_addr = isNull(accountTo) ? to : accountTo.pkh;
const config = getConfig();
const network = config.tezos.list.find(x => x.network === config.tezos.network);
print(`Transfering ${amount / 1000000} ꜩ from ${accountFrom.pkh} to ${to_addr}...`);
if (isMockupMode()) {
const a = (amount / 1000000).toString();
const args = ["transfer", a, "from", accountFrom.pkh, "to", to_addr, "--burn-cap", "0.06425"];
const { stdout, stderr, failed } = await callTezosClient(args);
if (failed) {
return new Promise((resolve, reject) => { reject(stderr) });
} else {
print(stdout);
}
return new Promise(resolve => { resolve(null) });
} else {
const tezos = getTezos(accountFrom.name);
return new Promise((resolve, reject) => {
tezos.contract
.transfer({ to: to_addr, amount: amount, mutez: true })
.then((op) => {
print(`Waiting for ${op.hash} to be confirmed...`);
op.confirmation(1)
.then((hash) => {
const op_inj = network.tzstat_url === undefined ? `${hash}` : `${network.tzstat_url}/${hash}`
print(`Operation injected: ${op_inj}`);
resolve(op);
})
.catch((error) => {
reject(`Error: ${error} ${JSON.stringify(error, null, 2)}`);
});
})
.catch((error) => {
reject(`Error: ${error} ${JSON.stringify(error, null, 2)}`);
});
});
}
}
async function confirmContract(force, id) {
if (force || isNull(getContract(id))) { return true }
const str = `${id} already exists, overwrite it?`;
return new Promise(resolve => { askQuestionBool(str, answer => { resolve(answer); }) });
}
async function copySource(arl, ext, contract_name) {
return new Promise(resolve => {
fs.readFile(arl, 'utf8', (err, data) => {
const source_path = sources_dir + '/' + contract_name + "." + ext;
fs.writeFile(source_path, data, (err) => {
if (err) throw err;
resolve(source_path);
});
});
});
}
async function copyContract(data, contract_name) {
return new Promise(resolve => {
const contract_path = contracts_dir + '/' + contract_name + ".tz";
fs.writeFile(contract_path, data, (err) => {
if (err) throw err;
resolve(contract_path);
});
});
}
function is_number(v) {
try {
const bigNumber = new BigNumber(v);
if (bigNumber.isNaN()) {
return false;
}
return true;
} catch (e) {
return false;
}
}
function build_from_js(type, jdata) {
const encode = (schema, type, jdata) => {
try {
return schema.Encode(jdata);
} catch (e) {
throw {
message: 'Typecheck error',
data: jdata,
type: json_micheline_to_expr(type)
}
}
}
if (type.prim !== undefined) {
const schema = new encoder.Schema(type);
const prim = type.prim;
switch (prim) {
case 'address':
case 'bls12_381_fr':
case 'bls12_381_g1':
case 'bls12_381_g2':
case 'bool':
case 'chain_id':
case 'contract':
case 'int':
case 'key':
case 'key_hash':
case 'lambda':
case 'nat':
case 'never':
case 'operation':
case 'sapling_state':
case 'sapling_transaction':
case 'signature':
case 'string':
case 'ticket':
case 'unit':
return encode(schema, type, jdata)
case 'bytes':
case 'chest':
case 'chest_key':
let bdata = jdata;
if (bdata.startsWith && bdata.startsWith("0x")) {
bdata = bdata.substring(2);
}
return { bytes: bdata }
case 'mutez':
if (typeof jdata === "string" && jdata.endsWith("tz")) {
const v = getAmount(jdata);
return { "int": v.toString() }
} else {
return encode(schema, type, jdata)
}
case 'timestamp':
let vdate;
if (is_number(jdata)) {
vdate = jdata.toString();
} else {
const toTimestamp = (strDate) => {
var datum = Date.parse(strDate);
return (datum / 1000).toString();
}
vdate = toTimestamp(jdata);
}
return encode(schema, type, vdate)
case 'big_map':
case 'map':
const kmtype = type.args[0];
const vmtype = type.args[1];
if (jdata instanceof Array) {
const mdata = jdata.map((x) => {
if (x.key === undefined ||