UNPKG

@newos/cli

Version:
822 lines 40.7 kB
"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