From 60debe35daef5807ed160c1921eac876ffc750ac Mon Sep 17 00:00:00 2001 From: primata Date: Tue, 17 Dec 2024 14:30:03 +0100 Subject: [PATCH 1/5] rename min required risk den --- protocol-units/bridge/contracts/src/NativeBridge.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/protocol-units/bridge/contracts/src/NativeBridge.sol b/protocol-units/bridge/contracts/src/NativeBridge.sol index 1b25451d0..83b9d388e 100644 --- a/protocol-units/bridge/contracts/src/NativeBridge.sol +++ b/protocol-units/bridge/contracts/src/NativeBridge.sol @@ -21,7 +21,9 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB mapping(uint256 day => uint256 amount) public inboundRateLimitBudget; bytes32 public constant RELAYER_ROLE = keccak256(abi.encodePacked("RELAYER_ROLE")); - uint256 public constant MINIMUM_RISK_DENOMINATOR = 3; + + // Risk denominator must be above 3 + uint256 public constant RISK_DENOMINATOR_LOWER_BOUND = 3; IERC20 public moveToken; address public insuranceFund; uint256 public riskDenominator; @@ -54,7 +56,7 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB // Set insurance fund insuranceFund = _insuranceFund; - riskDenominator = MINIMUM_RISK_DENOMINATOR + 1; + riskDenominator = RISK_DENOMINATOR_LOWER_BOUND + 1; // Maintainer is optional _grantRole(RELAYER_ROLE, _maintainer); @@ -72,7 +74,7 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB whenNotPaused returns (bytes32 bridgeTransferId) { - // Ensure there is a valid amount + // Ensure there is a valid amount` require(amount > 0, ZeroAmount()); address initiator = msg.sender; @@ -177,7 +179,7 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB */ function setRiskDenominator(uint256 _riskDenominator) external onlyRole(DEFAULT_ADMIN_ROLE) { // risk denominator must be at least 4 - require(_riskDenominator > MINIMUM_RISK_DENOMINATOR, InvalidRiskDenominator()); + require(_riskDenominator > RISK_DENOMINATOR_LOWER_BOUND, InvalidRiskDenominator()); riskDenominator = _riskDenominator; emit RiskDenominatorUpdated(_riskDenominator); } From cbd4ee6f5db817c33b6e2dd261b4030f470babf3 Mon Sep 17 00:00:00 2001 From: primata Date: Thu, 9 Jan 2025 19:48:44 -0300 Subject: [PATCH 2/5] implement mip74 --- Cargo.lock | 2 +- .../bridge/contracts/src/NativeBridge.sol | 28 ++++++++++++++++--- .../settlement/mcr/contracts/lib/v2-periphery | 1 + 3 files changed, 26 insertions(+), 5 deletions(-) create mode 160000 protocol-units/settlement/mcr/contracts/lib/v2-periphery diff --git a/Cargo.lock b/Cargo.lock index a98a4caab..63451e2e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,8 +4341,8 @@ dependencies = [ "hex", "rand 0.7.3", "serde", - "tokio", "thiserror 1.0.69", + "tokio", "tokio-stream", "tracing", ] diff --git a/protocol-units/bridge/contracts/src/NativeBridge.sol b/protocol-units/bridge/contracts/src/NativeBridge.sol index 83b9d388e..69b6f6d5a 100644 --- a/protocol-units/bridge/contracts/src/NativeBridge.sol +++ b/protocol-units/bridge/contracts/src/NativeBridge.sol @@ -21,6 +21,7 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB mapping(uint256 day => uint256 amount) public inboundRateLimitBudget; bytes32 public constant RELAYER_ROLE = keccak256(abi.encodePacked("RELAYER_ROLE")); + bytes32 public constant PAUSER_ROLE = keccak256(abi.encodePacked("PAUSER_ROLE")); // Risk denominator must be above 3 uint256 public constant RISK_DENOMINATOR_LOWER_BOUND = 3; @@ -40,6 +41,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, @@ -59,7 +61,9 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB riskDenominator = RISK_DENOMINATOR_LOWER_BOUND + 1; // Maintainer is optional - _grantRole(RELAYER_ROLE, _maintainer); + if (_maintainer != address(0)) { + _grantRole(RELAYER_ROLE, _maintainer); + } } /** @@ -73,9 +77,12 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB external whenNotPaused returns (bytes32 bridgeTransferId) - { + { // 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 @@ -187,16 +194,28 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB /** * @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 risk denominator * @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) / riskDenominator, + OutboundRateLimitExceeded() + ); + } + /** + * @dev Rate limits the inbound transfers based on the insurance fund and risk denominator + * @param amount The amount to rate limit + */ function _rateLimitInbound(uint256 amount) public { uint256 day = block.timestamp / 1 days; inboundRateLimitBudget[day] += amount; @@ -205,4 +224,5 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB InboundRateLimitExceeded() ); } + } diff --git a/protocol-units/settlement/mcr/contracts/lib/v2-periphery b/protocol-units/settlement/mcr/contracts/lib/v2-periphery new file mode 160000 index 000000000..0335e8f7e --- /dev/null +++ b/protocol-units/settlement/mcr/contracts/lib/v2-periphery @@ -0,0 +1 @@ +Subproject commit 0335e8f7e1bd1e8d8329fd300aea2ef2f36dd19f From 5fb55c9340969d7813fec2f62757b8e8276e6e45 Mon Sep 17 00:00:00 2001 From: primata Date: Thu, 9 Jan 2025 19:52:53 -0300 Subject: [PATCH 3/5] add tests --- protocol-units/bridge/contracts/src/NativeBridge.sol | 1 + protocol-units/bridge/contracts/test/NativeBridge.t.sol | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/protocol-units/bridge/contracts/src/NativeBridge.sol b/protocol-units/bridge/contracts/src/NativeBridge.sol index 69b6f6d5a..01fe5452d 100644 --- a/protocol-units/bridge/contracts/src/NativeBridge.sol +++ b/protocol-units/bridge/contracts/src/NativeBridge.sol @@ -19,6 +19,7 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB mapping(uint256 nonce => OutboundTransfer) public noncesToOutboundTransfers; 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")); bytes32 public constant PAUSER_ROLE = keccak256(abi.encodePacked("PAUSER_ROLE")); diff --git a/protocol-units/bridge/contracts/test/NativeBridge.t.sol b/protocol-units/bridge/contracts/test/NativeBridge.t.sol index 2604c64b7..d7043a899 100644 --- a/protocol-units/bridge/contracts/test/NativeBridge.t.sol +++ b/protocol-units/bridge/contracts/test/NativeBridge.t.sol @@ -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 From 071bf71bda113afb3a7a6676064565737320714c Mon Sep 17 00:00:00 2001 From: primata Date: Fri, 10 Jan 2025 13:45:05 -0300 Subject: [PATCH 4/5] rename risk denominator to insurance budget divider --- .../bridge/contracts/src/NativeBridge.sol | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/protocol-units/bridge/contracts/src/NativeBridge.sol b/protocol-units/bridge/contracts/src/NativeBridge.sol index 01fe5452d..9464cad88 100644 --- a/protocol-units/bridge/contracts/src/NativeBridge.sol +++ b/protocol-units/bridge/contracts/src/NativeBridge.sol @@ -16,6 +16,8 @@ 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; mapping(bytes32 bridgeTransferId => uint256 nonce) public idsToInboundNonces; mapping(uint256 day => uint256 amount) public inboundRateLimitBudget; @@ -24,11 +26,12 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB bytes32 public constant RELAYER_ROLE = keccak256(abi.encodePacked("RELAYER_ROLE")); bytes32 public constant PAUSER_ROLE = keccak256(abi.encodePacked("PAUSER_ROLE")); - // Risk denominator must be above 3 - uint256 public constant RISK_DENOMINATOR_LOWER_BOUND = 3; + // 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 @@ -59,7 +62,7 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB // Set insurance fund insuranceFund = _insuranceFund; - riskDenominator = RISK_DENOMINATOR_LOWER_BOUND + 1; + insuranceBudgetDivider = INSURANCE_BUDGET_DIVIDER_LOWER_BOUND + 1; // Maintainer is optional if (_maintainer != address(0)) { @@ -182,14 +185,14 @@ 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 > RISK_DENOMINATOR_LOWER_BOUND, 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); } /** @@ -201,27 +204,27 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB } /** - * @dev Rate limits the outboud 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) / riskDenominator, + outboundRateLimitBudget[day] < moveToken.balanceOf(insuranceFund) / insuranceBudgetDivider, OutboundRateLimitExceeded() ); } /** - * @dev Rate limits the inbound transfers based on the insurance fund and risk denominator + * @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() ); } From 70edbe39d4db0de1f448eaad90445da0658bfcd8 Mon Sep 17 00:00:00 2001 From: primata Date: Fri, 10 Jan 2025 13:48:25 -0300 Subject: [PATCH 5/5] INativeBridge insuranceBudgetDivider --- protocol-units/bridge/contracts/src/INativeBridge.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol-units/bridge/contracts/src/INativeBridge.sol b/protocol-units/bridge/contracts/src/INativeBridge.sol index 3e21a9d9c..cf079c352 100644 --- a/protocol-units/bridge/contracts/src/INativeBridge.sol +++ b/protocol-units/bridge/contracts/src/INativeBridge.sol @@ -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(); @@ -32,7 +32,7 @@ interface INativeBridge { error InvalidNonce(); error OutboundRateLimitExceeded(); error InboundRateLimitExceeded(); - error InvalidRiskDenominator(); + error InvalidInsuranceBudgetDivider(); /** * @dev Creates a new bridge