Skip to content

Commit e05b42b

Browse files
committed
Check mssg origin and settler when refunding
1 parent 5d4b3e1 commit e05b42b

File tree

8 files changed

+379
-44
lines changed

8 files changed

+379
-44
lines changed

solidity/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@
6969
"openOrder": "forge script script/OpenOrder.s.sol:OpenOrder -f $NETWORK --broadcast --verify --slow -vvv",
7070
"run:openOrder": "dotenv-run-script openOrder",
7171
"enrollRouter": "forge script script/EnrollRouter.s.sol:EnrollRouter -f $NETWORK --broadcast --slow -vvv",
72-
"run:enrollRouter": "dotenv-run-script enrollRouter"
72+
"run:enrollRouter": "dotenv-run-script enrollRouter",
73+
"upgradeSimple": "forge script script/UpgradeSimple.s.sol:UpgradeSimple -f $NETWORK --broadcast --slow -vvv",
74+
"run:upgradeSimple": "dotenv-run-script upgradeSimple"
7375
}
7476
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity >=0.8.25 <0.9.0;
3+
4+
import { Script } from "forge-std/Script.sol";
5+
import { console2 } from "forge-std/console2.sol";
6+
7+
import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
8+
import { ProxyAdmin } from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
9+
10+
import { Hyperlane7683 } from "../src/Hyperlane7683.sol";
11+
12+
/// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting
13+
contract UpgradeSimple is Script {
14+
function run() public {
15+
uint256 proxyAdminOwner = vm.envUint("PROXY_ADMIN_OWNER_PK");
16+
17+
address routerProxy = vm.envAddress("ROUTER");
18+
address proxyAdmin = vm.envAddress("PROXY_ADMIN");
19+
20+
vm.startBroadcast(proxyAdminOwner);
21+
22+
address newRouterImpl = deployImplementation();
23+
24+
ProxyAdmin(proxyAdmin).upgrade(ITransparentUpgradeableProxy(routerProxy), newRouterImpl);
25+
26+
vm.stopBroadcast();
27+
28+
// solhint-disable-next-line no-console
29+
console2.log("New Implementation:", newRouterImpl);
30+
}
31+
32+
function deployImplementation() internal returns (address routerImpl) {
33+
address mailbox = vm.envAddress("MAILBOX");
34+
address permit2 = vm.envAddress("PERMIT2");
35+
36+
return address(new Hyperlane7683(mailbox, permit2));
37+
}
38+
}

