diff --git a/solidity/contracts/hooks/OPL2ToL1Hook.sol b/solidity/contracts/hooks/OPL2ToL1Hook.sol index a8396b5de8..165289e57f 100644 --- a/solidity/contracts/hooks/OPL2ToL1Hook.sol +++ b/solidity/contracts/hooks/OPL2ToL1Hook.sol @@ -67,12 +67,8 @@ 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(metadataWithGasLimit, message); + metadata.msgValue(0) + childHook.quoteDispatch(metadata, message); } // ============ Internal functions ============ @@ -87,12 +83,9 @@ contract OPL2ToL1Hook is AbstractMessageIdAuthHook { (message.id(), metadata.msgValue(0)) ); - bytes memory metadataWithGasLimit = metadata.overrideGasLimit( - MIN_GAS_LIMIT - ); childHook.postDispatch{ - value: childHook.quoteDispatch(metadataWithGasLimit, message) - }(metadataWithGasLimit, message); + value: childHook.quoteDispatch(metadata, message) + }(metadata, message); l2Messenger.sendMessage{value: metadata.msgValue(0)}( TypeCasts.bytes32ToAddress(ism), payload, diff --git a/solidity/contracts/token/HypNative.sol b/solidity/contracts/token/HypNative.sol index 8a7ae06d72..c06fc624f4 100644 --- a/solidity/contracts/token/HypNative.sol +++ b/solidity/contracts/token/HypNative.sol @@ -1,57 +1,55 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 pragma solidity >=0.8.0; -/*@@@@@@@ @@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@@@ - @@@@@ HYPERLANE @@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ -@@@@@@@@@ @@@@@@@@*/ - -// ============ Internal Imports ============ import {TokenRouter} from "./libs/TokenRouter.sol"; -import {HypNativeCollateral} from "./HypNativeCollateral.sol"; -import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; /** - * @title HypNative + * @title Hyperlane Native Token Router that extends ERC20 with remote transfer functionality. * @author Abacus Works - * @notice This contract facilitates the transfer of value between chains using value transfer hooks + * @dev Supply on each chain is not constant but the aggregate supply across all chains is. */ -contract HypNative is HypNativeCollateral { - constructor(address _mailbox) HypNativeCollateral(_mailbox) {} +contract HypNative 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); - // ============ External Functions ============ + constructor(address _mailbox) TokenRouter(_mailbox) {} - /// @inheritdoc TokenRouter + /** + * @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) { - bytes calldata emptyBytes; - assembly { - emptyBytes.length := 0 - emptyBytes.offset := 0 - } - return - transferRemote( - _destination, - _recipient, - _amount, - emptyBytes, - address(hook) - ); + require(msg.value >= _amount, "Native: amount exceeds msg.value"); + uint256 _hookPayment = msg.value - _amount; + return _transferRemote(_destination, _recipient, _amount, _hookPayment); } /** * @inheritdoc TokenRouter - * @dev use _hook with caution, make sure that this hook can handle msg.value transfer using the metadata.msgValue() + * @dev uses (`msg.value` - `_amount`) as hook payment. */ function transferRemote( uint32 _destination, @@ -59,26 +57,50 @@ contract HypNative is HypNativeCollateral { uint256 _amount, bytes calldata _hookMetadata, address _hook - ) public payable virtual override returns (bytes32 messageId) { - uint256 quote = _GasRouter_quoteDispatch( - _destination, - _hookMetadata, - _hook - ); - - bytes memory hookMetadata = StandardHookMetadata.overrideMsgValue( - _hookMetadata, - _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, - _amount + quote, - hookMetadata, + _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/HypNativeCollateral.sol b/solidity/contracts/token/HypNativeCollateral.sol index ec6630d910..b11fa1030c 100644 --- a/solidity/contracts/token/HypNativeCollateral.sol +++ b/solidity/contracts/token/HypNativeCollateral.sol @@ -1,56 +1,57 @@ -// 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 {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {HypNative} from "./HypNative.sol"; +import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol"; /** - * @title Hyperlane Native Token Router that extends ERC20 with remote transfer functionality. + * @title HypNativeCollateral * @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 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); +contract HypNativeCollateral is HypNative { + constructor(address _mailbox) HypNative(_mailbox) {} - constructor(address _mailbox) TokenRouter(_mailbox) {} + // ============ External Functions ============ - /** - * @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. - */ + /// @inheritdoc TokenRouter 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); + bytes calldata emptyBytes; + assembly { + emptyBytes.length := 0 + emptyBytes.offset := 0 + } + return + transferRemote( + _destination, + _recipient, + _amount, + emptyBytes, + address(hook) + ); } /** * @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,50 +59,26 @@ contract HypNativeCollateral 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 + ); + + 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 - * @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/HypNativeCollateralScaled.sol b/solidity/contracts/token/extensions/HypNativeCollateralScaled.sol deleted file mode 100644 index c12117a6d5..0000000000 --- a/solidity/contracts/token/extensions/HypNativeCollateralScaled.sol +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity >=0.8.0; - -import {HypNativeCollateral} from "../HypNativeCollateral.sol"; -import {TokenRouter} from "../libs/TokenRouter.sol"; - -/** - * @title Hyperlane Native Token that scales native value by a fixed factor for consistency with other tokens. - * @dev The scale factor multiplies the `message.amount` to the local native token amount. - * Conversely, it divides the local native `msg.value` amount by `scale` to encode the `message.amount`. - * @author Abacus Works - */ -contract HypNativeCollateralScaled is HypNativeCollateral { - uint256 public immutable scale; - - constructor( - uint256 _scale, - address _mailbox - ) HypNativeCollateral(_mailbox) { - scale = _scale; - } - - /** - * @inheritdoc HypNativeCollateral - * @dev Sends scaled `msg.value` (divided by `scale`) to `_recipient`. - */ - function transferRemote( - uint32 _destination, - bytes32 _recipient, - uint256 _amount - ) external payable override returns (bytes32 messageId) { - require(msg.value >= _amount, "Native: amount exceeds msg.value"); - uint256 _hookPayment = msg.value - _amount; - uint256 _scaledAmount = _amount / scale; - return - _transferRemote( - _destination, - _recipient, - _scaledAmount, - _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 override returns (bytes32 messageId) { - require(msg.value >= _amount, "Native: amount exceeds msg.value"); - uint256 _hookPayment = msg.value - _amount; - uint256 _scaledAmount = _amount / scale; - return - _transferRemote( - _destination, - _recipient, - _scaledAmount, - _hookPayment, - _hookMetadata, - _hook - ); - } - - /** - * @dev Sends scaled `_amount` (multiplied by `scale`) to `_recipient`. - * @inheritdoc TokenRouter - */ - function _transferTo( - address _recipient, - uint256 _amount, - bytes calldata metadata // no metadata - ) internal override { - uint256 scaledAmount = _amount * scale; - HypNativeCollateral._transferTo(_recipient, scaledAmount, metadata); - } -} diff --git a/solidity/test/token/HypNative.t.sol b/solidity/test/token/HypNative.t.sol index eae4a8f21a..5d9004b41f 100644 --- a/solidity/test/token/HypNative.t.sol +++ b/solidity/test/token/HypNative.t.sol @@ -5,23 +5,23 @@ 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 {HypNativeCollateral} from "../../contracts/token/HypNativeCollateral.sol"; import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; import {TestIsm} from "../../contracts/test/TestIsm.sol"; -contract HypNativeTest is HypTokenTest { +contract HypNativeCollateralTest is HypTokenTest { using TypeCasts for address; - HypNative internal localValueRouter; - HypNative internal remoteValueRouter; + HypNativeCollateral internal localValueRouter; + HypNativeCollateral internal remoteValueRouter; TestPostDispatchHook internal valueHook; TestIsm internal ism; function setUp() public override { super.setUp(); - localValueRouter = new HypNative(address(localMailbox)); - remoteValueRouter = new HypNative(address(remoteMailbox)); + localValueRouter = new HypNativeCollateral(address(localMailbox)); + remoteValueRouter = new HypNativeCollateral(address(remoteMailbox)); localToken = TokenRouter(payable(address(localValueRouter))); remoteToken = HypERC20(payable(address(remoteValueRouter))); diff --git a/solidity/test/token/HypNativeCollateralScaled.t.sol b/solidity/test/token/HypNativeCollateralScaled.t.sol deleted file mode 100644 index d867caf7c6..0000000000 --- a/solidity/test/token/HypNativeCollateralScaled.t.sol +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity >=0.8.0; - -import "forge-std/Test.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - -import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; -import {HypNativeCollateralScaled} from "../../contracts/token/extensions/HypNativeCollateralScaled.sol"; -import {HypERC20} from "../../contracts/token/HypERC20.sol"; -import {HypNativeCollateral} from "../../contracts/token/HypNativeCollateral.sol"; -import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; -import {MockHyperlaneEnvironment} from "../../contracts/mock/MockHyperlaneEnvironment.sol"; - -contract HypNativeCollateralScaledTest is Test { - uint32 nativeDomain = 1; - uint32 synthDomain = 2; - - address internal constant ALICE = address(0x1); - - uint8 decimals = 9; - uint256 mintAmount = 123456789; - uint256 nativeDecimals = 18; - uint256 scale = 10 ** (nativeDecimals - decimals); - - event Donation(address indexed sender, uint256 amount); - event SentTransferRemote( - uint32 indexed destination, - bytes32 indexed recipient, - uint256 amount - ); - event ReceivedTransferRemote( - uint32 indexed origin, - bytes32 indexed recipient, - uint256 amount - ); - - HypNativeCollateralScaled native; - HypERC20 synth; - - MockHyperlaneEnvironment environment; - - function setUp() public { - environment = new MockHyperlaneEnvironment(synthDomain, nativeDomain); - - HypERC20 implementationSynth = new HypERC20( - decimals, - address(environment.mailboxes(synthDomain)) - ); - TransparentUpgradeableProxy proxySynth = new TransparentUpgradeableProxy( - address(implementationSynth), - address(9), - abi.encodeWithSelector( - HypERC20.initialize.selector, - mintAmount * (10 ** decimals), - "Zebec BSC Token", - "ZBC", - address(0), - address(0), - address(this) - ) - ); - synth = HypERC20(address(proxySynth)); - - HypNativeCollateralScaled implementationNative = new HypNativeCollateralScaled( - scale, - address(environment.mailboxes(nativeDomain)) - ); - TransparentUpgradeableProxy proxyNative = new TransparentUpgradeableProxy( - address(implementationNative), - address(9), - abi.encodeWithSelector( - HypNativeCollateral.initialize.selector, - address(0), - address(0), - address(this) - ) - ); - - native = HypNativeCollateralScaled(payable(address(proxyNative))); - - native.enrollRemoteRouter( - synthDomain, - TypeCasts.addressToBytes32(address(synth)) - ); - synth.enrollRemoteRouter( - nativeDomain, - TypeCasts.addressToBytes32(address(native)) - ); - } - - function test_constructor() public { - assertEq(native.scale(), scale); - } - - uint256 receivedValue; - - receive() external payable { - receivedValue = msg.value; - } - - function test_receive(uint256 amount) public { - vm.assume(amount < address(this).balance); - vm.expectEmit(true, true, true, true); - emit Donation(address(this), amount); - (bool success, bytes memory returnData) = address(native).call{ - value: amount - }(""); - assert(success); - assertEq(returnData.length, 0); - } - - function test_handle(uint256 amount) public { - vm.assume(amount <= mintAmount); - - uint256 synthAmount = amount * (10 ** decimals); - uint256 nativeAmount = amount * (10 ** nativeDecimals); - - vm.deal(address(native), nativeAmount); - - bytes32 recipient = TypeCasts.addressToBytes32(address(this)); - synth.transferRemote(nativeDomain, recipient, synthAmount); - - vm.expectEmit(true, true, true, true); - emit ReceivedTransferRemote(synthDomain, recipient, synthAmount); - environment.processNextPendingMessage(); - - assertEq(receivedValue, nativeAmount); - } - - function test_handle_reverts_whenAmountExceedsSupply( - uint256 amount - ) public { - vm.assume(amount <= mintAmount); - - bytes32 recipient = TypeCasts.addressToBytes32(address(this)); - synth.transferRemote(nativeDomain, recipient, amount); - - uint256 nativeValue = amount * scale; - vm.deal(address(native), nativeValue / 2); - - if (amount > 0) { - vm.expectRevert(bytes("Address: insufficient balance")); - } - environment.processNextPendingMessage(); - } - - function test_tranferRemote(uint256 amount) public { - vm.assume(amount <= mintAmount); - - uint256 nativeValue = amount * (10 ** nativeDecimals); - uint256 synthAmount = amount * (10 ** decimals); - address recipient = address(0xdeadbeef); - bytes32 bRecipient = TypeCasts.addressToBytes32(recipient); - - vm.deal(address(this), nativeValue); - vm.expectEmit(true, true, true, true); - emit SentTransferRemote(synthDomain, bRecipient, synthAmount); - native.transferRemote{value: nativeValue}( - synthDomain, - bRecipient, - nativeValue - ); - environment.processNextPendingMessageFromDestination(); - assertEq(synth.balanceOf(recipient), synthAmount); - } - - function testTransfer_withHookSpecified( - uint256 amount, - bytes calldata metadata - ) public { - vm.assume(amount <= mintAmount); - - uint256 nativeValue = amount * (10 ** nativeDecimals); - uint256 synthAmount = amount * (10 ** decimals); - address recipient = address(0xdeadbeef); - bytes32 bRecipient = TypeCasts.addressToBytes32(recipient); - - TestPostDispatchHook hook = new TestPostDispatchHook(); - - vm.deal(address(this), nativeValue); - vm.expectEmit(true, true, true, true); - emit SentTransferRemote(synthDomain, bRecipient, synthAmount); - native.transferRemote{value: nativeValue}( - synthDomain, - bRecipient, - nativeValue, - metadata, - address(hook) - ); - environment.processNextPendingMessageFromDestination(); - assertEq(synth.balanceOf(recipient), synthAmount); - } - - function test_transferRemote_reverts_whenAmountExceedsValue( - uint256 nativeValue - ) public { - vm.assume(nativeValue < address(this).balance); - - address recipient = address(0xdeadbeef); - bytes32 bRecipient = TypeCasts.addressToBytes32(recipient); - vm.expectRevert("Native: amount exceeds msg.value"); - native.transferRemote{value: nativeValue}( - synthDomain, - bRecipient, - nativeValue + 1 - ); - - vm.expectRevert("Native: amount exceeds msg.value"); - native.transferRemote{value: nativeValue}( - synthDomain, - bRecipient, - nativeValue + 1, - bytes(""), - address(0) - ); - } -} diff --git a/typescript/sdk/src/token/contracts.ts b/typescript/sdk/src/token/contracts.ts index 32a684cf30..40fc40d2d0 100644 --- a/typescript/sdk/src/token/contracts.ts +++ b/typescript/sdk/src/token/contracts.ts @@ -11,8 +11,8 @@ import { HypERC4626OwnerCollateral__factory, HypERC4626__factory, HypFiatToken__factory, - HypNativeCollateralScaled__factory, HypNativeCollateral__factory, + HypNativeScaled__factory, HypXERC20Lockbox__factory, HypXERC20__factory, } from '@hyperlane-xyz/core'; @@ -31,7 +31,7 @@ export const hypERC20contracts = { [TokenType.collateralVault]: 'HypERC4626OwnerCollateral', [TokenType.collateralVaultRebase]: 'HypERC4626Collateral', [TokenType.native]: 'HypNativeCollateral', - [TokenType.nativeScaled]: 'HypNativeCollateralScaled', + [TokenType.nativeScaled]: 'HypNativeScaled', }; export type HypERC20contracts = typeof hypERC20contracts; @@ -47,7 +47,7 @@ export const hypERC20factories = { [TokenType.XERC20]: new HypXERC20__factory(), [TokenType.XERC20Lockbox]: new HypXERC20Lockbox__factory(), [TokenType.native]: new HypNativeCollateral__factory(), - [TokenType.nativeScaled]: new HypNativeCollateralScaled__factory(), + [TokenType.nativeScaled]: new HypNativeScaled__factory(), }; export type HypERC20Factories = typeof hypERC20factories;