UNPKG

@completium/completium-cli

Version:
1,695 lines (1,477 loc) 156 kB
/*! * 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 ||