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

Implement MIP-74 #992

Open
wants to merge 6 commits into
base: feature/trusted-relayer
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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions protocol-units/bridge/contracts/src/INativeBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface INativeBridge {

event InsuranceFundUpdated(address insuranceFund);
event PauseToggled(bool paused);
event RiskDenominatorUpdated(uint256 riskDenominator);
event InsuranceBudgetDividerUpdated(uint256 insuranceBudgetDivider);

error ZeroAmount();
error MOVETransferFailed();
Expand All @@ -32,7 +32,7 @@ interface INativeBridge {
error InvalidNonce();
error OutboundRateLimitExceeded();
error InboundRateLimitExceeded();
error InvalidRiskDenominator();
error InvalidInsuranceBudgetDivider();

/**
* @dev Creates a new bridge
Expand Down
58 changes: 42 additions & 16 deletions protocol-units/bridge/contracts/src/NativeBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,22 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB
uint256 amount;
}

// Inbound: Circulating token is released on the destination chain (unlock or mint).
// Outbound: Circulating token is taken from the source chain (lock or burn).
mapping(uint256 nonce => OutboundTransfer) public noncesToOutboundTransfers;
Primata marked this conversation as resolved.
Show resolved Hide resolved
mapping(bytes32 bridgeTransferId => uint256 nonce) public idsToInboundNonces;
mapping(uint256 day => uint256 amount) public inboundRateLimitBudget;
mapping(uint256 day => uint256 amount) public outboundRateLimitBudget;

bytes32 public constant RELAYER_ROLE = keccak256(abi.encodePacked("RELAYER_ROLE"));
uint256 public constant MINIMUM_RISK_DENOMINATOR = 3;
bytes32 public constant PAUSER_ROLE = keccak256(abi.encodePacked("PAUSER_ROLE"));

// The insuranceBudgetDivider determines the fraction of the insurance fund that can be used for the per day budget for a given transfer direction.
// inusrance budget divider must be equal or above 4
uint256 public constant INSURANCE_BUDGET_DIVIDER_LOWER_BOUND = 3;
IERC20 public moveToken;
address public insuranceFund;
uint256 public riskDenominator;
uint256 public insuranceBudgetDivider;
uint256 private _nonce;

// Prevents initialization of implementation contract exploits
Expand All @@ -38,6 +45,7 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB
* @param _admin The address of the admin role
* @param _relayer The address of the relayer role
* @param _maintainer The address of the maintainer role
* @param _insuranceFund The address of the insurance fund
*/
function initialize(
address _moveToken,
Expand All @@ -54,10 +62,12 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB

// Set insurance fund
insuranceFund = _insuranceFund;
riskDenominator = MINIMUM_RISK_DENOMINATOR + 1;
insuranceBudgetDivider = INSURANCE_BUDGET_DIVIDER_LOWER_BOUND + 1;

// Maintainer is optional
_grantRole(RELAYER_ROLE, _maintainer);
if (_maintainer != address(0)) {
_grantRole(RELAYER_ROLE, _maintainer);
}
}

/**
Expand All @@ -71,9 +81,12 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB
external
whenNotPaused
returns (bytes32 bridgeTransferId)
{
// Ensure there is a valid amount
{
// Ensure there is a valid amount`
require(amount > 0, ZeroAmount());

_rateLimitOutbound(amount);

address initiator = msg.sender;

// Transfer the MOVE tokens from the user to the contract
Expand Down Expand Up @@ -172,35 +185,48 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB
}

/**
* @dev Sets the risk denominator for the bridge
* @param _riskDenominator The new risk denominator
* @dev Sets the insurance budget divider for the bridge
* @param _insuranceBudgetDivider The new insurance budget divider
*/
function setRiskDenominator(uint256 _riskDenominator) external onlyRole(DEFAULT_ADMIN_ROLE) {
// risk denominator must be at least 4
require(_riskDenominator > MINIMUM_RISK_DENOMINATOR, InvalidRiskDenominator());
riskDenominator = _riskDenominator;
emit RiskDenominatorUpdated(_riskDenominator);
function setInsuranceBudgetDivider(uint256 _insuranceBudgetDivider) external onlyRole(DEFAULT_ADMIN_ROLE) {
// insurance budget divider must be at least 4
require(_insuranceBudgetDivider > INSURANCE_BUDGET_DIVIDER_LOWER_BOUND, InvalidInsuranceBudgetDivider());
insuranceBudgetDivider = _insuranceBudgetDivider;
emit InsuranceBudgetDividerUpdated(_insuranceBudgetDivider);
}

/**
* @dev Toggles the paused state of the contract
*/
function togglePause() external onlyRole(DEFAULT_ADMIN_ROLE) {
function togglePause() external onlyRole(PAUSER_ROLE) {
paused() ? _pause() : _unpause();
emit PauseToggled(paused());
}

/**
* @dev Rate limits the inbound transfers based on the insurance fund and risk denominator
* @dev Rate limits the outboud transfers based on the insurance fund and insurance budget divider
* @param amount The amount to rate limit
*/
function _rateLimitOutbound(uint256 amount) public {
uint256 day = block.timestamp / 1 days;
outboundRateLimitBudget[day] += amount;
require(
outboundRateLimitBudget[day] < moveToken.balanceOf(insuranceFund) / insuranceBudgetDivider,
OutboundRateLimitExceeded()
);
}

/**
* @dev Rate limits the inbound transfers based on the insurance fund and insurance budget divider
* @param amount The amount to rate limit
*/
function _rateLimitInbound(uint256 amount) public {
uint256 day = block.timestamp / 1 days;
inboundRateLimitBudget[day] += amount;
require(
inboundRateLimitBudget[day] < moveToken.balanceOf(insuranceFund) / riskDenominator,
inboundRateLimitBudget[day] < moveToken.balanceOf(insuranceFund) / insuranceBudgetDivider,
InboundRateLimitExceeded()
);
}

}
2 changes: 1 addition & 1 deletion protocol-units/bridge/contracts/test/NativeBridge.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ contract NativeBridgeTest is Test {
vm.assume(_originator != address(0));
vm.assume(_originator != deployer);

_amount = bound(_amount, 1, 1000000 * 1e8 - 1);
_amount = bound(_amount, 1, 1000000 * 1e8 / 4 - 1);
moveToken.transfer(_originator, _amount);
vm.startPrank(_originator);
// require approval
Expand Down
1 change: 1 addition & 0 deletions protocol-units/settlement/mcr/contracts/lib/v2-periphery
Submodule v2-periphery added at 0335e8
Loading