diff --git a/solidity/contracts/hooks/OPL2ToL1Hook.sol b/solidity/contracts/hooks/OPL2ToL1Hook.sol index 165289e57f..a8396b5de8 100644 --- a/solidity/contracts/hooks/OPL2ToL1Hook.sol +++ b/solidity/contracts/hooks/OPL2ToL1Hook.sol @@ -67,8 +67,12 @@ contract OPL2ToL1Hook is AbstractMessageIdAuthHook { bytes calldata metadata, bytes calldata message ) internal view override returns (uint256) { + bytes memory metadataWithGasLimit = metadata.overrideGasLimit( + MIN_GAS_LIMIT + ); return - metadata.msgValue(0) + childHook.quoteDispatch(metadata, message); + metadata.msgValue(0) + + childHook.quoteDispatch(metadataWithGasLimit, message); } // ============ Internal functions ============ @@ -83,9 +87,12 @@ contract OPL2ToL1Hook is AbstractMessageIdAuthHook { (message.id(), metadata.msgValue(0)) ); + bytes memory metadataWithGasLimit = metadata.overrideGasLimit( + MIN_GAS_LIMIT + ); childHook.postDispatch{ - value: childHook.quoteDispatch(metadata, message) - }(metadata, message); + value: childHook.quoteDispatch(metadataWithGasLimit, message) + }(metadataWithGasLimit, message); l2Messenger.sendMessage{value: metadata.msgValue(0)}( TypeCasts.bytes32ToAddress(ism), payload, diff --git a/solidity/contracts/hooks/libs/StandardHookMetadata.sol b/solidity/contracts/hooks/libs/StandardHookMetadata.sol index e12f6822ad..72f84e06f1 100644 --- a/solidity/contracts/hooks/libs/StandardHookMetadata.sol +++ b/solidity/contracts/hooks/libs/StandardHookMetadata.sol @@ -165,4 +165,42 @@ library StandardHookMetadata { ) internal pure returns (bytes memory) { return formatMetadata(uint256(0), uint256(0), _refundAddress, ""); } + + /** + * @notice Overrides the msg.value in the metadata. + * @param _metadata encoded standard hook metadata. + * @param _msgValue msg.value for the message. + * @return encoded standard hook metadata. + */ + function overrideMsgValue( + bytes calldata _metadata, + uint256 _msgValue + ) internal view returns (bytes memory) { + return + formatMetadata( + _msgValue, + gasLimit(_metadata, 0), + refundAddress(_metadata, msg.sender), + getCustomMetadata(_metadata) + ); + } + + /** + * @notice Overrides the gas limit in the metadata. + * @param _metadata encoded standard hook metadata. + * @param _gasLimit gas limit for the message. + * @return encoded standard hook metadata. + */ + function overrideGasLimit( + bytes calldata _metadata, + uint256 _gasLimit + ) internal view returns (bytes memory) { + return + formatMetadata( + msgValue(_metadata, 0), + _gasLimit, + refundAddress(_metadata, msg.sender), + getCustomMetadata(_metadata) + ); + } } diff --git a/solidity/contracts/token/HypNative.sol b/solidity/contracts/token/HypNative.sol index cfb526fa40..dfee1a503b 100644 --- a/solidity/contracts/token/HypNative.sol +++ b/solidity/contracts/token/HypNative.sol @@ -1,14 +1,29 @@ -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.0; +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + +// ============ Internal Imports ============ import {TokenRouter} from "./libs/TokenRouter.sol"; -import {TokenMessage} from "./libs/TokenMessage.sol"; +import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol"; + +// ============ External Imports ============ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; /** - * @title Hyperlane Native Token Router that extends ERC20 with remote transfer functionality. + * @title HypNative * @author Abacus Works - * @dev Supply on each chain is not constant but the aggregate supply across all chains is. + * @notice This contract facilitates the transfer of value between chains using value transfer hooks */ contract HypNative is TokenRouter { /** @@ -17,40 +32,37 @@ contract HypNative is TokenRouter { * @param amount The amount of native tokens donated. */ event Donation(address indexed sender, uint256 amount); + // ============ Errors ============ + + error InsufficientValue(uint256 requiredValue, uint256 providedValue); constructor(address _mailbox) TokenRouter(_mailbox) {} + // ============ Initialization ============ + /** - * @notice Initializes the Hyperlane router - * @param _hook The post-dispatch hook contract. - @param _interchainSecurityModule The interchain security module contract. - @param _owner The this contract. + * @notice Initializes the contract + * @param _valuehook The address of the value transfer hook + * @param _interchainSecurityModule The address of the interchain security module + * @param _owner The owner of the contract */ function initialize( - address _hook, + address _valuehook, address _interchainSecurityModule, address _owner ) public initializer { - _MailboxClient_initialize(_hook, _interchainSecurityModule, _owner); + _MailboxClient_initialize( + _valuehook, + _interchainSecurityModule, + _owner + ); } - /** - * @inheritdoc TokenRouter - * @dev uses (`msg.value` - `_amount`) as hook payment and `msg.sender` as refund address. - */ - function transferRemote( - uint32 _destination, - bytes32 _recipient, - uint256 _amount - ) external payable virtual override returns (bytes32 messageId) { - require(msg.value >= _amount, "Native: amount exceeds msg.value"); - uint256 _hookPayment = msg.value - _amount; - return _transferRemote(_destination, _recipient, _amount, _hookPayment); - } + // ============ External Functions ============ /** * @inheritdoc TokenRouter - * @dev uses (`msg.value` - `_amount`) as hook payment. + * @dev use _hook with caution, make sure that this hook can handle msg.value transfer using the metadata.msgValue() */ function transferRemote( uint32 _destination, @@ -58,49 +70,84 @@ contract HypNative is TokenRouter { uint256 _amount, bytes calldata _hookMetadata, address _hook - ) external payable virtual override returns (bytes32 messageId) { - require(msg.value >= _amount, "Native: amount exceeds msg.value"); - uint256 _hookPayment = msg.value - _amount; + ) public payable virtual override returns (bytes32 messageId) { + uint256 quote = _GasRouter_quoteDispatch( + _destination, + _hookMetadata, + _hook + ); + if (msg.value < _amount + quote) { + revert InsufficientValue(_amount + quote, msg.value); + } + + bytes memory hookMetadata = StandardHookMetadata.overrideMsgValue( + _hookMetadata, + _amount + ); + return _transferRemote( _destination, _recipient, _amount, - _hookPayment, - _hookMetadata, + _amount + quote, + hookMetadata, _hook ); } - function balanceOf( - address _account - ) external view override returns (uint256) { - return _account.balance; + /// @inheritdoc TokenRouter + function transferRemote( + uint32 _destination, + bytes32 _recipient, + uint256 _amount + ) external payable virtual override returns (bytes32 messageId) { + bytes calldata emptyBytes; + assembly { + emptyBytes.length := 0 + emptyBytes.offset := 0 + } + return + transferRemote( + _destination, + _recipient, + _amount, + emptyBytes, + address(hook) + ); } + // ============ Internal Functions ============ + /** * @inheritdoc TokenRouter - * @dev No-op because native amount is transferred in `msg.value` - * @dev Compiler will not include this in the bytecode. + * @dev No token metadata is needed for value transfers */ function _transferFromSender( uint256 ) internal pure override returns (bytes memory) { - return bytes(""); // no metadata + return bytes(""); // no token metadata } /** - * @dev Sends `_amount` of native token to `_recipient` balance. * @inheritdoc TokenRouter + * @dev Sends the value to the recipient */ function _transferTo( address _recipient, uint256 _amount, - bytes calldata // no metadata + bytes calldata // no token metadata ) internal virtual override { Address.sendValue(payable(_recipient), _amount); } + /// @inheritdoc TokenRouter + function balanceOf( + address _account + ) external view override returns (uint256) { + return _account.balance; + } + receive() external payable { emit Donation(msg.sender, msg.value); } diff --git a/solidity/contracts/token/HypNativeCollateral.sol b/solidity/contracts/token/HypNativeCollateral.sol new file mode 100644 index 0000000000..ec6630d910 --- /dev/null +++ b/solidity/contracts/token/HypNativeCollateral.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.8.0; + +import {TokenRouter} from "./libs/TokenRouter.sol"; +import {TokenMessage} from "./libs/TokenMessage.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; + +/** + * @title Hyperlane Native Token Router that extends ERC20 with remote transfer functionality. + * @author Abacus Works + * @dev Supply on each chain is not constant but the aggregate supply across all chains is. + */ +contract HypNativeCollateral is TokenRouter { + /** + * @dev Emitted when native tokens are donated to the contract. + * @param sender The address of the sender. + * @param amount The amount of native tokens donated. + */ + event Donation(address indexed sender, uint256 amount); + + constructor(address _mailbox) TokenRouter(_mailbox) {} + + /** + * @notice Initializes the Hyperlane router + * @param _hook The post-dispatch hook contract. + * @param _interchainSecurityModule The interchain security module contract. + * @param _owner The this contract. + */ + function initialize( + address _hook, + address _interchainSecurityModule, + address _owner + ) public initializer { + _MailboxClient_initialize(_hook, _interchainSecurityModule, _owner); + } + + /** + * @inheritdoc TokenRouter + * @dev uses (`msg.value` - `_amount`) as hook payment and `msg.sender` as refund address. + */ + function transferRemote( + uint32 _destination, + bytes32 _recipient, + uint256 _amount + ) external payable virtual override returns (bytes32 messageId) { + require(msg.value >= _amount, "Native: amount exceeds msg.value"); + uint256 _hookPayment = msg.value - _amount; + return _transferRemote(_destination, _recipient, _amount, _hookPayment); + } + + /** + * @inheritdoc TokenRouter + * @dev uses (`msg.value` - `_amount`) as hook payment. + */ + function transferRemote( + uint32 _destination, + bytes32 _recipient, + uint256 _amount, + bytes calldata _hookMetadata, + address _hook + ) external payable virtual override returns (bytes32 messageId) { + require(msg.value >= _amount, "Native: amount exceeds msg.value"); + uint256 _hookPayment = msg.value - _amount; + return + _transferRemote( + _destination, + _recipient, + _amount, + _hookPayment, + _hookMetadata, + _hook + ); + } + + function balanceOf( + address _account + ) external view override returns (uint256) { + return _account.balance; + } + + /** + * @inheritdoc TokenRouter + * @dev No-op because native amount is transferred in `msg.value` + * @dev Compiler will not include this in the bytecode. + */ + function _transferFromSender( + uint256 + ) internal pure override returns (bytes memory) { + return bytes(""); // no metadata + } + + /** + * @dev Sends `_amount` of native token to `_recipient` balance. + * @inheritdoc TokenRouter + */ + function _transferTo( + address _recipient, + uint256 _amount, + bytes calldata // no metadata + ) internal virtual override { + Address.sendValue(payable(_recipient), _amount); + } + + receive() external payable { + emit Donation(msg.sender, msg.value); + } +} diff --git a/solidity/contracts/token/extensions/HypNativeScaled.sol b/solidity/contracts/token/extensions/HypNativeCollateralScaled.sol similarity index 86% rename from solidity/contracts/token/extensions/HypNativeScaled.sol rename to solidity/contracts/token/extensions/HypNativeCollateralScaled.sol index ad129fce80..c12117a6d5 100644 --- a/solidity/contracts/token/extensions/HypNativeScaled.sol +++ b/solidity/contracts/token/extensions/HypNativeCollateralScaled.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity >=0.8.0; -import {HypNative} from "../HypNative.sol"; +import {HypNativeCollateral} from "../HypNativeCollateral.sol"; import {TokenRouter} from "../libs/TokenRouter.sol"; /** @@ -10,15 +10,18 @@ import {TokenRouter} from "../libs/TokenRouter.sol"; * Conversely, it divides the local native `msg.value` amount by `scale` to encode the `message.amount`. * @author Abacus Works */ -contract HypNativeScaled is HypNative { +contract HypNativeCollateralScaled is HypNativeCollateral { uint256 public immutable scale; - constructor(uint256 _scale, address _mailbox) HypNative(_mailbox) { + constructor( + uint256 _scale, + address _mailbox + ) HypNativeCollateral(_mailbox) { scale = _scale; } /** - * @inheritdoc HypNative + * @inheritdoc HypNativeCollateral * @dev Sends scaled `msg.value` (divided by `scale`) to `_recipient`. */ function transferRemote( @@ -73,6 +76,6 @@ contract HypNativeScaled is HypNative { bytes calldata metadata // no metadata ) internal override { uint256 scaledAmount = _amount * scale; - HypNative._transferTo(_recipient, scaledAmount, metadata); + HypNativeCollateral._transferTo(_recipient, scaledAmount, metadata); } } diff --git a/solidity/test/isms/ExternalBridgeTest.sol b/solidity/test/isms/ExternalBridgeTest.sol index 937e39311a..e586297822 100644 --- a/solidity/test/isms/ExternalBridgeTest.sol +++ b/solidity/test/isms/ExternalBridgeTest.sol @@ -115,7 +115,6 @@ abstract contract ExternalBridgeTest is Test { function test_postDispatch_revertWhen_insufficientValue() public { bytes memory encodedHookData = _encodeHookData(messageId, 0); originMailbox.updateLatestDispatchedId(messageId); - _expectOriginExternalBridgeCall(encodedHookData); uint256 quote = hook.quoteDispatch(testMetadata, encodedMessage); diff --git a/solidity/test/isms/OPL2ToL1Ism.t.sol b/solidity/test/isms/OPL2ToL1Ism.t.sol index 9322713d5e..195a0704c0 100644 --- a/solidity/test/isms/OPL2ToL1Ism.t.sol +++ b/solidity/test/isms/OPL2ToL1Ism.t.sol @@ -21,6 +21,7 @@ contract OPL2ToL1IsmTest is ExternalBridgeTest { 0x4200000000000000000000000000000000000007; uint256 internal constant MOCK_NONCE = 0; + uint256 internal constant GAS_LIMIT = 300_000; TestInterchainGasPaymaster internal mockOverheadIgp; MockOptimismPortal internal portal; @@ -40,6 +41,8 @@ contract OPL2ToL1IsmTest is ExternalBridgeTest { deployAll(); super.setUp(); + + GAS_QUOTE = GAS_LIMIT * 10; } function deployHook() public { @@ -76,11 +79,14 @@ contract OPL2ToL1IsmTest is ExternalBridgeTest { _expectOriginExternalBridgeCall(encodedHookData); bytes memory igpMetadata = StandardHookMetadata.overrideGasLimit( - 78_000 + GAS_LIMIT ); uint256 quote = hook.quoteDispatch(igpMetadata, encodedMessage); - assertEq(quote, mockOverheadIgp.quoteGasPayment(ORIGIN_DOMAIN, 78_000)); + assertEq( + quote, + mockOverheadIgp.quoteGasPayment(ORIGIN_DOMAIN, GAS_LIMIT) + ); hook.postDispatch{value: quote}(igpMetadata, encodedMessage); } diff --git a/solidity/test/token/HypERC20.t.sol b/solidity/test/token/HypERC20.t.sol index c3b7da74cd..66acba13d6 100644 --- a/solidity/test/token/HypERC20.t.sol +++ b/solidity/test/token/HypERC20.t.sol @@ -33,7 +33,7 @@ import {IXERC20} from "../../contracts/token/interfaces/IXERC20.sol"; import {IFiatToken} from "../../contracts/token/interfaces/IFiatToken.sol"; import {HypXERC20} from "../../contracts/token/extensions/HypXERC20.sol"; import {HypFiatToken} from "../../contracts/token/extensions/HypFiatToken.sol"; -import {HypNative} from "../../contracts/token/HypNative.sol"; +import {HypNativeCollateral} from "../../contracts/token/HypNativeCollateral.sol"; import {TokenRouter} from "../../contracts/token/libs/TokenRouter.sol"; import {TokenMessage} from "../../contracts/token/libs/TokenMessage.sol"; import {Message} from "../../contracts/libs/Message.sol"; @@ -623,16 +623,16 @@ contract HypFiatTokenTest is HypTokenTest { } } -contract HypNativeTest is HypTokenTest { +contract HypNativeCollateralTest is HypTokenTest { using TypeCasts for address; - HypNative internal nativeToken; + HypNativeCollateral internal nativeToken; function setUp() public override { super.setUp(); - localToken = new HypNative(address(localMailbox)); - nativeToken = HypNative(payable(address(localToken))); + localToken = new HypNativeCollateral(address(localMailbox)); + nativeToken = HypNativeCollateral(payable(address(localToken))); nativeToken.enrollRemoteRouter( DESTINATION, diff --git a/solidity/test/token/HypNative.t.sol b/solidity/test/token/HypNative.t.sol new file mode 100644 index 0000000000..bac841b42d --- /dev/null +++ b/solidity/test/token/HypNative.t.sol @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; +import {HypTokenTest} from "./HypERC20.t.sol"; +import {HypERC20} from "../../contracts/token/HypERC20.sol"; +import {TokenRouter} from "../../contracts/token/libs/TokenRouter.sol"; +import {HypNative} from "../../contracts/token/HypNative.sol"; +import {OPL2ToL1Hook} from "../../contracts/hooks/OPL2ToL1Hook.sol"; +import {OPL2ToL1Ism} from "../../contracts/isms/hook/OPL2ToL1Ism.sol"; +import {IOptimismPortal} from "../../contracts/interfaces/optimism/IOptimismPortal.sol"; +import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; +import {ICrossDomainMessenger} from "../../contracts/interfaces/optimism/ICrossDomainMessenger.sol"; +import {AbstractMessageIdAuthorizedIsm} from "../../contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol"; +import {MockOptimismMessenger, MockOptimismPortal} from "../../contracts/mock/MockOptimism.sol"; +import {TestInterchainGasPaymaster} from "../../contracts/test/TestInterchainGasPaymaster.sol"; + +contract HypNativeTest is HypTokenTest { + using TypeCasts for address; + + address internal constant L2_MESSENGER_ADDRESS = + 0x4200000000000000000000000000000000000007; + uint256 internal constant OP_BRIDGE_GAS_LIMIT = 100_000; + uint256 internal constant MOCK_NONCE = 0; + + HypNative internal localValueRouter; + HypNative internal remoteValueRouter; + OPL2ToL1Hook internal valueHook; + OPL2ToL1Ism internal ism; + TestInterchainGasPaymaster internal mockOverheadIgp; + MockOptimismPortal internal portal; + MockOptimismMessenger internal l1Messenger; + + function setUp() public override { + super.setUp(); + vm.etch( + L2_MESSENGER_ADDRESS, + address(new MockOptimismMessenger()).code + ); + + localValueRouter = new HypNative(address(localMailbox)); + remoteValueRouter = new HypNative(address(remoteMailbox)); + + localToken = TokenRouter(payable(address(localValueRouter))); + remoteToken = HypERC20(payable(address(remoteValueRouter))); + + l1Messenger = new MockOptimismMessenger(); + portal = new MockOptimismPortal(); + l1Messenger.setPORTAL(address(portal)); + ism = new OPL2ToL1Ism(address(l1Messenger)); + + mockOverheadIgp = new TestInterchainGasPaymaster(); + valueHook = new OPL2ToL1Hook( + address(localMailbox), + DESTINATION, + address(localMailbox).addressToBytes32(), + L2_MESSENGER_ADDRESS, + address(mockOverheadIgp) + ); + + localValueRouter.initialize( + address(valueHook), + address(ism), + address(this) + ); + remoteValueRouter.initialize( + address(valueHook), + address(ism), + address(this) + ); + + localValueRouter.enrollRemoteRouter( + DESTINATION, + address(remoteToken).addressToBytes32() + ); + remoteValueRouter.enrollRemoteRouter( + ORIGIN, + address(localToken).addressToBytes32() + ); + + vm.deal(ALICE, TRANSFER_AMT * 10); + } + + function testRemoteTransfer() public { + uint256 quote = localValueRouter.quoteGasPayment(DESTINATION); + uint256 msgValue = TRANSFER_AMT + quote; + + vm.expectEmit(true, true, false, true); + emit TokenRouter.SentTransferRemote( + DESTINATION, + BOB.addressToBytes32(), + TRANSFER_AMT + ); + + vm.prank(ALICE); + bytes32 messageId = localToken.transferRemote{value: msgValue}( + DESTINATION, + BOB.addressToBytes32(), + TRANSFER_AMT + ); + + vm.assertEq(address(localToken).balance, 0); + vm.assertEq(address(valueHook).balance, 0); + + _externalBridgeDestinationCall(messageId, msgValue); + + vm.expectEmit(true, true, false, true); + emit ReceivedTransferRemote( + ORIGIN, + BOB.addressToBytes32(), + TRANSFER_AMT + ); + remoteMailbox.processNextInboundMessage(); + + assertEq(BOB.balance, TRANSFER_AMT); + assertEq(address(mockOverheadIgp).balance, quote); + } + + function testRemoteTransfer_invalidAmount() public { + uint256 quote = localValueRouter.quoteGasPayment(DESTINATION); + + vm.expectRevert( + abi.encodeWithSelector( + HypNative.InsufficientValue.selector, + TRANSFER_AMT + quote, + TRANSFER_AMT + ) + ); + vm.prank(ALICE); + localToken.transferRemote{value: TRANSFER_AMT}( + DESTINATION, + BOB.addressToBytes32(), + TRANSFER_AMT + ); + } + + function testTransfer_withHookSpecified( + uint256 fee, + bytes calldata metadata + ) public override { + vm.assume(fee < TRANSFER_AMT); + uint256 msgValue = TRANSFER_AMT + fee; + vm.deal(ALICE, msgValue); + + TestPostDispatchHook hook = new TestPostDispatchHook(); + hook.setFee(fee); + + vm.prank(ALICE); + localToken.transferRemote{value: msgValue}( + DESTINATION, + BOB.addressToBytes32(), + TRANSFER_AMT, + metadata, + address(hook) + ); + + vm.assertEq(address(localToken).balance, 0); + vm.assertEq(address(valueHook).balance, 0); + } + + function testTransfer_withHookSpecified_revertsInsufficientValue( + uint256 fee, + bytes calldata metadata + ) public { + vm.assume(fee < TRANSFER_AMT); + uint256 msgValue = TRANSFER_AMT + fee; + vm.deal(ALICE, msgValue); + + TestPostDispatchHook hook = new TestPostDispatchHook(); + hook.setFee(fee); + + vm.prank(ALICE); + vm.expectRevert( + abi.encodeWithSelector( + HypNative.InsufficientValue.selector, + msgValue, + msgValue - 1 + ) + ); + localToken.transferRemote{value: msgValue - 1}( + DESTINATION, + BOB.addressToBytes32(), + TRANSFER_AMT, + metadata, + address(hook) + ); + } + + function testBenchmark_overheadGasUsage() public override { + vm.deal(address(localValueRouter), TRANSFER_AMT); + super.testBenchmark_overheadGasUsage(); + } + + // helper function to simulate the external bridge destination call using OP L2 -> L1 bridge + function _externalBridgeDestinationCall( + bytes32 _messageId, + uint256 _msgValue + ) internal { + bytes memory encodedHookData = abi.encodeCall( + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (_messageId, _msgValue) + ); + + bytes memory messengerCalldata = abi.encodeCall( + ICrossDomainMessenger.relayMessage, + ( + MOCK_NONCE, + address(valueHook), + address(ism), + _msgValue, + OP_BRIDGE_GAS_LIMIT, + encodedHookData + ) + ); + vm.deal(address(portal), _msgValue); + IOptimismPortal.WithdrawalTransaction + memory withdrawal = IOptimismPortal.WithdrawalTransaction({ + nonce: MOCK_NONCE, + sender: L2_MESSENGER_ADDRESS, + target: address(l1Messenger), + value: _msgValue, + gasLimit: OP_BRIDGE_GAS_LIMIT, + data: messengerCalldata + }); + portal.finalizeWithdrawalTransaction(withdrawal); + } +} diff --git a/solidity/test/token/HypNativeScaled.t.sol b/solidity/test/token/HypNativeCollateralScaled.t.sol similarity index 91% rename from solidity/test/token/HypNativeScaled.t.sol rename to solidity/test/token/HypNativeCollateralScaled.t.sol index ffded65655..d867caf7c6 100644 --- a/solidity/test/token/HypNativeScaled.t.sol +++ b/solidity/test/token/HypNativeCollateralScaled.t.sol @@ -5,13 +5,13 @@ import "forge-std/Test.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; -import {HypNativeScaled} from "../../contracts/token/extensions/HypNativeScaled.sol"; +import {HypNativeCollateralScaled} from "../../contracts/token/extensions/HypNativeCollateralScaled.sol"; import {HypERC20} from "../../contracts/token/HypERC20.sol"; -import {HypNative} from "../../contracts/token/HypNative.sol"; +import {HypNativeCollateral} from "../../contracts/token/HypNativeCollateral.sol"; import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; import {MockHyperlaneEnvironment} from "../../contracts/mock/MockHyperlaneEnvironment.sol"; -contract HypNativeScaledTest is Test { +contract HypNativeCollateralScaledTest is Test { uint32 nativeDomain = 1; uint32 synthDomain = 2; @@ -34,7 +34,7 @@ contract HypNativeScaledTest is Test { uint256 amount ); - HypNativeScaled native; + HypNativeCollateralScaled native; HypERC20 synth; MockHyperlaneEnvironment environment; @@ -61,22 +61,22 @@ contract HypNativeScaledTest is Test { ); synth = HypERC20(address(proxySynth)); - HypNativeScaled implementationNative = new HypNativeScaled( - scale, - address(environment.mailboxes(nativeDomain)) - ); + HypNativeCollateralScaled implementationNative = new HypNativeCollateralScaled( + scale, + address(environment.mailboxes(nativeDomain)) + ); TransparentUpgradeableProxy proxyNative = new TransparentUpgradeableProxy( address(implementationNative), address(9), abi.encodeWithSelector( - HypNative.initialize.selector, + HypNativeCollateral.initialize.selector, address(0), address(0), address(this) ) ); - native = HypNativeScaled(payable(address(proxyNative))); + native = HypNativeCollateralScaled(payable(address(proxyNative))); native.enrollRemoteRouter( synthDomain,