fix: native value covers message value in HypNative (#4526)

### Description

`HypNative.transferRemote` overload checks that the native `msg.value`
deposited covers the warp message `amount` in addition to checking the
mailbox post-dispatch fee is covered.

### Backward compatibility

No, applies to net new `HypNative` and `HypNativeScaled` deployments

### Testing

Unit Tests
pull/4530/head
Yorke Rhodes 2 months ago committed by GitHub
parent 52c041862e
commit eb5afcf3eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      .changeset/curvy-elephants-breathe.md
  2. 24
      solidity/contracts/token/HypNative.sol
  3. 25
      solidity/contracts/token/extensions/HypNativeScaled.sol
  4. 46
      solidity/test/token/HypERC20.t.sol
  5. 41
      solidity/test/token/HypNativeScaled.t.sol

@ -0,0 +1,5 @@
---
'@hyperlane-xyz/core': patch
---
Patch `HypNative` with hook overrides `transferRemote` behavior

@ -48,6 +48,30 @@ contract HypNative is TokenRouter {
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) {

@ -38,6 +38,31 @@ contract HypNativeScaled is HypNative {
);
}
/**
* @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

@ -49,7 +49,7 @@ abstract contract HypTokenTest is Test {
uint256 internal constant TOTAL_SUPPLY = 1_000_000e18;
uint256 internal REQUIRED_VALUE; // initialized in setUp
uint256 internal constant GAS_LIMIT = 10_000;
uint256 internal constant TRANSFER_AMT = 100e18;
uint256 internal TRANSFER_AMT = 100e18;
string internal constant NAME = "HyperlaneInu";
string internal constant SYMBOL = "HYP";
address internal constant ALICE = address(0x1);
@ -640,7 +640,25 @@ contract HypNativeTest is HypTokenTest {
_enrollRemoteTokenRouter();
}
function testInitialize_revert_ifAlreadyInitialized() public {}
function testTransfer_withHookSpecified(
uint256 fee,
bytes calldata metadata
) public override {
TestPostDispatchHook hook = new TestPostDispatchHook();
hook.setFee(fee);
uint256 value = REQUIRED_VALUE + TRANSFER_AMT;
vm.prank(ALICE);
primaryToken.approve(address(localToken), TRANSFER_AMT);
bytes32 messageId = _performRemoteTransferWithHook(
value,
TRANSFER_AMT,
address(hook),
metadata
);
assertTrue(hook.messageDispatched(messageId));
}
function testRemoteTransfer() public {
_performRemoteTransferWithEmit(
@ -668,4 +686,28 @@ contract HypNativeTest is HypTokenTest {
TRANSFER_AMT + GAS_LIMIT * igp.gasPrice()
);
}
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");
nativeToken.transferRemote{value: nativeValue}(
DESTINATION,
bRecipient,
nativeValue + 1
);
vm.expectRevert("Native: amount exceeds msg.value");
nativeToken.transferRemote{value: nativeValue}(
DESTINATION,
bRecipient,
nativeValue + 1,
bytes(""),
address(0)
);
}
}

@ -4,6 +4,7 @@ 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 {HypNativeScaled} from "../../contracts/token/extensions/HypNativeScaled.sol";
import {HypERC20} from "../../contracts/token/HypERC20.sol";
import {HypNative} from "../../contracts/token/HypNative.sol";
@ -14,6 +15,8 @@ contract HypNativeScaledTest is Test {
uint32 nativeDomain = 1;
uint32 synthDomain = 2;
address internal constant ALICE = address(0x1);
uint8 decimals = 9;
uint256 mintAmount = 123456789;
uint256 nativeDecimals = 18;
@ -149,7 +152,7 @@ contract HypNativeScaledTest is Test {
address recipient = address(0xdeadbeef);
bytes32 bRecipient = TypeCasts.addressToBytes32(recipient);
vm.assume(nativeValue < address(this).balance);
vm.deal(address(this), nativeValue);
vm.expectEmit(true, true, true, true);
emit SentTransferRemote(synthDomain, bRecipient, synthAmount);
native.transferRemote{value: nativeValue}(
@ -161,6 +164,33 @@ contract HypNativeScaledTest is Test {
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 {
@ -174,5 +204,14 @@ contract HypNativeScaledTest is Test {
bRecipient,
nativeValue + 1
);
vm.expectRevert("Native: amount exceeds msg.value");
native.transferRemote{value: nativeValue}(
synthDomain,
bRecipient,
nativeValue + 1,
bytes(""),
address(0)
);
}
}

Loading…
Cancel
Save