// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.0; /*@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@ HYPERLANE @@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@*/ // ============ Internal Imports ============ import {StandardHookMetadata} from "../libs/StandardHookMetadata.sol"; import {Message} from "../../libs/Message.sol"; import {TypeCasts} from "../../libs/TypeCasts.sol"; import {AbstractPostDispatchHook} from "../libs/AbstractPostDispatchHook.sol"; import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol"; import {MetaProxy} from "../../libs/MetaProxy.sol"; // ============ External Imports ============ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; contract StaticAggregationHook is AbstractPostDispatchHook { using Message for bytes; using TypeCasts for bytes32; using StandardHookMetadata for bytes; using Address for address payable; // ============ External functions ============ /// @inheritdoc IPostDispatchHook function hookType() external pure override returns (uint8) { return uint8(IPostDispatchHook.HookTypes.AGGREGATION); } /// @inheritdoc AbstractPostDispatchHook /// @dev When metadata specifies a non-zero feeToken, native value is NOT /// forwarded to children. Each child that supports ERC20 fees is responsible /// for pulling tokens via transferFrom in its own _postDispatch. Any native /// value received is refunded to the refund address. function _postDispatch( bytes calldata metadata, bytes calldata message ) internal override { address[] memory _hooks = hooks(message); uint256 count = _hooks.length; bool isNativeFee = metadata.feeToken(address(0)) == address(0); uint256 valueRemaining = msg.value; for (uint256 i = 0; i < count; i++) { uint256 quote = isNativeFee ? IPostDispatchHook(_hooks[i]).quoteDispatch(metadata, message) : 0; require( valueRemaining >= quote, "StaticAggregationHook: insufficient value" ); IPostDispatchHook(_hooks[i]).postDispatch{value: quote}( metadata, message ); valueRemaining -= quote; } if (valueRemaining > 0) { payable(metadata.refundAddress(message.senderAddress())).sendValue( valueRemaining ); } } /// @inheritdoc AbstractPostDispatchHook function _quoteDispatch( bytes calldata metadata, bytes calldata message ) internal view override returns (uint256) { address[] memory _hooks = hooks(message); uint256 count = _hooks.length; uint256 total = 0; for (uint256 i = 0; i < count; i++) { total += IPostDispatchHook(_hooks[i]).quoteDispatch( metadata, message ); } return total; } function hooks(bytes calldata) public pure returns (address[] memory) { return abi.decode(MetaProxy.metadata(), (address[])); } }