Skip to content

Commit

Permalink
feat: add BalanceClaimer and contracts update (#3)
Browse files Browse the repository at this point in the history
feat: add BalanceClaimer and contracts update
---------

Signed-off-by: 0xRaccoon <[email protected]>
Co-authored-by: teddy <[email protected]>
  • Loading branch information
0xRaccoon and 0xteddybear authored Nov 8, 2024
1 parent df11f76 commit e0d5c85
Show file tree
Hide file tree
Showing 34 changed files with 2,821 additions and 23 deletions.
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=
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

0 comments on commit e0d5c85

Please sign in to comment.