From ae716fc2fefe162b316b48e293c863f58a394cd4 Mon Sep 17 00:00:00 2001 From: donosonaumczuk Date: Thu, 4 Apr 2024 17:01:22 -0300 Subject: [PATCH] misc: Script added to deploy fresh Lens V2 --- script/DeployFreshLensV2.s.sol | 493 +++++++++++++++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 script/DeployFreshLensV2.s.sol diff --git a/script/DeployFreshLensV2.s.sol b/script/DeployFreshLensV2.s.sol new file mode 100644 index 0000000..eda9a94 --- /dev/null +++ b/script/DeployFreshLensV2.s.sol @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {ForkManagement} from 'script/helpers/ForkManagement.sol'; +import 'forge-std/Script.sol'; +import {LensV2UpgradeContract} from 'contracts/misc/LensV2UpgradeContract.sol'; +import {FollowNFT} from 'contracts/FollowNFT.sol'; +import {LensHandles} from 'contracts/namespaces/LensHandles.sol'; +import {TokenHandleRegistry} from 'contracts/namespaces/TokenHandleRegistry.sol'; +import {TransparentUpgradeableProxy} from '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol'; +import {Governance} from 'contracts/misc/access/Governance.sol'; +import {LensV2UpgradeContract} from 'contracts/misc/LensV2UpgradeContract.sol'; +import {ProxyAdmin} from 'contracts/misc/access/ProxyAdmin.sol'; +import {LensHubInitializable} from 'contracts/misc/LensHubInitializable.sol'; +import {ILensHub} from 'contracts/interfaces/ILensHub.sol'; +import {ILensHandles} from 'contracts/interfaces/ILensHandles.sol'; +import {ITokenHandleRegistry} from 'contracts/interfaces/ITokenHandleRegistry.sol'; +import {PermissionlessCreator} from 'contracts/misc/PermissionlessCreator.sol'; +import {CreditsFaucet} from 'contracts/misc/CreditsFaucet.sol'; +import {CollectNFT} from 'contracts/modules/act/collect/CollectNFT.sol'; +import {CollectPublicationAction} from 'contracts/modules/act/collect/CollectPublicationAction.sol'; +import {SimpleFeeCollectModule} from 'contracts/modules/act/collect/SimpleFeeCollectModule.sol'; +import {MultirecipientFeeCollectModule} from 'contracts/modules/act/collect/MultirecipientFeeCollectModule.sol'; +import {FeeFollowModule} from './../contracts/modules/follow/FeeFollowModule.sol'; +import {RevertFollowModule} from 'contracts/modules/follow/RevertFollowModule.sol'; +import {DegreesOfSeparationReferenceModule} from 'contracts/modules/reference/DegreesOfSeparationReferenceModule.sol'; +import {FollowerOnlyReferenceModule} from 'contracts/modules/reference/FollowerOnlyReferenceModule.sol'; +import {TokenGatedReferenceModule} from 'contracts/modules/reference/TokenGatedReferenceModule.sol'; +import {Types} from 'contracts/libraries/constants/Types.sol'; +import {ModuleRegistry} from 'contracts/misc/ModuleRegistry.sol'; +import {IModuleRegistry} from 'contracts/interfaces/IModuleRegistry.sol'; +import {LitAccessControl} from 'contracts/misc/access/LitAccessControl.sol'; +import {PublicActProxy} from './../contracts/misc/PublicActProxy.sol'; +import {Governance} from './../contracts/misc/access/Governance.sol'; +import {ProxyAdmin} from './../contracts/misc/access/ProxyAdmin.sol'; +import {ERC2981CollectionRoyalties} from './../contracts/base/ERC2981CollectionRoyalties.sol'; + +import {ArrayHelpers} from 'test/helpers/ArrayHelpers.sol'; + +contract DeployFreshLensV2 is Script, ForkManagement, ArrayHelpers { + using stdJson for string; + + string addressesFile = 'addressesV2.txt'; + + bytes32 constant ADMIN_SLOT = bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1); + string mnemonic; + + uint256 internal PROFILE_GUARDIAN_COOLDOWN; + uint256 internal HANDLE_GUARDIAN_COOLDOWN; + + struct LensAccount { + uint256 ownerPk; + address owner; + uint256 profileId; + } + + LensAccount deployer; + LensAccount governance; + LensAccount proxyAdmin; + LensAccount treasury; + LensAccount profileCreator; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + ModuleRegistry moduleRegistryImpl; + TransparentUpgradeableProxy moduleRegistryProxy; + ModuleRegistry moduleRegistry; + FollowNFT followNFT; + LensHubInitializable lensHubImpl; + TransparentUpgradeableProxy hubProxy; + ILensHub hub; + LensHandles handlesImpl; + TransparentUpgradeableProxy handlesProxy; + ILensHandles handles; + TokenHandleRegistry tokenHandleRegistryImpl; + TransparentUpgradeableProxy tokenHandleRegistryProxy; + ITokenHandleRegistry tokenHandleRegistry; + PermissionlessCreator permissionlessCreatorImpl; + TransparentUpgradeableProxy permissionlessCreatorProxy; + PermissionlessCreator permissionlessCreator; + CreditsFaucet creditsFaucet; + CollectNFT collectNFT; + CollectPublicationAction collectPublicationActionImpl; + TransparentUpgradeableProxy collectPublicationActionProxy; + CollectPublicationAction collectPublicationAction; + SimpleFeeCollectModule simpleFeeCollectModule; + MultirecipientFeeCollectModule multirecipientFeeCollectModule; + FeeFollowModule feeFollowModule; + RevertFollowModule revertFollowModule; + DegreesOfSeparationReferenceModule degreesOfSeparationReferenceModule; + FollowerOnlyReferenceModule followerOnlyReferenceModule; + TokenGatedReferenceModule tokenGatedReferenceModule; + LitAccessControl litAccessControlImpl; + TransparentUpgradeableProxy litAccessControlProxy; + LitAccessControl litAccessControl; + PublicActProxy publicActProxy; + Governance governanceContract; + ProxyAdmin proxyAdminContract; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + function loadPrivateKeys() internal { + if (isEnvSet('MNEMONIC')) { + mnemonic = vm.envString('MNEMONIC'); + } + + if (bytes(mnemonic).length == 0) { + revert('Missing mnemonic'); + } + + console.log('\n- - - CURRENT BLOCK: ', block.number); + + (deployer.owner, deployer.ownerPk) = deriveRememberKey(mnemonic, 0); + console.log('\n- - - DEPLOYER: %s', deployer.owner); + (governance.owner, governance.ownerPk) = deriveRememberKey(mnemonic, 1); + console.log('\n- - - GOVERNANCE: %s', governance.owner); + (proxyAdmin.owner, proxyAdmin.ownerPk) = deriveRememberKey(mnemonic, 2); + console.log('\n- - - PROXYADMIN: %s', proxyAdmin.owner); + (treasury.owner, treasury.ownerPk) = deriveRememberKey(mnemonic, 3); + console.log('\n- - - TREASURY: %s', treasury.owner); + profileCreator.owner = 0x6C1e1bC39b13f9E0Af9424D76De899203F47755F; + console.log('\n- - - PROFILE CREATOR: %s', profileCreator.owner); + } + + function loadDeployParams() internal { + HANDLE_GUARDIAN_COOLDOWN = json.readUint( + string(abi.encodePacked('.', targetEnv, '.LensHandlesGuardianTimelock')) + ); + if (HANDLE_GUARDIAN_COOLDOWN == 0) { + console.log('HANDLE_GUARDIAN_COOLDOWN not set'); + revert('HANDLE_GUARDIAN_COOLDOWN not set'); + } + console.log('HANDLE_GUARDIAN_COOLDOWN: %s', HANDLE_GUARDIAN_COOLDOWN); + + PROFILE_GUARDIAN_COOLDOWN = json.readUint( + string(abi.encodePacked('.', targetEnv, '.LensProfilesGuardianTimelock')) + ); + if (PROFILE_GUARDIAN_COOLDOWN == 0) { + console.log('PROFILE_GUARDIAN_COOLDOWN not set'); + revert('PROFILE_GUARDIAN_COOLDOWN not set'); + } + console.log('PROFILE_GUARDIAN_COOLDOWN: %s', PROFILE_GUARDIAN_COOLDOWN); + } + + function _deployCore() internal { + moduleRegistryImpl = new ModuleRegistry(); + _logDeployedAddress(address(moduleRegistryImpl), 'ModuleRegistryImpl'); + moduleRegistryProxy = new TransparentUpgradeableProxy({ + _logic: address(moduleRegistryImpl), + admin_: proxyAdmin.owner, + _data: '' + }); + _logDeployedAddress(address(moduleRegistryProxy), 'ModuleRegistryProxy'); + moduleRegistry = ModuleRegistry(address(moduleRegistryProxy)); + + uint256 currentDeployerNonce = vm.getNonce(deployer.owner); + /** + * FollowNFT (currentDeployerNonce) + * LensHubInitializable aka LensHubImpl (currentDeployerNonce + 1) + * TransparentUpgradeableProxy aka LensHubProxy (currentDeployerNonce + 2) + */ + uint256 lensHubProxyDeploymentNonce = currentDeployerNonce + 2; + address expectedLensHubProxyAddress = computeCreateAddress(deployer.owner, lensHubProxyDeploymentNonce); + + followNFT = new FollowNFT(expectedLensHubProxyAddress); + _logDeployedAddress(address(followNFT), 'FollowNFT'); + + lensHubImpl = new LensHubInitializable({ + followNFTImpl: address(followNFT), + collectNFTImpl: address(0), // No needed on a fresh V2 deployment, no publications will have legacy collect. + moduleRegistry: address(moduleRegistry), + tokenGuardianCooldown: PROFILE_GUARDIAN_COOLDOWN, + migrationParams: Types.MigrationParams({ + lensHandlesAddress: address(0), + tokenHandleRegistryAddress: address(0), + legacyFeeFollowModule: address(0), + legacyProfileFollowModule: address(0), + newFeeFollowModule: address(0) + }) // No needed on a fresh V2 deployment, no migration needed. + }); + _logDeployedAddress(address(lensHubImpl), 'LensHubImpl'); + + hubProxy = new TransparentUpgradeableProxy({ + _logic: address(lensHubImpl), + admin_: proxyAdmin.owner, + _data: abi.encodeWithSelector( + LensHubInitializable.initialize.selector, + 'Lens Protocol Profiles', // Name + 'LPP', // Symbol + governance.owner + ) + }); + hub = ILensHub(address(hubProxy)); + _logDeployedAddress(address(hub), 'LensHubProxy'); + + handlesImpl = new LensHandles({ + owner: governance.owner, + lensHub: address(hub), + tokenGuardianCooldown: HANDLE_GUARDIAN_COOLDOWN + }); + _logDeployedAddress(address(handlesImpl), 'LensHandlesImpl'); + + handlesProxy = new TransparentUpgradeableProxy({ + _logic: address(handlesImpl), + admin_: proxyAdmin.owner, + _data: '' + }); + _logDeployedAddress(address(handlesProxy), 'LensHandles'); + handles = ILensHandles(address(handlesProxy)); + + tokenHandleRegistryImpl = new TokenHandleRegistry({lensHub: address(hub), lensHandles: address(handles)}); + _logDeployedAddress(address(tokenHandleRegistryImpl), 'TokenHandleRegistryImpl'); + + tokenHandleRegistryProxy = new TransparentUpgradeableProxy({ + _logic: address(tokenHandleRegistryImpl), + admin_: proxyAdmin.owner, + _data: '' + }); + _logDeployedAddress(address(tokenHandleRegistryProxy), 'TokenHandleRegistry'); + tokenHandleRegistry = ITokenHandleRegistry(address(tokenHandleRegistryProxy)); + + permissionlessCreatorImpl = new PermissionlessCreator( + governance.owner, + address(hub), + address(handles), + address(tokenHandleRegistry) + ); + permissionlessCreatorProxy = new TransparentUpgradeableProxy({ + _logic: address(permissionlessCreatorImpl), + admin_: proxyAdmin.owner, + _data: '' + }); + permissionlessCreator = PermissionlessCreator(address(permissionlessCreatorProxy)); + _logDeployedAddress(address(permissionlessCreator), 'PermissionlessCreator'); + + if (isTestnet) { + creditsFaucet = new CreditsFaucet(address(permissionlessCreatorProxy)); + _logDeployedAddress(address(creditsFaucet), 'CreditsFaucet'); + // Credit faucet is added as provider in the permissionless creator later in this script. + } + + currentDeployerNonce = vm.getNonce(deployer.owner); + /** + * CollectNFT (currentDeployerNonce) + * CollectPublicationAction aka CollectPublicationActionImpl (currentDeployerNonce + 1) + * TransparentUpgradeableProxy aka CollectPublicationActionProxy (currentDeployerNonce + 2) + */ + uint256 collectPublicationActionProxyDeploymentNonce = currentDeployerNonce + 2; + address expectedCollectPublicationActionProxyAddress = computeCreateAddress( + deployer.owner, + collectPublicationActionProxyDeploymentNonce + ); + + collectNFT = new CollectNFT({hub: address(hub), actionModule: expectedCollectPublicationActionProxyAddress}); + _logDeployedAddress(address(collectNFT), 'CollectNFT'); + + collectPublicationActionImpl = new CollectPublicationAction({ + hub: address(hub), + collectNFTImpl: address(collectNFT), + moduleOwner: governance.owner + }); + _logDeployedAddress(address(collectPublicationActionImpl), 'CollectPublicationActionImpl'); + + collectPublicationActionProxy = new TransparentUpgradeableProxy({ + _logic: address(collectPublicationActionImpl), + admin_: proxyAdmin.owner, + _data: '' + }); + _logDeployedAddress(address(collectPublicationActionProxy), 'CollectPublicationActionProxy'); + collectPublicationAction = CollectPublicationAction(address(collectPublicationActionProxy)); + } + + function _deployFollowModules() internal { + feeFollowModule = new FeeFollowModule({ + hub: address(hub), + moduleRegistry: address(moduleRegistry), + moduleOwner: governance.owner + }); + _logDeployedAddress(address(feeFollowModule), 'FeeFollowModule'); + + revertFollowModule = new RevertFollowModule({moduleOwner: governance.owner}); + _logDeployedAddress(address(revertFollowModule), 'RevertFollowModule'); + } + + function _deployCollectModules() internal { + simpleFeeCollectModule = new SimpleFeeCollectModule({ + hub: address(hub), + actionModule: address(collectPublicationActionProxy), + moduleRegistry: address(moduleRegistry), + moduleOwner: governance.owner + }); + _logDeployedAddress(address(simpleFeeCollectModule), 'SimpleFeeCollectModule'); + + multirecipientFeeCollectModule = new MultirecipientFeeCollectModule({ + hub: address(hub), + actionModule: address(collectPublicationActionProxy), + moduleRegistry: address(moduleRegistry), + moduleOwner: governance.owner + }); + _logDeployedAddress(address(multirecipientFeeCollectModule), 'MultirecipientFeeCollectModule'); + } + + function _deployReferenceModules() internal { + degreesOfSeparationReferenceModule = new DegreesOfSeparationReferenceModule({ + hub: address(hub), + moduleOwner: governance.owner + }); + _logDeployedAddress(address(degreesOfSeparationReferenceModule), 'DegreesOfSeparationReferenceModule'); + + followerOnlyReferenceModule = new FollowerOnlyReferenceModule({ + hub: address(hub), + moduleOwner: governance.owner + }); + _logDeployedAddress(address(followerOnlyReferenceModule), 'FollowerOnlyReferenceModule'); + } + + function _deployPeripherialContracts() internal { + litAccessControlImpl = new LitAccessControl(address(hub), address(collectPublicationAction)); + litAccessControlProxy = new TransparentUpgradeableProxy({ + _logic: address(litAccessControlImpl), + admin_: proxyAdmin.owner, + _data: '' + }); + litAccessControl = LitAccessControl(address(litAccessControlProxy)); + _logDeployedAddress(address(litAccessControl), 'LitAccessControl'); + + publicActProxy = new PublicActProxy({ + lensHub: address(hub), + collectPublicationAction: address(collectPublicationAction) + }); + _logDeployedAddress(address(publicActProxy), 'PublicActProxy'); + } + + // TODO: Use from test/ContractAddresses? + struct Currency { + address addy; + string symbol; + } + + function _registerCurrencies() internal { + console.log('\n[Registering currencies]'); + + Currency[] memory currencies = abi.decode( + vm.parseJson(json, string(abi.encodePacked('.', targetEnv, '.Currencies'))), + (Currency[]) + ); + + for (uint256 i = 0; i < currencies.length; i++) { + moduleRegistry.registerErc20Currency(currencies[i].addy); + console.log('\n* * * ', currencies[i].symbol, ' registered as currency: ', currencies[i].addy); + vm.writeLine( + addressesFile, + string.concat(currencies[i].symbol, string.concat(': ', vm.toString(currencies[i].addy))) + ); + } + } + + function _registerModules() internal { + // Follow modules + moduleRegistry.registerModule(address(feeFollowModule), uint256(IModuleRegistry.ModuleType.FOLLOW_MODULE)); + console.log('\n* * * FeeFollowModule registered as follow module'); + + moduleRegistry.registerModule(address(revertFollowModule), uint256(IModuleRegistry.ModuleType.FOLLOW_MODULE)); + console.log('\n* * * RevertFollowModule registered as follow module'); + + // Reference modules + moduleRegistry.registerModule( + address(degreesOfSeparationReferenceModule), + uint256(IModuleRegistry.ModuleType.REFERENCE_MODULE) + ); + console.log('\n* * * DegreesOfSeparationReferenceModule registered'); + + moduleRegistry.registerModule( + address(followerOnlyReferenceModule), + uint256(IModuleRegistry.ModuleType.REFERENCE_MODULE) + ); + console.log('\n* * * FollowerOnlyReferenceModule registered'); + + // Collect modules + moduleRegistry.registerModule( + address(collectPublicationAction), + uint256(IModuleRegistry.ModuleType.PUBLICATION_ACTION_MODULE) + ); + console.log('\n* * * CollectPublicationAction registered as action module'); + + collectPublicationAction.registerCollectModule(address(simpleFeeCollectModule)); + console.log('\n* * * SimpleFeeCollectModule registered as collect module'); + + collectPublicationAction.registerCollectModule(address(multirecipientFeeCollectModule)); + console.log('\n* * * MultirecipientFeeCollectModule registered as collect module'); + } + + function _logDeployedAddress(address deployedAddress, string memory addressLabel) internal { + console.log('\n+ + + ', addressLabel, ': ', deployedAddress); + vm.writeLine(addressesFile, string.concat(addressLabel, string.concat(': ', vm.toString(deployedAddress)))); + } + + function deploy() internal { + loadDeployParams(); + + vm.startBroadcast(deployer.ownerPk); + { + _deployCore(); + + _deployCollectModules(); + + _deployFollowModules(); + + _deployReferenceModules(); + + _deployPeripherialContracts(); + } + vm.stopBroadcast(); + + vm.startBroadcast(governance.ownerPk); + { + if (isTestnet) { + console.log('\n[Adding Credits Faucet as Credit Provider]'); + permissionlessCreator.addCreditProvider(address(creditsFaucet)); + } + + _registerCurrencies(); + + hub.whitelistProfileCreator(address(permissionlessCreator), true); + console.log('\n* * * Permissionless creator contract registered as profile creator'); + + _registerModules(); + + hub.setState(Types.ProtocolState.Unpaused); + console.log('\n* * * Protocol unpaused'); + + hub.setTreasury(address(treasury.owner)); + + uint256 treasuryFee = json.readUint(string(abi.encodePacked('.', targetEnv, '.TreasuryFee'))); + if (treasuryFee > 10_000) { + revert('Treasury fee exceeding max BPS'); + } + hub.setTreasuryFee(uint16(treasuryFee)); + + uint256 profileRoyaltyFee = json.readUint(string(abi.encodePacked('.', targetEnv, '.ProfileRoyaltyFee'))); + ERC2981CollectionRoyalties(address(hub)).setRoyalty(profileRoyaltyFee); + + uint256 handleRoyaltyFee = json.readUint(string(abi.encodePacked('.', targetEnv, '.HandleRoyaltyFee'))); + ERC2981CollectionRoyalties(address(handles)).setRoyalty(handleRoyaltyFee); + + uint256 anonymousProfileId = permissionlessCreator.createProfile({ + createProfileParams: Types.CreateProfileParams({ + to: deployer.owner, + followModule: address(0), + followModuleInitData: '' + }), + delegatedExecutors: new address[](0) + }); + + vm.writeLine(addressesFile, string.concat('AnonymousProfileId :', vm.toString(anonymousProfileId))); + } + vm.stopBroadcast(); + + vm.startBroadcast(deployer.ownerPk); + { + // Deploy governance and proxy-admin controllable-by-contract contracts, and transfer ownership. + governanceContract = new Governance(address(hub), governance.owner); + _logDeployedAddress(address(governanceContract), 'GovernanceContract'); + + proxyAdminContract = new ProxyAdmin(address(hub), address(0), proxyAdmin.owner); + _logDeployedAddress(address(proxyAdminContract), 'ProxyAdminContract'); + } + vm.stopBroadcast(); + + vm.startBroadcast(governance.ownerPk); + { + hub.setGovernance(address(governanceContract)); + // hub.setEmergencyAdmin(governance.owner); // TODO: WHO ? + } + vm.stopBroadcast(); + + vm.startBroadcast(proxyAdmin.ownerPk); + { + hubProxy.changeAdmin(address(proxyAdminContract)); + } + vm.stopBroadcast(); + } + + function run(string memory targetEnv_) external { + targetEnv = targetEnv_; + loadJson(); + checkNetworkParams(); + loadPrivateKeys(); + deploy(); + // _writeBackendEnvFile(); + // _interact(); + } +}