UNPKG

@vechain/vebetterdao-contracts

Version:

Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.

213 lines (212 loc) 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.initializeProxyAllVersions = exports.deployUpgradeableWithoutInitialization = exports.deployAndUpgrade = exports.upgradeProxy = exports.initializeProxy = exports.deployProxyWithoutInitialization = exports.deployProxyOnly = exports.deployAndInitializeLatest = exports.deployProxy = void 0; exports.getInitializerData = getInitializerData; const hardhat_1 = require("hardhat"); const upgrades_core_1 = require("@openzeppelin/upgrades-core"); const utils_1 = require("@repo/utils"); const deployProxy = async (contractName, args, libraries = {}, version, logOutput = false) => { // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory(contractName, { libraries: libraries, }); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); logOutput && console.log(`${contractName} impl.: ${await implementation.getAddress()}`); // Deploy the proxy contract, link it to the implementation and call the initializer const proxyFactory = await hardhat_1.ethers.getContractFactory("B3TRProxy"); const proxy = await proxyFactory.deploy(await implementation.getAddress(), getInitializerData(Contract.interface, args, version)); await proxy.waitForDeployment(); logOutput && console.log(`${contractName} proxy: ${await proxy.getAddress()}`); const newImplementationAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await proxy.getAddress()); if (!utils_1.AddressUtils.compareAddresses(newImplementationAddress, await implementation.getAddress())) { throw new Error(`The implementation address is not the one expected: ${newImplementationAddress} !== ${await implementation.getAddress()}`); } // Return an instance of the contract using the proxy address return Contract.attach(await proxy.getAddress()); }; exports.deployProxy = deployProxy; const deployAndInitializeLatest = async (contractName, initializerCalls, libraries = {}, logOutput = false) => { // Deploy implementation + proxy const proxyAddress = await (0, exports.deployProxyOnly)(contractName, libraries, logOutput); // Attach contract instance const Contract = await hardhat_1.ethers.getContractFactory(contractName, { libraries }); const contractAtProxy = Contract.attach(proxyAddress); const signer = (await hardhat_1.ethers.getSigners())[0]; for (const { name, args } of initializerCalls) { const fn = Contract.interface.getFunction(name); if (!fn) throw new Error(`Function ${name} not found in contract ABI`); const data = Contract.interface.encodeFunctionData(fn, args); const tx = await signer.sendTransaction({ to: proxyAddress, data }); await tx.wait(); } return contractAtProxy; }; exports.deployAndInitializeLatest = deployAndInitializeLatest; const deployProxyOnly = async (contractName, libraries = {}, logOutput = false) => { // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory(contractName, { libraries: libraries, }); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); logOutput && console.log(`${contractName} impl.: ${await implementation.getAddress()}`); // Deploy the proxy contract without initialization const proxyFactory = await hardhat_1.ethers.getContractFactory("B3TRProxy"); const proxy = await proxyFactory.deploy(await implementation.getAddress(), "0x"); await proxy.waitForDeployment(); logOutput && console.log(`${contractName} proxy: ${await proxy.getAddress()}`); const newImplementationAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await proxy.getAddress()); if (!utils_1.AddressUtils.compareAddresses(newImplementationAddress, await implementation.getAddress())) { throw new Error(`The implementation address is not the one expected: ${newImplementationAddress} !== ${await implementation.getAddress()}`); } // Return the proxy address return await proxy.getAddress(); }; exports.deployProxyOnly = deployProxyOnly; // It is used to deploy the proxy contract without initializating const deployProxyWithoutInitialization = async (contractName, libraries = {}, logOutput = false) => { // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory(contractName, { libraries: libraries, }); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); logOutput && console.log(`${contractName} impl.: ${await implementation.getAddress()}`); // Deploy the proxy contract without initialization const proxyFactory = await hardhat_1.ethers.getContractFactory("B3TRProxy"); const proxy = await proxyFactory.deploy(await implementation.getAddress(), "0x"); await proxy.waitForDeployment(); logOutput && console.log(`${contractName} proxy: ${await proxy.getAddress()}`); const newImplementationAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await proxy.getAddress()); if (!utils_1.AddressUtils.compareAddresses(newImplementationAddress, await implementation.getAddress())) { throw new Error(`The implementation address is not the one expected: ${newImplementationAddress} !== ${await implementation.getAddress()}`); } // Return the proxy address return await proxy.getAddress(); }; exports.deployProxyWithoutInitialization = deployProxyWithoutInitialization; const initializeProxy = async (proxyAddress, contractName, args, libraries = {}, version) => { // Get the ContractFactory const Contract = await hardhat_1.ethers.getContractFactory(contractName, { libraries: libraries, }); // Prepare the initializer data using getInitializerData const initializerData = getInitializerData(Contract.interface, args, version); // Interact with the proxy contract to call the initializer using the prepared initializer data const signer = (await hardhat_1.ethers.getSigners())[0]; const tx = await signer.sendTransaction({ to: proxyAddress, data: initializerData, gasLimit: 10000000, // Keep! Else, StargateNFT contract init tx will fail }); await tx.wait(); // Return an instance of the contract using the proxy address return Contract.attach(proxyAddress); }; exports.initializeProxy = initializeProxy; const upgradeProxy = async (previousVersionContractName, newVersionContractName, proxyAddress, args = [], options) => { // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory(newVersionContractName, { libraries: options.libraries, }); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); const currentImplementationContract = await hardhat_1.ethers.getContractAt(previousVersionContractName, proxyAddress); if (options.logOutput) { console.log(`${newVersionContractName} impl.: ${await implementation.getAddress()}`); } const tx = await currentImplementationContract.upgradeToAndCall(await implementation.getAddress(), args.length > 0 ? getInitializerData(Contract.interface, args, options.version) : "0x"); await tx.wait(); const newImplementationAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, proxyAddress); if (!utils_1.AddressUtils.compareAddresses(newImplementationAddress, await implementation.getAddress())) { throw new Error(`The implementation address is not the one expected: ${newImplementationAddress} !== ${await implementation.getAddress()}`); } return Contract.attach(proxyAddress); }; exports.upgradeProxy = upgradeProxy; const deployAndUpgrade = async (contractNames, args, options) => { if (contractNames.length === 0) throw new Error("No contracts to deploy"); if (contractNames.length !== args.length) throw new Error(`Contract ${contractNames} and arguments must have the same length`); if (options.libraries && contractNames.length !== options.libraries.length) throw new Error("Contract names and libraries must have the same length"); if (options.versions && contractNames.length !== options.versions.length) throw new Error("Contract names and versions must have the same length"); // 1. Deploy proxy and first implementation const contractName = contractNames[0]; const contractArgs = args[0]; let proxy = await (0, exports.deployProxy)(contractName, contractArgs, options.libraries?.[0], options.versions?.[0], options.logOutput); // 2. Upgrade the proxy to the next versions for (let i = 1; i < contractNames.length; i++) { const previousVersionContractName = contractNames[i - 1]; const newVersionContractName = contractNames[i]; const contractArgs = args[i]; proxy = await (0, exports.upgradeProxy)(previousVersionContractName, newVersionContractName, await proxy.getAddress(), contractArgs, { version: options.versions?.[i], libraries: options.libraries?.[i], logOutput: options.logOutput }); } return proxy; }; exports.deployAndUpgrade = deployAndUpgrade; function getInitializerData(contractInterface, args, version) { const initializer = version ? `initializeV${version}` : "initialize"; const fragment = contractInterface.getFunction(initializer); if (!fragment) { throw new Error(`Contract initializer not found`); } return contractInterface.encodeFunctionData(fragment, args); } const deployUpgradeableWithoutInitialization = async (contractName, libraries = {}, logOutput = false) => { // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory(contractName, { libraries: libraries, }); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); logOutput && console.log(`${contractName} impl.: ${await implementation.getAddress()}`); // Deploy the proxy contract without initialization const proxyFactory = await hardhat_1.ethers.getContractFactory("B3TRProxy"); const proxy = await proxyFactory.deploy(await implementation.getAddress(), "0x"); await proxy.waitForDeployment(); logOutput && console.log(`${contractName} proxy: ${await proxy.getAddress()}`); const newImplementationAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await proxy.getAddress()); if (!utils_1.AddressUtils.compareAddresses(newImplementationAddress, await implementation.getAddress())) { throw new Error(`The implementation address is not the one expected: ${newImplementationAddress} !== ${await implementation.getAddress()}`); } // Return the proxy address return await proxy.getAddress(); }; exports.deployUpgradeableWithoutInitialization = deployUpgradeableWithoutInitialization; const initializeProxyAllVersions = async (contractName, proxyAddress, initializerCalls, logOutput = false) => { // Get contract instance const Contract = await hardhat_1.ethers.getContractAt(contractName, proxyAddress); // Get the signer const signer = (await hardhat_1.ethers.getSigners())[0]; // Call all initializers let upgraderCheck = false; for (const { version, args } of initializerCalls) { logOutput && console.log(`Initializing ${contractName} V${version ?? "1"}...`); if (version !== undefined && upgraderCheck === false) { await revertIfSignerIsNotUpgrader(Contract, await signer.getAddress()); upgraderCheck = true; } const data = getInitializerData(Contract.interface, args, version); const tx = await signer.sendTransaction({ to: proxyAddress, data, gasLimit: 10000000, }); await tx.wait(); } // Return the contract instance return Contract; }; exports.initializeProxyAllVersions = initializeProxyAllVersions; async function revertIfSignerIsNotUpgrader(contract, signerAddress) { const upgraderRole = hardhat_1.ethers.keccak256(hardhat_1.ethers.toUtf8Bytes("UPGRADER_ROLE")); const hasUpgraderRole = await contract.hasRole(upgraderRole, signerAddress); if (!hasUpgraderRole) { throw new Error(`Signer ${signerAddress} is missing UPGRADER_ROLE. Cancelling upgrade.`); } }