UNPKG

@taqueria/plugin-taquito

Version:

A taqueria plugin for originating smart contracts using Taquito

615 lines (605 loc) • 26.2 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_signer.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_signer.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) { 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 https://teztnets.xyz and click "Faucet" of the target testnet 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)` ); } } } 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