UNPKG

@simbachain/truffle

Version:
445 lines 21.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.deployContract = exports.handler = exports.builder = exports.describe = exports.command = void 0; const chalk_1 = __importDefault(require("chalk")); const axios_1 = __importDefault(require("axios")); const prompts_1 = __importDefault(require("prompts")); const web3_suites_1 = require("@simbachain/web3-suites"); exports.command = 'deploy'; exports.describe = 'deploy the project to SIMBAChain SCaaS'; exports.builder = { 'primary': { 'string': true, 'type': 'string', 'describe': 'optional - name of contract to be deployed' }, 'url': { 'string': true, 'type': 'string', 'describe': 'optional - url to deploy contract to', }, 'api': { 'string': true, 'type': 'string', 'describe': 'optional - the name of the api to deploy to', }, 'app': { 'string': true, 'type': 'string', 'describe': 'optional - the name of the app to deploy to', }, 'blockchain': { 'string': true, 'type': 'string', 'describe': 'optional - the name of the blockchain to deploy to', }, 'storage': { 'string': true, 'type': 'string', 'describe': 'optional - the name of the storage to deploy to', }, 'args': { 'string': true, 'type': 'string', 'describe': 'optional - arguments for the contract as a JSON dictionary', }, }; /** * deploy a contract to your organisation and application on blocks * @param args * args: * args.primary name of primary contract if user wants to skip the prompt to choose contract * args.deployInfo used to non-interactively deploy contract * @returns */ exports.handler = async (args) => { web3_suites_1.SimbaConfig.log.debug(`:: ENTER : args: ${JSON.stringify(args)}`); const primary = args.primary; const url = args.url; const api = args.primary; const app = args.app; const blockchain = args.blockchain; const deployArgs = args.args ? JSON.parse(args.args) : undefined; let deployInfo; if (url !== undefined && api !== undefined && app !== undefined && blockchain !== undefined && deployArgs !== undefined) { deployInfo = { url, api, app, blockchain, args: deployArgs, }; } await deployContract(primary, deployInfo); web3_suites_1.SimbaConfig.log.debug(`:: EXIT :`); return; }; /** * deploy a contract to your organisation and application on blocks * @param primary name of primary contract if user wants to skip the prompt to choose contract * @param deployInfo used to non-interactively deploy contract * @returns */ async function deployContract(primary, deployInfo) { const entryParams = { primary, deployInfo, }; web3_suites_1.SimbaConfig.log.debug(`:: ENTER : args: ${JSON.stringify(entryParams)}`); const config = new web3_suites_1.SimbaConfig(); if (!config.ProjectConfigStore.has("contracts_info")) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: EXIT : Please export your contracts first with "truffle run simba export".`)}`); return; } const blockchainList = await web3_suites_1.getBlockchains(config); const storageList = await web3_suites_1.getStorages(config); if (!config.application) { try { await web3_suites_1.chooseApplicationFromList(config); } catch (e) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: EXIT : ${JSON.stringify(e)}`)}`); return; } } const contractsInfo = web3_suites_1.SimbaConfig.ProjectConfigStore.get("contracts_info"); if (!contractsInfo || !Object.keys(contractsInfo).length) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: no contracts present in your contracts_info in simba.json. Did you forget to deploy contracts first by running ${chalk_1.default.greenBright(`$ truffle run simba export`)} ?`)}`); return; } let contractName; if (!deployInfo) { if (primary) { if (primary in contractsInfo) { web3_suites_1.SimbaConfig.ProjectConfigStore.set('primary', primary); contractName = primary; } else { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: EXIT : Primary contract ${primary} is not the name of a contract in this project`)}`); return; } } else { const choices = []; for (const [contractName, _] of Object.entries(contractsInfo)) { choices.push({ title: contractName, value: contractName }); } const response = await prompts_1.default({ type: 'select', name: 'contract_name', message: 'Please pick which contract you want to deploy', choices, }); if (!response.contract_name) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright('\nsimba: EXIT : No contract selected for deployment!')}`); throw new Error('No Contract Selected!'); } contractName = response.contract_name; web3_suites_1.SimbaConfig.ProjectConfigStore.set("primary", contractName); } } let chosen = {}; let deployArgs = {}; let id; let _isLibrary = false; let sourceCode; if (!deployInfo) { const contractInfo = contractsInfo[contractName]; sourceCode = contractInfo.source_code; const contractType = contractInfo.contract_type; _isLibrary = (contractType === "library") ? true : false; web3_suites_1.SimbaConfig.log.info(`${chalk_1.default.cyanBright(`\nsimba deploy: gathering info for deployment of contract ${chalk_1.default.greenBright(`${contractName}`)}`)}`); const questions = [ { type: 'text', name: 'api', message: `Please enter an API name for contract ${chalk_1.default.greenBright(`${contractName}`)} [^[w-]*$]`, validate: (str) => !!/^[\w-]*$/.exec(str), }, { type: 'select', name: 'blockchain', message: 'Please choose the blockchain to deploy to.', choices: blockchainList, initial: 0, }, { type: 'select', name: 'storage', message: 'Please choose the storage to use.', choices: storageList, initial: 0, }, ]; const constructorRequiresParams = await web3_suites_1.primaryConstructorRequiresArgs(); const paramInputQuestions = []; let inputNameToTypeMap = {}; let inputsAsJson = true; if (constructorRequiresParams) { const constructorInputs = await web3_suites_1.primaryConstructorInputs(); const allParamsByJson = "enter all params as json object"; const paramsOneByOne = "enter params one by one from prompts"; const paramInputChoices = [paramsOneByOne, allParamsByJson]; const paramChoices = []; for (let i = 0; i < paramInputChoices.length; i++) { const entry = paramInputChoices[i]; paramChoices.push({ title: entry, value: entry, }); } const promptChosen = await prompts_1.default({ type: 'select', name: 'input_method', message: 'Your constructor parameters can be input as either a single json object or one by one from prompts. Which would you prefer?', choices: paramChoices, }); if (!promptChosen.input_method) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: EXIT : no param input method chosen!`)}`); return; } if (promptChosen.input_method === allParamsByJson) { questions.push({ type: 'text', name: 'args', message: 'Please enter any arguments for the contract as a JSON dictionary.', validate: (contractArgs) => { if (!contractArgs) { return true; } // Allow empty strings try { JSON.parse(contractArgs); return true; } catch (_a) { return false; } }, }); } else { inputsAsJson = false; for (let i = 0; i < constructorInputs.length; i++) { const inputEntry = constructorInputs[i]; const paramType = inputEntry.type; const paramName = inputEntry.name; inputNameToTypeMap[paramName] = paramType; paramInputQuestions.push({ type: "text", name: paramName, message: `please input value for param ${chalk_1.default.greenBright(`${paramName}`)} of type ${chalk_1.default.greenBright(`${paramType}`)}`, }); } } } chosen = await prompts_1.default(questions); let inputsChosen = {}; if (!inputsAsJson) { inputsChosen = await prompts_1.default(paramInputQuestions); web3_suites_1.SimbaConfig.log.debug(`:: inputsChosen : ${JSON.stringify(inputsChosen)}`); for (const key in inputsChosen) { if (!inputNameToTypeMap[key].startsWith("string") || !inputNameToTypeMap[key].startsWith("address")) { try { // trying and catching. there are custom data types that users can define // that we won't be able to anticipate. so we try to parse those, // and if they're really just extensions of 'string', then we continue inputsChosen[key] = JSON.parse(inputsChosen[key]); } catch (e) { continue; } } } } if (!chosen.api) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: EXIT : No API Name chosen!`)}`); return; } if (!chosen.blockchain) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: EXIT : No blockchain chosen!`)}`); return; } if (!chosen.storage) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: EXIT : No storage chosen!`)}`); return; } if (constructorRequiresParams && !chosen.args && !inputsChosen) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: EXIT : Your contract requires constructor arguments`)}`); return; } id = contractInfo.design_id; if (chosen.args) { deployArgs = JSON.parse(chosen.args); } else { if (config.ProjectConfigStore.has('defaultArgs')) { deployArgs = config.ProjectConfigStore.get('defaultArgs'); } else { if (inputsChosen) { deployArgs = JSON.parse(JSON.stringify(inputsChosen)); } } } } let deployURL; let deployment; if (deployInfo) { deployURL = deployInfo.url; deployment = { blockchain: deployInfo.blockchain, storage: deployInfo.storage, api_name: deployInfo.api, app_name: config.application.name, display_name: config.application.name, args: deployInfo.args, }; } else { if (_isLibrary) { deployURL = `v2/organisations/${config.organisation.id}/deployed_artifacts/create/`; const b64CodeBuffer = Buffer.from(sourceCode); const base64CodeString = b64CodeBuffer.toString('base64'); deployment = { args: deployArgs, language: "Solidity", code: base64CodeString, blockchain: chosen.blockchain, app_name: config.application.name, lib_name: config.ProjectConfigStore.get("primary"), }; } else { deployURL = `v2/organisations/${config.organisation.id}/contract_designs/${id}/deploy/`; deployment = { blockchain: chosen.blockchain, storage: chosen.storage, api_name: chosen.api, app_name: config.application.name, display_name: config.application.name, args: deployArgs, }; } } web3_suites_1.SimbaConfig.log.debug(`${chalk_1.default.greenBright(`\nsimba: deployment request: ${JSON.stringify(deployment)}`)}`); try { const authStore = await web3_suites_1.SimbaConfig.authStore(); if (!authStore) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: no authStore created. Please make sure your baseURL is properly configured in your simba.json`)}`); return Promise.resolve(new Error(web3_suites_1.authErrors.badAuthProviderInfo)); } const resp = await authStore.doPostRequest(deployURL, deployment, "application/json", true); if (!resp) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`simba: EXIT : error deploying contract`)}`); return; } const deployment_id = resp.deployment_id; const transaction_hash = resp.transaction_hash; config.ProjectConfigStore.set('deployment_id', deployment_id); web3_suites_1.SimbaConfig.log.info(`${chalk_1.default.cyanBright(`\nsimba deploy: Contract deployment ID for contract ${contractName}:`)} ${chalk_1.default.greenBright(`${deployment_id}`)}`); web3_suites_1.SimbaConfig.log.info(`${chalk_1.default.cyanBright(`\nsimba deploy: txn hash for contract ${contractName}:`)} ${chalk_1.default.greenBright(`${transaction_hash}`)}`); let deployed = false; let lastState = null; let retVal = null; do { const checkDeployURL = `v2/organisations/${config.organisation.id}/deployments/${deployment_id}/`; const check_resp = await authStore.doGetRequest(checkDeployURL); if (!check_resp) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`simba: EXIT : error checking deployment URL`)}`); return; } const state = check_resp.state; web3_suites_1.SimbaConfig.log.debug(`:: state : ${state}`); switch (state) { case 'INITIALISED': if (lastState !== state) { lastState = state; web3_suites_1.SimbaConfig.log.info(`${chalk_1.default.cyanBright('\nsimba deploy: Your contract deployment has been initialised...')}`); } break; case 'EXECUTING': if (lastState !== state) { lastState = state; web3_suites_1.SimbaConfig.log.info(`${chalk_1.default.cyanBright('\nsimba deploy: deployment is executing...')}`); } break; case 'COMPLETED': deployed = true; const contractName = config.ProjectConfigStore.get("primary"); let contractsInfo = config.ProjectConfigStore.get("contracts_info") ? config.ProjectConfigStore.get("contracts_info") : {}; contractsInfo[contractName] = contractsInfo[contractName] ? contractsInfo[contractName] : {}; contractsInfo[contractName].application = web3_suites_1.SimbaConfig.application.name; if (!_isLibrary) { const contractAddress = check_resp.primary.address; contractsInfo[contractName].address = contractAddress; contractsInfo[contractName].deployment_id = deployment_id; contractsInfo[contractName].transaction_hash = transaction_hash; config.ProjectConfigStore.set("contracts_info", contractsInfo); const most_recent_deployment_info = { address: contractAddress, deployment_id, transaction_hash, type: "contract" }; config.ProjectConfigStore.set('most_recent_deployment_info', most_recent_deployment_info); web3_suites_1.SimbaConfig.log.info(`${chalk_1.default.cyanBright(`\nsimba deploy: contract ${chalk_1.default.greenBright(`${contractName}`)} was deployed to ${chalk_1.default.greenBright(`${contractAddress}`)} with deployment_id ${chalk_1.default.greenBright(`${deployment_id}`)} and transaction_hash ${chalk_1.default.greenBright(`${transaction_hash}`)}. Information pertaining to this deployment can be found in your simba.json under contracts_info.${contractName}.`)}`); } else { const deploymentInfo = check_resp.deployment; for (let i = 0; i < deploymentInfo.length; i++) { const entry = deploymentInfo[i]; if (!(entry.name === contractName)) { continue; } const libraryAddress = entry.address; contractsInfo[contractName].address = libraryAddress; contractsInfo[contractName].deployment_id = deployment_id; config.ProjectConfigStore.set("contracts_info", contractsInfo); const most_recent_deployment_info = { address: libraryAddress, deployment_id, transaction_hash, type: "library", }; let libraryAddresses = config.ProjectConfigStore.get("library_addresses") ? config.ProjectConfigStore.get("library_addresses") : {}; libraryAddresses[contractName] = libraryAddress; config.ProjectConfigStore.set("library_addresses", libraryAddresses); config.ProjectConfigStore.set("most_recent_deployment_info", most_recent_deployment_info); web3_suites_1.SimbaConfig.log.info(`${chalk_1.default.cyanBright(`simba: your library was deployed to address ${chalk_1.default.greenBright(`${libraryAddress}`)}, with deployment_id ${chalk_1.default.greenBright(`${deployment_id}`)} and transaction_hash ${chalk_1.default.greenBright(`${transaction_hash}`)}. Information pertaining to this deployment can be found in your simba.json`)}`); } } break; case 'ABORTED': deployed = true; web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.red('\nsimba deploy: Your contract deployment was aborted...')}`); web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.red(`\nsimba deploy: ${check_resp.error}`)}${check_resp.error}`); retVal = new Error(check_resp.error); break; } } while (!deployed); Promise.resolve(retVal); } catch (error) { if (axios_1.default.isAxiosError(error) && error.response) { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: EXIT : ${JSON.stringify(error.response.data)}`)}`); return error; } else { web3_suites_1.SimbaConfig.log.error(`${chalk_1.default.redBright(`\nsimba: EXIT : ${JSON.stringify(error)}`)}`); return error; } } web3_suites_1.SimbaConfig.log.debug(`:: EXIT :`); return; } exports.deployContract = deployContract; //# sourceMappingURL=deploy.js.map