@fairyfromalfeya/locklift-deploy
Version:
Locklift plugin for replicable deployments and easy testing
248 lines (247 loc) • 11.4 kB
JavaScript
;
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;