solidity/src/Base7683.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ abstract contract Base7683 is IOriginSettler, IDestinationSettler {
226226
* @param _orderId Unique order identifier for this order
227227
* @param _originData Data emitted on the origin to parameterize the fill
228228
* @param _fillerData Data provided by the filler to inform the fill or express their preferences. It should
229-
* contain the bytes32 encoded address of the receiver which is the used at settlement time
229+
* contain the bytes32 encoded address of the receiver which is used at settlement time
230230
*/
231231
function fill(bytes32 _orderId, bytes calldata _originData, bytes calldata _fillerData) external payable virtual {
232232
if (orderStatus[_orderId] != UNKNOWN) revert InvalidOrderStatus();

solidity/src/BasicSwap7683.sol

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ abstract contract BasicSwap7683 is Base7683 {
9898
override
9999
{
100100
// at this point we are sure all orders are filled, use the first order to get the originDomain
101-
// if some order differs on the originDomain ir can be re-settle later
101+
// if some order differs on the originDomain it can be re-settle later
102102
_dispatchSettle(OrderEncoder.decode(_ordersOriginData[0]).originDomain, _orderIds, _ordersFillerData);
103103
}
104104

@@ -108,7 +108,7 @@ abstract contract BasicSwap7683 is Base7683 {
108108
* @param _orderIds The IDs of the orders to refund.
109109
*/
110110
function _refundOrders(OnchainCrossChainOrder[] memory _orders, bytes32[] memory _orderIds) internal override {
111-
// at this point we are sure all orders are filled, use the first order to get the originDomain
111+
// at this point we are sure all orders are NOT filled, use the first order to get the originDomain
112112
// if some order differs on the originDomain ir can be re-refunded later
113113
_dispatchRefund(OrderEncoder.decode(_orders[0].orderData).originDomain, _orderIds);
114114
}
@@ -119,64 +119,110 @@ abstract contract BasicSwap7683 is Base7683 {
119119
* @param _orderIds The IDs of the orders to refund.
120120
*/
121121
function _refundOrders(GaslessCrossChainOrder[] memory _orders, bytes32[] memory _orderIds) internal override {
122-
// at this point we are sure all orders are filled, use the first order to get the originDomain
123-
// if some order differs on the originDomain ir can be re-refunded later
122+
// at this point we are sure all orders are NOT filled, use the first order to get the originDomain
123+
// if some order differs on the originDomain it can be re-refunded later
124124
_dispatchRefund(OrderEncoder.decode(_orders[0].orderData).originDomain, _orderIds);
125125
}
126126

127127
/**
128128
* @dev Handles settling an individual order, should be called by the inheriting contract when receiving a setting
129129
* instruction from a remote chain.
130+
* @param _messageOrigin The domain from which the message originates (unused in this implementation).
131+
* @param _messageSender The address of the sender on the origin domain (unused in this implementation).
130132
* @param _orderId The ID of the order to settle.
131133
* @param _receiver The receiver address (encoded as bytes32).
132134
*/
133-
function _handleSettleOrder(bytes32 _orderId, bytes32 _receiver) internal virtual {
134-
// check if the order is opened to ensure it belongs to this domain, skip otherwise
135-
if (orderStatus[_orderId] != OPENED) return;
136-
137-
(,bytes memory _orderData) = abi.decode(openOrders[_orderId], (bytes32, bytes));
138-
OrderData memory orderData = OrderEncoder.decode(_orderData);
135+
function _handleSettleOrder(
136+
uint32 _messageOrigin,
137+
bytes32 _messageSender,
138+
bytes32 _orderId,
139+
bytes32 _receiver
140+
) internal virtual {
141+
(
142+
bool validOpenOrder,
143+
OrderData memory orderData
144+
) = _isValidOpenOrder(_messageOrigin, _messageSender, _orderId);
145+
146+
if (!validOpenOrder) return;
139147

140148
orderStatus[_orderId] = SETTLED;
141149

142150
address receiver = TypeCasts.bytes32ToAddress(_receiver);
143151
address inputToken = TypeCasts.bytes32ToAddress(orderData.inputToken);
144152

145-
if (inputToken == address(0)) {
146-
Address.sendValue(payable(receiver), orderData.amountIn);
147-
} else {
148-
IERC20(inputToken).safeTransfer(receiver, orderData.amountIn);
149-
}
153+
_transferTokenOut(inputToken, receiver, orderData.amountIn);
150154

151155
emit Settled(_orderId, receiver);
152156
}
153157

154158
/**
155159
* @dev Handles refunding an individual order, should be called by the inheriting contract when receiving a
156160
* refunding instruction from a remote chain.
161+
* @param _messageOrigin The domain from which the message originates (unused in this implementation).
162+
* @param _messageSender The address of the sender on the origin domain (unused in this implementation).
157163
* @param _orderId The ID of the order to refund.
158164
*/
159-
function _handleRefundOrder(bytes32 _orderId) internal virtual {
160-
// check if the order is opened to ensure it belongs to this domain, skip otherwise
161-
if (orderStatus[_orderId] != OPENED) return;
165+
function _handleRefundOrder(uint32 _messageOrigin, bytes32 _messageSender, bytes32 _orderId) internal virtual {
166+
(
167+
bool validOpenOrder,
168+
OrderData memory orderData
169+
) = _isValidOpenOrder(_messageOrigin, _messageSender, _orderId);
162170

163-
(,bytes memory _orderData) = abi.decode(openOrders[_orderId], (bytes32, bytes));
164-
OrderData memory orderData = OrderEncoder.decode(_orderData);
171+
if (!validOpenOrder) return;
165172

166173
orderStatus[_orderId] = REFUNDED;
167174

168175
address orderSender = TypeCasts.bytes32ToAddress(orderData.sender);
169176
address inputToken = TypeCasts.bytes32ToAddress(orderData.inputToken);
170177

171-
if (inputToken == address(0)) {
172-
Address.sendValue(payable(orderSender), orderData.amountIn);
173-
} else {
174-
IERC20(inputToken).safeTransfer(orderSender, orderData.amountIn);
175-
}
178+
_transferTokenOut(inputToken, orderSender, orderData.amountIn);
176179

177180
emit Refunded(_orderId, orderSender);
178181
}
179182

183+
/**
184+
* @notice Validates an open order.
185+
* @dev Checks that the order is open and that its destination domain and settler match the provided parameters.
186+
* @param _messageOrigin The origin domain of the message.
187+
* @param _messageSender The sender identifier of the message.
188+
* @param _orderId The unique identifier of the order.
189+
* @return A boolean indicating if the order is valid, and the decoded OrderData structure.
190+
*/
191+
function _isValidOpenOrder(
192+
uint32 _messageOrigin,
193+
bytes32 _messageSender,
194+
bytes32 _orderId
195+
) internal virtual returns (bool, OrderData memory) {
196+
OrderData memory orderData;
197+
198+
// check if the order is opened to ensure it belongs to this domain, skip otherwise
199+
if (orderStatus[_orderId] != OPENED) return (false, orderData);
200+
201+
(,bytes memory _orderData) = abi.decode(openOrders[_orderId], (bytes32, bytes));
202+
orderData = OrderEncoder.decode(_orderData);
203+
204+
if (orderData.destinationDomain != _messageOrigin || orderData.destinationSettler != _messageSender)
205+
return (false, orderData);
206+
207+
return (true, orderData);
208+
}
209+
210+
/**
211+
* @notice Transfers tokens or ETH out of the contract.
212+
* @dev If _token is the zero address, transfers ETH using a safe method; otherwise, performs an ERC20 token
213+
* transfer.
214+
* @param _token The address of the token to transfer (use address(0) for ETH).
215+
* @param _to The recipient address.
216+
* @param _amount The amount of tokens or ETH to transfer.
217+
*/
218+
function _transferTokenOut(address _token, address _to, uint256 _amount) internal {
219+
if (_token == address(0)) {
220+
Address.sendValue(payable(_to), _amount);
221+
} else {
222+
IERC20(_token).safeTransfer(_to, _amount);
223+
}
224+
}
225+
180226
/**
181227
* @dev Gets the ID of a GaslessCrossChainOrder.
182228
* @param _order The GaslessCrossChainOrder to compute the ID for.

solidity/src/Hyperlane7683.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,19 +84,19 @@ contract Hyperlane7683 is GasRouter, BasicSwap7683 {
8484
/**
8585
* @notice Handles incoming messages.
8686
* @dev Decodes the message and processes settlement or refund operations accordingly.
87-
* _originDomain The domain from which the message originates (unused in this implementation).
88-
* _sender The address of the sender on the origin domain (unused in this implementation).
87+
* @param _messageOrigin The domain from which the message originates (unused in this implementation).
88+
* @param _messageSender The address of the sender on the origin domain (unused in this implementation).
8989
* @param _message The encoded message received via Hyperlane.
9090
*/
91-
function _handle(uint32, bytes32, bytes calldata _message) internal virtual override {
91+
function _handle(uint32 _messageOrigin, bytes32 _messageSender, bytes calldata _message) internal virtual override {
9292
(bool _settle, bytes32[] memory _orderIds, bytes[] memory _ordersFillerData) =
9393
Hyperlane7683Message.decode(_message);
9494

9595
for (uint256 i = 0; i < _orderIds.length; i++) {
9696
if (_settle) {
97-
_handleSettleOrder(_orderIds[i], abi.decode(_ordersFillerData[i], (bytes32)));
97+
_handleSettleOrder(_messageOrigin, _messageSender, _orderIds[i], abi.decode(_ordersFillerData[i], (bytes32)));
9898
} else {
99-
_handleRefundOrder(_orderIds[i]);
99+
_handleRefundOrder(_messageOrigin, _messageSender, _orderIds[i]);
100100
}
101101
}
102102
}

0 commit comments

Comments
 (0)