Skip to content

feat: lock tokens in bridge after deployment #24

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

Merged
Merged
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
2 changes: 1 addition & 1 deletion docker/config-example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ L1_PLONK_VERIFIER_ADDR = "0x0000000000000000000000000000000000000001"
[contracts.overrides]

# L1_WETH = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"
# L1_GAS_TOKEN = "0x0000000000000000000000000000000000000000"
# L1_GAS_TOKEN = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"

L2_MESSAGE_QUEUE = "0x5300000000000000000000000000000000000000"
L1_GAS_PRICE_ORACLE = "0x5300000000000000000000000000000000000002"
Expand Down
5 changes: 0 additions & 5 deletions docker/scripts/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ if [ "$L2_RPC_ENDPOINT" = "" ]; then
L2_RPC_ENDPOINT="http://host.docker.internal:8545"
fi

if [ "${L1_RPC_ENDPOINT}" = "" ]; then
echo "L1_RPC_ENDPOINT is not set"
L1_RPC_ENDPOINT="http://host.docker.internal:8543"
fi

if [ "${BATCH_SIZE}" = "" ]; then
BATCH_SIZE="100"
fi
Expand Down
104 changes: 101 additions & 3 deletions scripts/deterministic/DeployScroll.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.24;

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import {TransparentUpgradeableProxy, ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
Expand Down Expand Up @@ -241,23 +242,88 @@ contract DeployScroll is DeterminsticDeployment {
}
}

function checkDeployerBalance() private view {
function checkDeployerBalance() private {
// ignore balance during simulation
if (broadcastLayer == Layer.None) {
return;
}

// check funds for deployment (L1 & L2)
if (DEPLOYER_ADDR.balance < MINIMUM_DEPLOYER_BALANCE) {
revert(
string(
abi.encodePacked(
"[ERROR] insufficient funds on deployer account (",
"[ERROR] insufficient funds on deployer account for contract deployment (",
vm.toString(DEPLOYER_ADDR),
")"
") minimum ETH balance (in wei): ",
vm.toString(MINIMUM_DEPLOYER_BALANCE)
)
)
);
}

// check funds for initial deposit (L1, ETH as gas token)
if (broadcastLayer == Layer.L1 && !ALTERNATIVE_GAS_TOKEN_ENABLED) {
uint256 l1MessengerBalance = address(L1_SCROLL_MESSENGER_PROXY_ADDR).balance;
uint256 amountToLock = L2_DEPLOYER_INITIAL_BALANCE;

uint256 amountToSend = 0;
if (l1MessengerBalance < amountToLock) {
amountToSend = amountToLock - l1MessengerBalance;
}

uint256 minBalance = MINIMUM_DEPLOYER_BALANCE + amountToSend;

if (DEPLOYER_ADDR.balance < minBalance) {
revert(
string(
abi.encodePacked(
"[ERROR] insufficient funds on deployer account for initial deposit (",
vm.toString(DEPLOYER_ADDR),
") minimum ETH balance (in wei): ",
vm.toString(minBalance)
)
)
);
}
}

// check funds for initial deposit (L1, alternative gas token)
// skip it if L1_GAS_TOKEN is not configured in the config file
address gasTokenAddr = tryGetOverride("L1_GAS_TOKEN");
if (broadcastLayer == Layer.L1 && ALTERNATIVE_GAS_TOKEN_ENABLED && gasTokenAddr != address(0)) {
uint256 l1GasTokenGatewayBalance = IERC20Metadata(L1_GAS_TOKEN_ADDR).balanceOf(
L1_GAS_TOKEN_GATEWAY_PROXY_ADDR
);

uint256 scale = 10**(18 - IERC20Metadata(L1_GAS_TOKEN_ADDR).decimals());
uint256 amountToLock = L2_DEPLOYER_INITIAL_BALANCE / scale;
if (L2_DEPLOYER_INITIAL_BALANCE % scale != 0) {
amountToLock += 1;
}

uint256 amountToSend = 0;
if (l1GasTokenGatewayBalance < amountToLock) {
amountToSend = amountToLock - l1GasTokenGatewayBalance;
}

uint256 minBalance = amountToSend;

if (IERC20Metadata(L1_GAS_TOKEN_ADDR).balanceOf(DEPLOYER_ADDR) < minBalance) {
revert(
string(
abi.encodePacked(
"[ERROR] insufficient funds on deployer account for initial deposit (",
vm.toString(DEPLOYER_ADDR),
") minimum ",
IERC20Metadata(L1_GAS_TOKEN_ADDR).symbol(),
" balance (in min token unit): ",
vm.toString(minBalance)
)
)
);
}
}
}

function deployAllContracts() private {
Expand Down Expand Up @@ -360,6 +426,11 @@ contract DeployScroll is DeterminsticDeployment {
// alternative gas token contracts
initializeL1GasTokenGateway();

// lock tokens on L1 to ensure bridge parity,
// we lock ETH in L1ScrollMessenger or GAS_TOKEN in L1GasTokenGateway
// note: this can only be done before transferring ownership
lockTokensOnL1();

transferL1ContractOwnership();
}

Expand Down Expand Up @@ -1292,6 +1363,33 @@ contract DeployScroll is DeterminsticDeployment {
}
}

function lockTokensOnL1() private {
if (!ALTERNATIVE_GAS_TOKEN_ENABLED) {
uint256 l1MessengerBalance = address(L1_SCROLL_MESSENGER_PROXY_ADDR).balance;
uint256 amountToLock = L2_DEPLOYER_INITIAL_BALANCE;

if (l1MessengerBalance < amountToLock) {
uint256 amountToSend = amountToLock - l1MessengerBalance;
payable(L1_SCROLL_MESSENGER_PROXY_ADDR).transfer(amountToSend);
}
} else {
uint256 l1GasTokenGatewayBalance = IERC20Metadata(L1_GAS_TOKEN_ADDR).balanceOf(
L1_GAS_TOKEN_GATEWAY_PROXY_ADDR
);

uint256 scale = 10**(18 - IERC20Metadata(L1_GAS_TOKEN_ADDR).decimals());
uint256 amountToLock = L2_DEPLOYER_INITIAL_BALANCE / scale;
if (L2_DEPLOYER_INITIAL_BALANCE % scale != 0) {
amountToLock += 1;
}

if (l1GasTokenGatewayBalance < amountToLock) {
uint256 amountTosend = amountToLock - l1GasTokenGatewayBalance;
IERC20Metadata(L1_GAS_TOKEN_ADDR).transfer(L1_GAS_TOKEN_GATEWAY_PROXY_ADDR, amountTosend);
}
}
}

function transferL1ContractOwnership() private {
if (Ownable(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR).owner() != OWNER_ADDR) {
Ownable(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR);
Expand Down
1 change: 1 addition & 0 deletions scripts/deterministic/GenerateConfigs.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ contract GenerateChainMonitorConfig is DeployScroll {
vm.writeJson(vm.toString(L1_MESSAGE_QUEUE_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.message_queue");
vm.writeJson(vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.scroll_chain");
vm.writeJson(vm.toString(L1_GAS_TOKEN_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.gas_token");
vm.writeJson(vm.toString(L2_DEPLOYER_INITIAL_BALANCE), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.start_messenger_balance");

// L2
vm.writeJson(L2_RPC_ENDPOINT, CHAIN_MONITOR_CONFIG_PATH, ".l2_config.l2_url");
Expand Down
Loading