@openzeppelin/cli
Version:
Command-line interface for the OpenZeppelin smart contract platform
910 lines • 46 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
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("@openzeppelin/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
fetchOrDeploy(requestedVersion) {
return __awaiter(this, void 0, void 0, function* () {
this.project = yield this.getDeployer(requestedVersion).fetchOrDeploy();
return this.project;
});
}
deployChangedSolidityLibs(contractNames) {
return __awaiter(this, void 0, void 0, function* () {
const libNames = this._getAllSolidityLibNames([contractNames]);
const changedLibraries = this.getLibsToDeploy(libNames, true);
yield this.uploadSolidityLibs(changedLibraries);
});
}
// DeployerController
push(aliases, { reupload = false, force = false } = {}) {
return __awaiter(this, void 0, void 0, function* () {
const changedLibraries = this._solidityLibsForPush(!reupload);
const contractObjects = this._contractsListForPush(aliases, !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();
yield this.fetchOrDeploy(this.projectVersion);
yield this.handleDependenciesLink();
this.checkNotFrozen();
yield this.uploadSolidityLibs(changedLibraries);
yield Promise.all([this.uploadContracts(contractObjects), this.unsetContracts()]);
yield 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
deployProxyFactory() {
return __awaiter(this, void 0, void 0, function* () {
yield this.fetchOrDeploy(this.projectVersion);
yield this.project.ensureProxyFactory();
yield this._tryRegisterProxyFactory();
});
}
// DeployerController
deployProxyAdmin() {
return __awaiter(this, void 0, void 0, function* () {
yield this.fetchOrDeploy(this.projectVersion);
yield this.project.ensureProxyAdmin();
yield this._tryRegisterProxyAdmin();
});
}
// DeployerController
_checkVersion() {
if (this._newVersionRequired()) {
this.networkFile.frozen = false;
this.networkFile.contracts = {};
}
}
// DeployerController
_newVersionRequired() {
return this.projectVersion !== this.currentVersion && this.isPublished;
}
// Contract model
_contractsListForPush(aliases, onlyChanged = false, changedLibraries = []) {
const newVersion = this._newVersionRequired();
aliases = aliases || Object.keys(this.projectFile.contracts);
return aliases
.map(alias => [alias, this.projectFile.contracts[alias]])
.map(([contractAlias, contractName]) => [contractAlias, upgrades_1.Contracts.getFromLocal(contractName)])
.filter(([contractAlias, contract]) => newVersion ||
!onlyChanged ||
this.hasContractChanged(contractAlias, 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, contractAliases } = this.projectFile;
const libNames = this._getAllSolidityLibNames(contractNames);
const clashes = lodash_1.intersection(libNames, contractAliases);
if (!lodash_1.isEmpty(clashes)) {
throw new Error(`Cannot upload libraries with the same name as a contract alias: ${clashes.join(', ')}`);
}
return this.getLibsToDeploy(libNames, onlyChanged);
}
// Contract model || SolidityLib model
uploadSolidityLibs(libs) {
return __awaiter(this, void 0, void 0, function* () {
// Libs may have dependencies, so deploy them in order
for (let i = 0; i < libs.length; i++) {
yield this._uploadSolidityLib(libs[i]);
}
});
}
// Contract model || SolidityLib model
_uploadSolidityLib(libClass) {
return __awaiter(this, void 0, void 0, function* () {
const libName = libClass.schema.contractName;
yield 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.
yield upgrades_1.Transactions.deployContract(libClass)
: yield this.project.setImplementation(libClass, libName);
this.networkFile.addSolidityLib(libName, libInstance);
upgrades_1.Loggy.succeed(`upload-solidity-lib${libName}`, `${libName} library uploaded`);
});
}
// Contract model
uploadContracts(contracts) {
return __awaiter(this, void 0, void 0, function* () {
yield async_1.allPromisesOrError(contracts.map(([contractAlias, contract]) => this.uploadContract(contractAlias, contract)));
});
}
// Contract model
uploadContract(contractAlias, contract) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this._setSolidityLibs(contract);
upgrades_1.Loggy.spin(__filename, 'uploadContract', `upload-contract${contract.schema.contractName}`, `Validating and deploying contract ${contract.schema.contractName}`);
const contractInstance = yield this.project.setImplementation(contract, contractAlias);
const { types, storage } = contract.schema.storageInfo || {
types: null,
storage: null,
};
this.networkFile.addContract(contractAlias, 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 = `${contractAlias} deployment failed with error: ${error.message}`;
throw error;
}
});
}
// Contract model || SolidityLib model
_setSolidityLibs(contract) {
return __awaiter(this, void 0, void 0, function* () {
const currentContractLibs = upgrades_1.getSolidityLibNames(contract.schema.bytecode);
const libraries = this.networkFile.getSolidityLibs(currentContractLibs);
contract.link(libraries);
});
}
// Contract model || SolidityLib model
_unsetSolidityLibs() {
return __awaiter(this, void 0, void 0, function* () {
const { contractNames } = this.projectFile;
const libNames = this._getAllSolidityLibNames(contractNames);
yield async_1.allPromisesOrError(this.networkFile.solidityLibsMissing(libNames).map(libName => this._unsetSolidityLib(libName)));
});
}
// Contract model || SolidityLib model
_unsetSolidityLib(libName) {
return __awaiter(this, void 0, void 0, function* () {
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) {
yield 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
unsetContracts() {
return __awaiter(this, void 0, void 0, function* () {
yield async_1.allPromisesOrError(this.networkFile.contractAliasesMissingFromPackage().map(contractAlias => this.unsetContract(contractAlias)));
});
}
// Contract model
unsetContract(contractAlias) {
return __awaiter(this, void 0, void 0, function* () {
try {
upgrades_1.Loggy.spin(__filename, 'unsetContract', `unset-contract-${contractAlias}`, `Removing ${contractAlias} contract`);
yield this.project.unsetImplementation(contractAlias);
this.networkFile.unsetContract(contractAlias);
upgrades_1.Loggy.succeed(`unset-contract-${contractAlias}`);
}
catch (error) {
error.message = `Removal of ${contractAlias} failed with error: ${error.message}`;
throw error;
}
});
}
// DeployerController || Contract model
validateContracts(contracts, buildArtifacts) {
return lodash_1.every(contracts.map(([contractAlias, contract]) => this.validateContract(contractAlias, contract, buildArtifacts)));
}
// DeployerController || Contract model
validateContract(contractAlias, contract, buildArtifacts) {
try {
const existingContractInfo = this.networkFile.contract(contractAlias) || {};
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
checkContractDeployed(packageName, contractAlias, throwIfFail = false) {
if (!packageName)
packageName = this.projectFile.name;
const err = this._errorForContractDeployed(packageName, contractAlias);
if (err)
this._handleErrorMessage(err, throwIfFail);
}
// Contract model
checkLocalContractsDeployed(throwIfFail = false) {
const err = this._errorForLocalContractsDeployed();
if (err)
this._handleErrorMessage(err, throwIfFail);
}
// Contract model
_errorForLocalContractsDeployed() {
const [contractsDeployed, contractsMissing] = lodash_1.partition(this.projectFile.contractAliases, alias => this.isContractDeployed(alias));
const contractsChanged = lodash_1.filter(contractsDeployed, alias => this.hasContractChanged(alias));
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
checkLocalContractDeployed(contractAlias, throwIfFail = false) {
// if (!packageName) packageName = this.projectFile.name
const err = this._errorForLocalContractDeployed(contractAlias);
if (err)
this._handleErrorMessage(err, throwIfFail);
}
// Contract model
_errorForLocalContractDeployed(contractAlias) {
if (!this.isContractDefined(contractAlias)) {
return `Contract ${contractAlias} not found in this project`;
}
else if (!this.isContractDeployed(contractAlias)) {
return `Contract ${contractAlias} is not deployed to ${this.network}.`;
}
else if (this.hasContractChanged(contractAlias)) {
return `Contract ${contractAlias} has changed locally since the last deploy, consider running 'openzeppelin push'.`;
}
}
// TODO: move to utils folder or somewhere else
_handleErrorMessage(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(contractAlias, contract) {
if (!this.isLocalContract(contractAlias))
return false;
if (!this.isContractDeployed(contractAlias))
return true;
if (!contract) {
const contractName = this.projectFile.contract(contractAlias);
contract = upgrades_1.Contracts.getFromLocal(contractName);
}
return !this.networkFile.hasSameBytecode(contractAlias, contract);
}
// Contract model
isLocalContract(contractAlias) {
return this.projectFile.hasContract(contractAlias);
}
// Contract model
isContractDefined(contractAlias) {
return this.projectFile.hasContract(contractAlias);
}
// Contract model
isContractDeployed(contractAlias) {
return !this.isLocalContract(contractAlias) || this.networkFile.hasContract(contractAlias);
}
// VerifierController
verifyAndPublishContract(contractAlias, optimizer, optimizerRuns, remote, apiKey) {
return __awaiter(this, void 0, void 0, function* () {
upgrades_1.Loggy.spin(__filename, 'verifyAndPublishContract', 'verify-and-publish', `Verifying and publishing contract source code of ${contractAlias} on ${remote} (this usually takes under 30 seconds)`);
const contractName = this.projectFile.contract(contractAlias);
const { compilerVersion, sourcePath } = this.localController.getContractSourcePath(contractAlias);
const contractSource = yield upgrades_1.flattenSourceCode([sourcePath]);
const contractAddress = this.networkFile.contracts[contractAlias].address;
if (this.networkFile.getProxies({ contract: 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.`);
}
yield Verifier_1.default.verifyAndPublish(remote, {
contractName,
compilerVersion,
optimizer,
optimizerRuns,
contractSource,
contractAddress,
apiKey,
network: this.network,
});
});
}
// NetworkController
writeNetworkPackageIfNeeded() {
this.networkFile.write();
this.projectFile.write();
}
// DeployerController
freeze() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.packageAddress)
throw Error('Cannot freeze an unpublished project');
yield this.fetchOrDeploy(this.currentVersion);
if (this.project instanceof upgrades_1.AppProject)
yield 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;
}
_migrate() {
return __awaiter(this, void 0, void 0, function* () {
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
? yield upgrades_1.ProxyAdmin.fetch(this.proxyAdminAddress, this.txParams)
: yield 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)');
yield upgrades_1.Transactions.awaitConfirmations(proxyAdmin.contract.deployment.transactionHash);
upgrades_1.Loggy.succeed('await-confirmations');
}
this._tryRegisterProxyAdmin(proxyAdmin.address);
yield async_1.allPromisesOrError(lodash_1.map(proxies, (proxy) => __awaiter(this, void 0, void 0, function* () {
const proxyInstance = yield upgrades_1.Proxy.at(proxy.address);
const currentAdmin = yield 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}`);
}
});
}
migrateManifestVersionIfNeeded() {
return __awaiter(this, void 0, void 0, function* () {
if (ManifestVersion_1.isMigratableManifestVersion(this.currentManifestVersion))
yield this._migrate();
this.updateManifestVersionsIfNeeded(ManifestVersion_2.MANIFEST_VERSION);
});
}
// DeployerController
publish() {
return __awaiter(this, void 0, void 0, function* () {
if (this.appAddress) {
upgrades_1.Loggy.noSpin(__filename, 'publish', 'publish-project', `Project is already published to ${this.network}`);
return;
}
yield this.migrateManifestVersionIfNeeded();
const proxyAdminProject = (yield this.fetchOrDeploy(this.currentVersion));
const deployer = new ProjectDeployer_1.AppProjectDeployer(this, this.projectVersion);
this.project = yield deployer.fromProxyAdminProject(proxyAdminProject);
upgrades_1.Loggy.succeed(`publish-project`, `Published to ${this.network}!`);
});
}
// Proxy model
createProxy(packageName, contractAlias, initMethod, initArgs, admin, salt, signature, kind) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this.migrateManifestVersionIfNeeded();
yield this.fetchOrDeploy(this.currentVersion);
if (!packageName)
packageName = this.projectFile.name;
const contract = this.contractManager.getContractClass(packageName, contractAlias);
yield this._setSolidityLibs(contract);
this.checkInitialization(contract, initMethod);
if (salt)
yield this._checkDeploymentAddress(salt);
const createArgs = {
packageName,
contractName: contractAlias,
initMethod,
initArgs,
admin,
};
const { proxy, instance } = yield this.createProxyInstance(kind, salt, contract, signature, createArgs);
const implementationAddress = yield proxy.implementation();
const projectVersion = packageName === this.projectFile.name
? this.currentVersion
: yield this.project.getDependencyVersion(packageName);
yield this._updateTruffleDeployedInformation(contractAlias, instance);
this.networkFile.addProxy(packageName, contractAlias, {
address: instance.address,
version: upgrades_1.semanticVersionToString(projectVersion),
implementation: implementationAddress,
admin: admin || this.networkFile.proxyAdminAddress || (yield this.project.getAdminAddress()),
kind,
});
return instance;
}
finally {
yield this._tryRegisterProxyAdmin();
yield this._tryRegisterProxyFactory();
}
});
}
createInstance(packageName, contractAlias, initArgs) {
return __awaiter(this, void 0, void 0, function* () {
yield this.migrateManifestVersionIfNeeded();
if (!packageName) {
packageName = this.projectFile.name;
}
if (packageName === this.projectFile.name) {
yield this.deployChangedSolidityLibs(contractAlias);
}
const contract = this.contractManager.getContractClass(packageName, contractAlias);
yield this._setSolidityLibs(contract);
upgrades_1.Loggy.spin(__filename, 'createInstance', 'create-instance', `Deploying an instance of ${contractAlias}`);
const instance = yield upgrades_1.Transactions.deployContract(contract, initArgs, this.txParams);
upgrades_1.Loggy.succeed('create-instance', `Deployed instance of ${contractAlias}`);
if (packageName === this.projectFile.name) {
yield this._updateTruffleDeployedInformation(contractAlias, instance);
}
this.networkFile.addProxy(packageName, contractAlias, {
address: instance.address,
kind: interfaces_1.ProxyType.NonProxy,
bytecodeHash: upgrades_1.bytecodeDigest(contract.schema.bytecode),
});
return instance;
});
}
createProxyInstance(kind, salt, contract, signature, createArgs) {
return __awaiter(this, void 0, void 0, function* () {
let instance, proxy;
switch (kind) {
case interfaces_1.ProxyType.Upgradeable:
instance = salt
? yield this.project.createProxyWithSalt(contract, salt, signature, createArgs)
: yield this.project.createProxy(contract, createArgs);
proxy = yield 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 = yield this.project.createMinimalProxy(contract, createArgs);
proxy = yield upgrades_1.MinimalProxy.at(instance.address);
break;
default:
throw new Error(`Unknown proxy type ${kind}`);
}
return { proxy, instance };
});
}
getProxyDeploymentAddress(salt, sender) {
return __awaiter(this, void 0, void 0, function* () {
yield this.migrateManifestVersionIfNeeded();
yield this.fetchOrDeploy(this.currentVersion);
const address = yield this.project.getProxyDeploymentAddress(salt, sender);
this._tryRegisterProxyFactory();
return address;
});
}
getProxySignedDeployment(salt, signature, packageName, contractAlias, initMethod, initArgs, admin) {
return __awaiter(this, void 0, void 0, function* () {
yield this.migrateManifestVersionIfNeeded();
yield this.fetchOrDeploy(this.currentVersion);
if (!packageName)
packageName = this.projectFile.name;
const contract = this.contractManager.getContractClass(packageName, contractAlias);
const args = {
packageName,
contractName: contractAlias,
initMethod,
initArgs,
admin,
};
const signer = yield this.project.getProxyDeploymentSigner(contract, salt, signature, args);
const address = yield this.project.getProxyDeploymentAddress(salt, signer);
this._tryRegisterProxyFactory();
return { address, signer };
});
}
// Proxy model
_checkDeploymentAddress(salt) {
return __awaiter(this, void 0, void 0, function* () {
const deploymentAddress = yield this.getProxyDeploymentAddress(salt);
if ((yield upgrades_1.ZWeb3.eth.getCode(deploymentAddress)) !== '0x')
throw new Error(`Deployment address for salt ${salt} is already in use`);
});
}
// Proxy model
_tryRegisterProxyAdmin(adminAddress) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.networkFile.proxyAdminAddress) {
const proxyAdminAddress = adminAddress || (yield this.project.getAdminAddress());
if (proxyAdminAddress)
this.networkFile.proxyAdmin = { address: proxyAdminAddress };
}
});
}
// Proxy model
_tryRegisterProxyFactory(factoryAddress) {
return __awaiter(this, void 0, void 0, function* () {
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
_updateTruffleDeployedInformation(contractAlias, implementation) {
return __awaiter(this, void 0, void 0, function* () {
const contractName = this.projectFile.contract(contractAlias);
if (contractName) {
const path = upgrades_1.Contracts.getLocalPath(contractName);
const data = fs_extra_1.default.readJsonSync(path);
if (!data.networks) {
data.networks = {};
}
const networkId = yield 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 });
}
});
}
// Proxy model
setProxiesAdmin(packageName, contractAlias, proxyAddress, newAdmin) {
return __awaiter(this, void 0, void 0, function* () {
yield this.migrateManifestVersionIfNeeded();
const proxies = this._fetchOwnedProxies(packageName, contractAlias, proxyAddress);
if (proxies.length === 0)
return [];
yield this.fetchOrDeploy(this.currentVersion);
yield this._changeProxiesAdmin(proxies, newAdmin);
return proxies;
});
}
// Proxy model
setProxyAdminOwner(newAdminOwner) {
return __awaiter(this, void 0, void 0, function* () {
yield this.migrateManifestVersionIfNeeded();
yield this.fetchOrDeploy(this.currentVersion);
yield this.project.transferAdminOwnership(newAdminOwner);
});
}
// Proxy model
_changeProxiesAdmin(proxies, newAdmin, project = null) {
return __awaiter(this, void 0, void 0, function* () {
if (!project)
project = this.project;
yield async_1.allPromisesOrError(lodash_1.map(proxies, (aProxy) => __awaiter(this, void 0, void 0, function* () {
yield project.changeProxyAdmin(aProxy.address, newAdmin);
this.networkFile.updateProxy(aProxy, anotherProxy => (Object.assign(Object.assign({}, anotherProxy), { admin: newAdmin })));
})));
});
}
// Proxy model
upgradeProxies(packageName, contractAlias, proxyAddress, initMethod, initArgs) {
return __awaiter(this, void 0, void 0, function* () {
yield this.migrateManifestVersionIfNeeded();
const proxies = this._fetchOwnedProxies(packageName, contractAlias, proxyAddress);
if (proxies.length === 0)
return [];
yield this.fetchOrDeploy(this.currentVersion);
// Update all out of date proxies
yield async_1.allPromisesOrError(lodash_1.map(proxies, proxy => this._upgradeProxy(proxy, initMethod, initArgs)));
return proxies;
});
}
// Proxy model
_upgradeProxy(proxy, initMethod, initArgs) {
return __awaiter(this, void 0, void 0, function* () {
try {
const name = { packageName: proxy.package, contractName: proxy.contract };
const contract = this.contractManager.getContractClass(proxy.package, proxy.contract);
yield this._setSolidityLibs(contract);
const currentImplementation = yield upgrades_1.Proxy.at(proxy.address).implementation();
const contractImplementation = yield this.project.getImplementation(name);
const projectVersion = proxy.package === this.projectFile.name
? this.currentVersion
: yield this.project.getDependencyVersion(proxy.package);
let newImplementation;
if (currentImplementation !== contractImplementation) {
yield 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.contract} 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.contract)} at ${proxy.address} failed to upgrade with error: ${error.message}`;
throw error;
}
});
}
// Proxy model
_fetchOwnedProxies(packageName, contractAlias, proxyAddress, ownerAddress) {
let criteriaDescription = '';
if (packageName || contractAlias)
criteriaDescription += ` contract ${naming_1.toContractFullName(packageName, contractAlias)}`;
if (proxyAddress)
criteriaDescription += ` address ${proxyAddress}`;
const proxies = this.networkFile.getProxies({
package: packageName || (contractAlias ? this.projectFile.name : undefined),
contract: contractAlias,
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
deployDependencies() {
return __awaiter(this, void 0, void 0, function* () {
yield async_1.allPromisesOrError(lodash_1.map(this.projectFile.dependencies, (version, dep) => this.deployDependencyIfNeeded(dep, version)));
});
}
// DependencyController
deployDependencyIfNeeded(depName, depVersion) {
return __awaiter(this, void 0, void 0, function* () {
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 = yield dependency.deploy(this.txParams);
this.networkFile.setDependency(depName, {
package: (yield 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
handleDependenciesLink() {
return __awaiter(this, void 0, void 0, function* () {
yield 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
unlinkDependency(depName) {
return __awaiter(this, void 0, void 0, function* () {
try {
if (yield this.project.hasDependency(depName)) {
upgrades_1.Loggy.spin(__filename, 'unlinkDependency', `unlink-dependency-${depName}`, `Unlinking dependency ${depName}`);
yield 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
linkDependency(depName, depVersion) {
return __awaiter(this, void 0, void 0, function* () {
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 yield 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 'openzeppelin push --deploy-dependencies'.`);
upgrades_1.Loggy.spin(__filename, 'linkDependency', `link-dependency-${depName}`, `Linking dependency ${depName} ${dependencyInfo.version}`);
yield 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
_errorForContractDeployed(packageName, contractAlias) {
if (packageName === this.projectFile.name) {
return this._errorForLocalContractDeployed(contractAlias);
}
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 'openzeppelin push'.`;
}
else if (!new Dependency_1.default(packageName).projectFile.contract(contractAlias)) {
return `Contract ${contractAlias} 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