diff --git a/foundry.toml b/foundry.toml index 6abf0fe2..4eb67227 100644 --- a/foundry.toml +++ b/foundry.toml @@ -26,6 +26,7 @@ polygon_pos = "https://polygon-mainnet.infura.io/v3/${INFURA_TOKEN}" polygon_zkevm = "https://zkevm-rpc.com" polygon_zkevm_testnet = "https://rpc.public.zkevm-test.net" tatara = "https://rpc.tatara.katanarpc.com/${TATARA_TOKEN}" +katana = "https://rpc.katanarpc.com/${KATANA_TOKEN}" [etherscan] mainnet = { key = "${API_KEY}" } diff --git a/script/DeployLayerY.s.sol b/script/DeployLayerY.s.sol new file mode 100644 index 00000000..d041003e --- /dev/null +++ b/script/DeployLayerY.s.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: LicenseRef-PolygonLabs-Open-Attribution OR LicenseRef-PolygonLabs-Source-Available +pragma solidity ^0.8.29; + +import "forge-std/Script.sol"; +import "../src/custom-tokens/GenericCustomToken.sol"; +import "../src/custom-tokens/GenericNativeConverter.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {ERC1967Proxy, ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +contract DeployLayerY is Script { + using stdJson for string; + + uint256 deployerPrivateKey = uint256(uint160(address(this))); // default placeholder for tests + + function run() public { + deployerPrivateKey = vm.promptSecretUint("PRIVATE_KEY"); + + deployLayerY(); + } + + function deployLayerY() public { + vm.startBroadcast(deployerPrivateKey); + + string memory input = vm.readFile("script/input.json"); + + string memory slug = string(abi.encodePacked('["', vm.toString(block.chainid), '"]')); + + address polygonEngineeringMultisig = input.readAddress(string.concat(slug, ".polygonEngineeringMultisig")); + address migrationManagerAddress = input.readAddress(string.concat(slug, ".migrationManager")); + address lxlyBridge = input.readAddress(string.concat(slug, ".lxlyBridge")); + + GenericNativeConverter[] memory nativeConverters = new GenericNativeConverter[](5); + + string[] memory vbTokens = new string[](4); + vbTokens[0] = "vbUSDC"; + vbTokens[1] = "vbUSDT"; + vbTokens[2] = "vbWBTC"; + vbTokens[3] = "vbUSDS"; + + // deploy token impl + GenericCustomToken customTokenImpl = new GenericCustomToken(); + GenericNativeConverter nativeConverterImpl = new GenericNativeConverter(); + + for (uint256 i = 0; i < vbTokens.length; i++) { + string memory vbSlug = + string(abi.encodePacked('["', vm.toString(block.chainid), '"]', '.["', vbTokens[i], '"]')); + + address customToken = input.readAddress(string.concat(vbSlug, ".customToken")); + address underlyingToken = input.readAddress(string.concat(vbSlug, ".underlyingToken")); + string memory name = input.readString(string.concat(vbSlug, ".name")); + string memory symbol = input.readString(string.concat(vbSlug, ".symbol")); + uint8 decimals = uint8(input.readUint(string.concat(vbSlug, ".decimals"))); + uint256 nonMigratableBackingPercentage = + input.readUint(string.concat(vbSlug, ".nonMigratableBackingPercentage")); + + bytes memory initNativeConverter = abi.encodeCall( + GenericNativeConverter.initialize, + ( + polygonEngineeringMultisig, + decimals, + customToken, + underlyingToken, + lxlyBridge, + 0, + nonMigratableBackingPercentage, + migrationManagerAddress + ) + ); + address nativeConverter = + _proxify(address(nativeConverterImpl), polygonEngineeringMultisig, initNativeConverter); + + nativeConverters[i] = GenericNativeConverter(nativeConverter); + + console.log("Native converter ", vbTokens[i], " deployed at: ", nativeConverter); + + // update custom token + bytes memory data = abi.encodeCall( + GenericCustomToken.reinitialize, + (polygonEngineeringMultisig, name, symbol, decimals, lxlyBridge, nativeConverter) + ); + + IERC1967Proxy customTokenProxy = IERC1967Proxy(payable(customToken)); + bytes memory payload = abi.encodeCall(customTokenProxy.upgradeToAndCall, (address(customTokenImpl), data)); + + console.log("Payload for upgrading custom token", vbTokens[i]); + console.logBytes(payload); + } + + console.log("Use this multisig: ", polygonEngineeringMultisig); + + vm.stopBroadcast(); + } + + function _proxify(address logic, address admin, bytes memory initData) internal returns (address payable proxy) { + proxy = payable(new TransparentUpgradeableProxy(logic, admin, initData)); + } +} + +interface IERC1967Proxy { + function upgradeToAndCall(address newImplementation, bytes calldata data) external; +} diff --git a/script/DeployLayerY_WETH.s.sol b/script/DeployLayerY_WETH.s.sol new file mode 100644 index 00000000..6e0d0c01 --- /dev/null +++ b/script/DeployLayerY_WETH.s.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: LicenseRef-PolygonLabs-Open-Attribution OR LicenseRef-PolygonLabs-Source-Available +pragma solidity ^0.8.29; + +import "forge-std/Script.sol"; +import "../src/custom-tokens/WETH/WETH.sol"; +import "../src/custom-tokens/WETH/WETHNativeConverter.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {ERC1967Proxy, ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +contract DeployLayerY_WETH is Script { + using stdJson for string; + + uint256 deployerPrivateKey = uint256(uint160(address(this))); // default placeholder for tests + + function run() public { + deployerPrivateKey = vm.promptSecretUint("PRIVATE_KEY"); + + deployLayerY_WETH(); + } + + function deployLayerY_WETH() public { + vm.startBroadcast(deployerPrivateKey); + + string memory input = vm.readFile("script/input.json"); + + string memory slug = string(abi.encodePacked('["', vm.toString(block.chainid), '"]')); + + address polygonEngineeringMultisig = input.readAddress(string.concat(slug, ".polygonEngineeringMultisig")); + address migrationManagerAddress = input.readAddress(string.concat(slug, ".migrationManager")); + address lxlyBridge = input.readAddress(string.concat(slug, ".lxlyBridge")); + + string memory vbETHSlug = string(abi.encodePacked('["', vm.toString(block.chainid), '"]', '.["vbETH"]')); + + address vbWETH = input.readAddress(string.concat(vbETHSlug, ".customToken")); + address wETH = input.readAddress(string.concat(vbETHSlug, ".underlyingToken")); + string memory name = input.readString(string.concat(vbETHSlug, ".name")); + string memory symbol = input.readString(string.concat(vbETHSlug, ".symbol")); + uint8 decimals = uint8(input.readUint(string.concat(vbETHSlug, ".decimals"))); + uint256 nonMigratableGasBackingPercentage = + input.readUint(string.concat(vbETHSlug, ".nonMigratableGasBackingPercentage")); + + WETHNativeConverter nativeConverterImpl = new WETHNativeConverter(); + + bytes memory initNativeConverter = abi.encodeCall( + WETHNativeConverter.initialize, + ( + polygonEngineeringMultisig, + decimals, + vbWETH, + wETH, + lxlyBridge, + 0, + 0, + migrationManagerAddress, + nonMigratableGasBackingPercentage + ) + ); + address wethNativeConverter = + _proxify(address(nativeConverterImpl), polygonEngineeringMultisig, initNativeConverter); + + // deploy vbWETH impl + WETH wethImpl = new WETH(); + + // update vbWETH + bytes memory data = abi.encodeCall( + WETH.reinitialize, (polygonEngineeringMultisig, name, symbol, decimals, lxlyBridge, wethNativeConverter) + ); + + IERC1967Proxy vbWethProxy = IERC1967Proxy(payable(vbWETH)); + bytes memory payload = abi.encodeCall(vbWethProxy.upgradeToAndCall, (address(wethImpl), data)); + + console.log("Payload for upgrading vbWETH", "use this multisig: ", polygonEngineeringMultisig); + console.logBytes(payload); + + /* bytes32 implementation = vm.load(vbWETH, 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc); + vm.assertEq(implementation, bytes32(uint256(uint160(address(wethImpl))))); */ + + vm.stopBroadcast(); + } + + function _proxify(address logic, address admin, bytes memory initData) internal returns (address payable proxy) { + proxy = payable(new TransparentUpgradeableProxy(logic, admin, initData)); + } +} + +interface IERC1967Proxy { + function upgradeToAndCall(address newImplementation, bytes calldata data) external; +} diff --git a/script/DepositAndBridge.s.sol b/script/DepositAndBridge.s.sol new file mode 100644 index 00000000..55549ec6 --- /dev/null +++ b/script/DepositAndBridge.s.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: LicenseRef-PolygonLabs-Open-Attribution OR LicenseRef-PolygonLabs-Source-Available +pragma solidity ^0.8.29; + +import "forge-std/Script.sol"; +import "../src/vault-bridge-tokens/vbETH/VbETH.sol"; + +/// @dev this can be used to send some initial ETH to LayerY. Needs to be replicated for other tokens as well, +/// @dev but can also be done manually. Ly token addresses are necessary for the rest of the deployment process. +contract DepositAndBridge is Script { + using stdJson for string; + + uint256 deployerPrivateKey = uint256(uint160(address(this))); // default placeholder for tests + + uint256 depositAmount = 0.001 ether; + uint32 NETWORK_ID_L2 = 20; + address receiver = 0x32bdc6A4e8C654dF65503CBb0eDc82B4Ce9158e6; + + function run() public { + deployerPrivateKey = vm.promptSecretUint("PRIVATE_KEY"); + + vm.startBroadcast(deployerPrivateKey); + + console.log(receiver); + + VbETH vbETH = VbETH(payable(0x2DC70fb75b88d2eB4715bc06E1595E6D97c34DFF)); + + uint256 shares = vbETH.depositGasTokenAndBridge{value: depositAmount}(receiver, NETWORK_ID_L2, true); + + console.log(shares); + + vm.stopBroadcast(); + } +} diff --git a/script/RegisterNativeConverters.s.sol b/script/RegisterNativeConverters.s.sol new file mode 100644 index 00000000..ab490a7e --- /dev/null +++ b/script/RegisterNativeConverters.s.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: LicenseRef-PolygonLabs-Open-Attribution OR LicenseRef-PolygonLabs-Source-Available +pragma solidity ^0.8.29; + +import "forge-std/Script.sol"; +import "../src/MigrationManager.sol"; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract RegisterNativeConverters is Script { + using stdJson for string; + + uint32 NETWORK_ID_Y = 20; + + address polygonSecurityMultisig = 0x9d851f8b8751c5FbC09b9E74E6e68E9950949052; + + function run() public { + string memory input = vm.readFile("script/input.json"); + + string memory migrationManagerSlug = + string(abi.encodePacked('["', vm.toString(block.chainid), '"]', '.["migrationManager"]')); + + // Read from input.json based on current chain ID + address migrationManagerAddress = input.readAddress(string.concat(migrationManagerSlug, ".address")); + + MigrationManager migrationManager = MigrationManager(payable(migrationManagerAddress)); + + string[] memory vbTokens = new string[](5); + vbTokens[0] = "vbUSDS"; + vbTokens[1] = "vbUSDT"; + vbTokens[2] = "vbUSDC"; + vbTokens[3] = "vbWBTC"; + vbTokens[4] = "vbETH"; + + vm.startBroadcast(polygonSecurityMultisig); + + // register NativeConverters + + for (uint256 i = 0; i < vbTokens.length; i++) { + address nativeConverter = + input.readAddress(string.concat(migrationManagerSlug, ".", vbTokens[i], "NativeConverter")); + address vbToken = input.readAddress(string.concat(migrationManagerSlug, ".", vbTokens[i])); + + uint32[] memory layerYLxlyIds = new uint32[](1); + layerYLxlyIds[0] = NETWORK_ID_Y; + address[] memory nativeConverters = new address[](1); + nativeConverters[0] = nativeConverter; + + // migrationManager.configureNativeConverters(layerYLxlyIds, nativeConverters, payable(address(vbToken))); + bytes memory payload = abi.encodeCall( + migrationManager.configureNativeConverters, (layerYLxlyIds, nativeConverters, payable(address(vbToken))) + ); + + console.log("Payload to be sent from", polygonSecurityMultisig); + console.logBytes(payload); + + /* MigrationManager.TokenPair memory tokenPair = + migrationManager.nativeConvertersConfiguration(NETWORK_ID_Y, nativeConverter); + + string memory vbTokenSlug = + string(abi.encodePacked('["', vm.toString(block.chainid), '"]', '.["', vbTokens[i], '"]')); + + address underlyingToken = input.readAddress(string.concat(vbTokenSlug, ".underlyingToken")); + + vm.assertEq(address(tokenPair.vbToken), address(vbToken)); + vm.assertEq(address(tokenPair.underlyingToken), address(underlyingToken)); + vm.assertEq( + IERC20(underlyingToken).allowance(address(migrationManager), address(vbToken)), type(uint256).max + ); */ + } + + vm.stopBroadcast(); + } +} diff --git a/script/input.json b/script/input.json index 741dbc76..c0de9bbf 100644 --- a/script/input.json +++ b/script/input.json @@ -3,7 +3,19 @@ "migrationManager": { "proxyAdmin": "0x9d851f8b8751c5FbC09b9E74E6e68E9950949052", "ownerMigrationManager": "0x9d851f8b8751c5FbC09b9E74E6e68E9950949052", - "lxlyBridge": "0x2a3DD3EB832aF982ec71669E178424b10Dca2EDe" + "lxlyBridge": "0x2a3DD3EB832aF982ec71669E178424b10Dca2EDe", + "address": "0x417d01B64Ea30C4E163873f3a1f77b727c689e02", + "layerYNetworkId": 20, + "vbUSDSNativeConverter": "0x000059dE96F9C28e3a343b831cbDC2B93c8C4855", + "vbUSDTNativeConverter": "0x000059dE96F9C28e3a343b831cbDC2B93c8C4855", + "vbUSDCNativeConverter": "0x000059dE96F9C28e3a343b831cbDC2B93c8C4855", + "vbWBTCNativeConverter": "0x000059dE96F9C28e3a343b831cbDC2B93c8C4855", + "vbETHNativeConverter": "0x000059dE96F9C28e3a343b831cbDC2B93c8C4855", + "vbUSDS": "0x3DD459dE96F9C28e3a343b831cbDC2B93c8C4855", + "vbUSDT": "0x6d4f9f9f8f0155509ecd6Ac6c544fF27999845CC", + "vbUSDC": "0x53E82ABbb12638F09d9e624578ccB666217a765e", + "vbWBTC": "0x2C24B57e2CCd1f273045Af6A5f632504C432374F", + "vbETH": "0x2DC70fb75b88d2eB4715bc06E1595E6D97c34DFF" }, "vbUSDS": { "proxyAdmin": "0xA8C31B2edd84c654d06d626383f4154D1E40C5Ff", @@ -77,7 +89,54 @@ } }, "747474": { - + "vbUSDS": { + "customToken": "0x62D6A123E8D19d06d68cf0d2294F9A3A0362c6b3", + "underlyingToken": "0x2134866886ce784fE2E0DE819118E4D32b4Be32C", + "owner": "0xA8C31B2edd84c654d06d626383f4154D1E40C5Ff", + "name": "Vault Bridge USDS", + "symbol": "vbUSDS", + "decimals": 18, + "nonMigratableBackingPercentage": 0.01e18 + }, + "vbUSDT": { + "customToken": "0x2DCa96907fde857dd3D816880A0df407eeB2D2F2", + "underlyingToken": "0xf44e3BCB7A2461CC08185E127B324f2486a74E20", + "owner": "0x2De242e27386e224E5fbF110EA8406d5B70740ec", + "name": "Vault Bridge USDT", + "symbol": "vbUSDT", + "decimals": 6, + "nonMigratableBackingPercentage": 0.02e18 + }, + "vbUSDC": { + "customToken": "0x203A662b0BD271A6ed5a60EdFbd04bFce608FD36", + "underlyingToken": "0xfd415D011FfaA8e6f17fa753CdB080d1dE266784", + "owner": "0xf4F2f5F6bAdBE05433C4604320ecC56BbECBC04E", + "name": "Vault Bridge USDC", + "symbol": "vbUSDC", + "decimals": 6, + "nonMigratableBackingPercentage": 0.02e18 + }, + "vbWBTC": { + "customToken": "0x0913DA6Da4b42f538B445599b46Bb4622342Cf52", + "underlyingToken": "0xB33e43A3F276e8e75792b941bccC996EcB2c0bBD", + "owner": "0x2De242e27386e224E5fbF110EA8406d5B70740ec", + "name": "Vault Bridge WBTC", + "symbol": "vbWBTC", + "decimals": 8, + "nonMigratableBackingPercentage": 0.01e18 + }, + "vbETH": { + "customToken": "0xEE7D8BCFb72bC1880D0Cf19822eB0A2e6577aB62", + "underlyingToken": "0x815955d051C6262C16c720b19D735426254Bec5B", + "owner": "0x2De242e27386e224E5fbF110EA8406d5B70740ec", + "name": "Vault Bridge ETH", + "symbol": "vbETH", + "decimals": 18, + "nonMigratableGasBackingPercentage": 0.1e18 + }, + "migrationManager": "0x417d01B64Ea30C4E163873f3a1f77b727c689e02", + "polygonEngineeringMultisig": "0x4e981bAe8E3cd06Ca911ffFE5504B2653ac1C38a", + "lxlyBridge": "0x2a3DD3EB832aF982ec71669E178424b10Dca2EDe" }, "31337": { "proxyAdmin": "0x0000c6A4e8C654dF65503CBb0eDc82B4Ce9158e6", @@ -151,4 +210,4 @@ "yieldVaultMaximumSlippagePercentage": 0 } } -} +} \ No newline at end of file diff --git a/src/CustomToken.sol b/src/CustomToken.sol index 6e4c7a4f..f3c5f84a 100644 --- a/src/CustomToken.sol +++ b/src/CustomToken.sol @@ -177,6 +177,10 @@ abstract contract CustomToken is onlyLxlyBridgeAndNativeConverter nonReentrant { + // When we migrate backing to Lx, we end up sending tokens to address(0) here. + // These need to be claimable so the bridge accounting is correct and we allow it here by not reverting. + if (account == address(0)) return; + _mint(account, value); }