UNPKG

@taqueria/plugin-taquito

Version:

A taqueria plugin for originating smart contracts using Taquito

618 lines (607 loc) • 26.3 kB
"use strict"; // index.ts var import_node_sdk7 = require("@taqueria/node-sdk"); // main.ts var import_node_sdk6 = require("@taqueria/node-sdk"); // fund.ts var import_node_sdk3 = require("@taqueria/node-sdk"); // common.ts var import_node_sdk = require("@taqueria/node-sdk"); var import_signer = require("@taquito/signer"); var import_taquito = require("@taquito/taquito"); var getEnvTypeAndNodeConfig = (parsedArgs, env) => { var _a, _b, _c, _d; const targetConstraintErrMsg = "Each environment can only have one target, be it a network or a sandbox"; if (((_a = env.networks) == null ? void 0 : _a.length) === 1 && ((_b = env.sandboxes) == null ? void 0 : _b.length) === 1) return (0, import_node_sdk.sendAsyncErr)(targetConstraintErrMsg); if (((_c = env.networks) == null ? void 0 : _c.length) === 1) { const networkName = env.networks[0]; const network = (0, import_node_sdk.getNetworkConfig)(parsedArgs)(networkName); if (!network) { return (0, import_node_sdk.sendAsyncErr)( `The current environment is configured to use a network called '${networkName}'; however, no network of this name has been configured in .taq/config.json` ); } return Promise.resolve(["Network", network]); } if (((_d = env.sandboxes) == null ? void 0 : _d.length) === 1) { const sandboxName = env.sandboxes[0]; const sandbox = (0, import_node_sdk.getSandboxConfig)(parsedArgs)(sandboxName); if (!sandbox) { return (0, import_node_sdk.sendAsyncErr)( `The current environment is configured to use a sandbox called '${sandboxName}'; however, no sandbox of this name has been configured in .taq/config.json` ); } return Promise.resolve(["Sandbox", sandbox]); } return (0, import_node_sdk.sendAsyncErr)(targetConstraintErrMsg); }; var configureToolKitForSandbox = async (sandbox, sender) => { let accountKey; if (sender && sender !== "default") { const accounts = getSandboxInstantiatedAccounts(sandbox); if (accounts.hasOwnProperty(sender)) { accountKey = accounts[sender].secretKey; } else { return (0, import_node_sdk.sendAsyncErr)( `${sender} is not an account instantiated in the current environment. Check .taq/config.json` ); } } else { const defaultAccount = (0, import_node_sdk.getDefaultSandboxAccount)(sandbox); if (!defaultAccount) { return (0, import_node_sdk.sendAsyncErr)( `No default account is specified in the sandbox to perform the operation. Please use the --sender flag to explicitly specify the account to use as the sender of the operation` ); } accountKey = defaultAccount.secretKey; } const tezos = new import_taquito.TezosToolkit(sandbox.rpcUrl); tezos.setProvider({ signer: new import_signer.InMemorySigner(accountKey.replace(/^unencrypted:/, "")) }); return tezos; }; var configureToolKitForNetwork = async (parsedArgs, network, sender) => { let account; if (sender && sender !== import_node_sdk.TAQ_OPERATOR_ACCOUNT) { const accounts = getNetworkInstantiatedAccounts(network); if (accounts.hasOwnProperty(sender)) { account = sender; } else { return (0, import_node_sdk.sendAsyncErr)( `${sender} is not an account instantiated in the current environment. Check .taq/config.json` ); } } else { account = import_node_sdk.TAQ_OPERATOR_ACCOUNT; } const tezos = new import_taquito.TezosToolkit(network.rpcUrl); const key = await (0, import_node_sdk.getAccountPrivateKey)(parsedArgs, network, account); await (0, import_taquito.importKey)(tezos, key); return tezos; }; var getDeclaredAccounts = (parsedArgs) => Object.entries(parsedArgs.config.accounts ?? {}).reduce( (acc, declaredAccount) => { const alias = declaredAccount[0]; const mutez = declaredAccount[1]; return { ...acc, [alias]: typeof mutez === "string" ? parseFloat(mutez.replaceAll("_", "")) : mutez }; }, {} ); var getSandboxInstantiatedAccounts = (sandbox) => (sandbox == null ? void 0 : sandbox.accounts) ? Object.entries(sandbox.accounts).reduce( (acc, instantiatedAccount) => { const alias = instantiatedAccount[0]; const keys = instantiatedAccount[1]; return alias !== "default" ? { ...acc, [alias]: keys } : acc; }, {} ) : {}; var getNetworkInstantiatedAccounts = (network) => network.accounts ? Object.entries(network.accounts).reduce( (acc, instantiatedAccount) => { const alias = instantiatedAccount[0]; const keys = instantiatedAccount[1]; return alias !== import_node_sdk.TAQ_OPERATOR_ACCOUNT ? { ...acc, [alias]: keys } : acc; }, {} ) : {}; var generateAccountKeys = async (parsedArgs, network, account) => { const tezos = new import_taquito.TezosToolkit(network.rpcUrl); const key = await (0, import_node_sdk.getAccountPrivateKey)(parsedArgs, network, account); await (0, import_taquito.importKey)(tezos, key); }; var handleOpsError = (err, env) => { if (err instanceof Error) { const msg = err.message; if (/ENOTFOUND/.test(msg)) return (0, import_node_sdk.sendAsyncErr)("The RPC URL may be invalid. Check ./.taq/config.json"); if (/ECONNREFUSED/.test(msg)) return (0, import_node_sdk.sendAsyncErr)("The RPC URL may be down or the sandbox is not running"); if (/empty_implicit_contract/.test(msg)) { const result = msg.match(/(?<="implicit":")tz[^"]+(?=")/); const publicKeyHash = result ? result[0] : void 0; if (publicKeyHash) { const faucetUrl = (0, import_node_sdk.getFaucetUrl)(env); return (0, import_node_sdk.sendAsyncErr)( `The account ${publicKeyHash} for the target environment, "${env}", may not be funded To fund this account: 1. Go to ${faucetUrl} 2. Copy and paste the above key into the wallet address field 3. Request some Tez (Note that you might need to wait for a few seconds for the network to register the funds) Alternatively, use --sender <accountAlias> if you have an already-funded account.` ); } } } return (0, import_node_sdk.sendAsyncErr)(`Error while performing operation: ${err} ${JSON.stringify(err, null, 2)}`); }; var doWithin = async (seconds, fn) => { let captured = new Error( "Operation timed out. Please try again and perhaps increase the timeout using the --timeout option." ); let timeout; const maxTimeout = new Promise((resolve, reject) => { timeout = setTimeout(() => { reject(captured); }, seconds * 1e3); }); const process2 = async () => { while (true) { try { const retval = await fn(); return retval; } catch (err) { captured = err; } } }; return Promise.race([process2(), maxTimeout]).then((retval) => { clearTimeout(timeout); return retval; }); }; // transfer.ts var import_node_sdk2 = require("@taqueria/node-sdk"); var import_michel_codec = require("@taquito/michel-codec"); var isContractAddress = (contract) => contract.startsWith("tz1") || contract.startsWith("tz2") || contract.startsWith("tz3") || contract.startsWith("KT1"); var getContractInfo = async (parsedArgs, env) => { const contract = parsedArgs.contract; const protocolArgs = import_node_sdk2.RequestArgs.create(parsedArgs); return { contractAlias: isContractAddress(contract) ? "N/A" : contract, contractAddress: isContractAddress(contract) ? contract : await (0, import_node_sdk2.getAddressOfAlias)(env, contract), parameter: parsedArgs.param ? await (0, import_node_sdk2.getParameter)(protocolArgs, parsedArgs.param) : "Unit", entrypoint: parsedArgs.entrypoint ?? "default", mutezTransfer: parseInt(parsedArgs.mutez ?? "0") }; }; var createBatchForTransfer = (tezos, contractsInfo, gasLimit, storageLimit, fee) => contractsInfo.reduce((acc, contractInfo) => acc.withTransfer({ fee, gasLimit, storageLimit, to: contractInfo.contractAddress, amount: contractInfo.mutezTransfer, parameter: { entrypoint: contractInfo.entrypoint, value: new import_michel_codec.Parser().parseMichelineExpression(contractInfo.parameter) }, mutez: true }), tezos.wallet.batch()); var performTransferOps = async (tezos, env, contractsInfo, maxTimeout, gasLimit, storageLimit, fee) => { const batch = createBatchForTransfer(tezos, contractsInfo, gasLimit, storageLimit, fee); try { return await doWithin(maxTimeout, async () => { const op = await batch.send(); await op.confirmation(); return op; }); } catch (err) { return handleOpsError(err, env); } }; var prepContractInfoForDisplay = (tezos, contractInfo) => { return { contractAlias: contractInfo.contractAlias, contractAddress: contractInfo.contractAddress, parameter: contractInfo.parameter, entrypoint: contractInfo.entrypoint, mutezTransfer: contractInfo.mutezTransfer.toString(), destination: tezos.rpc.getRpcUrl() }; }; var transfer = async (opts) => { const protocolArgs = import_node_sdk2.RequestArgs.create(opts); const env = (0, import_node_sdk2.getCurrentEnvironmentConfig)(protocolArgs); if (!env) return (0, import_node_sdk2.sendAsyncErr)(`There is no environment called ${protocolArgs.env} in your config.json`); try { const [envType, nodeConfig] = await getEnvTypeAndNodeConfig(protocolArgs, env); const tezos = await (envType === "Network" ? configureToolKitForNetwork(protocolArgs, nodeConfig, opts.sender) : configureToolKitForSandbox(nodeConfig, opts.sender)); const contractInfo = await getContractInfo(opts, env); await performTransferOps( tezos, (0, import_node_sdk2.getCurrentEnvironment)(protocolArgs), [contractInfo], opts.timeout, opts.gasLimit, opts.storageLimit, opts.fee ); const contractInfoForDisplay = prepContractInfoForDisplay(tezos, contractInfo); return (0, import_node_sdk2.sendJsonRes)([contractInfoForDisplay]); } catch { return (0, import_node_sdk2.sendAsyncErr)("No operations performed"); } }; var transfer_default = transfer; // fund.ts var getAccountsInfo = (parsedArgs, tezos, instantiatedAccounts) => Promise.all( Object.entries(instantiatedAccounts).map(async (instantiatedAccount) => { const alias = instantiatedAccount[0]; const aliasInfo = instantiatedAccount[1]; const declaredMutez = getDeclaredAccounts(parsedArgs)[alias]; const currentBalanceInMutez = (await tezos.tz.getBalance(aliasInfo.publicKeyHash)).toNumber(); const amountToFillInMutez = declaredMutez ? Math.max(declaredMutez - currentBalanceInMutez, 0) : 0; if (!declaredMutez) { (0, import_node_sdk3.sendWarn)( `Warning: ${alias} is instantiated in the target environment but not declared in the root level "accounts" field of ./.taq/config.json so ${alias} will not be funded as you don't have a declared tez amount set there for ${alias} ` ); } return { contractAlias: alias, contractAddress: aliasInfo.publicKeyHash, parameter: "Unit", entrypoint: "default", mutezTransfer: parseInt(amountToFillInMutez.toString().replaceAll("_", "")) }; }) ).then((accountsInfo) => accountsInfo.filter((accountInfo) => accountInfo.mutezTransfer !== 0)).catch((err) => (0, import_node_sdk3.sendAsyncErr)(`Something went wrong while extracting account information - ${err}`)); var prepAccountsInfoForDisplay = (accountsInfo) => accountsInfo.map((accountInfo) => { return { accountAlias: accountInfo.contractAlias, accountAddress: accountInfo.contractAddress, mutezFunded: accountInfo.mutezTransfer.toString() }; }); var fund = async (parsedArgs) => { const env = (0, import_node_sdk3.getCurrentEnvironmentConfig)(parsedArgs); if (!env) return (0, import_node_sdk3.sendAsyncErr)(`There is no environment called ${parsedArgs.env} in your config.json`); try { const [envType, nodeConfig] = await getEnvTypeAndNodeConfig(parsedArgs, env); if (envType !== "Network") return (0, import_node_sdk3.sendAsyncErr)("taq fund can only be executed in a network environment"); const tezos = await configureToolKitForNetwork(parsedArgs, nodeConfig); const instantiatedAccounts = getNetworkInstantiatedAccounts(nodeConfig); const accountsInfo = await getAccountsInfo(parsedArgs, tezos, instantiatedAccounts); if (accountsInfo.length === 0) { return (0, import_node_sdk3.sendJsonRes)( `All instantiated accounts in the current environment, "${parsedArgs.env}", are funded up to or beyond the declared amount` ); } await performTransferOps(tezos, (0, import_node_sdk3.getCurrentEnvironment)(parsedArgs), accountsInfo, parsedArgs.timeout); const accountsInfoForDisplay = prepAccountsInfoForDisplay(accountsInfo); return (0, import_node_sdk3.sendJsonRes)(accountsInfoForDisplay); } catch { return (0, import_node_sdk3.sendAsyncErr)("No operations performed"); } }; var fund_default = fund; // instantiate_account.ts var import_node_sdk4 = require("@taqueria/node-sdk"); var instantiate_account = async (parsedArgs) => { const env = (0, import_node_sdk4.getCurrentEnvironmentConfig)(parsedArgs); if (!env) return (0, import_node_sdk4.sendAsyncErr)(`There is no environment called ${parsedArgs.env} in your config.json`); try { const [envType, nodeConfig] = await getEnvTypeAndNodeConfig(parsedArgs, env); if (envType !== "Network") { return (0, import_node_sdk4.sendAsyncErr)("taq instantiate-account can only be executed in a network environment"); } const declaredAccountAliases = Object.keys(getDeclaredAccounts(parsedArgs)); const instantiatedAccounts = getNetworkInstantiatedAccounts(nodeConfig); let accountsInstantiated = []; for (const declaredAccountAlias of declaredAccountAliases) { if (!instantiatedAccounts.hasOwnProperty(declaredAccountAlias)) { await generateAccountKeys(parsedArgs, nodeConfig, declaredAccountAlias); accountsInstantiated.push(declaredAccountAlias); } else { (0, import_node_sdk4.sendWarn)( `Note: ${declaredAccountAlias} is already instantiated in the current environment, "${parsedArgs.env}"` ); } } if (accountsInstantiated.length !== 0) { return (0, import_node_sdk4.sendJsonRes)( `Accounts instantiated: ${accountsInstantiated.join(", ")}. Please execute "taq fund" targeting the same environment to fund these accounts` ); } else { return (0, import_node_sdk4.sendJsonRes)( `No accounts were instantiated because they were all instantiated in the target environment already` ); } } catch (err) { await (0, import_node_sdk4.sendAsyncErr)("No operations performed"); if (parsedArgs.debug) await (0, import_node_sdk4.sendAsyncErr)(String(err)); } }; var instantiate_account_default = instantiate_account; // originate.ts var import_node_sdk5 = require("@taqueria/node-sdk"); var import_path = require("path"); var getDefaultStorageFilename = (contractName) => { const baseFilename = (0, import_path.basename)(contractName, (0, import_path.extname)(contractName)); const extFilename = (0, import_path.extname)(contractName); const defaultStorage = `${baseFilename}.default_storage${extFilename}`; return defaultStorage; }; var getContractInfo2 = async (parsedArgs) => { const contract = parsedArgs.contract; const protocolArgs = import_node_sdk5.RequestArgs.create(parsedArgs); const contractWithTzExtension = (0, import_node_sdk5.addTzExtensionIfMissing)(contract); const contractCode = await (0, import_node_sdk5.getContractContent)(protocolArgs, contractWithTzExtension); if (contractCode === void 0) { return (0, import_node_sdk5.sendAsyncErr)( `Please generate ${contractWithTzExtension} with one of the compilers (LIGO, SmartPy, Archetype) or write it manually and put it under /${parsedArgs.config.artifactsDir} ` ); } const storageFilename = parsedArgs.storage ?? getDefaultStorageFilename(contractWithTzExtension); const contractInitStorage = await (0, import_node_sdk5.getContractContent)(protocolArgs, storageFilename); if (contractInitStorage === void 0) { return (0, import_node_sdk5.sendAsyncErr)( `\u274C No initial storage file was found for ${contractWithTzExtension} Storage must be specified in a file as a Michelson expression and will automatically be linked to this contract if specified with the name "${getDefaultStorageFilename(contractWithTzExtension)}" in the artifacts directory You can also manually pass a storage file to the originate task using the --storage STORAGE_FILE_NAME option ` ); } return { contract, code: contractCode, initStorage: contractInitStorage.trim(), mutezTransfer: parseInt(parsedArgs.mutez ?? "0") }; }; var performOriginateOps = async (tezos, env, contractsInfo, maxTimeout, isSandbox = false, gasLimit, storageLimit, fee) => { try { const result = await tezos.wallet.originate({ code: contractsInfo.code, init: contractsInfo.initStorage, balance: contractsInfo.mutezTransfer.toString(), mutez: true, fee, gasLimit, storageLimit }); const op = await result.send(); await op.confirmation(isSandbox ? 1 : 3); return op; } catch (err) { return handleOpsError(err, env); } }; var prepContractInfoForDisplay2 = async (parsedArgs, tezos, contractInfo, op) => { var _a, _b, _c; const protocolArgs = import_node_sdk5.RequestArgs.create(parsedArgs); const operationResults = await op.operationResults(); const originationResults = (operationResults ?? []).filter((result2) => result2.kind === "origination").map((result2) => result2); const result = originationResults.length === 1 ? originationResults[0] : void 0; const address = (_c = (_b = (_a = result == null ? void 0 : result.metadata) == null ? void 0 : _a.operation_result) == null ? void 0 : _b.originated_contracts) == null ? void 0 : _c.join(","); const alias = parsedArgs.alias ?? (0, import_path.basename)(contractInfo.contract, (0, import_path.extname)(contractInfo.contract)); const displayableAddress = address ?? "Something went wrong during origination"; if (address) { const validatedAddress = import_node_sdk5.NonEmptyString.create(address); await (0, import_node_sdk5.updateAddressAlias)(protocolArgs, alias, validatedAddress); } return { contract: contractInfo.contract, address: displayableAddress, alias: address ? alias : "N/A" // destination: tezos.rpc.getRpcUrl(), }; }; var originate = async (parsedArgs) => { const protocolArgs = import_node_sdk5.RequestArgs.create(parsedArgs); const env = (0, import_node_sdk5.getCurrentEnvironmentConfig)(protocolArgs); if (!env) return (0, import_node_sdk5.sendAsyncErr)(`There is no environment called ${parsedArgs.env} in your config.json`); try { const [envType, nodeConfig] = await getEnvTypeAndNodeConfig(protocolArgs, env); const tezos = await (envType === "Network" ? configureToolKitForNetwork(protocolArgs, nodeConfig, parsedArgs.sender) : configureToolKitForSandbox(nodeConfig, parsedArgs.sender)); const contractInfo = await getContractInfo2(parsedArgs); const op = await performOriginateOps( tezos, (0, import_node_sdk5.getCurrentEnvironment)(protocolArgs), contractInfo, parsedArgs.timeout, envType !== "Network", parsedArgs.gasLimit, parsedArgs.storageLimit, parsedArgs.fee ); const contractInfoForDisplay = await prepContractInfoForDisplay2(parsedArgs, tezos, contractInfo, op); return (0, import_node_sdk5.sendJsonRes)([contractInfoForDisplay]); } catch (e) { if (e instanceof Error && e.message.includes("503")) { try { const [envType, nodeConfig] = await getEnvTypeAndNodeConfig(protocolArgs, env); if (envType === "Network" && nodeConfig.rpcUrl.includes("ghostnet")) { return (0, import_node_sdk5.sendAsyncErr)( `\u274C Ghostnet is returning 503 errors, indicating that the server is under heavy load. Please try again later.` ); } return (0, import_node_sdk5.sendAsyncErr)( `\u274C The node you are trying to connect to is not available Please check if the node is running and the URL is correct in your config.json` ); } catch { } } return (0, import_node_sdk5.sendAsyncErr)("No operations performed"); } }; var originate_default = originate; // main.ts var main = (parsedArgs) => { const unsafeArgs = parsedArgs; switch (unsafeArgs.task) { case "deploy": return originate_default(unsafeArgs); case "transfer": return transfer_default(unsafeArgs); case "instantiate-account": return instantiate_account_default(parsedArgs); case "fund": return fund_default(unsafeArgs); default: return (0, import_node_sdk6.sendAsyncErr)(`${unsafeArgs} is not an understood task by the Taquito plugin`); } }; var main_default = main; // index.ts import_node_sdk7.Plugin.create((_i18n) => ({ alias: "taquito", schema: "1.0", version: "0.1", tasks: [ import_node_sdk7.Task.create({ task: "deploy", command: "deploy <contract>", description: "Deploy a smart contract to a particular environment", options: [ import_node_sdk7.Option.create({ flag: "alias", description: "Alias used to refer to the deployed contract's address", required: false }), import_node_sdk7.Option.create({ flag: "storage", description: "Name of the storage file that contains the storage value as a Michelson expression, in the artifacts directory, used for originating a contract", required: false }), import_node_sdk7.Option.create({ flag: "sender", description: "Name of an instantiated account to use as the sender of the originate operation", required: false }), import_node_sdk7.Option.create({ flag: "mutez", description: "Amount of Mutez to transfer", required: false }), import_node_sdk7.Option.create({ flag: "timeout", shortFlag: "t", defaultValue: 40, description: "Number of seconds to elapse before abandoning the operation (to avoid congestion and network failures)", required: false }), import_node_sdk7.Option.create({ flag: "gasLimit", shortFlag: "g", description: "Gas limit per contract/origination specified in mutez", required: false }), import_node_sdk7.Option.create({ flag: "storageLimit", shortFlag: "s", description: "Storage limit per contract/origination specified in mutez", required: false }), import_node_sdk7.Option.create({ flag: "fee", shortFlag: "f", description: "Fee per contract/origination specified in mutez", required: false }) ], aliases: ["originate"], handler: "proxy", encoding: "application/json" }), import_node_sdk7.Task.create({ task: "transfer", command: "transfer <contract>", description: "Transfer/call an implicit account or a smart contract (specified via its alias or address) deployed to a particular environment", options: [ import_node_sdk7.Option.create({ flag: "mutez", description: "Amount of Mutez to transfer", required: false }), import_node_sdk7.Option.create({ flag: "param", description: "Name of the parameter file that contains the parameter value as a Michelson expression, in the artifacts directory, used for invoking a deployed contract", required: false }), import_node_sdk7.Option.create({ flag: "entrypoint", description: "You may explicitly specify an entrypoint to make the parameter value shorter, without having to specify a chain of (Left (Right ... 14 ...))", required: false }), import_node_sdk7.Option.create({ flag: "sender", description: "Name of an instantiated account to use as the sender of the transfer operation", required: false }), import_node_sdk7.Option.create({ flag: "timeout", shortFlag: "t", defaultValue: 40, description: "Number of retry attempts (to avoid congestion and network failures)", required: false }), import_node_sdk7.Option.create({ flag: "gasLimit", shortFlag: "g", description: "Gas limit per contract/origination specified in mutez", required: false }), import_node_sdk7.Option.create({ flag: "storageLimit", shortFlag: "s", description: "Storage limit per contract/origination specified in mutez", required: false }), import_node_sdk7.Option.create({ flag: "fee", shortFlag: "f", description: "Fee per contract/origination specified in mutez", required: false }) ], aliases: ["call"], handler: "proxy", encoding: "application/json" }), import_node_sdk7.Task.create({ task: "fund", command: "fund", description: "Fund all the instantiated accounts up to the desired/declared amount in a target environment", handler: "proxy", encoding: "application/json", options: [ import_node_sdk7.Option.create({ flag: "timeout", shortFlag: "t", defaultValue: 40, description: "Number of retry attempts (to avoid congestion and network failures)", required: false }) ] }), import_node_sdk7.Task.create({ task: "instantiate-account", command: "instantiate-account", description: 'Instantiate all accounts declared in the "accounts" field at the root level of the config file to a target environment', handler: "proxy", encoding: "application/json" }) ], proxy: main_default }), process.argv); //# sourceMappingURL=index.js.map