44from unittest .mock import AsyncMock , MagicMock , patch
55
66import pytest
7- from eth_typing import BlockNumber , ChecksumAddress , HexStr
7+ from eth_typing import BlockNumber , ChecksumAddress
88from hexbytes import HexBytes
99from sw_utils .tests import faker
1010from web3 import Web3
@@ -518,16 +518,15 @@ async def test_meta_vault_claim_delay_logged_and_continues(self) -> None:
518518 exit_request = MagicMock ()
519519 exit_request .vault = VAULT_1
520520 exit_request .position_ticket = 1234
521+ params = make_harvest_params ()
521522 with _mock_update_vaults_state (
522523 meta_vaults_map = {VAULT_1 : MagicMock ()},
523- harvest_params = {VAULT_2 : make_harvest_params () },
524+ harvest_params = {VAULT_2 : params },
524525 update_state_exception = ClaimDelayNotPassedException (exit_request ),
525526 ) as mocks :
526527 await update_vaults_state (vaults = [VAULT_1 , VAULT_2 ], block_number = BlockNumber (100 ))
527528 mocks ['update_state' ].assert_awaited_once ()
528- mocks ['multicall' ].tx_aggregate .assert_awaited_once_with (
529- [(VAULT_2 , ENCODED_UPDATE_STATE_CALL )]
530- )
529+ mocks ['redeemer' ].update_vaults_state .assert_awaited_once_with ({VAULT_2 : params })
531530
532531 @pytest .mark .parametrize (
533532 'has_params, expected_multicall_calls' ,
@@ -542,25 +541,25 @@ async def test_regular_vault_multicall_gated_by_harvest_params(
542541 with _mock_update_vaults_state (harvest_params = {VAULT_1 : params }) as mocks :
543542 await update_vaults_state (vaults = [VAULT_1 ], block_number = BlockNumber (100 ))
544543
545- assert mocks ['multicall ' ].tx_aggregate .await_count == expected_multicall_calls
544+ assert mocks ['redeemer ' ].update_vaults_state .await_count == expected_multicall_calls
546545 if has_params :
547- mocks ['multicall' ].tx_aggregate .assert_awaited_once_with (
548- [(VAULT_1 , ENCODED_UPDATE_STATE_CALL )]
549- )
546+ mocks ['redeemer' ].update_vaults_state .assert_awaited_once_with ({VAULT_1 : params })
550547
551548 async def test_multicall_tx_failure_raises (self ) -> None :
552549 """A failed multicall receipt aborts the round."""
553550 with _mock_update_vaults_state (
554551 harvest_params = {VAULT_1 : make_harvest_params ()},
555552 multicall_tx_status = 0 ,
556553 ):
557- with pytest .raises (RuntimeError , match = 'Update State multicall tx failed' ):
554+ with pytest .raises (
555+ RuntimeError , match = 'OsTokenRedeemer updateVaultState multicall tx failed'
556+ ):
558557 await update_vaults_state (vaults = [VAULT_1 ], block_number = BlockNumber (100 ))
559558
560559 async def test_mix_of_meta_and_regular_vaults (self ) -> None :
561560 """Meta vault is harvested via meta_vault_tree_update_state; regular vaults are
562- batched into a single multicall. Harvest params are fetched only for the
563- regular vaults."""
561+ batched into a single multicall on the OsTokenRedeemer . Harvest params are
562+ fetched only for the regular vaults."""
564563 params = make_harvest_params ()
565564 meta_vaults_map = {VAULT_1 : MagicMock ()}
566565 with _mock_update_vaults_state (
@@ -574,9 +573,7 @@ async def test_mix_of_meta_and_regular_vaults(self) -> None:
574573 mocks ['update_state' ].await_args .kwargs ['root_meta_vault' ] is meta_vaults_map [VAULT_1 ]
575574 )
576575 mocks ['harvest_params' ].assert_awaited_once_with ([VAULT_2 ], BlockNumber (100 ))
577- mocks ['multicall' ].tx_aggregate .assert_awaited_once_with (
578- [(VAULT_2 , ENCODED_UPDATE_STATE_CALL )]
579- )
576+ mocks ['redeemer' ].update_vaults_state .assert_awaited_once_with ({VAULT_2 : params })
580577
581578
582579class TestRedeemMetaVaultSubVaults :
@@ -987,9 +984,6 @@ def _mock_submit_redeem_position(
987984 }
988985
989986
990- ENCODED_UPDATE_STATE_CALL = HexStr ('0xdeadbeef' )
991-
992-
993987@contextmanager
994988def _mock_update_vaults_state (
995989 needs_update : bool = True ,
@@ -1004,7 +998,8 @@ def _mock_update_vaults_state(
1004998 in this map are treated as meta vaults by update_vaults_state. ``harvest_params``
1005999 is the dict returned by get_multiple_harvest_params; a None value for a vault
10061000 skips it from the multicall (production behavior). ``update_state_exception``
1007- makes meta_vault_tree_update_state raise.
1001+ makes meta_vault_tree_update_state raise. ``multicall_tx_status`` controls the
1002+ receipt status of the OsTokenRedeemer.update_vaults_state batched tx.
10081003 """
10091004 meta_vaults_map = {} if meta_vaults_map is None else meta_vaults_map
10101005 harvest_params = {} if harvest_params is None else harvest_params
@@ -1014,11 +1009,10 @@ def _mock_update_vaults_state(
10141009 else :
10151010 update_state_mock = AsyncMock ()
10161011
1017- def vault_factory (addr : ChecksumAddress ) -> MagicMock :
1018- mock_vault = MagicMock ()
1019- mock_vault .contract_address = addr
1020- mock_vault .get_update_state_call = MagicMock (return_value = ENCODED_UPDATE_STATE_CALL )
1021- return mock_vault
1012+ mock_client = AsyncMock ()
1013+ mock_client .eth .wait_for_transaction_receipt = AsyncMock (
1014+ return_value = {'status' : multicall_tx_status }
1015+ )
10221016
10231017 with (
10241018 patch (
@@ -1034,34 +1028,20 @@ def vault_factory(addr: ChecksumAddress) -> MagicMock:
10341028 f'{ MODULE } .get_multiple_harvest_params' ,
10351029 new = AsyncMock (return_value = harvest_params ),
10361030 ) as mock_harvest_params ,
1037- patch (f'{ MODULE } .VaultContract' , side_effect = vault_factory ) as mock_vault_cls ,
1038- _mock_multicall_tx (tx_status = multicall_tx_status ) as multicall_mocks ,
1031+ patch (f'{ MODULE } .os_token_redeemer_contract' ) as mock_redeemer ,
1032+ patch (f'{ MODULE } .execution_client' , new = mock_client ),
1033+ patch (f'{ MODULE } .settings' ) as mock_settings ,
10391034 ):
1035+ mock_settings .execution_transaction_timeout = 120
1036+ mock_redeemer .update_vaults_state = AsyncMock (return_value = '0x' + '11' * 32 )
10401037 yield {
10411038 'graph_get_vaults' : mock_graph ,
10421039 'update_state' : update_state_mock ,
10431040 'harvest_params' : mock_harvest_params ,
1044- 'multicall' : multicall_mocks ['mock_multicall' ],
1045- 'vault_cls' : mock_vault_cls ,
1041+ 'redeemer' : mock_redeemer ,
10461042 }
10471043
10481044
1049- @contextmanager
1050- def _mock_multicall_tx (tx_status : int = 1 ) -> Iterator [dict [str , MagicMock ]]:
1051- """Mock multicall_contract.tx_aggregate with a fake tx hash + receipt status."""
1052- mock_client = AsyncMock ()
1053- mock_client .eth .wait_for_transaction_receipt = AsyncMock (return_value = {'status' : tx_status })
1054-
1055- with (
1056- patch (f'{ MODULE } .multicall_contract' ) as mock_multicall ,
1057- patch (f'{ MODULE } .execution_client' , new = mock_client ),
1058- patch (f'{ MODULE } .settings' ) as mock_settings ,
1059- ):
1060- mock_settings .execution_transaction_timeout = 120
1061- mock_multicall .tx_aggregate = AsyncMock (return_value = '0x' + '11' * 32 )
1062- yield {'mock_multicall' : mock_multicall }
1063-
1064-
10651045@contextmanager
10661046def _mock_process (
10671047 positions : list [OsTokenPosition ] | None = None ,
0 commit comments