@pytest.mark.parametrize(
"n,m",
[
(1, 2), # Swap positions 2 and 3
(1, 16), # Swap positions 2 and 17
(1, 29), # Swap positions 2 and 30 (n + m = 30)
(5, 10), # Swap positions 6 and 11
(13, 17), # Swap positions 14 and 18 (n + m = 30)
(14, 16), # Swap positions 15 and 17 (n + m = 30)
],
ids=lambda x: f"{x}",
)
def test_exchange_basic(
n: int,
m: int,
pre: Alloc,
fork: Fork,
state_test: StateTestFiller,
) -> None:
"""Test EXCHANGE with various n and m values."""
sender = pre.fund_eoa()
# EXCHANGE with decoded (n, m) swaps position (n+1) with position (m+1)
stack_height = m + 1 # Need at least m+1 items
value_at_n_plus_1 = 0xAAAA
value_at_m_plus_1 = 0xBBBB
# Build stack with known values at swap positions (n+1) and (m+1)
code = Bytecode()
for i in range(stack_height):
# Stack position is 1-indexed from top, so i=0 is bottom
stack_pos = stack_height - i # Position from top (1-indexed)
if stack_pos == n + 1:
code += Op.PUSH2(value_at_n_plus_1)
elif stack_pos == m + 1:
code += Op.PUSH2(value_at_m_plus_1)
else:
code += Op.PUSH2(0x1000 + i)
# Pass n and m directly - encoder will handle encoding
code += Op.EXCHANGE[n, m]
# Store all stack values to verify the swap
for i in range(stack_height):
code += Op.PUSH1(i) + Op.SSTORE
code += Op.STOP
contract_address = pre.deploy_contract(code=code)
tx = Transaction(to=contract_address, sender=sender)
# Build expected storage
expected_storage = {}
for i in range(stack_height):
stack_pos = i + 1 # Position from top (1-indexed)
if stack_pos == n + 1:
expected_storage[i] = value_at_m_plus_1 # Now has value from m+1
elif stack_pos == m + 1:
expected_storage[i] = value_at_n_plus_1 # Now has value from n+1
else:
# Original value at this position
original_i = stack_height - stack_pos
expected_storage[i] = 0x1000 + original_i
post = {contract_address: Account(storage=expected_storage)}
state_test(pre=pre, post=post, tx=tx)