Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ lcov.info
.env
.vscode
.password
.wake

broadcast/*/31337
deployments/**/31337.*
2 changes: 1 addition & 1 deletion snapshots/ERC7683Allocator_open.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"open_simpleOrder": "168301"
"open_simpleOrder": "168323"
}
2 changes: 1 addition & 1 deletion snapshots/ERC7683Allocator_openFor.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"openFor_simpleOrder_userHimself": "171750"
"openFor_simpleOrder_userHimself": "171772"
}
25 changes: 20 additions & 5 deletions src/allocators/lib/ERC7683AllocatorLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,26 @@ library ERC7683AllocatorLib {
// 0x40: OrderDataGasless.deposit

assembly ("memory-safe") {
let l := sub(orderData.length, 0x20)
let s := calldataload(add(orderData.offset, 0x20)) // Relative offset of `orderBytes` from `orderData.offset` and the `OrderData...` struct.
order := add(orderData.offset, add(s, 0x20)) // Add 0x20 since the OrderStruct is within the `OrderData...` struct
if shr(64, or(s, or(l, orderData.offset))) { revert(l, 0x00) }

// Enforce minimum length of 0x60 for: selector (outer), offsets (0x20, 0x40) and the additional input at 0x40
// Note: Here, orderData is already the bytes payload; we require at least 0x60 bytes to safely read up to +0x40
if lt(orderData.length, 0x60) {
// Empty revert to mirror prior behavior on malformed calldata
revert(0x00, 0x00)
}

// Load relative offset of nested Order within the OrderData struct
let s := calldataload(add(orderData.offset, 0x20))

// Bounds check: s must be >= 0x20 (points after the first slot) and s + 0x20 within orderData.length
// Also ensure no overflow on add(s, 0x20)
if or(lt(s, 0x20), gt(add(s, 0x20), orderData.length)) {
revert(0x00, 0x00)
}

// Compute pointer to nested Order (calldata pointer)
order := add(orderData.offset, add(s, 0x20))

// Read additional input (expires/deposit) at fixed position 0x40 in the OrderData
additionalInput := calldataload(add(orderData.offset, 0x40))
}
}
Expand Down
25 changes: 25 additions & 0 deletions test/ERC7683Allocator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ contract MockAllocator is GaslessCrossChainOrderData, OnChainCrossChainOrderData
}

contract ERC7683Allocator_open is MockAllocator {
function test_revert_ShortOrderData() public {
// Build a valid order then truncate orderData to force decode revert
IOriginSettler.OnchainCrossChainOrder memory onChainCrossChainOrder_ = _getOnChainCrossChainOrder();
bytes memory od = onChainCrossChainOrder_.orderData;
// truncate to less than 0x60
assembly ("memory-safe") {
mstore(od, 0x40)
}

onChainCrossChainOrder_.orderData = od;
vm.prank(user);
vm.expectRevert();
erc7683Allocator.open(onChainCrossChainOrder_);
}
function test_revert_InvalidOrderDataType() public {
// Order data type is invalid
bytes32 falseOrderDataType = keccak256('false');
Expand Down Expand Up @@ -198,6 +212,17 @@ contract ERC7683Allocator_open is MockAllocator {
}

contract ERC7683Allocator_openFor is MockAllocator {
function test_revert_ShortOrderData() public {
IOriginSettler.GaslessCrossChainOrder memory gasless = _getGaslessCrossChainOrder();
bytes memory od = gasless.orderData;
assembly ("memory-safe") {
mstore(od, 0x40)
}
gasless.orderData = od;
vm.prank(user);
vm.expectRevert();
erc7683Allocator.openFor(gasless, '', '');
}
function test_revert_InvalidOrderDataType() public {
// Order data type is invalid
bytes32 falseOrderDataType = keccak256('false');
Expand Down