certora-cli-beta-mirror 7.28.0__py3-none-any.whl → 8.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +10 -3
  2. certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +51 -16
  3. certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +3 -0
  4. certora_cli/CertoraProver/castingInstrumenter.py +192 -0
  5. certora_cli/CertoraProver/certoraApp.py +52 -0
  6. certora_cli/CertoraProver/certoraBuild.py +694 -207
  7. certora_cli/CertoraProver/certoraBuildCacheManager.py +21 -17
  8. certora_cli/CertoraProver/certoraBuildDataClasses.py +8 -2
  9. certora_cli/CertoraProver/certoraBuildRust.py +88 -54
  10. certora_cli/CertoraProver/certoraBuildSui.py +112 -0
  11. certora_cli/CertoraProver/certoraCloudIO.py +97 -96
  12. certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +230 -84
  13. certora_cli/CertoraProver/certoraCollectRunMetadata.py +52 -6
  14. certora_cli/CertoraProver/certoraCompilerParameters.py +11 -0
  15. certora_cli/CertoraProver/certoraConfigIO.py +43 -35
  16. certora_cli/CertoraProver/certoraContext.py +128 -54
  17. certora_cli/CertoraProver/certoraContextAttributes.py +415 -234
  18. certora_cli/CertoraProver/certoraContextValidator.py +152 -105
  19. certora_cli/CertoraProver/certoraContractFuncs.py +34 -1
  20. certora_cli/CertoraProver/certoraParseBuildScript.py +8 -10
  21. certora_cli/CertoraProver/certoraType.py +10 -1
  22. certora_cli/CertoraProver/certoraVerifyGenerator.py +22 -4
  23. certora_cli/CertoraProver/erc7201.py +45 -0
  24. certora_cli/CertoraProver/splitRules.py +23 -18
  25. certora_cli/CertoraProver/storageExtension.py +351 -0
  26. certora_cli/EquivalenceCheck/Eq_default.conf +0 -1
  27. certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -1
  28. certora_cli/EquivalenceCheck/equivCheck.py +2 -1
  29. certora_cli/Mutate/mutateApp.py +41 -22
  30. certora_cli/Mutate/mutateAttributes.py +11 -0
  31. certora_cli/Mutate/mutateValidate.py +42 -2
  32. certora_cli/Shared/certoraAttrUtil.py +21 -5
  33. certora_cli/Shared/certoraUtils.py +180 -60
  34. certora_cli/Shared/certoraValidateFuncs.py +68 -26
  35. certora_cli/Shared/proverCommon.py +308 -0
  36. certora_cli/certoraCVLFormatter.py +76 -0
  37. certora_cli/certoraConcord.py +39 -0
  38. certora_cli/certoraEVMProver.py +4 -3
  39. certora_cli/certoraRanger.py +39 -0
  40. certora_cli/certoraRun.py +83 -223
  41. certora_cli/certoraSolanaProver.py +40 -128
  42. certora_cli/certoraSorobanProver.py +59 -4
  43. certora_cli/certoraSuiProver.py +93 -0
  44. certora_cli_beta_mirror-8.5.0.dist-info/LICENSE +15 -0
  45. {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/METADATA +21 -5
  46. certora_cli_beta_mirror-8.5.0.dist-info/RECORD +81 -0
  47. {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/WHEEL +1 -1
  48. {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/entry_points.txt +3 -0
  49. certora_jars/ASTExtraction.jar +0 -0
  50. certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -1
  51. certora_jars/Typechecker.jar +0 -0
  52. certora_cli_beta_mirror-7.28.0.dist-info/LICENSE +0 -22
  53. certora_cli_beta_mirror-7.28.0.dist-info/RECORD +0 -70
  54. {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/top_level.txt +0 -0
@@ -25,8 +25,9 @@ from CertoraProver.Compiler.CompilerCollector import CompilerLang, CompilerColle
25
25
  from CertoraProver.Compiler.CompilerCollectorSol import CompilerCollectorSol, CompilerLangSol
26
26
  from CertoraProver.Compiler.CompilerCollectorYul import CompilerLangYul, CompilerCollectorYul
27
27
  from CertoraProver.Compiler.CompilerCollectorVy import CompilerCollectorVy, CompilerLangVy
28
- from Shared.certoraUtils import match_path_to_mapping_key, remove_file, get_certora_config_dir
28
+ from Shared.certoraUtils import remove_file, get_certora_config_dir
29
29
  from CertoraProver.certoraContextClass import CertoraContext
30
+ import CertoraProver.certoraContext as Ctx
30
31
 
31
32
  # logger for running the Solidity compiler and reporting any errors it emits
32
33
  compiler_logger = logging.getLogger("compiler")
@@ -42,10 +43,16 @@ def get_relevant_compiler(contract_file_path: Path, context: CertoraContext) ->
42
43
 
43
44
  if contract_file_path.is_absolute():
44
45
  contract_file_path = Path(os.path.relpath(contract_file_path, Path.cwd()))
46
+
47
+ # If the contract file is in the sources directory, we want to use the relative path from the "cwd" in the source tree.
48
+ if Util.get_certora_sources_dir() in contract_file_path.parents:
49
+ cwd_in_source = Util.get_certora_sources_dir() / context.cwd_rel_in_sources
50
+ contract_file_path = Path(os.path.relpath(contract_file_path, cwd_in_source))
51
+
45
52
  if context.compiler_map:
46
- match = match_path_to_mapping_key(contract_file_path, context.compiler_map)
53
+ match = Ctx.get_map_attribute_value(context, contract_file_path, 'compiler')
47
54
  if match:
48
- return match
55
+ return str(match)
49
56
  else:
50
57
  raise RuntimeError(f'cannot match compiler to {contract_file_path} from compiler_map')
51
58
 
@@ -556,43 +556,78 @@ class CompilerLangVy(CompilerLang, metaclass=Singleton):
556
556
  else:
557
557
  raise Exception(f"Unexpected ast_node {ast_node}, cannot evaluate constant")
558
558
 
559
+ @staticmethod
560
+ def subscript_node(ast_node: Dict[str, Any]) -> Optional[Dict[str, Any]]:
561
+ ty = ast_node.get("ast_type")
562
+ # pre-vyper 0.4.0, look deeper within
563
+ if ty == "Index":
564
+ nested = ast_node.get("value")
565
+ if nested is None or not isinstance(nested, dict):
566
+ return None
567
+ return nested
568
+ else:
569
+ return ast_node
570
+
571
+ @staticmethod
572
+ def int_subscript(ast_node: Dict[str, Any], named_constants: Dict[str, int]) -> Optional[int]:
573
+ node = CompilerLangVy.subscript_node(ast_node)
574
+ if node is None:
575
+ return None
576
+
577
+ if "id" in node and node["id"] in named_constants:
578
+ return named_constants[node["id"]]
579
+ elif node.get("ast_type") == "Int":
580
+ return node.get("value")
581
+ else:
582
+ return None
583
+
584
+ @staticmethod
585
+ def get_tuple_elements(ast_node: Dict[str, Any]) -> List[Dict[str, Any]]:
586
+ node = CompilerLangVy.subscript_node(ast_node)
587
+ if node is None or node.get("ast_type") != "Tuple":
588
+ raise Exception("Couldn't get tuple elements from ast node")
589
+ return node["elements"]
590
+
559
591
  @staticmethod
560
592
  def extract_type_from_subscript_node(ast_subscript_node: Dict[str, Any],
561
593
  named_constants: Dict[str, int]) -> VyperType:
562
594
  value_id = ast_subscript_node['value'].get('id', None)
563
595
  if value_id == 'String':
564
- max_bytes = ast_subscript_node['slice']['value']['value']
596
+ max_bytes = CompilerLangVy.int_subscript(ast_subscript_node['slice'], named_constants)
597
+ if max_bytes is None:
598
+ raise Exception("Failed to find max length for string type declaration")
565
599
  return CompilerLangVy.VyperTypeString(max_bytes)
566
600
  elif value_id == 'Bytes':
567
- max_bytes = ast_subscript_node['slice']['value']['value']
601
+ max_bytes = CompilerLangVy.int_subscript(ast_subscript_node['slice'], named_constants)
602
+ if max_bytes is None:
603
+ raise Exception("Failed to find max length for bytes type declaration")
568
604
  return CompilerLangVy.VyperTypeBytes(max_bytes)
569
605
  elif value_id == 'DynArray':
570
- elem_type = CompilerLangVy.extract_type_from_type_annotation_node(
571
- ast_subscript_node['slice']['value']['elements'][0], named_constants)
572
- max_elements = CompilerLangVy.extract_constant(ast_subscript_node['slice']['value']['elements'][1],
573
- named_constants)
606
+ tup_elems = CompilerLangVy.get_tuple_elements(ast_subscript_node["slice"])
607
+ elem_type = CompilerLangVy.extract_type_from_type_annotation_node(tup_elems[0], named_constants)
608
+ max_elements = CompilerLangVy.extract_constant(tup_elems[1], named_constants)
574
609
  return CompilerLangVy.VyperTypeDynArray(elem_type, max_elements)
575
610
  elif value_id == 'HashMap':
576
- elements_node = ast_subscript_node['slice']['value']['elements']
611
+ elements_node = CompilerLangVy.get_tuple_elements(ast_subscript_node['slice'])
577
612
  key_type = CompilerLangVy.extract_type_from_type_annotation_node(elements_node[0], named_constants)
578
613
  value_type = CompilerLangVy.extract_type_from_type_annotation_node(elements_node[1], named_constants)
579
614
  return CompilerLangVy.VyperTypeHashMap(key_type, value_type)
580
615
  else: # StaticArray key_type[size]
581
616
  key_type = CompilerLangVy.extract_type_from_type_annotation_node(ast_subscript_node['value'],
582
617
  named_constants)
583
- max_elements_node = ast_subscript_node['slice']['value']
584
- if 'id' in max_elements_node and max_elements_node['id'] in named_constants:
585
- return CompilerLangVy.VyperTypeStaticArray(key_type, named_constants[max_elements_node['id']])
618
+ max_elem_value = CompilerLangVy.int_subscript(ast_subscript_node['slice'], named_constants)
619
+ if max_elem_value is not None:
620
+ return CompilerLangVy.VyperTypeStaticArray(key_type, max_elem_value)
586
621
  else:
587
622
  # this is very specific to curve code which has uint256[CONST/2] static array declaration.
623
+ max_elements_node = CompilerLangVy.subscript_node(ast_subscript_node["slice"])
624
+ if max_elements_node is None:
625
+ raise Exception("Couldn't find subscript node for array type")
588
626
  if 'ast_type' in max_elements_node:
589
627
  if max_elements_node['ast_type'] == 'BinOp' or max_elements_node['ast_type'] in ('Int', 'Name'):
590
628
  # good chance this will succeed
591
629
  static_array_len = CompilerLangVy.extract_constant(max_elements_node, named_constants)
592
630
  return CompilerLangVy.VyperTypeStaticArray(key_type, static_array_len)
593
- elif 'value' in max_elements_node:
594
- return CompilerLangVy.VyperTypeStaticArray(key_type, max_elements_node['value'])
595
-
596
631
  raise Exception(
597
632
  f"Don't know how to deal with vyper static array declaration with length {max_elements_node}")
598
633
 
@@ -624,7 +659,7 @@ class CompilerLangVy(CompilerLang, metaclass=Singleton):
624
659
 
625
660
  @staticmethod
626
661
  def extract_type_from_enum_def(ast_enumdef_node: Dict[str, Any]) -> VyperType:
627
- fields = [n['target']['id'] for n in ast_enumdef_node['body']]
662
+ fields = [n['target']['id'] if "target" in n else n["value"]["id"] for n in ast_enumdef_node['body']]
628
663
  return CompilerLangVy.VyperTypeEnum(ast_enumdef_node['name'], fields)
629
664
 
630
665
  @staticmethod
@@ -1238,7 +1273,7 @@ class Collector:
1238
1273
  self._collect_const(v)
1239
1274
 
1240
1275
  # Extract and resolve types
1241
- type_asts = {'EnumDef', 'StructDef', 'InterfaceDef', 'Import', 'Import', 'ImportFrom'}
1276
+ type_asts = {'EnumDef', 'StructDef', 'InterfaceDef', 'Import', 'Import', 'ImportFrom', 'FlagDef'}
1242
1277
  types = [e for e in module_node['body'] if e['ast_type'] in type_asts]
1243
1278
  for t in types:
1244
1279
  self._collect_type_decl(t)
@@ -1265,7 +1300,7 @@ class Collector:
1265
1300
 
1266
1301
  def _collect_type_decl(self, type_decl_node: Dict[str, Any]) -> None:
1267
1302
  """Update self.types to add type definition from `type_decl_node`. Expects self.consts to be initialized"""
1268
- if type_decl_node['ast_type'] == 'EnumDef':
1303
+ if type_decl_node['ast_type'] in ('EnumDef', 'FlagDef'):
1269
1304
  vy_type = CompilerLangVy.extract_type_from_enum_def(type_decl_node)
1270
1305
  elif type_decl_node['ast_type'] == 'StructDef':
1271
1306
  vy_type = CompilerLangVy.extract_type_from_struct_def(type_decl_node, self.consts)
@@ -104,14 +104,17 @@ class CompilerLangYul(CompilerLangSol, metaclass=Singleton):
104
104
  notpayable=notpayable,
105
105
  fromLib=False,
106
106
  isConstructor=False,
107
+ is_free_func=False,
107
108
  stateMutability=state_mutability,
108
109
  visibility=visibility,
109
110
  implemented=True,
110
111
  overrides=False,
112
+ virtual=False,
111
113
  contractName=contract_name,
112
114
  source_bytes=None,
113
115
  ast_id=None,
114
116
  original_file=None,
117
+ location=None,
115
118
  body_location=None,
116
119
  )
117
120
  funcs.append(func)
@@ -0,0 +1,192 @@
1
+ # The Certora Prover
2
+ # Copyright (C) 2025 Certora Ltd.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, version 3 of the License.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+
17
+ from dataclasses import dataclass
18
+ from typing import Dict, Any, Optional, Generator
19
+
20
+ from CertoraProver.Compiler.CompilerCollectorSol import CompilerCollectorSol
21
+ from CertoraProver.certoraBuildDataClasses import SDC, Instrumentation, Replace
22
+ from Shared import certoraUtils as Util
23
+
24
+
25
+ @dataclass
26
+ class IntType:
27
+ is_signed: bool
28
+ bitwidth: int
29
+
30
+ def encode(self) -> Optional[int]:
31
+ """
32
+ Encodes into a 9 bit number: lowest bit is on if [IntType] is true, and upper 8 bits are the width.
33
+ returns none if width is out of bounds.
34
+ """
35
+ if self.bitwidth <= 0 or self.bitwidth > 256:
36
+ return None
37
+ return self.bitwidth * 2 + int(self.is_signed)
38
+
39
+
40
+ def parse_int_type(type_string: str) -> Optional[IntType]:
41
+ """
42
+ Parse an integer type string of the form 'int<bitwidth>' or 'uint<bitwidth>'.
43
+ Returns IntType or None if the string doesn't match the pattern.
44
+ """
45
+ if type_string == "int":
46
+ return IntType(True, 256)
47
+ elif type_string == "uint":
48
+ return IntType(False, 256)
49
+ elif type_string.startswith("int"):
50
+ is_signed = True
51
+ bitwidth_str = type_string[3:]
52
+ elif type_string.startswith("uint"):
53
+ is_signed = False
54
+ bitwidth_str = type_string[4:]
55
+ else:
56
+ return None
57
+
58
+ try:
59
+ bitwidth = int(bitwidth_str)
60
+ return IntType(is_signed, bitwidth)
61
+ except ValueError:
62
+ return None
63
+
64
+
65
+ def is_int_type(type_string: str) -> bool:
66
+ return parse_int_type(type_string) is not None
67
+
68
+
69
+ def encode_type(type_string: str) -> int:
70
+ int_type = parse_int_type(type_string)
71
+ if int_type is None:
72
+ raise Exception(f"Invalid type string: {type_string}")
73
+ encoded = int_type.encode()
74
+ if encoded is None:
75
+ raise Exception(f"Invalid type string: {type_string}")
76
+ return encoded
77
+
78
+
79
+ @dataclass
80
+ class CastInfo:
81
+ arg_type_str: str
82
+ res_type_str: str
83
+ expr_id: int
84
+
85
+
86
+ def find_casts(ast: Dict[int, Any]) -> list[CastInfo]:
87
+ """
88
+ Search all nodes that are in functions for kind "typeConversion" with one argument.
89
+ Returns a list of CastInfo instances.
90
+ """
91
+ conversions = []
92
+ function_nodes = [node for node in ast.values() if node.get('nodeType') == 'FunctionDefinition']
93
+
94
+ for func in function_nodes:
95
+ for node in iter_all_nodes(func):
96
+ if isinstance(node, dict) and node.get("kind") == "typeConversion":
97
+ arguments = node.get("arguments", [])
98
+ if len(arguments) == 1 and isinstance(arguments[0], dict):
99
+ expression = node["expression"]
100
+ expr_node_id = expression["id"]
101
+ arg_type_str = expression["argumentTypes"][0]["typeString"]
102
+ if not is_int_type(arg_type_str):
103
+ continue
104
+ if "typeName" not in expression:
105
+ continue
106
+ expr_type_node = expression["typeName"]
107
+ if isinstance(expr_type_node, str):
108
+ expr_type_str = expr_type_node
109
+ else:
110
+ expr_type_str = expr_type_node["name"]
111
+ if not is_int_type(expr_type_str):
112
+ continue
113
+ conversions.append(CastInfo(arg_type_str, expr_type_str, expr_node_id))
114
+
115
+ return conversions
116
+
117
+
118
+ def casting_func_name(counter: int) -> str:
119
+ return f"cast_{counter}"
120
+
121
+
122
+ def generate_casting_function(assembly_prefix: str, cast_info: CastInfo, counter: int) -> str:
123
+ """
124
+ returns the text of a solidity function that does casting according to CastInfo. It also has an encoded mload
125
+ call, to be decoded later on the kotlin side if we run the `safeCasting` builtin rule.
126
+ """
127
+ conversion_string = assembly_prefix + \
128
+ "{ mstore(0xffffff6e4604afefe123321beef1b03fffffffffffffffffffff" + \
129
+ f'{"%0.4x" % counter}{"%0.4x" % encode_type(cast_info.arg_type_str)}{"%0.4x" % encode_type(cast_info.res_type_str)}, x)' + "}"
130
+ function_head = f"function {casting_func_name(counter)}({cast_info.arg_type_str} x) internal pure returns ({cast_info.res_type_str})"
131
+ return function_head + "{\n" + conversion_string + f"return {cast_info.res_type_str}(x);\n" "}\n"
132
+
133
+
134
+ def generate_casting_instrumentation(asts: Dict[str, Dict[str, Dict[int, Any]]], contract_file: str, sdc: SDC) \
135
+ -> tuple[Dict[str, Dict[int, Instrumentation]], Dict[str, tuple[str, list[str]]]]:
136
+ """
137
+ Generate instrumentation for integer type casts in Solidity code.
138
+
139
+ Returns a tuple of:
140
+ - casting_instrumentation: Maps file paths to offset->Instrumentation mappings that replace type names with
141
+ library calls to casting functions we generate.
142
+ - casting_types: Maps file paths to library_name and the text for our new casting functions. This library is added
143
+ to the end of the file.
144
+
145
+ It's not hundred precent sure this works well in combination with `compiler_map`, because the counter for library
146
+ names may reset?
147
+ """
148
+ casting_instrumentation: Dict[str, Dict[int, Instrumentation]] = dict()
149
+
150
+ if not isinstance(sdc.compiler_collector, CompilerCollectorSol):
151
+ raise Exception(f"Encountered a compiler collector that is not solc for file {contract_file}"
152
+ " when trying to add casting instrumentation")
153
+ assembly_prefix = sdc.compiler_collector.gen_memory_safe_assembly_prefix()
154
+
155
+ casting_funcs : Dict[str, tuple[str, list[str]]] = dict()
156
+ counter = 0
157
+ original_files = sorted({Util.convert_path_for_solc_import(c.original_file) for c in sdc.contracts})
158
+ for file_count, solfile in enumerate(original_files, start=1):
159
+ main_ast = asts[contract_file]
160
+ curr_file_ast = main_ast.get(solfile, dict())
161
+
162
+ per_file_inst = casting_instrumentation.setdefault(solfile, dict())
163
+ libname, per_file_casts = casting_funcs.setdefault(solfile, (f"CertoraCastingLib{file_count}", []))
164
+
165
+ casts = find_casts(curr_file_ast)
166
+ for cast_info in casts:
167
+ start_offset, src_len, file = curr_file_ast[cast_info.expr_id]["src"].split(":")
168
+ counter += 1
169
+ per_file_inst[int(start_offset)] = Instrumentation(expected=bytes(cast_info.res_type_str[0], 'utf-8'),
170
+ to_ins=f"{libname}.{casting_func_name(counter)}",
171
+ mut=Replace(len(cast_info.res_type_str)))
172
+ new_func = generate_casting_function(assembly_prefix, cast_info, counter)
173
+ per_file_casts.append(new_func)
174
+
175
+ return casting_instrumentation, casting_funcs
176
+
177
+
178
+ def iter_all_nodes(node: Any) -> Generator[Any, Optional[Any], None]:
179
+ """
180
+ Yield a node and all its subnodes in depth-first order.
181
+ Works with dict nodes that may contain nested dicts and lists.
182
+ """
183
+ yield node
184
+
185
+ if isinstance(node, dict):
186
+ for value in node.values():
187
+ if isinstance(value, (dict, list)):
188
+ yield from iter_all_nodes(value)
189
+ elif isinstance(node, list):
190
+ for item in node:
191
+ if isinstance(item, (dict, list)):
192
+ yield from iter_all_nodes(item)
@@ -0,0 +1,52 @@
1
+ # The Certora Prover
2
+ # Copyright (C) 2025 Certora Ltd.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, version 3 of the License.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ import CertoraProver.certoraContextAttributes as Attrs
17
+ from Shared import certoraAttrUtil as AttrUtil
18
+
19
+
20
+ from abc import ABC
21
+ from typing import Type
22
+
23
+
24
+ class CertoraApp(ABC):
25
+ attr_class: Type[AttrUtil.Attributes] = Attrs.EvmProverAttributes
26
+
27
+ class EvmAppClass(CertoraApp):
28
+ pass
29
+
30
+ class EvmApp(EvmAppClass):
31
+ pass
32
+
33
+ class RustAppClass(CertoraApp):
34
+ pass
35
+
36
+ class MoveAppClass(CertoraApp):
37
+ pass
38
+
39
+ class SolanaApp(RustAppClass):
40
+ attr_class = Attrs.SolanaProverAttributes
41
+
42
+ class SorobanApp(RustAppClass):
43
+ attr_class = Attrs.SorobanProverAttributes
44
+
45
+ class RangerApp(EvmAppClass):
46
+ attr_class = Attrs.RangerAttributes
47
+
48
+ class ConcordApp(EvmAppClass):
49
+ attr_class = Attrs.ConcordAttributes
50
+
51
+ class SuiApp(MoveAppClass):
52
+ attr_class = Attrs.SuiProverAttributes