From ade9735ac2561345ed701bf21908f27799e233b7 Mon Sep 17 00:00:00 2001 From: Santiago Pittella Date: Tue, 20 Feb 2024 15:37:57 +0100 Subject: [PATCH] update contracts to use solpp for native token, update scripts to use token config --- .../contracts/bridge/L1ERC20Bridge.sol | 30 +++++++++++-------- .../contracts/bridge/L1WethBridge.sol | 6 ++-- .../libraries/BridgeInitializationHelper.sol | 22 +++++++++++--- .../contracts/zksync/facets/Mailbox.sol | 14 +++++---- l1-contracts/scripts/initialize-bridges.ts | 4 +-- l2-contracts/src/deployForceDeployUpgrader.ts | 8 +++-- l2-contracts/src/deployL2Weth.ts | 12 ++++++-- l2-contracts/src/deployTestnetPaymaster.ts | 2 +- l2-contracts/src/utils.ts | 2 +- 9 files changed, 66 insertions(+), 34 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1ERC20Bridge.sol b/l1-contracts/contracts/bridge/L1ERC20Bridge.sol index 6f83c2dcf..853b89477 100644 --- a/l1-contracts/contracts/bridge/L1ERC20Bridge.sol +++ b/l1-contracts/contracts/bridge/L1ERC20Bridge.sol @@ -87,18 +87,26 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, ReentrancyGuard { uint256 _deployBridgeProxyFee, uint256 _amount ) external payable reentrancyGuardInitializer { - bool nativeErc20 = _amount != 0; - require(_l2TokenBeacon != address(0), "nf"); require(_governor != address(0), "nh"); // We are expecting to see the exact three bytecodes that are needed to initialize the bridge require(_factoryDeps.length == 3, "mk"); - // The caller miscalculated deploy transactions fees - if (nativeErc20) { - require(_amount == _deployBridgeImplementationFee + _deployBridgeProxyFee, "fee"); - } else { - require(msg.value == _deployBridgeImplementationFee + _deployBridgeProxyFee, "fee"); - } + + uint256 amount; + // using the preprocessor to check the fees + // In the case of native ERC20, the fees are expected to be sent in the _amount field + // In the case of native ETH, the fees are expected to be sent in the msg.value field + // #if NATIVE_ERC20 == false + require(_amount == 0, "amount should be 0"); + require(msg.value == _deployBridgeImplementationFee + _deployBridgeProxyFee, "fee"); + amount = msg.value; + // #else + // In this case, we also should transfer the tokens from the sender to the contract. + require(msg.value == 0, "msg.value should be 0"); + require(_amount == _deployBridgeImplementationFee + _deployBridgeProxyFee, "fee"); + amount = _amount; + // #endif + l2TokenProxyBytecodeHash = L2ContractHelper.hashL2Bytecode(_factoryDeps[2]); l2TokenBeacon = _l2TokenBeacon; @@ -111,8 +119,7 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, ReentrancyGuard { _deployBridgeImplementationFee, l2BridgeImplementationBytecodeHash, "", // Empty constructor data - _factoryDeps, // All factory deps are needed for L2 bridge - _amount + _factoryDeps // All factory deps are needed for L2 bridge ); // Prepare the proxy constructor data @@ -133,8 +140,7 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, ReentrancyGuard { l2BridgeProxyBytecodeHash, l2BridgeProxyConstructorData, // No factory deps are needed for L2 bridge proxy, because it is already passed in previous step - new bytes[](0), - _amount + new bytes[](0) ); } diff --git a/l1-contracts/contracts/bridge/L1WethBridge.sol b/l1-contracts/contracts/bridge/L1WethBridge.sol index 8a8c8d979..a47f53217 100644 --- a/l1-contracts/contracts/bridge/L1WethBridge.sol +++ b/l1-contracts/contracts/bridge/L1WethBridge.sol @@ -111,8 +111,7 @@ contract L1WethBridge is IL1Bridge, ReentrancyGuard { _deployBridgeImplementationFee, l2WethBridgeImplementationBytecodeHash, "", // Empty constructor data - _factoryDeps, // All factory deps are needed for L2 bridge - _amount + _factoryDeps // All factory deps are needed for L2 bridge ); // Prepare the proxy constructor data @@ -137,8 +136,7 @@ contract L1WethBridge is IL1Bridge, ReentrancyGuard { l2WethBridgeProxyBytecodeHash, l2WethBridgeProxyConstructorData, // No factory deps are needed for L2 bridge proxy, because it is already passed in the previous step - new bytes[](0), - _amount + new bytes[](0) ); } diff --git a/l1-contracts/contracts/bridge/libraries/BridgeInitializationHelper.sol b/l1-contracts/contracts/bridge/libraries/BridgeInitializationHelper.sol index d59cd9469..703d350d6 100644 --- a/l1-contracts/contracts/bridge/libraries/BridgeInitializationHelper.sol +++ b/l1-contracts/contracts/bridge/libraries/BridgeInitializationHelper.sol @@ -32,17 +32,31 @@ library BridgeInitializationHelper { uint256 _deployTransactionFee, bytes32 _bytecodeHash, bytes memory _constructorData, - bytes[] memory _factoryDeps, - uint256 _amount + bytes[] memory _factoryDeps ) internal returns (address deployedAddress) { bytes memory deployCalldata = abi.encodeCall( IL2ContractDeployer.create2, (bytes32(0), _bytecodeHash, _constructorData) ); - _zkSync.requestL2Transaction{value: _deployTransactionFee}( + + uint256 msgValue; + uint256 amount; + + // Using the preprocessor to set the right values, as the `msg.value` and `amount` are not allowed to be set in the same transaction. + // In the native ERC20 case, the `msg.value` is set to 0, and the `amount` is set to the value of the ERC20 tokens to be transferred. + // In the ETH case, the `msg.value` is set to the value of the ETH to be transferred, and the `amount` is set to 0. + // #if NATIVE_ERC20 == false + msgValue = _deployTransactionFee; + amount = 0; + // #else + msgValue = 0; + amount = _deployTransactionFee; + // #endif + + _zkSync.requestL2Transaction{value: msgValue}( L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, 0, - _amount, + amount, deployCalldata, DEPLOY_L2_BRIDGE_COUNTERPART_GAS_LIMIT, REQUIRED_L2_GAS_PRICE_PER_PUBDATA, diff --git a/l1-contracts/contracts/zksync/facets/Mailbox.sol b/l1-contracts/contracts/zksync/facets/Mailbox.sol index bd36c8724..bf460172e 100644 --- a/l1-contracts/contracts/zksync/facets/Mailbox.sol +++ b/l1-contracts/contracts/zksync/facets/Mailbox.sol @@ -307,13 +307,15 @@ contract MailboxFacet is Base, IMailbox { // Check if we are operating with native tokens. // #if NATIVE_ERC20 == true // The address of the token that is used in the L2 as native. - address nativeTokenAddress = address($(L1_NATIVE_TOKEN_ADDRESS)); - // Check balance and allowance. - require(IERC20(nativeTokenAddress).balanceOf(tx.origin) >= amount, "Not enough balance"); - require(IERC20(nativeTokenAddress).allowance(tx.origin, address(this)) >= amount, "Not enough allowance"); + { + address nativeTokenAddress = address($(L1_NATIVE_TOKEN_ADDRESS)); + // Check balance and allowance. + require(IERC20(nativeTokenAddress).balanceOf(tx.origin) >= amount, "Not enough balance"); + require(IERC20(nativeTokenAddress).allowance(tx.origin, address(this)) >= amount, "Not enough allowance"); - // Transfer tokens to the contract. - IERC20(nativeTokenAddress).safeTransferFrom(tx.origin, address(this), amount); + // Transfer tokens to the contract. + IERC20(nativeTokenAddress).safeTransferFrom(tx.origin, address(this), amount); + } // #endif params.sender = _sender; diff --git a/l1-contracts/scripts/initialize-bridges.ts b/l1-contracts/scripts/initialize-bridges.ts index 90156f267..756ed2097 100644 --- a/l1-contracts/scripts/initialize-bridges.ts +++ b/l1-contracts/scripts/initialize-bridges.ts @@ -163,7 +163,7 @@ async function main() { SYSTEM_CONFIG.requiredL2GasPricePerPubdata, [L2_STANDARD_ERC20_PROXY_FACTORY_BYTECODE, L2_STANDARD_ERC20_IMPLEMENTATION_BYTECODE], deployWallet.address, - { gasPrice, nonce, value: requiredValueToPublishBytecodes } + { gasPrice, nonce, value: nativeErc20impl ? 0 : requiredValueToPublishBytecodes } ), erc20Bridge.initialize( [L2_ERC20_BRIDGE_IMPLEMENTATION_BYTECODE, L2_ERC20_BRIDGE_PROXY_BYTECODE, L2_STANDARD_ERC20_PROXY_BYTECODE], @@ -175,7 +175,7 @@ async function main() { { gasPrice, nonce: nonce + 1, - value: requiredValueToInitializeBridge.mul(2), + value: nativeErc20impl ? 0 : requiredValueToInitializeBridge.mul(2), } ), ]; diff --git a/l2-contracts/src/deployForceDeployUpgrader.ts b/l2-contracts/src/deployForceDeployUpgrader.ts index 88a142073..49e2404f8 100644 --- a/l2-contracts/src/deployForceDeployUpgrader.ts +++ b/l2-contracts/src/deployForceDeployUpgrader.ts @@ -23,7 +23,10 @@ async function main() { .name("deploy-force-deploy-upgrader") .description("Deploys the force deploy upgrader contract to L2"); - program.option("--private-key ").action(async (cmd) => { + program + .option("--native-erc20") + .option("--private-key ") + .action(async (cmd) => { const deployWallet = cmd.privateKey ? new Wallet(cmd.privateKey, provider) : Wallet.fromMnemonic( @@ -31,6 +34,7 @@ async function main() { "m/44'/60'/0'/0/1" ).connect(provider); console.log(`Using deployer wallet: ${deployWallet.address}`); + const nativeErc20impl = cmd.nativeErc20 ? true : false; const forceDeployUpgraderBytecode = hre.artifacts.readArtifactSync("ForceDeployUpgrader").bytecode; const create2Salt = ethers.constants.HashZero; @@ -42,7 +46,7 @@ async function main() { ); // TODO: request from API how many L2 gas needs for the transaction. - await create2DeployFromL1(deployWallet, forceDeployUpgraderBytecode, "0x", create2Salt, priorityTxMaxGasLimit); + await create2DeployFromL1(deployWallet, forceDeployUpgraderBytecode, "0x", create2Salt, priorityTxMaxGasLimit, undefined, nativeErc20impl); console.log(`CONTRACTS_L2_DEFAULT_UPGRADE_ADDR=${forceDeployUpgraderAddress}`); }); diff --git a/l2-contracts/src/deployL2Weth.ts b/l2-contracts/src/deployL2Weth.ts index 017f314bd..a242b8a25 100644 --- a/l2-contracts/src/deployL2Weth.ts +++ b/l2-contracts/src/deployL2Weth.ts @@ -42,6 +42,7 @@ async function main() { program.version("0.1.0").name("deploy-l2-weth"); program + .option("--native-erc20") .option("--private-key ") .option("--gas-price ") .option("--nonce ") @@ -99,13 +100,18 @@ async function main() { ethers.constants.HashZero ); + const nativeErc20impl = cmd.nativeErc20 ? true : false; + const tx = await create2DeployFromL1( deployWallet, L2_WETH_IMPLEMENTATION_BYTECODE, "0x", ethers.constants.HashZero, - priorityTxMaxGasLimit + priorityTxMaxGasLimit, + undefined, + nativeErc20impl ); + console.log( `WETH implementation transaction sent with hash ${tx.hash} and nonce ${tx.nonce}. Waiting for receipt...` ); @@ -117,7 +123,9 @@ async function main() { L2_WETH_PROXY_BYTECODE, l2ERC20BridgeProxyConstructor, ethers.constants.HashZero, - priorityTxMaxGasLimit + priorityTxMaxGasLimit, + undefined, + nativeErc20impl ); console.log(`WETH proxy transaction sent with hash ${tx2.hash} and nonce ${tx2.nonce}. Waiting for receipt...`); diff --git a/l2-contracts/src/deployTestnetPaymaster.ts b/l2-contracts/src/deployTestnetPaymaster.ts index daf5f4378..263e5b785 100644 --- a/l2-contracts/src/deployTestnetPaymaster.ts +++ b/l2-contracts/src/deployTestnetPaymaster.ts @@ -32,7 +32,7 @@ async function main() { const testnetPaymasterBytecode = hre.artifacts.readArtifactSync("TestnetPaymaster").bytecode; const create2Salt = ethers.constants.HashZero; const paymasterAddress = computeL2Create2Address(deployWallet, testnetPaymasterBytecode, "0x", create2Salt); - const nativeErc20impl = cmd.nativeErc20 ? true : false; + const nativeErc20impl = cmd.nativeErc20 ? true : false; // TODO: request from API how many L2 gas needs for the transaction. await ( diff --git a/l2-contracts/src/utils.ts b/l2-contracts/src/utils.ts index b57688496..d0c8015b8 100644 --- a/l2-contracts/src/utils.ts +++ b/l2-contracts/src/utils.ts @@ -108,7 +108,7 @@ export async function create2DeployFromL1( REQUIRED_L2_GAS_PRICE_PER_PUBDATA, [bytecode], wallet.address, - { value: expectedCost, gasPrice } + { value: nativeToken? 0 : expectedCost, gasPrice } ); }