@godzillaba/mutest 1.0.0
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.
- package/.devcontainer/Dockerfile +117 -0
- package/.devcontainer/devcontainer.json +62 -0
- package/.devcontainer/init-firewall.sh +118 -0
- package/.github/workflows/test.yml +38 -0
- package/.gitmodules +3 -0
- package/CLAUDE.md +39 -0
- package/README.md +33 -0
- package/foundry.lock +8 -0
- package/foundry.toml +6 -0
- package/index.ts +97 -0
- package/lib/forge-std/.gitattributes +1 -0
- package/lib/forge-std/.github/CODEOWNERS +1 -0
- package/lib/forge-std/.github/dependabot.yml +6 -0
- package/lib/forge-std/.github/workflows/ci.yml +125 -0
- package/lib/forge-std/.github/workflows/sync.yml +36 -0
- package/lib/forge-std/CONTRIBUTING.md +193 -0
- package/lib/forge-std/LICENSE-APACHE +203 -0
- package/lib/forge-std/LICENSE-MIT +25 -0
- package/lib/forge-std/README.md +314 -0
- package/lib/forge-std/RELEASE_CHECKLIST.md +12 -0
- package/lib/forge-std/foundry.toml +18 -0
- package/lib/forge-std/package.json +16 -0
- package/lib/forge-std/scripts/vm.py +636 -0
- package/lib/forge-std/src/Base.sol +48 -0
- package/lib/forge-std/src/Config.sol +60 -0
- package/lib/forge-std/src/LibVariable.sol +477 -0
- package/lib/forge-std/src/Script.sol +28 -0
- package/lib/forge-std/src/StdAssertions.sol +779 -0
- package/lib/forge-std/src/StdChains.sol +303 -0
- package/lib/forge-std/src/StdCheats.sol +825 -0
- package/lib/forge-std/src/StdConfig.sol +632 -0
- package/lib/forge-std/src/StdConstants.sol +30 -0
- package/lib/forge-std/src/StdError.sol +15 -0
- package/lib/forge-std/src/StdInvariant.sol +140 -0
- package/lib/forge-std/src/StdJson.sol +275 -0
- package/lib/forge-std/src/StdMath.sol +47 -0
- package/lib/forge-std/src/StdStorage.sol +475 -0
- package/lib/forge-std/src/StdStyle.sol +333 -0
- package/lib/forge-std/src/StdToml.sol +275 -0
- package/lib/forge-std/src/StdUtils.sol +200 -0
- package/lib/forge-std/src/Test.sol +32 -0
- package/lib/forge-std/src/Vm.sol +2533 -0
- package/lib/forge-std/src/console.sol +1551 -0
- package/lib/forge-std/src/console2.sol +4 -0
- package/lib/forge-std/src/interfaces/IERC1155.sol +105 -0
- package/lib/forge-std/src/interfaces/IERC165.sol +12 -0
- package/lib/forge-std/src/interfaces/IERC20.sol +43 -0
- package/lib/forge-std/src/interfaces/IERC4626.sol +190 -0
- package/lib/forge-std/src/interfaces/IERC6909.sol +72 -0
- package/lib/forge-std/src/interfaces/IERC721.sol +164 -0
- package/lib/forge-std/src/interfaces/IERC7540.sol +144 -0
- package/lib/forge-std/src/interfaces/IERC7575.sol +241 -0
- package/lib/forge-std/src/interfaces/IMulticall3.sol +68 -0
- package/lib/forge-std/src/safeconsole.sol +13248 -0
- package/lib/forge-std/test/CommonBase.t.sol +44 -0
- package/lib/forge-std/test/Config.t.sol +381 -0
- package/lib/forge-std/test/LibVariable.t.sol +452 -0
- package/lib/forge-std/test/StdAssertions.t.sol +141 -0
- package/lib/forge-std/test/StdChains.t.sol +227 -0
- package/lib/forge-std/test/StdCheats.t.sol +638 -0
- package/lib/forge-std/test/StdConstants.t.sol +38 -0
- package/lib/forge-std/test/StdError.t.sol +119 -0
- package/lib/forge-std/test/StdJson.t.sol +49 -0
- package/lib/forge-std/test/StdMath.t.sol +202 -0
- package/lib/forge-std/test/StdStorage.t.sol +485 -0
- package/lib/forge-std/test/StdStyle.t.sol +110 -0
- package/lib/forge-std/test/StdToml.t.sol +49 -0
- package/lib/forge-std/test/StdUtils.t.sol +342 -0
- package/lib/forge-std/test/Vm.t.sol +18 -0
- package/lib/forge-std/test/compilation/CompilationScript.sol +8 -0
- package/lib/forge-std/test/compilation/CompilationScriptBase.sol +8 -0
- package/lib/forge-std/test/compilation/CompilationTest.sol +8 -0
- package/lib/forge-std/test/compilation/CompilationTestBase.sol +8 -0
- package/lib/forge-std/test/fixtures/broadcast.log.json +187 -0
- package/lib/forge-std/test/fixtures/config.toml +81 -0
- package/lib/forge-std/test/fixtures/test.json +8 -0
- package/lib/forge-std/test/fixtures/test.toml +6 -0
- package/package.json +10 -0
- package/script/Counter.s.sol +19 -0
- package/src/Counter.sol +14 -0
- package/test/Counter.t.sol +24 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import copy
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
import subprocess
|
|
8
|
+
from enum import Enum as PyEnum
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Callable
|
|
11
|
+
from urllib import request
|
|
12
|
+
|
|
13
|
+
VoidFn = Callable[[], None]
|
|
14
|
+
|
|
15
|
+
CHEATCODES_JSON_URL = "https://raw.githubusercontent.com/foundry-rs/foundry/master/crates/cheatcodes/assets/cheatcodes.json"
|
|
16
|
+
OUT_PATH = "src/Vm.sol"
|
|
17
|
+
|
|
18
|
+
VM_SAFE_DOC = """\
|
|
19
|
+
/// The `VmSafe` interface does not allow manipulation of the EVM state or other actions that may
|
|
20
|
+
/// result in Script simulations differing from on-chain execution. It is recommended to only use
|
|
21
|
+
/// these cheats in scripts.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
VM_DOC = """\
|
|
25
|
+
/// The `Vm` interface does allow manipulation of the EVM state. These are all intended to be used
|
|
26
|
+
/// in tests, but it is not recommended to use these cheats in scripts.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def main():
|
|
31
|
+
parser = argparse.ArgumentParser(
|
|
32
|
+
description="Generate Vm.sol based on the cheatcodes json created by Foundry")
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--from",
|
|
35
|
+
metavar="PATH",
|
|
36
|
+
dest="path",
|
|
37
|
+
required=False,
|
|
38
|
+
help="path to a json file containing the Vm interface, as generated by Foundry")
|
|
39
|
+
args = parser.parse_args()
|
|
40
|
+
json_str = request.urlopen(CHEATCODES_JSON_URL).read().decode("utf-8") if args.path is None else Path(args.path).read_text()
|
|
41
|
+
contract = Cheatcodes.from_json(json_str)
|
|
42
|
+
|
|
43
|
+
ccs = contract.cheatcodes
|
|
44
|
+
ccs = list(filter(lambda cc: cc.status not in ["experimental", "internal"], ccs))
|
|
45
|
+
ccs.sort(key=lambda cc: cc.func.id)
|
|
46
|
+
|
|
47
|
+
safe = list(filter(lambda cc: cc.safety == "safe", ccs))
|
|
48
|
+
safe.sort(key=CmpCheatcode)
|
|
49
|
+
unsafe = list(filter(lambda cc: cc.safety == "unsafe", ccs))
|
|
50
|
+
unsafe.sort(key=CmpCheatcode)
|
|
51
|
+
assert len(safe) + len(unsafe) == len(ccs)
|
|
52
|
+
|
|
53
|
+
prefix_with_group_headers(safe)
|
|
54
|
+
prefix_with_group_headers(unsafe)
|
|
55
|
+
|
|
56
|
+
out = ""
|
|
57
|
+
|
|
58
|
+
out += "// Automatically @generated by scripts/vm.py. Do not modify manually.\n\n"
|
|
59
|
+
|
|
60
|
+
pp = CheatcodesPrinter(
|
|
61
|
+
spdx_identifier="MIT OR Apache-2.0",
|
|
62
|
+
solidity_requirement=">=0.8.13 <0.9.0",
|
|
63
|
+
)
|
|
64
|
+
pp.p_prelude()
|
|
65
|
+
pp.prelude = False
|
|
66
|
+
out += pp.finish()
|
|
67
|
+
|
|
68
|
+
out += "\n\n"
|
|
69
|
+
out += VM_SAFE_DOC
|
|
70
|
+
vm_safe = Cheatcodes(
|
|
71
|
+
# TODO: Custom errors were introduced in 0.8.4
|
|
72
|
+
errors=[], # contract.errors
|
|
73
|
+
events=contract.events,
|
|
74
|
+
enums=contract.enums,
|
|
75
|
+
structs=contract.structs,
|
|
76
|
+
cheatcodes=safe,
|
|
77
|
+
)
|
|
78
|
+
pp.p_contract(vm_safe, "VmSafe")
|
|
79
|
+
out += pp.finish()
|
|
80
|
+
|
|
81
|
+
out += "\n\n"
|
|
82
|
+
out += VM_DOC
|
|
83
|
+
vm_unsafe = Cheatcodes(
|
|
84
|
+
errors=[],
|
|
85
|
+
events=[],
|
|
86
|
+
enums=[],
|
|
87
|
+
structs=[],
|
|
88
|
+
cheatcodes=unsafe,
|
|
89
|
+
)
|
|
90
|
+
pp.p_contract(vm_unsafe, "Vm", "VmSafe")
|
|
91
|
+
out += pp.finish()
|
|
92
|
+
|
|
93
|
+
# Compatibility with <0.8.0
|
|
94
|
+
def memory_to_calldata(m: re.Match) -> str:
|
|
95
|
+
return " calldata " + m.group(1)
|
|
96
|
+
|
|
97
|
+
out = re.sub(r" memory (.*returns)", memory_to_calldata, out)
|
|
98
|
+
|
|
99
|
+
with open(OUT_PATH, "w") as f:
|
|
100
|
+
f.write(out)
|
|
101
|
+
|
|
102
|
+
forge_fmt = ["forge", "fmt", OUT_PATH]
|
|
103
|
+
res = subprocess.run(forge_fmt)
|
|
104
|
+
assert res.returncode == 0, f"command failed: {forge_fmt}"
|
|
105
|
+
|
|
106
|
+
print(f"Wrote to {OUT_PATH}")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class CmpCheatcode:
|
|
110
|
+
cheatcode: "Cheatcode"
|
|
111
|
+
|
|
112
|
+
def __init__(self, cheatcode: "Cheatcode"):
|
|
113
|
+
self.cheatcode = cheatcode
|
|
114
|
+
|
|
115
|
+
def __lt__(self, other: "CmpCheatcode") -> bool:
|
|
116
|
+
return cmp_cheatcode(self.cheatcode, other.cheatcode) < 0
|
|
117
|
+
|
|
118
|
+
def __eq__(self, other: "CmpCheatcode") -> bool:
|
|
119
|
+
return cmp_cheatcode(self.cheatcode, other.cheatcode) == 0
|
|
120
|
+
|
|
121
|
+
def __gt__(self, other: "CmpCheatcode") -> bool:
|
|
122
|
+
return cmp_cheatcode(self.cheatcode, other.cheatcode) > 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def cmp_cheatcode(a: "Cheatcode", b: "Cheatcode") -> int:
|
|
126
|
+
if a.group != b.group:
|
|
127
|
+
return -1 if a.group < b.group else 1
|
|
128
|
+
if a.status != b.status:
|
|
129
|
+
return -1 if a.status < b.status else 1
|
|
130
|
+
if a.safety != b.safety:
|
|
131
|
+
return -1 if a.safety < b.safety else 1
|
|
132
|
+
if a.func.id != b.func.id:
|
|
133
|
+
return -1 if a.func.id < b.func.id else 1
|
|
134
|
+
return 0
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# HACK: A way to add group header comments without having to modify printer code
|
|
138
|
+
def prefix_with_group_headers(cheats: list["Cheatcode"]):
|
|
139
|
+
s = set()
|
|
140
|
+
for i, cheat in enumerate(cheats):
|
|
141
|
+
if cheat.group in s:
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
s.add(cheat.group)
|
|
145
|
+
|
|
146
|
+
c = copy.deepcopy(cheat)
|
|
147
|
+
c.func.description = ""
|
|
148
|
+
c.func.declaration = f"// ======== {group(c.group)} ========"
|
|
149
|
+
cheats.insert(i, c)
|
|
150
|
+
return cheats
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def group(s: str) -> str:
|
|
154
|
+
if s == "evm":
|
|
155
|
+
return "EVM"
|
|
156
|
+
if s == "json":
|
|
157
|
+
return "JSON"
|
|
158
|
+
return s[0].upper() + s[1:]
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class Visibility(PyEnum):
|
|
162
|
+
EXTERNAL: str = "external"
|
|
163
|
+
PUBLIC: str = "public"
|
|
164
|
+
INTERNAL: str = "internal"
|
|
165
|
+
PRIVATE: str = "private"
|
|
166
|
+
|
|
167
|
+
def __str__(self):
|
|
168
|
+
return self.value
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class Mutability(PyEnum):
|
|
172
|
+
PURE: str = "pure"
|
|
173
|
+
VIEW: str = "view"
|
|
174
|
+
NONE: str = ""
|
|
175
|
+
|
|
176
|
+
def __str__(self):
|
|
177
|
+
return self.value
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class Function:
|
|
181
|
+
id: str
|
|
182
|
+
description: str
|
|
183
|
+
declaration: str
|
|
184
|
+
visibility: Visibility
|
|
185
|
+
mutability: Mutability
|
|
186
|
+
signature: str
|
|
187
|
+
selector: str
|
|
188
|
+
selector_bytes: bytes
|
|
189
|
+
|
|
190
|
+
def __init__(
|
|
191
|
+
self,
|
|
192
|
+
id: str,
|
|
193
|
+
description: str,
|
|
194
|
+
declaration: str,
|
|
195
|
+
visibility: Visibility,
|
|
196
|
+
mutability: Mutability,
|
|
197
|
+
signature: str,
|
|
198
|
+
selector: str,
|
|
199
|
+
selector_bytes: bytes,
|
|
200
|
+
):
|
|
201
|
+
self.id = id
|
|
202
|
+
self.description = description
|
|
203
|
+
self.declaration = declaration
|
|
204
|
+
self.visibility = visibility
|
|
205
|
+
self.mutability = mutability
|
|
206
|
+
self.signature = signature
|
|
207
|
+
self.selector = selector
|
|
208
|
+
self.selector_bytes = selector_bytes
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def from_dict(d: dict) -> "Function":
|
|
212
|
+
return Function(
|
|
213
|
+
d["id"],
|
|
214
|
+
d["description"],
|
|
215
|
+
d["declaration"],
|
|
216
|
+
Visibility(d["visibility"]),
|
|
217
|
+
Mutability(d["mutability"]),
|
|
218
|
+
d["signature"],
|
|
219
|
+
d["selector"],
|
|
220
|
+
bytes(d["selectorBytes"]),
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class Cheatcode:
|
|
225
|
+
func: Function
|
|
226
|
+
group: str
|
|
227
|
+
status: str
|
|
228
|
+
safety: str
|
|
229
|
+
|
|
230
|
+
def __init__(self, func: Function, group: str, status: str, safety: str):
|
|
231
|
+
self.func = func
|
|
232
|
+
self.group = group
|
|
233
|
+
self.status = status
|
|
234
|
+
self.safety = safety
|
|
235
|
+
|
|
236
|
+
@staticmethod
|
|
237
|
+
def from_dict(d: dict) -> "Cheatcode":
|
|
238
|
+
return Cheatcode(
|
|
239
|
+
Function.from_dict(d["func"]),
|
|
240
|
+
str(d["group"]),
|
|
241
|
+
str(d["status"]),
|
|
242
|
+
str(d["safety"]),
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class Error:
|
|
247
|
+
name: str
|
|
248
|
+
description: str
|
|
249
|
+
declaration: str
|
|
250
|
+
|
|
251
|
+
def __init__(self, name: str, description: str, declaration: str):
|
|
252
|
+
self.name = name
|
|
253
|
+
self.description = description
|
|
254
|
+
self.declaration = declaration
|
|
255
|
+
|
|
256
|
+
@staticmethod
|
|
257
|
+
def from_dict(d: dict) -> "Error":
|
|
258
|
+
return Error(**d)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class Event:
|
|
262
|
+
name: str
|
|
263
|
+
description: str
|
|
264
|
+
declaration: str
|
|
265
|
+
|
|
266
|
+
def __init__(self, name: str, description: str, declaration: str):
|
|
267
|
+
self.name = name
|
|
268
|
+
self.description = description
|
|
269
|
+
self.declaration = declaration
|
|
270
|
+
|
|
271
|
+
@staticmethod
|
|
272
|
+
def from_dict(d: dict) -> "Event":
|
|
273
|
+
return Event(**d)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class EnumVariant:
|
|
277
|
+
name: str
|
|
278
|
+
description: str
|
|
279
|
+
|
|
280
|
+
def __init__(self, name: str, description: str):
|
|
281
|
+
self.name = name
|
|
282
|
+
self.description = description
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class Enum:
|
|
286
|
+
name: str
|
|
287
|
+
description: str
|
|
288
|
+
variants: list[EnumVariant]
|
|
289
|
+
|
|
290
|
+
def __init__(self, name: str, description: str, variants: list[EnumVariant]):
|
|
291
|
+
self.name = name
|
|
292
|
+
self.description = description
|
|
293
|
+
self.variants = variants
|
|
294
|
+
|
|
295
|
+
@staticmethod
|
|
296
|
+
def from_dict(d: dict) -> "Enum":
|
|
297
|
+
return Enum(
|
|
298
|
+
d["name"],
|
|
299
|
+
d["description"],
|
|
300
|
+
list(map(lambda v: EnumVariant(**v), d["variants"])),
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class StructField:
|
|
305
|
+
name: str
|
|
306
|
+
ty: str
|
|
307
|
+
description: str
|
|
308
|
+
|
|
309
|
+
def __init__(self, name: str, ty: str, description: str):
|
|
310
|
+
self.name = name
|
|
311
|
+
self.ty = ty
|
|
312
|
+
self.description = description
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
class Struct:
|
|
316
|
+
name: str
|
|
317
|
+
description: str
|
|
318
|
+
fields: list[StructField]
|
|
319
|
+
|
|
320
|
+
def __init__(self, name: str, description: str, fields: list[StructField]):
|
|
321
|
+
self.name = name
|
|
322
|
+
self.description = description
|
|
323
|
+
self.fields = fields
|
|
324
|
+
|
|
325
|
+
@staticmethod
|
|
326
|
+
def from_dict(d: dict) -> "Struct":
|
|
327
|
+
return Struct(
|
|
328
|
+
d["name"],
|
|
329
|
+
d["description"],
|
|
330
|
+
list(map(lambda f: StructField(**f), d["fields"])),
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class Cheatcodes:
|
|
335
|
+
errors: list[Error]
|
|
336
|
+
events: list[Event]
|
|
337
|
+
enums: list[Enum]
|
|
338
|
+
structs: list[Struct]
|
|
339
|
+
cheatcodes: list[Cheatcode]
|
|
340
|
+
|
|
341
|
+
def __init__(
|
|
342
|
+
self,
|
|
343
|
+
errors: list[Error],
|
|
344
|
+
events: list[Event],
|
|
345
|
+
enums: list[Enum],
|
|
346
|
+
structs: list[Struct],
|
|
347
|
+
cheatcodes: list[Cheatcode],
|
|
348
|
+
):
|
|
349
|
+
self.errors = errors
|
|
350
|
+
self.events = events
|
|
351
|
+
self.enums = enums
|
|
352
|
+
self.structs = structs
|
|
353
|
+
self.cheatcodes = cheatcodes
|
|
354
|
+
|
|
355
|
+
@staticmethod
|
|
356
|
+
def from_dict(d: dict) -> "Cheatcodes":
|
|
357
|
+
return Cheatcodes(
|
|
358
|
+
errors=[Error.from_dict(e) for e in d["errors"]],
|
|
359
|
+
events=[Event.from_dict(e) for e in d["events"]],
|
|
360
|
+
enums=[Enum.from_dict(e) for e in d["enums"]],
|
|
361
|
+
structs=[Struct.from_dict(e) for e in d["structs"]],
|
|
362
|
+
cheatcodes=[Cheatcode.from_dict(e) for e in d["cheatcodes"]],
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
@staticmethod
|
|
366
|
+
def from_json(s) -> "Cheatcodes":
|
|
367
|
+
return Cheatcodes.from_dict(json.loads(s))
|
|
368
|
+
|
|
369
|
+
@staticmethod
|
|
370
|
+
def from_json_file(file_path: str) -> "Cheatcodes":
|
|
371
|
+
with open(file_path, "r") as f:
|
|
372
|
+
return Cheatcodes.from_dict(json.load(f))
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class Item(PyEnum):
|
|
376
|
+
ERROR: str = "error"
|
|
377
|
+
EVENT: str = "event"
|
|
378
|
+
ENUM: str = "enum"
|
|
379
|
+
STRUCT: str = "struct"
|
|
380
|
+
FUNCTION: str = "function"
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
class ItemOrder:
|
|
384
|
+
_list: list[Item]
|
|
385
|
+
|
|
386
|
+
def __init__(self, list: list[Item]) -> None:
|
|
387
|
+
assert len(list) <= len(Item), "list must not contain more items than Item"
|
|
388
|
+
assert len(list) == len(set(list)), "list must not contain duplicates"
|
|
389
|
+
self._list = list
|
|
390
|
+
pass
|
|
391
|
+
|
|
392
|
+
def get_list(self) -> list[Item]:
|
|
393
|
+
return self._list
|
|
394
|
+
|
|
395
|
+
@staticmethod
|
|
396
|
+
def default() -> "ItemOrder":
|
|
397
|
+
return ItemOrder(
|
|
398
|
+
[
|
|
399
|
+
Item.ERROR,
|
|
400
|
+
Item.EVENT,
|
|
401
|
+
Item.ENUM,
|
|
402
|
+
Item.STRUCT,
|
|
403
|
+
Item.FUNCTION,
|
|
404
|
+
]
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
class CheatcodesPrinter:
|
|
409
|
+
buffer: str
|
|
410
|
+
|
|
411
|
+
prelude: bool
|
|
412
|
+
spdx_identifier: str
|
|
413
|
+
solidity_requirement: str
|
|
414
|
+
|
|
415
|
+
block_doc_style: bool
|
|
416
|
+
|
|
417
|
+
indent_level: int
|
|
418
|
+
_indent_str: str
|
|
419
|
+
|
|
420
|
+
nl_str: str
|
|
421
|
+
|
|
422
|
+
items_order: ItemOrder
|
|
423
|
+
|
|
424
|
+
def __init__(
|
|
425
|
+
self,
|
|
426
|
+
buffer: str = "",
|
|
427
|
+
prelude: bool = True,
|
|
428
|
+
spdx_identifier: str = "UNLICENSED",
|
|
429
|
+
solidity_requirement: str = "",
|
|
430
|
+
block_doc_style: bool = False,
|
|
431
|
+
indent_level: int = 0,
|
|
432
|
+
indent_with: int | str = 4,
|
|
433
|
+
nl_str: str = "\n",
|
|
434
|
+
items_order: ItemOrder = ItemOrder.default(),
|
|
435
|
+
):
|
|
436
|
+
self.prelude = prelude
|
|
437
|
+
self.spdx_identifier = spdx_identifier
|
|
438
|
+
self.solidity_requirement = solidity_requirement
|
|
439
|
+
self.block_doc_style = block_doc_style
|
|
440
|
+
self.buffer = buffer
|
|
441
|
+
self.indent_level = indent_level
|
|
442
|
+
self.nl_str = nl_str
|
|
443
|
+
|
|
444
|
+
if isinstance(indent_with, int):
|
|
445
|
+
assert indent_with >= 0
|
|
446
|
+
self._indent_str = " " * indent_with
|
|
447
|
+
elif isinstance(indent_with, str):
|
|
448
|
+
self._indent_str = indent_with
|
|
449
|
+
else:
|
|
450
|
+
assert False, "indent_with must be int or str"
|
|
451
|
+
|
|
452
|
+
self.items_order = items_order
|
|
453
|
+
|
|
454
|
+
def finish(self) -> str:
|
|
455
|
+
ret = self.buffer.rstrip()
|
|
456
|
+
self.buffer = ""
|
|
457
|
+
return ret
|
|
458
|
+
|
|
459
|
+
def p_contract(self, contract: Cheatcodes, name: str, inherits: str = ""):
|
|
460
|
+
if self.prelude:
|
|
461
|
+
self.p_prelude(contract)
|
|
462
|
+
|
|
463
|
+
self._p_str("interface ")
|
|
464
|
+
name = name.strip()
|
|
465
|
+
if name != "":
|
|
466
|
+
self._p_str(name)
|
|
467
|
+
self._p_str(" ")
|
|
468
|
+
if inherits != "":
|
|
469
|
+
self._p_str("is ")
|
|
470
|
+
self._p_str(inherits)
|
|
471
|
+
self._p_str(" ")
|
|
472
|
+
self._p_str("{")
|
|
473
|
+
self._p_nl()
|
|
474
|
+
self._with_indent(lambda: self._p_items(contract))
|
|
475
|
+
self._p_str("}")
|
|
476
|
+
self._p_nl()
|
|
477
|
+
|
|
478
|
+
def _p_items(self, contract: Cheatcodes):
|
|
479
|
+
for item in self.items_order.get_list():
|
|
480
|
+
if item == Item.ERROR:
|
|
481
|
+
self.p_errors(contract.errors)
|
|
482
|
+
elif item == Item.EVENT:
|
|
483
|
+
self.p_events(contract.events)
|
|
484
|
+
elif item == Item.ENUM:
|
|
485
|
+
self.p_enums(contract.enums)
|
|
486
|
+
elif item == Item.STRUCT:
|
|
487
|
+
self.p_structs(contract.structs)
|
|
488
|
+
elif item == Item.FUNCTION:
|
|
489
|
+
self.p_functions(contract.cheatcodes)
|
|
490
|
+
else:
|
|
491
|
+
assert False, f"unknown item {item}"
|
|
492
|
+
|
|
493
|
+
def p_prelude(self, contract: Cheatcodes | None = None):
|
|
494
|
+
self._p_str(f"// SPDX-License-Identifier: {self.spdx_identifier}")
|
|
495
|
+
self._p_nl()
|
|
496
|
+
|
|
497
|
+
if self.solidity_requirement != "":
|
|
498
|
+
req = self.solidity_requirement
|
|
499
|
+
else:
|
|
500
|
+
req = ">=0.8.13 <0.9.0"
|
|
501
|
+
self._p_str(f"pragma solidity {req};")
|
|
502
|
+
self._p_nl()
|
|
503
|
+
|
|
504
|
+
self._p_nl()
|
|
505
|
+
|
|
506
|
+
def p_errors(self, errors: list[Error]):
|
|
507
|
+
for error in errors:
|
|
508
|
+
self._p_line(lambda: self.p_error(error))
|
|
509
|
+
|
|
510
|
+
def p_error(self, error: Error):
|
|
511
|
+
self._p_comment(error.description, doc=True)
|
|
512
|
+
self._p_line(lambda: self._p_str(error.declaration))
|
|
513
|
+
|
|
514
|
+
def p_events(self, events: list[Event]):
|
|
515
|
+
for event in events:
|
|
516
|
+
self._p_line(lambda: self.p_event(event))
|
|
517
|
+
|
|
518
|
+
def p_event(self, event: Event):
|
|
519
|
+
self._p_comment(event.description, doc=True)
|
|
520
|
+
self._p_line(lambda: self._p_str(event.declaration))
|
|
521
|
+
|
|
522
|
+
def p_enums(self, enums: list[Enum]):
|
|
523
|
+
for enum in enums:
|
|
524
|
+
self._p_line(lambda: self.p_enum(enum))
|
|
525
|
+
|
|
526
|
+
def p_enum(self, enum: Enum):
|
|
527
|
+
self._p_comment(enum.description, doc=True)
|
|
528
|
+
self._p_line(lambda: self._p_str(f"enum {enum.name} {{"))
|
|
529
|
+
self._with_indent(lambda: self.p_enum_variants(enum.variants))
|
|
530
|
+
self._p_line(lambda: self._p_str("}"))
|
|
531
|
+
|
|
532
|
+
def p_enum_variants(self, variants: list[EnumVariant]):
|
|
533
|
+
for i, variant in enumerate(variants):
|
|
534
|
+
self._p_indent()
|
|
535
|
+
self._p_comment(variant.description)
|
|
536
|
+
|
|
537
|
+
self._p_indent()
|
|
538
|
+
self._p_str(variant.name)
|
|
539
|
+
if i < len(variants) - 1:
|
|
540
|
+
self._p_str(",")
|
|
541
|
+
self._p_nl()
|
|
542
|
+
|
|
543
|
+
def p_structs(self, structs: list[Struct]):
|
|
544
|
+
for struct in structs:
|
|
545
|
+
self._p_line(lambda: self.p_struct(struct))
|
|
546
|
+
|
|
547
|
+
def p_struct(self, struct: Struct):
|
|
548
|
+
self._p_comment(struct.description, doc=True)
|
|
549
|
+
self._p_line(lambda: self._p_str(f"struct {struct.name} {{"))
|
|
550
|
+
self._with_indent(lambda: self.p_struct_fields(struct.fields))
|
|
551
|
+
self._p_line(lambda: self._p_str("}"))
|
|
552
|
+
|
|
553
|
+
def p_struct_fields(self, fields: list[StructField]):
|
|
554
|
+
for field in fields:
|
|
555
|
+
self._p_line(lambda: self.p_struct_field(field))
|
|
556
|
+
|
|
557
|
+
def p_struct_field(self, field: StructField):
|
|
558
|
+
self._p_comment(field.description)
|
|
559
|
+
self._p_indented(lambda: self._p_str(f"{field.ty} {field.name};"))
|
|
560
|
+
|
|
561
|
+
def p_functions(self, cheatcodes: list[Cheatcode]):
|
|
562
|
+
for cheatcode in cheatcodes:
|
|
563
|
+
self._p_line(lambda: self.p_function(cheatcode.func))
|
|
564
|
+
|
|
565
|
+
def p_function(self, func: Function):
|
|
566
|
+
self._p_comment(func.description, doc=True)
|
|
567
|
+
self._p_line(lambda: self._p_str(func.declaration))
|
|
568
|
+
|
|
569
|
+
def _p_comment(self, s: str, doc: bool = False):
|
|
570
|
+
s = s.strip()
|
|
571
|
+
if s == "":
|
|
572
|
+
return
|
|
573
|
+
|
|
574
|
+
s = map(lambda line: line.lstrip(), s.split("\n"))
|
|
575
|
+
if self.block_doc_style:
|
|
576
|
+
self._p_str("/*")
|
|
577
|
+
if doc:
|
|
578
|
+
self._p_str("*")
|
|
579
|
+
self._p_nl()
|
|
580
|
+
for line in s:
|
|
581
|
+
self._p_indent()
|
|
582
|
+
self._p_str(" ")
|
|
583
|
+
if doc:
|
|
584
|
+
self._p_str("* ")
|
|
585
|
+
self._p_str(line)
|
|
586
|
+
self._p_nl()
|
|
587
|
+
self._p_indent()
|
|
588
|
+
self._p_str(" */")
|
|
589
|
+
self._p_nl()
|
|
590
|
+
else:
|
|
591
|
+
first_line = True
|
|
592
|
+
for line in s:
|
|
593
|
+
if not first_line:
|
|
594
|
+
self._p_indent()
|
|
595
|
+
first_line = False
|
|
596
|
+
|
|
597
|
+
if doc:
|
|
598
|
+
self._p_str("/// ")
|
|
599
|
+
else:
|
|
600
|
+
self._p_str("// ")
|
|
601
|
+
self._p_str(line)
|
|
602
|
+
self._p_nl()
|
|
603
|
+
|
|
604
|
+
def _with_indent(self, f: VoidFn):
|
|
605
|
+
self._inc_indent()
|
|
606
|
+
f()
|
|
607
|
+
self._dec_indent()
|
|
608
|
+
|
|
609
|
+
def _p_line(self, f: VoidFn):
|
|
610
|
+
self._p_indent()
|
|
611
|
+
f()
|
|
612
|
+
self._p_nl()
|
|
613
|
+
|
|
614
|
+
def _p_indented(self, f: VoidFn):
|
|
615
|
+
self._p_indent()
|
|
616
|
+
f()
|
|
617
|
+
|
|
618
|
+
def _p_indent(self):
|
|
619
|
+
for _ in range(self.indent_level):
|
|
620
|
+
self._p_str(self._indent_str)
|
|
621
|
+
|
|
622
|
+
def _p_nl(self):
|
|
623
|
+
self._p_str(self.nl_str)
|
|
624
|
+
|
|
625
|
+
def _p_str(self, txt: str):
|
|
626
|
+
self.buffer += txt
|
|
627
|
+
|
|
628
|
+
def _inc_indent(self):
|
|
629
|
+
self.indent_level += 1
|
|
630
|
+
|
|
631
|
+
def _dec_indent(self):
|
|
632
|
+
self.indent_level -= 1
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
if __name__ == "__main__":
|
|
636
|
+
main()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
2
|
+
pragma solidity >=0.8.13 <0.9.0;
|
|
3
|
+
|
|
4
|
+
import {StdStorage} from "./StdStorage.sol";
|
|
5
|
+
import {Vm, VmSafe} from "./Vm.sol";
|
|
6
|
+
|
|
7
|
+
abstract contract CommonBase {
|
|
8
|
+
/// @dev Cheat code address.
|
|
9
|
+
/// Calculated as `address(uint160(uint256(keccak256("hevm cheat code"))))`.
|
|
10
|
+
address internal constant VM_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
|
|
11
|
+
|
|
12
|
+
/// @dev console.sol and console2.sol work by executing a staticcall to this address.
|
|
13
|
+
/// Calculated as `address(uint160(uint88(bytes11("console.log"))))`.
|
|
14
|
+
address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67;
|
|
15
|
+
|
|
16
|
+
/// @dev Used when deploying with create2.
|
|
17
|
+
/// Taken from https://github.com/Arachnid/deterministic-deployment-proxy.
|
|
18
|
+
address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
|
|
19
|
+
|
|
20
|
+
/// @dev The default address for tx.origin and msg.sender.
|
|
21
|
+
/// Calculated as `address(uint160(uint256(keccak256("foundry default caller"))))`.
|
|
22
|
+
address internal constant DEFAULT_SENDER = 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38;
|
|
23
|
+
|
|
24
|
+
/// @dev The address of the first contract `CREATE`d by a running test contract.
|
|
25
|
+
/// When running tests, each test contract is `CREATE`d by `DEFAULT_SENDER` with nonce 1.
|
|
26
|
+
/// Calculated as `VM.computeCreateAddress(VM.computeCreateAddress(DEFAULT_SENDER, 1), 1)`.
|
|
27
|
+
address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f;
|
|
28
|
+
|
|
29
|
+
/// @dev Deterministic deployment address of the Multicall3 contract.
|
|
30
|
+
/// Taken from https://www.multicall3.com.
|
|
31
|
+
address internal constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11;
|
|
32
|
+
|
|
33
|
+
/// @dev The order of the secp256k1 curve.
|
|
34
|
+
uint256 internal constant SECP256K1_ORDER =
|
|
35
|
+
115792089237316195423570985008687907852837564279074904382605163141518161494337;
|
|
36
|
+
|
|
37
|
+
uint256 internal constant UINT256_MAX =
|
|
38
|
+
115792089237316195423570985008687907853269984665640564039457584007913129639935;
|
|
39
|
+
|
|
40
|
+
Vm internal constant vm = Vm(VM_ADDRESS);
|
|
41
|
+
StdStorage internal stdstore;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
abstract contract TestBase is CommonBase {}
|
|
45
|
+
|
|
46
|
+
abstract contract ScriptBase is CommonBase {
|
|
47
|
+
VmSafe internal constant vmSafe = VmSafe(VM_ADDRESS);
|
|
48
|
+
}
|