Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Use preprocessor for native token checks. #19

Open
wants to merge 2 commits into
base: erc20_base_token
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions l1-contracts/contracts/bridge/L1ERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
using SafeERC20 for IERC20;

/// @dev zkSync smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication
IZkSync internal immutable zkSync;

Check warning on line 33 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Immutable variables name are set to be in capitalized SNAKE_CASE

Check warning on line 33 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Immutable variables name are set to be in capitalized SNAKE_CASE

Check warning on line 33 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Immutable variables name are set to be in capitalized SNAKE_CASE

/// @dev A mapping L2 batch number => message number => flag
/// @dev Used to indicate that zkSync L2 -> L1 message was already processed
Expand Down Expand Up @@ -87,18 +87,26 @@
uint256 _deployBridgeProxyFee,
uint256 _amount
) external payable reentrancyGuardInitializer {
bool nativeErc20 = _amount != 0;

require(_l2TokenBeacon != address(0), "nf");

Check warning on line 90 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 90 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 90 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements
require(_governor != address(0), "nh");

Check warning on line 91 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 91 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 91 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements
// We are expecting to see the exact three bytecodes that are needed to initialize the bridge
require(_factoryDeps.length == 3, "mk");

Check warning on line 93 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 93 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 93 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements
// 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");

Check warning on line 100 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 100 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 100 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements
require(msg.value == _deployBridgeImplementationFee + _deployBridgeProxyFee, "fee");

Check warning on line 101 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 101 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 101 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements
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");

Check warning on line 105 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 105 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 105 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements
require(_amount == _deployBridgeImplementationFee + _deployBridgeProxyFee, "fee");

Check warning on line 106 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 106 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 106 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements
amount = _amount;
// #endif

l2TokenProxyBytecodeHash = L2ContractHelper.hashL2Bytecode(_factoryDeps[2]);
l2TokenBeacon = _l2TokenBeacon;

Expand All @@ -111,8 +119,7 @@
_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
Expand All @@ -133,8 +140,7 @@
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)
);
}

Expand Down Expand Up @@ -203,9 +209,9 @@
address _refundRecipient,
uint256 _l2MaxFee
) public payable nonReentrant returns (bytes32 l2TxHash) {
require(_amount != 0, "2T"); // empty deposit amount

Check warning on line 212 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 212 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 212 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements
uint256 amount = _depositFunds(msg.sender, IERC20(_l1Token), _amount);
require(amount == _amount, "1T"); // The token has non-standard transfer logic

Check warning on line 214 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 214 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 214 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

l2TxHash = _getRefundRecipientAndRequestL2Transaction(_refundRecipient, _l2MaxFee, _l2Receiver, _l1Token, _l2TxGasLimit, _l2TxGasPerPubdataByte, amount);
// Save the deposited amount to claim funds on L1 if the deposit failed on L2
Expand Down
6 changes: 2 additions & 4 deletions l1-contracts/contracts/bridge/L1WethBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
22 changes: 18 additions & 4 deletions l1-contracts/contracts/zksync/facets/Mailbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ contract MailboxFacet is Base, IMailbox {
sender = AddressAliasHelper.applyL1ToL2Alias(msg.sender);
}

// Using the preprocessor to ensure that in the case of ETH as native, _amount is 0. Else, in the case of ERC20 as native, msg.value is 0.\
// #if NATIVE_ERC20 == false
require(_amount == 0, "_amount should be 0");
// #elif NATIVE_ERC20 == true
require(msg.value == 0, "msg.value should be 0");
// #endif

// Enforcing that `_l2GasPerPubdataByteLimit` equals to a certain constant number. This is needed
// to ensure that users do not get used to using "exotic" numbers for _l2GasPerPubdataByteLimit, e.g. 1-2, etc.
// VERY IMPORTANT: nobody should rely on this constant to be fixed and every contract should give their users the ability to provide the
Expand Down Expand Up @@ -274,8 +281,13 @@ contract MailboxFacet is Base, IMailbox {
// Here we manually assign fields for the struct to prevent "stack too deep" error
WritePriorityOpParams memory params;

uint256 amount = _amount != 0 ? _amount : msg.value;
// uint256 amount = msg.value;
// Using the preprocessor to set the amount depending on the native token.
// In case of ETH as native, the amount is set to the value of the transaction. In case of ERC20 as native, the amount is set to the value of the `_amount` parameter.
// #if NATIVE_ERC20 == false
uint256 amount = msg.value;
// #elif NATIVE_ERC20 == true
uint256 amount = _amount;
// #endif

// Checking that the user provided enough ether to pay for the transaction.
// Using a new scope to prevent "stack too deep" error
Expand All @@ -293,8 +305,9 @@ contract MailboxFacet is Base, IMailbox {
}

// Check if we are operating with native tokens.
if (_amount != 0) {
// The address of the token that is used in the L2 as native.
// #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");
Expand All @@ -303,6 +316,7 @@ contract MailboxFacet is Base, IMailbox {
// Transfer tokens to the contract.
IERC20(nativeTokenAddress).safeTransferFrom(tx.origin, address(this), amount);
}
// #endif

params.sender = _sender;
params.txId = s.priorityQueue.getTotalPriorityTxs();
Expand Down
4 changes: 2 additions & 2 deletions l1-contracts/scripts/initialize-bridges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
? new Wallet(cmd.privateKey, provider)
: Wallet.fromMnemonic(
process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic,
"m/44'/60'/0'/0/0"

Check failure on line 70 in l1-contracts/scripts/initialize-bridges.ts

View workflow job for this annotation

GitHub Actions / lint

Insert `··`

Check failure on line 70 in l1-contracts/scripts/initialize-bridges.ts

View workflow job for this annotation

GitHub Actions / lint

Insert `··`

Check failure on line 70 in l1-contracts/scripts/initialize-bridges.ts

View workflow job for this annotation

GitHub Actions / lint

Insert `··`
).connect(provider);

const nativeErc20impl = cmd.nativeErc20 ? true : false;
Expand Down Expand Up @@ -163,7 +163,7 @@
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],
Expand All @@ -175,7 +175,7 @@
{
gasPrice,
nonce: nonce + 1,
value: requiredValueToInitializeBridge.mul(2),
value: nativeErc20impl ? 0 : requiredValueToInitializeBridge.mul(2),
}
),
];
Expand Down
8 changes: 6 additions & 2 deletions l2-contracts/src/deployForceDeployUpgrader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@ async function main() {
.name("deploy-force-deploy-upgrader")
.description("Deploys the force deploy upgrader contract to L2");

program.option("--private-key <private-key>").action(async (cmd) => {
program
.option("--native-erc20")
.option("--private-key <private-key>")
.action(async (cmd) => {
const deployWallet = cmd.privateKey
? new Wallet(cmd.privateKey, provider)
: Wallet.fromMnemonic(
process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic,
"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;
Expand All @@ -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}`);
});
Expand Down
12 changes: 10 additions & 2 deletions l2-contracts/src/deployL2Weth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ async function main() {
program.version("0.1.0").name("deploy-l2-weth");

program
.option("--native-erc20")
.option("--private-key <private-key>")
.option("--gas-price <gas-price>")
.option("--nonce <nonce>")
Expand Down Expand Up @@ -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...`
);
Expand All @@ -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...`);

Expand Down
2 changes: 1 addition & 1 deletion l2-contracts/src/deployTestnetPaymaster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
2 changes: 1 addition & 1 deletion l2-contracts/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
);
}

Expand Down
Loading