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

ignore #40

Merged
23 commits merged into from
Nov 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
14 changes: 14 additions & 0 deletions src/interfaces/root/IRootERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ interface IRootERC20Bridge {
* @param amount The amount of tokens to deposit.
*/
function depositTo(IERC20Metadata rootToken, address receiver, uint256 amount) external payable;

/**
* @notice Initiate sending an ETH deposit message to the child chain.
* @custom:requires `rootToken` to already be mapped with `mapToken`.
* @param amount The amount of tokens to deposit.
*/
function depositETH(uint256 amount) external payable;
/**
* @notice Initiate sending an ETH deposit message to the child chain, with a specified receiver.
* @custom:requires `rootToken` to already be mapped with `mapToken`.
* @param receiver The address of the receiver on the child chain.
* @param amount The amount of tokens to deposit.
*/
function depositToETH(address receiver, uint256 amount) external payable;
}

interface IRootERC20BridgeEvents {
Expand Down
48 changes: 48 additions & 0 deletions src/interfaces/root/flowrate/IRootERC20BridgeFlowRate.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: Apache 2.0
pragma solidity 0.8.19;

interface IRootERC20BridgeFlowRate {}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Empty interface?


interface IRootERC20BridgeFlowRateEvents {
/**
* @notice Indicates rate control thresholds have been set for a certain token.
* @param token The token thresholds applied to.
* @param capacity The size of the bucket in tokens.
* @param refillRate How quickly the bucket refills in tokens per second.
* @param largeTransferThreshold Threshold over which a withdrawal is deemed to be large,
* and will be put in the withdrawal queue.
*/
event RateControlThresholdSet(
address indexed token, uint256 capacity, uint256 refillRate, uint256 largeTransferThreshold
);

/**
* @notice Indicates a withdrawal was queued.
* @param token Address of token that is being withdrawn.
* @param withdrawer Child chain sender of tokens.
* @param receiver Recipient of tokens.
* @param amount The number of tokens.
* @param delayWithdrawalLargeAmount is true if the reason for queuing was a large transfer.
* @param delayWithdrawalUnknownToken is true if the reason for queuing was that the
* token had not been configured using the setRateControlThreshold function.
* @param withdrawalQueueActivated is true if the withdrawal queue has been activated.
*/
event QueuedWithdrawal(
address indexed token,
address indexed withdrawer,
address indexed receiver,
uint256 amount,
bool delayWithdrawalLargeAmount,
bool delayWithdrawalUnknownToken,
bool withdrawalQueueActivated
);
}

interface IRootERC20BridgeFlowRateErrors {
// Error if the RootERC20Bridge initializer is called, and not the one for this contract.
error WrongInitializer();
// finaliseQueuedWithdrawalsAggregated was called with a zero length indices array.
error ProvideAtLeastOneIndex();
// The expected and actual token did not match for an aggregated withdrawal.
error MixedTokens(address token, address actualToken);
}
50 changes: 42 additions & 8 deletions src/root/RootERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/acce
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
// import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import {IAxelarGateway} from "@axelar-cgp-solidity/contracts/interfaces/IAxelarGateway.sol";
import {
IRootERC20Bridge,
Expand All @@ -28,6 +30,7 @@ import {IWETH} from "../interfaces/root/IWETH.sol";
*/
contract RootERC20Bridge is
AccessControlUpgradeable, // AccessControlUpgradeable inherits Initializable
PausableUpgradeable,
IRootERC20Bridge,
IRootERC20BridgeEvents,
IRootERC20BridgeErrors
Expand Down Expand Up @@ -71,6 +74,30 @@ contract RootERC20Bridge is
/// @dev A limit of zero indicates unlimited.
uint256 public imxCumulativeDepositLimit;

function initialize(
InitializationRoles memory newRoles,
address newRootBridgeAdaptor,
address newChildERC20Bridge,
string memory newChildBridgeAdaptor,
address newChildTokenTemplate,
address newRootIMXToken,
address newRootWETHToken,
string memory newChildChain,
uint256 newImxCumulativeDepositLimit
) external virtual initializer {
__RootERC20Bridge_init(
newRoles,
newRootBridgeAdaptor,
newChildERC20Bridge,
newChildBridgeAdaptor,
newChildTokenTemplate,
newRootIMXToken,
newRootWETHToken,
newChildChain,
newImxCumulativeDepositLimit
);
}

/**
* @notice Initialization function for RootERC20Bridge.
* @param newRoles Struct containing addresses of roles.
Expand All @@ -84,7 +111,7 @@ contract RootERC20Bridge is
* @param newImxCumulativeDepositLimit The cumulative IMX deposit limit.
* @dev Can only be called once.
*/
function initialize(
function __RootERC20Bridge_init(
InitializationRoles memory newRoles,
address newRootBridgeAdaptor,
address newChildERC20Bridge,
Expand All @@ -94,7 +121,7 @@ contract RootERC20Bridge is
address newRootWETHToken,
string memory newChildChain,
uint256 newImxCumulativeDepositLimit
) public initializer {
) internal {
if (
newRootBridgeAdaptor == address(0) || newChildERC20Bridge == address(0)
|| newChildTokenTemplate == address(0) || newRootIMXToken == address(0) || newRootWETHToken == address(0)
Expand Down Expand Up @@ -386,21 +413,28 @@ contract RootERC20Bridge is
}
}

function _withdraw(bytes memory data) private {
(address rootToken, address withdrawer, address receiver, uint256 amount) =
abi.decode(data, (address, address, address, uint256));
address childToken;
function _withdraw(bytes memory data) internal virtual {
(address rootToken, address childToken, address withdrawer, address receiver, uint256 amount) =
_decodeAndValidateWithdrawal(data);
_executeTransfer(rootToken, childToken, withdrawer, receiver, amount);
}

function _decodeAndValidateWithdrawal(bytes memory data)
internal
view
returns (address rootToken, address childToken, address withdrawer, address receiver, uint256 amount)
{
(rootToken, withdrawer, receiver, amount) = abi.decode(data, (address, address, address, uint256));
if (rootToken == rootIMXToken) {
childToken = NATIVE_IMX;
} else if (rootToken == NATIVE_ETH) {
childToken = childETHToken;
} else {
} else {
childToken = rootTokenToChildToken[rootToken];
if (childToken == address(0)) {
revert NotMapped();
}
}
_executeTransfer(rootToken, childToken, withdrawer, receiver, amount);
}

function _executeTransfer(
Expand Down
130 changes: 130 additions & 0 deletions src/root/flowrate/FlowRateDetection.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright Immutable Pty Ltd 2018 - 2023
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

/**
* @title Flow Rate Detection
* @author Immutable Pty Ltd (Peter Robinson @drinkcoffee and Zhenyang Shi @wcgcyx)
* @notice Detects large flows of tokens using a bucket system.
* @dev Each token has a bucket. The bucket is filled at a constant rate: a number of
* tokens per second. The bucket empties each time there is a withdrawal. Withdrawal
* requests for tokens that don't have a configured bucket are delayed.
* Note: This code is part of RootERC20BridgeFlowRate. It has been separated out
* to make it easier to understand and test the functionality.
* Note that this contract is upgradeable.
*/
abstract contract FlowRateDetection {
// Holds flow rate information for a single token.
struct Bucket {
// The number of tokens that can fit in the bucket.
// A capacity of zero indicates that flow rate detection is disabled for the token.
uint256 capacity;
// The number of tokens in the bucket.
uint256 depth;
// The last time the bucket was updated.
uint256 refillTime;
// The number of tokens added per second.
uint256 refillRate;
}

// Map ERC 20 token address to buckets
mapping(address => Bucket) public flowRateBuckets;

// True if all tokens should be put in the withdrawal queue.
bool public withdrawalQueueActivated;

// Emitted when there is a withdrawal request for a token for which there is no bucket.
event WithdrawalForNonFlowRatedToken(address indexed token, uint256 amount);
// Emitted when queue activated or deactivated
event AutoActivatedWithdrawalQueue();
event ActivatedWithdrawalQueue(address who);
event DeactivatedWithdrawalQueue(address who);

error InvalidToken();
error InvalidCapacity();
error InvalidRefillRate();

/**
* @notice Activate the withdrawal queue for all tokens.
*/
function _activateWithdrawalQueue() internal {
withdrawalQueueActivated = true;
emit ActivatedWithdrawalQueue(msg.sender);
}

/**
* @notice Deactivate the withdrawal queue for all tokens.
* @dev This does not affect withdrawals already in the queue.
*/
function _deactivateWithdrawalQueue() internal {
withdrawalQueueActivated = false;
emit DeactivatedWithdrawalQueue(msg.sender);
}

/**
* @notice Initialise or update a bucket for a token.
* @param token Address of the token to configure the bucket for.
* @param capacity The number of tokens before the bucket overflows.
* @param refillRate The number of tokens added to the bucket each second.
* @dev If this is a new bucket, then the depth is the capacity. If the bucket is existing, then
* the depth is not altered.
*/
function _setFlowRateThreshold(address token, uint256 capacity, uint256 refillRate) internal {
if (token == address(0)) {
revert InvalidToken();
}
if (capacity == 0) {
revert InvalidCapacity();
}
if (refillRate == 0) {
revert InvalidRefillRate();
}
Bucket storage bucket = flowRateBuckets[token];
if (bucket.capacity == 0) {
bucket.depth = capacity;
}
bucket.capacity = capacity;
bucket.refillRate = refillRate;
}

/**
* @notice Update the flow rate measurement for a token.
* @param token Address of token being withdrawn.
* @param amount The number of tokens being withdrawn.
* @return delayWithdrawal Delay this withdrawal because it is for an unconfigured token.
*/
function _updateFlowRateBucket(address token, uint256 amount) internal returns (bool delayWithdrawal) {
Bucket storage bucket = flowRateBuckets[token];

uint256 capacity = bucket.capacity;
if (capacity == 0) {
emit WithdrawalForNonFlowRatedToken(token, amount);
return true;
}

// Calculate the depth assuming no withdrawal.
// slither-disable-next-line timestamp
uint256 depth = bucket.depth + (block.timestamp - bucket.refillTime) * bucket.refillRate;
// slither-disable-next-line timestamp
bucket.refillTime = block.timestamp;
// slither-disable-next-line timestamp
if (depth > capacity) {
depth = capacity;
}

// slither-disable-next-line timestamp
if (amount >= depth) {
// The bucket is empty indicating the flow rate is high. Automatically
// enable the withdrawal queue.
emit AutoActivatedWithdrawalQueue();
withdrawalQueueActivated = true;
bucket.depth = 0;
} else {
bucket.depth = depth - amount;
}
return false;
}

// slither-disable-next-line unused-state,naming-convention
uint256[50] private __gapFlowRateDetecton;
}
Loading
Loading