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

Scroll native minting deploy scripts #21

Open
wants to merge 16 commits into
base: master
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
11 changes: 0 additions & 11 deletions .vscode/settings.json

This file was deleted.

82 changes: 82 additions & 0 deletions contracts/NativeMinting/BucketRateLimiter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

import "forge-std/console.sol";

import "../../interfaces/IRateLimiter.sol";
import "../../utils/BucketLimiter.sol";

contract BucketRateLimiter is IRateLimiter, Initializable, PausableUpgradeable, OwnableUpgradeable, UUPSUpgradeable {

BucketLimiter.Limit public limit;
address public consumer;

mapping(address => bool) public admins;
mapping(address => bool) public pausers;

event UpdatedAdmin(address indexed admin, bool status);
event UpdatedPauser(address indexed pauser, bool status);

constructor() {
_disableInitializers();
}

function initialize(address owner) external initializer {
__Pausable_init();
__Ownable_init(owner);
__UUPSUpgradeable_init();

limit = BucketLimiter.create(0, 0);
}

function updateRateLimit(address sender, address tokenIn, uint256 amountIn, uint256 amountOut) external whenNotPaused {
require(msg.sender == consumer, "NOT_CONSUMER");
// Count both 'amountIn' and 'amountOut' as rate limit consumption
uint64 consumedAmount = SafeCast.toUint64((amountIn + amountOut + 1e12 - 1) / 1e12);
require(BucketLimiter.consume(limit, consumedAmount), "BucketRateLimiter: rate limit exceeded");
}

function setCapacity(uint256 capacity) external onlyOwner {
// max capacity = max(uint64) * 1e12 ~= 16 * 1e18 * 1e12 = 16 * 1e12 ether, which is practically enough
uint64 capacity64 = SafeCast.toUint64(capacity / 1e12);
BucketLimiter.setCapacity(limit, capacity64);
}

function setRefillRatePerSecond(uint256 refillRate) external onlyOwner {
// max refillRate = max(uint64) * 1e12 ~= 16 * 1e18 * 1e12 = 16 * 1e12 ether per second, which is practically enough
uint64 refillRate64 = SafeCast.toUint64(refillRate / 1e12);
BucketLimiter.setRefillRate(limit, refillRate64);
}

function updateConsumer(address _consumer) external onlyOwner {
consumer = _consumer;
}

function updateAdmin(address admin, bool status) external onlyOwner {
admins[admin] = status;
emit UpdatedAdmin(admin, status);
}

function updatePauser(address pauser, bool status) external onlyOwner {
pausers[pauser] = status;
emit UpdatedPauser(pauser, status);
}

function pauseContract() external {
require(pausers[msg.sender] || admins[msg.sender] || msg.sender == owner(), "NOT_PAUSER");
_pause();
}

function unPauseContract() external {
require(admins[msg.sender] || msg.sender == owner(), "NOT_ADMIN");
_unpause();
}

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

}
68 changes: 68 additions & 0 deletions contracts/NativeMinting/DummyTokenUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";

import {IDummyToken} from "../../interfaces/IDummyToken.sol";

