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: add BalanceClaimer and contracts update #3

Merged
merged 13 commits into from
Nov 8, 2024
Merged
29 changes: 29 additions & 0 deletions .github/workflows/medusa.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: CI

on: [push]

jobs:
medusa-tests:
name: Medusa Test
runs-on: ubuntu-latest
container: ghcr.io/defi-wonderland/eth-security-toolbox-ci:dev

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

- name: Setup Node.js 16.x
uses: actions/setup-node@master
with:
node-version: 16.x
cache: yarn

- name: Install dependencies
working-directory: ./packages/contracts-bedrock
run: yarn --frozen-lockfile --network-concurrency 1

- name: Run Medusa
working-directory: ./packages/contracts-bedrock
run: medusa fuzz --test-limit 200000
5 changes: 5 additions & 0 deletions packages/contracts-bedrock/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ PRIVATE_KEY_DEPLOYER=
TENDERLY_PROJECT=
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we check
/packages/contracts-bedrock/README.md ?

Specifically that is mandatory to use foundry version:
foundryup -C da2392e58bb8a7fefeba46b40c4df1afad8ccd22

TENDERLY_USERNAME=

# RPC
ETHEREUM_MAINNET_RPC=
LOCAL_RPC=


# The following settings are useful for manually testing the migration scripts.

Expand All @@ -22,3 +26,4 @@ DISABLE_LIVE_DEPLOYER=true

# Sets the deployer's key to match the first default hardhat account
PRIVATE_KEY_DEPLOYER=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
PRIVATE_KEY_PROXY_ADMIN=
36 changes: 31 additions & 5 deletions packages/contracts-bedrock/contracts/L1/L1StandardBridge.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

// Libraries
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

