Skip to content

Commit e4d9efc

Browse files
authored
New test_many_accounts_balance_change (#10)
Co-Authored-By: Claude claude-opus-4-5-20251101
1 parent dbc48e9 commit e4d9efc

1 file changed

Lines changed: 93 additions & 0 deletions

File tree

tests/monad_eight/reserve_balance/test_transfers.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from enum import Enum, auto, unique
6+
from typing import Callable
67

78
import pytest
89
from execution_testing import (
@@ -1629,3 +1630,95 @@ def test_two_step_balance_change(
16291630
post={contract_address: Account(storage=storage)},
16301631
blocks=[Block(txs=[tx])],
16311632
)
1633+
1634+
1635+
@pytest.mark.parametrize(
1636+
"violation_index_fn",
1637+
[
1638+
pytest.param(lambda _n: None, id="no_violation"),
1639+
pytest.param(lambda _n: 0, id="first_violates"),
1640+
pytest.param(lambda n: n // 2, id="middle_violates"),
1641+
pytest.param(lambda n: n - 1, id="last_violates"),
1642+
],
1643+
)
1644+
def test_many_accounts_balance_change(
1645+
blockchain_test: BlockchainTestFiller,
1646+
pre: Alloc,
1647+
violation_index_fn: Callable[[int], int | None],
1648+
fork: Fork,
1649+
) -> None:
1650+
"""
1651+
Test reserve balance with many accounts having their balance changed.
1652+
1653+
A single wallet is deployed and many EOAs delegate to it. Each EOA sends
1654+
a transfer when the wallet code executes. The violation_index parameter
1655+
determines which account (if any) ends up in violation.
1656+
1657+
The number of accounts to involve depends on how cheaply can we call them
1658+
and on the transaction gas limit cap.
1659+
"""
1660+
gas_costs = fork.gas_costs()
1661+
gas_per_account = (
1662+
Op.CALL(
1663+
# Warmed using access lists for cheapest call
1664+
address_warm=True,
1665+
value_transfer=True,
1666+
account_new=False,
1667+
delegated_address=True,
1668+
# Warmed using access lists for cheapest call
1669+
delegated_address_warm=True,
1670+
).gas_cost(fork)
1671+
+ gas_costs.G_ACCESS_LIST_ADDRESS
1672+
)
1673+
gas_limit = fork.transaction_gas_limit_cap()
1674+
assert gas_limit is not None
1675+
# Using generous_gas(fork) as margin for constant gas expenses.
1676+
num_accounts = (gas_limit - generous_gas(fork)) // gas_per_account
1677+
assert num_accounts >= 2560 # 2570 minus margin
1678+
violation_index = violation_index_fn(num_accounts)
1679+
1680+
value = 1
1681+
1682+
initial_sink_balance = 1
1683+
sink_address = pre.fund_eoa(initial_sink_balance)
1684+
wallet_code = Op.CALL(address=sink_address, value=value)
1685+
wallet_address = pre.deploy_contract(code=wallet_code)
1686+
1687+
senders = []
1688+
for i in range(num_accounts):
1689+
if i == violation_index:
1690+
balance = Spec.RESERVE_BALANCE
1691+
else:
1692+
balance = Spec.RESERVE_BALANCE + value
1693+
senders.append(pre.fund_eoa(balance, delegation=wallet_address))
1694+
1695+
contract_code = Op.SSTORE(slot_code_worked, value_code_worked)
1696+
for sender in senders:
1697+
contract_code += Op.CALL(address=sender) + Op.POP
1698+
contract_address = pre.deploy_contract(contract_code)
1699+
1700+
tx = Transaction(
1701+
gas_limit=gas_limit,
1702+
to=contract_address,
1703+
sender=pre.fund_eoa(),
1704+
access_list=[AccessList(address=s, storage_keys=[]) for s in senders]
1705+
+ [AccessList(address=wallet_address, storage_keys=[])],
1706+
)
1707+
1708+
reverted = violation_index is not None
1709+
total_sent = value * num_accounts
1710+
1711+
blockchain_test(
1712+
pre=pre,
1713+
post={
1714+
contract_address: Account(
1715+
storage={}
1716+
if reverted
1717+
else {slot_code_worked: value_code_worked}
1718+
),
1719+
sink_address: Account(
1720+
balance=initial_sink_balance + (0 if reverted else total_sent)
1721+
),
1722+
},
1723+
blocks=[Block(txs=[tx])],
1724+
)

0 commit comments

Comments
 (0)