/**
* @title Dummy Token
* @dev ERC20 token with mint and burn functions.
* This token is expected to be used as an accounting token for anticipated deposits.
* For example, when a user deposit ETH on an L2, it needs ~7 days to be sent back to the L1,
* using a faster bridge such as LayerZero allows to deposit a dummy ETH token on the L1
* to keep track of the actual ETH amount deposited on the L1 and L2, without any delay.
* The dummy token will then be exchanged against the actual ETH when the ETH withdrawal is completed.
*/
contract DummyTokenUpgradeable is ERC20Upgradeable, AccessControlUpgradeable, IDummyToken {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

uint8 private immutable _decimals;

/**
* @dev Constructor for DummyToken
* @param decimals_ The number of decimals the token uses
*/
constructor(uint8 decimals_) {
_decimals = decimals_;
_disableInitializers();
}

/**
* @dev Initializes the contract
* @param name The name of the token
* @param symbol The symbol of the token
* @param owner The owner of the token
*/
function initialize(string memory name, string memory symbol, address owner) external initializer {
__ERC20_init(name, symbol);

_grantRole(DEFAULT_ADMIN_ROLE, owner);
}

/**
* @dev Get the number of decimals the token uses
* @return The number of decimals the token uses
*/
function decimals() public view override returns (uint8) {
return _decimals;
}

/**
* @dev Mint function that can only be called by a minter
* @param to The account to mint the tokens to
* @param amount The amount of tokens to mint
*/
function mint(address to, uint256 amount) external virtual override onlyRole(MINTER_ROLE) {
_mint(to, amount);
}

/**
* @dev Burn function that can be called by anyone
* @param amount The amount of tokens to burn
*/
function burn(uint256 amount) external virtual override {
_burn(msg.sender, amount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {IDummyToken} from "../interfaces/IDummyToken.sol";
import {IDummyToken} from "../../interfaces/IDummyToken.sol";
import {L1BaseSyncPoolUpgradeable, Constants} from "./L1BaseSyncPoolUpgradeable.sol";
import {ILiquifier} from "../interfaces/ILiquifier.sol";
import {IWeEth} from "../interfaces/IWeEth.sol";
import {ILiquifier} from "../../interfaces/ILiquifier.sol";
import {IWeEth} from "../../interfaces/IWeEth.sol";

contract EtherfiL1SyncPoolETH is L1BaseSyncPoolUpgradeable {
error EtherfiL1SyncPoolETH__OnlyETH();
Expand Down
39 changes: 39 additions & 0 deletions contracts/NativeMinting/EtherfiL2ExchangeRateProvider.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {L2ExchangeRateProviderUpgradeable} from "./L2ExchangeRateProviderUpgradeable.sol";
import {IAggregatorV3} from "../../interfaces/IAggregatorV3.sol";

contract EtherfiL2ExchangeRateProvider is L2ExchangeRateProviderUpgradeable {
error EtherfiL2ExchangeRateProvider__InvalidRate();

constructor() {
_disableInitializers();
}

function initialize(address owner) external initializer {
__Ownable_init(owner);
}

/**
* @dev Internal function to get rate and last updated time from a rate oracle
* @param rateOracle Rate oracle contract
* @return rate The exchange rate in 1e18 precision
* @return lastUpdated Last updated time
*/
function _getRateAndLastUpdated(address rateOracle, address)
internal
view
override
returns (uint256 rate, uint256 lastUpdated)
{
(, int256 answer,, uint256 updatedAt,) = IAggregatorV3(rateOracle).latestRoundData();

if (answer <= 0) revert EtherfiL2ExchangeRateProvider__InvalidRate();

// adjust 'answer' based on Oracle feed's precision to have 1e18 precision
// rate * 1e18 / 10**oracle.decimals()
uint8 oracleDecimals = IAggregatorV3(rateOracle).decimals();
return (uint256(uint256(answer) * 1e18 / 10**oracleDecimals), updatedAt);
}
}
108 changes: 108 additions & 0 deletions contracts/NativeMinting/L1BaseReceiverUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import {BaseMessengerUpgradeable} from "../../utils/BaseMessengerUpgradeable.sol";
import {IL1SyncPool} from "../../interfaces/IL1SyncPool.sol";
import {IL1Receiver} from "../../interfaces/IL1Receiver.sol";

/**
* @title L1 Base Receiver
* @notice Base contract for L1 receivers
* This contract is intended to receive messages from the native L2 bridge, decode the message
* and then forward it to the L1 sync pool.
*/
abstract contract L1BaseReceiverUpgradeable is OwnableUpgradeable, BaseMessengerUpgradeable, IL1Receiver {
struct L1BaseReceiverStorage {
IL1SyncPool l1SyncPool;
}

// keccak256(abi.encode(uint256(keccak256(l1basereceiver.storage.l1syncpool)) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant L1BaseReceiverStorageLocation =
0xec90cfc37697dc33dbcf188d524bdc2a41f251df5a390991a45d6388ac04b500;

function _getL1BaseReceiverStorage() internal pure returns (L1BaseReceiverStorage storage $) {
assembly {
$.slot := L1BaseReceiverStorageLocation
}
}

error L1BaseReceiver__UnauthorizedCaller();
error L1BaseReceiver__UnauthorizedL2Sender();

event L1SyncPoolSet(address l1SyncPool);

function __L1BaseReceiver_init(address l1SyncPool, address messenger) internal onlyInitializing {
__BaseMessenger_init(messenger);
__L1BaseReceiver_init_unchained(l1SyncPool);
}

function __L1BaseReceiver_init_unchained(address l1SyncPool) internal onlyInitializing {
_setL1SyncPool(l1SyncPool);
}

/**
* @dev Get the L1 sync pool address
* @return The L1 sync pool address
*/
function getL1SyncPool() public view virtual returns (address) {
L1BaseReceiverStorage storage $ = _getL1BaseReceiverStorage();
return address($.l1SyncPool);
}

/**
* @dev Set the L1 sync pool address
* @param l1SyncPool The L1 sync pool address
*/
function setL1SyncPool(address l1SyncPool) public virtual onlyOwner {
_setL1SyncPool(l1SyncPool);
}

/**
* @dev Internal function to set the L1 sync pool address
* @param l1SyncPool The L1 sync pool address
*/
function _setL1SyncPool(address l1SyncPool) internal virtual {
L1BaseReceiverStorage storage $ = _getL1BaseReceiverStorage();
$.l1SyncPool = IL1SyncPool(l1SyncPool);

emit L1SyncPoolSet(l1SyncPool);
}

/**
* @dev Internal function to forward the message to the L1 sync pool
* @param originEid Origin endpoint ID
* @param sender Sender address
* @param guid Message GUID
* @param tokenIn Token address
* @param amountIn Amount of tokens
* @param amountOut Amount of tokens
* @param valueToL1SyncPool Value to send to the L1 sync pool
*/
function _forwardToL1SyncPool(
uint32 originEid,
bytes32 sender,
bytes32 guid,
address tokenIn,
uint256 amountIn,
uint256 amountOut,
uint256 valueToL1SyncPool
) internal virtual {
if (msg.sender != getMessenger()) revert L1BaseReceiver__UnauthorizedCaller();
if (_getAuthorizedL2Address(originEid) != sender) revert L1BaseReceiver__UnauthorizedL2Sender();

L1BaseReceiverStorage storage $ = _getL1BaseReceiverStorage();
$.l1SyncPool.onMessageReceived{value: valueToL1SyncPool}(originEid, guid, tokenIn, amountIn, amountOut);
}

/**
* @dev Internal function to get the authorized L2 address
* @param originEid Origin endpoint ID
* @return The authorized L2 address
*/
function _getAuthorizedL2Address(uint32 originEid) internal view virtual returns (bytes32) {
L1BaseReceiverStorage storage $ = _getL1BaseReceiverStorage();
return $.l1SyncPool.peers(originEid);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
} from "@layerzerolabs/lz-evm-oapp-v2/contracts-upgradeable/oapp/OAppReceiverUpgradeable.sol";
import {Origin} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";

import {IL1SyncPool} from "../interfaces/IL1SyncPool.sol";
import {Constants} from "../libraries/Constants.sol";
import {IL1SyncPool} from "../../interfaces/IL1SyncPool.sol";
import {Constants} from "../../libraries/Constants.sol";

/**
* @title L1 Base Sync Pool
Expand Down
Loading