Skip to content

Commit

Permalink
Add quoteDispatch to IPostDispatchHook (#2660)
Browse files Browse the repository at this point in the history
- `quoteDispatch` added to `IPostDispatchHook` interface and the
relevant hooks:
     - StaticProtocolFee
     - OPStackHook
     - InterchainGasPaymaster
     - MerkleTreeHook
     - PausableHook (no tests)
     - DomainRoutingHook (no tests)
     - ConfigFallbackDomainRoutingHook
     - ConfigurableDomainRoutingHook (no tests)

- `expectEmit` -> `expectCall`

- Quote in V3

Yes

Uint tests
  • Loading branch information
aroralanuk authored and yorhodes committed Sep 13, 2023
1 parent 6a32287 commit 2b7ecfc
Show file tree
Hide file tree
Showing 15 changed files with 241 additions and 33 deletions.
34 changes: 27 additions & 7 deletions solidity/contracts/hooks/ConfigFallbackDomainRoutingHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,18 @@ contract ConfigFallbackDomainRoutingHook is IPostDispatchHook {
payable
override
{
IPostDispatchHook configuredHook = customHooks[message.senderAddress()][
message.destination()
][message.recipient()];
if (address(configuredHook) == address(0)) {
configuredHook = mailbox.defaultHook();
}
_getConfiguredHook(message).postDispatch{value: msg.value}(
metadata,
message
);
}

configuredHook.postDispatch{value: msg.value}(metadata, message);
function quoteDispatch(bytes calldata metadata, bytes calldata message)
public
view
returns (uint256)
{
return _getConfiguredHook(message).quoteDispatch(metadata, message);
}

function setHook(
Expand All @@ -52,4 +56,20 @@ contract ConfigFallbackDomainRoutingHook is IPostDispatchHook {
) external {
customHooks[msg.sender][destinationDomain][recipient] = hook;
}

// ============ Internal Functions ============

function _getConfiguredHook(bytes calldata message)
internal
view
returns (IPostDispatchHook)
{
IPostDispatchHook configuredHook = customHooks[message.senderAddress()][
message.destination()
][message.recipient()];
if (address(configuredHook) == address(0)) {
configuredHook = mailbox.defaultHook();
}
return configuredHook;
}
}
22 changes: 21 additions & 1 deletion solidity/contracts/hooks/DomainRoutingHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,29 @@ contract DomainRoutingHook is IPostDispatchHook, Ownable {
virtual
override
{
hooks[message.destination()].postDispatch{value: msg.value}(
_getConfiguredHook(message).postDispatch{value: msg.value}(
metadata,
message
);
}

function quoteDispatch(bytes calldata metadata, bytes calldata message)
public
view
virtual
override
returns (uint256)
{
return _getConfiguredHook(message).quoteDispatch(metadata, message);
}

// ============ Internal Functions ============

function _getConfiguredHook(bytes calldata message)
internal
view
returns (IPostDispatchHook)
{
return hooks[message.destination()];
}
}
9 changes: 9 additions & 0 deletions solidity/contracts/hooks/ERC5164Hook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ contract ERC5164Hook is AbstractMessageIdAuthHook {
dispatcher = IMessageDispatcher(_dispatcher);
}

function quoteDispatch(bytes calldata, bytes calldata)
external
pure
override
returns (uint256)
{
revert("not implemented");
}

function _sendMessageId(
bytes calldata, /* metadata */
bytes memory payload
Expand Down
7 changes: 7 additions & 0 deletions solidity/contracts/hooks/MerkleTreeHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,11 @@ contract MerkleTreeHook is IPostDispatchHook, MailboxClient {
require(isLatestDispatched(id), "message not dispatching");
_tree.insert(id);
}

function quoteDispatch(
bytes calldata, /*metadata*/
bytes calldata /*message*/
) external pure override returns (uint256) {
return 0;
}
}
12 changes: 12 additions & 0 deletions solidity/contracts/hooks/OPStackHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ contract OPStackHook is AbstractMessageIdAuthHook {
l1Messenger = ICrossDomainMessenger(_messenger);
}

// ============ External functions ============

/// @inheritdoc IPostDispatchHook
function quoteDispatch(bytes calldata, bytes calldata)
external
pure
override
returns (uint256)
{
return 0; // gas subsidized by the L2
}

// ============ Internal functions ============

/// @inheritdoc AbstractMessageIdAuthHook
Expand Down
10 changes: 10 additions & 0 deletions solidity/contracts/hooks/PausableHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ contract PausableHook is IPostDispatchHook, Ownable, Pausable {
whenNotPaused
{}

/// @inheritdoc IPostDispatchHook
function quoteDispatch(bytes calldata, bytes calldata)
external
pure
override
returns (uint256)
{
return 0;
}

function pause() external onlyOwner {
_pause();
}
Expand Down
10 changes: 10 additions & 0 deletions solidity/contracts/hooks/StaticProtocolFee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ contract StaticProtocolFee is IPostDispatchHook, Ownable {
if (refund > 0) payable(message.senderAddress()).sendValue(refund);
}

/// @inheritdoc IPostDispatchHook
function quoteDispatch(bytes calldata, bytes calldata)
external
view
override
returns (uint256)
{
return protocolFee;
}

/**
* @notice Sets the protocol fee.
* @param _protocolFee The new protocol fee.
Expand Down
40 changes: 29 additions & 11 deletions solidity/contracts/igps/InterchainGasPaymaster.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/

// ============ Internal Imports ============
import {Message} from "../libs/Message.sol";
import {IGPMetadata} from "../libs/hooks/IGPMetadata.sol";
Expand Down Expand Up @@ -86,20 +98,26 @@ contract InterchainGasPaymaster is
payable
override
{
uint256 gasLimit;
address refundAddress;
if (metadata.length == 0) {
gasLimit = DEFAULT_GAS_USAGE;
refundAddress = message.senderAddress();
} else {
gasLimit = metadata.gasLimit();
refundAddress = metadata.refundAddress();
if (refundAddress == address(0))
refundAddress = message.senderAddress();
}
uint256 gasLimit = metadata.gasLimit(DEFAULT_GAS_USAGE);
address refundAddress = metadata.refundAddress(message.senderAddress());
payForGas(message.id(), message.destination(), gasLimit, refundAddress);
}

/**
* @notice Quote gas payment for a hook call.
* @param metadata The metadata as gasConfig.
* @param message The message to pay for.
*/
function quoteDispatch(bytes calldata metadata, bytes calldata message)
external
view
override
returns (uint256)
{
uint256 gasLimit = metadata.gasLimit(DEFAULT_GAS_USAGE);
return quoteGasPayment(message.destination(), gasLimit);
}

/**
* @notice Transfers the entire native token balance to the beneficiary.
* @dev The beneficiary must be able to receive native tokens.
Expand Down
16 changes: 16 additions & 0 deletions solidity/contracts/interfaces/hooks/IPostDispatchHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,23 @@
pragma solidity >=0.8.0;

interface IPostDispatchHook {
/**
* @notice Post action afte a message is dispatched via the Mailbox
* @param metadata The metadata required for the hook
* @param message The message passed from the Mailbox.dispatch() call
*/
function postDispatch(bytes calldata metadata, bytes calldata message)
external
payable;

/**
* @notice Estimate the amount of gas consumed by the postDispatch call
* @param metadata The metadata required for the hook
* @param message The message passed from the Mailbox.dispatch() call
* @return Gas quote for the postDispatch call
*/
function quoteDispatch(bytes calldata metadata, bytes calldata message)
external
view
returns (uint256);
}
15 changes: 11 additions & 4 deletions solidity/contracts/libs/hooks/IGPMetadata.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ library IGPMetadata {
* @param _metadata ABI encoded IGP hook metadata.
* @return Gas limit for the message as uint256.
*/
function gasLimit(bytes calldata _metadata)
function gasLimit(bytes calldata _metadata, uint256 _default)
internal
pure
returns (uint256)
{
if (_metadata.length < GAS_LIMIT_OFFSET + 32) return _default;
return
uint256(bytes32(_metadata[GAS_LIMIT_OFFSET:GAS_LIMIT_OFFSET + 32]));
}
Expand All @@ -42,17 +43,23 @@ library IGPMetadata {
* @param _metadata ABI encoded IGP hook metadata.
* @return Refund address for the message as address.
*/
function refundAddress(bytes calldata _metadata)
function refundAddress(bytes calldata _metadata, address _default)
internal
pure
returns (address)
{
return
address(
address _refundAddress;
if (_metadata.length < REFUND_ADDRESS_OFFSET + 20) {
_refundAddress = _default;
} else {
_refundAddress = address(
bytes20(
_metadata[REFUND_ADDRESS_OFFSET:REFUND_ADDRESS_OFFSET + 20]
)
);
if (_refundAddress == address(0)) _refundAddress = _default;
}
return _refundAddress;
}

/**
Expand Down
12 changes: 9 additions & 3 deletions solidity/contracts/test/TestPostDispatchHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ pragma solidity >=0.8.0;
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";

contract TestPostDispatchHook is IPostDispatchHook {
event PostDispatchHookCalled();
uint256 public mockGasQuote = 25000;

function postDispatch(
bytes calldata, /*metadata*/
bytes calldata /*message*/
) external payable override {
// test - emit event
emit PostDispatchHookCalled();
// test - empty
}

function quoteDispatch(
bytes calldata, /*metadata*/
bytes calldata /*message*/
) external view override returns (uint256) {
return mockGasQuote;
}
}
39 changes: 34 additions & 5 deletions solidity/test/hooks/FallbackDomainRoutingHook.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,51 @@ contract FallbackDomainRoutingHookTest is Test {
mailbox.setDefaultHook(address(mailboxDefaultHook));
}

function test_postDispatchHook_configured() public payable {
/* ============ hook.quoteDispatch ============ */

function test_quoteDispatchHook_configured() public {
fallbackHook.setHook(
TEST_DESTINATION_DOMAIN,
address(testRecipient).addressToBytes32(),
configuredTestHook
);

vm.expectEmit(false, false, false, false, address(configuredTestHook));
emit PostDispatchHookCalled();
vm.expectCall(
address(configuredTestHook),
abi.encodeCall(configuredTestHook.quoteDispatch, ("", testMessage))
);
assertEq(fallbackHook.quoteDispatch("", testMessage), 25000);
}

function test_quoteDispatch_default() public payable {
vm.expectCall(
address(mailboxDefaultHook),
abi.encodeCall(mailboxDefaultHook.quoteDispatch, ("", testMessage))
);
fallbackHook.quoteDispatch("", testMessage);
}

/* ============ hook.postDispatch ============ */

function test_postDispatchHook_configured() public payable {
fallbackHook.setHook(
TEST_DESTINATION_DOMAIN,
address(testRecipient).addressToBytes32(),
configuredTestHook
);

vm.expectCall(
address(configuredTestHook),
abi.encodeCall(configuredTestHook.postDispatch, ("", testMessage))
);
fallbackHook.postDispatch{value: msg.value}("", testMessage);
}

function test_postDispatch_default() public payable {
vm.expectEmit(false, false, false, false, address(mailboxDefaultHook));
emit PostDispatchHookCalled();
vm.expectCall(
address(mailboxDefaultHook),
abi.encodeCall(mailboxDefaultHook.postDispatch, ("", testMessage))
);

fallbackHook.postDispatch{value: msg.value}("", testMessage);
}
Expand Down
4 changes: 4 additions & 0 deletions solidity/test/hooks/StaticProtocolFee.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ contract StaticProtocolFeeTest is Test {
assertEq(fees.beneficiary(), bob);
}

function testQuoteDispatch() public {
assertEq(fees.quoteDispatch("", testMessage), 1e15);
}

function testFuzz_postDispatch_inusfficientFees(
uint256 feeRequired,
uint256 feeSent
Expand Down
Loading

0 comments on commit 2b7ecfc

Please sign in to comment.