From 60effa443463e92fc332821010dc0a87f6b6c1fe Mon Sep 17 00:00:00 2001 From: Maxim Andreev Date: Wed, 4 Dec 2024 16:41:55 +0000 Subject: [PATCH] add new contract_info API, add experimental storage layout extractor --- Cargo.lock | 42 +- Cargo.toml | 9 +- README.md | 76 +- benchmark/providers/evmole-js/main.mjs | 18 +- benchmark/providers/evmole-py/main.py | 22 +- benchmark/providers/evmole-rs/Cargo.lock | 91 ++ benchmark/providers/evmole-rs/src/main.rs | 111 +- evmole.pyi | 91 +- javascript/Makefile | 2 +- javascript/README.md | 85 +- javascript/examples/esbuild/main.js | 10 +- javascript/examples/node/with_import.mjs | 17 +- javascript/examples/node/with_require.cjs | 13 +- javascript/examples/parcel/src/app.js | 11 +- .../examples/parcel_packageExports/src/app.js | 11 +- javascript/examples/vite/main.js | 10 +- javascript/examples/webpack/index.js | 10 +- javascript/package-lock.json | 1005 +++++++------ javascript/package.json | 4 +- javascript/src/evmole_esm.js | 2 +- javascript/src/evmole_node.cjs | 2 +- javascript/src/evmole_node.mjs | 2 +- javascript/src/evmole_wasm_import.js | 2 +- javascript/tests/package-lock.json | 33 +- javascript/tests/package.json | 2 +- python/README.md | 98 +- python/test_python.py | 18 +- src/arguments/mod.rs | 3 + src/contract_info.rs | 166 +++ src/evm/calldata.rs | 4 + src/evm/stack.rs | 5 +- src/evm/vm.rs | 18 +- src/interface_js.rs | 207 +++ src/interface_py.rs | 185 ++- src/lib.rs | 27 +- src/selectors/mod.rs | 1 + src/state_mutability/mod.rs | 1 + src/storage/calldata.rs | 226 +++ src/storage/keccak_precalc.rs | 1283 +++++++++++++++++ src/storage/mod.rs | 713 +++++++++ 40 files changed, 3962 insertions(+), 674 deletions(-) create mode 100644 src/contract_info.rs create mode 100644 src/storage/calldata.rs create mode 100644 src/storage/keccak_precalc.rs create mode 100644 src/storage/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 7f2ff7b..82b90c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -569,6 +569,8 @@ dependencies = [ "alloy-dyn-abi", "alloy-primitives", "pyo3", + "serde", + "serde-wasm-bindgen", "wasm-bindgen", ] @@ -754,6 +756,15 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "k256" version = "0.13.4" @@ -1008,9 +1019,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.22.6" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" +checksum = "f54b3d09cbdd1f8c20650b28e7b09e338881482f4aa908a5f61a00c98fba2690" dependencies = [ "cfg-if", "indoc", @@ -1026,9 +1037,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.22.6" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" +checksum = "3015cf985888fe66cfb63ce0e321c603706cd541b7aec7ddd35c281390af45d8" dependencies = [ "once_cell", "target-lexicon", @@ -1036,9 +1047,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.22.6" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" +checksum = "6fca7cd8fd809b5ac4eefb89c1f98f7a7651d3739dfb341ca6980090f554c270" dependencies = [ "libc", "pyo3-build-config", @@ -1046,9 +1057,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.22.6" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" +checksum = "34e657fa5379a79151b6ff5328d9216a84f55dc93b17b08e7c3609a969b73aa0" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -1058,9 +1069,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.22.6" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" +checksum = "295548d5ffd95fd1981d2d3cf4458831b21d60af046b729b6fd143b0ba7aee2f" dependencies = [ "heck", "proc-macro2", @@ -1300,6 +1311,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_derive" version = "1.0.215" diff --git a/Cargo.toml b/Cargo.toml index 0e3efc1..8cb1fa4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,18 +13,21 @@ exclude = ["/javascript", "/python", "/benchmark", "/.github"] alloy-primitives = "0.8" alloy-dyn-abi = "0.8" -pyo3 = { version = "0.22.2", features = ["extension-module"], optional = true } +pyo3 = { version = "0.23.2", features = ["extension-module"], optional = true } wasm-bindgen = { version = "0.2", optional = true } +serde-wasm-bindgen = { version = "0.6.5", optional = true } +serde = { version = "1.0", features = ["derive"], optional = true } [features] python = ["dep:pyo3"] -javascript = ["dep:wasm-bindgen"] +javascript = ["dep:wasm-bindgen", "dep:serde-wasm-bindgen", "dep:serde"] # for dev trace_selectors = [] trace_arguments = [] trace_mutability = [] -trace = ["trace_selectors", "trace_arguments", "trace_mutability"] +trace_storage = [] +trace = ["trace_selectors", "trace_arguments", "trace_mutability", "trace_storage"] [lib] crate-type = ["cdylib", "lib"] diff --git a/README.md b/README.md index da8bfaa..ad0e7ea 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # EVMole -[![try it online](https://img.shields.io/badge/Try_It_Online-github.io-brightgreen)](https://cdump.github.io/evmole/) +[![try it online](https://img.shields.io/badge/Try_It_Online-evmole.xyz-brightgreen)](https://evmole.xyz/) [![npm](https://img.shields.io/npm/v/evmole)](https://www.npmjs.com/package/evmole) [![Crates.io](https://img.shields.io/crates/v/evmole?color=e9b44f)](https://crates.io/crates/evmole) [![PyPI](https://img.shields.io/pypi/v/evmole?color=006dad)](https://pypi.org/project/evmole) -EVMole is a powerful library that extracts information from Ethereum Virtual Machine (EVM) bytecode, including [function selectors](https://docs.soliditylang.org/en/latest/abi-spec.html#function-selector), arguments, and [state mutability](https://docs.soliditylang.org/en/latest/contracts.html#state-mutability), even for unverified contracts. +EVMole is a powerful library that extracts information from Ethereum Virtual Machine (EVM) bytecode, including [function selectors](https://docs.soliditylang.org/en/latest/abi-spec.html#function-selector), arguments, [state mutability](https://docs.soliditylang.org/en/latest/contracts.html#state-mutability), and storage layout, even for unverified contracts. ## Key Features @@ -24,13 +24,28 @@ EVMole is a powerful library that extracts information from Ethereum Virtual Mac $ npm i evmole ``` ```javascript -import { functionSelectors, functionArguments, functionStateMutability } from 'evmole' +import { contractInfo } from 'evmole' const code = '0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256' -console.log(functionSelectors(code)); // [ '2125b65b', 'b69ef8a8' ] -console.log(functionArguments(code, '2125b65b')); // 'uint32,address,uint224' -console.log(functionStateMutability(code, '2125b65b')); // 'pure' +console.log( contractInfo(code, {selectors:true, arguments:true, stateMutability:true}) ) +// { +// functions: [ +// { +// selector: '2125b65b', +// bytecodeOffset: 52, +// arguments: 'uint32,address,uint224', +// stateMutability: 'pure' +// }, +// { +// selector: 'b69ef8a8', +// bytecodeOffset: 68, +// arguments: '', +// stateMutability: 'pure' +// } +// ], +// storage: undefined +// } ``` ### Rust @@ -38,12 +53,30 @@ Documentation is available on [docs.rs](https://docs.rs/evmole/latest/evmole/) ```rust let code = hex::decode("6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256").unwrap(); -println!("{:x?} | {} | {:?}", - evmole::function_selectors(&code, 0), - evmole::function_arguments(&code, &[0x21, 0x25, 0xb6, 0x5b], 0), - evmole::function_state_mutability(&code, &[0x21, 0x25, 0xb6, 0x5b], 0), +println!("{:?}", evmole::contract_info( + evmole::ContractInfoArgs::new(&code) + .with_selectors() + .with_arguments() + .with_state_mutability() + ) ); -// [[21, 25, b6, 5b], [b6, 9e, f8, a8]] | uint32,address,uint224 | Pure +// Contract { +// functions: Some([ +// Function { +// selector: [33, 37, 182, 91], +// bytecode_offset: 52, +// arguments: Some([Uint(32), Address, Uint(224)]), +// state_mutability: Some(Pure) +// }, +// Function { +// selector: [182, 158, 248, 168], +// bytecode_offset: 68, +// arguments: Some([]), +// state_mutability: Some(Pure) +// } +// ]), +// storage: None +// } ``` ### Python @@ -52,13 +85,26 @@ println!("{:x?} | {} | {:?}", $ pip install evmole --upgrade ``` ```python -from evmole import function_selectors, function_arguments, function_state_mutability +from evmole import contract_info code = '0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256' -print(function_selectors(code)) # ['2125b65b', 'b69ef8a8'] -print(function_arguments(code, '2125b65b')) # uint32,address,uint224 -print(function_state_mutability(code, '2125b65b')) # pure +print( contract_info(code, selectors=True, arguments=True, state_mutability=True) ) +# Contract( +# functions=[ +# Function( +# selector=2125b65b, +# bytecode_offset=52, +# arguments=uint32,address,uint224, +# state_mutability=pure), +# Function( +# selector=b69ef8a8, +# bytecode_offset=68, +# arguments=, +# state_mutability=pure) +# ], +# storage=None +# ) ``` ### Foundry diff --git a/benchmark/providers/evmole-js/main.mjs b/benchmark/providers/evmole-js/main.mjs index dda1fbc..371298e 100644 --- a/benchmark/providers/evmole-js/main.mjs +++ b/benchmark/providers/evmole-js/main.mjs @@ -1,7 +1,7 @@ import { readdirSync, readFileSync, writeFileSync } from 'fs' import { hrtime } from 'process' -import { functionArguments, functionSelectors, functionStateMutability } from 'evmole' +import { contractInfo } from 'evmole' const argv = process.argv; if (argv.length < 5) { @@ -24,14 +24,20 @@ function timeit(fn) { function extract(code, mode, fname) { if (mode === 'selectors') { - let [duration_ms, r] = timeit(() => functionSelectors(code)); + let [duration_ms, r] = timeit(() => contractInfo(code, {selectors: true})); return [duration_ms, r]; } else if (mode === 'arguments') { - let [duration_ms, r] = timeit(() => selectors[fname][1].map((s) => [s, functionArguments(code, s)])); - return [duration_ms, Object.fromEntries(r)]; + let [duration_ms, r] = timeit(() => contractInfo(code, {arguments: true})); + const by_sel = new Map(r.functions.map((f) => [f.selector, f.arguments])); + return [duration_ms, Object.fromEntries( + selectors[fname][1].map((s) => [s, by_sel.get(s) ?? 'notfound']) + )]; } else if (mode === 'mutability') { - let [duration_ms, r] = timeit(() => selectors[fname][1].map((s) => [s, functionStateMutability(code, s)])); - return [duration_ms, Object.fromEntries(r)]; + let [duration_ms, r] = timeit(() => contractInfo(code, {stateMutability: true})); + const by_sel = new Map(r.functions.map((f) => [f.selector, f.stateMutability])); + return [duration_ms, Object.fromEntries( + selectors[fname][1].map((s) => [s, by_sel.get(s) ?? 'notfound']) + )]; } else { throw 'unsupported mode'; } diff --git a/benchmark/providers/evmole-py/main.py b/benchmark/providers/evmole-py/main.py index 508c76d..ee58240 100644 --- a/benchmark/providers/evmole-py/main.py +++ b/benchmark/providers/evmole-py/main.py @@ -3,7 +3,7 @@ import os import time -from evmole import function_arguments, function_selectors, function_state_mutability +from evmole import contract_info parser = argparse.ArgumentParser() parser.add_argument('mode', choices=['selectors', 'arguments', 'mutability']) @@ -24,16 +24,26 @@ code = d['code'] t0 = time.perf_counter() if cfg.mode == 'selectors': - r = function_selectors(code) + r = contract_info(code, selectors=True) elif cfg.mode == 'arguments': - fsel = selectors[fname][1] - r = {s: function_arguments(code, s) for s in fsel} + r = contract_info(code, arguments=True) elif cfg.mode == 'mutability': - fsel = selectors[fname][1] - r = {s: function_state_mutability(code, s) for s in fsel} + r = contract_info(code, state_mutability=True) else: raise Exception(f'Unknown mode {cfg.mode}') duration_ms = int((time.perf_counter() - t0) * 1000) + + if cfg.mode == 'selectors': + r = [f.selector for f in r.functions] + elif cfg.mode == 'arguments': + by_sel = {f.selector: f.arguments for f in r.functions} + r = {s: by_sel.get(s, 'notfound') for s in selectors[fname][1]} + elif cfg.mode == 'mutability': + by_sel = {f.selector: f.state_mutability for f in r.functions} + r = {s: by_sel.get(s, 'notfound') for s in selectors[fname][1]} + else: + raise Exception(f'Unknown mode {cfg.mode}') + ret[fname] = [duration_ms, r] with open(cfg.output_file, 'w') as fh: diff --git a/benchmark/providers/evmole-rs/Cargo.lock b/benchmark/providers/evmole-rs/Cargo.lock index 5a8021c..920be90 100644 --- a/benchmark/providers/evmole-rs/Cargo.lock +++ b/benchmark/providers/evmole-rs/Cargo.lock @@ -400,6 +400,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byte-slice-cast" version = "1.2.2" @@ -668,6 +674,9 @@ version = "0.5.1" dependencies = [ "alloy-dyn-abi", "alloy-primitives", + "serde", + "serde-wasm-bindgen", + "wasm-bindgen", ] [[package]] @@ -846,6 +855,16 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "k256" version = "0.13.4" @@ -902,6 +921,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + [[package]] name = "main" version = "0.1.0" @@ -1312,6 +1337,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_derive" version = "1.0.210" @@ -1584,6 +1620,61 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/benchmark/providers/evmole-rs/src/main.rs b/benchmark/providers/evmole-rs/src/main.rs index 2e93508..3d85cfa 100644 --- a/benchmark/providers/evmole-rs/src/main.rs +++ b/benchmark/providers/evmole-rs/src/main.rs @@ -8,7 +8,8 @@ use hex::FromHex; #[derive(serde::Deserialize)] struct Input { - code: String, + code: Option, + runtimeBytecode: Option, } #[derive(ValueEnum, Clone, PartialEq)] @@ -16,6 +17,7 @@ enum Mode { Selectors, Arguments, Mutability, + Storage, } #[derive(Parser)] @@ -41,7 +43,7 @@ fn main() -> Result<(), Box> { type Meta = u64; // duration in ms let selectors: HashMap)> = match cfg.mode { - Mode::Selectors => HashMap::new(), + Mode::Selectors | Mode::Storage => HashMap::new(), Mode::Arguments | Mode::Mutability => { let file_content = fs::read_to_string(cfg.selectors_file.unwrap())?; serde_json::from_str(&file_content)? @@ -71,7 +73,12 @@ fn main() -> Result<(), Box> { let code = { let file_content = fs::read_to_string(path)?; let v: Input = serde_json::from_str(&file_content)?; - hex::decode(v.code.strip_prefix("0x").expect("0x prefix expected"))? + let code = if v.runtimeBytecode.is_some() { + v.runtimeBytecode.unwrap() + } else { + v.code.unwrap() + }; + hex::decode(code.strip_prefix("0x").expect("0x prefix expected"))? }; // eprintln!("processing {}", fname); @@ -79,12 +86,18 @@ fn main() -> Result<(), Box> { match cfg.mode { Mode::Selectors => { let now = Instant::now(); - let r = evmole::function_selectors(&code, 0); + let info = + evmole::contract_info(evmole::ContractInfoArgs::new(&code).with_selectors()); + let dur = now.elapsed().as_millis() as u64; ret_selectors.insert( fname, ( - now.elapsed().as_millis() as u64, - r.iter().map(hex::encode).collect(), + dur, + info.functions + .unwrap() + .iter() + .map(|f| hex::encode(f.selector)) + .collect(), ), ); } @@ -94,19 +107,43 @@ fn main() -> Result<(), Box> { } else { &selectors[&fname].1 }; + let now = Instant::now(); - let args = fsel + let info = + evmole::contract_info(evmole::ContractInfoArgs::new(&code).with_arguments()); + let dur = now.elapsed().as_millis() as u64; + + let args: HashMap = info + .functions + .unwrap() + .into_iter() + .map(|f| { + ( + hex::encode(f.selector), + f.arguments + .unwrap() + .iter() + .map(|t| t.sol_type_name().to_string()) + .collect::>() + .join(","), + ) + }) + .collect(); + + let res = fsel .iter() .map(|s| { - let selector = <[u8; 4]>::from_hex(s).unwrap(); ( s.to_string(), - evmole::function_arguments(&code, &selector, 0), + match args.get(s) { + Some(v) => v.to_string(), + None => "not_found".to_string(), + }, ) }) .collect(); - ret_other.insert(fname, (now.elapsed().as_millis() as u64, args)); + ret_other.insert(fname, (dur, res)); } Mode::Mutability => { let fsel = if !only_selector.is_empty() { @@ -116,35 +153,53 @@ fn main() -> Result<(), Box> { }; let now = Instant::now(); - let res: HashMap<_, _> = fsel + let info = evmole::contract_info( + evmole::ContractInfoArgs::new(&code).with_state_mutability(), + ); + let dur = now.elapsed().as_millis() as u64; + + let smut: HashMap = info + .functions + .unwrap() + .into_iter() + .map(|f| { + ( + hex::encode(f.selector), + f.state_mutability.unwrap().as_json_str().to_string(), + ) + }) + .collect(); + + let res = fsel .iter() .map(|s| { - let selector = <[u8; 4]>::from_hex(s).unwrap(); ( - selector, - evmole::function_state_mutability(&code, &selector, 0), + s.to_string(), + match smut.get(s) { + Some(v) => v.to_string(), + None => "not_found".to_string(), + }, ) }) .collect(); - let dur = now.elapsed().as_millis() as u64; + ret_other.insert(fname, (dur, res)); + } + + Mode::Storage => { + let now = Instant::now(); + let info = + evmole::contract_info(evmole::ContractInfoArgs::new(&code).with_storage()); + let dur = now.elapsed().as_millis() as u64; ret_other.insert( fname, ( dur, - fsel.iter() - .map(|s| { - let selector = <[u8; 4]>::from_hex(s).unwrap(); - ( - s.to_string(), - if let Some(sm) = res.get(&selector) { - sm.as_json_str().to_string() - } else { - "".to_string() - }, // evmole::function_state_mutability(&code, &selector, 0) - // .as_json_str() - // .to_string() - ) + info.storage + .unwrap() + .into_iter() + .map(|sr| { + (format!("{}_{}", hex::encode(sr.slot), sr.offset), sr.r#type) }) .collect(), ), diff --git a/evmole.pyi b/evmole.pyi index 49262cd..153ef95 100644 --- a/evmole.pyi +++ b/evmole.pyi @@ -1,5 +1,84 @@ -from typing import List, Union +from typing import List, Optional, Union +from warnings import deprecated +class Function: + """ + Represents a public smart contract function. + + Attributes: + selector (str): Function selector as a 4-byte hex string without '0x' prefix (e.g., 'aabbccdd'). + bytecode_offset (int): Starting byte offset within the EVM bytecode for the function body. + arguments (Optional[str]): Function argument types in canonical format (e.g., 'uint256,address[]'). + None if arguments were not extracted + state_mutability (Optional[str]): Function's state mutability ('pure', 'view', 'payable', or 'nonpayable'). + None if state mutability was not extracted + """ + + selector: str + bytecode_offset: int + arguments: Optional[str] + state_mutability: Optional[str] + +class StorageRecord: + """ + Represents a storage variable record in a smart contract's storage layout. + + Attributes: + slot (str): Storage slot number as a hex string (e.g., '0', '1b'). + offset (int): Byte offset within the storage slot (0-31). + type (str): Variable type (e.g., 'uint256', 'mapping(address => uint256)', 'bytes32'). + reads (List[str]): List of function selectors that read from this storage location. + writes (List[str]): List of function selectors that write to this storage location. + """ + + slot: str + offset: int + type: str + reads: List[str] + writes: List[str] + +class Contract: + """ + Contains analyzed information about a smart contract. + + Attributes: + functions (Optional[List[Function]]): List of detected contract functions. + None if no functions were extracted + storage (Optional[List[StorageRecord]]): List of contract storage records. + None if storage layout was not extracted + """ + + functions: Optional[List[Function]] + storage: Optional[List[StorageRecord]] + +def contract_info( + code: Union[bytes, str], + *, + selectors: bool = False, + arguments: bool = False, + state_mutability: bool = False, + storage: bool = False, +) -> Contract: + """ + Extracts information about a smart contract from its EVM bytecode. + + Args: + code (Union[bytes, str]): Runtime bytecode as a hex string (with or without '0x' prefix) + or raw bytes. + selectors (bool, optional): When True, extracts function selectors. Defaults to False. + arguments (bool, optional): When True, extracts function arguments. Defaults to False. + state_mutability (bool, optional): When True, extracts function state mutability. + Defaults to False. + storage (bool, optional): When True, extracts the contract's storage layout. + Defaults to False. + + Returns: + Contract: Object containing the requested smart contract information. Fields that + weren't requested to be extracted will be None. + """ + ... + +@deprecated("Use contract_info() with selectors=True instead") def function_selectors(code: Union[bytes, str], gas_limit: int = 500000) -> List[str]: """ Extracts function selectors from the given bytecode. @@ -13,7 +92,10 @@ def function_selectors(code: Union[bytes, str], gas_limit: int = 500000) -> List """ ... -def function_arguments(code: Union[bytes, str], selector: Union[bytes, str], gas_limit: int = 50000) -> str: +@deprecated("Use contract_info() with arguments=True instead") +def function_arguments( + code: Union[bytes, str], selector: Union[bytes, str], gas_limit: int = 50000 +) -> str: """ Extracts function arguments for a given selector from the bytecode. @@ -27,7 +109,10 @@ def function_arguments(code: Union[bytes, str], selector: Union[bytes, str], gas """ ... -def function_state_mutability(code: Union[bytes, str], selector: Union[bytes, str], gas_limit: int = 500000) -> str: +@deprecated("Use contract_info() with state_mutability=True instead") +def function_state_mutability( + code: Union[bytes, str], selector: Union[bytes, str], gas_limit: int = 500000 +) -> str: """ Extracts function state mutability for a given selector from the bytecode. diff --git a/javascript/Makefile b/javascript/Makefile index 0c81d51..83e9a09 100644 --- a/javascript/Makefile +++ b/javascript/Makefile @@ -31,7 +31,7 @@ ifeq ($(GITHUB_ACTIONS),true) else $(DOCKER) run --network=none --rm \ -v $(shell pwd)/:/mnt \ - -it mcr.microsoft.com/playwright:v1.46.0-jammy \ + -it mcr.microsoft.com/playwright:v1.49.0-jammy \ /bin/bash -c \ 'cd /mnt/tests && npx playwright test' endif diff --git a/javascript/README.md b/javascript/README.md index e2eca56..57f3460 100644 --- a/javascript/README.md +++ b/javascript/README.md @@ -14,13 +14,13 @@ TODO: fix jsdelivr esm import import { functionSelectors } from 'https://cdn.jsdelivr.net/npm/evmole/+esm'; --> ```html -
+
``` @@ -83,7 +83,7 @@ After that, you can use it as: const bytecode = '0x6080...'; // Replace with actual bytecode async function main() { await init(); - console.log(functionSelectors(bytecode)); + console.log(contractInfo(bytecode, {selectors: true})); } main() ``` @@ -91,7 +91,7 @@ or ```javascript const bytecode = '0x6080...'; // Replace with actual bytecode init().then() => { - console.log(functionSelectors(bytecode)); + console.log(contractInfo(bytecode, {selectors: true})); } ``` @@ -100,21 +100,70 @@ See full example without Top Level Await in [Parcel example](./examples/parcel/s ### API -
-
functionSelectors(code, gas_limit)Array.<string>
-

Extracts function selectors from the given bytecode.

-
-
functionArguments(code, selector, gas_limit)string
-

Extracts function arguments for a given selector from the bytecode.

-
-
functionStateMutability(code, selector, gas_limit)string
-

Extracts function state mutability for a given selector from the bytecode.

-
-
+ + +### contractInfo(code, args) ⇒ [Contract](#Contract) +Analyzes contract bytecode and returns contract information based on specified options. + +**Kind**: global function +**Returns**: [Contract](#Contract) - Analyzed contract information + +| Param | Type | Description | +| --- | --- | --- | +| code | string | Runtime bytecode as a hex string | +| args | Object | Configuration options for the analysis | +| [args.selectors] | boolean | When true, includes function selectors in the output | +| [args.arguments] | boolean | When true, includes function arguments information | +| [args.state_mutability] | boolean | When true, includes state mutability information for functions | +| [args.storage] | boolean | When true, includes contract storage layout information | + + + + +### Contract : Object +**Kind**: global typedef +**Properties** + +| Name | Type | Description | +| --- | --- | --- | +| [functions] | [Array.<ContractFunction>](#ContractFunction) | Array of functions found in the contract. Not present if no functions were extracted | +| [storage] | [Array.<StorageRecord>](#StorageRecord) | Array of storage records found in the contract. Not present if storage layout was not extracted | + + +### ContractFunction : Object +**Kind**: global typedef +**Properties** + +| Name | Type | Description | +| --- | --- | --- | +| selector | string | Function selector as a 4-byte hex string without '0x' prefix (e.g., 'aabbccdd') | +| bytecode_offset | number | Starting byte offset within the EVM bytecode for the function body | +| [arguments] | string | Function argument types in canonical format (e.g., 'uint256,address[]'). Not present if arguments were not extracted | +| [state_mutability] | string | Function's state mutability ("pure", "view", "payable", or "nonpayable"). Not present if state mutability were not extracted | + + + + +### StorageRecord : Object +**Kind**: global typedef +**Properties** + +| Name | Type | Description | +| --- | --- | --- | +| slot | string | Storage slot number as a hex string (e.g., '0', '1b') | +| offset | number | Byte offset within the storage slot (0-31) | +| type | string | Variable type (e.g., 'uint256', 'mapping(address => uint256)', 'bytes32') | +| reads | Array.<string> | Array of function selectors that read from this storage location | +| writes | Array.<string> | Array of function selectors that write to this storage location | + + +### Deprecated API ### functionSelectors(code, gas_limit) ⇒ Array.<string> +**Please use [contractInfo(code, {selectors: true})](#contractInfo) instead** + Extracts function selectors from the given bytecode. **Returns**: Array.<string> - Function selectors as a hex strings @@ -127,6 +176,8 @@ Extracts function selectors from the given bytecode. ### functionArguments(code, selector, gas_limit) ⇒ string +**Please use [contractInfo(code, {arguments: true})](#contractInfo) instead** + Extracts function arguments for a given selector from the bytecode. **Returns**: string - Function arguments (ex: 'uint32,address') @@ -140,6 +191,8 @@ Extracts function arguments for a given selector from the bytecode. ### functionStateMutability(code, selector, gas_limit) ⇒ string +**Please use [contractInfo(code, {stateMutability: true})](#contractInfo) instead** + Extracts function state mutability for a given selector from the bytecode. **Returns**: string - `payable` | `nonpayable` | `view` | `pure` diff --git a/javascript/examples/esbuild/main.js b/javascript/examples/esbuild/main.js index 1204eff..34235b0 100644 --- a/javascript/examples/esbuild/main.js +++ b/javascript/examples/esbuild/main.js @@ -1,4 +1,4 @@ -import { functionSelectors, functionArguments, functionStateMutability } from 'evmole/wasm_import' +import { contractInfo } from 'evmole/wasm_import' document.body.innerHTML = `
@@ -10,8 +10,10 @@ document.body.innerHTML = ` const code = '6080604052348015600e575f80fd5b50600436106026575f3560e01c8063fae7ab8214602a575b5f80fd5b603960353660046062565b6052565b60405163ffffffff909116815260200160405180910390f35b5f605c826001608a565b92915050565b5f602082840312156071575f80fd5b813563ffffffff811681146083575f80fd5b9392505050565b63ffffffff8181168382160190811115605c57634e487b7160e01b5f52601160045260245ffd'; -document.getElementById('selectors').innerText = functionSelectors(code); +const info = contractInfo(code, { selectors: false, arguments: true, stateMutability: true }); -document.getElementById('arguments').innerText = functionArguments(code, 'fae7ab82'); +document.getElementById('selectors').innerText = info.functions[0].selector; -document.getElementById('state_mutability').innerText = functionStateMutability(code, 'fae7ab82'); +document.getElementById('arguments').innerText = info.functions[0].arguments; + +document.getElementById('state_mutability').innerText = info.functions[0].stateMutability; diff --git a/javascript/examples/node/with_import.mjs b/javascript/examples/node/with_import.mjs index 8cbb59d..3d5495b 100644 --- a/javascript/examples/node/with_import.mjs +++ b/javascript/examples/node/with_import.mjs @@ -1,13 +1,10 @@ -import {functionArguments, functionSelectors, functionStateMutability} from 'evmole'; -import {equal, deepEqual} from 'node:assert'; +import { contractInfo } from 'evmole'; +import { equal, deepEqual } from 'node:assert'; const code = '6080604052348015600e575f80fd5b50600436106026575f3560e01c8063fae7ab8214602a575b5f80fd5b603960353660046062565b6052565b60405163ffffffff909116815260200160405180910390f35b5f605c826001608a565b92915050565b5f602082840312156071575f80fd5b813563ffffffff811681146083575f80fd5b9392505050565b63ffffffff8181168382160190811115605c57634e487b7160e01b5f52601160045260245ffd'; -const r = functionSelectors(code); -deepEqual(r, ['fae7ab82']); - -const a = functionArguments(code, 'fae7ab82'); -equal(a, 'uint32'); - -const m = functionStateMutability(code, 'fae7ab82'); -equal(m, 'pure'); +const info = contractInfo(code, { selectors: false, arguments: true, stateMutability: true }); +equal(info.functions.length, 1); +equal(info.functions[0].selector, 'fae7ab82'); +equal(info.functions[0].arguments, 'uint32'); +equal(info.functions[0].stateMutability, 'pure'); diff --git a/javascript/examples/node/with_require.cjs b/javascript/examples/node/with_require.cjs index f78e9d4..97c6896 100644 --- a/javascript/examples/node/with_require.cjs +++ b/javascript/examples/node/with_require.cjs @@ -3,11 +3,8 @@ const evmole = require('evmole'); const code = '6080604052348015600e575f80fd5b50600436106026575f3560e01c8063fae7ab8214602a575b5f80fd5b603960353660046062565b6052565b60405163ffffffff909116815260200160405180910390f35b5f605c826001608a565b92915050565b5f602082840312156071575f80fd5b813563ffffffff811681146083575f80fd5b9392505050565b63ffffffff8181168382160190811115605c57634e487b7160e01b5f52601160045260245ffd'; -const r = evmole.functionSelectors(code); -assert.deepEqual(r, ['fae7ab82']); - -const a = evmole.functionArguments(code, 'fae7ab82'); -assert.equal(a, 'uint32'); - -const m = evmole.functionStateMutability(code, 'fae7ab82'); -assert.equal(m, 'pure'); +const info = evmole.contractInfo(code, { selectors: false, arguments: true, stateMutability: true }); +assert.equal(info.functions.length, 1); +assert.equal(info.functions[0].selector, 'fae7ab82'); +assert.equal(info.functions[0].arguments, 'uint32'); +assert.equal(info.functions[0].stateMutability, 'pure'); diff --git a/javascript/examples/parcel/src/app.js b/javascript/examples/parcel/src/app.js index d46a50f..4d7da38 100644 --- a/javascript/examples/parcel/src/app.js +++ b/javascript/examples/parcel/src/app.js @@ -1,4 +1,4 @@ -import init, { functionSelectors, functionArguments, functionStateMutability } from 'evmole/dist/evmole.js' +import init, { contractInfo } from 'evmole/dist/evmole.js' // https://parceljs.org/blog/v2-9-0/#new-resolver document.body.innerHTML = ` @@ -14,9 +14,12 @@ const code = '6080604052348015600e575f80fd5b50600436106026575f3560e01c8063fae7ab // or: `init().then() => { }` async function main() { await init(); - document.getElementById('selectors').innerText = functionSelectors(code); - document.getElementById('arguments').innerText = functionArguments(code, 'fae7ab82'); - document.getElementById('state_mutability').innerText = functionStateMutability(code, 'fae7ab82'); + + const info = contractInfo(code, { selectors: false, arguments: true, stateMutability: true }); + + document.getElementById('selectors').innerText = info.functions[0].selector; + document.getElementById('arguments').innerText = info.functions[0].arguments; + document.getElementById('state_mutability').innerText = info.functions[0].stateMutability; } main(); diff --git a/javascript/examples/parcel_packageExports/src/app.js b/javascript/examples/parcel_packageExports/src/app.js index 6bad62b..35d1bc5 100644 --- a/javascript/examples/parcel_packageExports/src/app.js +++ b/javascript/examples/parcel_packageExports/src/app.js @@ -1,4 +1,4 @@ -import init, { functionSelectors, functionArguments, functionStateMutability } from 'evmole/no_tla' +import init, { contractInfo } from 'evmole/no_tla' // https://parceljs.org/blog/v2-9-0/#new-resolver document.body.innerHTML = ` @@ -14,9 +14,12 @@ const code = '6080604052348015600e575f80fd5b50600436106026575f3560e01c8063fae7ab // or: `init().then() => { }` async function main() { await init(); - document.getElementById('selectors').innerText = functionSelectors(code); - document.getElementById('arguments').innerText = functionArguments(code, 'fae7ab82'); - document.getElementById('state_mutability').innerText = functionStateMutability(code, 'fae7ab82'); + + const info = contractInfo(code, { selectors: false, arguments: true, stateMutability: true }); + + document.getElementById('selectors').innerText = info.functions[0].selector; + document.getElementById('arguments').innerText = info.functions[0].arguments; + document.getElementById('state_mutability').innerText = info.functions[0].stateMutability; } main(); diff --git a/javascript/examples/vite/main.js b/javascript/examples/vite/main.js index a5347c4..7c151a2 100644 --- a/javascript/examples/vite/main.js +++ b/javascript/examples/vite/main.js @@ -1,4 +1,4 @@ -import { functionSelectors, functionArguments, functionStateMutability } from 'evmole' +import { contractInfo } from 'evmole' document.getElementById('app').innerHTML = `
@@ -10,8 +10,10 @@ document.getElementById('app').innerHTML = ` const code = '6080604052348015600e575f80fd5b50600436106026575f3560e01c8063fae7ab8214602a575b5f80fd5b603960353660046062565b6052565b60405163ffffffff909116815260200160405180910390f35b5f605c826001608a565b92915050565b5f602082840312156071575f80fd5b813563ffffffff811681146083575f80fd5b9392505050565b63ffffffff8181168382160190811115605c57634e487b7160e01b5f52601160045260245ffd'; -document.getElementById('selectors').innerText = functionSelectors(code); +const info = contractInfo(code, { selectors: false, arguments: true, stateMutability: true }); -document.getElementById('arguments').innerText = functionArguments(code, 'fae7ab82'); +document.getElementById('selectors').innerText = info.functions[0].selector; -document.getElementById('state_mutability').innerText = functionStateMutability(code, 'fae7ab82'); +document.getElementById('arguments').innerText = info.functions[0].arguments; + +document.getElementById('state_mutability').innerText = info.functions[0].stateMutability; diff --git a/javascript/examples/webpack/index.js b/javascript/examples/webpack/index.js index 3f7ec08..8b98c6a 100644 --- a/javascript/examples/webpack/index.js +++ b/javascript/examples/webpack/index.js @@ -1,4 +1,4 @@ -import { functionSelectors, functionArguments, functionStateMutability } from 'evmole' +import { contractInfo } from 'evmole' document.body.innerHTML = `
@@ -10,8 +10,10 @@ document.body.innerHTML = ` const code = '6080604052348015600e575f80fd5b50600436106026575f3560e01c8063fae7ab8214602a575b5f80fd5b603960353660046062565b6052565b60405163ffffffff909116815260200160405180910390f35b5f605c826001608a565b92915050565b5f602082840312156071575f80fd5b813563ffffffff811681146083575f80fd5b9392505050565b63ffffffff8181168382160190811115605c57634e487b7160e01b5f52601160045260245ffd'; -document.getElementById('selectors').innerText = functionSelectors(code); +const info = contractInfo(code, { selectors: false, arguments: true, stateMutability: true }); -document.getElementById('arguments').innerText = functionArguments(code, 'fae7ab82'); +document.getElementById('selectors').innerText = info.functions[0].selector; -document.getElementById('state_mutability').innerText = functionStateMutability(code, 'fae7ab82'); +document.getElementById('arguments').innerText = info.functions[0].arguments; + +document.getElementById('state_mutability').innerText = info.functions[0].stateMutability; diff --git a/javascript/package-lock.json b/javascript/package-lock.json index e496ccc..dbbbbe6 100644 --- a/javascript/package-lock.json +++ b/javascript/package-lock.json @@ -1,44 +1,44 @@ { "name": "evmole", - "version": "0.3.7", + "version": "0.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "evmole", - "version": "0.3.7", + "version": "0.5.1", "license": "MIT", "devDependencies": { - "jsdoc-to-markdown": "^8.0.3", - "rollup": "^4.21", + "jsdoc-to-markdown": "^9.1", + "rollup": "^4.28", "wasm-pack": "^0.13" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, "dependencies": { - "@babel/types": "^7.25.6" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -48,14 +48,13 @@ } }, "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -73,10 +72,45 @@ "node": ">=v12.0.0" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz", - "integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.0.tgz", + "integrity": "sha512-wLJuPLT6grGZsy34g4N1yRfYeouklTgPhH1gWXCYspenKYD0s3cR99ZevOGw5BexMNywkbV3UkjADisozBmpPQ==", "cpu": [ "arm" ], @@ -87,9 +121,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz", - "integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.0.tgz", + "integrity": "sha512-eiNkznlo0dLmVG/6wf+Ifi/v78G4d4QxRhuUl+s8EWZpDewgk7PX3ZyECUXU0Zq/Ca+8nU8cQpNC4Xgn2gFNDA==", "cpu": [ "arm64" ], @@ -100,9 +134,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz", - "integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.0.tgz", + "integrity": "sha512-lmKx9yHsppblnLQZOGxdO66gT77bvdBtr/0P+TPOseowE7D9AJoBw8ZDULRasXRWf1Z86/gcOdpBrV6VDUY36Q==", "cpu": [ "arm64" ], @@ -113,9 +147,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz", - "integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.0.tgz", + "integrity": "sha512-8hxgfReVs7k9Js1uAIhS6zq3I+wKQETInnWQtgzt8JfGx51R1N6DRVy3F4o0lQwumbErRz52YqwjfvuwRxGv1w==", "cpu": [ "x64" ], @@ -125,10 +159,36 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.0.tgz", + "integrity": "sha512-lA1zZB3bFx5oxu9fYud4+g1mt+lYXCoch0M0V/xhqLoGatbzVse0wlSQ1UYOWKpuSu3gyN4qEc0Dxf/DII1bhQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.0.tgz", + "integrity": "sha512-aI2plavbUDjCQB/sRbeUZWX9qp12GfYkYSJOrdYTL/C5D53bsE2/nBPuoiJKoWp5SN78v2Vr8ZPnB+/VbQ2pFA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz", - "integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.0.tgz", + "integrity": "sha512-WXveUPKtfqtaNvpf0iOb0M6xC64GzUX/OowbqfiCSXTdi/jLlOmH0Ba94/OkiY2yTGTwteo4/dsHRfh5bDCZ+w==", "cpu": [ "arm" ], @@ -139,9 +199,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz", - "integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.0.tgz", + "integrity": "sha512-yLc3O2NtOQR67lI79zsSc7lk31xjwcaocvdD1twL64PK1yNaIqCeWI9L5B4MFPAVGEVjH5k1oWSGuYX1Wutxpg==", "cpu": [ "arm" ], @@ -152,9 +212,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz", - "integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.0.tgz", + "integrity": "sha512-+P9G9hjEpHucHRXqesY+3X9hD2wh0iNnJXX/QhS/J5vTdG6VhNYMxJ2rJkQOxRUd17u5mbMLHM7yWGZdAASfcg==", "cpu": [ "arm64" ], @@ -165,9 +225,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz", - "integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.0.tgz", + "integrity": "sha512-1xsm2rCKSTpKzi5/ypT5wfc+4bOGa/9yI/eaOLW0oMs7qpC542APWhl4A37AENGZ6St6GBMWhCCMM6tXgTIplw==", "cpu": [ "arm64" ], @@ -178,9 +238,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz", - "integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.0.tgz", + "integrity": "sha512-zgWxMq8neVQeXL+ouSf6S7DoNeo6EPgi1eeqHXVKQxqPy1B2NvTbaOUWPn/7CfMKL7xvhV0/+fq/Z/J69g1WAQ==", "cpu": [ "ppc64" ], @@ -191,9 +251,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz", - "integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.0.tgz", + "integrity": "sha512-VEdVYacLniRxbRJLNtzwGt5vwS0ycYshofI7cWAfj7Vg5asqj+pt+Q6x4n+AONSZW/kVm+5nklde0qs2EUwU2g==", "cpu": [ "riscv64" ], @@ -204,9 +264,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz", - "integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.0.tgz", + "integrity": "sha512-LQlP5t2hcDJh8HV8RELD9/xlYtEzJkm/aWGsauvdO2ulfl3QYRjqrKW+mGAIWP5kdNCBheqqqYIGElSRCaXfpw==", "cpu": [ "s390x" ], @@ -217,9 +277,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz", - "integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.0.tgz", + "integrity": "sha512-Nl4KIzteVEKE9BdAvYoTkW19pa7LR/RBrT6F1dJCV/3pbjwDcaOq+edkP0LXuJ9kflW/xOK414X78r+K84+msw==", "cpu": [ "x64" ], @@ -230,9 +290,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz", - "integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.0.tgz", + "integrity": "sha512-eKpJr4vBDOi4goT75MvW+0dXcNUqisK4jvibY9vDdlgLx+yekxSm55StsHbxUsRxSTt3JEQvlr3cGDkzcSP8bw==", "cpu": [ "x64" ], @@ -243,9 +303,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz", - "integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.0.tgz", + "integrity": "sha512-Vi+WR62xWGsE/Oj+mD0FNAPY2MEox3cfyG0zLpotZdehPFXwz6lypkGs5y38Jd/NVSbOD02aVad6q6QYF7i8Bg==", "cpu": [ "arm64" ], @@ -256,9 +316,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz", - "integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.0.tgz", + "integrity": "sha512-kN/Vpip8emMLn/eOza+4JwqDZBL6MPNpkdaEsgUtW1NYN3DZvZqSQrbKzJcTL6hd8YNmFTn7XGWMwccOcJBL0A==", "cpu": [ "ia32" ], @@ -269,9 +329,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz", - "integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.0.tgz", + "integrity": "sha512-Bvno2/aZT6usSa7lRDL2+hMjVAGjuqaymF1ApZm31JXzniR/hvr14jpU+/z4X6Gt5BPlzosscyJZGUvguXIqeQ==", "cpu": [ "x64" ], @@ -282,9 +342,9 @@ ] }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "node_modules/@types/linkify-it": { @@ -309,25 +369,19 @@ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", "dev": true }, - "node_modules/ansi-escape-sequences": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", - "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "array-back": "^3.0.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/ansi-escape-sequences/node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true, - "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { @@ -390,27 +444,36 @@ "concat-map": "0.0.1" } }, - "node_modules/cache-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", - "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "array-back": "^4.0.1", - "fs-then-native": "^2.0.0", - "mkdirp2": "^1.0.4" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, - "node_modules/cache-point/node_modules/array-back": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "node_modules/cache-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-3.0.0.tgz", + "integrity": "sha512-LDGNWYv/tqRWAAZxMy75PIYynaIuhcyoyjJtwA7X5uMZjdzvGm+XmTey/GXUy2EJ+lwc2eBFzFYxjvNYyE/0Iw==", "dev": true, + "dependencies": { + "array-back": "^6.2.2" + }, "engines": { - "node": ">=8" + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "^0.1.1" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/catharsis": { @@ -425,123 +488,109 @@ "node": ">= 10" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/collect-all": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", - "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "stream-connect": "^1.0.2", - "stream-via": "^1.0.4" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/command-line-args": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", "dev": true, "dependencies": { - "array-back": "^3.1.0", - "find-replace": "^3.0.0", - "lodash.camelcase": "^4.3.0", - "typical": "^4.0.0" + "chalk": "^4.1.2" }, "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-args/node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true, - "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" } }, - "node_modules/command-line-args/node_modules/typical": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/command-line-tool": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.8.0.tgz", - "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "ansi-escape-sequences": "^4.0.0", - "array-back": "^2.0.0", - "command-line-args": "^5.0.0", - "command-line-usage": "^4.1.0", - "typical": "^2.6.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=4.0.0" + "node": ">=7.0.0" } }, - "node_modules/command-line-tool/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", - "dev": true, - "dependencies": { - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4" - } + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "node_modules/command-line-usage": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", - "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", + "node_modules/command-line-args": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", + "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==", "dev": true, "dependencies": { - "ansi-escape-sequences": "^4.0.0", - "array-back": "^2.0.0", - "table-layout": "^0.4.2", - "typical": "^2.6.1" + "array-back": "^6.2.2", + "find-replace": "^5.0.2", + "lodash.camelcase": "^4.3.0", + "typical": "^7.2.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=12.20" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, - "node_modules/command-line-usage/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "node_modules/command-line-usage": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", "dev": true, "dependencies": { - "typical": "^2.6.1" + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.0", + "typical": "^7.1.1" }, "engines": { - "node": ">=4" + "node": ">=12.20.0" } }, "node_modules/common-sequence": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-2.0.2.tgz", - "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-3.0.0.tgz", + "integrity": "sha512-g/CgSYk93y+a1IKm50tKl7kaT/OjjTYVQlEbUlt/49ZLV1mcKpUU7iyDiqTAeLdb4QDtQfq3ako8y8v//fzrWQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12.17" } }, "node_modules/concat-map": { @@ -568,36 +617,39 @@ "node": ">=0.10.0" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "node_modules/current-module-paths": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/current-module-paths/-/current-module-paths-1.1.2.tgz", + "integrity": "sha512-H4s4arcLx/ugbu1XkkgSvcUZax0L6tXUqnppGniQb8l5VjUKGHoayXE5RiriiPhYDd+kjZnaok1Uig13PKtKYQ==", "dev": true, "engines": { - "node": ">=4.0.0" + "node": ">=12.17" } }, "node_modules/dmd": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.2.3.tgz", - "integrity": "sha512-SIEkjrG7cZ9GWZQYk/mH+mWtcRPly/3ibVuXO/tP/MFoWz6KiRK77tSMq6YQBPl7RljPtXPQ/JhxbNuCdi1bNw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-7.1.1.tgz", + "integrity": "sha512-Ap2HP6iuOek7eShReDLr9jluNJm9RMZESlt29H/Xs1qrVMkcS9X6m5h1mBC56WMxNiSo0wvjGICmZlYUSFjwZQ==", "dev": true, "dependencies": { "array-back": "^6.2.2", - "cache-point": "^2.0.0", - "common-sequence": "^2.0.2", - "file-set": "^4.0.2", + "cache-point": "^3.0.0", + "common-sequence": "^3.0.0", + "file-set": "^5.2.2", "handlebars": "^4.7.8", "marked": "^4.3.0", - "object-get": "^2.1.1", - "reduce-flatten": "^3.0.1", - "reduce-unique": "^2.0.1", - "reduce-without": "^1.0.1", - "test-value": "^3.0.0", - "walk-back": "^5.1.0" + "walk-back": "^5.1.1" }, "engines": { - "node": ">=12" + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/entities": { @@ -621,53 +673,85 @@ "node": ">=8" } }, - "node_modules/file-set": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.2.tgz", - "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { - "array-back": "^5.0.0", - "glob": "^7.1.6" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">=10" + "node": ">=8.6.0" } }, - "node_modules/file-set/node_modules/array-back": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", - "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-set": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/file-set/-/file-set-5.2.2.tgz", + "integrity": "sha512-/KgJI1V/QaDK4enOk/E2xMFk1cTWJghEr7UmWiRZfZ6upt6gQCfMn4jJ7aOm64OKurj4TaVnSSgSDqv5ZKYA3A==", + "dev": true, + "dependencies": { + "array-back": "^6.2.2", + "fast-glob": "^3.3.2" + }, "engines": { - "node": ">=10" + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, - "node_modules/find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { - "array-back": "^3.0.1" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=4.0.0" + "node": ">=8" } }, - "node_modules/find-replace/node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "node_modules/find-replace": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", + "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", "dev": true, "engines": { - "node": ">=6" + "node": ">=14" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "dev": true, "funding": [ { @@ -708,15 +792,6 @@ "node": ">=8" } }, - "node_modules/fs-then-native": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", - "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -758,6 +833,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -785,6 +872,15 @@ "uglify-js": "^3.1.4" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -802,6 +898,36 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/js2xmlparser": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", @@ -812,9 +938,9 @@ } }, "node_modules/jsdoc": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.3.tgz", - "integrity": "sha512-Nu7Sf35kXJ1MWDZIMAuATRQTg1iIPdzh7tqJ6jjvaU/GfDf+qi5UV8zJR3Mo+/pYFvm8mzay4+6O5EWigaQBQw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", + "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", "dev": true, "dependencies": { "@babel/parser": "^7.20.15", @@ -841,72 +967,74 @@ } }, "node_modules/jsdoc-api": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-8.1.1.tgz", - "integrity": "sha512-yas9E4h8NHp1CTEZiU/DPNAvLoUcip+Hl8Xi1RBYzHqSrgsF+mImAZNtwymrXvgbrgl4bNGBU9syulM0JzFeHQ==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-9.3.4.tgz", + "integrity": "sha512-di8lggLACEttpyAZ6WjKKafUP4wC4prAGjt40nMl7quDpp2nD7GmLt6/WxhRu9Q6IYoAAySsNeidBXYVAMwlqg==", "dev": true, "dependencies": { "array-back": "^6.2.2", - "cache-point": "^2.0.0", - "collect-all": "^1.0.4", - "file-set": "^4.0.2", - "fs-then-native": "^2.0.0", - "jsdoc": "^4.0.3", + "cache-point": "^3.0.0", + "current-module-paths": "^1.1.2", + "file-set": "^5.2.2", + "jsdoc": "^4.0.4", "object-to-spawn-args": "^2.0.1", - "temp-path": "^1.0.0", - "walk-back": "^5.1.0" + "walk-back": "^5.1.1" }, "engines": { "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/jsdoc-parse": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.2.tgz", - "integrity": "sha512-O7PqEAMjsK2hAp6v0Goj+G+wFRsQjT/TYbEV7lB2ow3DdE+/ws/a/BuHpIzVlRy4RBAAppu2pZ70ADhBJEtXeQ==", + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.4.tgz", + "integrity": "sha512-MQA+lCe3ioZd0uGbyB3nDCDZcKgKC7m/Ivt0LgKZdUoOlMJxUWJQ3WI6GeyHp9ouznKaCjlp7CU9sw5k46yZTw==", "dev": true, "dependencies": { "array-back": "^6.2.2", "find-replace": "^5.0.1", "lodash.omit": "^4.5.0", - "sort-array": "^5.0.0", - "test-value": "^3.0.0" + "sort-array": "^5.0.0" }, "engines": { "node": ">=12" } }, - "node_modules/jsdoc-parse/node_modules/find-replace": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.1.tgz", - "integrity": "sha512-o5/Y8HrCNRuFF5rdNTkX8Vhv6kTFTV0t1zIoigwlCdbkA9qaapRzxvWPND2VvlFa9LBI05Q1i8ml/saMqkOJUQ==", - "dev": true, - "dependencies": { - "array-back": "^6.2.2" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/jsdoc-to-markdown": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-8.0.3.tgz", - "integrity": "sha512-JGYYd5xygnQt1DIxH+HUI+X/ynL8qWihzIF0n15NSCNtM6MplzawURRcaLI2WkiS2hIjRIgsphCOfM7FkaWiNg==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-9.1.1.tgz", + "integrity": "sha512-QqYVSo58iHXpD5Jwi1u4AFeuMcQp4jfk7SmWzvXKc3frM9Kop17/OHudmi0phzkT/K137Rlroc9Q0y+95XpUsw==", "dev": true, "dependencies": { "array-back": "^6.2.2", - "command-line-tool": "^0.8.0", + "command-line-args": "^6.0.1", + "command-line-usage": "^7.0.3", "config-master": "^3.1.0", - "dmd": "^6.2.3", - "jsdoc-api": "^8.1.1", - "jsdoc-parse": "^6.2.1", - "walk-back": "^5.1.0" + "dmd": "^7.1.1", + "jsdoc-api": "^9.3.4", + "jsdoc-parse": "^6.2.4", + "walk-back": "^5.1.1" }, "bin": { "jsdoc2md": "bin/cli.js" }, "engines": { "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/klaw": { @@ -945,12 +1073,6 @@ "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", "dev": true }, - "node_modules/lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", - "dev": true - }, "node_modules/markdown-it": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", @@ -996,6 +1118,28 @@ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1063,24 +1207,12 @@ "node": ">=10" } }, - "node_modules/mkdirp2": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", - "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", - "dev": true - }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node_modules/object-get": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", - "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", - "dev": true - }, "node_modules/object-to-spawn-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.1.tgz", @@ -1108,69 +1240,46 @@ "node": ">=0.10.0" } }, - "node_modules/punycode.js": { + "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/reduce-flatten": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.1.tgz", - "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/reduce-unique": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-2.0.1.tgz", - "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { - "node": ">=6" - } - }, - "node_modules/reduce-without": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", - "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", - "dev": true, - "dependencies": { - "test-value": "^2.0.0" + "node": ">=8.6" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/reduce-without/node_modules/array-back": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", "dev": true, - "dependencies": { - "typical": "^2.6.0" - }, "engines": { - "node": ">=0.12.0" + "node": ">=6" } }, - "node_modules/reduce-without/node_modules/test-value": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", - "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "dependencies": { - "array-back": "^1.0.3", - "typical": "^2.6.0" - }, - "engines": { - "node": ">=0.10.0" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/requizzle": { "version": "0.2.4", @@ -1181,6 +1290,16 @@ "lodash": "^4.17.21" } }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -1198,12 +1317,12 @@ } }, "node_modules/rollup": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz", - "integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.0.tgz", + "integrity": "sha512-G9GOrmgWHBma4YfCcX8PjH0qhXSdH8B4HDE2o4/jaxj93S4DPCIDoLcXz99eWMji4hB29UFCEd7B2gwGJDR9cQ==", "dev": true, "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -1213,25 +1332,50 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.0", - "@rollup/rollup-android-arm64": "4.21.0", - "@rollup/rollup-darwin-arm64": "4.21.0", - "@rollup/rollup-darwin-x64": "4.21.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.0", - "@rollup/rollup-linux-arm-musleabihf": "4.21.0", - "@rollup/rollup-linux-arm64-gnu": "4.21.0", - "@rollup/rollup-linux-arm64-musl": "4.21.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.0", - "@rollup/rollup-linux-riscv64-gnu": "4.21.0", - "@rollup/rollup-linux-s390x-gnu": "4.21.0", - "@rollup/rollup-linux-x64-gnu": "4.21.0", - "@rollup/rollup-linux-x64-musl": "4.21.0", - "@rollup/rollup-win32-arm64-msvc": "4.21.0", - "@rollup/rollup-win32-ia32-msvc": "4.21.0", - "@rollup/rollup-win32-x64-msvc": "4.21.0", + "@rollup/rollup-android-arm-eabi": "4.28.0", + "@rollup/rollup-android-arm64": "4.28.0", + "@rollup/rollup-darwin-arm64": "4.28.0", + "@rollup/rollup-darwin-x64": "4.28.0", + "@rollup/rollup-freebsd-arm64": "4.28.0", + "@rollup/rollup-freebsd-x64": "4.28.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.28.0", + "@rollup/rollup-linux-arm-musleabihf": "4.28.0", + "@rollup/rollup-linux-arm64-gnu": "4.28.0", + "@rollup/rollup-linux-arm64-musl": "4.28.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.28.0", + "@rollup/rollup-linux-riscv64-gnu": "4.28.0", + "@rollup/rollup-linux-s390x-gnu": "4.28.0", + "@rollup/rollup-linux-x64-gnu": "4.28.0", + "@rollup/rollup-linux-x64-musl": "4.28.0", + "@rollup/rollup-win32-arm64-msvc": "4.28.0", + "@rollup/rollup-win32-ia32-msvc": "4.28.0", + "@rollup/rollup-win32-x64-msvc": "4.28.0", "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/sort-array": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-5.0.0.tgz", @@ -1253,15 +1397,6 @@ } } }, - "node_modules/sort-array/node_modules/typical": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-7.1.1.tgz", - "integrity": "sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==", - "dev": true, - "engines": { - "node": ">=12.17" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1271,39 +1406,6 @@ "node": ">=0.10.0" } }, - "node_modules/stream-connect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", - "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", - "dev": true, - "dependencies": { - "array-back": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stream-connect/node_modules/array-back": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", - "dev": true, - "dependencies": { - "typical": "^2.6.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/stream-via": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", - "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1316,32 +1418,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/table-layout": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", - "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "array-back": "^2.0.0", - "deep-extend": "~0.6.0", - "lodash.padend": "^4.6.1", - "typical": "^2.6.1", - "wordwrapjs": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=8" } }, - "node_modules/table-layout/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "node_modules/table-layout": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", "dev": true, "dependencies": { - "typical": "^2.6.1" + "array-back": "^6.2.2", + "wordwrapjs": "^5.1.0" }, "engines": { - "node": ">=4" + "node": ">=12.17" } }, "node_modules/tar": { @@ -1361,52 +1460,27 @@ "node": ">=10" } }, - "node_modules/temp-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", - "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", - "dev": true - }, - "node_modules/test-value": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", - "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", - "dev": true, - "dependencies": { - "array-back": "^2.0.0", - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/test-value/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "typical": "^2.6.1" + "is-number": "^7.0.0" }, "engines": { - "node": ">=4" + "node": ">=8.0" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "node_modules/typical": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", "dev": true, "engines": { - "node": ">=4" + "node": ">=12.17" } }, - "node_modules/typical": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", - "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", - "dev": true - }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -1442,9 +1516,9 @@ } }, "node_modules/wasm-pack": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.13.0.tgz", - "integrity": "sha512-AmboGZEnZoIcVCzSlkLEmNFEqJN+IwgshJ5S7pi30uNUTce4LvWkifQzsQRxnWj47G8gkqZxlyGlyQplsnIS7w==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.13.1.tgz", + "integrity": "sha512-P9exD4YkjpDbw68xUhF3MDm/CC/3eTmmthyG5bHJ56kalxOTewOunxTke4SyF8MTXV6jUtNjXggPgrGmMtczGg==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -1461,25 +1535,12 @@ "dev": true }, "node_modules/wordwrapjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", - "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz", + "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==", "dev": true, - "dependencies": { - "reduce-flatten": "^1.0.1", - "typical": "^2.6.1" - }, "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/wordwrapjs/node_modules/reduce-flatten": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", - "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">=12.17" } }, "node_modules/wrappy": { diff --git a/javascript/package.json b/javascript/package.json index 6ecb93e..5256a13 100644 --- a/javascript/package.json +++ b/javascript/package.json @@ -45,8 +45,8 @@ "doc": "jsdoc2md --files ./dist/evmole.js --heading-depth 3" }, "devDependencies": { - "jsdoc-to-markdown": "^8.0.3", - "rollup": "^4.21", + "jsdoc-to-markdown": "^9.1", + "rollup": "^4.28", "wasm-pack": "^0.13" } } diff --git a/javascript/src/evmole_esm.js b/javascript/src/evmole_esm.js index c696bf3..4c1616b 100644 --- a/javascript/src/evmole_esm.js +++ b/javascript/src/evmole_esm.js @@ -1,4 +1,4 @@ -export {functionSelectors, functionArguments, functionStateMutability} from "../dist/evmole.js"; +export {contractInfo, functionSelectors, functionArguments, functionStateMutability} from "../dist/evmole.js"; import initEvmole from "../dist/evmole.js"; await initEvmole({module_or_path: new URL('evmole_bg.wasm', import.meta.url)}) diff --git a/javascript/src/evmole_node.cjs b/javascript/src/evmole_node.cjs index eb151eb..a5f9d9e 100644 --- a/javascript/src/evmole_node.cjs +++ b/javascript/src/evmole_node.cjs @@ -5,4 +5,4 @@ const bytes = require("fs").readFileSync(path); initSync({module: bytes}); -export {functionSelectors, functionArguments, functionStateMutability} from "../dist/evmole.js"; +export {contractInfo, functionSelectors, functionArguments, functionStateMutability} from "../dist/evmole.js"; diff --git a/javascript/src/evmole_node.mjs b/javascript/src/evmole_node.mjs index d1d51d8..5fcf3dd 100644 --- a/javascript/src/evmole_node.mjs +++ b/javascript/src/evmole_node.mjs @@ -6,4 +6,4 @@ const bytes = fs.readFileSync(path); initSync({module: bytes}); -export {functionSelectors, functionArguments, functionStateMutability} from "../dist/evmole.js"; +export {contractInfo, functionSelectors, functionArguments, functionStateMutability} from "../dist/evmole.js"; diff --git a/javascript/src/evmole_wasm_import.js b/javascript/src/evmole_wasm_import.js index e074013..fc17dc4 100644 --- a/javascript/src/evmole_wasm_import.js +++ b/javascript/src/evmole_wasm_import.js @@ -1,4 +1,4 @@ -export {functionSelectors, functionArguments, functionStateMutability} from "../dist/evmole.js"; +export {contractInfo, functionSelectors, functionArguments, functionStateMutability} from "../dist/evmole.js"; import initEvmole from "../dist/evmole.js"; import wasmUrl from "../dist/evmole_bg.wasm"; diff --git a/javascript/tests/package-lock.json b/javascript/tests/package-lock.json index ca45072..aaee4a3 100644 --- a/javascript/tests/package-lock.json +++ b/javascript/tests/package-lock.json @@ -1,24 +1,23 @@ { - "name": "tests", - "version": "1.0.0", + "name": "examples-tests", + "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "tests", - "version": "1.0.0", - "license": "ISC", + "name": "examples-tests", + "version": "0.0.1", "devDependencies": { - "@playwright/test": "^1.46.1" + "@playwright/test": "^1.49" } }, "node_modules/@playwright/test": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.1.tgz", - "integrity": "sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==", + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0.tgz", + "integrity": "sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==", "dev": true, "dependencies": { - "playwright": "1.46.1" + "playwright": "1.49.0" }, "bin": { "playwright": "cli.js" @@ -42,12 +41,12 @@ } }, "node_modules/playwright": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz", - "integrity": "sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==", + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0.tgz", + "integrity": "sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==", "dev": true, "dependencies": { - "playwright-core": "1.46.1" + "playwright-core": "1.49.0" }, "bin": { "playwright": "cli.js" @@ -60,9 +59,9 @@ } }, "node_modules/playwright-core": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.1.tgz", - "integrity": "sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==", + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0.tgz", + "integrity": "sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==", "dev": true, "bin": { "playwright-core": "cli.js" diff --git a/javascript/tests/package.json b/javascript/tests/package.json index 997429e..8e4a036 100644 --- a/javascript/tests/package.json +++ b/javascript/tests/package.json @@ -3,6 +3,6 @@ "version": "0.0.1", "main": "main.test.js", "devDependencies": { - "@playwright/test": "^1.46.1" + "@playwright/test": "^1.49" } } diff --git a/python/README.md b/python/README.md index e994b42..a14c698 100644 --- a/python/README.md +++ b/python/README.md @@ -8,12 +8,102 @@ To install or upgrade EVMole, use pip: $ pip install evmole --upgrade ``` - + ## API +### contract\_info + +```python +def contract_info(code: Union[bytes, str], + *, + selectors: bool = False, + arguments: bool = False, + state_mutability: bool = False, + storage: bool = False) -> Contract +``` + +Extracts information about a smart contract from its EVM bytecode. + +**Arguments**: + +- `code` - Runtime bytecode as a hex string (with or without '0x' prefix) + or raw bytes. +- `selectors` - When True, extracts function selectors. +- `arguments` - When True, extracts function arguments. +- `state_mutability` - When True, extracts function state mutability. +- `storage` - When True, extracts the contract's storage layout. + + +**Returns**: + +- `Contract` - Object containing the requested smart contract information. Fields that + weren't requested to be extracted will be None. + +### Contract + +```python +class Contract(): + functions: Optional[List[Function]] + storage: Optional[List[StorageRecord]] +``` + +Contains analyzed information about a smart contract. + +**Attributes**: + +- `functions` - List of detected contract functions. None if no functions were extracted +- `storage` - List of contract storage records. None if storage layout was not extracted + +### Function + +```python +class Function(): + selector: str + bytecode_offset: int + arguments: Optional[str] + state_mutability: Optional[str] +``` + +Represents a public smart contract function. + +**Attributes**: + +- `selector` - Function selector as a 4-byte hex string without '0x' prefix (e.g., 'aabbccdd'). +- `bytecode_offset` - Starting byte offset within the EVM bytecode for the function body. +- `arguments` - Function argument types in canonical format (e.g., 'uint256,address[]'). + None if arguments were not extracted +- `state_mutability` - Function's state mutability ('pure', 'view', 'payable', or 'nonpayable'). + None if state mutability was not extracted + +### StorageRecord + +```python +class StorageRecord(): + slot: str + offset: int + type: str + reads: List[str] + writes: List[str] +``` + +Represents a storage variable record in a smart contract's storage layout. + +**Attributes**: + +- `slot` - Storage slot number as a hex string (e.g., '0', '1b'). +- `offset` - Byte offset within the storage slot (0-31). +- `type` - Variable type (e.g., 'uint256', 'mapping(address => uint256)', 'bytes32'). +- `reads` - List of function selectors that read from this storage location. +- `writes` - List of function selectors that write to this storage location. + + +## Deprecated API functions +Please use `contract_info()` + ### function\_selectors ```python +@deprecated("Use contract_info() with selectors=True instead") def function_selectors(code: Union[bytes, str], gas_limit: int = 500000) -> List[str] ``` @@ -24,7 +114,7 @@ Extracts function selectors from the given bytecode. - `code` _Union[bytes, str]_ - Runtime bytecode as a hex string or bytes. - `gas_limit` _int, optional_ - Maximum gas to use. Defaults to 500000. - + **Returns**: @@ -33,6 +123,7 @@ Extracts function selectors from the given bytecode. ### function\_arguments ```python +@deprecated("Use contract_info() with arguments=True instead") def function_arguments(code: Union[bytes, str], selector: Union[bytes, str], gas_limit: int = 50000) -> str @@ -46,7 +137,6 @@ Extracts function arguments for a given selector from the bytecode. - `selector` _Union[bytes, str]_ - Function selector as a hex string or bytes. - `gas_limit` _int, optional_ - Maximum gas to use. Defaults to 50000. - **Returns**: - `str` - Arguments of the function. @@ -54,6 +144,7 @@ Extracts function arguments for a given selector from the bytecode. ### function\_state\_mutability ```python +@deprecated("Use contract_info() with state_mutability=True instead") def function_state_mutability(code: Union[bytes, str], selector: Union[bytes, str], gas_limit: int = 500000) -> str @@ -67,7 +158,6 @@ Extracts function state mutability for a given selector from the bytecode. - `selector` _Union[bytes, str]_ - Function selector as a hex string or bytes. - `gas_limit` _int, optional_ - Maximum gas to use. Defaults to 500000. - **Returns**: - `str` - "payable" | "nonpayable" | "view" | "pure" diff --git a/python/test_python.py b/python/test_python.py index 2eb4dda..71576a8 100644 --- a/python/test_python.py +++ b/python/test_python.py @@ -1,14 +1,12 @@ -from evmole import function_arguments, function_selectors, function_state_mutability +from evmole import contract_info code = '6080604052348015600e575f80fd5b50600436106026575f3560e01c8063fae7ab8214602a575b5f80fd5b603960353660046062565b6052565b60405163ffffffff909116815260200160405180910390f35b5f605c826001608a565b92915050565b5f602082840312156071575f80fd5b813563ffffffff811681146083575f80fd5b9392505050565b63ffffffff8181168382160190811115605c57634e487b7160e01b5f52601160045260245ffd' -r = function_selectors(code) -assert r == ['fae7ab82'] +info = contract_info(code, selectors=True, arguments=True, state_mutability=True) +assert info.functions is not None +assert len(info.functions) == 1 +assert info.functions[0].selector == 'fae7ab82' +assert info.functions[0].arguments == 'uint32' +assert info.functions[0].state_mutability == 'pure' -a = function_arguments(code, 'fae7ab82') -assert a == 'uint32' - -m = function_state_mutability(code, 'fae7ab82') -assert m == 'pure' - -print(f'Success, {r} : {a}') +print(f'Success, {info}') diff --git a/src/arguments/mod.rs b/src/arguments/mod.rs index 7e5e54d..a92f9c0 100644 --- a/src/arguments/mod.rs +++ b/src/arguments/mod.rs @@ -550,6 +550,7 @@ fn analyze( /// /// assert_eq!(arguments, vec![DynSolType::Uint(32), DynSolType::Address, DynSolType::Uint(224)]); /// ``` +#[deprecated(since = "0.6.0", note = "Use contract_info(ContractInfoArgs(code).with_arguments()) instead")] pub fn function_arguments_alloy( code: &[u8], selector: &Selector, @@ -629,7 +630,9 @@ pub fn function_arguments_alloy( /// /// assert_eq!(arguments, "uint32,address,uint224"); /// ``` +#[deprecated(since = "0.6.0", note = "Use contract_info(ContractInfoArgs(code).with_arguments()) instead")] pub fn function_arguments(code: &[u8], selector: &Selector, gas_limit: u32) -> String { + #[allow(deprecated)] function_arguments_alloy(code, selector, gas_limit) .into_iter() .map(|t| t.sol_type_name().to_string()) diff --git a/src/contract_info.rs b/src/contract_info.rs new file mode 100644 index 0000000..81d67f9 --- /dev/null +++ b/src/contract_info.rs @@ -0,0 +1,166 @@ +use crate::selectors::function_selectors_with_pc; +#[allow(deprecated)] +use crate::{ + function_arguments_alloy, state_mutability, storage, Selector, StateMutability, StorageRecord, +}; +use alloy_dyn_abi::DynSolType; + +/// Represents a public smart contract function +#[derive(Debug)] +pub struct Function { + /// Function selector (4 bytes) + pub selector: Selector, + + /// The starting byte offset within the EVM bytecode for the function body + pub bytecode_offset: usize, + + /// Function arguments + pub arguments: Option>, + + /// State mutability + pub state_mutability: Option, +} + +/// Contains analyzed information about a smart contract +#[derive(Debug)] +pub struct Contract { + /// List of contract functions with their metadata + pub functions: Option>, + + /// Contract storage layout + pub storage: Option>, +} + +/// Builder for configuring contract analysis parameters +/// +/// See [`contract_info`] for usage examples. +pub struct ContractInfoArgs<'a> { + code: &'a [u8], + + need_selectors: bool, + need_arguments: bool, + need_state_mutability: bool, + need_storage: bool, +} + +impl<'a> ContractInfoArgs<'a> { + /// Creates a new instance of contract analysis configuration + /// + /// # Arguments + /// + /// * `code` - A slice of deployed contract bytecode + pub fn new(code: &'a [u8]) -> Self { + ContractInfoArgs { + code, + need_selectors: false, + need_arguments: false, + need_state_mutability: false, + need_storage: false, + } + } + + /// Enables the extraction of function selectors + pub fn with_selectors(mut self) -> Self { + self.need_selectors = true; + self + } + + /// Enables the extraction of function arguments + pub fn with_arguments(mut self) -> Self { + self.need_selectors = true; + self.need_arguments = true; + self + } + + /// Enables the extraction of state mutability + pub fn with_state_mutability(mut self) -> Self { + self.need_selectors = true; + self.need_state_mutability = true; + self + } + + /// Enables the extraction of the contract's storage layout + pub fn with_storage(mut self) -> Self { + self.need_selectors = true; + self.need_arguments = true; + self.need_storage = true; + self + } +} + +/// Extracts information about a smart contract from its EVM bytecode. +/// +/// # Parameters +/// +/// - `args`: A [`ContractInfoArgs`] instance specifying what data to extract from the provided +/// bytecode. Use the builder-style methods on `ContractInfoArgs` (e.g., `.with_selectors()`, +/// `.with_arguments()`) to enable specific analyses. +/// +/// # Returns +/// +/// Returns a [`Contract`] object containing the requested smart contract information. The +/// `Contract` struct wraps optional fields depending on the configuration provided in `args`. +/// # Examples +/// +/// ``` +/// use evmole::{ContractInfoArgs, StateMutability, contract_info}; +/// use alloy_primitives::hex; +/// +/// let code = hex::decode("6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256").unwrap(); +/// +/// // Extract function selectors and their state mutability +/// let args = ContractInfoArgs::new(&code) +/// .with_selectors() +/// .with_state_mutability(); +/// +/// let info = contract_info(args); +/// let fns = info.functions.unwrap(); +/// assert_eq!(fns.len(), 2); +/// assert_eq!(fns[0].selector, [0x21, 0x25, 0xb6, 0x5b]); +/// assert_eq!(fns[0].state_mutability, Some(StateMutability::Pure)); +/// ``` +pub fn contract_info(args: ContractInfoArgs) -> Contract { + const GAS_LIMIT: u32 = 0; + + let functions = if args.need_selectors { + Some( + function_selectors_with_pc(args.code, GAS_LIMIT) + .into_iter() + .map(|(selector, bytecode_offset)| Function { + selector, + arguments: if args.need_arguments { + #[allow(deprecated)] + Some(function_arguments_alloy(args.code, &selector, GAS_LIMIT)) + } else { + None + }, + state_mutability: if args.need_state_mutability { + #[allow(deprecated)] + Some(state_mutability::function_state_mutability( + args.code, &selector, GAS_LIMIT, + )) + } else { + None + }, + bytecode_offset, + }) + .collect::>(), + ) + } else { + None + }; + + //TODO: filter fns by state_mutability if available + let storage = if args.need_storage { + let fns = functions + .as_ref() + .expect("enabled on with_storage()") + .iter() + .map(|f| (f.selector, f.bytecode_offset, f.arguments.as_ref().unwrap())); + Some(storage::contract_storage(args.code, fns, GAS_LIMIT)) + } else { + None + }; + + Contract { functions, storage } +} diff --git a/src/evm/calldata.rs b/src/evm/calldata.rs index 9286ec9..53df06c 100644 --- a/src/evm/calldata.rs +++ b/src/evm/calldata.rs @@ -8,3 +8,7 @@ pub trait CallData { fn len(&self) -> U256; fn selector(&self) -> [u8; 4]; } + +pub trait CallDataLabel { + fn label(n: usize, tp: &alloy_dyn_abi::DynSolType) -> Self; +} diff --git a/src/evm/stack.rs b/src/evm/stack.rs index 005fd7b..694162b 100644 --- a/src/evm/stack.rs +++ b/src/evm/stack.rs @@ -81,10 +81,7 @@ impl Stack { } pub fn push_data(&mut self, data: [u8; 32]) { - self.data.push(Element { - data, - label: None, - }); + self.data.push(Element { data, label: None }); } pub fn push_uint(&mut self, val: U256) { diff --git a/src/evm/vm.rs b/src/evm/vm.rs index a3be9ef..a34edfa 100644 --- a/src/evm/vm.rs +++ b/src/evm/vm.rs @@ -47,7 +47,7 @@ where pub calldata: &'a U, } -impl<'a, T, U> fmt::Debug for Vm<'a, T, U> +impl fmt::Debug for Vm<'_, T, U> where T: Clone + std::fmt::Debug, U: CallData, @@ -55,7 +55,8 @@ where fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "Vm:\n .pc = 0x{:x} | {}\n .stack = {:?}\n .memory = {:?}", + "Vm:\n .pc = 0x{:x} ({}) | {}\n .stack = {:?}\n .memory = {:?}", + self.pc, self.pc, if !self.stopped { op::info(self.code[self.pc]).name @@ -85,7 +86,7 @@ where } // not Clone trait because Cow experiments - pub fn clone(&'a self) -> Self { + pub fn clone(&self) -> Self { Vm { code: self.code, pc: self.pc, @@ -431,9 +432,11 @@ where } op::CODECOPY => { - let mem_off: u32 = self.stack.pop_uint()?.try_into()?; + let raws0 = self.stack.pop()?; let src_off: usize = self.stack.pop_uint()?.try_into()?; - let size: usize = self.stack.pop_uint()?.try_into()?; + let raws2 = self.stack.pop()?; + let mem_off: u32 = (&raws0).try_into()?; + let size: usize = (&raws2).try_into()?; if size > 32768 { Err(UnsupportedOpError { op }.into()) @@ -444,8 +447,11 @@ where let n = std::cmp::min(size, code_len - src_off); data[0..n].copy_from_slice(&self.code[src_off..src_off + n]); } + let mut ret = StepResult::new(op, 3); + ret.fa = Some(raws0); + ret.sa = Some(raws2); self.memory.store(mem_off, data, None); - Ok(StepResult::new(op, 3)) + Ok(ret) } } diff --git a/src/interface_js.rs b/src/interface_js.rs index 472556b..29ed0bc 100644 --- a/src/interface_js.rs +++ b/src/interface_js.rs @@ -1,4 +1,5 @@ use alloy_primitives::hex::{self, FromHex}; +use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; fn decode_hex_code(input: &str) -> Result, JsError> { @@ -10,8 +11,206 @@ fn decode_hex_selector(input: &str) -> Result<[u8; 4], JsError> { .map_err(|e| JsError::new(&format!("Failed to decode selector hex input: {e}"))) } +#[wasm_bindgen(typescript_custom_section)] +const DOC_FUNCTION: &'static str = r#" +/** + * Represents a function found in the contract bytecode + * @property selector - Function selector as a 4-byte hex string without '0x' prefix (e.g., 'aabbccdd'). + * @property bytecode_offset - Starting byte offset within the EVM bytecode for the function body. + * @property arguments - Function argument types in canonical format (e.g., 'uint256,address[]'). Not present if arguments were not extracted + * @property state_mutability - Function's state mutability ("pure", "view", "payable", or "nonpayable"). Not present if state mutability were not extracted + */ +export type ContractFunction = { + selector: string, + bytecode_offset: number, + arguments?: string, + state_mutability?: string, +}; +"#; +/// @typedef {Object} ContractFunction +/// @property {string} selector - Function selector as a 4-byte hex string without '0x' prefix (e.g., 'aabbccdd') +/// @property {number} bytecode_offset - Starting byte offset within the EVM bytecode for the function body +/// @property {string} [arguments] - Function argument types in canonical format (e.g., 'uint256,address[]'). Not present if arguments were not extracted +/// @property {string} [state_mutability] - Function's state mutability ("pure", "view", "payable", or "nonpayable"). Not present if state mutability were not extracted +#[wasm_bindgen(skip_jsdoc)] +pub fn dummy_function() {} +#[derive(Serialize)] +struct JsFunction { + selector: String, + + #[serde(default, rename = "bytecodeOffset")] + bytecode_offset: usize, + + arguments: Option, + + #[serde(default, rename = "stateMutability")] + state_mutability: Option, +} + +#[wasm_bindgen(typescript_custom_section)] +const DOC_STORAGE: &'static str = r#" +/** + * Represents a storage record found in the contract + * @property slot - Storage slot number as a hex string (e.g., '0', '1b'). + * @property offset - Byte offset within the storage slot (0-31). + * @property type - Variable type (e.g., 'uint256', 'mapping(address => uint256)', 'bytes32'). + * @property reads - Array of function selectors that read from this storage location. + * @property writes - Array of function selectors that write to this storage location. + */ +export type StorageRecord = { + slot: string, + offset: number, + type: string, + reads: string[], + writes: string[] +}; +"#; +/// @typedef {Object} StorageRecord +/// @property {string} slot - Storage slot number as a hex string (e.g., '0', '1b') +/// @property {number} offset - Byte offset within the storage slot (0-31) +/// @property {string} type - Variable type (e.g., 'uint256', 'mapping(address => uint256)', 'bytes32') +/// @property {string[]} reads - Array of function selectors that read from this storage location +/// @property {string[]} writes - Array of function selectors that write to this storage location +#[wasm_bindgen(skip_jsdoc)] +pub fn dummy_storage_record() {} +#[derive(Serialize)] +struct JsStorageRecord { + slot: String, + offset: u8, + r#type: String, + reads: Vec, + writes: Vec, +} + +#[wasm_bindgen(typescript_custom_section)] +const DOC_CONTRACT: &'static str = r#" +/** + * Contains the analysis results of a contract + * @property functions - Array of functions found in the contract. Not present if no functions were extracted. + * @property storage - Array of storage records found in the contract. Not present if storage layout was not extracted. + * @see ContractFunction + * @see StorageRecord + */ +export type Contract = { + functions?: ContractFunction[], + storage?: StorageRecord[], +}; +"#; +/// @typedef {Object} Contract +/// @property {ContractFunction[]} [functions] - Array of functions found in the contract. Not present if no functions were extracted +/// @property {StorageRecord[]} [storage] - Array of storage records found in the contract. Not present if storage layout was not extracted +#[wasm_bindgen(skip_jsdoc)] +pub fn dummy_contract() {} +#[derive(Serialize)] +struct JsContract { + functions: Option>, + storage: Option>, +} + +#[derive(Deserialize)] +struct ContractInfoArgs { + #[serde(default)] + selectors: bool, + + #[serde(default)] + arguments: bool, + + #[serde(default, rename = "stateMutability")] + state_mutability: bool, + + #[serde(default)] + storage: bool, +} + +#[wasm_bindgen(typescript_custom_section)] +const DOC_CONTRACT_INFO: &'static str = r#" +/** + * Analyzes contract bytecode and returns contract information based on specified options. + * + * @param code - Runtime bytecode as a hex string + * @param args - Configuration options for the analysis + * @param args.selectors - When true, includes function selectors in the output + * @param args.arguments - When true, includes function arguments information + * @param args.state_mutability - When true, includes state mutability information for functions + * @param args.storage - When true, includes contract storage layout information + * @returns Analyzed contract information + */ +export function contractInfo(code: string, { + selectors?: boolean, + arguments?: boolean, + state_mutability?: boolean, + storage?: boolean +}): Contract; +"#; +/// Analyzes contract bytecode and returns contract information based on specified options. +/// +/// @param {string} code - Runtime bytecode as a hex string +/// @param {Object} args - Configuration options for the analysis +/// @param {boolean} [args.selectors] - When true, includes function selectors in the output +/// @param {boolean} [args.arguments] - When true, includes function arguments information +/// @param {boolean} [args.state_mutability] - When true, includes state mutability information for functions +/// @param {boolean} [args.storage] - When true, includes contract storage layout information +/// @returns {Contract} Analyzed contract information +#[wasm_bindgen(js_name = contractInfo, skip_typescript, skip_jsdoc)] +pub fn contract_info(code: &str, args: JsValue) -> Result { + let c = decode_hex_code(code)?; + let args: ContractInfoArgs = serde_wasm_bindgen::from_value(args)?; + + let mut cargs = crate::ContractInfoArgs::new(&c); + + if args.selectors { + cargs = cargs.with_selectors(); + } + if args.arguments { + cargs = cargs.with_arguments(); + } + if args.state_mutability { + cargs = cargs.with_state_mutability(); + } + if args.storage { + cargs = cargs.with_storage(); + } + + let info = crate::contract_info(cargs); + + let functions = info.functions.map(|fns| { + fns.into_iter() + .map(|f| JsFunction { + selector: hex::encode(f.selector), + bytecode_offset: f.bytecode_offset, + arguments: f.arguments.map(|fargs| { + fargs + .into_iter() + .map(|t| t.sol_type_name().to_string()) + .collect::>() + .join(",") + }), + state_mutability: f.state_mutability.map(|sm| sm.as_json_str().to_string()), + }) + .collect() + }); + + let storage = info.storage.map(|st| { + st.into_iter() + .map(|v| JsStorageRecord { + slot: hex::encode(v.slot), + offset: v.offset, + r#type: v.r#type, + reads: v.reads.into_iter().map(hex::encode).collect(), + writes: v.writes.into_iter().map(hex::encode).collect(), + }) + .collect() + }); + + Ok(serde_wasm_bindgen::to_value(&JsContract { + functions, + storage, + })?) +} + /// Extracts function selectors from the given bytecode. /// +/// @deprecated Please use contractInfo() with {selectors: true} instead /// @param {string} code - Runtime bytecode as a hex string /// @param {number} gas_limit - Maximum allowed gas usage; set to `0` to use defaults /// @returns {string[]} Function selectors as a hex strings @@ -19,6 +218,8 @@ fn decode_hex_selector(input: &str) -> Result<[u8; 4], JsError> { pub fn function_selectors(code: &str, gas_limit: u32) -> Result, JsError> { // TODO: accept Uint8Array | str(hex) input let c = decode_hex_code(code)?; + + #[allow(deprecated)] Ok(crate::selectors::function_selectors(&c, gas_limit) .into_iter() .map(hex::encode) @@ -27,6 +228,7 @@ pub fn function_selectors(code: &str, gas_limit: u32) -> Result, JsE /// Extracts function arguments for a given selector from the bytecode. /// +/// @deprecated Please use contractInfo() with {arguments: true} instead /// @param {string} code - Runtime bytecode as a hex string /// @param {string} selector - Function selector as a hex string /// @param {number} gas_limit - Maximum allowed gas usage; set to `0` to use defaults @@ -35,11 +237,14 @@ pub fn function_selectors(code: &str, gas_limit: u32) -> Result, JsE pub fn function_arguments(code: &str, selector: &str, gas_limit: u32) -> Result { let c = decode_hex_code(code)?; let s = decode_hex_selector(selector)?; + + #[allow(deprecated)] Ok(crate::arguments::function_arguments(&c, &s, gas_limit)) } /// Extracts function state mutability for a given selector from the bytecode. /// +/// @deprecated Please use contractInfo() with {state_mutability: true} instead /// @param {string} code - Runtime bytecode as a hex string /// @param {string} selector - Function selector as a hex string /// @param {number} gas_limit - Maximum allowed gas usage; set to `0` to use defaults @@ -52,6 +257,8 @@ pub fn function_state_mutability( ) -> Result { let c = decode_hex_code(code)?; let s = decode_hex_selector(selector)?; + + #[allow(deprecated)] Ok( crate::state_mutability::function_state_mutability(&c, &s, gas_limit) .as_json_str() diff --git a/src/interface_py.rs b/src/interface_py.rs index 6b606cf..06bae24 100644 --- a/src/interface_py.rs +++ b/src/interface_py.rs @@ -22,33 +22,172 @@ fn input_to_bytes<'a>(code: &'a Bound<'a, PyAny>) -> PyResult> { } } -/// Extracts function selectors from the given bytecode. -/// -/// Args: -/// code (Union[bytes, str]): Runtime bytecode as a hex string or bytes. -/// gas_limit (int, optional): Maximum gas to use. Defaults to 500000. -/// -/// Returns: -/// List[str]: List of selectors encoded as hex strings. +#[pyclass(name = "Function")] +#[derive(Clone)] +struct PyFunction { + #[pyo3(get)] + selector: String, + + #[pyo3(get)] + bytecode_offset: usize, + + #[pyo3(get)] + arguments: Option, + + #[pyo3(get)] + state_mutability: Option, +} + +impl PyFunction { + fn repr(&self) -> String { + format!( + "Function(selector={},bytecode_offset={},arguments={},state_mutability={})", + self.selector, + self.bytecode_offset, + self.arguments.as_deref().unwrap_or("None"), + self.state_mutability.as_deref().unwrap_or("None"), + ) + } +} + +#[pymethods] +impl PyFunction { + fn __repr__(slf: &Bound<'_, Self>) -> PyResult { + Ok(PyFunction::repr(&slf.borrow())) + } +} + +#[pyclass(name = "StorageRecord")] +#[derive(Clone)] +struct PyStorageRecord { + slot: String, + offset: u8, + r#type: String, + reads: Vec, + writes: Vec, +} + +impl PyStorageRecord { + fn repr(&self) -> String { + format!( + "StorageRecord(slot={},offset={},type={},reads={:?},writes={:?})", + self.slot, self.offset, self.r#type, self.reads, self.writes + ) + } +} + +#[pymethods] +impl PyStorageRecord { + fn __repr__(slf: &Bound<'_, Self>) -> PyResult { + Ok(PyStorageRecord::repr(&slf.borrow())) + } +} + +#[pyclass(name = "Contract")] +struct PyContract { + #[pyo3(get)] + functions: Option>, + + #[pyo3(get)] + storage: Option>, +} + +#[pymethods] +impl PyContract { + fn __repr__(slf: &Bound<'_, Self>) -> PyResult { + Ok(format!( + "Contract(functions={},storage={})", + if let Some(ref v) = slf.borrow().functions { + format!( + "[{}]", + v.iter().map(|v| v.repr()).collect::>().join(",") + ) + } else { + "None".to_string() + }, + if let Some(ref v) = slf.borrow().storage { + format!( + "[{}]", + v.iter().map(|v| v.repr()).collect::>().join(",") + ) + } else { + "None".to_string() + }, + )) + } +} + +#[pyfunction] +#[pyo3(signature = (code, *, selectors=false, arguments=false, state_mutability=false, storage=false))] +fn contract_info( + code: &Bound<'_, PyAny>, + selectors: bool, + arguments: bool, + state_mutability: bool, + storage: bool, +) -> PyResult { + let code_bytes = input_to_bytes(code)?; + let mut args = crate::ContractInfoArgs::new(&code_bytes); + + if selectors { + args = args.with_selectors(); + } + if arguments { + args = args.with_arguments(); + } + if state_mutability { + args = args.with_state_mutability(); + } + if storage { + args = args.with_storage(); + } + + let info = crate::contract_info(args); + + let functions = info.functions.map(|fns| { + fns.into_iter() + .map(|f| PyFunction { + selector: hex::encode(f.selector), + bytecode_offset: f.bytecode_offset, + arguments: f.arguments.map(|fargs| { + fargs + .into_iter() + .map(|t| t.sol_type_name().to_string()) + .collect::>() + .join(",") + }), + state_mutability: f.state_mutability.map(|sm| sm.as_json_str().to_string()), + }) + .collect() + }); + + let storage = info.storage.map(|st| { + st.into_iter() + .map(|v| PyStorageRecord { + slot: hex::encode(v.slot), + offset: v.offset, + r#type: v.r#type, + reads: v.reads.into_iter().map(hex::encode).collect(), + writes: v.writes.into_iter().map(hex::encode).collect(), + }) + .collect() + }); + + Ok(PyContract { functions, storage }) +} + #[pyfunction] #[pyo3(signature = (code, gas_limit=500_000))] fn function_selectors(code: &Bound<'_, PyAny>, gas_limit: u32) -> PyResult> { let code_bytes = input_to_bytes(code)?; + + #[allow(deprecated)] Ok(crate::selectors::function_selectors(&code_bytes, gas_limit) .into_iter() .map(hex::encode) .collect()) } -/// Extracts function arguments for a given selector from the bytecode. -/// -/// Args: -/// code (Union[bytes, str]): Runtime bytecode as a hex string or bytes. -/// selector (Union[bytes, str]): Function selector as a hex string or bytes. -/// gas_limit (int, optional): Maximum gas to use. Defaults to 50000. -/// -/// Returns: -/// str: Arguments of the function. #[pyfunction] #[pyo3(signature = (code, selector, gas_limit=50_000))] fn function_arguments( @@ -65,6 +204,7 @@ fn function_arguments( <[u8; 4]>::try_from(selectors_ref).expect("len checked above") }; + #[allow(deprecated)] Ok(crate::arguments::function_arguments( &code_bytes, &sel, @@ -72,15 +212,6 @@ fn function_arguments( )) } -/// Extracts function state mutability for a given selector from the bytecode. -/// -/// Args: -/// code (Union[bytes, str]): Runtime bytecode as a hex string or bytes. -/// selector (Union[bytes, str]): Function selector as a hex string or bytes. -/// gas_limit (int, optional): Maximum gas to use. Defaults to 100000. -/// -/// Returns: -/// str: "payable" | "nonpayable" | "view" | "pure" #[pyfunction] #[pyo3(signature = (code, selector, gas_limit=500_000))] fn function_state_mutability( @@ -97,6 +228,7 @@ fn function_state_mutability( <[u8; 4]>::try_from(selectors_ref).expect("len checked above") }; + #[allow(deprecated)] Ok( crate::state_mutability::function_state_mutability(&code_bytes, &sel, gas_limit) .as_json_str() @@ -106,6 +238,7 @@ fn function_state_mutability( #[pymodule] fn evmole(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(wrap_pyfunction!(contract_info, m)?)?; m.add_function(wrap_pyfunction!(function_selectors, m)?)?; m.add_function(wrap_pyfunction!(function_arguments, m)?)?; m.add_function(wrap_pyfunction!(function_state_mutability, m)?)?; diff --git a/src/lib.rs b/src/lib.rs index 7a34b01..1bbc460 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,42 @@ -//! Extracts function selectors and arguments from bytecode, even for unverified contracts. +//! Analyzes EVM bytecode to extract contract information, even for unverified contracts. //! -//! Accuracy and speed comparison with other tools, as well as Python and JavaScript implementations, are available on [GitHub](https://github.com/cdump/evmole/tree/master#benchmark). +//! The library can extract function selectors, function arguments, state mutability, and storage layout. +//! +//! Use the [`contract_info()`] function with its builder pattern to analyze contracts. See its documentation for usage examples. +//! +//! Accuracy and speed comparison with other tools, as well as Python and JavaScript implementations, are available on [GitHub](https://github.com/cdump/evmole/tree/master#benchmark) +#[allow(deprecated)] pub use arguments::function_arguments; + +#[allow(deprecated)] pub use arguments::function_arguments_alloy; + +#[allow(deprecated)] pub use selectors::function_selectors; + +#[allow(deprecated)] pub use state_mutability::function_state_mutability; +pub use contract_info::contract_info; +pub use contract_info::{Contract, Function, ContractInfoArgs}; +pub use storage::StorageRecord; + mod arguments; mod evm; mod selectors; mod state_mutability; mod utils; +mod storage; +mod contract_info; +/// A 4-byte function selector pub type Selector = [u8; 4]; + +/// A 32-byte storage slot identifier in EVM storage. +pub type Slot = [u8; 32]; + +/// Function's state mutability pub type StateMutability = alloy_dyn_abi::parser::StateMutability; #[cfg(feature = "python")] diff --git a/src/selectors/mod.rs b/src/selectors/mod.rs index a7d67e1..892459c 100644 --- a/src/selectors/mod.rs +++ b/src/selectors/mod.rs @@ -183,6 +183,7 @@ fn process( /// /// assert_eq!(selectors, vec![[0x21, 0x25, 0xb6, 0x5b], [0xb6, 0x9e, 0xf8, 0xa8]]) /// ``` +#[deprecated(since = "0.6.0", note = "Use contract_info(ContractInfoArgs(code).with_selectors()) instead")] pub fn function_selectors(code: &[u8], gas_limit: u32) -> Vec { let selectors_with_pc = function_selectors_with_pc(code, gas_limit); selectors_with_pc.into_keys().collect() diff --git a/src/state_mutability/mod.rs b/src/state_mutability/mod.rs index b9c1bd9..66633b8 100644 --- a/src/state_mutability/mod.rs +++ b/src/state_mutability/mod.rs @@ -223,6 +223,7 @@ fn analyze_view_pure(vm: Vm, gas_limit: u32) -> ViewPureRes /// /// assert_eq!(state_mutability, StateMutability::Pure); /// ``` +#[deprecated(since = "0.6.0", note = "Use contract_info(ContractInfoArgs(code).with_state_mutability()) instead")] pub fn function_state_mutability( code: &[u8], selector: &Selector, diff --git a/src/storage/calldata.rs b/src/storage/calldata.rs new file mode 100644 index 0000000..c8067a9 --- /dev/null +++ b/src/storage/calldata.rs @@ -0,0 +1,226 @@ +use alloy_dyn_abi::DynSolType; +use alloy_dyn_abi::DynSolType::*; +use std::{collections::BTreeMap, marker::PhantomData}; + +use crate::evm::{ + calldata::{CallData, CallDataLabel}, + element::Element, + U256, VAL_131072, +}; +use std::error; + +pub struct CallDataImpl { + pub selector: [u8; 4], + arg_types: BTreeMap, + arg_vals: BTreeMap, + + phantom: std::marker::PhantomData, +} + +impl CallDataImpl { + pub fn new(selector: [u8; 4], arguments: &[DynSolType]) -> Self { + let (_, types, vals) = encode(arguments); + Self { + selector, + arg_types: BTreeMap::from_iter(types), + arg_vals: BTreeMap::from_iter(vals), + phantom: PhantomData, + } + } +} + +impl CallData for CallDataImpl { + fn load32(&self, offset: U256) -> Element { + let mut data = [0; 32]; + let mut label = None; + + if let Ok(off) = usize::try_from(offset) { + if off < 4 { + data[..4 - off].copy_from_slice(&self.selector[off..]); + } else { + let xoff = off - 4; + if let Some(val) = self.arg_vals.get(&xoff) { + data = U256::from(*val).to_be_bytes(); + } + if let Some(tp) = self.arg_types.get(&xoff) { + // label = Some(Label::Typed(tp.clone())); + label = Some(T::label(xoff, tp)); + } + } + } + Element { data, label } + } + + fn load( + &self, + offset: U256, + size: U256, + ) -> Result<(Vec, Option), Box> { + let mut data = vec![0; u8::try_from(size)? as usize]; // max len limited to max_u8 + let mut label = None; + + if let Ok(off) = usize::try_from(offset) { + if off < 4 { + let nlen = std::cmp::min(data.len(), 4 - off); + data[..nlen].copy_from_slice(&self.selector[off..off + nlen]); + } else { + if let Some(val) = self.arg_vals.get(&off) { + //TODO: look to the left to find proper element + data = U256::from(*val).to_be_bytes_vec(); + } + if let Some(tp) = self.arg_types.get(&off) { + label = Some(T::label(off - 4, tp)); + } + } + } + + Ok((data, label)) + } + + fn selector(&self) -> [u8; 4] { + self.selector + } + + fn len(&self) -> U256 { + VAL_131072 + } +} + +fn is_dynamic(ty: &DynSolType) -> bool { + match ty { + Bool | Int(_) | Uint(_) | Address | FixedBytes(_) => false, + FixedArray(val, _) => is_dynamic(val), + Bytes | String | Array(_) => true, + Tuple(val) => val.iter().any(is_dynamic), + _ => panic!("Unexpected type {:?}", ty), + } +} + +type ArgTypes = Vec<(usize, DynSolType)>; +type ArgNonZero = Vec<(usize, usize)>; + +fn encode(elements: &[DynSolType]) -> (usize, ArgTypes, ArgNonZero) { + // (offset, type) + let mut ret_types: Vec<(usize, DynSolType)> = Vec::with_capacity(elements.len()); + + // (offset, value) + let mut ret_nonzero: Vec<(usize, usize)> = Vec::with_capacity(elements.len()); + + let mut off = 0; + + // (offset, type) + let mut dynamic: Vec<(usize, &DynSolType)> = Vec::with_capacity(elements.len() / 2); + + for ty in elements.iter() { + if is_dynamic(ty) { + dynamic.push((off, ty)); + off += 32; + } else { + match ty { + FixedArray(val, sz) => { + for _ in 0..*sz { + // sz or cnt? + ret_types.push((off, *val.clone())); + off += 32; + } + } + Tuple(val) => { + for v in val { + ret_types.push((off, v.clone())); + off += 32; + } + } + + _ => { + ret_types.push((off, ty.clone())); + off += 32; + } + } + } + } + + for (el_off, ty) in dynamic.into_iter() { + ret_nonzero.push((el_off, off)); + + match ty { + Bytes | String => { + // string '0x41' with len = 1 + ret_nonzero.push((off, 32)); + ret_nonzero.push((off + 32, 0x41)); // TODO: padd right, not left + ret_types.push((off + 32, ty.clone())); + off += 64; + } + Array(val) => { + // len = 1 + ret_nonzero.push((off, 1)); + off += 32; + + let (dyn_off, dyn_ret_types, dyn_ret_nonzero) = encode(&[*val.clone()]); + + ret_types.extend(dyn_ret_types.into_iter().map(|(o, v)| (o + off, v))); + ret_nonzero.extend(dyn_ret_nonzero.into_iter().map(|(o, v)| (o + off, v))); + off += dyn_off; + } + Tuple(val) => { + let (dyn_off, dyn_ret_types, dyn_ret_nonzero) = encode(val); + + ret_types.extend(dyn_ret_types.into_iter().map(|(o, v)| (o + off, v))); + ret_nonzero.extend(dyn_ret_nonzero.into_iter().map(|(o, v)| (o + off, v))); + off += dyn_off; + } + + FixedArray(val, sz) => { + let (dyn_off, dyn_ret_types, dyn_ret_nonzero) = encode(&[*val.clone()]); + + let data_start = 32 * sz; + for i in 0..*sz { + ret_nonzero.push((off, data_start + i * (dyn_off - 32))); + off += 32; + } + + for _ in 0..*sz { + ret_types.extend(dyn_ret_types.iter().map(|(o, v)| (o + off - 32, v.clone()))); + ret_nonzero.extend( + dyn_ret_nonzero + .iter() + .skip(1) + .map(|(o, v)| (o + off - 32, *v)), + ); + off += dyn_off - 32; + } + } + _ => panic!("Unexpected type {:?}", ty), + } + } + + (off, ret_types, ret_nonzero) +} + +// #[cfg(test)] +// mod test { +// use super::encode; +// use std::collections::BTreeMap; +// +// #[test] +// fn test_encode() { +// let x = vec![ +// "string[3]".parse().unwrap(), +// // "(string)[3]".parse().unwrap(), +// // "(uint8, string)[3]".parse().unwrap(), +// ]; +// let (end_off, a, b) = encode(&x); +// println!("{}", end_off); +// println!("{:?}", a); +// println!("{:?}", b); +// +// let ma = BTreeMap::from_iter(a); +// let mb = BTreeMap::from_iter(b); +// +// for off in (0..end_off).step_by(32) { +// println!("{:064x} - {:?}", +// mb.get(&off).unwrap_or(&0), +// ma.get(&off), +// ); +// } +// } +// } diff --git a/src/storage/keccak_precalc.rs b/src/storage/keccak_precalc.rs new file mode 100644 index 0000000..cdadbee --- /dev/null +++ b/src/storage/keccak_precalc.rs @@ -0,0 +1,1283 @@ +// for i in range(256): kec(i.to_bytes(32, 'big')) +pub(super) const KEC_PRECALC: [[u8; 32]; 256] = [ + [ + 0x29, 0x0d, 0xec, 0xd9, 0x54, 0x8b, 0x62, 0xa8, 0xd6, 0x03, 0x45, 0xa9, 0x88, 0x38, 0x6f, + 0xc8, 0x4b, 0xa6, 0xbc, 0x95, 0x48, 0x40, 0x08, 0xf6, 0x36, 0x2f, 0x93, 0x16, 0x0e, 0xf3, + 0xe5, 0x63, + ], + [ + 0xb1, 0x0e, 0x2d, 0x52, 0x76, 0x12, 0x07, 0x3b, 0x26, 0xee, 0xcd, 0xfd, 0x71, 0x7e, 0x6a, + 0x32, 0x0c, 0xf4, 0x4b, 0x4a, 0xfa, 0xc2, 0xb0, 0x73, 0x2d, 0x9f, 0xcb, 0xe2, 0xb7, 0xfa, + 0x0c, 0xf6, + ], + [ + 0x40, 0x57, 0x87, 0xfa, 0x12, 0xa8, 0x23, 0xe0, 0xf2, 0xb7, 0x63, 0x1c, 0xc4, 0x1b, 0x3b, + 0xa8, 0x82, 0x8b, 0x33, 0x21, 0xca, 0x81, 0x11, 0x11, 0xfa, 0x75, 0xcd, 0x3a, 0xa3, 0xbb, + 0x5a, 0xce, + ], + [ + 0xc2, 0x57, 0x5a, 0x0e, 0x9e, 0x59, 0x3c, 0x00, 0xf9, 0x59, 0xf8, 0xc9, 0x2f, 0x12, 0xdb, + 0x28, 0x69, 0xc3, 0x39, 0x5a, 0x3b, 0x05, 0x02, 0xd0, 0x5e, 0x25, 0x16, 0x44, 0x6f, 0x71, + 0xf8, 0x5b, + ], + [ + 0x8a, 0x35, 0xac, 0xfb, 0xc1, 0x5f, 0xf8, 0x1a, 0x39, 0xae, 0x7d, 0x34, 0x4f, 0xd7, 0x09, + 0xf2, 0x8e, 0x86, 0x00, 0xb4, 0xaa, 0x8c, 0x65, 0xc6, 0xb6, 0x4b, 0xfe, 0x7f, 0xe3, 0x6b, + 0xd1, 0x9b, + ], + [ + 0x03, 0x6b, 0x63, 0x84, 0xb5, 0xec, 0xa7, 0x91, 0xc6, 0x27, 0x61, 0x15, 0x2d, 0x0c, 0x79, + 0xbb, 0x06, 0x04, 0xc1, 0x04, 0xa5, 0xfb, 0x6f, 0x4e, 0xb0, 0x70, 0x3f, 0x31, 0x54, 0xbb, + 0x3d, 0xb0, + ], + [ + 0xf6, 0x52, 0x22, 0x23, 0x13, 0xe2, 0x84, 0x59, 0x52, 0x8d, 0x92, 0x0b, 0x65, 0x11, 0x5c, + 0x16, 0xc0, 0x4f, 0x3e, 0xfc, 0x82, 0xaa, 0xed, 0xc9, 0x7b, 0xe5, 0x9f, 0x3f, 0x37, 0x7c, + 0x0d, 0x3f, + ], + [ + 0xa6, 0x6c, 0xc9, 0x28, 0xb5, 0xed, 0xb8, 0x2a, 0xf9, 0xbd, 0x49, 0x92, 0x29, 0x54, 0x15, + 0x5a, 0xb7, 0xb0, 0x94, 0x26, 0x94, 0xbe, 0xa4, 0xce, 0x44, 0x66, 0x1d, 0x9a, 0x87, 0x36, + 0xc6, 0x88, + ], + [ + 0xf3, 0xf7, 0xa9, 0xfe, 0x36, 0x4f, 0xaa, 0xb9, 0x3b, 0x21, 0x6d, 0xa5, 0x0a, 0x32, 0x14, + 0x15, 0x4f, 0x22, 0xa0, 0xa2, 0xb4, 0x15, 0xb2, 0x3a, 0x84, 0xc8, 0x16, 0x9e, 0x8b, 0x63, + 0x6e, 0xe3, + ], + [ + 0x6e, 0x15, 0x40, 0x17, 0x1b, 0x6c, 0x0c, 0x96, 0x0b, 0x71, 0xa7, 0x02, 0x0d, 0x9f, 0x60, + 0x07, 0x7f, 0x6a, 0xf9, 0x31, 0xa8, 0xbb, 0xf5, 0x90, 0xda, 0x02, 0x23, 0xda, 0xcf, 0x75, + 0xc7, 0xaf, + ], + [ + 0xc6, 0x5a, 0x7b, 0xb8, 0xd6, 0x35, 0x1c, 0x1c, 0xf7, 0x0c, 0x95, 0xa3, 0x16, 0xcc, 0x6a, + 0x92, 0x83, 0x9c, 0x98, 0x66, 0x82, 0xd9, 0x8b, 0xc3, 0x5f, 0x95, 0x8f, 0x48, 0x83, 0xf9, + 0xd2, 0xa8, + ], + [ + 0x01, 0x75, 0xb7, 0xa6, 0x38, 0x42, 0x77, 0x03, 0xf0, 0xdb, 0xe7, 0xbb, 0x9b, 0xbf, 0x98, + 0x7a, 0x25, 0x51, 0x71, 0x7b, 0x34, 0xe7, 0x9f, 0x33, 0xb5, 0xb1, 0x00, 0x8d, 0x1f, 0xa0, + 0x1d, 0xb9, + ], + [ + 0xdf, 0x69, 0x66, 0xc9, 0x71, 0x05, 0x1c, 0x3d, 0x54, 0xec, 0x59, 0x16, 0x26, 0x06, 0x53, + 0x14, 0x93, 0xa5, 0x14, 0x04, 0xa0, 0x02, 0x84, 0x2f, 0x56, 0x00, 0x9d, 0x7e, 0x5c, 0xf4, + 0xa8, 0xc7, + ], + [ + 0xd7, 0xb6, 0x99, 0x01, 0x05, 0x71, 0x91, 0x01, 0xda, 0xbe, 0xb7, 0x71, 0x44, 0xf2, 0xa3, + 0x38, 0x5c, 0x80, 0x33, 0xac, 0xd3, 0xaf, 0x97, 0xe9, 0x42, 0x3a, 0x69, 0x5e, 0x81, 0xad, + 0x1e, 0xb5, + ], + [ + 0xbb, 0x7b, 0x4a, 0x45, 0x4d, 0xc3, 0x49, 0x39, 0x23, 0x48, 0x2f, 0x07, 0x82, 0x23, 0x29, + 0xed, 0x19, 0xe8, 0x24, 0x4e, 0xff, 0x58, 0x2c, 0xc2, 0x04, 0xf8, 0x55, 0x4c, 0x36, 0x20, + 0xc3, 0xfd, + ], + [ + 0x8d, 0x11, 0x08, 0xe1, 0x0b, 0xcb, 0x7c, 0x27, 0xdd, 0xdf, 0xc0, 0x2e, 0xd9, 0xd6, 0x93, + 0xa0, 0x74, 0x03, 0x9d, 0x02, 0x6c, 0xf4, 0xea, 0x42, 0x40, 0xb4, 0x0f, 0x7d, 0x58, 0x1a, + 0xc8, 0x02, + ], + [ + 0x1b, 0x68, 0x47, 0xdc, 0x74, 0x1a, 0x1b, 0x0c, 0xd0, 0x8d, 0x27, 0x88, 0x45, 0xf9, 0xd8, + 0x19, 0xd8, 0x7b, 0x73, 0x47, 0x59, 0xaf, 0xb5, 0x5f, 0xe2, 0xde, 0x5c, 0xb8, 0x2a, 0x9a, + 0xe6, 0x72, + ], + [ + 0x31, 0xec, 0xc2, 0x1a, 0x74, 0x5e, 0x39, 0x68, 0xa0, 0x4e, 0x95, 0x70, 0xe4, 0x42, 0x5b, + 0xc1, 0x8f, 0xa8, 0x01, 0x9c, 0x68, 0x02, 0x81, 0x96, 0xb5, 0x46, 0xd1, 0x66, 0x9c, 0x20, + 0x0c, 0x68, + ], + [ + 0xbb, 0x8a, 0x6a, 0x46, 0x69, 0xba, 0x25, 0x0d, 0x26, 0xcd, 0x7a, 0x45, 0x9e, 0xca, 0x9d, + 0x21, 0x5f, 0x83, 0x07, 0xe3, 0x3a, 0xeb, 0xe5, 0x03, 0x79, 0xbc, 0x5a, 0x36, 0x17, 0xec, + 0x34, 0x44, + ], + [ + 0x66, 0xde, 0x8f, 0xfd, 0xa7, 0x97, 0xe3, 0xde, 0x9c, 0x05, 0xe8, 0xfc, 0x57, 0xb3, 0xbf, + 0x0e, 0xc2, 0x8a, 0x93, 0x0d, 0x40, 0xb0, 0xd2, 0x85, 0xd9, 0x3c, 0x06, 0x50, 0x1c, 0xf6, + 0xa0, 0x90, + ], + [ + 0xce, 0x6d, 0x7b, 0x52, 0x82, 0xbd, 0x9a, 0x36, 0x61, 0xae, 0x06, 0x1f, 0xee, 0xd1, 0xdb, + 0xda, 0x4e, 0x52, 0xab, 0x07, 0x3b, 0x1f, 0x92, 0x85, 0xbe, 0x6e, 0x15, 0x5d, 0x9c, 0x38, + 0xd4, 0xec, + ], + [ + 0x55, 0xf4, 0x48, 0xfd, 0xea, 0x98, 0xc4, 0xd2, 0x9e, 0xb3, 0x40, 0x75, 0x7e, 0xf0, 0xa6, + 0x6c, 0xd0, 0x3d, 0xbb, 0x95, 0x38, 0x90, 0x8a, 0x6a, 0x81, 0xd9, 0x60, 0x26, 0xb7, 0x1e, + 0xc4, 0x75, + ], + [ + 0xd8, 0x33, 0x14, 0x7d, 0x7d, 0xc3, 0x55, 0xba, 0x45, 0x9f, 0xc7, 0x88, 0xf6, 0x69, 0xe5, + 0x8c, 0xfa, 0xf9, 0xdc, 0x25, 0xdd, 0xcd, 0x07, 0x02, 0xe8, 0x7d, 0x69, 0xc7, 0xb5, 0x12, + 0x42, 0x89, + ], + [ + 0xc6, 0x24, 0xb6, 0x6c, 0xc0, 0x13, 0x8b, 0x8f, 0xab, 0xc2, 0x09, 0x24, 0x7f, 0x72, 0xd7, + 0x58, 0xe1, 0xcf, 0x33, 0x43, 0x75, 0x6d, 0x54, 0x3b, 0xad, 0xbf, 0x24, 0x21, 0x2b, 0xed, + 0x8c, 0x15, + ], + [ + 0xb1, 0x3d, 0x2d, 0x76, 0xd1, 0xf4, 0xb7, 0xbe, 0x83, 0x48, 0x82, 0xe4, 0x10, 0xb3, 0xe3, + 0xa8, 0xaf, 0xaf, 0x69, 0xf8, 0x36, 0x00, 0xae, 0x24, 0xdb, 0x35, 0x43, 0x91, 0xd2, 0x37, + 0x8d, 0x2e, + ], + [ + 0x94, 0x49, 0x98, 0x27, 0x3e, 0x47, 0x7b, 0x49, 0x51, 0x44, 0xfb, 0x87, 0x94, 0xc9, 0x14, + 0x19, 0x7f, 0x3c, 0xcb, 0x46, 0xbe, 0x29, 0x00, 0xf4, 0x69, 0x8f, 0xd0, 0xef, 0x74, 0x3c, + 0x96, 0x95, + ], + [ + 0x05, 0x7c, 0x38, 0x4a, 0x7d, 0x1c, 0x54, 0xf3, 0xa1, 0xb2, 0xe5, 0xe6, 0x7b, 0x26, 0x17, + 0xb8, 0x22, 0x4f, 0xdf, 0xd1, 0xea, 0x72, 0x34, 0xee, 0xa5, 0x73, 0xa6, 0xff, 0x66, 0x5f, + 0xf6, 0x3e, + ], + [ + 0x3a, 0xd8, 0xaa, 0x4f, 0x87, 0x54, 0x43, 0x23, 0xa9, 0xd1, 0xe5, 0xdd, 0x90, 0x2f, 0x40, + 0xc3, 0x56, 0x52, 0x7a, 0x79, 0x55, 0x68, 0x71, 0x13, 0xdb, 0x5f, 0x9a, 0x85, 0xad, 0x57, + 0x9d, 0xc1, + ], + [ + 0x0e, 0x45, 0x62, 0xa1, 0x03, 0x81, 0xde, 0xc2, 0x1b, 0x20, 0x5e, 0xd7, 0x26, 0x37, 0xe6, + 0xb1, 0xb5, 0x23, 0xbd, 0xd0, 0xe4, 0xd4, 0xd5, 0x0a, 0xf5, 0xcd, 0x23, 0xdd, 0x45, 0x00, + 0xa2, 0x11, + ], + [ + 0x6d, 0x44, 0x07, 0xe7, 0xbe, 0x21, 0xf8, 0x08, 0xe6, 0x50, 0x9a, 0xa9, 0xfa, 0x91, 0x43, + 0x36, 0x95, 0x79, 0xdd, 0x7d, 0x76, 0x0f, 0xe2, 0x0a, 0x2c, 0x09, 0x68, 0x0f, 0xc1, 0x46, + 0x13, 0x4f, + ], + [ + 0x50, 0xbb, 0x66, 0x9a, 0x95, 0xc7, 0xb5, 0x0b, 0x7e, 0x8a, 0x6f, 0x09, 0x45, 0x40, 0x34, + 0xb2, 0xb1, 0x4c, 0xf2, 0xb8, 0x5c, 0x73, 0x0d, 0xca, 0x9a, 0x53, 0x9c, 0xa8, 0x2c, 0xb6, + 0xe3, 0x50, + ], + [ + 0xa0, 0x38, 0x37, 0xa2, 0x52, 0x10, 0xee, 0x28, 0x0c, 0x21, 0x13, 0xff, 0x4b, 0x77, 0xca, + 0x23, 0x44, 0x0b, 0x19, 0xd4, 0x86, 0x6c, 0xca, 0x72, 0x1c, 0x80, 0x12, 0x78, 0xfd, 0x08, + 0xd8, 0x07, + ], + [ + 0xc9, 0x7b, 0xfa, 0xf2, 0xf8, 0xee, 0x70, 0x8c, 0x30, 0x3a, 0x06, 0xd1, 0x34, 0xf5, 0xec, + 0xd8, 0x38, 0x9a, 0xe0, 0x43, 0x2a, 0xf6, 0x2d, 0xc1, 0x32, 0xa2, 0x41, 0x18, 0x29, 0x28, + 0x66, 0xbb, + ], + [ + 0x3a, 0x63, 0x57, 0x01, 0x2c, 0x1a, 0x3a, 0xe0, 0xa1, 0x7d, 0x30, 0x4c, 0x99, 0x20, 0x31, + 0x03, 0x82, 0xd9, 0x68, 0xeb, 0xcc, 0x4b, 0x17, 0x71, 0xf4, 0x1c, 0x6b, 0x30, 0x42, 0x05, + 0xb5, 0x70, + ], + [ + 0x61, 0x03, 0x5b, 0x26, 0xe3, 0xe9, 0xee, 0xe0, 0x0e, 0x0d, 0x72, 0xfd, 0x1e, 0xe8, 0xdd, + 0xca, 0x68, 0x94, 0x55, 0x0d, 0xca, 0x69, 0x16, 0xea, 0x2a, 0xc6, 0xba, 0xa9, 0x0d, 0x11, + 0xe5, 0x10, + ], + [ + 0xd5, 0x7b, 0x2b, 0x51, 0x66, 0x47, 0x8f, 0xd4, 0x31, 0x8d, 0x2a, 0xcc, 0x6c, 0xc2, 0xc7, + 0x04, 0x58, 0x43, 0x12, 0xbd, 0xd8, 0x78, 0x1b, 0x32, 0xd5, 0xd0, 0x6a, 0xbd, 0xa5, 0x7f, + 0x42, 0x30, + ], + [ + 0x7c, 0xd3, 0x32, 0xd1, 0x9b, 0x93, 0xbc, 0xab, 0xe3, 0xcc, 0xe7, 0xca, 0x0c, 0x18, 0xa0, + 0x52, 0xf5, 0x7e, 0x5f, 0xd0, 0x3b, 0x47, 0x58, 0xa0, 0x9f, 0x30, 0xf5, 0xdd, 0xc4, 0xb2, + 0x2e, 0xc4, + ], + [ + 0x40, 0x19, 0x68, 0xff, 0x42, 0xa1, 0x54, 0x44, 0x1d, 0xa5, 0xf6, 0xc4, 0xc9, 0x35, 0xac, + 0x46, 0xb8, 0x67, 0x1f, 0x0e, 0x06, 0x2b, 0xaa, 0xa6, 0x2a, 0x75, 0x45, 0xba, 0x53, 0xbb, + 0x6e, 0x4c, + ], + [ + 0x74, 0x4a, 0x2c, 0xf8, 0xfd, 0x70, 0x08, 0xe3, 0xd5, 0x3b, 0x67, 0x91, 0x6e, 0x73, 0x46, + 0x0d, 0xf9, 0xfa, 0x52, 0x14, 0xe3, 0xef, 0x23, 0xdd, 0x42, 0x59, 0xca, 0x09, 0x49, 0x3a, + 0x35, 0x94, + ], + [ + 0x98, 0xa4, 0x76, 0xf1, 0x68, 0x7b, 0xc3, 0xd6, 0x0a, 0x2d, 0xa2, 0xad, 0xbc, 0xba, 0x2c, + 0x46, 0x95, 0x8e, 0x61, 0xfa, 0x2f, 0xb4, 0x04, 0x2c, 0xd7, 0xbc, 0x58, 0x16, 0xa7, 0x10, + 0x19, 0x5b, + ], + [ + 0xe1, 0x6d, 0xa9, 0x23, 0xa2, 0xd8, 0x81, 0x92, 0xe5, 0x07, 0x0f, 0x37, 0xb4, 0x57, 0x1d, + 0x58, 0x68, 0x2c, 0x0d, 0x66, 0x21, 0x2e, 0xc6, 0x34, 0xd4, 0x95, 0xf3, 0x3d, 0xe3, 0xf7, + 0x7a, 0xb5, + ], + [ + 0xcb, 0x7c, 0x14, 0xce, 0x17, 0x8f, 0x56, 0xe2, 0xe8, 0xd8, 0x6a, 0xb3, 0x3e, 0xbc, 0x0a, + 0xe0, 0x81, 0xba, 0x85, 0x56, 0xa0, 0x0c, 0xd1, 0x22, 0x03, 0x88, 0x41, 0x86, 0x71, 0x81, + 0xca, 0xac, + ], + [ + 0xbe, 0xce, 0xd0, 0x95, 0x21, 0x04, 0x7d, 0x05, 0xb8, 0x96, 0x0b, 0x7e, 0x7b, 0xcc, 0x1d, + 0x12, 0x92, 0xcf, 0x3e, 0x4b, 0x2a, 0x6b, 0x63, 0xf4, 0x83, 0x35, 0xcb, 0xde, 0x5f, 0x75, + 0x45, 0xd2, + ], + [ + 0x11, 0xc4, 0x4e, 0x48, 0x75, 0xb7, 0x4d, 0x31, 0xff, 0x9f, 0xd7, 0x79, 0xbf, 0x25, 0x66, + 0xaf, 0x7b, 0xd1, 0x5b, 0x87, 0xfc, 0x98, 0x5d, 0x01, 0xf5, 0x09, 0x4b, 0x89, 0xe3, 0x66, + 0x9e, 0x4f, + ], + [ + 0x74, 0x16, 0xc9, 0x43, 0xb4, 0xa0, 0x98, 0x59, 0x52, 0x10, 0x22, 0xfd, 0x2e, 0x90, 0xea, + 0xc0, 0xdd, 0x90, 0x26, 0xda, 0xd2, 0x8f, 0xa3, 0x17, 0x78, 0x2a, 0x13, 0x5f, 0x28, 0xa8, + 0x60, 0x91, + ], + [ + 0x4a, 0x2c, 0xc9, 0x1e, 0xe6, 0x22, 0xda, 0x3b, 0xc8, 0x33, 0xa5, 0x4c, 0x37, 0xff, 0xcb, + 0x6f, 0x3e, 0xc2, 0x3b, 0x77, 0x93, 0xef, 0xc5, 0xea, 0xf5, 0xe7, 0x1b, 0x7b, 0x40, 0x6c, + 0x5c, 0x06, + ], + [ + 0x37, 0xfa, 0x16, 0x6c, 0xbd, 0xbf, 0xbb, 0x15, 0x61, 0xcc, 0xd9, 0xea, 0x98, 0x5e, 0xc0, + 0x21, 0x8b, 0x5e, 0x68, 0x50, 0x2e, 0x23, 0x05, 0x25, 0xf5, 0x44, 0x28, 0x5b, 0x2b, 0xdf, + 0x3d, 0x7e, + ], + [ + 0xa8, 0x13, 0x48, 0x4a, 0xef, 0x6f, 0xb5, 0x98, 0xf9, 0xf7, 0x53, 0xda, 0xf1, 0x62, 0x06, + 0x8f, 0xf3, 0x9c, 0xce, 0xa4, 0x07, 0x5c, 0xb9, 0x5e, 0x1a, 0x30, 0xf8, 0x69, 0x95, 0xb5, + 0xb7, 0xee, + ], + [ + 0x6f, 0xf9, 0x7a, 0x59, 0xc9, 0x0d, 0x62, 0xcc, 0x72, 0x36, 0xba, 0x3a, 0x37, 0xcd, 0x85, + 0x35, 0x1b, 0xf5, 0x64, 0x55, 0x67, 0x80, 0xcf, 0x8c, 0x11, 0x57, 0xa2, 0x20, 0xf3, 0x1f, + 0x0c, 0xbb, + ], + [ + 0xc5, 0x40, 0x45, 0xfa, 0x7c, 0x6e, 0xc7, 0x65, 0xe8, 0x25, 0xdf, 0x7f, 0x9e, 0x9b, 0xf9, + 0xde, 0xc1, 0x2c, 0x5c, 0xef, 0x14, 0x6f, 0x93, 0xa5, 0xee, 0xe5, 0x67, 0x72, 0xee, 0x64, + 0x7f, 0xbc, + ], + [ + 0x11, 0xdf, 0x49, 0x13, 0x16, 0xf1, 0x49, 0x31, 0x03, 0x9e, 0xdf, 0xd4, 0xf8, 0x96, 0x4c, + 0x9a, 0x44, 0x3b, 0x86, 0x2f, 0x02, 0xd4, 0xc7, 0x61, 0x1d, 0x18, 0xc2, 0xbc, 0x4e, 0x6f, + 0xf6, 0x97, + ], + [ + 0x82, 0xa7, 0x5b, 0xde, 0xea, 0xe8, 0x60, 0x4d, 0x83, 0x94, 0x76, 0xae, 0x9e, 0xfd, 0x8b, + 0x0e, 0x15, 0xaa, 0x44, 0x7e, 0x21, 0xbf, 0xd7, 0xf4, 0x12, 0x83, 0xbb, 0x54, 0xe2, 0x2c, + 0x9a, 0x82, + ], + [ + 0x46, 0xbd, 0xdb, 0x11, 0x78, 0xe9, 0x4d, 0x7f, 0x28, 0x92, 0xff, 0x5f, 0x36, 0x68, 0x40, + 0xeb, 0x65, 0x89, 0x11, 0x79, 0x4f, 0x2c, 0x3a, 0x44, 0xc4, 0x50, 0xaa, 0x2c, 0x50, 0x51, + 0x86, 0xc1, + ], + [ + 0xcf, 0xa4, 0xbe, 0xc1, 0xd3, 0x29, 0x84, 0x08, 0xbb, 0x5a, 0xfc, 0xfc, 0xd9, 0xc4, 0x30, + 0x54, 0x9c, 0x5b, 0x31, 0xf8, 0xaa, 0x5c, 0x58, 0x48, 0x15, 0x1c, 0x0a, 0x55, 0xf4, 0x73, + 0xc3, 0x4d, + ], + [ + 0x4a, 0x11, 0xf9, 0x4e, 0x20, 0xa9, 0x3c, 0x79, 0xf6, 0xec, 0x74, 0x3a, 0x19, 0x54, 0xec, + 0x4f, 0xc2, 0xc0, 0x84, 0x29, 0xae, 0x21, 0x22, 0x11, 0x8b, 0xf2, 0x34, 0xb2, 0x18, 0x5c, + 0x81, 0xb8, + ], + [ + 0x42, 0xa7, 0xb7, 0xdd, 0x78, 0x5c, 0xd6, 0x97, 0x14, 0xa1, 0x89, 0xdf, 0xfb, 0x3f, 0xd7, + 0xd7, 0x17, 0x4e, 0xdc, 0x9e, 0xce, 0x83, 0x76, 0x94, 0xce, 0x50, 0xf7, 0x07, 0x8f, 0x7c, + 0x31, 0xae, + ], + [ + 0x38, 0x39, 0x5c, 0x5d, 0xce, 0xad, 0xe9, 0x60, 0x34, 0x79, 0xb1, 0x77, 0xb6, 0x89, 0x59, + 0x04, 0x94, 0x85, 0xdf, 0x8a, 0xa9, 0x7b, 0x39, 0xf3, 0x53, 0x30, 0x39, 0xaf, 0x5f, 0x45, + 0x61, 0x99, + ], + [ + 0xdc, 0x16, 0xfe, 0xf7, 0x0f, 0x8d, 0x5d, 0xdb, 0xc0, 0x1e, 0xe3, 0xd9, 0x03, 0xd1, 0xe6, + 0x9c, 0x18, 0xa3, 0xc7, 0xbe, 0x08, 0x0e, 0xb8, 0x6a, 0x81, 0xe0, 0x57, 0x88, 0x14, 0xee, + 0x58, 0xd3, + ], + [ + 0xa2, 0x99, 0x9d, 0x81, 0x7b, 0x67, 0x57, 0x29, 0x0b, 0x50, 0xe8, 0xec, 0xf3, 0xfa, 0x93, + 0x96, 0x73, 0x40, 0x3d, 0xd3, 0x5c, 0x97, 0xde, 0x39, 0x2f, 0xdb, 0x34, 0x3b, 0x40, 0x15, + 0xce, 0x9e, + ], + [ + 0xbb, 0xe3, 0x21, 0x21, 0x24, 0x85, 0x3f, 0x8b, 0x00, 0x84, 0xa6, 0x6a, 0x2d, 0x05, 0x7c, + 0x29, 0x66, 0xe2, 0x51, 0xe1, 0x32, 0xaf, 0x36, 0x91, 0xdb, 0x15, 0x3a, 0xb6, 0x5f, 0x0d, + 0x1a, 0x4d, + ], + [ + 0xc6, 0xbb, 0x06, 0xcb, 0x7f, 0x92, 0x60, 0x3d, 0xe1, 0x81, 0xbf, 0x25, 0x6c, 0xd1, 0x68, + 0x46, 0xb9, 0x3b, 0x75, 0x2a, 0x17, 0x0f, 0xf2, 0x48, 0x24, 0x09, 0x8b, 0x31, 0xaa, 0x00, + 0x8a, 0x7e, + ], + [ + 0xec, 0xe6, 0x6c, 0xfd, 0xbd, 0x22, 0xe3, 0xf3, 0x7d, 0x34, 0x8a, 0x3d, 0x8e, 0x19, 0x07, + 0x44, 0x52, 0x86, 0x2c, 0xd6, 0x5f, 0xd4, 0xb9, 0xa1, 0x1f, 0x03, 0x36, 0xd1, 0xac, 0x6d, + 0x1d, 0xc3, + ], + [ + 0x8d, 0x80, 0x0d, 0x66, 0x14, 0xd3, 0x5e, 0xed, 0x73, 0x73, 0x3e, 0xe4, 0x53, 0x16, 0x4a, + 0x3b, 0x48, 0x07, 0x6e, 0xb3, 0x13, 0x8f, 0x46, 0x6a, 0xde, 0xeb, 0x9d, 0xec, 0x7b, 0xb3, + 0x1f, 0x70, + ], + [ + 0xc0, 0x30, 0x04, 0xe3, 0xce, 0x07, 0x84, 0xbf, 0x68, 0x18, 0x63, 0x94, 0x30, 0x68, 0x49, + 0xf9, 0xb7, 0xb1, 0x20, 0x00, 0x73, 0x10, 0x5c, 0xd9, 0xae, 0xb5, 0x54, 0xa1, 0x80, 0x2b, + 0x58, 0xfd, + ], + [ + 0x35, 0x2f, 0xee, 0xe0, 0xee, 0xa1, 0x25, 0xf1, 0x1f, 0x79, 0x1c, 0x1b, 0x77, 0x52, 0x41, + 0x72, 0xe9, 0xbc, 0x20, 0xf1, 0xb7, 0x19, 0xb6, 0xce, 0xf0, 0xfc, 0x24, 0xf6, 0x4d, 0xb8, + 0xe1, 0x5e, + ], + [ + 0x7c, 0x97, 0x85, 0xe8, 0x24, 0x16, 0x15, 0xbc, 0x80, 0x41, 0x5d, 0x89, 0x77, 0x59, 0x84, + 0xa1, 0x33, 0x7d, 0x15, 0xdc, 0x1b, 0xf4, 0xce, 0x50, 0xf4, 0x19, 0x88, 0xb2, 0xa2, 0xb3, + 0x36, 0xa7, + ], + [ + 0x38, 0xdf, 0xe4, 0x63, 0x5b, 0x27, 0xba, 0xbe, 0xca, 0x8b, 0xe3, 0x8d, 0x3b, 0x44, 0x8c, + 0xb5, 0x16, 0x1a, 0x63, 0x9b, 0x89, 0x9a, 0x14, 0x82, 0x5b, 0xa9, 0xc8, 0xd7, 0x89, 0x2e, + 0xb8, 0xc3, + ], + [ + 0x96, 0x90, 0xad, 0x99, 0xd6, 0xce, 0x24, 0x4e, 0xfa, 0x8a, 0x0f, 0x6c, 0x2d, 0x04, 0x03, + 0x6d, 0x3b, 0x33, 0xa9, 0x47, 0x4d, 0xb3, 0x2a, 0x71, 0xb7, 0x11, 0x35, 0xc6, 0x95, 0x10, + 0x27, 0x93, + ], + [ + 0x9b, 0x22, 0xd3, 0xd6, 0x19, 0x59, 0xb4, 0xd3, 0x52, 0x8b, 0x1d, 0x8b, 0xa9, 0x32, 0xc9, + 0x6f, 0xbe, 0x30, 0x2b, 0x36, 0xa1, 0xaa, 0xd1, 0xd9, 0x5c, 0xab, 0x54, 0xf9, 0xe0, 0xa1, + 0x35, 0xea, + ], + [ + 0xa8, 0x0a, 0x8f, 0xcc, 0x11, 0x76, 0x01, 0x62, 0xf0, 0x8b, 0xb0, 0x91, 0xd2, 0xc9, 0x38, + 0x9d, 0x07, 0xf2, 0xb7, 0x3d, 0x0e, 0x99, 0x61, 0x61, 0xdf, 0xac, 0x6f, 0x10, 0x43, 0xb5, + 0xfc, 0x0b, + ], + [ + 0x12, 0x86, 0x67, 0xf5, 0x41, 0xfe, 0xd7, 0x4a, 0x84, 0x29, 0xf9, 0xd5, 0x92, 0xc2, 0x6c, + 0x2c, 0x6a, 0x4b, 0xeb, 0x9a, 0xe5, 0xea, 0xd9, 0x91, 0x2c, 0x98, 0xb2, 0x59, 0x5c, 0x84, + 0x23, 0x10, + ], + [ + 0xc4, 0x3c, 0x1e, 0x24, 0xe1, 0x88, 0x4c, 0x4e, 0x28, 0xa1, 0x6b, 0xbd, 0x95, 0x06, 0xf6, + 0x0b, 0x5c, 0xa9, 0xf1, 0x8f, 0xc9, 0x06, 0x35, 0xe7, 0x29, 0xd3, 0xcf, 0xe1, 0x3a, 0xbc, + 0xf0, 0x01, + ], + [ + 0x15, 0x04, 0x01, 0x56, 0x07, 0x6f, 0x78, 0x05, 0x7c, 0x0a, 0x88, 0x6f, 0x6d, 0xba, 0xc2, + 0x92, 0x21, 0xfa, 0x3c, 0x26, 0x46, 0xad, 0xbc, 0x8e, 0xff, 0xed, 0xab, 0x98, 0x15, 0x2f, + 0xf3, 0x2b, + ], + [ + 0x37, 0xe4, 0x72, 0xf5, 0x04, 0xe9, 0x37, 0x44, 0xdf, 0x80, 0xd8, 0x73, 0x16, 0x86, 0x2f, + 0x9a, 0x8f, 0xd4, 0x1a, 0x7b, 0xc2, 0x66, 0xc7, 0x23, 0xbf, 0x77, 0xdf, 0x78, 0x66, 0xd7, + 0x5f, 0x55, + ], + [ + 0xfc, 0xc5, 0xba, 0x1a, 0x98, 0xfc, 0x47, 0x7b, 0x89, 0x48, 0xa0, 0x4d, 0x08, 0xc6, 0xf4, + 0xa7, 0x61, 0x81, 0xfe, 0x75, 0x02, 0x13, 0x70, 0xab, 0x5e, 0x6a, 0xbd, 0x22, 0xb1, 0x79, + 0x2a, 0x2a, + ], + [ + 0x17, 0xb0, 0xaf, 0x15, 0x6a, 0x92, 0x9e, 0xdf, 0x60, 0xc3, 0x51, 0xf3, 0xdf, 0x2d, 0x53, + 0xed, 0x64, 0x3f, 0xdd, 0x75, 0x0a, 0xef, 0x9e, 0xda, 0x90, 0xdc, 0x7c, 0x87, 0x59, 0xa1, + 0x04, 0xa8, + ], + [ + 0x42, 0x85, 0x9d, 0x4f, 0x25, 0x3f, 0x4d, 0x4a, 0x28, 0xee, 0x9a, 0x59, 0xf9, 0xc9, 0x68, + 0x3a, 0x94, 0x04, 0xda, 0x2c, 0x5d, 0x32, 0x9c, 0x73, 0x3a, 0xb8, 0x4f, 0x15, 0x0d, 0xb7, + 0x98, 0xa8, + ], + [ + 0x1b, 0x52, 0x4e, 0x1c, 0x8b, 0x53, 0x82, 0xbb, 0x91, 0x3d, 0x0a, 0x2a, 0xae, 0x8a, 0xd8, + 0x3b, 0xb9, 0x2a, 0x45, 0xfc, 0xb4, 0x77, 0x61, 0xfa, 0x4a, 0x12, 0xf5, 0xb6, 0x31, 0x6c, + 0x2b, 0x20, + ], + [ + 0x9b, 0x65, 0xe4, 0x84, 0xce, 0x3d, 0x96, 0x1a, 0x55, 0x70, 0x81, 0xa4, 0x4c, 0x6c, 0x68, + 0xa0, 0xa2, 0x7e, 0xca, 0x0b, 0x88, 0xfc, 0xe8, 0x20, 0xbd, 0xd9, 0x9c, 0x3d, 0xc2, 0x23, + 0xdc, 0xc7, + ], + [ + 0xa2, 0xe8, 0xf9, 0x72, 0xdc, 0x9f, 0x7d, 0x0b, 0x76, 0x17, 0x7b, 0xb8, 0xbe, 0x10, 0x2e, + 0x6b, 0xec, 0x06, 0x9e, 0xe4, 0x2c, 0x61, 0x08, 0x07, 0x45, 0xe8, 0x82, 0x54, 0x70, 0xe8, + 0x0c, 0x6c, + ], + [ + 0x55, 0x29, 0x61, 0x25, 0x56, 0x95, 0x9e, 0xf8, 0x13, 0xdb, 0xe8, 0xd0, 0xed, 0x29, 0x33, + 0x6a, 0xb7, 0x5e, 0x80, 0xa9, 0xb7, 0x85, 0x50, 0x30, 0x76, 0x0b, 0x29, 0x17, 0xb0, 0x1e, + 0x56, 0x8a, + ], + [ + 0x99, 0x4a, 0x4b, 0x4e, 0xdd, 0xb3, 0x00, 0x69, 0x1e, 0xe1, 0x99, 0x01, 0x71, 0x28, 0x48, + 0xb1, 0x11, 0x4b, 0xad, 0x8a, 0x1a, 0x4a, 0xe1, 0x95, 0xe5, 0xab, 0xe0, 0xec, 0x38, 0x02, + 0x1b, 0x94, + ], + [ + 0xa9, 0x14, 0x4a, 0x5e, 0x7e, 0xfd, 0x25, 0x9b, 0x8b, 0x0d, 0x55, 0x46, 0x7f, 0x46, 0x96, + 0xed, 0x47, 0xec, 0x83, 0x31, 0x7d, 0x61, 0x50, 0x1b, 0x76, 0x36, 0x6d, 0xbc, 0xca, 0x65, + 0xce, 0x73, + ], + [ + 0x4c, 0x83, 0xef, 0xb3, 0x98, 0x2a, 0xfb, 0xd5, 0x00, 0xab, 0x7c, 0x66, 0xd0, 0x2b, 0x99, + 0x6d, 0xf5, 0xfd, 0xc3, 0xd2, 0x06, 0x60, 0xe6, 0x16, 0x00, 0x39, 0x0a, 0xad, 0x6d, 0x5f, + 0x7f, 0x1e, + ], + [ + 0xf0, 0xd6, 0x42, 0xdb, 0xc7, 0x51, 0x76, 0x72, 0xe2, 0x17, 0x23, 0x8a, 0x2f, 0x00, 0x8f, + 0x4f, 0x8c, 0xda, 0xd0, 0x58, 0x6d, 0x8c, 0xe5, 0x11, 0x3e, 0x9e, 0x09, 0xdc, 0xc6, 0x86, + 0x06, 0x19, + ], + [ + 0x71, 0xbe, 0xda, 0x12, 0x0a, 0xaf, 0xdd, 0x3b, 0xb9, 0x22, 0xb3, 0x60, 0xa0, 0x66, 0xd1, + 0x0b, 0x7c, 0xe8, 0x1d, 0x7a, 0xc2, 0xad, 0x98, 0x74, 0xda, 0xac, 0x46, 0xe2, 0x28, 0x2f, + 0x6b, 0x45, + ], + [ + 0xea, 0x74, 0x19, 0xf5, 0xae, 0x82, 0x1e, 0x72, 0x04, 0x86, 0x4e, 0x6a, 0x08, 0x71, 0x43, + 0x3b, 0xa6, 0x12, 0x01, 0x19, 0x08, 0x96, 0x3b, 0xb4, 0x2a, 0x64, 0xf4, 0x2d, 0x65, 0xad, + 0x2f, 0x72, + ], + [ + 0xe8, 0xe5, 0x59, 0x5d, 0x26, 0x8a, 0xaa, 0x85, 0xb3, 0x6c, 0x35, 0x57, 0xe9, 0xd9, 0x6c, + 0x14, 0xa4, 0xff, 0xfa, 0xee, 0x9f, 0x45, 0xbc, 0xae, 0x0c, 0x40, 0x79, 0x68, 0xa7, 0x10, + 0x96, 0x30, + ], + [ + 0x65, 0x70, 0x00, 0xd4, 0x7e, 0x97, 0x1d, 0xcf, 0xb2, 0x13, 0x75, 0xbc, 0xfa, 0x34, 0x96, + 0xf4, 0x7a, 0x2a, 0x2f, 0x0f, 0x12, 0xc8, 0xae, 0xb7, 0x8a, 0x00, 0x8a, 0xce, 0x6a, 0xe5, + 0x5c, 0xa5, + ], + [ + 0xd7, 0x39, 0x56, 0xb9, 0xe0, 0x0d, 0x8f, 0x8b, 0xc5, 0xe4, 0x4f, 0x71, 0x84, 0xdf, 0x13, + 0x87, 0xcd, 0xd6, 0x52, 0xe7, 0x72, 0x6b, 0x8c, 0xcd, 0xa3, 0xdb, 0x48, 0x59, 0xe0, 0x2f, + 0x31, 0xbf, + ], + [ + 0xe8, 0xc3, 0xab, 0xd4, 0x19, 0x3a, 0x84, 0xec, 0x8a, 0x3f, 0xff, 0x3e, 0xeb, 0x3e, 0xcb, + 0xcb, 0xd0, 0x97, 0x9e, 0x0c, 0x97, 0x7a, 0xc1, 0xde, 0xe0, 0x6c, 0x6e, 0x01, 0xa6, 0x0a, + 0xca, 0x1b, + ], + [ + 0xfc, 0xeb, 0xc0, 0x2d, 0xd3, 0x07, 0xdc, 0x58, 0xcd, 0x01, 0xb1, 0x56, 0xd6, 0x3c, 0x69, + 0x48, 0xb8, 0xf3, 0x42, 0x20, 0x55, 0xfa, 0xc1, 0xd8, 0x36, 0x34, 0x9b, 0x01, 0x72, 0x2e, + 0x9c, 0x52, + ], + [ + 0xec, 0x0b, 0x85, 0x49, 0x38, 0x34, 0x3f, 0x85, 0xeb, 0x39, 0xa6, 0x64, 0x8b, 0x9e, 0x44, + 0x9c, 0x2e, 0x4a, 0xee, 0x4d, 0xc9, 0xb4, 0xe9, 0x6a, 0xb5, 0x92, 0xf9, 0xf4, 0x97, 0xd0, + 0x51, 0x38, + ], + [ + 0x26, 0x19, 0xec, 0x68, 0xb2, 0x55, 0x54, 0x2e, 0x3d, 0xa6, 0x8c, 0x05, 0x4b, 0xfe, 0x0d, + 0x7d, 0x0f, 0x27, 0xb7, 0xfd, 0xbe, 0xfc, 0x8b, 0xbc, 0xcd, 0xd2, 0x31, 0x88, 0xfc, 0x71, + 0xfe, 0x7f, + ], + [ + 0x34, 0xd3, 0xc3, 0x19, 0xf5, 0x36, 0xde, 0xb7, 0x4e, 0xd8, 0xf1, 0xf3, 0x20, 0x5d, 0x9a, + 0xef, 0xef, 0x74, 0x87, 0xc8, 0x19, 0xe7, 0x7d, 0x33, 0x51, 0x63, 0x08, 0x20, 0xdb, 0xff, + 0x11, 0x18, + ], + [ + 0xcc, 0x7e, 0xe5, 0x99, 0xe5, 0xd5, 0x9f, 0xee, 0x88, 0xc8, 0x31, 0x57, 0xbd, 0x89, 0x78, + 0x47, 0xc5, 0x91, 0x1d, 0xc7, 0xd3, 0x17, 0xb3, 0x17, 0x5e, 0x0b, 0x08, 0x51, 0x98, 0x34, + 0x99, 0x73, + ], + [ + 0x41, 0xc7, 0xae, 0x75, 0x87, 0x95, 0x76, 0x5c, 0x66, 0x64, 0xa5, 0xd3, 0x9b, 0xf6, 0x38, + 0x41, 0xc7, 0x1f, 0xf1, 0x91, 0xe9, 0x18, 0x95, 0x22, 0xba, 0xd8, 0xeb, 0xff, 0x5d, 0x4e, + 0xca, 0x98, + ], + [ + 0xf0, 0xec, 0xb7, 0x5d, 0xd1, 0x82, 0x08, 0x44, 0xc5, 0x7b, 0x67, 0x62, 0x23, 0x3d, 0x4e, + 0x26, 0x85, 0x3b, 0x3a, 0x7b, 0x81, 0x57, 0xbb, 0xd9, 0xf4, 0x1f, 0x28, 0x0a, 0x0f, 0x1c, + 0xee, 0x9b, + ], + [ + 0xb9, 0x12, 0xc5, 0xeb, 0x63, 0x19, 0xa4, 0xa6, 0xa8, 0x35, 0x80, 0xb9, 0x61, 0x16, 0x10, + 0xbe, 0xdb, 0x31, 0x61, 0x41, 0x79, 0x33, 0x02, 0x61, 0xbf, 0xd8, 0x7a, 0x41, 0x34, 0x7c, + 0xae, 0x1c, + ], + [ + 0xd8, 0x6d, 0x8a, 0x3f, 0x7c, 0x82, 0xc8, 0x9e, 0xd8, 0xe0, 0x41, 0x40, 0x01, 0x7a, 0xa1, + 0x08, 0xa0, 0xa1, 0x46, 0x92, 0x49, 0xf9, 0x2c, 0x8f, 0x02, 0x2b, 0x9d, 0xba, 0xfa, 0x87, + 0xb8, 0x83, + ], + [ + 0x26, 0x70, 0x0e, 0x13, 0x98, 0x3f, 0xef, 0xbd, 0x9c, 0xf1, 0x6d, 0xa2, 0xed, 0x70, 0xfa, + 0x5c, 0x67, 0x98, 0xac, 0x55, 0x06, 0x2a, 0x48, 0x03, 0x12, 0x1a, 0x86, 0x97, 0x31, 0xe3, + 0x08, 0xd2, + ], + [ + 0x8f, 0xf9, 0x74, 0x19, 0x36, 0x3f, 0xfd, 0x70, 0x00, 0x16, 0x7f, 0x13, 0x0e, 0xf7, 0x16, + 0x8f, 0xbe, 0xa0, 0x5f, 0xaf, 0x92, 0x51, 0x82, 0x4c, 0xa5, 0x04, 0x3f, 0x11, 0x3c, 0xc6, + 0xa7, 0xc7, + ], + [ + 0x46, 0x50, 0x18, 0x79, 0xb8, 0xca, 0x85, 0x25, 0xe8, 0xc2, 0xfd, 0x51, 0x9e, 0x2f, 0xbf, + 0xcf, 0xa2, 0xeb, 0xea, 0x26, 0x50, 0x12, 0x94, 0xaa, 0x02, 0xcb, 0xfc, 0xfb, 0x12, 0xe9, + 0x43, 0x54, + ], + [ + 0x97, 0x87, 0xee, 0xb9, 0x1f, 0xe3, 0x10, 0x12, 0x35, 0xe4, 0xa7, 0x60, 0x63, 0xc7, 0x02, + 0x3e, 0xcb, 0x40, 0xf9, 0x23, 0xf9, 0x79, 0x16, 0x63, 0x9c, 0x59, 0x85, 0x92, 0xfa, 0x30, + 0xd6, 0xae, + ], + [ + 0xa2, 0x15, 0x34, 0x20, 0xd8, 0x44, 0x92, 0x8b, 0x44, 0x21, 0x65, 0x02, 0x03, 0xc7, 0x7b, + 0xab, 0xc8, 0xb3, 0x3d, 0x7f, 0x2e, 0x7b, 0x45, 0x0e, 0x29, 0x66, 0xdb, 0x0c, 0x22, 0x09, + 0x77, 0x53, + ], + [ + 0x7f, 0xb4, 0x30, 0x2e, 0x8e, 0x91, 0xf9, 0x11, 0x0a, 0x65, 0x54, 0xc2, 0xc0, 0xa2, 0x46, + 0x01, 0x25, 0x2c, 0x2a, 0x42, 0xc2, 0x22, 0x0c, 0xa9, 0x88, 0xef, 0xcf, 0xe3, 0x99, 0x91, + 0x43, 0x08, + ], + [ + 0x11, 0x6f, 0xea, 0x13, 0x7d, 0xb6, 0xe1, 0x31, 0x13, 0x3e, 0x7f, 0x2b, 0xab, 0x29, 0x60, + 0x45, 0xd8, 0xf4, 0x1c, 0xc5, 0x60, 0x72, 0x79, 0xdb, 0x17, 0xb2, 0x18, 0xca, 0xb0, 0x92, + 0x9a, 0x51, + ], + [ + 0xbd, 0x43, 0xcb, 0x8e, 0xce, 0x8c, 0xd1, 0x86, 0x3b, 0xcd, 0x60, 0x82, 0xd6, 0x5c, 0x5b, + 0x0d, 0x25, 0x66, 0x5b, 0x1c, 0xe1, 0x79, 0x80, 0xf0, 0xda, 0x43, 0xc0, 0xed, 0x54, 0x5f, + 0x98, 0xb4, + ], + [ + 0x2b, 0x4a, 0x51, 0xab, 0x50, 0x5f, 0xc9, 0x6a, 0x09, 0x52, 0xef, 0xda, 0x2b, 0xa6, 0x1b, + 0xcd, 0x30, 0x78, 0xd4, 0xc0, 0x2c, 0x39, 0xa1, 0x86, 0xec, 0x16, 0xf2, 0x18, 0x83, 0xfb, + 0xe0, 0x16, + ], + [ + 0x50, 0x06, 0xb8, 0x38, 0x20, 0x7c, 0x6a, 0x9a, 0xe9, 0xb8, 0x4d, 0x68, 0xf4, 0x67, 0xdd, + 0x4b, 0xb5, 0xc3, 0x05, 0xfb, 0xfb, 0x6b, 0x04, 0xea, 0xb8, 0xfa, 0xaa, 0xbe, 0xec, 0x1e, + 0x18, 0xd8, + ], + [ + 0x99, 0x30, 0xd9, 0xff, 0x0d, 0xee, 0x0e, 0xf5, 0xca, 0x2f, 0x77, 0x10, 0xea, 0x66, 0xb8, + 0xf8, 0x4d, 0xd0, 0xf5, 0xf5, 0x35, 0x1e, 0xcf, 0xfe, 0x72, 0xb9, 0x52, 0xcd, 0x9d, 0xb7, + 0x14, 0x2a, + ], + [ + 0x39, 0xf2, 0xba, 0xbe, 0x52, 0x60, 0x38, 0x52, 0x08, 0x77, 0xfc, 0x7c, 0x33, 0xd8, 0x1a, + 0xcc, 0xf5, 0x78, 0xaf, 0x4a, 0x06, 0xc5, 0xfa, 0x6b, 0x0d, 0x03, 0x8c, 0xae, 0x36, 0xe1, + 0x27, 0x11, + ], + [ + 0x8f, 0x6b, 0x23, 0xff, 0xa1, 0x5f, 0x04, 0x65, 0xe3, 0x17, 0x6e, 0x15, 0xca, 0x64, 0x4c, + 0xf2, 0x4f, 0x86, 0xdc, 0x13, 0x12, 0xfe, 0x71, 0x54, 0x84, 0xe3, 0xc4, 0xae, 0xad, 0x5e, + 0xb7, 0x8b, + ], + [ + 0xa1, 0xfc, 0xd1, 0x9b, 0xfe, 0x8c, 0x32, 0xa6, 0x10, 0x95, 0xb6, 0xbf, 0xbb, 0x26, 0x64, + 0x84, 0x28, 0x57, 0xe1, 0x48, 0xfc, 0xbb, 0x51, 0x88, 0x38, 0x6c, 0x8c, 0xd4, 0x03, 0x48, + 0xd5, 0xb6, + ], + [ + 0xdf, 0xfb, 0xd6, 0x4c, 0xc7, 0xc1, 0xa7, 0xeb, 0x27, 0x98, 0x43, 0x35, 0xd9, 0x41, 0x6d, + 0x51, 0x13, 0x7a, 0x03, 0xd3, 0xfa, 0xbe, 0xc7, 0x14, 0x10, 0x25, 0xc6, 0x26, 0x63, 0x25, + 0x3f, 0xe1, + ], + [ + 0xf7, 0x9b, 0xde, 0x9d, 0xdd, 0x17, 0x96, 0x3e, 0xbc, 0xe6, 0xf7, 0xd0, 0x21, 0xd6, 0x0d, + 0xe7, 0xc2, 0xbd, 0x0d, 0xb9, 0x44, 0xd2, 0x3c, 0x90, 0x0c, 0x0c, 0x0e, 0x77, 0x5f, 0x53, + 0x00, 0x52, + ], + [ + 0x19, 0xa0, 0xb3, 0x9a, 0xa2, 0x5a, 0xc7, 0x93, 0xb5, 0xf6, 0xe9, 0xa0, 0x53, 0x43, 0x64, + 0xcc, 0x0b, 0x3f, 0xd1, 0xea, 0x9b, 0x65, 0x1e, 0x79, 0xc7, 0xf5, 0x0a, 0x59, 0xd4, 0x8e, + 0xf8, 0x13, + ], + [ + 0x9a, 0x8d, 0x93, 0x98, 0x6a, 0x7b, 0x9e, 0x62, 0x94, 0x57, 0x2e, 0xa6, 0x73, 0x66, 0x96, + 0x11, 0x9c, 0x19, 0x5c, 0x1a, 0x9f, 0x5e, 0xae, 0x64, 0x2d, 0x3c, 0x5f, 0xcd, 0x44, 0xe4, + 0x9d, 0xea, + ], + [ + 0xb5, 0x73, 0x27, 0x05, 0xf5, 0x24, 0x13, 0x70, 0xa2, 0x89, 0x08, 0xc2, 0xfe, 0x13, 0x03, + 0xcb, 0x22, 0x3f, 0x03, 0xb9, 0x0d, 0x85, 0x7f, 0xd0, 0x57, 0x3f, 0x00, 0x3f, 0x79, 0xfe, + 0xfe, 0xd4, + ], + [ + 0x79, 0x01, 0xcb, 0x5a, 0xdd, 0xca, 0xe2, 0xd2, 0x10, 0xa5, 0x31, 0xc6, 0x04, 0xa7, 0x6a, + 0x66, 0x0d, 0x77, 0x03, 0x90, 0x93, 0xba, 0xc3, 0x14, 0xde, 0x08, 0x16, 0xa1, 0x63, 0x92, + 0xaf, 0xf1, + ], + [ + 0x8d, 0xc6, 0xfb, 0x69, 0x53, 0x1d, 0x98, 0xd7, 0x0d, 0xc0, 0x42, 0x0e, 0x63, 0x8d, 0x2d, + 0xfd, 0x04, 0xe0, 0x9e, 0x1e, 0xc7, 0x83, 0xed, 0xe9, 0xaa, 0xc7, 0x7d, 0xa9, 0xc5, 0xa0, + 0xda, 0xc4, + ], + [ + 0x95, 0x7b, 0xbd, 0xc7, 0xfa, 0xd0, 0xde, 0xc5, 0x6e, 0x7c, 0x96, 0xaf, 0x4a, 0x3a, 0xb6, + 0x3a, 0xa9, 0xda, 0xf9, 0x34, 0xa5, 0x2f, 0xfc, 0xe8, 0x91, 0x94, 0x5b, 0x7f, 0xb6, 0x22, + 0xd7, 0x91, + ], + [ + 0xf0, 0x44, 0x07, 0x71, 0xa2, 0x9e, 0x57, 0xe1, 0x8c, 0x66, 0x72, 0x79, 0x44, 0x77, 0x0b, + 0x82, 0xcc, 0x77, 0x92, 0x4a, 0xef, 0x33, 0x3c, 0x92, 0x7c, 0xe6, 0xbd, 0xd2, 0xcd, 0xb3, + 0xae, 0x03, + ], + [ + 0x55, 0x69, 0x04, 0x47, 0x19, 0xa1, 0xec, 0x3b, 0x04, 0xd0, 0xaf, 0xa9, 0xe7, 0xa5, 0x31, + 0x0c, 0x7c, 0x04, 0x73, 0x33, 0x1d, 0x13, 0xdc, 0x9f, 0xaf, 0xe1, 0x43, 0xb2, 0xc4, 0xe8, + 0x14, 0x8a, + ], + [ + 0x92, 0x22, 0xcb, 0xf5, 0xd0, 0xdd, 0xc5, 0x05, 0xa6, 0xf2, 0xf0, 0x47, 0x16, 0xe2, 0x2c, + 0x22, 0x6c, 0xee, 0x16, 0xa9, 0x55, 0xfe, 0xf8, 0x8c, 0x61, 0x89, 0x22, 0x09, 0x6d, 0xae, + 0x2f, 0xd0, + ], + [ + 0xa9, 0x13, 0xc8, 0xac, 0x53, 0x20, 0xda, 0xe1, 0xc4, 0xa0, 0x0f, 0xf2, 0x33, 0x43, 0x94, + 0x7e, 0xd0, 0xfd, 0xf8, 0x8d, 0x25, 0x1e, 0x9b, 0xd2, 0xa5, 0x51, 0x9d, 0x3d, 0x61, 0x62, + 0xd2, 0x22, + ], + [ + 0x0f, 0x2a, 0xda, 0x1f, 0x2d, 0xba, 0xe4, 0x8a, 0xe4, 0x68, 0xfe, 0x0c, 0xdb, 0x7b, 0xcd, + 0xa7, 0xd0, 0xcf, 0xfe, 0xe8, 0x54, 0x54, 0x42, 0xe6, 0x82, 0x27, 0x3b, 0xa0, 0x1a, 0x62, + 0x03, 0xa7, + ], + [ + 0x66, 0x92, 0x5e, 0x85, 0xf1, 0xa4, 0x74, 0x3f, 0xd8, 0xd6, 0x0b, 0xa5, 0x95, 0xed, 0x74, + 0x88, 0x7b, 0x7c, 0xaf, 0x32, 0x1d, 0xd8, 0x3b, 0x21, 0xe0, 0x4d, 0x77, 0xc1, 0x15, 0x38, + 0x34, 0x08, + ], + [ + 0x59, 0xf3, 0xfb, 0x05, 0x8c, 0x6b, 0xba, 0x7a, 0x4e, 0x76, 0x39, 0x66, 0x39, 0xfc, 0x4d, + 0xd2, 0x1b, 0xd5, 0x91, 0x63, 0xdb, 0x79, 0x88, 0x99, 0xcf, 0x56, 0xce, 0xf4, 0x8b, 0x3c, + 0x9e, 0xc9, + ], + [ + 0x76, 0xfc, 0xe4, 0x94, 0x79, 0x4d, 0x92, 0xac, 0x28, 0x6b, 0x20, 0xd6, 0x12, 0x6f, 0xc4, + 0x9e, 0xcb, 0x9c, 0xca, 0x2f, 0xa9, 0x4b, 0x5c, 0x72, 0x6f, 0x6e, 0xc1, 0x10, 0x9b, 0x89, + 0x14, 0x14, + ], + [ + 0xb2, 0x24, 0x4e, 0x64, 0x4c, 0xfe, 0x16, 0xf7, 0x2b, 0x65, 0x4f, 0xbc, 0x48, 0xff, 0x0f, + 0xec, 0xec, 0x8f, 0xc5, 0x96, 0x49, 0xca, 0x86, 0x25, 0x09, 0x4b, 0xeb, 0xd9, 0xbd, 0x2e, + 0x40, 0x35, + ], + [ + 0x13, 0x97, 0xb8, 0x8f, 0x41, 0x2a, 0x83, 0xa7, 0xf1, 0xc0, 0xd8, 0x34, 0xc5, 0x33, 0xe4, + 0x86, 0xff, 0x1f, 0x24, 0xf4, 0x2a, 0x31, 0x81, 0x9e, 0x91, 0xb6, 0x24, 0x93, 0x10, 0x60, + 0xa8, 0x63, + ], + [ + 0x50, 0x25, 0x0e, 0x93, 0xf8, 0xc7, 0x3d, 0x2c, 0x1b, 0xe0, 0x15, 0xec, 0x28, 0xe8, 0xcd, + 0x2f, 0xeb, 0x87, 0x1e, 0xfa, 0x71, 0xe9, 0x55, 0xad, 0x24, 0x47, 0x7a, 0xaf, 0xb0, 0x94, + 0x84, 0xfa, + ], + [ + 0xdb, 0xda, 0xec, 0x72, 0xd8, 0x41, 0x24, 0xd8, 0xc7, 0xc5, 0x7a, 0xe4, 0x48, 0xf5, 0xa4, + 0xe3, 0xee, 0xdb, 0x34, 0xdb, 0xa4, 0x37, 0xfd, 0xcb, 0xe6, 0xd2, 0x64, 0x96, 0xb6, 0x8a, + 0xfe, 0x87, + ], + [ + 0x46, 0xb7, 0xea, 0x84, 0x94, 0x42, 0x50, 0x85, 0x6a, 0x71, 0x67, 0x37, 0x05, 0x94, 0x79, + 0x85, 0x42, 0x46, 0xa0, 0x26, 0xd9, 0x47, 0xc1, 0x3d, 0x5a, 0x09, 0x29, 0xbc, 0x8c, 0x1b, + 0xc8, 0x1d, + ], + [ + 0x17, 0x1a, 0xb0, 0x89, 0x01, 0xbe, 0x24, 0x76, 0x9d, 0xbe, 0xbe, 0xdb, 0xdf, 0x7e, 0x02, + 0x45, 0x48, 0x6f, 0xbc, 0x64, 0xab, 0x97, 0x5c, 0xd4, 0x31, 0xa3, 0x95, 0x33, 0x03, 0x2d, + 0x54, 0x15, + ], + [ + 0x7e, 0xf4, 0x64, 0xcf, 0x5a, 0x52, 0x1d, 0x70, 0xc9, 0x33, 0x97, 0x75, 0x10, 0x81, 0x6a, + 0x03, 0x55, 0xb9, 0x1a, 0x50, 0xec, 0xa2, 0x77, 0x88, 0x37, 0xfb, 0x82, 0xda, 0x84, 0x48, + 0xec, 0xf6, + ], + [ + 0x5b, 0xfa, 0x74, 0xc7, 0x43, 0x91, 0x40, 0x28, 0x16, 0x1a, 0xe6, 0x45, 0xd3, 0x00, 0xd9, + 0x0b, 0xbd, 0xc6, 0x59, 0xf1, 0x69, 0xca, 0x14, 0x69, 0xec, 0x86, 0xb4, 0x96, 0x0f, 0x72, + 0x66, 0xcb, + ], + [ + 0x83, 0x43, 0x55, 0xd3, 0x5c, 0xbf, 0xbd, 0x33, 0xb2, 0x39, 0x7e, 0x20, 0x1a, 0xf0, 0x4b, + 0x52, 0xbd, 0xd4, 0x0b, 0x9b, 0x51, 0x27, 0x5f, 0x27, 0x9e, 0xa4, 0x7e, 0x93, 0x54, 0x7b, + 0x63, 0x1e, + ], + [ + 0x7b, 0x6b, 0xb1, 0xe9, 0xd1, 0xb0, 0x17, 0xff, 0x82, 0x94, 0x55, 0x96, 0xcf, 0x3c, 0xfb, + 0x1a, 0x6c, 0xee, 0x97, 0x1c, 0x1e, 0xbb, 0x16, 0xf2, 0xc6, 0xbd, 0x23, 0xc2, 0xd6, 0x42, + 0x72, 0x8e, + ], + [ + 0x5f, 0x2f, 0x2d, 0xca, 0x1d, 0x95, 0x1c, 0x74, 0x29, 0xb5, 0x20, 0x07, 0xf3, 0x96, 0x32, + 0x8c, 0x64, 0xc2, 0x5e, 0x22, 0x6c, 0x18, 0x67, 0x31, 0x81, 0x58, 0xf7, 0xf2, 0xcb, 0xdd, + 0x40, 0xa9, + ], + [ + 0x37, 0xa1, 0xbe, 0x2a, 0x88, 0xda, 0xdc, 0xd0, 0xe6, 0x06, 0x2f, 0x54, 0xdd, 0xcc, 0x01, + 0xa0, 0x33, 0x60, 0xba, 0x61, 0xca, 0x77, 0x84, 0xa7, 0x44, 0xe7, 0x57, 0x48, 0x8b, 0xf8, + 0xce, 0xb2, + ], + [ + 0x8e, 0xdd, 0x81, 0xff, 0x20, 0x32, 0x4e, 0xa0, 0xcf, 0xe7, 0x0c, 0x70, 0x0f, 0xf4, 0xe9, + 0xdb, 0x75, 0x80, 0xd2, 0x69, 0xb4, 0x23, 0xd9, 0xf6, 0x14, 0x70, 0xb3, 0x70, 0x81, 0x9c, + 0xbd, 0x17, + ], + [ + 0x33, 0x7f, 0x79, 0x13, 0xdb, 0x22, 0xd9, 0x1e, 0xf4, 0x25, 0xf8, 0x21, 0x02, 0xbc, 0x80, + 0x75, 0xef, 0x67, 0xe2, 0x3a, 0x2b, 0xe3, 0x59, 0x96, 0x5e, 0xa3, 0x16, 0xe7, 0x8e, 0x1e, + 0xff, 0x3f, + ], + [ + 0x60, 0xb1, 0xe3, 0x25, 0x50, 0xf9, 0xd5, 0xf2, 0x5f, 0x9d, 0xd0, 0x40, 0xe7, 0xa1, 0x06, + 0xb1, 0x5d, 0x8e, 0xb2, 0x82, 0xdd, 0x6b, 0x3e, 0x19, 0x14, 0xc7, 0x3d, 0x80, 0x66, 0x89, + 0x64, 0x12, + ], + [ + 0xcd, 0xae, 0x18, 0x4e, 0xdd, 0x6b, 0xf7, 0x1c, 0x1f, 0xb6, 0x2d, 0x6e, 0x66, 0x82, 0xfd, + 0xb2, 0x03, 0x24, 0x55, 0xc0, 0xe5, 0x01, 0x43, 0x74, 0x21, 0x35, 0xfb, 0xbe, 0x80, 0x9b, + 0xd7, 0x93, + ], + [ + 0x6e, 0x45, 0x28, 0x48, 0x78, 0x41, 0x97, 0xf0, 0x09, 0x27, 0xd3, 0x79, 0xe3, 0xdb, 0x9e, + 0x69, 0xa5, 0x13, 0x1d, 0x22, 0x69, 0xf8, 0x62, 0xbf, 0xcd, 0x05, 0xa0, 0xb3, 0x8f, 0x6a, + 0xbf, 0x7f, + ], + [ + 0x28, 0xda, 0x5c, 0xa8, 0x14, 0x3b, 0xfa, 0x5e, 0x9f, 0x64, 0x2e, 0x58, 0xe5, 0xe8, 0x7b, + 0xef, 0x0a, 0x2e, 0xb0, 0xc0, 0x0b, 0xcd, 0x4e, 0xfd, 0xd0, 0x10, 0x50, 0x29, 0x3f, 0x5f, + 0xac, 0x91, + ], + [ + 0x70, 0x47, 0xa3, 0xcc, 0x0a, 0x76, 0xed, 0xce, 0xe4, 0x57, 0x92, 0xca, 0x71, 0x52, 0x7c, + 0x75, 0x3f, 0x61, 0x67, 0x48, 0x4f, 0x14, 0xb9, 0x4c, 0x4a, 0x3b, 0xd2, 0x99, 0x75, 0x16, + 0x72, 0x5c, + ], + [ + 0x94, 0x70, 0x35, 0xe9, 0x7d, 0x0f, 0x7e, 0x19, 0x37, 0xf7, 0x91, 0xbc, 0x18, 0x9f, 0x60, + 0xc9, 0x84, 0xce, 0xaa, 0xa7, 0xa8, 0x49, 0x4f, 0xc6, 0x7f, 0x9f, 0x8f, 0x4d, 0xe8, 0xcc, + 0xf2, 0xc6, + ], + [ + 0x6a, 0xa7, 0xec, 0x8a, 0xc2, 0xa9, 0x99, 0xa9, 0x0c, 0xe6, 0xc7, 0x86, 0x68, 0xdf, 0xfe, + 0x4e, 0x48, 0x7e, 0x25, 0x76, 0xa9, 0x7c, 0xa3, 0x66, 0xec, 0x81, 0xec, 0xb3, 0x35, 0xaf, + 0x90, 0xd0, + ], + [ + 0x35, 0x4a, 0x83, 0xed, 0x99, 0x88, 0xf7, 0x9f, 0x60, 0x38, 0xd4, 0xc7, 0xa7, 0xda, 0xdb, + 0xad, 0x8a, 0xf3, 0x2f, 0x4a, 0xd6, 0xdf, 0x89, 0x3e, 0x0e, 0x58, 0x07, 0xa1, 0xb1, 0x94, + 0x4f, 0xf9, + ], + [ + 0x22, 0x37, 0xa9, 0x76, 0xfa, 0x96, 0x1f, 0x59, 0x21, 0xfd, 0x19, 0xf2, 0xb0, 0x3c, 0x92, + 0x5c, 0x72, 0x5d, 0x77, 0xb2, 0x0c, 0xe8, 0xf7, 0x90, 0xc1, 0x97, 0x09, 0xc0, 0x3d, 0xe4, + 0xd8, 0x14, + ], + [ + 0x72, 0xa1, 0x52, 0xdd, 0xfb, 0x8e, 0x86, 0x42, 0x97, 0xc9, 0x17, 0xaf, 0x52, 0xea, 0x6c, + 0x1c, 0x68, 0xae, 0xad, 0x0f, 0xee, 0x1a, 0x62, 0x67, 0x3f, 0xcc, 0x7e, 0x0c, 0x94, 0x97, + 0x9d, 0x00, + ], + [ + 0x44, 0xda, 0x15, 0x8b, 0xa2, 0x7f, 0x92, 0x52, 0x71, 0x2a, 0x74, 0xff, 0x6a, 0x55, 0xc5, + 0xd5, 0x31, 0xf6, 0x96, 0x09, 0xf1, 0xf6, 0xe7, 0xf1, 0x7c, 0x44, 0x43, 0xa8, 0xe2, 0x08, + 0x9b, 0xe4, + ], + [ + 0xbb, 0xa9, 0xdb, 0x4c, 0xdb, 0xea, 0x0a, 0x37, 0xc2, 0x07, 0xbb, 0xb8, 0x3e, 0x20, 0xf8, + 0x28, 0xcd, 0x44, 0x41, 0xc4, 0x98, 0x91, 0x10, 0x1d, 0xc9, 0x4f, 0xd2, 0x0d, 0xc8, 0xef, + 0xc3, 0x49, + ], + [ + 0xaf, 0x85, 0xb9, 0x07, 0x1d, 0xfa, 0xfe, 0xac, 0x14, 0x09, 0xd3, 0xf1, 0xd1, 0x9b, 0xaf, + 0xc9, 0xbc, 0x7c, 0x37, 0x97, 0x4c, 0xde, 0x8d, 0xf0, 0xee, 0x61, 0x68, 0xf0, 0x08, 0x6e, + 0x53, 0x9c, + ], + [ + 0xd2, 0x6e, 0x83, 0x24, 0x54, 0x29, 0x9e, 0x9f, 0xab, 0xb8, 0x9e, 0x0e, 0x5f, 0xff, 0xdc, + 0x04, 0x6d, 0x4e, 0x14, 0x43, 0x1b, 0xc1, 0xbf, 0x60, 0x7f, 0xfb, 0x2e, 0x8a, 0x1d, 0xde, + 0xcf, 0x7b, + ], + [ + 0xcf, 0xe2, 0xa2, 0x0f, 0xf7, 0x01, 0xa1, 0xf3, 0xe1, 0x4f, 0x63, 0xbd, 0x70, 0xd6, 0xc6, + 0xbc, 0x6f, 0xba, 0x81, 0x72, 0xec, 0x6d, 0x5a, 0x50, 0x5c, 0xda, 0xb3, 0x92, 0x7c, 0x0a, + 0x9d, 0xe6, + ], + [ + 0x0b, 0xc1, 0x40, 0x66, 0xc3, 0x30, 0x13, 0xfe, 0x88, 0xf6, 0x6e, 0x31, 0x4e, 0x4c, 0xf1, + 0x50, 0xb0, 0xb2, 0xd4, 0xd6, 0x45, 0x1a, 0x1a, 0x51, 0xdb, 0xbd, 0x1c, 0x27, 0xcd, 0x11, + 0xde, 0x28, + ], + [ + 0x78, 0xfd, 0xc8, 0xd4, 0x22, 0xc4, 0x9c, 0xed, 0x03, 0x5a, 0x9e, 0xdf, 0x18, 0xd0, 0x0d, + 0x3c, 0x6a, 0x8d, 0x81, 0xdf, 0x21, 0x0f, 0x3e, 0x5e, 0x44, 0x8e, 0x04, 0x5e, 0x77, 0xb4, + 0x1e, 0x88, + ], + [ + 0xaa, 0xdc, 0x37, 0xb8, 0xba, 0x56, 0x45, 0xe6, 0x2f, 0x45, 0x46, 0x80, 0x2d, 0xb2, 0x21, + 0x59, 0x3a, 0x94, 0x72, 0x9c, 0xcb, 0xfc, 0x5a, 0x97, 0xd0, 0x13, 0x65, 0xa8, 0x8f, 0x64, + 0x98, 0x78, + ], + [ + 0xaa, 0xf4, 0xf5, 0x8d, 0xe9, 0x93, 0x00, 0xcf, 0xad, 0xc4, 0x58, 0x57, 0x55, 0xf3, 0x76, + 0xd5, 0xfa, 0x74, 0x7d, 0x5b, 0xc5, 0x61, 0xd5, 0xbd, 0x9d, 0x71, 0x0d, 0xe1, 0xf9, 0x1b, + 0xf4, 0x2d, + ], + [ + 0x60, 0x85, 0x91, 0x88, 0xcf, 0xfe, 0x29, 0x7f, 0x44, 0xdd, 0xe2, 0x9f, 0x2d, 0x28, 0x65, + 0x63, 0x46, 0x21, 0xf2, 0x62, 0x15, 0x04, 0x9c, 0xae, 0xb3, 0x04, 0xcc, 0xba, 0x56, 0x6a, + 0x8b, 0x17, + ], + [ + 0xe4, 0x34, 0xdc, 0x35, 0xda, 0x08, 0x4c, 0xf8, 0xd7, 0xe8, 0x18, 0x66, 0x88, 0xea, 0x2d, + 0xac, 0xb5, 0x3d, 0xb7, 0x00, 0x3d, 0x42, 0x7a, 0xf3, 0xab, 0xf3, 0x51, 0xbd, 0x9d, 0x0a, + 0x4e, 0x8d, + ], + [ + 0xb2, 0x9a, 0x2b, 0x3b, 0x6f, 0x2f, 0xf1, 0xb7, 0x65, 0x77, 0x7a, 0x23, 0x17, 0x25, 0x94, + 0x1d, 0xa5, 0x07, 0x2c, 0xc4, 0xfc, 0xc3, 0x0a, 0xc4, 0xa2, 0xce, 0x09, 0x70, 0x6e, 0x8d, + 0xde, 0xff, + ], + [ + 0x2d, 0xa5, 0x66, 0x74, 0x72, 0x93, 0x43, 0xac, 0xc9, 0x93, 0x37, 0x52, 0xc8, 0xc4, 0x69, + 0xa2, 0x44, 0x25, 0x29, 0x15, 0x24, 0x2e, 0xb6, 0xd4, 0xc0, 0x2d, 0x11, 0xdd, 0xd6, 0x91, + 0x64, 0xa1, + ], + [ + 0xb6, 0x87, 0x92, 0x69, 0x7e, 0xd8, 0x76, 0xaf, 0x8b, 0x48, 0x58, 0xb3, 0x16, 0xf5, 0xb5, + 0x4d, 0x81, 0xf6, 0x86, 0x11, 0x91, 0xad, 0x29, 0x50, 0xc1, 0xfd, 0xe6, 0xc3, 0xdc, 0x7b, + 0x3d, 0xea, + ], + [ + 0xbe, 0xe8, 0x94, 0x03, 0xb5, 0xbf, 0x0e, 0x62, 0x6c, 0x2f, 0x71, 0xad, 0xb3, 0x66, 0x31, + 0x1c, 0x69, 0x70, 0x13, 0xdf, 0x53, 0x10, 0x71, 0x81, 0xa9, 0x63, 0xad, 0xc4, 0x59, 0xef, + 0x4d, 0x99, + ], + [ + 0xdc, 0x47, 0x18, 0x88, 0xe6, 0x13, 0x6f, 0x84, 0xc4, 0x9e, 0x53, 0x1e, 0x9c, 0x92, 0x40, + 0xdc, 0x4e, 0x3f, 0xba, 0x66, 0xda, 0x9d, 0x3a, 0x49, 0xe2, 0xaf, 0x62, 0x02, 0x13, 0x36, + 0x83, 0xe0, + ], + [ + 0x55, 0x0d, 0x3d, 0xe9, 0x5b, 0xe0, 0xbd, 0x28, 0xa7, 0x9c, 0x3e, 0xb4, 0xea, 0x7f, 0x05, + 0x69, 0x2c, 0x60, 0xb0, 0x60, 0x2e, 0x48, 0xb4, 0x94, 0x61, 0xe7, 0x03, 0x37, 0x9b, 0x08, + 0xa7, 0x1a, + ], + [ + 0xfc, 0x37, 0x72, 0x60, 0xa6, 0x9a, 0x39, 0xdd, 0x78, 0x62, 0x35, 0xc8, 0x9f, 0x4b, 0xcd, + 0x5d, 0x96, 0x39, 0x15, 0x77, 0x31, 0xca, 0xc3, 0x80, 0x71, 0xa0, 0x50, 0x87, 0x50, 0xeb, + 0x11, 0x5a, + ], + [ + 0x0a, 0x0a, 0x1b, 0xca, 0xdd, 0x9f, 0x6a, 0x55, 0x39, 0x37, 0x6f, 0xa8, 0x22, 0x76, 0xe0, + 0x43, 0xae, 0x3c, 0xb4, 0x49, 0x9d, 0xaa, 0xaf, 0x81, 0x36, 0x57, 0x2e, 0xcb, 0x1f, 0x9f, + 0x0d, 0x60, + ], + [ + 0x04, 0x40, 0xfd, 0x76, 0xb4, 0xe6, 0x85, 0xd1, 0x70, 0x19, 0xb0, 0xee, 0xf8, 0x36, 0xce, + 0xa9, 0x99, 0x46, 0x50, 0x02, 0x8b, 0x99, 0xdd, 0xdf, 0xb4, 0x8b, 0xe0, 0x6f, 0xa4, 0x24, + 0x0a, 0xa6, + ], + [ + 0xdf, 0x5d, 0x40, 0x0f, 0x26, 0x50, 0x39, 0x45, 0x02, 0x28, 0xfa, 0x54, 0x7d, 0xf2, 0xbe, + 0xe7, 0x9e, 0x6a, 0x35, 0x0d, 0xaa, 0x43, 0xfb, 0xa4, 0xbd, 0x32, 0x8b, 0xc6, 0x54, 0x82, + 0x4c, 0x64, + ], + [ + 0xde, 0xf9, 0x93, 0xa6, 0x52, 0x05, 0x23, 0x16, 0x25, 0x28, 0x0c, 0x5e, 0x3c, 0x23, 0xe4, + 0x4b, 0x26, 0x3d, 0x0a, 0xa9, 0x48, 0xfb, 0xc3, 0x30, 0x05, 0x56, 0x26, 0xb8, 0xab, 0x25, + 0xa5, 0xa1, + ], + [ + 0x23, 0x8b, 0xa8, 0xd0, 0x20, 0x78, 0x54, 0x48, 0x47, 0x43, 0x8d, 0xb7, 0x77, 0x37, 0x30, + 0xa2, 0x5d, 0x58, 0x40, 0x74, 0xea, 0xc9, 0x44, 0x89, 0xbd, 0x8e, 0xb8, 0x6c, 0xa2, 0x67, + 0xc9, 0x37, + ], + [ + 0x04, 0xcb, 0x44, 0xc8, 0x0b, 0x6f, 0xbf, 0x8c, 0xeb, 0x1d, 0x80, 0xaf, 0x68, 0x8c, 0x9f, + 0x7c, 0x0b, 0x2a, 0xb5, 0xbf, 0x4a, 0x96, 0x4c, 0xab, 0xe3, 0x70, 0x41, 0xf2, 0x3b, 0x23, + 0xf7, 0xa8, + ], + [ + 0xbb, 0xf2, 0x65, 0xbe, 0xa1, 0xb9, 0x05, 0xc8, 0x54, 0x05, 0x4a, 0x8d, 0xbe, 0x97, 0xfe, + 0xdc, 0xc0, 0x6f, 0xa5, 0x43, 0x06, 0x55, 0x14, 0x23, 0x71, 0x12, 0x31, 0xa4, 0xad, 0x06, + 0x10, 0xc9, + ], + [ + 0x23, 0x6f, 0x28, 0x40, 0xbf, 0xc5, 0xdc, 0x34, 0xb2, 0x87, 0x42, 0xdd, 0x0b, 0x4c, 0x9d, + 0xef, 0xe8, 0xa4, 0xa5, 0xfa, 0x95, 0x92, 0xe4, 0x9c, 0xef, 0xfb, 0x9a, 0xb5, 0x1b, 0x7e, + 0xb9, 0x74, + ], + [ + 0x1c, 0x5f, 0x5a, 0xc1, 0x47, 0xec, 0x2d, 0xee, 0x04, 0xd8, 0xce, 0x29, 0xbd, 0xbe, 0xbb, + 0xc5, 0x8f, 0x57, 0x8e, 0x0e, 0x13, 0x92, 0xda, 0x66, 0xf3, 0x52, 0xa6, 0x2e, 0x5c, 0x09, + 0xc5, 0x03, + ], + [ + 0x22, 0xb8, 0x8d, 0x74, 0xa6, 0xb2, 0x3b, 0xe6, 0x87, 0xaa, 0x96, 0x34, 0x0c, 0x88, 0x12, + 0x53, 0xc2, 0xe9, 0x87, 0x3c, 0x52, 0x6e, 0xec, 0x73, 0x66, 0xdc, 0x5f, 0x73, 0x3a, 0xda, + 0x30, 0x6a, + ], + [ + 0x3a, 0xe7, 0x97, 0xce, 0xef, 0x26, 0x5e, 0x3a, 0x4f, 0x9c, 0x19, 0x78, 0xc4, 0x7c, 0x75, + 0x9e, 0xb3, 0x4a, 0x32, 0x90, 0x92, 0x51, 0xde, 0xe7, 0x27, 0x6d, 0xb3, 0x39, 0xb1, 0x7b, + 0x3d, 0xe3, + ], + [ + 0x6a, 0x79, 0xcc, 0x29, 0x4e, 0x25, 0xeb, 0x1a, 0x13, 0x38, 0x1e, 0x9f, 0x33, 0x61, 0xee, + 0x96, 0xc4, 0x7e, 0xe7, 0xed, 0x00, 0xbf, 0x73, 0xab, 0xad, 0xb8, 0xf9, 0x66, 0x4b, 0xff, + 0xd0, 0xa7, + ], + [ + 0xd9, 0x1d, 0x69, 0x1c, 0x89, 0x4f, 0x82, 0x66, 0xe3, 0xf2, 0xd5, 0xe5, 0x58, 0xad, 0x23, + 0x49, 0xd6, 0x78, 0x33, 0x27, 0xa7, 0x52, 0xa4, 0x94, 0x9b, 0xc5, 0x54, 0xf5, 0x14, 0xe3, + 0x49, 0x88, + ], + [ + 0xe3, 0x58, 0x48, 0xa7, 0xc6, 0x47, 0x7c, 0xfe, 0x93, 0x66, 0xae, 0x64, 0x57, 0x10, 0x69, + 0xfd, 0x3a, 0x5a, 0xd7, 0x52, 0xa4, 0x60, 0xd2, 0x8c, 0x5f, 0x73, 0xd4, 0x38, 0xb5, 0xe4, + 0x32, 0xbf, + ], + [ + 0xf3, 0xb9, 0xeb, 0x9e, 0x16, 0x3a, 0xf2, 0x08, 0x8b, 0x11, 0xde, 0x0a, 0x36, 0x9f, 0xb5, + 0x83, 0xf5, 0x8f, 0x94, 0x40, 0xe0, 0xe5, 0xc7, 0x0f, 0xce, 0x0c, 0x59, 0x90, 0x9e, 0xce, + 0xce, 0x8a, + ], + [ + 0x28, 0xaf, 0xdd, 0x85, 0x19, 0x6b, 0x63, 0x7a, 0x3c, 0x64, 0xff, 0x1f, 0x53, 0xaf, 0x1a, + 0xd8, 0xde, 0x14, 0x5c, 0xf6, 0x52, 0x29, 0x7e, 0xde, 0x1b, 0x38, 0xf2, 0xcb, 0xd6, 0xa4, + 0xb4, 0xbf, + ], + [ + 0x6f, 0x1f, 0x00, 0x41, 0x08, 0x4f, 0x67, 0xce, 0xd1, 0x74, 0x80, 0x84, 0x84, 0xbd, 0x05, + 0x85, 0x1d, 0xe9, 0x44, 0x43, 0xd7, 0x75, 0x58, 0x5e, 0x9d, 0x86, 0xd4, 0xc2, 0x58, 0x9d, + 0xba, 0x59, + ], + [ + 0xd3, 0x44, 0xf0, 0x74, 0xc8, 0x15, 0xfd, 0xed, 0x54, 0x3c, 0xd5, 0xa2, 0x9a, 0x47, 0x65, + 0x9d, 0xe5, 0x29, 0xcd, 0x0a, 0xdb, 0x1c, 0x1f, 0xae, 0x6e, 0xda, 0x2d, 0x68, 0x5d, 0x42, + 0x2b, 0xd8, + ], + [ + 0x40, 0x82, 0xd8, 0xaa, 0x0b, 0xe1, 0x3a, 0xb1, 0x43, 0xf5, 0x5d, 0x60, 0x06, 0x65, 0xa8, + 0xae, 0x7e, 0xf9, 0x0b, 0xa0, 0x9d, 0x57, 0xc3, 0x8f, 0xa5, 0x38, 0xa2, 0x60, 0x4d, 0x7e, + 0x98, 0x27, + ], + [ + 0xb5, 0x2c, 0xf1, 0x38, 0xa3, 0x50, 0x5d, 0xc3, 0xd3, 0xcd, 0x84, 0xa7, 0x79, 0x12, 0xf4, + 0xbe, 0x1a, 0x33, 0xdf, 0x2c, 0x30, 0x65, 0xd3, 0xe4, 0xcb, 0x37, 0xfb, 0x1d, 0x5d, 0x1b, + 0x50, 0x72, + ], + [ + 0x5e, 0x29, 0xe3, 0x0c, 0x8e, 0xa9, 0xa8, 0x95, 0x60, 0x28, 0x1b, 0x90, 0xdb, 0xe9, 0x6f, + 0xe6, 0xf0, 0x67, 0xa8, 0xac, 0xc0, 0xf1, 0x64, 0xa7, 0x14, 0x49, 0xbf, 0x0d, 0xa7, 0xd5, + 0x8d, 0x7e, + ], + [ + 0xa4, 0xc9, 0xb5, 0xd9, 0x89, 0xfa, 0x12, 0xd6, 0x08, 0x05, 0x2e, 0x66, 0xdc, 0x5a, 0x37, + 0xa4, 0x31, 0xd6, 0x79, 0xe9, 0x3d, 0x0e, 0xd2, 0x55, 0x72, 0xf9, 0x7f, 0x67, 0x46, 0x0b, + 0xb1, 0x57, + ], + [ + 0xb9, 0x3e, 0xdc, 0xd1, 0xe7, 0x47, 0x16, 0xac, 0x76, 0xd7, 0x1e, 0x26, 0xce, 0x34, 0x91, + 0xbe, 0x20, 0x74, 0x53, 0x75, 0xdc, 0xd4, 0x84, 0x8d, 0x8f, 0x3b, 0x91, 0xa3, 0xf7, 0x85, + 0xdb, 0xb1, + ], + [ + 0x6d, 0x91, 0x8f, 0x65, 0x0e, 0x2b, 0x4a, 0x9f, 0x36, 0x09, 0x77, 0xc4, 0x44, 0x7e, 0x63, + 0x76, 0xeb, 0x63, 0x2e, 0xc1, 0xf6, 0x87, 0xba, 0x96, 0x3a, 0xa9, 0x98, 0x3e, 0x90, 0x08, + 0x65, 0x94, + ], + [ + 0x2b, 0xde, 0x9b, 0x0c, 0x08, 0x57, 0xae, 0xe2, 0xcf, 0xfd, 0xea, 0x6b, 0x87, 0x23, 0xea, + 0xf5, 0x98, 0x94, 0x49, 0x9e, 0xc2, 0x78, 0xc1, 0x8f, 0x02, 0x0e, 0xdd, 0x3c, 0x22, 0x95, + 0xe4, 0x24, + ], + [ + 0xba, 0xcd, 0xda, 0x17, 0xed, 0x98, 0x6c, 0x07, 0xf8, 0x27, 0x22, 0x97, 0x09, 0xe1, 0xde, + 0xd9, 0x9d, 0x4d, 0xa9, 0x17, 0xa5, 0xe7, 0xe7, 0xec, 0x15, 0x81, 0x6e, 0xaf, 0x2c, 0xac, + 0xf5, 0x4c, + ], + [ + 0xcf, 0xc4, 0x79, 0x82, 0x8d, 0x81, 0x33, 0xd8, 0x24, 0xa4, 0x7f, 0xe2, 0x63, 0x26, 0xd4, + 0x58, 0xb6, 0xb9, 0x41, 0x34, 0x27, 0x6b, 0x94, 0x54, 0x04, 0x19, 0x7f, 0x42, 0x41, 0x15, + 0x64, 0xc3, + ], + [ + 0xc1, 0xd0, 0x55, 0x86, 0x04, 0x08, 0x2a, 0xf4, 0x38, 0x0f, 0x8a, 0xf6, 0xe6, 0xdf, 0x68, + 0x6f, 0x24, 0xc7, 0x43, 0x8c, 0xa4, 0xf2, 0xa6, 0x7c, 0x86, 0xa7, 0x1e, 0xe7, 0x85, 0x26, + 0x01, 0xf9, + ], + [ + 0xe7, 0x1f, 0xac, 0x6f, 0xb7, 0x85, 0x94, 0x2c, 0xc6, 0xc6, 0x40, 0x4a, 0x42, 0x3f, 0x94, + 0xf3, 0x2a, 0x28, 0xae, 0x66, 0xd6, 0x9f, 0xf4, 0x14, 0x94, 0xc3, 0x8b, 0xfd, 0x47, 0x88, + 0xb2, 0xf8, + ], + [ + 0x66, 0xbe, 0x4f, 0x15, 0x5c, 0x5e, 0xf2, 0xeb, 0xd3, 0x77, 0x2b, 0x22, 0x8f, 0x2f, 0x00, + 0x68, 0x1e, 0x4e, 0xd5, 0x82, 0x6c, 0xdb, 0x3b, 0x19, 0x43, 0xcc, 0x11, 0xad, 0x15, 0xad, + 0x1d, 0x28, + ], + [ + 0x42, 0xd7, 0x26, 0x74, 0x97, 0x4f, 0x69, 0x4b, 0x5f, 0x51, 0x59, 0x59, 0x32, 0x43, 0x11, + 0x4d, 0x38, 0xa5, 0xc3, 0x9c, 0x89, 0xd6, 0xb6, 0x2f, 0xee, 0x06, 0x1f, 0xf5, 0x23, 0x24, + 0x0e, 0xe1, + ], + [ + 0xa7, 0xce, 0x83, 0x6d, 0x03, 0x2b, 0x2b, 0xf6, 0x2b, 0x7e, 0x20, 0x97, 0xa8, 0xe0, 0xa6, + 0xd8, 0xae, 0xb3, 0x54, 0x05, 0xad, 0x15, 0x27, 0x1e, 0x96, 0xd3, 0xb0, 0x18, 0x8a, 0x1d, + 0x06, 0xfb, + ], + [ + 0x47, 0x19, 0x72, 0x30, 0xe1, 0xe4, 0xb2, 0x9f, 0xc0, 0xbd, 0x84, 0xd7, 0xd7, 0x89, 0x66, + 0xc0, 0x92, 0x54, 0x52, 0xaf, 0xf7, 0x2a, 0x2a, 0x12, 0x15, 0x38, 0xb1, 0x02, 0x45, 0x7e, + 0x9e, 0xbe, + ], + [ + 0x83, 0x97, 0x8b, 0x4c, 0x69, 0xc4, 0x8d, 0xd9, 0x78, 0xab, 0x43, 0xfe, 0x30, 0xf0, 0x77, + 0x61, 0x52, 0x94, 0xf9, 0x38, 0xfb, 0x7f, 0x93, 0x6d, 0x9e, 0xb3, 0x40, 0xe5, 0x1e, 0xa7, + 0xdb, 0x2e, + ], + [ + 0xd3, 0x6c, 0xd1, 0xc7, 0x4e, 0xf8, 0xd7, 0x32, 0x6d, 0x80, 0x21, 0xb7, 0x76, 0xc1, 0x8f, + 0xb5, 0xa5, 0x72, 0x4b, 0x7f, 0x7b, 0xc9, 0x3c, 0x2f, 0x42, 0xe4, 0x3e, 0x10, 0xef, 0x27, + 0xd1, 0x2a, + ], + [ + 0xac, 0xb8, 0xd9, 0x54, 0xe2, 0xcf, 0xef, 0x49, 0x58, 0x62, 0x22, 0x1e, 0x91, 0xbd, 0x75, + 0x23, 0x61, 0x3c, 0xf8, 0x80, 0x88, 0x27, 0xcb, 0x33, 0xed, 0xfe, 0x49, 0x04, 0xcc, 0x51, + 0xbf, 0x29, + ], + [ + 0xe8, 0x9d, 0x44, 0xc8, 0xfd, 0x6a, 0x9b, 0xac, 0x8a, 0xf3, 0x3c, 0xe4, 0x7f, 0x56, 0x33, + 0x76, 0x17, 0xd4, 0x49, 0xbf, 0x7f, 0xf3, 0x95, 0x6b, 0x61, 0x8c, 0x64, 0x6d, 0xe8, 0x29, + 0xcb, 0xcb, + ], + [ + 0x69, 0x5f, 0xb3, 0x13, 0x4a, 0xd8, 0x2c, 0x3b, 0x80, 0x22, 0xbc, 0x54, 0x64, 0xed, 0xd0, + 0xbc, 0xc9, 0x42, 0x4e, 0xf6, 0x72, 0xb5, 0x22, 0x45, 0xdc, 0xb6, 0xab, 0x23, 0x74, 0x32, + 0x7c, 0xe3, + ], + [ + 0xf2, 0x19, 0x2e, 0x10, 0x30, 0x36, 0x34, 0x15, 0xd7, 0xb4, 0xfb, 0x04, 0x06, 0x54, 0x0a, + 0x00, 0x60, 0xe8, 0xe2, 0xfc, 0x89, 0x82, 0xf3, 0xf3, 0x22, 0x89, 0x37, 0x9e, 0x11, 0xfa, + 0x65, 0x46, + ], + [ + 0x91, 0x5c, 0x3e, 0xb9, 0x87, 0xb2, 0x0e, 0x1a, 0xf6, 0x20, 0xc1, 0x40, 0x31, 0x97, 0xbf, + 0x68, 0x7f, 0xb7, 0xf1, 0x85, 0x13, 0xb3, 0xa7, 0x3f, 0xde, 0x6e, 0x78, 0xc7, 0x07, 0x2c, + 0x41, 0xa6, + ], + [ + 0x97, 0x80, 0xe2, 0x6d, 0x96, 0xb1, 0xf2, 0xa9, 0xa1, 0x8e, 0xf8, 0xfc, 0x72, 0xd5, 0x89, + 0xdb, 0xf0, 0x3e, 0xf7, 0x88, 0x13, 0x7b, 0x64, 0xf4, 0x38, 0x97, 0xe8, 0x3a, 0x91, 0xe7, + 0xfe, 0xec, + ], + [ + 0x51, 0x85, 0x8d, 0xe9, 0x98, 0x9b, 0xf7, 0x44, 0x18, 0x65, 0xeb, 0xda, 0xdb, 0xf7, 0x38, + 0x2c, 0x88, 0x38, 0xed, 0xbf, 0x83, 0x0f, 0x5d, 0x86, 0xa9, 0xa5, 0x1a, 0xc7, 0x73, 0x67, + 0x6d, 0xd6, + ], + [ + 0xe7, 0x67, 0x80, 0x3f, 0x8e, 0xcf, 0x1d, 0xee, 0x6b, 0xb0, 0x34, 0x58, 0x11, 0xf7, 0x31, + 0x2c, 0xda, 0x55, 0x60, 0x58, 0xb1, 0x9d, 0xb6, 0x38, 0x9a, 0xd9, 0xae, 0x35, 0x68, 0x64, + 0x3d, 0xdd, + ], + [ + 0x8a, 0x01, 0x2a, 0x6d, 0xe2, 0x94, 0x3a, 0x5a, 0xa4, 0xd7, 0x7a, 0xcf, 0x5e, 0x69, 0x5d, + 0x44, 0x56, 0x76, 0x0a, 0x3f, 0x1f, 0x30, 0xa5, 0xd6, 0xdc, 0x20, 0x79, 0x59, 0x91, 0x87, + 0xa0, 0x71, + ], + [ + 0x53, 0x20, 0xad, 0x99, 0xa6, 0x19, 0xa9, 0x08, 0x04, 0xcd, 0x2e, 0xfe, 0x3a, 0x5c, 0xf0, + 0xac, 0x1a, 0xc5, 0xc4, 0x1a, 0xd9, 0xff, 0x2c, 0x61, 0xcf, 0x69, 0x9e, 0xfd, 0xad, 0x77, + 0x10, 0x96, + ], + [ + 0xcc, 0x67, 0x82, 0xfd, 0x46, 0xdd, 0x71, 0xc5, 0xf5, 0x12, 0x30, 0x1a, 0xb0, 0x49, 0x78, + 0x24, 0x50, 0xb4, 0xea, 0xf7, 0x9f, 0xda, 0xc5, 0x44, 0x3d, 0x93, 0xd2, 0x74, 0xd3, 0x91, + 0x67, 0x86, + ], + [ + 0xb3, 0xd6, 0xe8, 0x63, 0x17, 0xc3, 0x88, 0x44, 0x91, 0x5b, 0x05, 0x3a, 0x0c, 0x35, 0xff, + 0x2f, 0xc1, 0x03, 0xb6, 0x84, 0xe9, 0x6c, 0xef, 0x29, 0x18, 0xab, 0x06, 0x84, 0x4e, 0xb5, + 0x1a, 0xaf, + ], + [ + 0x4c, 0x0d, 0x34, 0x71, 0xea, 0xd8, 0xee, 0x99, 0xfb, 0xd8, 0x24, 0x9e, 0x33, 0xf6, 0x83, + 0xe0, 0x7c, 0x6c, 0xd6, 0x07, 0x1f, 0xe1, 0x02, 0xdd, 0x09, 0x61, 0x7b, 0x2c, 0x35, 0x3d, + 0xe4, 0x30, + ], + [ + 0x31, 0x62, 0xb0, 0x98, 0x8d, 0x42, 0x10, 0xbf, 0xf4, 0x84, 0x41, 0x3e, 0xd4, 0x51, 0xd1, + 0x70, 0xa0, 0x38, 0x87, 0x27, 0x21, 0x77, 0xef, 0xc0, 0xb7, 0xd0, 0x00, 0xf1, 0x0a, 0xbe, + 0x9e, 0xdf, + ], + [ + 0xac, 0x50, 0x7b, 0x9f, 0x8b, 0xf8, 0x6a, 0xd8, 0xbb, 0x77, 0x0f, 0x71, 0xcd, 0x2b, 0x19, + 0x92, 0x90, 0x2a, 0xe0, 0x31, 0x4d, 0x93, 0xfc, 0x0f, 0x2b, 0xb0, 0x11, 0xd7, 0x0e, 0x79, + 0x62, 0x26, + ], + [ + 0xfa, 0xe8, 0x13, 0x0c, 0x06, 0x19, 0xf8, 0x4b, 0x4b, 0x44, 0xf0, 0x1b, 0x84, 0x80, 0x6f, + 0x04, 0xe8, 0x2e, 0x53, 0x6d, 0x70, 0xe0, 0x5f, 0x23, 0x56, 0x97, 0x7f, 0xa3, 0x18, 0xae, + 0xcc, 0x1a, + ], + [ + 0x65, 0xe3, 0xd4, 0x8f, 0xa8, 0x60, 0xa7, 0x61, 0xb4, 0x61, 0xce, 0x12, 0x74, 0xf0, 0xd5, + 0x62, 0xf3, 0xdb, 0x9a, 0x6a, 0x57, 0xcf, 0x04, 0xd8, 0xc9, 0x0d, 0x68, 0xf5, 0x67, 0x0b, + 0x6a, 0xea, + ], + [ + 0x8b, 0x43, 0x72, 0x62, 0x43, 0xee, 0xaf, 0x83, 0x25, 0x40, 0x45, 0x68, 0xab, 0xec, 0xe3, + 0x26, 0x4b, 0x54, 0x6c, 0xf9, 0xd8, 0x86, 0x71, 0xf0, 0x9c, 0x24, 0xc8, 0x70, 0x45, 0xfc, + 0xcb, 0x4f, + ], + [ + 0x3e, 0xfd, 0xd7, 0xa8, 0x84, 0xff, 0x9e, 0x18, 0xc9, 0xe5, 0x71, 0x1c, 0x18, 0x5a, 0xa6, + 0xc5, 0xe4, 0x13, 0xb6, 0x8f, 0x23, 0x19, 0x79, 0x97, 0xda, 0x5b, 0x16, 0x65, 0xca, 0x97, + 0x8f, 0x99, + ], + [ + 0x26, 0xa6, 0x2d, 0x79, 0x19, 0x2c, 0x78, 0xc3, 0x89, 0x1f, 0x38, 0x18, 0x93, 0x68, 0x67, + 0x31, 0x10, 0xb8, 0x87, 0x34, 0xc0, 0x9e, 0xd7, 0x45, 0x35, 0x15, 0xde, 0xf7, 0x52, 0x5e, + 0x07, 0xd8, + ], + [ + 0x37, 0xf6, 0xa7, 0xf9, 0x6b, 0x94, 0x5f, 0x2f, 0x9a, 0x91, 0x27, 0xcc, 0xb4, 0xa8, 0x55, + 0x2f, 0xcb, 0x69, 0x38, 0xe5, 0x3f, 0xe8, 0xf0, 0x46, 0xdb, 0x8d, 0xa2, 0x38, 0x39, 0x80, + 0x93, 0xe9, + ], + [ + 0x04, 0xe4, 0xa0, 0xbb, 0x09, 0x32, 0x61, 0xee, 0x16, 0x38, 0x6d, 0xad, 0xce, 0xf9, 0xe2, + 0xa8, 0x39, 0x13, 0xf4, 0xe1, 0x89, 0x94, 0x64, 0x89, 0x14, 0x21, 0xd2, 0x0c, 0x1b, 0xbf, + 0xf7, 0x4d, + ], + [ + 0x56, 0x25, 0xf7, 0xc9, 0x30, 0xb8, 0xb4, 0x0d, 0xe8, 0x7d, 0xc8, 0xe6, 0x91, 0x45, 0xd8, + 0x3f, 0xd1, 0xd8, 0x1c, 0x61, 0xb6, 0xc3, 0x1f, 0xb7, 0xcf, 0xe6, 0x9f, 0xac, 0x65, 0xb2, + 0x86, 0x42, + ], + [ + 0xd3, 0x1d, 0xdb, 0x47, 0xb5, 0xe8, 0x66, 0x47, 0x17, 0xd3, 0x71, 0x8a, 0xcb, 0xd1, 0x32, + 0x39, 0x6f, 0xf4, 0x96, 0xfe, 0x33, 0x71, 0x59, 0xc9, 0x94, 0x10, 0xbe, 0x86, 0x58, 0x40, + 0x8a, 0x27, + ], + [ + 0x6c, 0xb0, 0xdb, 0x1d, 0x73, 0x54, 0xdf, 0xb4, 0xa1, 0x46, 0x43, 0x18, 0x00, 0x6d, 0xf0, + 0x64, 0x3c, 0xaf, 0xe2, 0x00, 0x2a, 0x86, 0xa2, 0x9f, 0xf8, 0x56, 0x0f, 0x90, 0x0f, 0xef, + 0x28, 0xa1, + ], + [ + 0x53, 0xc8, 0xda, 0x29, 0xbf, 0xa2, 0x75, 0x27, 0x1d, 0xf3, 0xf2, 0x70, 0x29, 0x6d, 0x5a, + 0x7d, 0x61, 0xb5, 0x7f, 0x88, 0x48, 0xc8, 0x9b, 0x3f, 0x65, 0xf4, 0x9e, 0x21, 0x34, 0x0b, + 0x75, 0x92, + ], + [ + 0xea, 0x64, 0x26, 0xb4, 0xb8, 0xd7, 0x0c, 0xaa, 0x8e, 0xce, 0x9a, 0x88, 0xfb, 0x0a, 0x9d, + 0x4a, 0x6b, 0x81, 0x7b, 0xb4, 0xa4, 0x3a, 0xc6, 0xfb, 0xef, 0x64, 0xcb, 0x0e, 0x58, 0x91, + 0x29, 0xee, + ], + [ + 0x61, 0xc8, 0x31, 0xbe, 0xab, 0x28, 0xd6, 0x7d, 0x1b, 0xb4, 0x0b, 0x5a, 0xe1, 0xa1, 0x1e, + 0x27, 0x57, 0xfa, 0x84, 0x2f, 0x03, 0x1a, 0x2d, 0x0b, 0xc9, 0x4a, 0x78, 0x67, 0xbc, 0x5d, + 0x26, 0xc2, + ], + [ + 0x04, 0x46, 0xc5, 0x98, 0xf3, 0x35, 0x5e, 0xd7, 0xd8, 0xa3, 0xb7, 0xe0, 0xb9, 0x9f, 0x92, + 0x99, 0xd1, 0x5e, 0x95, 0x6a, 0x97, 0xfa, 0xae, 0x08, 0x1a, 0x0b, 0x49, 0xd1, 0x70, 0x24, + 0xab, 0xd2, + ], + [ + 0xe7, 0xdf, 0xac, 0x38, 0x0f, 0x4a, 0x6e, 0xd3, 0xa0, 0x3e, 0x62, 0xf8, 0x13, 0x16, 0x1e, + 0xff, 0x82, 0x87, 0x66, 0xfa, 0x01, 0x43, 0x93, 0x55, 0x8e, 0x07, 0x5e, 0x9c, 0xeb, 0x77, + 0xd5, 0x49, + ], + [ + 0x05, 0x04, 0xe0, 0xa1, 0x32, 0xd2, 0xef, 0x5c, 0xa5, 0xf2, 0xfe, 0x74, 0xfc, 0x64, 0x43, + 0x72, 0x05, 0xbc, 0x10, 0xf3, 0x2d, 0x5f, 0x13, 0xd5, 0x33, 0xbf, 0x55, 0x29, 0x16, 0xa9, + 0x4d, 0x3f, + ], + [ + 0xdb, 0x44, 0x4d, 0xa6, 0x8c, 0x84, 0xf0, 0xa9, 0xce, 0x08, 0x60, 0x91, 0x00, 0xb6, 0x9b, + 0x8f, 0x3d, 0x56, 0x72, 0x68, 0x7e, 0x0c, 0xa1, 0x3f, 0xa3, 0xc0, 0xac, 0x9e, 0xb2, 0xbd, + 0xe5, 0xd2, + ], + [ + 0xdd, 0x0d, 0xc6, 0x20, 0xe7, 0x58, 0x46, 0x74, 0xcb, 0x3d, 0xba, 0x49, 0x0d, 0x2e, 0xba, + 0x9e, 0x68, 0xec, 0xa0, 0xbe, 0xf2, 0x28, 0xee, 0x56, 0x9a, 0x4a, 0x64, 0xf6, 0x55, 0x90, + 0x56, 0xe9, + ], + [ + 0x68, 0x14, 0x83, 0xe2, 0x25, 0x1c, 0xd5, 0xe2, 0x88, 0x55, 0x07, 0xbb, 0x09, 0xf7, 0x6b, + 0xed, 0x3b, 0x99, 0xd3, 0xc3, 0x77, 0xdd, 0x48, 0x39, 0x61, 0x77, 0x64, 0x7b, 0xfb, 0x4a, + 0xaf, 0xda, + ], + [ + 0xc2, 0x9b, 0x39, 0x91, 0x7e, 0x4e, 0x60, 0xf0, 0xfe, 0xe5, 0xb6, 0x87, 0x1b, 0x30, 0xa3, + 0x8e, 0x50, 0x53, 0x1d, 0x76, 0xd1, 0xb0, 0x83, 0x78, 0x11, 0xbd, 0x63, 0x51, 0xb3, 0x48, + 0x54, 0xec, + ], + [ + 0x83, 0xd7, 0x6a, 0xfc, 0x38, 0x87, 0xc0, 0xb7, 0xed, 0xd1, 0x4a, 0x1a, 0xff, 0xa7, 0x55, + 0x4b, 0xed, 0x33, 0x45, 0xba, 0x68, 0xdd, 0xcd, 0x2a, 0x33, 0x26, 0xc7, 0xea, 0xe9, 0x7b, + 0x80, 0xd8, + ], + [ + 0x2f, 0x55, 0x53, 0x80, 0x32, 0x73, 0xe8, 0xbb, 0x29, 0xd9, 0x13, 0xcc, 0x31, 0xba, 0xb9, + 0x53, 0x05, 0x1c, 0x59, 0xf3, 0xba, 0x57, 0xa7, 0x1c, 0xf5, 0x59, 0x15, 0x63, 0xca, 0x72, + 0x14, 0x05, + ], + [ + 0xfc, 0x6a, 0x67, 0x23, 0x27, 0x47, 0x4e, 0x13, 0x87, 0xfc, 0xbc, 0xe1, 0x81, 0x4a, 0x1d, + 0xe3, 0x76, 0xd8, 0x56, 0x1f, 0xc1, 0x38, 0x56, 0x14, 0x41, 0xac, 0x6e, 0x39, 0x60, 0x89, + 0xe0, 0x62, + ], + [ + 0x81, 0x63, 0x06, 0x54, 0xdf, 0xb0, 0xfd, 0x28, 0x2a, 0x37, 0x11, 0x79, 0x95, 0x64, 0x6c, + 0xdd, 0xe2, 0xcf, 0x8e, 0xef, 0xe9, 0xf3, 0xf9, 0x6f, 0xdb, 0x12, 0xcf, 0xda, 0x88, 0xdf, + 0x66, 0x68, + ], + [ + 0xdd, 0xf7, 0x8c, 0xfa, 0x37, 0x8b, 0x5e, 0x06, 0x8a, 0x24, 0x8e, 0xda, 0xf3, 0xab, 0xef, + 0x23, 0xea, 0x9e, 0x62, 0xc6, 0x6f, 0x86, 0xf1, 0x8c, 0xc5, 0xe6, 0x95, 0xcd, 0x36, 0xc9, + 0x80, 0x9b, + ], + [ + 0xe9, 0x94, 0x4e, 0xbe, 0xf6, 0xe5, 0xa2, 0x40, 0x35, 0xa3, 0x1a, 0x72, 0x7e, 0x8f, 0xf6, + 0xda, 0x7c, 0x37, 0x2d, 0x99, 0x94, 0x9c, 0x12, 0x24, 0x48, 0x3b, 0x85, 0x7f, 0x64, 0x01, + 0xe3, 0x46, + ], + [ + 0x61, 0x20, 0xb1, 0x23, 0x38, 0x2f, 0x98, 0xf7, 0xef, 0xe6, 0x6a, 0xbe, 0x6a, 0x3a, 0x34, + 0x45, 0x78, 0x8a, 0x87, 0xe4, 0x8d, 0x4e, 0x69, 0x91, 0xf3, 0x7b, 0xaa, 0xdc, 0xac, 0x0b, + 0xef, 0x95, + ], + [ + 0x16, 0x8c, 0x81, 0x66, 0x29, 0x2b, 0x85, 0x07, 0x04, 0x09, 0x83, 0x06, 0x17, 0xe8, 0x4b, + 0xdd, 0x7e, 0x35, 0x18, 0xb3, 0x8e, 0x5a, 0xc4, 0x30, 0xdc, 0x35, 0xed, 0x7d, 0x16, 0xb0, + 0x7a, 0x86, + ], + [ + 0xd8, 0x4f, 0x57, 0xf3, 0xff, 0xa7, 0x6c, 0xc1, 0x89, 0x82, 0xda, 0x43, 0x53, 0xcc, 0x59, + 0x91, 0x15, 0x8e, 0xc5, 0xae, 0x4f, 0x6a, 0x91, 0x09, 0xd1, 0xd7, 0xa0, 0xae, 0x2c, 0xba, + 0x77, 0xed, + ], + [ + 0x3e, 0x72, 0x57, 0xb7, 0x27, 0x2b, 0xb4, 0x6d, 0x49, 0xcd, 0x60, 0x19, 0xb0, 0x4d, 0xde, + 0xe2, 0x0d, 0xa7, 0xc0, 0xcb, 0x13, 0xf7, 0xc1, 0xec, 0x33, 0x91, 0x29, 0x1b, 0x2c, 0xce, + 0xba, 0xbc, + ], + [ + 0x37, 0x1f, 0x36, 0x87, 0x0d, 0x18, 0xf3, 0x2a, 0x11, 0xfe, 0xa0, 0xf1, 0x44, 0xb0, 0x21, + 0xc8, 0xb4, 0x07, 0xbb, 0x50, 0xf8, 0xe0, 0x26, 0x7c, 0x71, 0x11, 0x23, 0xf4, 0x54, 0xb9, + 0x63, 0xc0, + ], + [ + 0x93, 0x46, 0xac, 0x6d, 0xd7, 0xde, 0x6b, 0x96, 0x97, 0x5f, 0xec, 0x38, 0x0d, 0x4d, 0x99, + 0x4c, 0x4c, 0x12, 0xe6, 0xa8, 0x89, 0x75, 0x44, 0xf2, 0x29, 0x15, 0x31, 0x6c, 0xc6, 0xcc, + 0xa2, 0x80, + ], + [ + 0x54, 0x07, 0x5d, 0xf8, 0x0e, 0xc1, 0xae, 0x6a, 0xc9, 0x10, 0x0e, 0x1f, 0xd0, 0xeb, 0xf3, + 0x24, 0x6c, 0x17, 0xf5, 0xc9, 0x33, 0x13, 0x7a, 0xf3, 0x92, 0x01, 0x1f, 0x4c, 0x5f, 0x61, + 0x51, 0x3a, + ], + [ + 0xe0, 0x8e, 0xc2, 0xaf, 0x2c, 0xfc, 0x25, 0x12, 0x25, 0xe1, 0x96, 0x8f, 0xd6, 0xca, 0x21, + 0xe4, 0x04, 0x4f, 0x12, 0x9b, 0xff, 0xa9, 0x5b, 0xac, 0x35, 0x03, 0xbe, 0x8b, 0xdb, 0x30, + 0xa3, 0x67, + ], +]; diff --git a/src/storage/mod.rs b/src/storage/mod.rs new file mode 100644 index 0000000..ebc7fce --- /dev/null +++ b/src/storage/mod.rs @@ -0,0 +1,713 @@ +//! # Warning +//! This code is in an experimental state and under active development. +//! Code structure are subject to change. +use std::{cell::RefCell, collections::{BTreeMap, BTreeSet, HashMap}, rc::Rc}; +use crate::{ + evm::{ + calldata::CallDataLabel, element::Element, op, vm::{StepResult, Vm}, U256, VAL_1, VAL_1_B, VAL_32_B + }, utils::{and_mask_to_type, execute_until_function_start}, Selector, Slot, +}; +use alloy_dyn_abi::DynSolType; + +mod calldata; +use calldata::CallDataImpl; + +mod keccak_precalc; +use keccak_precalc::KEC_PRECALC; + + +/// Represents a storage variable record in a smart contract's storage layout. +#[derive(Debug)] +pub struct StorageRecord { + /// Storage slot location for the variable + pub slot: Slot, + + /// Byte offset within the storage slot (0-31) + pub offset: u8, + + /// Variable type + pub r#type: String, + + /// Function selectors that read from this storage location + pub reads: Vec, + + /// Function selectors that write to this storage location + pub writes: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +enum Label { + Constant, + + Typed(DynSolType), + Sloaded(Rc>), + IsZero(Rc>), + Keccak(u32, Vec>), +} + +impl CallDataLabel for Label { + fn label(_: usize, tp: &alloy_dyn_abi::DynSolType) -> Label { + Label::Typed(tp.clone()) + } +} + + +fn get_base_internal_type(val: &DynSolType) -> DynSolType { + if let DynSolType::Array(t) = val { + get_base_internal_type(t) + } else { + val.clone() + } +} + +fn get_base_score(t: &DynSolType) -> usize { + match t { + DynSolType::Uint(256) => 1, + DynSolType::Uint(8) => 3, + DynSolType::Bool => 4, + DynSolType::FixedBytes(32) => 6, + DynSolType::FixedBytes(_) => 2, + DynSolType::String | DynSolType::Bytes => 500, + DynSolType::Array(v) => 5 * get_base_score(v), + _ => 5, + } +} + + +#[derive(Clone, PartialEq, Eq)] +enum StorageType { + Base(DynSolType), + Map(DynSolType, Box), +} + +impl StorageType { + fn set_type(&mut self, tp: DynSolType) { + if let StorageType::Base(DynSolType::String) = self { + return + } + match self { + StorageType::Base(DynSolType::Array(ref mut v)) => { + let mut current = v.as_mut(); + while let DynSolType::Array(inner) = current { + current = inner; + if let DynSolType::Uint(256) = ¤t { + } + } + *current = tp; + } + StorageType::Base(ref mut v) => *v = tp, + StorageType::Map(_, ref mut v) => v.set_type(tp), + } + } + + + fn get_internal_type(&self) -> DynSolType { + match self { + StorageType::Base(t) => get_base_internal_type(t), + StorageType::Map(_, v) => v.get_internal_type(), + } + } + + + fn get_score(&self) -> usize { + match self { + StorageType::Base(t) => get_base_score(t), + StorageType::Map(k, v) => 1000 * get_base_score(k) + v.get_score(), + } + } +} + +impl std::fmt::Debug for StorageType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + StorageType::Base(v) => write!(f, "{}", v.sol_type_name()), + StorageType::Map(k, v) => write!(f, "mapping({} => {:?})", k.sol_type_name(), v), + } + } +} + + +#[derive(Clone, PartialEq, Eq)] +struct StorageElement { + slot: Slot, + stype: StorageType, + rshift: u8, // in bytes + is_write: bool, + last_and: Option, + last_or2: Option>, +} +impl std::fmt::Debug for StorageElement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{:?}:{}:{:?}", alloy_primitives::hex::encode(self.slot), self.stype, self.rshift, self.last_and) + } +} + +type SlotHashMap = HashMap>>>; + +struct Storage { + loaded: SlotHashMap, +} +impl Storage { + fn new() -> Self { + Self{ + loaded: HashMap::new(), + } + } + + fn remove(&mut self, val: &Rc>) { + self.loaded.get_mut(&val.borrow().slot).unwrap().retain(|x| x != val); + } + + fn sstore(&mut self, slot: Element