From 81e7c3dde721e4fecc8926fa67e3c0fae63d6e21 Mon Sep 17 00:00:00 2001 From: nxqbao Date: Wed, 6 Mar 2024 15:19:29 +0700 Subject: [PATCH 1/7] fix(MainchainGateway): handle deposit out-of-gas with WETH --- src/extensions/WETHVault.sol | 44 +++++++++++++++++++ src/interfaces/IWETH.sol | 4 ++ src/mainchain/MainchainGatewayV3.sol | 66 ++++++++++++++-------------- 3 files changed, 82 insertions(+), 32 deletions(-) create mode 100644 src/extensions/WETHVault.sol diff --git a/src/extensions/WETHVault.sol b/src/extensions/WETHVault.sol new file mode 100644 index 00000000..8ce38ec0 --- /dev/null +++ b/src/extensions/WETHVault.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.9; + +import "@openzeppelin/contracts/access/Ownable.sol"; + +import "../interfaces/IWETH.sol"; + +contract WETHVault is Ownable { + IWETH public weth; + + error ErrInsufficientBalance(); + error ErrExternalCallFailed(address sender, bytes4 sig); + + constructor(address weth_, address owner_) { + weth = IWETH(weth_); + + if (owner_ != address(0)) { + _transferOwnership(owner_); + } + } + + fallback() external payable { } + receive() external payable { } + + function transferToVault(uint val) external { + weth.withdraw(val); + } + + function withdrawToOwner(uint val) external onlyOwner { + if (val > address(this).balance) { + revert ErrInsufficientBalance(); + } + + (bool success,) = payable(msg.sender).call{ value: val }(""); + if (!success) { + revert ErrExternalCallFailed(msg.sender, msg.sig); + } + } + + function setWeth(address weth_) external onlyOwner { + weth = IWETH(weth_); + } +} diff --git a/src/interfaces/IWETH.sol b/src/interfaces/IWETH.sol index 44871433..5c00275f 100644 --- a/src/interfaces/IWETH.sol +++ b/src/interfaces/IWETH.sol @@ -2,8 +2,12 @@ pragma solidity ^0.8.0; interface IWETH { + event Transfer(address indexed src, address indexed dst, uint wad); + function deposit() external payable; + function transfer(address dst, uint wad) external returns (bool); + function withdraw(uint256 _wad) external; function balanceOf(address) external view returns (uint256); diff --git a/src/mainchain/MainchainGatewayV3.sol b/src/mainchain/MainchainGatewayV3.sol index dd1fab10..c5e44709 100644 --- a/src/mainchain/MainchainGatewayV3.sol +++ b/src/mainchain/MainchainGatewayV3.sol @@ -6,18 +6,12 @@ import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { IBridgeManager } from "../interfaces/bridge/IBridgeManager.sol"; import { IBridgeManagerCallback } from "../interfaces/bridge/IBridgeManagerCallback.sol"; import { HasContracts, ContractType } from "../extensions/collections/HasContracts.sol"; +import "../extensions/WETHVault.sol"; import "../extensions/WithdrawalLimitation.sol"; import "../libraries/Transfer.sol"; import "../interfaces/IMainchainGatewayV3.sol"; -contract MainchainGatewayV3 is - WithdrawalLimitation, - Initializable, - AccessControlEnumerable, - IMainchainGatewayV3, - HasContracts, - IBridgeManagerCallback -{ +contract MainchainGatewayV3 is WithdrawalLimitation, Initializable, AccessControlEnumerable, IMainchainGatewayV3, HasContracts, IBridgeManagerCallback { using Token for Token.Info; using Transfer for Transfer.Request; using Transfer for Transfer.Receipt; @@ -47,13 +41,14 @@ contract MainchainGatewayV3 is uint96 private _totalOperatorWeight; mapping(address operator => uint96 weight) private _operatorWeight; + WETHVault public wethVault; fallback() external payable { - _fallback(); + // _fallback(); } receive() external payable { - _fallback(); + // _fallback(); } /** @@ -115,10 +110,14 @@ contract MainchainGatewayV3 is _totalOperatorWeight = totalWeight; } + function initializeV4(address payable wethVault_) external reinitializer(4) { + wethVault = WETHVault(wethVault_); + } + /** * @dev Receives ether without doing anything. Use this function to topup native token. */ - function receiveEther() external payable {} + function receiveEther() external payable { } /** * @inheritdoc IMainchainGatewayV3 @@ -144,10 +143,7 @@ contract MainchainGatewayV3 is /** * @inheritdoc IMainchainGatewayV3 */ - function submitWithdrawal( - Transfer.Receipt calldata _receipt, - Signature[] calldata _signatures - ) external virtual whenNotPaused returns (bool _locked) { + function submitWithdrawal(Transfer.Receipt calldata _receipt, Signature[] calldata _signatures) external virtual whenNotPaused returns (bool _locked) { return _submitWithdrawal(_receipt, _signatures); } @@ -184,11 +180,7 @@ contract MainchainGatewayV3 is /** * @inheritdoc IMainchainGatewayV3 */ - function mapTokens( - address[] calldata _mainchainTokens, - address[] calldata _roninTokens, - Token.Standard[] calldata _standards - ) external virtual onlyAdmin { + function mapTokens(address[] calldata _mainchainTokens, address[] calldata _roninTokens, Token.Standard[] calldata _standards) external virtual onlyAdmin { if (_mainchainTokens.length == 0) revert ErrEmptyArray(); _mapTokens(_mainchainTokens, _roninTokens, _standards); } @@ -349,9 +341,13 @@ contract MainchainGatewayV3 is if (_token.erc != _request.info.erc) revert ErrInvalidTokenStandard(); _request.info.transferFrom(_requester, address(this), _request.tokenAddr); + // Withdraw if token is WETH + // The withdraw of WETH must go via `WethVault`, because `WETH.withdraw` only sends 2300 gas, which is insufficient when recipient is a proxy. if (_roninWeth == _request.tokenAddr) { - IWETH(_roninWeth).withdraw(_request.info.quantity); + IWETH(_roninWeth).transfer(address(wethVault), _request.info.quantity); + wethVault.transferToVault(_request.info.quantity); + wethVault.withdrawToOwner(_request.info.quantity); } } @@ -416,15 +412,24 @@ contract MainchainGatewayV3 is } /** - * @dev Receives ETH from WETH or creates deposit request. + * @dev Receives ETH from WETH or creates deposit request if sender is not WETH. */ - function _fallback() internal virtual whenNotPaused { - if (msg.sender != address(wrappedNativeToken)) { - Transfer.Request memory _request; - _request.recipientAddr = msg.sender; - _request.info.quantity = msg.value; - _requestDepositFor(_request, _request.recipientAddr); + function _fallback() internal virtual { + if (msg.sender == address(wrappedNativeToken)) { + return; } + + _createDepositOnFallback(); + } + + /** + * @dev Creates deposit request. + */ + function _createDepositOnFallback() internal virtual whenNotPaused { + Transfer.Request memory _request; + _request.recipientAddr = msg.sender; + _request.info.quantity = msg.value; + _requestDepositFor(_request, _request.recipientAddr); } /** @@ -484,10 +489,7 @@ contract MainchainGatewayV3 is /** * @inheritdoc IBridgeManagerCallback */ - function onBridgeOperatorsRemoved( - address[] calldata operators, - bool[] calldata removeds - ) external onlyContract(ContractType.BRIDGE_MANAGER) returns (bytes4) { + function onBridgeOperatorsRemoved(address[] calldata operators, bool[] calldata removeds) external onlyContract(ContractType.BRIDGE_MANAGER) returns (bytes4) { uint length = operators.length; if (length != removeds.length) revert ErrLengthMismatch(msg.sig); if (length == 0) { From 1c1435a04f4c62cdad7666b68413c9fe5fb8fde3 Mon Sep 17 00:00:00 2001 From: nxqbao Date: Wed, 6 Mar 2024 15:19:53 +0700 Subject: [PATCH 2/7] test: config for wethVault and fix test --- script/GeneralConfig.sol | 2 ++ .../contracts/MainchainWethVaultDeploy.s.sol | 23 +++++++++++++++++ script/interfaces/ISharedArgument.sol | 6 +++++ script/utils/Contract.sol | 4 ++- test/bridge/integration/BaseIntegration.t.sol | 16 ++++++++++-- .../submitWithdrawal.MainchainGatewayV3.t.sol | 25 ++++++++++++++----- ...al.MainchainGatewayV3.weth.benchmark.t.sol | 21 +++------------- test/mocks/MockDiscardEther.sol | 16 ++++++++++++ 8 files changed, 87 insertions(+), 26 deletions(-) create mode 100644 script/contracts/MainchainWethVaultDeploy.s.sol create mode 100644 test/mocks/MockDiscardEther.sol diff --git a/script/GeneralConfig.sol b/script/GeneralConfig.sol index 2651f1f0..da6b26bb 100644 --- a/script/GeneralConfig.sol +++ b/script/GeneralConfig.sol @@ -57,6 +57,8 @@ contract GeneralConfig is BaseGeneralConfig, Utils { _contractNameMap[Contract.RoninPauseEnforcer.key()] = "PauseEnforcer"; _contractNameMap[Contract.MainchainPauseEnforcer.key()] = "PauseEnforcer"; + + _contractNameMap[Contract.MainchainWETHVault.key()] = "WETHVault"; } function _mapContractName(Contract contractEnum) internal { diff --git a/script/contracts/MainchainWethVaultDeploy.s.sol b/script/contracts/MainchainWethVaultDeploy.s.sol new file mode 100644 index 00000000..bdbaecfa --- /dev/null +++ b/script/contracts/MainchainWethVaultDeploy.s.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { WETHVault } from "@ronin/contracts/extensions/WETHVault.sol"; +import { Contract } from "../utils/Contract.sol"; +import { ISharedArgument } from "../interfaces/ISharedArgument.sol"; +import { Migration } from "../Migration.s.sol"; + + +contract MainchainWethVaultDeploy is Migration { + function _defaultArguments() internal virtual override returns (bytes memory args) { + ISharedArgument.WETHVaultParam memory param = config.sharedArguments().mainchainWethVault; + + args = abi.encode( + param.weth, + param.owner + ); + } + + function run() public virtual returns (WETHVault) { + return WETHVault(_deployImmutable(Contract.MainchainWETHVault.key())); + } +} diff --git a/script/interfaces/ISharedArgument.sol b/script/interfaces/ISharedArgument.sol index 37971d4d..6c5b0719 100644 --- a/script/interfaces/ISharedArgument.sol +++ b/script/interfaces/ISharedArgument.sol @@ -106,11 +106,17 @@ interface ISharedArgument is IGeneralConfig { uint256[] governorPKs; } + struct WETHVaultParam { + address weth; + address owner; + } + struct SharedParameter { // mainchain BridgeManagerParam mainchainBridgeManager; MainchainGatewayV3Param mainchainGatewayV3; PauseEnforcerParam mainchainPauseEnforcer; + WETHVaultParam mainchainWethVault; // ronin BridgeManagerParam roninBridgeManager; RoninGatewayV3Param roninGatewayV3; diff --git a/script/utils/Contract.sol b/script/utils/Contract.sol index e94823f6..2cfecfe7 100644 --- a/script/utils/Contract.sol +++ b/script/utils/Contract.sol @@ -18,7 +18,8 @@ enum Contract { RoninBridgeManager, MainchainPauseEnforcer, MainchainGatewayV3, - MainchainBridgeManager + MainchainBridgeManager, + MainchainWETHVault } using { key, name } for Contract global; @@ -45,6 +46,7 @@ function name(Contract contractEnum) pure returns (string memory) { if (contractEnum == Contract.MainchainPauseEnforcer) return "PauseEnforcer"; if (contractEnum == Contract.MainchainGatewayV3) return "MainchainGatewayV3"; if (contractEnum == Contract.MainchainBridgeManager) return "MainchainBridgeManager"; + if (contractEnum == Contract.MainchainWETHVault) return "WETHVault"; revert("Contract: Unknown contract"); } diff --git a/test/bridge/integration/BaseIntegration.t.sol b/test/bridge/integration/BaseIntegration.t.sol index 0be51cd2..105fe476 100644 --- a/test/bridge/integration/BaseIntegration.t.sol +++ b/test/bridge/integration/BaseIntegration.t.sol @@ -16,6 +16,7 @@ import { BridgeSlash } from "@ronin/contracts/ronin/gateway/BridgeSlash.sol"; import { BridgeReward } from "@ronin/contracts/ronin/gateway/BridgeReward.sol"; import { MainchainGatewayV3 } from "@ronin/contracts/mainchain/MainchainGatewayV3.sol"; import { MainchainBridgeManager } from "@ronin/contracts/mainchain/MainchainBridgeManager.sol"; +import { WETHVault } from "@ronin/contracts/extensions/WETHVault.sol"; import { MockERC20 } from "@ronin/contracts/mocks/token/MockERC20.sol"; import { MockERC721 } from "@ronin/contracts/mocks/token/MockERC721.sol"; @@ -48,6 +49,7 @@ import { RoninPauseEnforcerDeploy } from "@ronin/script/contracts/RoninPauseEnfo import { MainchainGatewayV3Deploy } from "@ronin/script/contracts/MainchainGatewayV3Deploy.s.sol"; import { MainchainBridgeManagerDeploy } from "@ronin/script/contracts/MainchainBridgeManagerDeploy.s.sol"; import { MainchainPauseEnforcerDeploy } from "@ronin/script/contracts/MainchainPauseEnforcerDeploy.s.sol"; +import { MainchainWethVaultDeploy } from "@ronin/script/contracts/MainchainWethVaultDeploy.s.sol"; import { WETHDeploy } from "@ronin/script/contracts/token/WETHDeploy.s.sol"; import { WRONDeploy } from "@ronin/script/contracts/token/WRONDeploy.s.sol"; import { AXSDeploy } from "@ronin/script/contracts/token/AXSDeploy.s.sol"; @@ -74,6 +76,7 @@ contract BaseIntegration_Test is Base_Test { PauseEnforcer _mainchainPauseEnforcer; MainchainGatewayV3 _mainchainGatewayV3; MainchainBridgeManager _mainchainBridgeManager; + WETHVault _mainchainWethVault; MockWrappedToken _roninWeth; MockWrappedToken _roninWron; @@ -135,6 +138,7 @@ contract BaseIntegration_Test is Base_Test { _mainchainPauseEnforcer = new MainchainPauseEnforcerDeploy().run(); _mainchainGatewayV3 = new MainchainGatewayV3Deploy().run(); _mainchainBridgeManager = new MainchainBridgeManagerDeploy().run(); + _mainchainWethVault = new MainchainWethVaultDeploy().run(); _mainchainWeth = new WETHDeploy().run(); _mainchainAxs = new AXSDeploy().run(); @@ -163,6 +167,7 @@ contract BaseIntegration_Test is Base_Test { _mainchainPauseEnforcerInitialize(); _constructForMainchainBridgeManager(); _mainchainGatewayV3Initialize(); + _constructForMainchainWethVault(); } function _getMainchainAndRoninTokens() internal view returns (address[] memory mainchainTokens, address[] memory roninTokens) { @@ -428,6 +433,13 @@ contract BaseIntegration_Test is Base_Test { } } + function _constructForMainchainWethVault() internal { + vm.startPrank(_config.getSender()); + _mainchainWethVault.setWeth(address(_mainchainWeth)); + _mainchainWethVault.transferOwnership(address(_mainchainGatewayV3)); + vm.stopPrank(); + } + function _mainchainGatewayV3Initialize() internal { (address[] memory mainchainTokens, address[] memory roninTokens) = _getMainchainAndRoninTokens(); uint256 tokenNum = mainchainTokens.length; @@ -486,13 +498,13 @@ contract BaseIntegration_Test is Base_Test { _mainchainGatewayV3.initializeV2(address(_mainchainBridgeManager)); _mainchainGatewayV3.initializeV3(_param.mainchainBridgeManager.bridgeOperators, _param.mainchainBridgeManager.voteWeights); + _mainchainGatewayV3.initializeV4(payable(address(_mainchainWethVault))); } function _mainchainPauseEnforcerInitialize() internal { _param.mainchainPauseEnforcer.target = address(_mainchainGatewayV3); ISharedArgument.PauseEnforcerParam memory param = _param.mainchainPauseEnforcer; - _mainchainPauseEnforcer.initialize(IPauseTarget(param.target), param.admin, param.sentries); } @@ -576,7 +588,7 @@ contract BaseIntegration_Test is Base_Test { LibTransfer.Receipt memory receipt, uint256[] memory signerPKs, bytes32 domainSeparator - ) internal pure returns (SignatureConsumer.Signature[] memory sigs) { + ) internal pure returns (SignatureConsumer.Signature[] memory sigs) { sigs = new SignatureConsumer.Signature[](signerPKs.length); for (uint256 i; i < signerPKs.length; i++) { diff --git a/test/bridge/integration/mainchain-gateway/submit-withdrawal/submitWithdrawal.MainchainGatewayV3.t.sol b/test/bridge/integration/mainchain-gateway/submit-withdrawal/submitWithdrawal.MainchainGatewayV3.t.sol index 49b71529..4c84f393 100644 --- a/test/bridge/integration/mainchain-gateway/submit-withdrawal/submitWithdrawal.MainchainGatewayV3.t.sol +++ b/test/bridge/integration/mainchain-gateway/submit-withdrawal/submitWithdrawal.MainchainGatewayV3.t.sol @@ -6,9 +6,10 @@ import { ContractType } from "@ronin/contracts/utils/ContractType.sol"; import { Transfer } from "@ronin/contracts/libraries/Transfer.sol"; import { Token } from "@ronin/contracts/libraries/Token.sol"; import { SignatureConsumer } from "@ronin/contracts/interfaces/consumers/SignatureConsumer.sol"; +import { MockDiscardEther } from "@ronin/test/mocks/MockDiscardEther.sol"; import "../../BaseIntegration.t.sol"; -contract SubmitWithdrawal_MainchainGatewayV3_Test is BaseIntegration_Test{ +contract SubmitWithdrawal_MainchainGatewayV3_Test is BaseIntegration_Test { using Transfer for Transfer.Receipt; Transfer.Receipt _withdrawalReceipt; @@ -30,10 +31,6 @@ contract SubmitWithdrawal_MainchainGatewayV3_Test is BaseIntegration_Test{ _withdrawalReceipt.info.erc = Token.Standard.ERC20; _withdrawalReceipt.info.id = 0; _withdrawalReceipt.info.quantity = 0; - - vm.deal(address(_mainchainGatewayV3), 10 ether); - vm.prank(address(_mainchainGatewayV3)); - _mainchainWeth.deposit{ value: 10 ether }(); } function test_submitWithdrawal_Native() public { @@ -41,6 +38,22 @@ contract SubmitWithdrawal_MainchainGatewayV3_Test is BaseIntegration_Test{ SignatureConsumer.Signature[] memory signatures = _generateSignaturesFor(_withdrawalReceipt, _param.test.operatorPKs, _domainSeparator); + vm.deal(address(_mainchainGatewayV3), 10 ether); + _mainchainGatewayV3.submitWithdrawal(_withdrawalReceipt, signatures); + } + + function test_submitWithdrawal_WETH() public { + MockDiscardEther notReceiveEtherRecipient = new MockDiscardEther(); + + _withdrawalReceipt.mainchain.addr = address(notReceiveEtherRecipient); + + vm.deal(address(_mainchainGatewayV3), 10 ether); + + _withdrawalReceipt.info.quantity = 10; + SignatureConsumer.Signature[] memory signatures = _generateSignaturesFor(_withdrawalReceipt, _param.test.operatorPKs, _domainSeparator); + + vm.expectEmit(address(_mainchainWeth)); + emit IWETH.Transfer(address(_mainchainGatewayV3), address(notReceiveEtherRecipient), _withdrawalReceipt.info.quantity); _mainchainGatewayV3.submitWithdrawal(_withdrawalReceipt, signatures); } @@ -65,4 +78,4 @@ contract SubmitWithdrawal_MainchainGatewayV3_Test is BaseIntegration_Test{ _mainchainGatewayV3.submitWithdrawal(_withdrawalReceipt, signatures); } - } +} diff --git a/test/bridge/integration/mainchain-gateway/submit-withdrawal/submitWithdrawal.MainchainGatewayV3.weth.benchmark.t.sol b/test/bridge/integration/mainchain-gateway/submit-withdrawal/submitWithdrawal.MainchainGatewayV3.weth.benchmark.t.sol index cf190f85..fc5cd777 100644 --- a/test/bridge/integration/mainchain-gateway/submit-withdrawal/submitWithdrawal.MainchainGatewayV3.weth.benchmark.t.sol +++ b/test/bridge/integration/mainchain-gateway/submit-withdrawal/submitWithdrawal.MainchainGatewayV3.weth.benchmark.t.sol @@ -6,6 +6,7 @@ import { ContractType } from "@ronin/contracts/utils/ContractType.sol"; import { Transfer } from "@ronin/contracts/libraries/Transfer.sol"; import { Token } from "@ronin/contracts/libraries/Token.sol"; import { SignatureConsumer } from "@ronin/contracts/interfaces/consumers/SignatureConsumer.sol"; +import { MockDiscardEther } from "@ronin/test/mocks/MockDiscardEther.sol"; import "../../BaseIntegration.t.sol"; contract SubmitWithdrawal_MainchainGatewayV3_Weth_Benchmark_Test is BaseIntegration_Test { @@ -19,7 +20,7 @@ contract SubmitWithdrawal_MainchainGatewayV3_Weth_Benchmark_Test is BaseIntegrat function setUp() public virtual override { super.setUp(); - DiscardEther notReceiveEtherRecipient = new DiscardEther(); + MockDiscardEther notReceiveEtherRecipient = new MockDiscardEther(); _domainSeparator = _mainchainGatewayV3.DOMAIN_SEPARATOR(); @@ -27,10 +28,10 @@ contract SubmitWithdrawal_MainchainGatewayV3_Weth_Benchmark_Test is BaseIntegrat _withdrawalReceipt.kind = Transfer.Kind.Withdrawal; _withdrawalReceipt.ronin.addr = makeAddr("requester"); _withdrawalReceipt.ronin.tokenAddr = address(_roninWeth); - _withdrawalReceipt.ronin.chainId =block.chainid; + _withdrawalReceipt.ronin.chainId = block.chainid; _withdrawalReceipt.mainchain.addr = address(notReceiveEtherRecipient); _withdrawalReceipt.mainchain.tokenAddr = address(_mainchainWeth); - _withdrawalReceipt.mainchain.chainId =block.chainid; + _withdrawalReceipt.mainchain.chainId = block.chainid; _withdrawalReceipt.info.erc = Token.Standard.ERC20; _withdrawalReceipt.info.id = 0; _withdrawalReceipt.info.quantity = 0; @@ -50,17 +51,3 @@ contract SubmitWithdrawal_MainchainGatewayV3_Weth_Benchmark_Test is BaseIntegrat _mainchainGatewayV3.submitWithdrawal(_withdrawalReceipt, _signatures); } } - -contract DiscardEther { - fallback() external payable { - _fallback(); - } - - receive() external payable { - _fallback(); - } - - function _fallback() internal { - revert("Not receive ether"); - } -} \ No newline at end of file diff --git a/test/mocks/MockDiscardEther.sol b/test/mocks/MockDiscardEther.sol new file mode 100644 index 00000000..6e7c11f6 --- /dev/null +++ b/test/mocks/MockDiscardEther.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +contract MockDiscardEther { + fallback() external payable { + _fallback(); + } + + receive() external payable { + _fallback(); + } + + function _fallback() internal view { + revert("Not receive ether"); + } +} From 0f1dc4790e761f6121f6c121051f295da58ab5e2 Mon Sep 17 00:00:00 2001 From: nxqbao Date: Wed, 6 Mar 2024 15:22:57 +0700 Subject: [PATCH 3/7] nit: rename WETHVault to WethMediator --- script/GeneralConfig.sol | 2 +- ...s.sol => MainchainWethMediatorDeploy.s.sol} | 10 +++++----- script/interfaces/ISharedArgument.sol | 4 ++-- script/utils/Contract.sol | 4 ++-- .../{WETHVault.sol => WethMediator.sol} | 2 +- src/mainchain/MainchainGatewayV3.sol | 16 ++++++++-------- test/bridge/integration/BaseIntegration.t.sol | 18 +++++++++--------- 7 files changed, 28 insertions(+), 28 deletions(-) rename script/contracts/{MainchainWethVaultDeploy.s.sol => MainchainWethMediatorDeploy.s.sol} (51%) rename src/extensions/{WETHVault.sol => WethMediator.sol} (96%) diff --git a/script/GeneralConfig.sol b/script/GeneralConfig.sol index da6b26bb..8b6a4f16 100644 --- a/script/GeneralConfig.sol +++ b/script/GeneralConfig.sol @@ -58,7 +58,7 @@ contract GeneralConfig is BaseGeneralConfig, Utils { _contractNameMap[Contract.RoninPauseEnforcer.key()] = "PauseEnforcer"; _contractNameMap[Contract.MainchainPauseEnforcer.key()] = "PauseEnforcer"; - _contractNameMap[Contract.MainchainWETHVault.key()] = "WETHVault"; + _contractNameMap[Contract.MainchainWethMediator.key()] = "WethMediator"; } function _mapContractName(Contract contractEnum) internal { diff --git a/script/contracts/MainchainWethVaultDeploy.s.sol b/script/contracts/MainchainWethMediatorDeploy.s.sol similarity index 51% rename from script/contracts/MainchainWethVaultDeploy.s.sol rename to script/contracts/MainchainWethMediatorDeploy.s.sol index bdbaecfa..65d15b9b 100644 --- a/script/contracts/MainchainWethVaultDeploy.s.sol +++ b/script/contracts/MainchainWethMediatorDeploy.s.sol @@ -1,15 +1,15 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import { WETHVault } from "@ronin/contracts/extensions/WETHVault.sol"; +import { WethMediator } from "@ronin/contracts/extensions/WethMediator.sol"; import { Contract } from "../utils/Contract.sol"; import { ISharedArgument } from "../interfaces/ISharedArgument.sol"; import { Migration } from "../Migration.s.sol"; -contract MainchainWethVaultDeploy is Migration { +contract MainchainWethMediatorDeploy is Migration { function _defaultArguments() internal virtual override returns (bytes memory args) { - ISharedArgument.WETHVaultParam memory param = config.sharedArguments().mainchainWethVault; + ISharedArgument.WethMediatorParam memory param = config.sharedArguments().mainchainWethMediator; args = abi.encode( param.weth, @@ -17,7 +17,7 @@ contract MainchainWethVaultDeploy is Migration { ); } - function run() public virtual returns (WETHVault) { - return WETHVault(_deployImmutable(Contract.MainchainWETHVault.key())); + function run() public virtual returns (WethMediator) { + return WethMediator(_deployImmutable(Contract.MainchainWethMediator.key())); } } diff --git a/script/interfaces/ISharedArgument.sol b/script/interfaces/ISharedArgument.sol index 6c5b0719..dc2e8090 100644 --- a/script/interfaces/ISharedArgument.sol +++ b/script/interfaces/ISharedArgument.sol @@ -106,7 +106,7 @@ interface ISharedArgument is IGeneralConfig { uint256[] governorPKs; } - struct WETHVaultParam { + struct WethMediatorParam { address weth; address owner; } @@ -116,7 +116,7 @@ interface ISharedArgument is IGeneralConfig { BridgeManagerParam mainchainBridgeManager; MainchainGatewayV3Param mainchainGatewayV3; PauseEnforcerParam mainchainPauseEnforcer; - WETHVaultParam mainchainWethVault; + WethMediatorParam mainchainWethMediator; // ronin BridgeManagerParam roninBridgeManager; RoninGatewayV3Param roninGatewayV3; diff --git a/script/utils/Contract.sol b/script/utils/Contract.sol index 2cfecfe7..a1c8e9c4 100644 --- a/script/utils/Contract.sol +++ b/script/utils/Contract.sol @@ -19,7 +19,7 @@ enum Contract { MainchainPauseEnforcer, MainchainGatewayV3, MainchainBridgeManager, - MainchainWETHVault + MainchainWethMediator } using { key, name } for Contract global; @@ -46,7 +46,7 @@ function name(Contract contractEnum) pure returns (string memory) { if (contractEnum == Contract.MainchainPauseEnforcer) return "PauseEnforcer"; if (contractEnum == Contract.MainchainGatewayV3) return "MainchainGatewayV3"; if (contractEnum == Contract.MainchainBridgeManager) return "MainchainBridgeManager"; - if (contractEnum == Contract.MainchainWETHVault) return "WETHVault"; + if (contractEnum == Contract.MainchainWethMediator) return "WethMediator"; revert("Contract: Unknown contract"); } diff --git a/src/extensions/WETHVault.sol b/src/extensions/WethMediator.sol similarity index 96% rename from src/extensions/WETHVault.sol rename to src/extensions/WethMediator.sol index 8ce38ec0..6cc6ee00 100644 --- a/src/extensions/WETHVault.sol +++ b/src/extensions/WethMediator.sol @@ -6,7 +6,7 @@ import "@openzeppelin/contracts/access/Ownable.sol"; import "../interfaces/IWETH.sol"; -contract WETHVault is Ownable { +contract WethMediator is Ownable { IWETH public weth; error ErrInsufficientBalance(); diff --git a/src/mainchain/MainchainGatewayV3.sol b/src/mainchain/MainchainGatewayV3.sol index c5e44709..f51333f7 100644 --- a/src/mainchain/MainchainGatewayV3.sol +++ b/src/mainchain/MainchainGatewayV3.sol @@ -6,7 +6,7 @@ import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { IBridgeManager } from "../interfaces/bridge/IBridgeManager.sol"; import { IBridgeManagerCallback } from "../interfaces/bridge/IBridgeManagerCallback.sol"; import { HasContracts, ContractType } from "../extensions/collections/HasContracts.sol"; -import "../extensions/WETHVault.sol"; +import "../extensions/WethMediator.sol"; import "../extensions/WithdrawalLimitation.sol"; import "../libraries/Transfer.sol"; import "../interfaces/IMainchainGatewayV3.sol"; @@ -41,7 +41,7 @@ contract MainchainGatewayV3 is WithdrawalLimitation, Initializable, AccessContro uint96 private _totalOperatorWeight; mapping(address operator => uint96 weight) private _operatorWeight; - WETHVault public wethVault; + WethMediator public wethMediator; fallback() external payable { // _fallback(); @@ -110,8 +110,8 @@ contract MainchainGatewayV3 is WithdrawalLimitation, Initializable, AccessContro _totalOperatorWeight = totalWeight; } - function initializeV4(address payable wethVault_) external reinitializer(4) { - wethVault = WETHVault(wethVault_); + function initializeV4(address payable wethMediator_) external reinitializer(4) { + wethMediator = WethMediator(wethMediator_); } /** @@ -343,11 +343,11 @@ contract MainchainGatewayV3 is WithdrawalLimitation, Initializable, AccessContro _request.info.transferFrom(_requester, address(this), _request.tokenAddr); // Withdraw if token is WETH - // The withdraw of WETH must go via `WethVault`, because `WETH.withdraw` only sends 2300 gas, which is insufficient when recipient is a proxy. + // The withdraw of WETH must go via `WethMediator`, because `WETH.withdraw` only sends 2300 gas, which is insufficient when recipient is a proxy. if (_roninWeth == _request.tokenAddr) { - IWETH(_roninWeth).transfer(address(wethVault), _request.info.quantity); - wethVault.transferToVault(_request.info.quantity); - wethVault.withdrawToOwner(_request.info.quantity); + IWETH(_roninWeth).transfer(address(wethMediator), _request.info.quantity); + wethMediator.transferToVault(_request.info.quantity); + wethMediator.withdrawToOwner(_request.info.quantity); } } diff --git a/test/bridge/integration/BaseIntegration.t.sol b/test/bridge/integration/BaseIntegration.t.sol index 105fe476..e87e2293 100644 --- a/test/bridge/integration/BaseIntegration.t.sol +++ b/test/bridge/integration/BaseIntegration.t.sol @@ -16,7 +16,7 @@ import { BridgeSlash } from "@ronin/contracts/ronin/gateway/BridgeSlash.sol"; import { BridgeReward } from "@ronin/contracts/ronin/gateway/BridgeReward.sol"; import { MainchainGatewayV3 } from "@ronin/contracts/mainchain/MainchainGatewayV3.sol"; import { MainchainBridgeManager } from "@ronin/contracts/mainchain/MainchainBridgeManager.sol"; -import { WETHVault } from "@ronin/contracts/extensions/WETHVault.sol"; +import { WethMediator } from "@ronin/contracts/extensions/WethMediator.sol"; import { MockERC20 } from "@ronin/contracts/mocks/token/MockERC20.sol"; import { MockERC721 } from "@ronin/contracts/mocks/token/MockERC721.sol"; @@ -49,7 +49,7 @@ import { RoninPauseEnforcerDeploy } from "@ronin/script/contracts/RoninPauseEnfo import { MainchainGatewayV3Deploy } from "@ronin/script/contracts/MainchainGatewayV3Deploy.s.sol"; import { MainchainBridgeManagerDeploy } from "@ronin/script/contracts/MainchainBridgeManagerDeploy.s.sol"; import { MainchainPauseEnforcerDeploy } from "@ronin/script/contracts/MainchainPauseEnforcerDeploy.s.sol"; -import { MainchainWethVaultDeploy } from "@ronin/script/contracts/MainchainWethVaultDeploy.s.sol"; +import { MainchainWethMediatorDeploy } from "@ronin/script/contracts/MainchainWethMediatorDeploy.s.sol"; import { WETHDeploy } from "@ronin/script/contracts/token/WETHDeploy.s.sol"; import { WRONDeploy } from "@ronin/script/contracts/token/WRONDeploy.s.sol"; import { AXSDeploy } from "@ronin/script/contracts/token/AXSDeploy.s.sol"; @@ -76,7 +76,7 @@ contract BaseIntegration_Test is Base_Test { PauseEnforcer _mainchainPauseEnforcer; MainchainGatewayV3 _mainchainGatewayV3; MainchainBridgeManager _mainchainBridgeManager; - WETHVault _mainchainWethVault; + WethMediator _mainchainWethMediator; MockWrappedToken _roninWeth; MockWrappedToken _roninWron; @@ -138,7 +138,7 @@ contract BaseIntegration_Test is Base_Test { _mainchainPauseEnforcer = new MainchainPauseEnforcerDeploy().run(); _mainchainGatewayV3 = new MainchainGatewayV3Deploy().run(); _mainchainBridgeManager = new MainchainBridgeManagerDeploy().run(); - _mainchainWethVault = new MainchainWethVaultDeploy().run(); + _mainchainWethMediator = new MainchainWethMediatorDeploy().run(); _mainchainWeth = new WETHDeploy().run(); _mainchainAxs = new AXSDeploy().run(); @@ -167,7 +167,7 @@ contract BaseIntegration_Test is Base_Test { _mainchainPauseEnforcerInitialize(); _constructForMainchainBridgeManager(); _mainchainGatewayV3Initialize(); - _constructForMainchainWethVault(); + _constructForMainchainWethMediator(); } function _getMainchainAndRoninTokens() internal view returns (address[] memory mainchainTokens, address[] memory roninTokens) { @@ -433,10 +433,10 @@ contract BaseIntegration_Test is Base_Test { } } - function _constructForMainchainWethVault() internal { + function _constructForMainchainWethMediator() internal { vm.startPrank(_config.getSender()); - _mainchainWethVault.setWeth(address(_mainchainWeth)); - _mainchainWethVault.transferOwnership(address(_mainchainGatewayV3)); + _mainchainWethMediator.setWeth(address(_mainchainWeth)); + _mainchainWethMediator.transferOwnership(address(_mainchainGatewayV3)); vm.stopPrank(); } @@ -498,7 +498,7 @@ contract BaseIntegration_Test is Base_Test { _mainchainGatewayV3.initializeV2(address(_mainchainBridgeManager)); _mainchainGatewayV3.initializeV3(_param.mainchainBridgeManager.bridgeOperators, _param.mainchainBridgeManager.voteWeights); - _mainchainGatewayV3.initializeV4(payable(address(_mainchainWethVault))); + _mainchainGatewayV3.initializeV4(payable(address(_mainchainWethMediator))); } function _mainchainPauseEnforcerInitialize() internal { From 60409395369c72e76716f52d1f989c123bed68c0 Mon Sep 17 00:00:00 2001 From: nxqbao Date: Wed, 6 Mar 2024 15:25:11 +0700 Subject: [PATCH 4/7] nit: fix mock --- test/mocks/MockDiscardEther.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mocks/MockDiscardEther.sol b/test/mocks/MockDiscardEther.sol index 6e7c11f6..bbf4befb 100644 --- a/test/mocks/MockDiscardEther.sol +++ b/test/mocks/MockDiscardEther.sol @@ -10,7 +10,7 @@ contract MockDiscardEther { _fallback(); } - function _fallback() internal view { + function _fallback() internal pure { revert("Not receive ether"); } } From db9277df4ce074629908659c20cf425ae04eacda Mon Sep 17 00:00:00 2001 From: nxqbao Date: Thu, 14 Mar 2024 17:01:34 +0700 Subject: [PATCH 5/7] fix(MainchainGateway): enable fallback --- src/mainchain/MainchainGatewayV3.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mainchain/MainchainGatewayV3.sol b/src/mainchain/MainchainGatewayV3.sol index f51333f7..6b0cbb79 100644 --- a/src/mainchain/MainchainGatewayV3.sol +++ b/src/mainchain/MainchainGatewayV3.sol @@ -44,11 +44,11 @@ contract MainchainGatewayV3 is WithdrawalLimitation, Initializable, AccessContro WethMediator public wethMediator; fallback() external payable { - // _fallback(); + _fallback(); } receive() external payable { - // _fallback(); + _fallback(); } /** From 532a9c89bf6548b8b70d093bd29b338551e9c6c0 Mon Sep 17 00:00:00 2001 From: nxqbao Date: Thu, 14 Mar 2024 18:11:09 +0700 Subject: [PATCH 6/7] fix(MainchainGateway): change logic of WethMediator to WethUnwrapper --- script/BridgeMigration.sol | 2 +- script/GeneralConfig.sol | 2 +- .../MainchainWethMediatorDeploy.s.sol | 23 -------- .../MainchainWethUnwrapperDeploy.s.sol | 23 ++++++++ script/interfaces/ISharedArgument.sol | 4 +- script/utils/Contract.sol | 4 +- src/extensions/WethMediator.sol | 44 --------------- src/extensions/WethUnwrapper.sol | 54 +++++++++++++++++++ src/interfaces/IWETH.sol | 4 ++ src/mainchain/MainchainGatewayV3.sol | 15 +++--- test/bridge/integration/BaseIntegration.t.sol | 20 +++---- 11 files changed, 101 insertions(+), 94 deletions(-) delete mode 100644 script/contracts/MainchainWethMediatorDeploy.s.sol create mode 100644 script/contracts/MainchainWethUnwrapperDeploy.s.sol delete mode 100644 src/extensions/WethMediator.sol create mode 100644 src/extensions/WethUnwrapper.sol diff --git a/script/BridgeMigration.sol b/script/BridgeMigration.sol index 17612c72..4cde39f8 100644 --- a/script/BridgeMigration.sol +++ b/script/BridgeMigration.sol @@ -139,4 +139,4 @@ contract BridgeMigration is BaseMigration { } } } -} +} \ No newline at end of file diff --git a/script/GeneralConfig.sol b/script/GeneralConfig.sol index 8b6a4f16..3d5de15f 100644 --- a/script/GeneralConfig.sol +++ b/script/GeneralConfig.sol @@ -58,7 +58,7 @@ contract GeneralConfig is BaseGeneralConfig, Utils { _contractNameMap[Contract.RoninPauseEnforcer.key()] = "PauseEnforcer"; _contractNameMap[Contract.MainchainPauseEnforcer.key()] = "PauseEnforcer"; - _contractNameMap[Contract.MainchainWethMediator.key()] = "WethMediator"; + _contractNameMap[Contract.MainchainWethUnwrapper.key()] = "WethUnwrapper"; } function _mapContractName(Contract contractEnum) internal { diff --git a/script/contracts/MainchainWethMediatorDeploy.s.sol b/script/contracts/MainchainWethMediatorDeploy.s.sol deleted file mode 100644 index 65d15b9b..00000000 --- a/script/contracts/MainchainWethMediatorDeploy.s.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { WethMediator } from "@ronin/contracts/extensions/WethMediator.sol"; -import { Contract } from "../utils/Contract.sol"; -import { ISharedArgument } from "../interfaces/ISharedArgument.sol"; -import { Migration } from "../Migration.s.sol"; - - -contract MainchainWethMediatorDeploy is Migration { - function _defaultArguments() internal virtual override returns (bytes memory args) { - ISharedArgument.WethMediatorParam memory param = config.sharedArguments().mainchainWethMediator; - - args = abi.encode( - param.weth, - param.owner - ); - } - - function run() public virtual returns (WethMediator) { - return WethMediator(_deployImmutable(Contract.MainchainWethMediator.key())); - } -} diff --git a/script/contracts/MainchainWethUnwrapperDeploy.s.sol b/script/contracts/MainchainWethUnwrapperDeploy.s.sol new file mode 100644 index 00000000..bdce4293 --- /dev/null +++ b/script/contracts/MainchainWethUnwrapperDeploy.s.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { WethUnwrapper } from "@ronin/contracts/extensions/WethUnwrapper.sol"; +import { Contract } from "../utils/Contract.sol"; +import { ISharedArgument } from "../interfaces/ISharedArgument.sol"; +import { Migration } from "../Migration.s.sol"; + +contract MainchainWethUnwrapperDeploy is Migration { + function _defaultArguments() internal virtual override returns (bytes memory args) { + ISharedArgument.WethUnwrapperParam memory param = config.sharedArguments().mainchainWethUnwrapper; + + args = abi.encode(param.weth); + } + + function run() public virtual returns (WethUnwrapper) { + return WethUnwrapper(_deployImmutable(Contract.MainchainWethUnwrapper.key())); + } + + function runWithArgs(bytes memory args) public virtual returns (WethUnwrapper) { + return WethUnwrapper(_deployImmutable(Contract.MainchainWethUnwrapper.key(), args)); + } +} diff --git a/script/interfaces/ISharedArgument.sol b/script/interfaces/ISharedArgument.sol index dc2e8090..a736e615 100644 --- a/script/interfaces/ISharedArgument.sol +++ b/script/interfaces/ISharedArgument.sol @@ -106,7 +106,7 @@ interface ISharedArgument is IGeneralConfig { uint256[] governorPKs; } - struct WethMediatorParam { + struct WethUnwrapperParam { address weth; address owner; } @@ -116,7 +116,7 @@ interface ISharedArgument is IGeneralConfig { BridgeManagerParam mainchainBridgeManager; MainchainGatewayV3Param mainchainGatewayV3; PauseEnforcerParam mainchainPauseEnforcer; - WethMediatorParam mainchainWethMediator; + WethUnwrapperParam mainchainWethUnwrapper; // ronin BridgeManagerParam roninBridgeManager; RoninGatewayV3Param roninGatewayV3; diff --git a/script/utils/Contract.sol b/script/utils/Contract.sol index a1c8e9c4..3442649f 100644 --- a/script/utils/Contract.sol +++ b/script/utils/Contract.sol @@ -19,7 +19,7 @@ enum Contract { MainchainPauseEnforcer, MainchainGatewayV3, MainchainBridgeManager, - MainchainWethMediator + MainchainWethUnwrapper } using { key, name } for Contract global; @@ -46,7 +46,7 @@ function name(Contract contractEnum) pure returns (string memory) { if (contractEnum == Contract.MainchainPauseEnforcer) return "PauseEnforcer"; if (contractEnum == Contract.MainchainGatewayV3) return "MainchainGatewayV3"; if (contractEnum == Contract.MainchainBridgeManager) return "MainchainBridgeManager"; - if (contractEnum == Contract.MainchainWethMediator) return "WethMediator"; + if (contractEnum == Contract.MainchainWethUnwrapper) return "WethUnwrapper"; revert("Contract: Unknown contract"); } diff --git a/src/extensions/WethMediator.sol b/src/extensions/WethMediator.sol deleted file mode 100644 index 6cc6ee00..00000000 --- a/src/extensions/WethMediator.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.9; - -import "@openzeppelin/contracts/access/Ownable.sol"; - -import "../interfaces/IWETH.sol"; - -contract WethMediator is Ownable { - IWETH public weth; - - error ErrInsufficientBalance(); - error ErrExternalCallFailed(address sender, bytes4 sig); - - constructor(address weth_, address owner_) { - weth = IWETH(weth_); - - if (owner_ != address(0)) { - _transferOwnership(owner_); - } - } - - fallback() external payable { } - receive() external payable { } - - function transferToVault(uint val) external { - weth.withdraw(val); - } - - function withdrawToOwner(uint val) external onlyOwner { - if (val > address(this).balance) { - revert ErrInsufficientBalance(); - } - - (bool success,) = payable(msg.sender).call{ value: val }(""); - if (!success) { - revert ErrExternalCallFailed(msg.sender, msg.sig); - } - } - - function setWeth(address weth_) external onlyOwner { - weth = IWETH(weth_); - } -} diff --git a/src/extensions/WethUnwrapper.sol b/src/extensions/WethUnwrapper.sol new file mode 100644 index 00000000..b86b3f88 --- /dev/null +++ b/src/extensions/WethUnwrapper.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "../interfaces/IWETH.sol"; + +contract WethUnwrapper is ReentrancyGuard { + IWETH public immutable weth; + + error ErrCannotTransferFrom(); + error ErrNotWrappedContract(); + error ErrExternalCallFailed(address sender, bytes4 sig); + + constructor(address weth_) { + if (address(weth_).code.length == 0) revert ErrNotWrappedContract(); + weth = IWETH(weth_); + } + + fallback() external payable { + _fallback(); + } + + receive() external payable { + _fallback(); + } + + function unwrap(uint256 amount) external nonReentrant { + _deductWrappedAndWithdraw(amount); + _sendNativeTo(payable(msg.sender), amount); + } + + function unwrapTo(uint256 amount, address payable to) external nonReentrant { + _deductWrappedAndWithdraw(amount); + _sendNativeTo(payable(to), amount); + } + + function _deductWrappedAndWithdraw(uint256 amount) internal { + (bool success,) = address(weth).call(abi.encodeCall(IWETH.transferFrom, (msg.sender, address(this), amount))); + if (!success) revert ErrCannotTransferFrom(); + + weth.withdraw(amount); + } + + function _sendNativeTo(address payable to, uint256 val) internal { + (bool success,) = to.call{ value: val }(""); + if (!success) { + revert ErrExternalCallFailed(to, msg.sig); + } + } + + function _fallback() internal view { + if (msg.sender != address(weth)) revert ErrNotWrappedContract(); + } +} diff --git a/src/interfaces/IWETH.sol b/src/interfaces/IWETH.sol index 5c00275f..67588c49 100644 --- a/src/interfaces/IWETH.sol +++ b/src/interfaces/IWETH.sol @@ -8,6 +8,10 @@ interface IWETH { function transfer(address dst, uint wad) external returns (bool); + function approve(address guy, uint wad) external returns (bool); + + function transferFrom(address src, address dst, uint wad) external returns (bool); + function withdraw(uint256 _wad) external; function balanceOf(address) external view returns (uint256); diff --git a/src/mainchain/MainchainGatewayV3.sol b/src/mainchain/MainchainGatewayV3.sol index 6b0cbb79..98ac1ca6 100644 --- a/src/mainchain/MainchainGatewayV3.sol +++ b/src/mainchain/MainchainGatewayV3.sol @@ -6,7 +6,7 @@ import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { IBridgeManager } from "../interfaces/bridge/IBridgeManager.sol"; import { IBridgeManagerCallback } from "../interfaces/bridge/IBridgeManagerCallback.sol"; import { HasContracts, ContractType } from "../extensions/collections/HasContracts.sol"; -import "../extensions/WethMediator.sol"; +import "../extensions/WethUnwrapper.sol"; import "../extensions/WithdrawalLimitation.sol"; import "../libraries/Transfer.sol"; import "../interfaces/IMainchainGatewayV3.sol"; @@ -41,7 +41,7 @@ contract MainchainGatewayV3 is WithdrawalLimitation, Initializable, AccessContro uint96 private _totalOperatorWeight; mapping(address operator => uint96 weight) private _operatorWeight; - WethMediator public wethMediator; + WethUnwrapper public wethUnwrapper; fallback() external payable { _fallback(); @@ -110,8 +110,8 @@ contract MainchainGatewayV3 is WithdrawalLimitation, Initializable, AccessContro _totalOperatorWeight = totalWeight; } - function initializeV4(address payable wethMediator_) external reinitializer(4) { - wethMediator = WethMediator(wethMediator_); + function initializeV4(address payable wethUnwrapper_) external reinitializer(4) { + wethUnwrapper = WethUnwrapper(wethUnwrapper_); } /** @@ -343,11 +343,10 @@ contract MainchainGatewayV3 is WithdrawalLimitation, Initializable, AccessContro _request.info.transferFrom(_requester, address(this), _request.tokenAddr); // Withdraw if token is WETH - // The withdraw of WETH must go via `WethMediator`, because `WETH.withdraw` only sends 2300 gas, which is insufficient when recipient is a proxy. + // The withdraw of WETH must go via `WethUnwrapper`, because `WETH.withdraw` only sends 2300 gas, which is insufficient when recipient is a proxy. if (_roninWeth == _request.tokenAddr) { - IWETH(_roninWeth).transfer(address(wethMediator), _request.info.quantity); - wethMediator.transferToVault(_request.info.quantity); - wethMediator.withdrawToOwner(_request.info.quantity); + wrappedNativeToken.approve(address(wethUnwrapper), _request.info.quantity); + wethUnwrapper.unwrap(_request.info.quantity); } } diff --git a/test/bridge/integration/BaseIntegration.t.sol b/test/bridge/integration/BaseIntegration.t.sol index e87e2293..d2cabd9c 100644 --- a/test/bridge/integration/BaseIntegration.t.sol +++ b/test/bridge/integration/BaseIntegration.t.sol @@ -16,7 +16,7 @@ import { BridgeSlash } from "@ronin/contracts/ronin/gateway/BridgeSlash.sol"; import { BridgeReward } from "@ronin/contracts/ronin/gateway/BridgeReward.sol"; import { MainchainGatewayV3 } from "@ronin/contracts/mainchain/MainchainGatewayV3.sol"; import { MainchainBridgeManager } from "@ronin/contracts/mainchain/MainchainBridgeManager.sol"; -import { WethMediator } from "@ronin/contracts/extensions/WethMediator.sol"; +import { WethUnwrapper } from "@ronin/contracts/extensions/WethUnwrapper.sol"; import { MockERC20 } from "@ronin/contracts/mocks/token/MockERC20.sol"; import { MockERC721 } from "@ronin/contracts/mocks/token/MockERC721.sol"; @@ -49,7 +49,7 @@ import { RoninPauseEnforcerDeploy } from "@ronin/script/contracts/RoninPauseEnfo import { MainchainGatewayV3Deploy } from "@ronin/script/contracts/MainchainGatewayV3Deploy.s.sol"; import { MainchainBridgeManagerDeploy } from "@ronin/script/contracts/MainchainBridgeManagerDeploy.s.sol"; import { MainchainPauseEnforcerDeploy } from "@ronin/script/contracts/MainchainPauseEnforcerDeploy.s.sol"; -import { MainchainWethMediatorDeploy } from "@ronin/script/contracts/MainchainWethMediatorDeploy.s.sol"; +import { MainchainWethUnwrapperDeploy } from "@ronin/script/contracts/MainchainWethUnwrapperDeploy.s.sol"; import { WETHDeploy } from "@ronin/script/contracts/token/WETHDeploy.s.sol"; import { WRONDeploy } from "@ronin/script/contracts/token/WRONDeploy.s.sol"; import { AXSDeploy } from "@ronin/script/contracts/token/AXSDeploy.s.sol"; @@ -76,7 +76,7 @@ contract BaseIntegration_Test is Base_Test { PauseEnforcer _mainchainPauseEnforcer; MainchainGatewayV3 _mainchainGatewayV3; MainchainBridgeManager _mainchainBridgeManager; - WethMediator _mainchainWethMediator; + WethUnwrapper _mainchainWethUnwrapper; MockWrappedToken _roninWeth; MockWrappedToken _roninWron; @@ -138,7 +138,6 @@ contract BaseIntegration_Test is Base_Test { _mainchainPauseEnforcer = new MainchainPauseEnforcerDeploy().run(); _mainchainGatewayV3 = new MainchainGatewayV3Deploy().run(); _mainchainBridgeManager = new MainchainBridgeManagerDeploy().run(); - _mainchainWethMediator = new MainchainWethMediatorDeploy().run(); _mainchainWeth = new WETHDeploy().run(); _mainchainAxs = new AXSDeploy().run(); @@ -146,6 +145,9 @@ contract BaseIntegration_Test is Base_Test { _mainchainUsdc = new USDCDeploy().run(); _mainchainMockERC721 = new MockERC721Deploy().run(); + bytes memory args = abi.encode(_mainchainWeth); + _mainchainWethUnwrapper = new MainchainWethUnwrapperDeploy().runWithArgs(args); + _param = ISharedArgument(LibSharedAddress.CONFIG).sharedArguments(); _mainchainProposalUtils = new MainchainBridgeAdminUtils(_param.test.governorPKs, _mainchainBridgeManager, _param.mainchainBridgeManager.governors[0]); } @@ -167,7 +169,6 @@ contract BaseIntegration_Test is Base_Test { _mainchainPauseEnforcerInitialize(); _constructForMainchainBridgeManager(); _mainchainGatewayV3Initialize(); - _constructForMainchainWethMediator(); } function _getMainchainAndRoninTokens() internal view returns (address[] memory mainchainTokens, address[] memory roninTokens) { @@ -433,13 +434,6 @@ contract BaseIntegration_Test is Base_Test { } } - function _constructForMainchainWethMediator() internal { - vm.startPrank(_config.getSender()); - _mainchainWethMediator.setWeth(address(_mainchainWeth)); - _mainchainWethMediator.transferOwnership(address(_mainchainGatewayV3)); - vm.stopPrank(); - } - function _mainchainGatewayV3Initialize() internal { (address[] memory mainchainTokens, address[] memory roninTokens) = _getMainchainAndRoninTokens(); uint256 tokenNum = mainchainTokens.length; @@ -498,7 +492,7 @@ contract BaseIntegration_Test is Base_Test { _mainchainGatewayV3.initializeV2(address(_mainchainBridgeManager)); _mainchainGatewayV3.initializeV3(_param.mainchainBridgeManager.bridgeOperators, _param.mainchainBridgeManager.voteWeights); - _mainchainGatewayV3.initializeV4(payable(address(_mainchainWethMediator))); + _mainchainGatewayV3.initializeV4(payable(address(_mainchainWethUnwrapper))); } function _mainchainPauseEnforcerInitialize() internal { From 515692c9648ddeb550f9aca44c59195c994b5a9e Mon Sep 17 00:00:00 2001 From: nxqbao Date: Mon, 25 Mar 2024 10:58:45 +0700 Subject: [PATCH 7/7] fix(MainchainGateway): not createDepositAllFallback with WETHWrapper --- src/mainchain/MainchainGatewayV3.sol | 4 ++-- ...requestDepositFor.MainchainGatewayV3.t.sol | 19 ++++++------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/mainchain/MainchainGatewayV3.sol b/src/mainchain/MainchainGatewayV3.sol index c5eb4a23..20cfa732 100644 --- a/src/mainchain/MainchainGatewayV3.sol +++ b/src/mainchain/MainchainGatewayV3.sol @@ -416,10 +416,10 @@ contract MainchainGatewayV3 is WithdrawalLimitation, Initializable, AccessContro } /** - * @dev Receives ETH from WETH or creates deposit request if sender is not WETH. + * @dev Receives ETH from WETH or creates deposit request if sender is not WETH or WETHUnwrapper. */ function _fallback() internal virtual { - if (msg.sender == address(wrappedNativeToken)) { + if (msg.sender == address(wrappedNativeToken) || msg.sender == address(wethUnwrapper)) { return; } diff --git a/test/bridge/integration/mainchain-gateway/requestDepositFor.MainchainGatewayV3.t.sol b/test/bridge/integration/mainchain-gateway/requestDepositFor.MainchainGatewayV3.t.sol index 9de95d9c..ac941c5e 100644 --- a/test/bridge/integration/mainchain-gateway/requestDepositFor.MainchainGatewayV3.t.sol +++ b/test/bridge/integration/mainchain-gateway/requestDepositFor.MainchainGatewayV3.t.sol @@ -49,9 +49,7 @@ contract RequestDepositFor_MainchainGatewayV3_Test is BaseIntegration_Test { cachedRequest.tokenAddr = address(_mainchainWeth); vm.expectEmit(address(_mainchainGatewayV3)); - LibTransfer.Receipt memory receipt = cachedRequest.into_deposit_receipt( - _sender, _mainchainGatewayV3.depositCount(), address(_roninWeth), block.chainid - ); + LibTransfer.Receipt memory receipt = cachedRequest.into_deposit_receipt(_sender, _mainchainGatewayV3.depositCount(), address(_roninWeth), block.chainid); emit DepositRequested(receipt.hash(), receipt); vm.prank(_sender); @@ -70,9 +68,7 @@ contract RequestDepositFor_MainchainGatewayV3_Test is BaseIntegration_Test { _depositRequest.tokenAddr = address(_mainchainAxs); vm.expectEmit(address(_mainchainGatewayV3)); - LibTransfer.Receipt memory receipt = _depositRequest.into_deposit_receipt( - _sender, _mainchainGatewayV3.depositCount(), address(_roninAxs), block.chainid - ); + LibTransfer.Receipt memory receipt = _depositRequest.into_deposit_receipt(_sender, _mainchainGatewayV3.depositCount(), address(_roninAxs), block.chainid); emit DepositRequested(receipt.hash(), receipt); vm.prank(_sender); @@ -94,9 +90,8 @@ contract RequestDepositFor_MainchainGatewayV3_Test is BaseIntegration_Test { _depositRequest.info.id = tokenId; _depositRequest.info.quantity = 0; - LibTransfer.Receipt memory receipt = _depositRequest.into_deposit_receipt( - _sender, _mainchainGatewayV3.depositCount(), address(_roninMockERC721), block.chainid - ); + LibTransfer.Receipt memory receipt = + _depositRequest.into_deposit_receipt(_sender, _mainchainGatewayV3.depositCount(), address(_roninMockERC721), block.chainid); vm.expectEmit(address(_mainchainGatewayV3)); emit DepositRequested(receipt.hash(), receipt); @@ -118,15 +113,13 @@ contract RequestDepositFor_MainchainGatewayV3_Test is BaseIntegration_Test { _depositRequest.tokenAddr = address(_mainchainWeth); - LibTransfer.Receipt memory receipt = _depositRequest.into_deposit_receipt( - _sender, _mainchainGatewayV3.depositCount(), address(_roninWeth), block.chainid - ); + LibTransfer.Receipt memory receipt = _depositRequest.into_deposit_receipt(_sender, _mainchainGatewayV3.depositCount(), address(_roninWeth), block.chainid); vm.expectEmit(address(_mainchainGatewayV3)); emit DepositRequested(receipt.hash(), receipt); assertEq(address(_mainchainWeth).balance, _quantity); - vm.prank(_sender); + vm.startPrank(_sender); _mainchainGatewayV3.requestDepositFor(_depositRequest); assertEq(address(_mainchainGatewayV3).balance, _quantity);