@newos/cli
Version:
Command-line interface for the NewOS
822 lines • 40.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_extra_1 = __importDefault(require("fs-extra"));
const lodash_1 = require("lodash");
const toposort_1 = __importDefault(require("toposort"));
const upgrades_1 = require("@newos/upgrades");
const ManifestVersion_1 = require("../files/ManifestVersion");
const async_1 = require("../../utils/async");
const naming_1 = require("../../utils/naming");
const ProjectDeployer_1 = require("./ProjectDeployer");
const Dependency_1 = __importDefault(require("../dependency/Dependency"));
const ValidationLogger_1 = __importDefault(require("../../interface/ValidationLogger"));
const Verifier_1 = __importDefault(require("../Verifier"));
const LocalController_1 = __importDefault(require("../local/LocalController"));
const ContractManager_1 = __importDefault(require("../local/ContractManager"));
const NetworkFile_1 = __importDefault(require("../files/NetworkFile"));
const ProjectFile_1 = __importDefault(require("../files/ProjectFile"));
const ManifestVersion_2 = require("../files/ManifestVersion");
const interfaces_1 = require("../../scripts/interfaces");
class NetworkController {
constructor(network, txParams, networkFile) {
if (!networkFile) {
const projectFile = new ProjectFile_1.default();
this.networkFile = new NetworkFile_1.default(projectFile, network);
}
else {
this.networkFile = networkFile;
}
this.localController = new LocalController_1.default(this.networkFile.projectFile);
this.contractManager = new ContractManager_1.default(this.networkFile.projectFile);
this.txParams = txParams;
this.network = network;
}
// NetworkController
get projectFile() {
return this.localController.projectFile;
}
// NetworkController
get projectVersion() {
return this.projectFile.version;
}
// NetworkController
get currentVersion() {
return this.networkFile.version;
}
get currentManifestVersion() {
return this.networkFile.manifestVersion;
}
// NetworkController
get packageAddress() {
return this.networkFile.packageAddress;
}
get proxyAdminAddress() {
return this.networkFile.proxyAdminAddress;
}
get proxyFactoryAddress() {
return this.networkFile.proxyFactoryAddress;
}
// NetworkController
checkNotFrozen() {
if (this.networkFile.frozen) {
throw Error(`Cannot modify contracts in a frozen version. Run 'openzeppelin bump' to create a new version first.`);
}
}
// DeployerController
async fetchOrDeploy(requestedVersion) {
this.project = await this.getDeployer(requestedVersion).fetchOrDeploy();
return this.project;
}
async deployChangedSolidityLibs(contractNames) {
const libNames = this.getAllSolidityLibNames([contractNames]);
const changedLibraries = this.getLibsToDeploy(libNames, true);
await this.uploadSolidityLibs(changedLibraries);
}
// DeployerController
async push(contracts, { reupload = false, force = false } = {}) {
const changedLibraries = this.solidityLibsForPush(!reupload);
const contractObjects = this.contractsListForPush(contracts, !reupload, changedLibraries);
const buildArtifacts = upgrades_1.getBuildArtifacts();
// ValidateContracts also extends each contract class with validation errors and storage info
if (!this.validateContracts(contractObjects, buildArtifacts) && !force) {
throw Error('One or more contracts have validation errors. Please review the items listed above and fix them, or run this command again with the --force option.');
}
this.checkVersion();
await this.fetchOrDeploy(this.projectVersion);
await this.handleDependenciesLink();
this.checkNotFrozen();
await this.uploadSolidityLibs(changedLibraries);
await Promise.all([this.uploadContracts(contractObjects), this.unsetMissingContracts()]);
await this.unsetSolidityLibs();
if (lodash_1.isEmpty(contractObjects) && lodash_1.isEmpty(changedLibraries)) {
upgrades_1.Loggy.noSpin(__filename, 'push', `after-push`, `All implementations are up to date`);
}
else {
upgrades_1.Loggy.noSpin(__filename, 'push', `after-push`, `All implementations have been deployed`);
}
}
// DeployerController
async deployProxyFactory() {
await this.fetchOrDeploy(this.projectVersion);
await this.project.ensureProxyFactory();
await this.tryRegisterProxyFactory();
}
// DeployerController
async deployProxyAdmin() {
await this.fetchOrDeploy(this.projectVersion);
await this.project.ensureProxyAdmin();
await this.tryRegisterProxyAdmin();
}
// DeployerController
checkVersion() {
if (this.isNewVersionRequired()) {
this.networkFile.frozen = false;
this.networkFile.contracts = {};
}
}
// DeployerController
isNewVersionRequired() {
return this.projectVersion !== this.currentVersion && this.isPublished;
}
// Contract model
contractsListForPush(contracts, onlyChanged = false, changedLibraries = []) {
const newVersion = this.isNewVersionRequired();
return contracts
.map((contractName) => [contractName, upgrades_1.Contracts.getFromLocal(contractName).upgradeable])
.filter(([contractName, contract]) => newVersion ||
!onlyChanged ||
this.hasContractChanged(contractName, contract) ||
this.hasChangedLibraries(contract, changedLibraries));
}
getLibsToDeploy(libNames, onlyChanged = false) {
return libNames
.map(libName => upgrades_1.Contracts.getFromLocal(libName))
.filter(libClass => {
const hasSolidityLib = this.networkFile.hasSolidityLib(libClass.schema.contractName);
const hasChanged = this.hasSolidityLibChanged(libClass);
return !hasSolidityLib || !onlyChanged || hasChanged;
});
}
// Contract model || SolidityLib model
solidityLibsForPush(onlyChanged = false) {
const contractNames = this.projectFile.contracts;
const libNames = this.getAllSolidityLibNames(contractNames);
const clashes = lodash_1.intersection(libNames, contractNames);
if (!lodash_1.isEmpty(clashes)) {
throw new Error(`Cannot upload libraries with the same name as a contract name: ${clashes.join(', ')}`);
}
return this.getLibsToDeploy(libNames, onlyChanged);
}
// Contract model || SolidityLib model
async uploadSolidityLibs(libs) {
// Libs may have dependencies, so deploy them in order
for (const lib of libs) {
await this.uploadSolidityLib(lib);
}
}
// Contract model || SolidityLib model
async uploadSolidityLib(libClass) {
const libName = libClass.schema.contractName;
await this.setSolidityLibs(libClass); // Libraries may depend on other libraries themselves
upgrades_1.Loggy.spin(__filename, '_uploadSolidityLib', `upload-solidity-lib${libName}`, `Uploading ${libName} library`);
const libInstance = this.project === undefined
? // There is no project for non-upgradeable deploys.
await upgrades_1.Transactions.deployContract(libClass)
: await this.project.setImplementation(libClass, libName);
this.networkFile.addSolidityLib(libName, libInstance);
upgrades_1.Loggy.succeed(`upload-solidity-lib${libName}`, `${libName} library uploaded`);
}
// Contract model
async uploadContracts(contracts) {
await async_1.allPromisesOrError(contracts.map(([contractName, contract]) => this.uploadContract(contractName, contract)));
}
// Contract model
async uploadContract(contractName, contract) {
try {
await this.setSolidityLibs(contract);
upgrades_1.Loggy.spin(__filename, 'uploadContract', `upload-contract${contract.schema.contractName}`, `Validating and deploying contract ${contract.schema.contractName}`);
const contractInstance = await this.project.setImplementation(contract, contractName);
const { types, storage } = contract.schema.storageInfo || {
types: null,
storage: null,
};
this.networkFile.addContract(contractName, contractInstance, {
warnings: contract.schema.warnings,
types,
storage,
});
upgrades_1.Loggy.succeed(`upload-contract${contract.schema.contractName}`, `Contract ${contract.schema.contractName} deployed`);
}
catch (error) {
error.message = `${contractName} deployment failed with error: ${error.message}`;
throw error;
}
}
// Contract model || SolidityLib model
async setSolidityLibs(contract) {
const currentContractLibs = upgrades_1.getSolidityLibNames(contract.schema.bytecode);
const libraries = this.networkFile.getSolidityLibs(currentContractLibs);
contract.link(libraries);
}
// Contract model || SolidityLib model
async unsetSolidityLibs() {
const contractNames = this.projectFile.contracts;
const libNames = this.getAllSolidityLibNames(contractNames);
await async_1.allPromisesOrError(this.networkFile.solidityLibsMissing(libNames).map(libName => this.unsetSolidityLib(libName)));
}
// Contract model || SolidityLib model
async unsetSolidityLib(libName) {
try {
upgrades_1.Loggy.spin(__filename, '_unsetSolidityLib', `unset-solidity-lib-${libName}`, `Removing ${libName} library`);
// There is no project for non-upgradeable deploys.
if (this.project !== undefined) {
await this.project.unsetImplementation(libName);
}
this.networkFile.unsetSolidityLib(libName);
upgrades_1.Loggy.succeed(`unset-solidity-lib-${libName}`);
}
catch (error) {
error.message = `Removal of ${libName} failed with error: ${error.message}`;
throw error;
}
}
// Contract model || SolidityLib model
hasChangedLibraries(contract, changedLibraries) {
const libNames = upgrades_1.getSolidityLibNames(contract.schema.bytecode);
return !lodash_1.isEmpty(lodash_1.intersection(changedLibraries.map(c => c.schema.contractName), libNames));
}
// Contract model || SolidityLib model
getAllSolidityLibNames(contractNames) {
const graph = [];
const nodes = [];
contractNames.forEach(contractName => {
this.populateDependencyGraph(contractName, nodes, graph);
});
// exclude original contracts
return [...lodash_1.difference(toposort_1.default(graph), contractNames).reverse()];
}
populateDependencyGraph(contractName, nodes, graph) {
// if library is already added just ingore it
if (!nodes.includes(contractName)) {
nodes.push(contractName);
this.getContractDependencies(contractName).forEach(dependencyContractName => {
this.populateDependencyGraph(dependencyContractName, nodes, graph);
graph.push([contractName, dependencyContractName]);
});
}
}
getContractDependencies(contractName) {
const contract = upgrades_1.Contracts.getFromLocal(contractName);
return upgrades_1.getSolidityLibNames(contract.schema.bytecode);
}
// Contract model
async unsetMissingContracts() {
await async_1.allPromisesOrError(this.networkFile
.contractsMissingFromPackage()
.map(contractName => this.unsetContractAndImplementation(contractName)));
}
// Contract model
async unsetContractAndImplementation(contractName) {
try {
upgrades_1.Loggy.spin(__filename, 'unsetContract', `unset-contract-${contractName}`, `Removing ${contractName} contract`);
await this.project.unsetImplementation(contractName);
this.networkFile.unsetContract(contractName);
upgrades_1.Loggy.succeed(`unset-contract-${contractName}`);
}
catch (error) {
error.message = `Removal of ${contractName} failed with error: ${error.message}`;
throw error;
}
}
// DeployerController || Contract model
validateContracts(contracts, buildArtifacts) {
return lodash_1.every(contracts.map(([contractName, contract]) => this.validateContract(contractName, contract, buildArtifacts)));
}
// DeployerController || Contract model
validateContract(contractName, contract, buildArtifacts) {
try {
const existingContractInfo = this.networkFile.contract(contractName) || {};
const warnings = upgrades_1.validate(contract, existingContractInfo, buildArtifacts);
const newWarnings = upgrades_1.newValidationErrors(warnings, existingContractInfo.warnings);
const validationLogger = new ValidationLogger_1.default(contract, existingContractInfo);
validationLogger.log(newWarnings, buildArtifacts);
contract.schema.warnings = warnings;
contract.schema.storageInfo = upgrades_1.getStorageLayout(contract, buildArtifacts);
return upgrades_1.validationPasses(newWarnings);
}
catch (err) {
upgrades_1.Loggy.noSpin.error(__filename, 'validateContract', `validate-contract`, `Error while validating contract ${contract.schema.contractName}: ${err}`);
return false;
}
}
// Contract model
logErrorIfContractPackageIsInvalid(packageName, contractName, throwIfFail = false) {
if (!packageName)
packageName = this.projectFile.name;
const err = this.getPackageContractError(packageName, contractName);
if (err)
this.logErrorMessage(err, throwIfFail);
}
// Contract model
logErrorIfProjectDeploymentIsInvalid(throwIfFail = false) {
const err = this.getDeploymentErrorForProject();
if (err)
this.logErrorMessage(err, throwIfFail);
}
// Contract model
logErrorIfContractDeploymentIsInvalid(contractName, throwIfFail = false) {
const err = this.getContractDeploymentError(contractName);
if (err)
this.logErrorMessage(err, throwIfFail);
}
// Contract model
getDeploymentErrorForProject() {
const contractsMissing = this.projectFile.contracts.filter(o => this.isProjectFileContract(o) && !this.isNetworkFileContract(o));
const contractsDeployed = this.projectFile.contracts.filter(o => this.isNetworkFileContract(o));
const contractsChanged = lodash_1.filter(contractsDeployed, contractName => this.hasContractChanged(contractName));
if (!lodash_1.isEmpty(contractsMissing)) {
return `Contracts ${contractsMissing.join(', ')} are not deployed.`;
}
else if (!lodash_1.isEmpty(contractsChanged)) {
return `Contracts ${contractsChanged.join(', ')} have changed since the last deploy.`;
}
}
// Contract model
getContractDeploymentError(contractName) {
if (!this.isProjectFileContract(contractName)) {
return `Contract ${contractName} not found in this project`;
}
else if (!this.isNetworkFileContract(contractName)) {
return `Contract ${contractName} is not deployed to ${this.network}.`;
}
else if (this.hasContractChanged(contractName)) {
return `Contract ${contractName} has changed locally since the last deploy, consider running 'newos push'.`;
}
}
// TODO: move to utils folder or somewhere else
logErrorMessage(msg, throwIfFail = false) {
if (throwIfFail) {
throw Error(msg);
}
else {
upgrades_1.Loggy.noSpin(__filename, 'handleErrorMessage', `handle-error-message`, msg);
}
}
// Contract model || SolidityLib model
hasSolidityLibChanged(libClass) {
return !this.networkFile.hasSameBytecode(libClass.schema.contractName, libClass);
}
// Contract model
hasContractChanged(contractName, contract) {
if (!this.isProjectFileContract(contractName))
return false;
if (this.isProjectFileContract(contractName) && !this.isNetworkFileContract(contractName))
return true;
if (!contract) {
contract = upgrades_1.Contracts.getFromLocal(contractName).upgradeable;
}
return !this.networkFile.hasSameBytecode(contractName, contract);
}
// Contract model
isProjectFileContract(contractName) {
return this.projectFile.hasContract(contractName);
}
// Contract model
isNetworkFileContract(contractName) {
return this.networkFile.hasContract(contractName);
}
// VerifierController
async verifyAndPublishContract(contractName, optimizer, optimizerRuns, remote, apiKey) {
upgrades_1.Loggy.spin(__filename, 'verifyAndPublishContract', 'verify-and-publish', `Verifying and publishing contract source code of ${contractName} on ${remote} (this usually takes under 30 seconds)`);
const { compilerVersion, sourcePath } = this.localController.getContractSourcePath(contractName);
const contractSource = await upgrades_1.flattenSourceCode([sourcePath]);
const contractAddress = this.networkFile.contracts[contractName].address;
if (this.networkFile.getProxies({ contractName, kind: interfaces_1.ProxyType.NonProxy }).length > 0) {
upgrades_1.Loggy.noSpin(__filename, 'verifyAndPublishContract', 'verify-and-publish-nonproxy', `A regular instance of ${contractName} was found. Verification of regular instances is not yet supported.`);
}
await Verifier_1.default.verifyAndPublish(remote, {
contractName,
compilerVersion,
optimizer,
optimizerRuns,
contractSource,
contractAddress,
apiKey,
network: this.network,
});
}
// NetworkController
writeNetworkPackageIfNeeded() {
this.networkFile.write();
this.projectFile.write();
}
// DeployerController
async freeze() {
if (!this.packageAddress)
throw Error('Cannot freeze an unpublished project');
await this.fetchOrDeploy(this.currentVersion);
if (this.project instanceof upgrades_1.AppProject)
await this.project.freeze();
this.networkFile.frozen = true;
}
// DeployerController
get isPublished() {
return this.projectFile.isPublished || this.appAddress !== undefined;
}
// DeployerController
getDeployer(requestedVersion) {
return this.isPublished
? new ProjectDeployer_1.AppProjectDeployer(this, requestedVersion)
: new ProjectDeployer_1.ProxyAdminProjectDeployer(this, requestedVersion);
}
// NetworkController
get appAddress() {
return this.networkFile.appAddress;
}
// NetworkController
get app() {
if (this.project instanceof upgrades_1.AppProject)
return this.project.getApp();
else
return null;
}
async migrate() {
const owner = this.isPublished ? this.appAddress : this.txParams.from;
const proxies = this.fetchOwnedProxies(null, null, null, owner);
if (proxies.length !== 0) {
const proxyAdmin = this.proxyAdminAddress
? await upgrades_1.ProxyAdmin.fetch(this.proxyAdminAddress, this.txParams)
: await upgrades_1.ProxyAdmin.deploy(this.txParams);
if (!this.proxyAdminAddress) {
upgrades_1.Loggy.spin(__filename, 'fetchOrDeploy', 'await-confirmations', 'Awaiting confirmations before transferring proxies to ProxyAdmin (this may take a few minutes)');
await upgrades_1.Transactions.awaitConfirmations(proxyAdmin.contract.deployment.transactionHash);
upgrades_1.Loggy.succeed('await-confirmations');
}
this.tryRegisterProxyAdmin(proxyAdmin.address);
await async_1.allPromisesOrError(lodash_1.map(proxies, async (proxy) => {
const proxyInstance = await upgrades_1.Proxy.at(proxy.address);
const currentAdmin = await proxyInstance.admin();
if (currentAdmin !== proxyAdmin.address) {
if (this.appAddress) {
return upgrades_1.AppProxyMigrator(this.appAddress, proxy.address, proxyAdmin.address, this.txParams);
}
else {
const simpleProject = new upgrades_1.SimpleProject(this.projectFile.name, null, this.txParams);
return simpleProject.changeProxyAdmin(proxy.address, proxyAdmin.address);
}
}
}));
upgrades_1.Loggy.noSpin(__filename, '_migrate', 'migrate-version-cli', `Successfully migrated to manifest version ${ManifestVersion_2.MANIFEST_VERSION}`);
}
else {
upgrades_1.Loggy.noSpin(__filename, '_migrate', 'migrate-version-cli', `No proxies were found. Updating manifest version to ${ManifestVersion_2.MANIFEST_VERSION}`);
}
}
async migrateManifestVersionIfNeeded() {
if (ManifestVersion_1.isMigratableManifestVersion(this.currentManifestVersion))
await this.migrate();
this.updateManifestVersionsIfNeeded(ManifestVersion_2.MANIFEST_VERSION);
}
// DeployerController
async publish() {
if (this.appAddress) {
upgrades_1.Loggy.noSpin(__filename, 'publish', 'publish-project', `Project is already published to ${this.network}`);
return;
}
await this.migrateManifestVersionIfNeeded();
const proxyAdminProject = (await this.fetchOrDeploy(this.currentVersion));
const deployer = new ProjectDeployer_1.AppProjectDeployer(this, this.projectVersion);
this.project = await deployer.fromProxyAdminProject(proxyAdminProject);
upgrades_1.Loggy.succeed(`publish-project`, `Published to ${this.network}!`);
}
// Proxy model
async createProxy(packageName, contractName, initMethod, initArgs, kind, admin, salt, signature) {
try {
await this.migrateManifestVersionIfNeeded();
await this.fetchOrDeploy(this.currentVersion);
if (!packageName)
packageName = this.projectFile.name;
const contract = this.contractManager.getContractClass(packageName, contractName).upgradeable;
await this.setSolidityLibs(contract);
this.checkInitialization(contract, initMethod);
if (salt)
await this.checkDeploymentAddress(salt);
const createArgs = {
packageName,
contractName,
initMethod,
initArgs,
admin,
};
const { proxy, instance } = await this.createProxyInstance(kind, salt, contract, signature, createArgs);
const implementationAddress = await proxy.implementation();
const projectVersion = packageName === this.projectFile.name
? this.currentVersion
: await this.project.getDependencyVersion(packageName);
await this.updateTruffleDeployedInformation(contractName, instance);
this.networkFile.addProxy(packageName, contractName, {
address: instance.address,
version: upgrades_1.semanticVersionToString(projectVersion),
implementation: implementationAddress,
admin: admin || this.networkFile.proxyAdminAddress || (await this.project.getAdminAddress()),
kind,
});
return instance;
}
finally {
await this.tryRegisterProxyAdmin();
await this.tryRegisterProxyFactory();
}
}
async createInstance(packageName, contractName, initArgs) {
await this.migrateManifestVersionIfNeeded();
if (!packageName) {
packageName = this.projectFile.name;
}
if (packageName === this.projectFile.name) {
await this.deployChangedSolidityLibs(contractName);
}
const contract = this.contractManager.getContractClass(packageName, contractName);
await this.setSolidityLibs(contract);
upgrades_1.Loggy.spin(__filename, 'createInstance', 'create-instance', `Deploying an instance of ${contractName}`);
const instance = await upgrades_1.Transactions.deployContract(contract, initArgs, this.txParams);
upgrades_1.Loggy.succeed('create-instance', `Deployed instance of ${contractName}`);
if (packageName === this.projectFile.name) {
await this.updateTruffleDeployedInformation(contractName, instance);
}
this.networkFile.addProxy(packageName, contractName, {
address: instance.address,
kind: interfaces_1.ProxyType.NonProxy,
bytecodeHash: upgrades_1.bytecodeDigest(contract.schema.bytecode),
});
return instance;
}
async createProxyInstance(kind, salt, contract, signature, createArgs) {
let instance, proxy;
switch (kind) {
case interfaces_1.ProxyType.Upgradeable:
instance = salt
? await this.project.createProxyWithSalt(contract, salt, signature, createArgs)
: await this.project.createProxy(contract, createArgs);
proxy = await upgrades_1.Proxy.at(instance.address);
break;
case interfaces_1.ProxyType.Minimal:
if (salt) {
throw new Error(`Cannot create a minimal proxy with a precomputed address, use an Upgradeable proxy instead.`);
}
instance = await this.project.createMinimalProxy(contract, createArgs);
proxy = await upgrades_1.MinimalProxy.at(instance.address);
break;
default:
throw new Error(`Unknown proxy type ${kind}`);
}
return { proxy, instance };
}
async getProxyDeploymentAddress(salt, sender) {
await this.migrateManifestVersionIfNeeded();
await this.fetchOrDeploy(this.currentVersion);
const address = await this.project.getProxyDeploymentAddress(salt, sender);
this.tryRegisterProxyFactory();
return address;
}
async getProxySignedDeployment(salt, signature, packageName, contractName, initMethod, initArgs, admin) {
await this.migrateManifestVersionIfNeeded();
await this.fetchOrDeploy(this.currentVersion);
if (!packageName)
packageName = this.projectFile.name;
const contract = this.contractManager.getContractClass(packageName, contractName).upgradeable;
const args = {
packageName,
contractName,
initMethod,
initArgs,
admin,
};
const signer = await this.project.getProxyDeploymentSigner(contract, salt, signature, args);
const address = await this.project.getProxyDeploymentAddress(salt, signer);
this.tryRegisterProxyFactory();
return { address, signer };
}
// Proxy model
async checkDeploymentAddress(salt) {
const deploymentAddress = await this.getProxyDeploymentAddress(salt);
if ((await upgrades_1.ZWeb3.eth.getCode(deploymentAddress)) !== '0x')
throw new Error(`Deployment address for salt ${salt} is already in use`);
}
// Proxy model
async tryRegisterProxyAdmin(adminAddress) {
if (!this.networkFile.proxyAdminAddress) {
const proxyAdminAddress = adminAddress || (await this.project.getAdminAddress());
if (proxyAdminAddress)
this.networkFile.proxyAdmin = { address: proxyAdminAddress };
}
}
// Proxy model
async tryRegisterProxyFactory(factoryAddress) {
if (!this.networkFile.proxyFactoryAddress) {
const proxyFactoryAddress = factoryAddress || (this.project.proxyFactory && this.project.proxyFactory.address);
if (proxyFactoryAddress)
this.networkFile.proxyFactory = { address: proxyFactoryAddress };
}
}
// Proxy model
checkInitialization(contract, calledInitMethod) {
// If there is an initializer called, assume it's ok
if (calledInitMethod)
return;
// Otherwise, warn the user to invoke it
const contractMethods = upgrades_1.contractMethodsFromAbi(contract);
const initializerMethods = contractMethods
.filter(({ hasInitializer, name }) => hasInitializer || name === 'initialize')
.map(({ name }) => name);
if (initializerMethods.length === 0)
return;
upgrades_1.Loggy.noSpin.warn(__filename, 'validateContract', `validate-contract`, `Possible initialization method (${lodash_1.uniq(initializerMethods).join(', ')}) found in contract. Make sure you initialize your instance.`);
}
// Proxy model
async updateTruffleDeployedInformation(contractName, implementation) {
try {
const path = upgrades_1.Contracts.getLocalPath(contractName);
const data = fs_extra_1.default.readJsonSync(path);
if (!data.networks) {
data.networks = {};
}
const networkId = await upgrades_1.ZWeb3.getNetwork();
data.networks[networkId] = {
links: {},
events: {},
address: implementation.address,
// eslint-disable-next-line @typescript-eslint/camelcase
updated_at: Date.now(),
};
fs_extra_1.default.writeJsonSync(path, data, { spaces: 2 });
}
catch (error) {
error.message = `$Could not find ${contractName} in contracts directory. Error: ${error.message}.`;
throw error;
}
}
// Proxy model
async setProxiesAdmin(packageName, contractName, proxyAddress, newAdmin) {
await this.migrateManifestVersionIfNeeded();
const proxies = this.fetchOwnedProxies(packageName, contractName, proxyAddress);
if (proxies.length === 0)
return [];
await this.fetchOrDeploy(this.currentVersion);
await this.changeProxiesAdmin(proxies, newAdmin);
return proxies;
}
// Proxy model
async setProxyAdminOwner(newAdminOwner) {
await this.migrateManifestVersionIfNeeded();
await this.fetchOrDeploy(this.currentVersion);
await this.project.transferAdminOwnership(newAdminOwner);
}
// Proxy model
async changeProxiesAdmin(proxies, newAdmin, project = null) {
if (!project)
project = this.project;
await async_1.allPromisesOrError(lodash_1.map(proxies, async (aProxy) => {
await project.changeProxyAdmin(aProxy.address, newAdmin);
this.networkFile.updateProxy(aProxy, anotherProxy => (Object.assign(Object.assign({}, anotherProxy), { admin: newAdmin })));
}));
}
// Proxy model
async upgradeProxies(packageName, contractName, proxyAddress, initMethod, initArgs) {
await this.migrateManifestVersionIfNeeded();
const proxies = this.fetchOwnedProxies(packageName, contractName, proxyAddress);
if (proxies.length === 0)
return [];
await this.fetchOrDeploy(this.currentVersion);
// Update all out of date proxies
await async_1.allPromisesOrError(lodash_1.map(proxies, proxy => this.upgradeProxy(proxy, initMethod, initArgs)));
return proxies;
}
// Proxy model
async upgradeProxy(proxy, initMethod, initArgs) {
try {
const name = { packageName: proxy.package, contractName: proxy.contractName };
const contract = this.contractManager.getContractClass(proxy.package, proxy.contractName).upgradeable;
await this.setSolidityLibs(contract);
const currentImplementation = await upgrades_1.Proxy.at(proxy.address).implementation();
const contractImplementation = await this.project.getImplementation(name);
const projectVersion = proxy.package === this.projectFile.name
? this.currentVersion
: await this.project.getDependencyVersion(proxy.package);
let newImplementation;
if (currentImplementation !== contractImplementation) {
await this.project.upgradeProxy(proxy.address, contract, Object.assign({ initMethod,
initArgs }, name));
newImplementation = contractImplementation;
}
else {
upgrades_1.Loggy.noSpin(__filename, '_upgradeProxy', `upgrade-proxy-${proxy.address}`, `Contract ${proxy.contractName} at ${proxy.address} is up to date.`);
newImplementation = currentImplementation;
}
this.networkFile.updateProxy(proxy, aProxy => (Object.assign(Object.assign({}, aProxy), { implementation: newImplementation, version: upgrades_1.semanticVersionToString(projectVersion) })));
}
catch (error) {
error.message = `Proxy ${naming_1.toContractFullName(proxy.package, proxy.contractName)} at ${proxy.address} failed to upgrade with error: ${error.message}`;
throw error;
}
}
// Proxy model
fetchOwnedProxies(packageName, contractName, proxyAddress, ownerAddress) {
let criteriaDescription = '';
if (packageName || contractName)
criteriaDescription += ` contract ${naming_1.toContractFullName(packageName, contractName)}`;
if (proxyAddress)
criteriaDescription += ` address ${proxyAddress}`;
const proxies = this.networkFile.getProxies({
package: packageName || (contractName ? this.projectFile.name : undefined),
contractName,
address: proxyAddress,
kind: interfaces_1.ProxyType.Upgradeable,
});
if (lodash_1.isEmpty(proxies)) {
upgrades_1.Loggy.noSpin(__filename, '_fetchOwnedProxies', `fetch-owned-proxies`, `No upgradeable contract instances that match${criteriaDescription} were found`);
return [];
}
const expectedOwner = upgrades_1.ZWeb3.toChecksumAddress(ownerAddress || this.networkFile.proxyAdminAddress);
const ownedProxies = proxies.filter(proxy => !proxy.admin || !expectedOwner || upgrades_1.ZWeb3.toChecksumAddress(proxy.admin) === expectedOwner);
if (lodash_1.isEmpty(ownedProxies)) {
upgrades_1.Loggy.noSpin(__filename, '_fetchOwnedProxies', `fetch-owned-proxies`, `No contract instances that match${criteriaDescription} are owned by this project`);
}
return ownedProxies;
}
// Dependency Controller
async deployDependencies() {
await async_1.allPromisesOrError(lodash_1.map(this.projectFile.dependencies, (version, dep) => this.deployDependencyIfNeeded(dep, version)));
}
// DependencyController
async deployDependencyIfNeeded(depName, depVersion) {
try {
const dependency = new Dependency_1.default(depName, depVersion);
if (dependency.isDeployedOnNetwork(this.network) || this.networkFile.dependencyHasMatchingCustomDeploy(depName))
return;
upgrades_1.Loggy.spin(__filename, 'deployDependencyIfNeeded', `deploy-dependency-${depName}`, `Deploying ${depName} dependency to network ${this.network}`);
const deployment = await dependency.deploy(this.txParams);
this.networkFile.setDependency(depName, {
package: (await deployment.getProjectPackage()).address,
version: deployment.version,
customDeploy: true,
});
upgrades_1.Loggy.succeed(`deploy-dependency-${depName}`);
}
catch (error) {
error.message = `Failed deployment of dependency ${depName} with error: ${error.message}`;
throw error;
}
}
// DependencyController
async handleDependenciesLink() {
await async_1.allPromisesOrError(lodash_1.concat(lodash_1.map(this.projectFile.dependencies, (version, dep) => this.linkDependency(dep, version)), lodash_1.map(this.networkFile.dependenciesNamesMissingFromPackage(), dep => this.unlinkDependency(dep))));
}
// DependencyController
async unlinkDependency(depName) {
try {
if (await this.project.hasDependency(depName)) {
upgrades_1.Loggy.spin(__filename, 'unlinkDependency', `unlink-dependency-${depName}`, `Unlinking dependency ${depName}`);
await this.project.unsetDependency(depName);
upgrades_1.Loggy.succeed(`unlink-dependency-${depName}`);
}
this.networkFile.unsetDependency(depName);
}
catch (error) {
throw Error(`Failed to unlink dependency ${depName} with error: ${error.message}`);
}
}
// DependencyController
async linkDependency(depName, depVersion) {
try {
if (this.networkFile.dependencyHasMatchingCustomDeploy(depName)) {
upgrades_1.Loggy.onVerbose(__filename, 'linkDependency', `link-dependency-${depName}`, `Using custom deployment of ${depName}`);
const depInfo = this.networkFile.getDependency(depName);
return await this.project.setDependency(depName, depInfo.package, depInfo.version);
}
if (!this.networkFile.dependencySatisfiesVersionRequirement(depName)) {
const dependencyInfo = new Dependency_1.default(depName, depVersion).getNetworkFile(this.network);
if (!dependencyInfo.packageAddress)
throw Error(`Dependency '${depName}' has not been published to network '${this.network}', so it cannot be linked. Hint: you can create a custom deployment of all unpublished dependencies by running 'newos push --deploy-dependencies'.`);
upgrades_1.Loggy.spin(__filename, 'linkDependency', `link-dependency-${depName}`, `Linking dependency ${depName} ${dependencyInfo.version}`);
await this.project.setDependency(depName, dependencyInfo.packageAddress, dependencyInfo.version);
const depInfo = {
package: dependencyInfo.packageAddress,
version: dependencyInfo.version,
};
this.networkFile.setDependency(depName, depInfo);
upgrades_1.Loggy.succeed(`link-dependency-${depName}`, `Linked dependency ${depName} ${dependencyInfo.version}`);
}
}
catch (error) {
error.message = `Failed to link dependency ${depName}@${depVersion} with error: ${error.message}`;
throw error;
}
}
// Contract model
getPackageContractError(packageName, contractName) {
if (packageName === this.projectFile.name) {
return this.getContractDeploymentError(contractName);
}
else if (!this.projectFile.hasDependency(packageName)) {
return `Dependency ${packageName} not found in project.`;
}
else if (!this.networkFile.hasDependency(packageName)) {
return `Dependency ${packageName} has not been linked yet. Please run 'newos push'.`;
}
else if (!new Dependency_1.default(packageName).projectFile.hasContract(contractName)) {
return `Contract ${contractName} is not provided by ${packageName}.`;
}
}
updateManifestVersionsIfNeeded(version) {
if (this.networkFile.manifestVersion !== ManifestVersion_2.MANIFEST_VERSION)
this.networkFile.manifestVersion = version;
if (this.projectFile.manifestVersion !== ManifestVersion_2.MANIFEST_VERSION)
this.projectFile.manifestVersion = version;
}
}
exports.default = NetworkController;
//# sourceMappingURL=NetworkController.js.map