Skip to content

getters return structs instead of tuples#1004

Closed
prd-carapulse[bot] wants to merge 11 commits into
mainfrom
hermes/pr-1003-position-getter-struct
Closed

getters return structs instead of tuples#1004
prd-carapulse[bot] wants to merge 11 commits into
mainfrom
hermes/pr-1003-position-getter-struct

Conversation

@prd-carapulse

@prd-carapulse prd-carapulse Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Rename the backing position and marketState storage mappings to _position and _marketState, so that the getters can return a struct instead of a big tuple (confusing with so many returned variables)

Screenshot 2026-06-22 at 14 55 44

Size follow-up

  • forge build --force --sizes (Midnight 24,522 B, +54 B margin, with optimizer_runs = 700)

@prd-carapulse

prd-carapulse Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Added the same struct-getter treatment for marketState in this PR.

  • Renamed backing storage mapping to _marketState.
  • Added explicit marketState(bytes32) getter returning MarketState memory.
  • Updated contract/CVL/test references and existing marketState tests to use the struct.
  • Local verification: forge fmt --check ..., forge lint src test script certora --deny notes, forge build --force, focused getter/marketState tests.

Rename the backing storage mappings to _position and _marketState, then add explicit getters returning Position and MarketState structs.
@prd-carapulse prd-carapulse Bot force-pushed the hermes/pr-1003-position-getter-struct branch from 4b79e23 to ac3480d Compare June 22, 2026 11:59
@prd-carapulse

prd-carapulse Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Rebased this draft PR on the latest review-minor-findings head and folded in marketState.

Current branch now includes both struct getters:

  • position(bytes32,address) returns (Position memory) backed by _position
  • marketState(bytes32) returns (MarketState memory) backed by _marketState

Local verification passed after rebase:

  • forge fmt --check src/Midnight.sol src/interfaces/IMidnight.sol test/OtherFunctionsTest.sol certora/helpers/MidnightWrapper.sol
  • forge lint src test script certora --deny notes
  • forge build --force
  • forge test --force --match-path test/OtherFunctionsTest.sol --match-test 'test(Position|MarketState)GetterReturns.*Struct|testMarketStateGetter'

@QGarchery QGarchery changed the title refactor: return Position from position getter getter return structs instead of tuples Jun 22, 2026
@QGarchery QGarchery changed the title getter return structs instead of tuples getters return structs instead of tuples Jun 22, 2026
@QGarchery QGarchery marked this pull request as ready for review June 22, 2026 12:59
@QGarchery QGarchery requested review from MathisGD and adhusson June 22, 2026 12:59
Base automatically changed from review-minor-findings to main June 22, 2026 13:33
Comment thread src/Midnight.sol

