locklift-deploy-artifacts
Version:
Locklift plugin that enables you to store build artifacts across contract migration scripts.
266 lines (265 loc) • 11.4 kB
JavaScript
;
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;
}