Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(contracts): ValueRouter #4814

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3e2f9e3
refactor: implement outbound and inbound warp route amount transforms…
yorhodes Oct 22, 2024
a51b288
fix(contracts): check for sufficient msgValue for `AggregationHook` …
aroralanuk Oct 29, 2024
7215577
init
aroralanuk Nov 5, 2024
4556e24
Merge branch 'main' into kunal/value-router
aroralanuk Nov 7, 2024
99647fe
revert
aroralanuk Nov 7, 2024
6876905
more
aroralanuk Nov 7, 2024
2514043
more
aroralanuk Nov 7, 2024
670912b
fixes
aroralanuk Nov 8, 2024
3fec6a0
rename
aroralanuk Nov 11, 2024
1e54bf6
account.balance
aroralanuk Nov 11, 2024
d8d4d2f
resolve sdk imports
aroralanuk Nov 20, 2024
e302e9c
Merge branch 'main' into kunal/value-router
aroralanuk Nov 20, 2024
ecd1e90
override if more
aroralanuk Nov 27, 2024
8fb963c
testpostdispatchhook
aroralanuk Nov 28, 2024
bee6d22
rm quote fetch
aroralanuk Nov 28, 2024
bab9928
Merge branch 'main' into kunal/value-router
aroralanuk Dec 10, 2024
139ba55
rm extra insuff check
aroralanuk Dec 13, 2024
9fcc4d9
rm comment
aroralanuk Dec 13, 2024
41ff083
inherit from hypnativecollateral
aroralanuk Dec 13, 2024
9c38a7a
interchange names
aroralanuk Dec 13, 2024
5c1f2e8
more
aroralanuk Dec 13, 2024
ec225b4
revert
aroralanuk Dec 13, 2024
d996b3a
Merge branch 'main' into kunal/value-router
aroralanuk Dec 13, 2024
4f04099
rm override if higher
aroralanuk Dec 13, 2024
7cf2d75
docs(changeset): Added a new router HypNativeCollateral with a unifie…
aroralanuk Dec 13, 2024
b541129
revrt
aroralanuk Dec 13, 2024
13e79af
Update long-llamas-fly.md
aroralanuk Dec 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions solidity/contracts/hooks/OPL2ToL1Hook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,12 @@
bytes calldata metadata,
bytes calldata message
) internal view override returns (uint256) {
bytes memory metadataWithGasLimit = metadata.overrideGasLimit(
Fixed Show fixed Hide fixed
MIN_GAS_LIMIT
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
);
return
metadata.msgValue(0) + childHook.quoteDispatch(metadata, message);
metadata.msgValue(0) +
childHook.quoteDispatch(metadataWithGasLimit, message);
}

// ============ Internal functions ============
Expand All @@ -83,9 +87,12 @@
(message.id(), metadata.msgValue(0))
);

bytes memory metadataWithGasLimit = metadata.overrideGasLimit(
Fixed Show fixed Hide fixed
MIN_GAS_LIMIT
);
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
childHook.postDispatch{
value: childHook.quoteDispatch(metadata, message)
}(metadata, message);
value: childHook.quoteDispatch(metadataWithGasLimit, message)
Fixed Show fixed Hide fixed
}(metadataWithGasLimit, message);
l2Messenger.sendMessage{value: metadata.msgValue(0)}(
TypeCasts.bytes32ToAddress(ism),
payload,
Expand Down
41 changes: 41 additions & 0 deletions solidity/contracts/hooks/libs/StandardHookMetadata.sol
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,45 @@ 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(
Fixed Show fixed Hide fixed
_msgValue,
gasLimit(_metadata, 0),
refundAddress(_metadata, msg.sender),
Comment on lines +182 to +183
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont understand the 0 and msg.sender here
this will also memcopy for every field iiuc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are just default values in case the specific part of the metadata is null
true but memcopy is cheap

Copy link
Member

@yorhodes yorhodes Nov 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 gas limit and msg.sender for refund address as defaults does not make sense to me

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what would you have in their place?

getCustomMetadata(_metadata)
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
);
}

