UNPKG

locklift-deploy-artifacts

Version:

Locklift plugin that enables you to store build artifacts across contract migration scripts.

266 lines (265 loc) 11.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.stringifyObj = exports.createTypeDefinitions = exports.Artifacts = void 0; // import * as fs from "fs"; const p = __importStar(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); const LATEST_VERSION = "latest"; const TYPE_DEFINITION_FILE = "artifacts.d.ts"; class Artifacts { constructor(journalPath) { this.journalPath = journalPath; this.dir = p.dirname(journalPath); this.data = this.load(); } addContract(network, contractName, aliasName, versionName = LATEST_VERSION, version) { let networkContracts = this.data.get(network); if (!networkContracts) { (networkContracts = new Map()), this.data.set(network, networkContracts); } let contract = networkContracts.get(contractName); if (!contract) { contract = new Map(); networkContracts.set(contractName, contract); } let alias = contract.get(aliasName); if (!alias) { alias = new Map(); contract.set(aliasName, alias); } version.updatedAt = version.updatedAt ? version.updatedAt : Date.now(); let ver = versionName == LATEST_VERSION ? this.getContractNextVersion(network, contractName, aliasName) : versionName; alias.set(ver, version); this.saveJournal(); this.saveContractArtifacts(network, contractName, aliasName, ver, { abi: version.abi, code: version.code, tvc: version.tvc, }); this.saveTypeDefinitions(); } getDataObj() { return this.toObject(this.data); } reset() { deleteDirectory(this.dir); this.data = new Map(); this.saveJournal(); this.saveTypeDefinitions(); } // loadLogData - loads data from json file and fs and returns it as a map of networks load() { let data = new Map(); if (fs_extra_1.default.existsSync(this.dir) && fs_extra_1.default.existsSync(this.journalPath)) { const file = fs_extra_1.default.readFileSync(this.journalPath, "utf8"); data = this.parseArtifactsData(file); } return data; } // saveLogFile - saves map of networks to json file saveJournal() { fs_extra_1.default.ensureDirSync(this.dir); const journal = this.toObject(this.dataToJournalView()); fs_extra_1.default.writeFileSync(this.journalPath, JSON.stringify(journal, null, 2)); } saveContractArtifacts(network, contractName, aliasName, versionName, params) { const versionPath = p.join(this.dir, network, contractName, aliasName, versionName); if (!fs_extra_1.default.existsSync(versionPath)) { fs_extra_1.default.mkdirSync(versionPath, { recursive: true }); } fs_extra_1.default.writeFileSync(p.join(versionPath, "tvc"), params.tvc); fs_extra_1.default.writeFileSync(p.join(versionPath, "abi.json"), JSON.stringify(params.abi, null, 2)); fs_extra_1.default.writeFileSync(p.join(versionPath, "code"), params.code); fs_extra_1.default.writeFileSync(p.join(versionPath, "source.ts"), generateContractCode(JSON.stringify(params.abi), contractName)); } saveTypeDefinitions() { const typeDefinitions = createTypeDefinitions("IDeployArtifacts", this.data); fs_extra_1.default.writeFileSync(p.join(this.dir, TYPE_DEFINITION_FILE), typeDefinitions); } dataToJournalView() { const res = new Map(); for (const [networkName, networkContracts] of this.data.entries()) { const contracts = new Map(); for (const [contractName, contract] of networkContracts.entries()) { const aliases = new Map(); for (const [aliasName, alias] of contract.entries()) { const versions = new Map(); for (const [versionName, version] of alias.entries()) { versions.set(versionName, toJournalVersion(version)); } aliases.set(aliasName, versions); } contracts.set(contractName, aliases); } res.set(networkName, contracts); } return res; } // parse - parses json string and returns map of networks parseArtifactsData(jsonContent) { const parsed = new Map(); if (!jsonContent) { return parsed; } const json = JSON.parse(jsonContent); Object.keys(json).forEach((networkName) => { const networkContracts = json[networkName]; if (!networkContracts) { return parsed; } const contracts = new Map(); Object.keys(networkContracts).forEach((contractName) => { const contract = networkContracts[contractName]; const aliases = new Map(); Object.keys(contract).forEach((aliasName) => { const versions = new Map(); Object.keys(contract[aliasName]).forEach((versionName) => { const ver = contract[aliasName][versionName]; const { abi, code, tvc } = this.getContractArtifacts(networkName, contractName, aliasName, versionName); const abiObj = JSON.parse(abi); const extended = { ...ver, abi: abiObj, code, tvc }; versions.set(versionName, extended); }); aliases.set(aliasName, versions); }); contracts.set(contractName, aliases); }); parsed.set(networkName, contracts); }); return parsed; } // getContractArtifacts - returns contract artifacts from fs getContractArtifacts(network, contractName, alias, versionName) { const versionPath = p.join(this.dir, network, contractName, alias, versionName); const tvc = fs_extra_1.default.readFileSync(p.join(versionPath, "tvc"), "utf8"); const abi = fs_extra_1.default.readFileSync(p.join(versionPath, "abi.json"), "utf8"); const code = fs_extra_1.default.readFileSync(p.join(versionPath, "code"), "utf8"); return { tvc, abi, code }; } getContractNextVersion(network, contractName, aliasName) { var _a, _b, _c; let alias = (_c = (_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a.get(network)) === null || _b === void 0 ? void 0 : _b.get(contractName)) === null || _c === void 0 ? void 0 : _c.get(aliasName); return alias ? `v${alias.size}` : "v0"; } toObject(map = new Map()) { return Object.fromEntries(Array.from(map.entries(), ([k, v]) => (v instanceof Map ? [k, this.toObject(v)] : [k, v]))); } } exports.Artifacts = Artifacts; function createTypeDefinitions(interfaceName, data) { let ii = ""; for (const [network, contracts] of data) { let indent = 2; ii += formatIndent(indent, `${network}: {`); indent += 2; for (const [contract, aliases] of contracts) { ii += formatIndent(indent, `${contract}: {`); indent += 2; for (const [alias, versions] of aliases) { ii += formatIndent(indent, `${alias}: {`); indent += 2; for (const [version, versionData] of versions) { ii += formatIndent(indent, `${version}: {`); const obj = typeLeafNode(versionData, network, contract, alias, version); ii += `\n${stringifyObj(obj, indent + 2)}${" ".repeat(indent)}};`; } indent -= 2; ii += formatIndent(indent, `};`); } indent -= 2; ii += formatIndent(indent, `};`); } indent -= 2; ii += formatIndent(indent, `};\n`); } return `\nexport interface ${interfaceName} {${ii}}\n`; } exports.createTypeDefinitions = createTypeDefinitions; function stringifyObj(obj, indent) { let res = ""; for (const key in obj) { if (typeof obj[key] !== "object") { res += `${" ".repeat(indent)}${key}: ${obj[key]};\n`; } else { res += `${" ".repeat(indent)}${key}: {\n${stringifyObj(obj[key], indent + 2)}${" ".repeat(indent)}};\n`; } } return res; } exports.stringifyObj = stringifyObj; function formatIndent(indention, mask) { return `\n${" ".repeat(indention)}` + mask; } function typeLeafNode(obj, network, contract, alias, version) { if (typeof obj !== "object") { return typeof obj; } const result = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { if (key === "abi") { result[key] = `import("./${network}/${contract}/${alias}/${version}/source").${contract}Abi`; continue; } result[key] = typeLeafNode(obj[key], network, contract, alias, version); } } return result; } function toJournalVersion(v) { return { address: v.address, codeHash: v.codeHash, initParams: v.initParams, constructorParams: v.constructorParams, publicKey: v.publicKey, updatedAt: v.updatedAt, }; } function deleteDirectory(directoryPath) { if (fs_extra_1.default.existsSync(directoryPath)) { fs_extra_1.default.readdirSync(directoryPath).forEach((file) => { const filePath = p.join(directoryPath, file); if (fs_extra_1.default.lstatSync(filePath).isDirectory()) { deleteDirectory(filePath); } else { fs_extra_1.default.unlinkSync(filePath); } }); fs_extra_1.default.rmdirSync(directoryPath); } } function generateContractCode(abiSource, contractName) { const abiSourceName = contractName.slice(0, 1).toLowerCase() + contractName.slice(1) + "Abi"; let code = `const ${abiSourceName} = ${abiSource.replace(/\s/g, "")} as const`; code += `\nexport type ${contractName}Abi = typeof ${abiSourceName};\n`; return code; }