// Interfaces
import { IBalanceClaimer } from "./interfaces/winddown/IBalanceClaimer.sol";
import { IErc20BalanceWithdrawer } from "./interfaces/winddown/IErc20BalanceWithdrawer.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { Predeploys } from "../libraries/Predeploys.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
import { Semver } from "../universal/Semver.sol";
Expand All @@ -17,7 +25,9 @@ import { Semver } from "../universal/Semver.sol";
* of some token types that may not be properly supported by this contract include, but are
* not limited to: tokens with transfer fees, rebasing tokens, and tokens with blocklists.
*/
contract L1StandardBridge is StandardBridge, Semver {
contract L1StandardBridge is StandardBridge, Semver, IErc20BalanceWithdrawer {
using SafeERC20 for IERC20;

/**
* @custom:legacy
* @notice Emitted whenever a deposit of ETH from L1 into L2 is initiated.
Expand Down Expand Up @@ -90,15 +100,20 @@ contract L1StandardBridge is StandardBridge, Semver {
bytes extraData
);

/// @inheritdoc IErc20BalanceWithdrawer
address public immutable BALANCE_CLAIMER;

/**
* @custom:semver 1.1.0
* @custom:semver 1.2.0
*
* @param _messenger Address of the L1CrossDomainMessenger.
*/
constructor(address payable _messenger)
Semver(1, 1, 0)
constructor(address payable _messenger, address _balanceClaimer)
Semver(1, 2, 0)
StandardBridge(_messenger, payable(Predeploys.L2_STANDARD_BRIDGE))
{}
{
BALANCE_CLAIMER = _balanceClaimer;
}

/**
* @notice Allows EOAs to bridge ETH by sending directly to the bridge.
Expand Down Expand Up @@ -244,6 +259,17 @@ contract L1StandardBridge is StandardBridge, Semver {
finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _extraData);
}

/// @inheritdoc IErc20BalanceWithdrawer
function withdrawErc20Balance(address _user, Erc20BalanceClaim[] calldata _erc20Claim) external {
if (msg.sender != BALANCE_CLAIMER) {
revert CallerNotBalanceClaimer();
}

for (uint256 _i; _i < _erc20Claim.length; _i++) {
IERC20(_erc20Claim[_i].token).safeTransfer(_user, _erc20Claim[_i].balance);
}
}

/**
* @custom:legacy
* @notice Retrieves the access of the corresponding L2 bridge contract.
Expand Down
27 changes: 23 additions & 4 deletions packages/contracts-bedrock/contracts/L1/OptimismPortal.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

// Interfaces
import { IEthBalanceWithdrawer } from "./interfaces/winddown/IEthBalanceWithdrawer.sol";
import { IBalanceClaimer } from "./interfaces/winddown/IBalanceClaimer.sol";

import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { SafeCall } from "../libraries/SafeCall.sol";
import { L2OutputOracle } from "./L2OutputOracle.sol";
Expand All @@ -20,7 +24,7 @@ import { Semver } from "../universal/Semver.sol";
* and L2. Messages sent directly to the OptimismPortal have no form of replayability.
* Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface.
*/
contract OptimismPortal is Initializable, ResourceMetering, Semver {
contract OptimismPortal is Initializable, ResourceMetering, Semver, IEthBalanceWithdrawer {
/**
* @notice Represents a proven withdrawal.
*
Expand Down Expand Up @@ -82,6 +86,9 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
*/
bool public paused;

/// @inheritdoc IEthBalanceWithdrawer
address public immutable BALANCE_CLAIMER;

/**
* @notice Emitted when a transaction is deposited from L1 to L2. The parameters of this event
* are read by the rollup node and used to derive deposit transactions on L2.
Expand Down Expand Up @@ -140,22 +147,25 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
}

/**
* @custom:semver 1.6.0
* @custom:semver 1.7.0
*
* @param _l2Oracle Address of the L2OutputOracle contract.
* @param _guardian Address that can pause deposits and withdrawals.
* @param _paused Sets the contract's pausability state.
* @param _config Address of the SystemConfig contract.
* @param _balanceClaimer Address of the BalanceClaimer contract.
*/
constructor(
L2OutputOracle _l2Oracle,
address _guardian,
bool _paused,
SystemConfig _config
) Semver(1, 6, 0) {
SystemConfig _config,
address _balanceClaimer
) Semver(1, 7, 0) {
L2_ORACLE = _l2Oracle;
GUARDIAN = _guardian;
SYSTEM_CONFIG = _config;
BALANCE_CLAIMER = _balanceClaimer;
initialize(_paused);
}

Expand Down Expand Up @@ -482,6 +492,15 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData);
}

/// @inheritdoc IEthBalanceWithdrawer
function withdrawEthBalance(address _user, uint256 _ethClaim) external {
if (msg.sender != BALANCE_CLAIMER) revert CallerNotBalanceClaimer();
(bool success,) = _user.call{value: _ethClaim}("");
if (!success) {
revert IEthBalanceWithdrawer.EthTransferFailed();
}
}

/**
* @notice Determine if a given output is finalized. Reverts if the call to
* L2_ORACLE.getL2Output reverts. Returns a boolean otherwise.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import { IEthBalanceWithdrawer } from "./IEthBalanceWithdrawer.sol";
import { IErc20BalanceWithdrawer } from "./IErc20BalanceWithdrawer.sol";


/**
* @title IBalanceClaimer
* @notice Interface for the BalanceClaimer contract
*/
interface IBalanceClaimer {
/**
* @notice Emitted when a user claims their balance
* @param user The user who claimed their balance
* @param ethBalance The eth balance of the user
* @param erc20TokenBalances The ERC20 token balances of the user
*/
event BalanceClaimed(
address indexed user,
uint256 ethBalance,
IErc20BalanceWithdrawer.Erc20BalanceClaim[] erc20TokenBalances
);

/// @notice Thrown when the user has no balance to claim
error NoBalanceToClaim();

/// @notice Thrown when the merkle root is invalid
error InvalidMerkleRoot();

/// @notice the root of the merkle tree
function ROOT() external view returns (bytes32);

/// @notice OptimismPortal ethBalanceWithdrawer contract
function ETH_BALANCE_WITHDRAWER() external view returns (IEthBalanceWithdrawer);

/// @notice erc20BalanceWithdrawer contract
function ERC20_BALANCE_WITHDRAWER() external view returns (IErc20BalanceWithdrawer);

/// @notice return users who claimed their balances
function claimed(address) external view returns (bool);

/**
* @notice Claims the tokens for the user
* @param _proof The merkle proof
* @param _user The user address
* @param _ethBalance The eth balance of the user
* @param _erc20Claim The ERC20 tokens balances of the user
*/
function claim(
bytes32[] calldata _proof,
address _user,
uint256 _ethBalance,
IErc20BalanceWithdrawer.Erc20BalanceClaim[] calldata _erc20Claim
) external;

/**
* @notice Checks if the user can claim the tokens
* @param _proof The merkle proof
* @param _user The user address
* @param _ethBalance The eth balance of the user
* @param _erc20Claim The ERC20 tokens balances of the user
* @return _canClaimTokens True if the user can claim the tokens
*/
function canClaim(
bytes32[] calldata _proof,
address _user,
uint256 _ethBalance,
IErc20BalanceWithdrawer.Erc20BalanceClaim[] calldata _erc20Claim
) external view returns (bool _canClaimTokens);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import { IBalanceClaimer } from "./IBalanceClaimer.sol";

/**
* @title IErc20BalanceWithdrawer
* @notice Interface for the Erc20BalanceWithdrawer contract
*/
interface IErc20BalanceWithdrawer {
/**
* @notice Struct for ERC20 balance claim
* @param token The ERC20 token address
* @param balance The balance of the user
*/
struct Erc20BalanceClaim {
address token;
uint256 balance;
}

/// @notice Thrown when the caller is not the BalanceClaimer contract
error CallerNotBalanceClaimer();

/**
* @notice Withdraws the ERC20 balance to the user.
* @param _user Address of the user.
* @param _erc20Claim Array of Erc20BalanceClaim structs containing the token address
*/
function withdrawErc20Balance(address _user, Erc20BalanceClaim[] calldata _erc20Claim)
external;

/**
* @notice Address of the balance claimer contract.
* @dev This contract is responsible for claiming the ERC20 balances of the bridge.
*/
function BALANCE_CLAIMER() external view returns (address);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import { IBalanceClaimer } from "./IBalanceClaimer.sol";

/**
* @title IEthBalanceWithdrawer
* @notice Interface for the EthBalanceWithdrawer contract
*/
interface IEthBalanceWithdrawer {
/// @notice Thrown when the caller is not the BalanceClaimer contract
error CallerNotBalanceClaimer();

/// @notice Thrown when the eth transfer fails
error EthTransferFailed();

/**
* @notice Withdraws the ETH balance to the user.
* @param _user Address of the user.
* @param _ethClaim Amount of ETH to withdraw.
* @dev This function is only callable by the BalanceClaimer contract.
*/
function withdrawEthBalance(address _user, uint256 _ethClaim) external;

/**
* @notice Address of the BalanceClaimer contract.
* @dev This contract is responsible for claiming the ETH balances of the OptimismPortal.
*/
function BALANCE_CLAIMER() external view returns (address);
}
Loading
Loading