diff --git a/script/BaseData.s.sol b/script/BaseData.s.sol new file mode 100644 index 0000000..8e634c2 --- /dev/null +++ b/script/BaseData.s.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {Script} from "forge-std/Script.sol"; + +contract BaseData is Script { + struct Addresses { + address OFT_DELEGATE; + address TOKEN_ADMIN; + address PROXY_ADMIN; + address LZ_ENDPOINT; + } + + struct ChainIds { + uint256 mainnet; + uint256 base; + uint256 optimism; + uint256 arbitrum; + uint256 fraxtal; + uint256 holesky; + uint256 fraxtalTestnet; + } + + mapping(bool => Addresses) private __addresses; // isTestnet => Actors + + mapping(uint256 => uint32) private __chainIdToLzEID; + + ChainIds private __chainIds = ChainIds({ + mainnet: 1, + base: 8453, + optimism: 10, + arbitrum: 42161, + fraxtal: 252, + holesky: 17000, + fraxtalTestnet: 2522 + }); + + function setUp() public virtual { + // mainnets + __addresses[true] = Addresses({ + OFT_DELEGATE: 0xfcad670592a3b24869C0b51a6c6FDED4F95D6975, // yn security council + TOKEN_ADMIN: 0xfcad670592a3b24869C0b51a6c6FDED4F95D6975, + PROXY_ADMIN: 0xfcad670592a3b24869C0b51a6c6FDED4F95D6975, + LZ_ENDPOINT: 0x1a44076050125825900e736c501f859c50fE728c + }); + + // testnets + __addresses[false] = Addresses({ + OFT_DELEGATE: 0x743b91CDB1C694D4F51bCDA3a4A59DcC0d02b913, // yn security council + TOKEN_ADMIN: 0x743b91CDB1C694D4F51bCDA3a4A59DcC0d02b913, + PROXY_ADMIN: 0x743b91CDB1C694D4F51bCDA3a4A59DcC0d02b913, + LZ_ENDPOINT: 0x6EDCE65403992e310A62460808c4b910D972f10f + }); + + // mainnets + __chainIdToLzEID[__chainIds.mainnet] = 30101; + __chainIdToLzEID[__chainIds.base] = 30184; + __chainIdToLzEID[__chainIds.optimism] = 30111; + __chainIdToLzEID[__chainIds.arbitrum] = 30110; + __chainIdToLzEID[__chainIds.fraxtal] = 30255; + + // testnets + __chainIdToLzEID[__chainIds.holesky] = 40217; + __chainIdToLzEID[__chainIds.fraxtalTestnet] = 40255; + } + + function getAddresses() internal view returns (Addresses storage) { + require(isSupportedChainId(block.chainid), "BaseData: unsupported chainId"); + return __addresses[!isTestnet()]; + } + + function getEID(uint256 chainId) internal view returns (uint32) { + require(isSupportedChainId(chainId), "BaseData: unsupported chainId"); + return __chainIdToLzEID[chainId]; + } + + function isSupportedChainId(uint256 chainId) internal view returns (bool) { + bool isSupported = chainId == __chainIds.mainnet || chainId == __chainIds.base || chainId == __chainIds.fraxtal + || chainId == __chainIds.optimism || chainId == __chainIds.arbitrum || chainId == __chainIds.holesky + || chainId == __chainIds.fraxtalTestnet; + bool isEID = __chainIdToLzEID[chainId] != 0; + return isSupported && isEID; + } + + function isTestnet() internal view returns (bool) { + return block.chainid == __chainIds.holesky || block.chainid == __chainIds.fraxtalTestnet; + } +} diff --git a/script/BaseData.sol b/script/BaseData.sol deleted file mode 100644 index f499a2f..0000000 --- a/script/BaseData.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import {Script} from "forge-std/Script.sol"; - -contract BaseData is Script { - struct Actors { - address OFT_DELEGATE; - address TOKEN_ADMIN; - address PROXY_ADMIN; - } - - struct ChainAddresses { - address lzEndpoint; - } - - struct ChainIds { - uint256 mainnet; - uint256 base; - uint256 optimism; - uint256 arbitrum; - uint256 fraxtal; - uint256 holesky; - uint256 fraxtalTestnet; - } - - mapping(uint256 => Actors) public actors; - mapping(uint256 => ChainAddresses) public addresses; - - ChainIds public chainIds = ChainIds({ - mainnet: 1, - base: 8453, - optimism: 10, - arbitrum: 42161, - fraxtal: 252, - holesky: 17000, - fraxtalTestnet: 2522 - }); - - function setUp() public virtual { - addresses[chainIds.mainnet] = ChainAddresses({lzEndpoint: 0x1a44076050125825900e736c501f859c50fE728c}); - actors[chainIds.mainnet] = Actors({ - OFT_DELEGATE: 0xfcad670592a3b24869C0b51a6c6FDED4F95D6975, // yn security council - TOKEN_ADMIN: 0xfcad670592a3b24869C0b51a6c6FDED4F95D6975, - PROXY_ADMIN: 0xfcad670592a3b24869C0b51a6c6FDED4F95D6975 - }); - - addresses[chainIds.fraxtal] = ChainAddresses({lzEndpoint: 0x1a44076050125825900e736c501f859c50fE728c}); - actors[chainIds.fraxtal] = Actors({OFT_DELEGATE: address(0), TOKEN_ADMIN: address(0), PROXY_ADMIN: address(0)}); - - addresses[chainIds.optimism] = ChainAddresses({lzEndpoint: 0x1a44076050125825900e736c501f859c50fE728c}); - actors[chainIds.optimism] = Actors({OFT_DELEGATE: address(0), TOKEN_ADMIN: address(0), PROXY_ADMIN: address(0)}); - - addresses[chainIds.arbitrum] = ChainAddresses({lzEndpoint: 0x1a44076050125825900e736c501f859c50fE728c}); - actors[chainIds.arbitrum] = Actors({OFT_DELEGATE: address(0), TOKEN_ADMIN: address(0), PROXY_ADMIN: address(0)}); - - addresses[chainIds.base] = ChainAddresses({lzEndpoint: 0x1a44076050125825900e736c501f859c50fE728c}); - actors[chainIds.base] = Actors({OFT_DELEGATE: address(0), TOKEN_ADMIN: address(0), PROXY_ADMIN: address(0)}); - - addresses[chainIds.holesky] = ChainAddresses({lzEndpoint: 0x6EDCE65403992e310A62460808c4b910D972f10f}); - actors[chainIds.holesky] = Actors({ - OFT_DELEGATE: 0x743b91CDB1C694D4F51bCDA3a4A59DcC0d02b913, // yn security council - TOKEN_ADMIN: 0x743b91CDB1C694D4F51bCDA3a4A59DcC0d02b913, - PROXY_ADMIN: 0x743b91CDB1C694D4F51bCDA3a4A59DcC0d02b913 - }); - - addresses[chainIds.fraxtalTestnet] = ChainAddresses({lzEndpoint: 0x6EDCE65403992e310A62460808c4b910D972f10f}); - actors[chainIds.fraxtalTestnet] = - Actors({OFT_DELEGATE: address(0), TOKEN_ADMIN: address(0), PROXY_ADMIN: address(0)}); - } - - function getActors(uint256 chainId) public view returns (Actors memory) { - return actors[chainId]; - } - - function getChainAddresses(uint256 chainId) public view returns (ChainAddresses memory) { - return addresses[chainId]; - } - - function isSupportedChainId(uint256 chainId) public view returns (bool) { - return chainId == chainIds.mainnet || chainId == chainIds.base || chainId == chainIds.fraxtal - || chainId == chainIds.optimism || chainId == chainIds.arbitrum || chainId == chainIds.holesky - || chainId == chainIds.fraxtalTestnet; - } -} diff --git a/script/BaseScript.s.sol b/script/BaseScript.s.sol index 108ff8a..b830510 100644 --- a/script/BaseScript.s.sol +++ b/script/BaseScript.s.sol @@ -1,158 +1,223 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; -import {BaseData} from "./BaseData.sol"; +import {BaseData} from "./BaseData.s.sol"; import {ImmutableMultiChainDeployer} from "@factory/ImmutableMultiChainDeployer.sol"; import {RateLimiter} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/utils/RateLimiter.sol"; import {EndpointV2} from "@layerzerolabs/lz-evm-protocol-v2/contracts/EndpointV2.sol"; import "forge-std/console.sol"; -struct L2YnOFTAdapterInput { - address adapterImplementation; - uint256 chainId; - address erc20Address; - bytes32 implementationSalt; - address proxyController; - bytes32 proxySalt; - RateLimitConfig[] rateLimitConfigs; +struct RateLimitConfig { + uint256 limit; + uint256 window; } -struct L1YnOFTAdapterInput { - uint256 chainId; - address erc20Address; - RateLimitConfig[] rateLimitConfigs; +struct BaseInput { + string erc20Name; + string erc20Symbol; + uint256 l1ChainId; + address l1ERC20Address; + uint256[] l2ChainIds; + RateLimitConfig rateLimitConfig; } -struct RateLimitConfig { - uint256 limit; - uint256 window; +struct Deployment { + ChainDeployment[] chains; + address deployerAddress; + string erc20Name; + string erc20Symbol; } -struct YnERC20Input { +struct ChainDeployment { uint256 chainId; - string name; - string symbol; + address erc20Address; + bool isL1; + address lzEndpoint; + uint256 lzEID; + address multiChainDeployer; + address oftAdapter; } -//forge script script/DeployMainnetImplementations.s.sol:DeployMainnetImplementations --rpc-url ${rpc} --account ${deployerAccountName} --sender ${deployer} --broadcast --etherscan-api-key ${api} --verify contract BaseScript is BaseData { - // TODO: parse token address from json or as input from user - // TODO: setup forks based on if testnet or mainnet deployment as per json - // TODO: setup saving of deployment data in deployments json file - uint256 _chainId; - bytes public data; - string public json; - address public immutableDeployer; - address public adapterImplementation; - L2YnOFTAdapterInput public _ynOFTAdapterInputs; - L1YnOFTAdapterInput public _ynOFTImplementationInputs; - YnERC20Input public _ynERC20Inputs; - RateLimiter.RateLimitConfig[] public _rateLimitConfigs; - - function _loadERC20Data(string memory _inputPath) internal { - _loadJson(_inputPath); - _loadYnERC20Inputs(); - _loadDeployerForChain(block.chainid); - _verifyChain(); - } - - function _loadOFTAdapterData(string memory _inputPath) internal { - _loadJson(_inputPath); - _loadYnOFTAdapterInputs(); - _verifyChain(); - _loadDeployerForChain(block.chainid); - _getRateLimiterConfigs(); + BaseInput public baseInput; + Deployment public deployment; + ChainDeployment public currentDeployment; + string private constant _version = "1.0.0"; + + function _getRateLimitConfigs() internal view returns (RateLimiter.RateLimitConfig[] memory) { + RateLimiter.RateLimitConfig[] memory rateLimitConfigs = + new RateLimiter.RateLimitConfig[](baseInput.l2ChainIds.length + 1); + rateLimitConfigs[0] = RateLimiter.RateLimitConfig( + getEID(baseInput.l1ChainId), baseInput.rateLimitConfig.limit, baseInput.rateLimitConfig.window + ); + for (uint256 i; i < baseInput.l2ChainIds.length; i++) { + rateLimitConfigs[i + 1] = RateLimiter.RateLimitConfig( + getEID(baseInput.l2ChainIds[i]), baseInput.rateLimitConfig.limit, baseInput.rateLimitConfig.window + ); + } + return rateLimitConfigs; } - function _loadOFTImplementationData(string memory _inputPath) internal { + function _loadInput(string calldata _inputPath) internal { _loadJson(_inputPath); - _loadYnOFTImplementationInputs(); - _verifyChain(); - _getRateLimiterConfigs(); + _validateInput(); + bool isL1 = _getIsL1(); + _loadDeployment(); + if (deployment.deployerAddress != address(0)) { + require(deployment.deployerAddress == msg.sender, "Invalid Deployer"); + } + for (uint256 i; i < deployment.chains.length; i++) { + if (deployment.chains[i].chainId == block.chainid) { + currentDeployment = deployment.chains[i]; + break; + } + } + currentDeployment.chainId = block.chainid; + currentDeployment.isL1 = isL1; + if (isL1) { + currentDeployment.erc20Address = baseInput.l1ERC20Address; + } + currentDeployment.lzEndpoint = getAddresses().LZ_ENDPOINT; + currentDeployment.lzEID = getEID(block.chainid); + } + + function _validateInput() internal view { + require(bytes(baseInput.erc20Name).length > 0, "Invalid ERC20 Name"); + require(bytes(baseInput.erc20Symbol).length > 0, "Invalid ERC20 Symbol"); + require(baseInput.rateLimitConfig.limit > 0, "Invalid Rate Limit"); + require(baseInput.rateLimitConfig.window > 0, "Invalid Rate Window"); + require(isSupportedChainId(baseInput.l1ChainId), "Invalid L1 ChainId"); + require(baseInput.l1ERC20Address != address(0), "Invalid L1 ERC20 Address"); + require(baseInput.l2ChainIds.length > 0, "Invalid L2 ChainIds"); + for (uint256 i; i < baseInput.l2ChainIds.length; i++) { + require(isSupportedChainId(baseInput.l2ChainIds[i]), "Invalid L2 ChainId"); + } } - function _loadYnOFTImplementationInputs() internal { - L1YnOFTAdapterInput memory implementationInputs = abi.decode(data, (L1YnOFTAdapterInput)); - this.loadImplementationInputs(implementationInputs); - _chainId = _ynOFTImplementationInputs.chainId; + function _getIsL1() internal view returns (bool) { + bool isL1 = block.chainid == baseInput.l1ChainId; + bool isL2 = false; + for (uint256 i; i < baseInput.l2ChainIds.length; i++) { + isL2 = block.chainid == baseInput.l2ChainIds[i]; + if (isL2) { + break; + } + } + if (isL1 == isL2) { + console.log("isL1: %s, isL2: %s", isL1, isL2); + revert("Invalid ChainId"); + } + return isL1; } - function _loadDeployerForChain(uint256 chainId) internal { - string memory path = string( + function _getDeploymentFilePath() internal view returns (string memory) { + return string( abi.encodePacked( - vm.projectRoot(), "/deployments/ImmutableMultiChainDeployer-", vm.toString(chainId), ".json" + vm.projectRoot(), "/deployments/", baseInput.erc20Symbol, "-", vm.toString(baseInput.l1ChainId), ".json" ) ); - string memory _json = vm.readFile(path); - immutableDeployer = vm.parseJsonAddress(_json, ".ImmutableMultiChainDeployerAddress"); - require(immutableDeployer != address(0), "invalid deployer"); } - function _loadAdapterImplementationForChain(uint32 chainId) internal { - string memory path = string( - abi.encodePacked(vm.projectRoot(), "/deployments/MainnetImplementations-", vm.toString(chainId), ".json") - ); - string memory _json = vm.readFile(path); - adapterImplementation = vm.parseJsonAddress(_json, ".OFTAdapterImplementation"); - require(adapterImplementation != address(0), "invalid adapter Implementation"); - } + function _saveDeployment() internal { + deployment.deployerAddress = msg.sender; + bool found = false; + for (uint256 i; i < deployment.chains.length; i++) { + if (deployment.chains[i].chainId == block.chainid) { + deployment.chains[i] = currentDeployment; + found = true; + break; + } + } + if (!found) { + deployment.chains.push(currentDeployment); + } + string memory json = vm.serializeAddress("deployment", "deployerAddress", deployment.deployerAddress); - function _loadJson(string memory _path) internal { - string memory path = string(abi.encodePacked(vm.projectRoot(), "/", _path)); - json = vm.readFile(path); - data = vm.parseJson(json); - } + string memory chainsJson = ""; - function _loadYnOFTAdapterInputs() internal { - L2YnOFTAdapterInput memory ynOFTAdapterInputs = abi.decode(data, (L2YnOFTAdapterInput)); - this.loadAdapterInputs(ynOFTAdapterInputs); - _chainId = _ynOFTAdapterInputs.chainId; - } + for (uint256 i = 0; i < deployment.chains.length; i++) { + string memory chainKey = string(abi.encodePacked("chains[", vm.toString(i), "]")); - function loadAdapterInputs(L2YnOFTAdapterInput calldata _ynInput) external { - _ynOFTAdapterInputs = _ynInput; - } + string memory chainJson = vm.serializeBool(chainKey, "isL1", deployment.chains[i].isL1); + chainJson = vm.serializeUint(chainKey, "chainId", deployment.chains[i].chainId); + chainJson = vm.serializeAddress(chainKey, "lzEndpoint", deployment.chains[i].lzEndpoint); + chainJson = vm.serializeUint(chainKey, "lzEID", deployment.chains[i].lzEID); + chainJson = vm.serializeAddress(chainKey, "multiChainDeployer", deployment.chains[i].multiChainDeployer); + chainJson = vm.serializeAddress(chainKey, "erc20Address", deployment.chains[i].erc20Address); + chainJson = vm.serializeAddress(chainKey, "oftAdapter", deployment.chains[i].oftAdapter); - function loadImplementationInputs(L1YnOFTAdapterInput calldata _ynImpInput) external { - _ynOFTImplementationInputs = _ynImpInput; - } + chainsJson = vm.serializeString("chains", vm.toString(deployment.chains[i].chainId), chainJson); + } + + json = vm.serializeString("deployment", "chains", chainsJson); - function _loadYnERC20Inputs() internal { - _ynERC20Inputs = abi.decode(data, (YnERC20Input)); - _chainId = _ynERC20Inputs.chainId; + string memory filePath = _getDeploymentFilePath(); + vm.writeJson(json, filePath); } - function _getRateLimiterConfigs() internal { - RateLimiter.RateLimitConfig memory _tempConfig; - uint32 tempDstEid = EndpointV2(addresses[_chainId].lzEndpoint).eid(); - for (uint256 i; i < _ynOFTAdapterInputs.rateLimitConfigs.length; i++) { - _tempConfig.dstEid = tempDstEid; - _tempConfig.limit = _ynOFTAdapterInputs.rateLimitConfigs[i].limit; - _tempConfig.window = _ynOFTAdapterInputs.rateLimitConfigs[i].window; - _rateLimitConfigs.push(_tempConfig); + function _loadDeployment() internal { + // Reset the deployment struct + delete deployment; + + string memory filePath = _getDeploymentFilePath(); + + if (!vm.isFile(filePath)) { + return; } - } - function _serializeOutputs(string memory objectKey) internal virtual { - // left blank on purpose - } + string memory json = vm.readFile(filePath); - function _verifyChain() internal view returns (bool) { - require(isSupportedChainId(_chainId) && block.chainid == _chainId, "Invalid chain"); - return isSupportedChainId(_chainId) && block.chainid == _chainId; - } + // Parse deployerAddress + deployment.deployerAddress = vm.parseJsonAddress(json, ".deployerAddress"); + + // Get the array of chain deployments + string[] memory chainKeys = vm.parseJsonKeys(json, ".chains"); + ChainDeployment[] memory chains = new ChainDeployment[](chainKeys.length); - function _getOutputPath(string memory _deploymentType) internal view returns (string memory) { - string memory root = vm.projectRoot(); - return string.concat(root, "/deployments/", _deploymentType, "-", vm.toString(block.chainid), ".json"); + // Loop through each chain and parse its fields + for (uint256 i = 0; i < chainKeys.length; i++) { + string memory chainKey = string(abi.encodePacked(".chains.", chainKeys[i])); + + chains[i].isL1 = vm.parseJsonBool(json, string(abi.encodePacked(chainKey, ".isL1"))); + chains[i].chainId = vm.parseJsonUint(json, string(abi.encodePacked(chainKey, ".chainId"))); + chains[i].lzEndpoint = vm.parseJsonAddress(json, string(abi.encodePacked(chainKey, ".lzEndpoint"))); + chains[i].lzEID = vm.parseJsonUint(json, string(abi.encodePacked(chainKey, ".lzEID"))); + chains[i].multiChainDeployer = + vm.parseJsonAddress(json, string(abi.encodePacked(chainKey, ".multiChainDeployer"))); + chains[i].erc20Address = vm.parseJsonAddress(json, string(abi.encodePacked(chainKey, ".erc20Address"))); + chains[i].oftAdapter = vm.parseJsonAddress(json, string(abi.encodePacked(chainKey, ".oftAdapter"))); + + // Add the chain to the deployment + deployment.chains.push(chains[i]); + } } - function _writeOutput(string memory deploymentType, string memory _json) internal { - string memory path = _getOutputPath(deploymentType); - vm.writeFile(path, _json); + function _loadJson(string calldata _path) internal { + string memory filePath = string(abi.encodePacked(vm.projectRoot(), "/", _path)); + string memory json = vm.readFile(filePath); + + // Reset the baseInput struct + delete baseInput; + + // Parse simple fields + baseInput.erc20Name = vm.parseJsonString(json, ".erc20Name"); + baseInput.erc20Symbol = vm.parseJsonString(json, ".erc20Symbol"); + + // Parse the L1Input struct + baseInput.l1ChainId = vm.parseJsonUint(json, ".l1ChainId"); + baseInput.l1ERC20Address = vm.parseJsonAddress(json, ".l1ERC20Address"); + + // Parse the L2ChainIds array + baseInput.l2ChainIds = vm.parseJsonUintArray(json, ".l2ChainIds"); + + // Parse RateLimitConfig struct + baseInput.rateLimitConfig.limit = vm.parseJsonUint(json, ".rateLimitConfig.limit"); + baseInput.rateLimitConfig.window = vm.parseJsonUint(json, ".rateLimitConfig.window"); } - function createSalt(address deployerAddress, string memory label) public pure returns (bytes32 _salt) { - _salt = bytes32(abi.encodePacked(bytes20(deployerAddress), bytes12(bytes32(keccak256(abi.encode(label)))))); + function createSalt(address _deployerAddress, string memory _label) internal pure returns (bytes32 _salt) { + _salt = bytes32( + abi.encodePacked(bytes20(_deployerAddress), bytes12(bytes32(keccak256(abi.encode(_label, _version))))) + ); } } diff --git a/script/DeployImmutableMultiChainDeployer.s.sol b/script/DeployImmutableMultiChainDeployer.s.sol deleted file mode 100644 index 2f2d64f..0000000 --- a/script/DeployImmutableMultiChainDeployer.s.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import {BaseScript} from "./BaseScript.s.sol"; -import {ImmutableMultiChainDeployer} from "@factory/ImmutableMultiChainDeployer.sol"; -import "forge-std/console.sol"; - -//forge script script/DeployImmutableMultiChainDeployer.s.sol:DeployImmutableMultiChainDeployer --rpc-url ${rpc} --sig "run(bytes32)" ${salt} --account ${deployerAccountName} --sender ${deployer} --broadcast --etherscan-api-key ${api} --verify - -contract DeployImmutableMultiChainDeployer is BaseScript { - address public multiChainDeployerAddress; - - function run(bytes32 _salt) public { - vm.broadcast(); - multiChainDeployerAddress = address(new ImmutableMultiChainDeployer{salt: _salt}()); - - console.log("ImmutableMultiChainDeployer deployed at: ", multiChainDeployerAddress); - _serializeOutputs("ImmutableMultiChainDeployer"); - } - - function _serializeOutputs(string memory objectKey) internal override { - vm.serializeString(objectKey, "chainid", vm.toString(block.chainid)); - string memory finalJson = - vm.serializeAddress(objectKey, "ImmutableMultiChainDeployerAddress", address(multiChainDeployerAddress)); - _writeOutput("ImmutableMultiChainDeployer", finalJson); - } -} diff --git a/script/DeployL1OFTAdapter.s.sol b/script/DeployL1OFTAdapter.s.sol new file mode 100644 index 0000000..984cade --- /dev/null +++ b/script/DeployL1OFTAdapter.s.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {BaseScript} from "./BaseScript.s.sol"; +import {RateLimiter} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/utils/RateLimiter.sol"; +import {L1YnOFTAdapterUpgradeable} from "@/L1YnOFTAdapterUpgradeable.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "forge-std/console.sol"; + +// forge script script/DeployL1OFTAdapter.s.sol:DeployL1OFTAdapter --rpc-url ${rpc} --sig "run(string calldata)" ${path} --account ${deployerAccountName} --sender ${deployer} --broadcast --etherscan-api-key ${api} --verify + +contract DeployL1OFTAdapter is BaseScript { + L1YnOFTAdapterUpgradeable public l1OFTAdapter; + + function run(string calldata _jsonPath) public { + _loadInput(_jsonPath); + + require(currentDeployment.isL1 == true, "Must be L1 deployment"); + + RateLimiter.RateLimitConfig[] memory rateLimitConfigs = _getRateLimitConfigs(); + + if (currentDeployment.oftAdapter != address(0)) { + console.log("L1 OFT Adapter already deployed at: %s", currentDeployment.oftAdapter); + l1OFTAdapter = L1YnOFTAdapterUpgradeable(currentDeployment.oftAdapter); + bool needsChange = false; + + for (uint256 i = 0; i < rateLimitConfigs.length; i++) { + (,, uint256 limit, uint256 window) = l1OFTAdapter.rateLimits(rateLimitConfigs[i].dstEid); + RateLimiter.RateLimitConfig memory config = rateLimitConfigs[i]; + if (config.limit != limit || config.window != window) { + needsChange = true; + break; + } + } + if (!needsChange) { + console.log("Rate limits are already set"); + return; + } + vm.broadcast(); + l1OFTAdapter.setRateLimits(rateLimitConfigs); + console.log("Rate limits updated"); + return; + } + + bytes32 proxySalt = createSalt(msg.sender, "L1YnOFTAdapterUpgradeableProxy"); + bytes32 implementationSalt = createSalt(msg.sender, "L1YnOFTAdapterUpgradeable"); + + vm.startBroadcast(); + + address l1OFTAdapterImpl = address( + new L1YnOFTAdapterUpgradeable{salt: implementationSalt}( + baseInput.l1ERC20Address, getAddresses().LZ_ENDPOINT + ) + ); + + bytes memory initializeData = abi.encodeWithSelector( + L1YnOFTAdapterUpgradeable.initialize.selector, getAddresses().OFT_DELEGATE, rateLimitConfigs + ); + + l1OFTAdapter = L1YnOFTAdapterUpgradeable( + address( + new TransparentUpgradeableProxy{salt: proxySalt}( + l1OFTAdapterImpl, getAddresses().PROXY_ADMIN, initializeData + ) + ) + ); + + console.log("L1 OFT Adapter deployed at: %s", address(l1OFTAdapter)); + + vm.stopBroadcast(); + + currentDeployment.oftAdapter = address(l1OFTAdapter); + + _saveDeployment(); + } +} diff --git a/script/DeployL2Adapter.s.sol b/script/DeployL2Adapter.s.sol deleted file mode 100644 index 6ccad04..0000000 --- a/script/DeployL2Adapter.s.sol +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import {BaseScript} from "./BaseScript.s.sol"; -import {IImmutableMultiChainDeployer} from "@interfaces/IImmutableMultiChainDeployer.sol"; -import {RateLimiter} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/utils/RateLimiter.sol"; -import {L2YnOFTAdapterUpgradeable} from "@/L2YnOFTAdapterUpgradeable.sol"; -import {L2YnERC20Upgradeable} from "@/L2YnERC20Upgradeable.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import "forge-std/console.sol"; -// forge script script/DeployL2Adapter.s.sol:DeployL2Adapter --rpc-url ${rpc} --sig "run(string memory, string memory)" ${path2ERC20Input} ${path2OFTAdapterInput} --account ${deployerAccountName} --sender ${deployer} --broadcast --etherscan-api-key ${api} --verify - -contract DeployL2Adapter is BaseScript { - address l2YnOFTAdapter; - address l2ERC20Address; - - function run(string memory _erc20Inputpath, string memory _oftAdapterInputpath) public { - _loadERC20Data(_erc20Inputpath); - - l2ERC20Address = IImmutableMultiChainDeployer(immutableDeployer).deployL2YnERC20( - createSalt(address(0), string.concat(_ynERC20Inputs.name, "-", _ynERC20Inputs.symbol)), - createSalt(address(0), string.concat(_ynERC20Inputs.symbol, "-", _ynERC20Inputs.name)), - _ynERC20Inputs.name, - _ynERC20Inputs.symbol, - msg.sender, - msg.sender, - type(L2YnERC20Upgradeable).creationCode - ); - _loadOFTAdapterData(_oftAdapterInputpath); - l2YnOFTAdapter = IImmutableMultiChainDeployer(immutableDeployer).deployL2YnOFTAdapter( - _ynOFTAdapterInputs.implementationSalt, - _ynOFTAdapterInputs.proxySalt, - _ynOFTAdapterInputs.erc20Address, - addresses[_chainId].lzEndpoint, - msg.sender, - _rateLimitConfigs, - _ynOFTAdapterInputs.proxyController, - type(L2YnOFTAdapterUpgradeable).creationCode - ); - - _serializeOutputs("l2OFTAdapter"); - } - - function _serializeOutputs(string memory objectKey) internal override { - vm.serializeString(objectKey, "chainid", vm.toString(block.chainid)); - vm.serializeAddress(objectKey, "deployer", msg.sender); - vm.serializeAddress(objectKey, "YnErc20", l2ERC20Address); - vm.serializeUint(objectKey, "dstEid", uint256(_rateLimitConfigs[0].dstEid)); - vm.serializeJson(objectKey, json); - string memory finalJson = vm.serializeAddress(objectKey, "l2YnOFTAdapter", address(l2YnOFTAdapter)); - _writeOutput("L2OFTAdapter", finalJson); - } -} diff --git a/script/DeployL2OFTAdapter.s.sol b/script/DeployL2OFTAdapter.s.sol new file mode 100644 index 0000000..84ed328 --- /dev/null +++ b/script/DeployL2OFTAdapter.s.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {BaseScript} from "./BaseScript.s.sol"; +import {IImmutableMultiChainDeployer} from "@interfaces/IImmutableMultiChainDeployer.sol"; +import {RateLimiter} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/utils/RateLimiter.sol"; +import {L2YnOFTAdapterUpgradeable} from "@/L2YnOFTAdapterUpgradeable.sol"; +import {L2YnERC20Upgradeable} from "@/L2YnERC20Upgradeable.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "forge-std/console.sol"; +// forge script script/DeployL2OFTAdapter.s.sol:DeployL2Adapter --rpc-url ${rpc} --sig "run(string memory, string memory)" ${path2ERC20Input} ${path2OFTAdapterInput} --account ${deployerAccountName} --sender ${deployer} --broadcast --etherscan-api-key ${api} --verify + +contract DeployL2OFTAdapter is BaseScript { + address l2YnOFTAdapter; + address l2ERC20Address; + + function run(string calldata _jsonPath) public { + _loadInput(_jsonPath); + + require(currentDeployment.isL1 != true, "Must be L2 deployment"); + + require(currentDeployment.multiChainDeployer != address(0), "MultiChainDeployer not deployed"); + + IImmutableMultiChainDeployer currentDeployer = + IImmutableMultiChainDeployer(currentDeployment.multiChainDeployer); + + RateLimiter.RateLimitConfig[] memory rateLimitConfigs = _getRateLimitConfigs(); + + if (currentDeployment.oftAdapter != address(0)) { + console.log("L2 OFT Adapter already deployed at: %s", currentDeployment.oftAdapter); + L2YnOFTAdapterUpgradeable oftAdapter = L2YnOFTAdapterUpgradeable(currentDeployment.oftAdapter); + bool needsChange = false; + + for (uint256 i = 0; i < rateLimitConfigs.length; i++) { + (,, uint256 limit, uint256 window) = oftAdapter.rateLimits(rateLimitConfigs[i].dstEid); + RateLimiter.RateLimitConfig memory config = rateLimitConfigs[i]; + if (config.limit != limit || config.window != window) { + needsChange = true; + break; + } + } + if (!needsChange) { + console.log("Rate limits are already set"); + return; + } + vm.broadcast(); + oftAdapter.setRateLimits(rateLimitConfigs); + console.log("Rate limits updated"); + return; + } + + bytes32 proxySalt = createSalt(msg.sender, "L2YnERC20UpgradeableProxy"); + bytes32 implementationSalt = createSalt(msg.sender, "L2YnERC20Upgradeable"); + + l2ERC20Address = currentDeployer.deployL2YnERC20( + implementationSalt, + proxySalt, + baseInput.erc20Name, + baseInput.erc20Symbol, + getAddresses().TOKEN_ADMIN, + getAddresses().PROXY_ADMIN, + type(L2YnERC20Upgradeable).creationCode + ); + + console.log("L2 ERC20 deployed at: ", l2ERC20Address); + + proxySalt = createSalt(msg.sender, "L2YnOFTAdapterUpgradeableProxy"); + implementationSalt = createSalt(msg.sender, "L2YnOFTAdapterUpgradeable"); + + l2YnOFTAdapter = (currentDeployer).deployL2YnOFTAdapter( + implementationSalt, + proxySalt, + l2ERC20Address, + getAddresses().LZ_ENDPOINT, + getAddresses().OFT_DELEGATE, + rateLimitConfigs, + getAddresses().PROXY_ADMIN, + type(L2YnOFTAdapterUpgradeable).creationCode + ); + + console.log("L2 OFT Adapter deployed at: ", l2YnOFTAdapter); + + currentDeployment.erc20Address = l2ERC20Address; + currentDeployment.oftAdapter = l2YnOFTAdapter; + + _saveDeployment(); + } +} diff --git a/script/DeployMainnetImplementations.s.sol b/script/DeployMainnetImplementations.s.sol deleted file mode 100644 index 0b6cd40..0000000 --- a/script/DeployMainnetImplementations.s.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import {BaseScript} from "./BaseScript.s.sol"; -import {ImmutableMultiChainDeployer} from "@factory/ImmutableMultiChainDeployer.sol"; -import {RateLimiter} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/utils/RateLimiter.sol"; -import {L1YnOFTAdapterUpgradeable} from "@/L1YnOFTAdapterUpgradeable.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - -// forge script script/DeployMainnetImplementations.s.sol:DeployMainnetImplementations --rpc-url ${rpc} --sig "run(string memory)" ${path} --account ${deployerAccountName} --sender ${deployer} --broadcast --etherscan-api-key ${api} --verify -contract DeployMainnetImplementations is BaseScript { - address public mainnetOFTAdapterImpl; - L1YnOFTAdapterUpgradeable public mainnetOFTAdapter; - - function run(string memory __path) public { - _loadOFTImplementationData(__path); - - _loadDeployerForChain(block.chainid); - - vm.broadcast(); - - mainnetOFTAdapterImpl = address( - new L1YnOFTAdapterUpgradeable(_ynOFTImplementationInputs.erc20Address, addresses[_chainId].lzEndpoint) - ); - mainnetOFTAdapter = - L1YnOFTAdapterUpgradeable(address(new TransparentUpgradeableProxy(mainnetOFTAdapterImpl, msg.sender, ""))); - - mainnetOFTAdapter.initialize(msg.sender, _rateLimitConfigs); - - _serializeOutputs("MainnetImplementations"); - } - - function _serializeOutputs(string memory objectKey) internal override { - vm.serializeAddress(objectKey, "erc20", _ynOFTImplementationInputs.erc20Address); - vm.serializeString(objectKey, "chainid", vm.toString(block.chainid)); - vm.serializeAddress(objectKey, "OFTAdapterImplementation", address(mainnetOFTAdapterImpl)); - string memory finalJson = vm.serializeAddress(objectKey, "OFTAdapter", address(mainnetOFTAdapter)); - _writeOutput("MainnetImplementations", finalJson); - } -} diff --git a/script/DeployMultiChainDeployer.s.sol b/script/DeployMultiChainDeployer.s.sol new file mode 100644 index 0000000..74c9413 --- /dev/null +++ b/script/DeployMultiChainDeployer.s.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {BaseScript} from "./BaseScript.s.sol"; +import {ImmutableMultiChainDeployer} from "@factory/ImmutableMultiChainDeployer.sol"; +import "forge-std/console.sol"; + +//forge script script/DeployMultiChainDeployer.s.sol:DeployMultiChainDeployer --rpc-url ${rpc} --sig "run(string calldata)" ${path} --account ${deployerAccountName} --sender ${deployer} --broadcast --etherscan-api-key ${api} --verify + +contract DeployMultiChainDeployer is BaseScript { + address public multiChainDeployerAddress; + + function run(string calldata _jsonPath) public { + _loadInput(_jsonPath); + + require(currentDeployment.isL1 != true, "Must be L2 deployment"); + + bytes32 salt = createSalt(msg.sender, "ImmutableMultiChainDeployer"); + + address predictedAddress = + vm.computeCreate2Address(salt, keccak256(type(ImmutableMultiChainDeployer).creationCode)); + console.log("Predicted ImmutableMultiChainDeployer address: ", predictedAddress); + + if (currentDeployment.multiChainDeployer != address(0)) { + require(currentDeployment.multiChainDeployer == predictedAddress, "Already deployed"); + console.log("ImmutableMultiChainDeployer already deployed at: ", currentDeployment.multiChainDeployer); + return; + } + + vm.broadcast(); + multiChainDeployerAddress = address(new ImmutableMultiChainDeployer{salt: salt}()); + + console.log("ImmutableMultiChainDeployer deployed at: ", multiChainDeployerAddress); + require(multiChainDeployerAddress == predictedAddress, "Deployment failed"); + + currentDeployment.multiChainDeployer = multiChainDeployerAddress; + + _saveDeployment(); + } +} diff --git a/script/inputs/L1OFTAdapterImplementationsInput.json b/script/inputs/L1OFTAdapterImplementationsInput.json deleted file mode 100644 index 30f61c8..0000000 --- a/script/inputs/L1OFTAdapterImplementationsInput.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "chainId": 17000, - "erc20Address": "0xd9029669BC74878BCB5BE58c259ed0A277C5c16E", - "rateLimiterConfigs": [ - { - "limit": "100000000000000000000", - "window": "86400" - } - ] -} \ No newline at end of file diff --git a/script/inputs/L2ERC20DeploymentInput.json b/script/inputs/L2ERC20DeploymentInput.json deleted file mode 100644 index 5187e62..0000000 --- a/script/inputs/L2ERC20DeploymentInput.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "chainId": 17000, - "name": "name", - "symbol": "symbol" -} \ No newline at end of file diff --git a/script/inputs/L2OFTAdapterInput.json b/script/inputs/L2OFTAdapterInput.json deleted file mode 100644 index 388659a..0000000 --- a/script/inputs/L2OFTAdapterInput.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "chainId": 17000, - "erc20Address": "0xd9029669BC74878BCB5BE58c259ed0A277C5c16E", - "adapterImplementation": "0x1D5B39612a354C2Ff1f50cfd8D9FDEdB4d4F9e62", - "implementationSalt": "0x000000000000000000000000000000000000000093329e122e1b1b30629b7c7b", - "proxyController": "0x941E92c9Eff78a2b7217057752cf938040a59aE9", - "proxySalt": "0x000000000000000000000000000000000000000093329e122e1b1b30629f7b7b", - "rateLimiterConfigs": [ - { - "limit": "100000000000000000000", - "window": "86400" - } - ] -} \ No newline at end of file diff --git a/script/inputs/holesky-ynETH.json b/script/inputs/holesky-ynETH.json new file mode 100644 index 0000000..b987ccf --- /dev/null +++ b/script/inputs/holesky-ynETH.json @@ -0,0 +1,11 @@ +{ + "erc20Name": "ynETH", + "erc20Symbol": "ynETH", + "l1ChainId": 17000, + "l2ChainIds": [2522], + "l1ERC20Address": "0xd9029669BC74878BCB5BE58c259ed0A277C5c16E", + "rateLimitConfig": { + "limit": "100000000000000000000", + "window": "86400" + } +} diff --git a/script/inputs/mainnet-ynETH.json b/script/inputs/mainnet-ynETH.json new file mode 100644 index 0000000..c080fdd --- /dev/null +++ b/script/inputs/mainnet-ynETH.json @@ -0,0 +1,11 @@ +{ + "erc20Name": "ynETH", + "erc20Symbol": "ynETH", + "l1ChainId": 1, + "l2ChainIds": [10, 8453], + "l1ERC20Address": "0x09db87A538BD693E9d08544577d5cCfAA6373A48", + "rateLimitConfig": { + "limit": "100000000000000000000", + "window": "86400" + } +}