@@ -1031,10 +1031,11 @@ def test_check_order(
10311031
10321032def _pairwise_compatible (
10331033 scenarios : tuple [CallScenario , ...],
1034+ incompatible : set [frozenset [CallScenario ]] = _INCOMPATIBLE_SCENARIOS ,
10341035) -> bool :
10351036 """Check that no pair in the tuple is incompatible."""
10361037 return all (
1037- frozenset ({a , b }) not in _INCOMPATIBLE_SCENARIOS
1038+ frozenset ({a , b }) not in incompatible
10381039 for i , a in enumerate (scenarios )
10391040 for b in scenarios [i + 1 :]
10401041 )
@@ -1325,6 +1326,92 @@ def test_tx_revert_scenario_pairs(
13251326 )
13261327
13271328
1329+ _TX_SCENARIO_TRIPLES = [
1330+ pytest .param (
1331+ s1 ,
1332+ s2 ,
1333+ s3 ,
1334+ id = f"{ s1 .name .lower ()} __{ s2 .name .lower ()} __{ s3 .name .lower ()} " ,
1335+ )
1336+ for s1 in CallScenario
1337+ for s2 in CallScenario
1338+ for s3 in CallScenario
1339+ if CallScenario .SUCCESS not in {s1 , s2 , s3 }
1340+ and CallScenario .NOT_CALL not in {s1 , s2 , s3 }
1341+ and s1 .check_priority < s2 .check_priority < s3 .check_priority
1342+ and _pairwise_compatible ((s1 , s2 , s3 ), _TX_INCOMPATIBLE_SCENARIOS )
1343+ ]
1344+
1345+
1346+ @pytest .mark .parametrize (
1347+ "func" ,
1348+ [pytest .param (f , id = f .name ) for f in REPRESENTATIVE_FUNCTIONS ],
1349+ )
1350+ @pytest .mark .parametrize ("scenario1,scenario2,scenario3" , _TX_SCENARIO_TRIPLES )
1351+ def test_tx_revert_scenario_triples (
1352+ state_test : StateTestFiller ,
1353+ pre : Alloc ,
1354+ fork : Fork ,
1355+ func : FunctionInfo ,
1356+ scenario1 : CallScenario ,
1357+ scenario2 : CallScenario ,
1358+ scenario3 : CallScenario ,
1359+ ) -> None :
1360+ """
1361+ Test when the precompile is called directly as transaction
1362+ `to` with 3 reasons to revert.
1363+ """
1364+ normalized = {
1365+ _normalize (s , func ) for s in (scenario1 , scenario2 , scenario3 )
1366+ }
1367+ if not _pairwise_compatible (tuple (normalized ), _TX_INCOMPATIBLE_SCENARIOS ):
1368+ pytest .skip ("normalized scenarios are incompatible" )
1369+
1370+ gas_price = 10
1371+
1372+ calldata , value , to , gas_limit = _tx_params (
1373+ scenario1 ,
1374+ scenario2 ,
1375+ scenario3 ,
1376+ func = func ,
1377+ pre = pre ,
1378+ fork = fork ,
1379+ )
1380+ gas_cost = gas_limit * gas_price
1381+ sender = pre .fund_eoa (gas_cost + value )
1382+
1383+ outcome1 = resolve_outcome (func , scenario1 )
1384+ outcome2 = resolve_outcome (func , scenario2 )
1385+ outcome3 = resolve_outcome (func , scenario3 )
1386+
1387+ tx = Transaction (
1388+ gas_limit = gas_limit ,
1389+ max_fee_per_gas = gas_price ,
1390+ max_priority_fee_per_gas = gas_price ,
1391+ to = to ,
1392+ sender = sender ,
1393+ data = calldata ,
1394+ value = value ,
1395+ expected_receipt = TransactionReceipt (
1396+ status = 0x1
1397+ if outcome1 .call_success
1398+ and outcome2 .call_success
1399+ and outcome3 .call_success
1400+ else 0x0
1401+ ),
1402+ )
1403+
1404+ post : dict = {
1405+ sender : Account (balance = value ),
1406+ }
1407+
1408+ state_test (
1409+ pre = pre ,
1410+ post = post ,
1411+ tx = tx ,
1412+ )
1413+
1414+
13281415@pytest .mark .parametrize (
13291416 "selector" ,
13301417 [
0 commit comments