UNPKG

@fairyfromalfeya/locklift-deploy

Version:

Locklift plugin for replicable deployments and easy testing

248 lines (247 loc) 11.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Deployments = void 0; const locklift_1 = require("locklift"); const path_1 = __importDefault(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); const rxjs_1 = require("rxjs"); const utils_1 = require("./utils"); const logger_1 = require("./logger"); class Deployments { locklift; tags; network; networkId; deploymentsStore = {}; accountsStore = {}; // private readonly pathToLogFile: string; pathToNetworkFolder; logger = new logger_1.Logger(); constructor(locklift, // private readonly deployFolderPath: string, tags, network, networkId) { this.locklift = locklift; this.tags = tags; this.network = network; this.networkId = networkId; this.pathToNetworkFolder = path_1.default.join("deployments", this.network); fs_extra_1.default.ensureDirSync(this.pathToNetworkFolder); fs_extra_1.default.writeFileSync(`${this.pathToNetworkFolder}/.networkInfo.json`, JSON.stringify({ chainId: this.networkId }, null, 4)); } getLogContent = () => { const files = fs_extra_1.default.readdirSync(this.pathToNetworkFolder); return files .filter((file) => file.endsWith("json")) .map((fileName) => { try { const deployInfo = JSON.parse(fs_extra_1.default.readFileSync(path_1.default.join(this.pathToNetworkFolder, fileName), "utf-8")); if (deployInfo.type === "Contract" || deployInfo.type === "Account") { return deployInfo; } } catch { } }) .filter(utils_1.isT); }; getAccountOrContractFilePath = (deploymentName, type) => path_1.default.join(this.pathToNetworkFolder, `${type}__${deploymentName}.json`); writeDeployInfo = (deployInfo, enableLogs) => { const fileName = this.getAccountOrContractFilePath(deployInfo.deploymentName, deployInfo.type); fs_extra_1.default.writeFileSync(fileName, JSON.stringify(deployInfo, null, 4)); if (enableLogs) { this.logger.printLog(deployInfo); } }; //region contract deploy = ({ deployConfig, deploymentName, enableLogs = false, }) => { return this.locklift.factory.deployContract(deployConfig).then(async (res) => { this.setContractToStore({ deploymentName: deploymentName, contract: res.contract }); this.writeDeployInfo({ type: "Contract", deploymentName, abi: JSON.parse(res.contract.abi), address: res.contract.address.toString(), transaction: res.tx, codeHash: await res.contract.getFullState().then((res) => res.state?.codeHash), contractName: deployConfig.contract, // @ts-ignore deployContractParams: deployConfig, }, enableLogs); return res; }); }; saveContract = async ({ deploymentName, address, contractName, }, enableLogs = false) => { const contract = this.locklift.factory.getDeployedContract(contractName, typeof address === "string" ? new locklift_1.Address(address) : address); this.writeDeployInfo({ type: "Contract", deploymentName, address: contract.address.toString(), // @ts-ignore contractName: contractName, abi: JSON.parse(contract.abi), codeHash: await contract.getFullState().then((res) => res.state?.codeHash), }, enableLogs); this.setContractToStore({ contract, deploymentName }); }; setContractToStore = ({ deploymentName, contract }) => { this.deploymentsStore[deploymentName] = contract; }; getContract = (contractName) => { debugger; const contract = this.deploymentsStore[contractName]; if (!contract) { throw new Error(`Contract ${contractName} not found in deployments store\nList of deployed contracts: \n${Object.keys(this.deploymentsStore).join("\n")}`); } return contract; }; //endregion //region account deployAccounts = async (accounts, enableLogs = false) => { return (0, rxjs_1.lastValueFrom)((0, rxjs_1.from)(accounts).pipe((0, rxjs_1.concatMap)((accountSetup) => (0, rxjs_1.defer)(async () => { const { accountSettings, deploymentName, signerId } = accountSetup; const signer = await this.locklift.keystore.getSigner(signerId).then((mayBeSigner) => { if (!mayBeSigner) { throw new Error(`Signer with signerId ${signerId} not found`); } return mayBeSigner; }); // @ts-ignore const { account } = await this.locklift.factory.accounts.addNewAccount({ ...accountSettings, publicKey: signer.publicKey, }); this.writeDeployInfo({ type: "Account", address: account.address.toString(), deploymentName, createAccountParams: { ...accountSettings, publicKey: signer.publicKey, }, publicKey: signer.publicKey, signerId, }, enableLogs); return { account, signer, deploymentName, }; })), (0, rxjs_1.tap)(({ account, deploymentName, signer }) => { this.setAccountToStore({ accountName: deploymentName, account, signer }); }), (0, rxjs_1.toArray)())); }; getAccount = (accountName) => { const accountWithSigner = this.accountsStore[accountName]; if (!accountWithSigner) { throw new Error(`Account ${accountName} not found in deployments store`); } return accountWithSigner; }; saveAccount = async ({ deploymentName, signerId, address, accountSettings, }, enableLogs = false) => { const signer = await this.locklift.keystore.getSigner(signerId); if (!signer) { throw new Error(`Signer not found`); } this.writeDeployInfo({ type: "Account", address: address, signerId, deploymentName, publicKey: signer.publicKey, saveAccountParams: accountSettings, }, enableLogs); const account = await this.locklift.factory.accounts.addExistingAccount({ // @ts-ignore address: new locklift_1.Address(address), publicKey: signer.publicKey, ...accountSettings, }); this.setAccountToStore({ account, accountName: deploymentName, signer }); }; setAccountToStore = ({ account, accountName, signer }) => { this.accountsStore[accountName] = { account, signer }; }; //endregion reset = () => { try { // reset log files const files = fs_extra_1.default.readdirSync(this.pathToNetworkFolder); files.forEach((file) => fs_extra_1.default.rmSync(path_1.default.join(this.pathToNetworkFolder, file))); // reset stores this.deploymentsStore = {}; this.accountsStore = {}; } catch { } }; load = async () => { const accountsAndContracts = this.getLogContent(); const { contracts, accounts } = { accounts: accountsAndContracts.filter(({ type }) => type === "Account"), contracts: accountsAndContracts.filter(({ type }) => type === "Contract"), }; if (accounts.length > 0) { await (0, rxjs_1.lastValueFrom)((0, rxjs_1.from)(accounts).pipe((0, rxjs_1.mergeMap)((deployedAccount) => (0, rxjs_1.defer)(async () => { const accountSigner = deployedAccount.signerId && (await this.locklift.keystore.getSigner(deployedAccount.signerId)); if (!accountSigner) { throw new Error(`Signer id ${deployedAccount.signerId} not found`); } if (accountSigner.publicKey !== deployedAccount.publicKey) { throw new Error(`Signer ${deployedAccount.signerId} publicKey doesn't match with account ${deployedAccount.deploymentName} publicKey, did you forgot to set seed phrase in locklift.config.ts ?`); } const account = await this.locklift.factory.accounts.addExistingAccount({ ...(deployedAccount.createAccountParams || deployedAccount.saveAccountParams), address: new locklift_1.Address(deployedAccount.address), publicKey: accountSigner.publicKey, }); this.setAccountToStore({ accountName: deployedAccount.deploymentName, account, signer: accountSigner, }); })))); } contracts.forEach(({ deploymentName, contractName, address }) => { debugger; this.setContractToStore({ deploymentName: deploymentName, contract: this.locklift.factory.getDeployedContract(contractName, new locklift_1.Address(address)), }); }); }; fixture = async (fixtureConfig) => { const { include, exclude } = fixtureConfig || {}; if (include && exclude) { throw new Error("includes and excludes can't be defined together"); } const deploymentsConfig = this.tags .filter(utils_1.isT) .map((el, idx, arr) => ({ ...el, dependenciesCount: (0, utils_1.calculateDependenciesCount)(arr, el.tag, el.tag) })) .sort((prev, next) => prev.dependenciesCount.deep - next.dependenciesCount.deep); const includedDependencies = include ? deploymentsConfig.filter(({ tag }) => include.some((include) => tag === include)) : exclude ? deploymentsConfig.filter(({ tag }) => !exclude.some((exclude) => exclude === tag)) : deploymentsConfig; const notResolvedDependencies = includedDependencies .map((deployment) => ({ ...deployment, canBeInitialized: !deployment.dependencies || deployment.dependencies.some((dependency) => includedDependencies.some((included) => dependency === included.tag)), })) .filter(({ canBeInitialized }) => !canBeInitialized); if (notResolvedDependencies.length > 0) { throw new Error(`${notResolvedDependencies.map(({ tag }) => `Tag ${tag} can't be initialized without required dependencies`)}`); } if (includedDependencies.length > 0) { await (0, rxjs_1.lastValueFrom)((0, rxjs_1.from)(includedDependencies).pipe((0, rxjs_1.concatMap)(({ default: deployFunction, tag }) => { if (deployFunction && "name" in deployFunction) { return deployFunction(); } return (0, rxjs_1.of)(undefined); }))); } }; } exports.Deployments = Deployments;