/**
* @notice Overrides the gas limit in the metadata if _gasLimit is higher than the current gas limit.
* @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) {
if (gasLimit(_metadata, 0) < _gasLimit) {
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
return
formatMetadata(
Fixed Show fixed Hide fixed
msgValue(_metadata, 0),
_gasLimit,
refundAddress(_metadata, msg.sender),
getCustomMetadata(_metadata)
Fixed Show fixed Hide fixed
);
}
return _metadata;
}
}
121 changes: 83 additions & 38 deletions solidity/contracts/token/HypNative.sol
Original file line number Diff line number Diff line change
@@ -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 {
/**
Expand All @@ -17,90 +32,120 @@
* @param amount The amount of native tokens donated.
*/
event Donation(address indexed sender, uint256 amount);
// ============ Errors ============

error InsufficientValue(uint256 requiredValue, uint256 providedValue);
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved

constructor(address _mailbox) TokenRouter(_mailbox) {}

// ============ Initialization ============
Dismissed Show dismissed Hide dismissed

/**
* @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()
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
*/
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;
) public payable virtual override returns (bytes32 messageId) {
uint256 quote = _GasRouter_quoteDispatch(
_destination,
_hookMetadata,
_hook
);
require(msg.value >= _amount + quote, "HypNative: insufficient value");
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved

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

Check warning on line 106 in solidity/contracts/token/HypNative.sol

View check run for this annotation

Codecov / codecov/patch

solidity/contracts/token/HypNative.sol#L105-L106

Added lines #L105 - L106 were not covered by tests
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do not use inline assembly anywhere else, please do not introduce it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need a way to do bytes memory to calldata

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
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @dev Sends `_amount` of native token to `_recipient` balance.
* @inheritdoc TokenRouter
* @dev Sends the value to the recipient
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
*/
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(

Check warning on line 143 in solidity/contracts/token/HypNative.sol

View check run for this annotation

Codecov / codecov/patch

solidity/contracts/token/HypNative.sol#L143

Added line #L143 was not covered by tests
address _account
) external view override returns (uint256) {
return _account.balance;

Check warning on line 146 in solidity/contracts/token/HypNative.sol

View check run for this annotation

Codecov / codecov/patch

solidity/contracts/token/HypNative.sol#L146

Added line #L146 was not covered by tests
}

aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
receive() external payable {
emit Donation(msg.sender, msg.value);
}
Expand Down
107 changes: 107 additions & 0 deletions solidity/contracts/token/HypNativeCollateral.sol
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0;
Fixed Show fixed Hide fixed

Check notice

Code scanning / Olympix Integrated Security

Using an unbounded pragma for Solidity version may be unsafe if future versions introduce breaking changes. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unbounded-pragma Low

Using an unbounded pragma for Solidity version may be unsafe if future versions introduce breaking changes. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unbounded-pragma

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 {
Fixed Show fixed Hide fixed
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved
/**
* @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);
Fixed Show fixed Hide fixed

constructor(address _mailbox) TokenRouter(_mailbox) {}
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed

/**
* @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(
Fixed Show fixed Hide fixed
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(
Dismissed Show dismissed Hide dismissed
uint32 _destination,
bytes32 _recipient,
uint256 _amount
) external payable virtual override returns (bytes32 messageId) {
require(msg.value >= _amount, "Native: amount exceeds msg.value");
Fixed Show fixed Hide fixed
uint256 _hookPayment = msg.value - _amount;
return _transferRemote(_destination, _recipient, _amount, _hookPayment);
}

/**
* @inheritdoc TokenRouter
* @dev uses (`msg.value` - `_amount`) as hook payment.
*/
function transferRemote(
Dismissed Show dismissed Hide dismissed
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");
Fixed Show fixed Hide fixed
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 {
Fixed Show fixed Hide fixed
emit Donation(msg.sender, msg.value);
}
}
Loading
Loading