function setMarketSettlementFee(bytes32 id, uint256 index, uint256 newSettlementFee) external {
MarketState storage _marketState = marketState[id];
MarketState storage state = _marketState[id];

@adhusson adhusson Jun 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
MarketState storage state = _marketState[id];
MarketState storage currentMarketState = _marketState[id];

not a strong take

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't like current too much, because it's not clear what "now" is

Comment thread src/Midnight.sol
_updatePosition(market, id, onBehalf);

Position storage _position = position[id][onBehalf];
Position storage userPosition = _position[id][onBehalf];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
Position storage userPosition = _position[id][onBehalf];
Position storage userPos = _position[id][onBehalf];

to match buyerPos sellerPos

Lower optimizer_runs from 800 to the round value 700 so Midnight stays below the EIP-170 runtime size limit.
@prd-carapulse

prd-carapulse Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Fixed the sizes failure by lowering optimizer_runs from 800 to the round value 700.

Evidence:

  • Failing CI had Midnight at 24,582 B runtime size, -6 B margin.
  • Local forge build --force --sizes now has Midnight at 24,522 B, +54 B margin.
  • forge lint src test script certora --deny notes passed.
  • Focused getter/marketState tests passed.

@QGarchery QGarchery marked this pull request as draft June 22, 2026 16:23
Comment thread src/Midnight.sol
/// OTHER VIEW FUNCTIONS ///

function position(bytes32 id, address user) external view returns (Position memory) {
return _position[id][user];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This returns a 128 elements which was skipped before by solidity.

  • It increases bytecode size by ~300 bytes because there is a yul loop added.
  • It costs 16k (!!) gas to decode for callers.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

remark from @MathisGD this is only the abi.decode gas, there is also the SLOAD cost to take into account...

Comment thread src/Midnight.sol
}

function marketState(bytes32 id) external view returns (MarketState memory) {
return _marketState[id];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This somehow generated assembly that takes ~350 more bytes than returning the tuple.

Bytecode in each The code for `marketState` in main vs in here:
case 0xead29b0b {
    if callvalue()
    {
        revert(mload(0x02e0), mload(0x02e0))
    }
    if slt(add(calldatasize(), not(3)), 32)
    {
        revert(mload(0x02e0), mload(0x02e0))
    }
    mstore(mload(0x02e0), calldataload(4))
    mstore(32, 1)
    let dataSlot_58 := keccak256(mload(0x02e0), 64)
    let _207 := sload(/** @src 0:47805:47817  "s.totalUnits" */ dataSlot_58)
    /// @src 0:13826:53814  "contract Midnight is IMidnight {..."
    let _208 := sload(/** @src 0:47857:47871  "s.withdrawable" */ add(dataSlot_58, /** @src 0:13826:53814  "contract Midnight is IMidnight {..." */ 1))
    let _209 := sload(/** @src 0:47920:47939  "s.settlementFeeCbp0" */ add(dataSlot_58, 2))
    /// @src 0:13826:53814  "contract Midnight is IMidnight {..."
    let memPos_33 := mload(64)
    mstore(memPos_33, and(_207, 0xffffffffffffffffffffffffffffffff))
    mstore(add(memPos_33, 32), shr(128, _207))
    mstore(add(memPos_33, 64), and(_208, 0xffffffffffffffffffffffffffffffff))
    mstore(add(memPos_33, 96), shr(128, _208))
    mstore(add(memPos_33, 128), and(_209, 0xffff))
    mstore(add(memPos_33, 160), and(shr(16, _209), 0xffff))
    mstore(add(memPos_33, 192), and(shr(32, _209), 0xffff))
    mstore(add(memPos_33, 224), and(shr(48, _209), 0xffff))
    mstore(add(memPos_33, 256), and(shr(64, _209), 0xffff))
    mstore(add(memPos_33, 288), and(shr(80, _209), 0xffff))
    mstore(add(memPos_33, 320), and(shr(96, _209), 0xffff))
    mstore(add(memPos_33, 352), and(/** @src 7:1229:1230  "4" */ shr(112, /** @src 0:13826:53814  "contract Midnight is IMidnight {..." */ _209), 0xffffffff))
    mstore(add(memPos_33, 384), and(shr(144, _209), 0xff))
    return(memPos_33, 416)
}
case 0xead29b0b {
    if callvalue()
    {
        revert(mload(0x02e0), mload(0x02e0))
    }
    if slt(add(calldatasize(), not(3)), 32)
    {
        revert(mload(0x02e0), mload(0x02e0))
    }
    let memPtr_6 := mload(64)
    finalize_allocation_46041(memPtr_6)
    mstore(memPtr_6, mload(0x02e0))
    mstore(add(memPtr_6, 32), mload(0x02e0))
    mstore(add(memPtr_6, 64), mload(0x02e0))
    mstore(add(memPtr_6, 96), mload(0x02e0))
    mstore(add(memPtr_6, 128), mload(0x02e0))
    mstore(add(memPtr_6, 160), mload(0x02e0))
    mstore(add(memPtr_6, 192), mload(0x02e0))
    mstore(add(memPtr_6, 224), mload(0x02e0))
    mstore(add(memPtr_6, 256), mload(0x02e0))
    mstore(add(memPtr_6, 288), mload(0x02e0))
    mstore(add(memPtr_6, 320), mload(0x02e0))
    mstore(add(memPtr_6, 352), mload(0x02e0))
    mstore(add(memPtr_6, 384), mload(0x02e0))
    mstore(mload(0x02e0), calldataload(4))
    mstore(32, 1)
    let dataSlot_58 := keccak256(mload(0x02e0), 64)
    let memPtr_7 := mload(64)
    finalize_allocation_46041(memPtr_7)
    let _207 := sload(dataSlot_58)
    let value_64 := and(_207, 0xffffffffffffffffffffffffffffffff)
    mstore(memPtr_7, value_64)
    let _208 := add(memPtr_7, 32)
    mstore(_208, shr(128, _207))
    let _209 := sload(add(dataSlot_58, 1))
    let _210 := add(memPtr_7, 64)
    mstore(_210, and(_209, 0xffffffffffffffffffffffffffffffff))
    let _211 := add(memPtr_7, 96)
    mstore(_211, shr(128, _209))
    let _212 := sload(add(dataSlot_58, 2))
    let _213 := add(memPtr_7, 128)
    mstore(_213, and(_212, 0xffff))
    let _214 := add(memPtr_7, 160)
    mstore(_214, and(shr(16, _212), 0xffff))
    let _215 := add(memPtr_7, 192)
    mstore(_215, and(shr(32, _212), 0xffff))
    let _216 := add(memPtr_7, 224)
    mstore(_216, and(shr(48, _212), 0xffff))
    let _217 := add(memPtr_7, 256)
    mstore(_217, and(shr(64, _212), 0xffff))
    let _218 := add(memPtr_7, 288)
    mstore(_218, and(shr(80, _212), 0xffff))
    let _219 := add(memPtr_7, 320)
    mstore(_219, and(shr(96, _212), 0xffff))
    let _220 := add(memPtr_7, 352)
    mstore(_220, and(/** @src 7:1229:1230  "4" */ shr(112, /** @src 0:13826:53087  "contract Midnight is IMidnight {..." */ _212), 0xffffffff))
    let _221 := add(memPtr_7, 384)
    mstore(_221, and(shr(144, _212), 0xff))
    let memPos_33 := mload(64)
    mstore(memPos_33, value_64)
    mstore(add(memPos_33, 32), and(mload(_208), 0xffffffffffffffffffffffffffffffff))
    mstore(add(memPos_33, 64), and(mload(_210), 0xffffffffffffffffffffffffffffffff))
    mstore(add(memPos_33, 96), and(mload(_211), 0xffffffffffffffffffffffffffffffff))
    mstore(add(memPos_33, 128), and(mload(_213), 0xffff))
    mstore(add(memPos_33, 160), and(mload(_214), 0xffff))
    mstore(add(memPos_33, 192), and(mload(_215), 0xffff))
    mstore(add(memPos_33, 224), and(mload(_216), 0xffff))
    mstore(add(memPos_33, 256), and(mload(_217), 0xffff))
    mstore(add(memPos_33, 288), and(mload(_218), 0xffff))
    mstore(add(memPos_33, 320), and(mload(_219), 0xffff))
    mstore(add(memPos_33, 352), and(mload(_220), 0xffffffff))
    mstore(add(memPos_33, 384), and(mload(_221), 0xff))
    return(memPos_33, 416)
}

@MathisGD MathisGD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

don't like that we need to rename stuff in Midnight etc...

@QGarchery

Copy link
Copy Markdown
Collaborator

closing since it's the getters are not expected to be called much anyway, and this way it stays simple without changing src

@QGarchery QGarchery closed this Jun 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants