From 3674e10ee939d57b4e07ac0aa8b8390e3d5d4327 Mon Sep 17 00:00:00 2001 From: findolor Date: Wed, 25 Sep 2024 11:30:39 +0300 Subject: [PATCH 01/15] feat: add deployment debug function to fuzzer --- crates/common/src/fuzz/mod.rs | 330 ++++++++++++++++++++- tauri-app/src-tauri/src/commands/charts.rs | 15 +- tauri-app/src-tauri/src/main.rs | 3 +- 3 files changed, 341 insertions(+), 7 deletions(-) diff --git a/crates/common/src/fuzz/mod.rs b/crates/common/src/fuzz/mod.rs index 771cdacec..08d0970a6 100644 --- a/crates/common/src/fuzz/mod.rs +++ b/crates/common/src/fuzz/mod.rs @@ -14,12 +14,12 @@ pub use rain_interpreter_eval::trace::{ use rain_interpreter_eval::{ error::ForkCallError, eval::ForkEvalArgs, fork::Forker, trace::RainEvalResult, }; -use rain_orderbook_app_settings::blocks::BlockError; -use rain_orderbook_app_settings::chart::Chart; -use rain_orderbook_app_settings::config::*; -use rain_orderbook_app_settings::scenario::Scenario; +use rain_orderbook_app_settings::{ + blocks::BlockError, chart::Chart, config::*, order::OrderIO, scenario::Scenario, +}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use std::str::FromStr; use std::sync::Arc; use thiserror::Error; use typeshare::typeshare; @@ -31,6 +31,21 @@ pub struct ChartData { charts: HashMap, } +#[typeshare] +#[derive(Debug, Serialize, Deserialize)] +pub struct DeploymentDebugData { + pub result: HashMap>, + #[typeshare(typescript(type = "string"))] + pub block_number: U256, +} +#[typeshare] +#[derive(Debug, Serialize, Deserialize)] +pub struct DeploymentDebugPairData { + pub order_name: String, + pub pair: String, + pub fuzz_result: FuzzResultFlat, +} + #[derive(Debug)] pub struct FuzzResult { pub scenario: String, @@ -67,6 +82,10 @@ pub struct FuzzRunner { pub enum FuzzRunnerError { #[error("Scenario not found")] ScenarioNotFound(String), + #[error("Deployment not found")] + DeploymentNotFound(String), + #[error("Order not found")] + OrderNotFound, #[error("Scenario has no runs defined")] ScenarioNoRuns, #[error("Corrupt traces")] @@ -245,6 +264,138 @@ impl FuzzRunner { }) } + pub async fn run_debug( + &mut self, + block_number: u64, + input: OrderIO, + output: OrderIO, + scenario: &Arc, + ) -> Result { + let deployer = scenario.deployer.clone(); + + // Create a fork with the first block number + self.forker + .add_or_select( + NewForkedEvm { + fork_url: deployer.network.rpc.clone().into(), + fork_block_number: Some(block_number), + }, + None, + ) + .await?; + + // Pull out the bindings from the scenario + let scenario_bindings: Vec = scenario + .bindings + .clone() + .into_iter() + .map(|(k, v)| Rebind(k, v)) + .collect(); + + // Create a new RainDocument with the dotrain and the bindings + // The bindings in the dotrain string are ignored by the RainDocument + let rain_document = RainDocument::create( + self.dotrain.clone(), + None, + None, + Some(scenario_bindings.clone()), + ); + + // Search the namespace hash map for NamespaceItems that are elided and make a vec of the keys + let elided_binding_keys = Arc::new( + rain_document + .namespace() + .iter() + .filter(|(_, v)| v.is_elided_binding()) + .map(|(k, _)| k.clone()) + .collect::>(), + ); + + let dotrain = Arc::new(self.dotrain.clone()); + let mut handles = vec![]; + + self.forker.roll_fork(Some(block_number), None)?; + let fork = Arc::new(self.forker.clone()); // Wrap in Arc for shared ownership + let fork_clone = Arc::clone(&fork); // Clone the Arc for each thread + let elided_binding_keys = Arc::clone(&elided_binding_keys); + let deployer = Arc::clone(&deployer); + let scenario_bindings = scenario_bindings.clone(); + let dotrain = Arc::clone(&dotrain); + + let mut final_bindings: Vec = vec![]; + + // For each scenario.fuzz_binds, add a random value + for elided_binding in elided_binding_keys.as_slice() { + let mut val: [u8; 32] = [0; 32]; + self.rng.fill_bytes(&mut val); + let hex = alloy::primitives::hex::encode_prefixed(val); + final_bindings.push(Rebind(elided_binding.to_string(), hex)); + } + + let handle = tokio::spawn(async move { + final_bindings.extend(scenario_bindings.clone()); + + let rainlang_string = RainDocument::compose_text( + &dotrain, + &ORDERBOOK_ORDER_ENTRYPOINTS, + None, + Some(final_bindings), + )?; + + // Create a 5x5 grid of zero values for context - later we'll + // replace these with sane values based on Orderbook context + let mut context = vec![vec![U256::from(0); 5]; 5]; + // set random hash for context order hash cell + context[1][0] = rand::random(); + + // set input values in context + // input token + context[3][0] = U256::from_be_slice(input.token.address.0.as_slice()); + // input decimals + context[3][1] = U256::from(input.token.decimals.unwrap_or(18)); + // input vault id + context[3][2] = input.vault_id.unwrap_or(U256::from(0)); + // input vault balance before + context[3][3] = U256::from(0); + + // set output values in context + // output token + context[4][0] = U256::from_be_slice(output.token.address.0.as_slice()); + // output decimals + context[4][1] = U256::from(output.token.decimals.unwrap_or(18)); + // output vault id + context[4][2] = output.vault_id.unwrap_or(U256::from(0)); + // output vault balance before + context[4][3] = U256::from(0); + + let args = ForkEvalArgs { + rainlang_string, + source_index: 0, + deployer: deployer.address, + namespace: FullyQualifiedNamespace::default(), + context, + decode_errors: true, + }; + fork_clone + .fork_eval(args) + .map_err(FuzzRunnerError::ForkCallError) + .await + }); + handles.push(handle); + + let mut runs: Vec = Vec::new(); + + for handle in handles { + let res = handle.await??; + runs.push(res.into()); + } + + Ok(FuzzResult { + scenario: scenario.name.clone(), + runs: runs.into(), + }) + } + pub async fn make_chart_data(&self) -> Result { let charts = self.settings.charts.clone(); let mut scenarios_data: HashMap = HashMap::new(); @@ -270,13 +421,79 @@ impl FuzzRunner { charts, }) } + + pub async fn make_debug_data( + &self, + block_number: Option, + ) -> Result { + let mut block = block_number.unwrap_or(0); + let mut pair_datas: HashMap> = HashMap::new(); + + let deployments = self.settings.deployments.clone(); + + for (deployment_name, deployment) in deployments.clone() { + let scenario = deployment.scenario.clone(); + + if block_number.is_none() { + // Fetch the latest block number + block = + ReadableClientHttp::new_from_url(scenario.deployer.network.rpc.to_string())? + .get_block_number() + .await?; + } + + let order_name = self + .settings + .orders + .iter() + .find(|(_, order)| *order == &deployment.order) + .map(|(key, _)| key.clone()) + .ok_or(FuzzRunnerError::OrderNotFound)?; + + for input in &deployment.order.inputs { + for output in &deployment.order.outputs { + if input.token.address != output.token.address { + let pair = format!( + "{}/{}", + input.token.symbol.clone().unwrap_or("UNKNOWN".to_string()), + output.token.symbol.clone().unwrap_or("UNKNOWN".to_string()) + ); + + let mut runner = self.clone(); + let fuzz_result = runner + .run_debug(block, input.clone(), output.clone(), &scenario) + .await? + .flatten_traces()?; + + let pair_data = DeploymentDebugPairData { + order_name: order_name.clone(), + pair, + fuzz_result, + }; + + pair_datas + .entry(deployment_name.clone()) + .or_insert_with(Vec::new) + .push(pair_data); + } + } + } + } + + let result = DeploymentDebugData { + result: pair_datas, + block_number: U256::from(block), + }; + + Ok(result) + } } #[cfg(test)] mod tests { use super::*; use alloy::{ - primitives::utils::parse_ether, + primitives::{utils::parse_ether, Address}, providers::{ext::AnvilApi, Provider}, }; use rain_orderbook_app_settings::config_source::ConfigSource; @@ -613,4 +830,107 @@ _: context<1 0>(); } } } + + #[tokio::test(flavor = "multi_thread", worker_threads = 10)] + async fn test_debug() { + let local_evm = LocalEvm::new().await; + let dotrain = format!( + r#" +deployers: + flare: + address: {deployer} +networks: + flare: + rpc: {rpc_url} + chain-id: 123 +tokens: + wflr: + network: flare + address: 0x1D80c49BbBCd1C0911346656B529DF9E5c2F783d + decimals: 18 + usdce: + network: flare + address: 0xFbDa5F676cB37624f28265A144A48B0d6e87d3b6 + decimals: 6 +scenarios: + flare: + deployer: flare + runs: 1 + bindings: + orderbook-subparser: {orderbook_subparser} +orders: + sell-wflr: + network: flare + inputs: + - token: usdce + vault-id: 10 + outputs: + - token: wflr + vault-id: 20 +deployments: + sell-wflr: + order: sell-wflr + scenario: flare +--- +#orderbook-subparser ! + +#calculate-io +using-words-from orderbook-subparser + +_: input-token(), +_: input-token-decimals(), +_: input-vault-id(), +_: output-token(), +_: output-token-decimals(), +_: output-vault-id(), + +max-output: 30, +io-ratio: mul(0.99 20); +#handle-io +:; + "#, + rpc_url = local_evm.url(), + deployer = local_evm.deployer.address(), + orderbook_subparser = local_evm.orderbook_subparser.address() + ); + + let frontmatter = RainDocument::get_front_matter(&dotrain).unwrap(); + let settings = serde_yaml::from_str::(frontmatter).unwrap(); + let config = settings + .try_into() + .map_err(|e| println!("{:?}", e)) + .unwrap(); + + let runner = FuzzRunner::new(&dotrain, config, None).await; + + let res = runner + .make_debug_data(None) + .await + .map_err(|e| println!("{:#?}", e)) + .unwrap(); + + let result_rows = res.result["sell-wflr"][0].fuzz_result.data.rows[0].clone(); + assert_eq!( + result_rows[0], + U256::from_be_slice( + Address::from_str("0xFbDa5F676cB37624f28265A144A48B0d6e87d3b6") + .unwrap() + .0 + .as_slice() + ) + ); + assert_eq!(result_rows[1], U256::from(6)); + assert_eq!(result_rows[2], U256::from(10)); + assert_eq!( + result_rows[3], + U256::from_be_slice( + Address::from_str("0x1D80c49BbBCd1C0911346656B529DF9E5c2F783d") + .unwrap() + .0 + .as_slice() + ) + ); + assert_eq!(result_rows[4], U256::from(18)); + assert_eq!(result_rows[5], U256::from(20)); + } } diff --git a/tauri-app/src-tauri/src/commands/charts.rs b/tauri-app/src-tauri/src/commands/charts.rs index 1298517c7..cd67b0271 100644 --- a/tauri-app/src-tauri/src/commands/charts.rs +++ b/tauri-app/src-tauri/src/commands/charts.rs @@ -10,4 +10,17 @@ pub async fn make_charts(dotrain: String, settings: String) -> CommandResult, +) -> CommandResult { + let config = merge_configstrings(dotrain.clone(), settings).await?; + let final_config: Config = config.try_into()?; + let fuzzer = FuzzRunner::new(dotrain.as_str(), final_config.clone(), None).await; + + Ok(fuzzer.make_debug_data(block_number).await?) +} diff --git a/tauri-app/src-tauri/src/main.rs b/tauri-app/src-tauri/src/main.rs index f579e3984..b12b45114 100644 --- a/tauri-app/src-tauri/src/main.rs +++ b/tauri-app/src-tauri/src/main.rs @@ -9,7 +9,7 @@ mod commands; use commands::app::get_app_commit_sha; use commands::authoring_meta::get_authoring_meta_v2_for_scenarios; use commands::chain::{get_block_number, get_chainid}; -use commands::charts::make_charts; +use commands::charts::{make_charts, make_deployment_debug}; use commands::config::{convert_configstring_to_config, merge_configstrings, parse_configstring}; use commands::dotrain::parse_dotrain; use commands::dotrain_add_order_lsp::{call_lsp_completion, call_lsp_hover, call_lsp_problems}; @@ -67,6 +67,7 @@ fn run_tauri_app() { merge_configstrings, convert_configstring_to_config, make_charts, + make_deployment_debug, order_add_calldata, order_remove_calldata, vault_deposit_approve_calldata, From d512cd72c53be92cc1cb5f7b6948d8fe5ddf40a6 Mon Sep 17 00:00:00 2001 From: findolor Date: Wed, 25 Sep 2024 11:31:02 +0300 Subject: [PATCH 02/15] feat: add another modal for deployment debug --- .../components/modal/ModalScenarioDebug.svelte | 17 +++++++++++++++++ tauri-app/src/lib/services/modal.ts | 6 ++++++ 2 files changed, 23 insertions(+) create mode 100644 tauri-app/src/lib/components/modal/ModalScenarioDebug.svelte diff --git a/tauri-app/src/lib/components/modal/ModalScenarioDebug.svelte b/tauri-app/src/lib/components/modal/ModalScenarioDebug.svelte new file mode 100644 index 000000000..997e7d294 --- /dev/null +++ b/tauri-app/src/lib/components/modal/ModalScenarioDebug.svelte @@ -0,0 +1,17 @@ + + + +
+
+
+ +
+
diff --git a/tauri-app/src/lib/services/modal.ts b/tauri-app/src/lib/services/modal.ts index f397b1513..7a89f8e59 100644 --- a/tauri-app/src/lib/services/modal.ts +++ b/tauri-app/src/lib/services/modal.ts @@ -8,6 +8,8 @@ import type { Order as OrderListOrder } from '$lib/typeshare/subgraphTypes'; import ModalTradeDebug from '$lib/components/modal/ModalTradeDebug.svelte'; import type { Hex } from 'viem'; import ModalQuoteDebug from '$lib/components/modal/ModalQuoteDebug.svelte'; +import ModalScenarioDebug from '$lib/components/modal/ModalScenarioDebug.svelte'; +import type { RainEvalResultsTable } from '$lib/typeshare/config'; export const handleDepositGenericModal = () => { new ModalVaultDepositGeneric({ target: document.body, props: { open: true } }); @@ -55,3 +57,7 @@ export const handleQuoteDebugModal = ( }, }); }; + +export const handleScenarioDebugModal = (pair: string, data: RainEvalResultsTable) => { + new ModalScenarioDebug({ target: document.body, props: { open: true, pair, data } }); +}; From 4453759b8a0d00ee8efac3b20160071469f60509 Mon Sep 17 00:00:00 2001 From: findolor Date: Wed, 25 Sep 2024 11:31:41 +0300 Subject: [PATCH 03/15] feat: update scenario debug component to display the new deployment debug --- .../src/lib/components/EditableSpan.svelte | 12 +- .../lib/components/ScenarioDebugTable.svelte | 141 ++++++++++++++---- tauri-app/src/lib/services/chart.ts | 9 +- tauri-app/src/routes/orders/add/+page.svelte | 4 +- 4 files changed, 132 insertions(+), 34 deletions(-) diff --git a/tauri-app/src/lib/components/EditableSpan.svelte b/tauri-app/src/lib/components/EditableSpan.svelte index 1d9ac1ce4..08f51f167 100644 --- a/tauri-app/src/lib/components/EditableSpan.svelte +++ b/tauri-app/src/lib/components/EditableSpan.svelte @@ -1,5 +1,6 @@ -{#if chartData} -
- {#each Object.values(chartData.scenarios_data) as scenario} - {@const data = transformData(scenario)} -
- {scenario.scenario} - - - Stack item - Value - Hex - - - {#each Object.entries(data[0]) as [key, value]} - - {key} - {value[0]} - {value[1]} - - {/each} - -
-
- {/each} +
+
+
+ {#if $data && isHex($data.block_number)} + { + enabled = false; + }} + on:blur={({ detail: { value } }) => { + blockNumber = parseInt(value); + fetchData(); + }} + /> + {/if} + + {#if data} + + { + enabled = false; + }} + /> + { + enabled = true; + blockNumber = undefined; + fetchData(); + }} + class={`ml-2 h-8 w-3 cursor-pointer text-gray-400 dark:text-gray-400 ${enabled ? 'hidden' : ''}`} + /> + {/if} +
-{:else} - No scenario data -{/if} + + {#each Object.entries($data?.result ?? {}) as [deploymentName, results]} +

Deployment: {deploymentName}

+ + + Order + Scenario + Pair + Maximum Output + Ratio + + + + + {#each results as item} + {@const data = transformData(item.fuzz_result)[0]} + {@const ioRatio = Object.entries(data)[Object.entries(data).length - 1]} + {@const maxOutput = Object.entries(data)[Object.entries(data).length - 2]} + + {item.order_name} + {item.fuzz_result.scenario} + {item.pair} + + {maxOutput[1][0]} + + + {ioRatio[1][0]} + + ({formatUnits(10n ** 36n / BigInt(ioRatio[1][1]), 18)}) + + + + + + + {/each} + +
+ {/each} +
diff --git a/tauri-app/src/lib/services/chart.ts b/tauri-app/src/lib/services/chart.ts index aeb59bb03..6231f9c4b 100644 --- a/tauri-app/src/lib/services/chart.ts +++ b/tauri-app/src/lib/services/chart.ts @@ -1,5 +1,12 @@ -import type { ChartData } from '$lib/typeshare/config'; +import type { ChartData, DeploymentDebugData } from '$lib/typeshare/config'; import { invoke } from '@tauri-apps/api'; export const makeChartData = async (dotrain: string, settings: string): Promise => invoke('make_charts', { dotrain, settings }); + +export const makeDeploymentDebugData = async ( + dotrain: string, + settings: string, + blockNumber?: number, +): Promise => + invoke('make_deployment_debug', { dotrain, settings, blockNumber }); diff --git a/tauri-app/src/routes/orders/add/+page.svelte b/tauri-app/src/routes/orders/add/+page.svelte index 3d3495690..0319e63dc 100644 --- a/tauri-app/src/routes/orders/add/+page.svelte +++ b/tauri-app/src/routes/orders/add/+page.svelte @@ -279,7 +279,9 @@ {/if} - + + + From bb952a0540eca502b69bb294f08c05ea69195437 Mon Sep 17 00:00:00 2001 From: findolor Date: Wed, 25 Sep 2024 11:42:27 +0300 Subject: [PATCH 04/15] fix: update fetch logic --- crates/common/src/fuzz/mod.rs | 2 +- .../lib/components/ScenarioDebugTable.svelte | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/common/src/fuzz/mod.rs b/crates/common/src/fuzz/mod.rs index 08d0970a6..ac5436eca 100644 --- a/crates/common/src/fuzz/mod.rs +++ b/crates/common/src/fuzz/mod.rs @@ -19,7 +19,6 @@ use rain_orderbook_app_settings::{ }; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::str::FromStr; use std::sync::Arc; use thiserror::Error; use typeshare::typeshare; @@ -498,6 +497,7 @@ mod tests { }; use rain_orderbook_app_settings::config_source::ConfigSource; use rain_orderbook_test_fixtures::LocalEvm; + use std::str::FromStr; #[tokio::test(flavor = "multi_thread", worker_threads = 10)] async fn test_fuzz_runner() { diff --git a/tauri-app/src/lib/components/ScenarioDebugTable.svelte b/tauri-app/src/lib/components/ScenarioDebugTable.svelte index 3e221c14d..142d5eff8 100644 --- a/tauri-app/src/lib/components/ScenarioDebugTable.svelte +++ b/tauri-app/src/lib/components/ScenarioDebugTable.svelte @@ -22,13 +22,9 @@ let loading = false; let blockNumber: number | undefined; - const fetchData = async () => { + const fetchData = async (dotrain: string, settings: string, blockNumber?: number) => { loading = true; - const res = await makeDeploymentDebugData( - $globalDotrainFile.text, - $settingsText, - enabled ? undefined : blockNumber, - ); + const res = await makeDeploymentDebugData(dotrain, settings, enabled ? undefined : blockNumber); blockNumber = parseInt(res.block_number); @@ -39,7 +35,11 @@ fetchData, 500, ); - $: debounceMakeDeploymentDebugData(); + $: debounceMakeDeploymentDebugData($globalDotrainFile.text, $settingsText, blockNumber); + + const handleRefresh = () => { + fetchData($globalDotrainFile.text, $settingsText, blockNumber); + };
@@ -53,7 +53,7 @@ }} on:blur={({ detail: { value } }) => { blockNumber = parseInt(value); - fetchData(); + handleRefresh(); }} /> {/if} @@ -62,7 +62,7 @@ { enabled = true; blockNumber = undefined; - fetchData(); + handleRefresh(); }} class={`ml-2 h-8 w-3 cursor-pointer text-gray-400 dark:text-gray-400 ${enabled ? 'hidden' : ''}`} /> From 7c39a2a54480ed8db0195694369f81451975b704 Mon Sep 17 00:00:00 2001 From: findolor Date: Wed, 25 Sep 2024 11:43:23 +0300 Subject: [PATCH 05/15] fix: adjust spacing --- tauri-app/src/lib/components/ScenarioDebugTable.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tauri-app/src/lib/components/ScenarioDebugTable.svelte b/tauri-app/src/lib/components/ScenarioDebugTable.svelte index 142d5eff8..20a0e4d21 100644 --- a/tauri-app/src/lib/components/ScenarioDebugTable.svelte +++ b/tauri-app/src/lib/components/ScenarioDebugTable.svelte @@ -42,7 +42,7 @@ }; -
+
{#if $data && isHex($data.block_number)} @@ -84,7 +84,7 @@
{#each Object.entries($data?.result ?? {}) as [deploymentName, results]} -

Deployment: {deploymentName}

+

Deployment: {deploymentName}

Order From 6d4f945e120b41b48000bde7c2ed07a589b72b45 Mon Sep 17 00:00:00 2001 From: findolor Date: Wed, 25 Sep 2024 11:51:13 +0300 Subject: [PATCH 06/15] refactor: fix spacing --- tauri-app/src/lib/components/ScenarioDebugTable.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tauri-app/src/lib/components/ScenarioDebugTable.svelte b/tauri-app/src/lib/components/ScenarioDebugTable.svelte index 20a0e4d21..f46d53d0f 100644 --- a/tauri-app/src/lib/components/ScenarioDebugTable.svelte +++ b/tauri-app/src/lib/components/ScenarioDebugTable.svelte @@ -84,7 +84,7 @@ {#each Object.entries($data?.result ?? {}) as [deploymentName, results]} -

Deployment: {deploymentName}

+

Deployment: {deploymentName}

Order From c85b9fecdb2585143bef4ce2d8924712c05f52c7 Mon Sep 17 00:00:00 2001 From: findolor Date: Wed, 25 Sep 2024 18:55:07 +0300 Subject: [PATCH 07/15] refactor: update for CI --- crates/common/src/fuzz/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/fuzz/mod.rs b/crates/common/src/fuzz/mod.rs index ac5436eca..8ddaffbf7 100644 --- a/crates/common/src/fuzz/mod.rs +++ b/crates/common/src/fuzz/mod.rs @@ -472,7 +472,7 @@ impl FuzzRunner { pair_datas .entry(deployment_name.clone()) - .or_insert_with(Vec::new) + .or_default() .push(pair_data); } } From 88144192b1991496dea870ae452a66b043d7816b Mon Sep 17 00:00:00 2001 From: findolor Date: Fri, 27 Sep 2024 14:29:08 +0300 Subject: [PATCH 08/15] fix: handle the missing stack data --- .../lib/components/ScenarioDebugTable.svelte | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/tauri-app/src/lib/components/ScenarioDebugTable.svelte b/tauri-app/src/lib/components/ScenarioDebugTable.svelte index f46d53d0f..83e2d26b2 100644 --- a/tauri-app/src/lib/components/ScenarioDebugTable.svelte +++ b/tauri-app/src/lib/components/ScenarioDebugTable.svelte @@ -98,21 +98,28 @@ {#each results as item} {@const data = transformData(item.fuzz_result)[0]} - {@const ioRatio = Object.entries(data)[Object.entries(data).length - 1]} - {@const maxOutput = Object.entries(data)[Object.entries(data).length - 2]} + {@const dataEntries = Object.entries(data)} {item.order_name} {item.fuzz_result.scenario} {item.pair} - - {maxOutput[1][0]} - - - {ioRatio[1][0]} - - ({formatUnits(10n ** 36n / BigInt(ioRatio[1][1]), 18)}) - - + {#if dataEntries.length === 1} + Missing stack data for max output and ratio + {:else} + {@const maxOutput = dataEntries[dataEntries.length - 2]} + {@const ioRatio = dataEntries[dataEntries.length - 1]} + + {maxOutput[1][0]} + + + {ioRatio[1][0]} + + ({formatUnits(10n ** 36n / BigInt(ioRatio[1][1]), 18)}) + + + {/if}
- - Order - Scenario - Pair - Maximum Output - Ratio - - +{#each Object.entries($data?.result ?? {}) as [deploymentName, results]} +

Deployment: {deploymentName}

+
+ + Order + Scenario + Pair + Maximum Output + Ratio + + - - {#each results as item} - {@const data = transformData(item.fuzz_result)[0]} - {@const dataEntries = Object.entries(data)} - - {item.order_name} - {item.fuzz_result.scenario} - {item.pair} - {#if dataEntries.length === 1} - Missing stack data for max output and ratio - {:else} - {@const maxOutput = dataEntries[dataEntries.length - 2]} - {@const ioRatio = dataEntries[dataEntries.length - 1]} - - {maxOutput[1][0]} - - - {ioRatio[1][0]} - - ({BigInt(ioRatio[1][1]) === 0n - ? '0' - : formatUnits(10n ** 36n / BigInt(ioRatio[1][1]), 18)}) - - - {/if} + + {#each results as item} + {@const data = transformData(item.fuzz_result)[0]} + {@const dataEntries = Object.entries(data)} + + {item.order_name} + {item.fuzz_result.scenario} + {item.pair} + {#if dataEntries.length === 1} + Missing stack data for max output and ratio + {:else} + {@const maxOutput = dataEntries[dataEntries.length - 2]} + {@const ioRatio = dataEntries[dataEntries.length - 1]} - + {maxOutput[1][0]} - - {/each} - -
- {/each} -
+ + {ioRatio[1][0]} + + ({BigInt(ioRatio[1][1]) === 0n + ? '0' + : formatUnits(10n ** 36n / BigInt(ioRatio[1][1]), 18)}) + + + {/if} + + + + + {/each} + + +{/each} From ea6a232c8b1339d5285979bc920de4ecd3658d50 Mon Sep 17 00:00:00 2001 From: findolor Date: Fri, 27 Sep 2024 17:19:42 +0300 Subject: [PATCH 11/15] refactor: return errors from debug function --- crates/common/src/fuzz/mod.rs | 61 ++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/crates/common/src/fuzz/mod.rs b/crates/common/src/fuzz/mod.rs index 8ddaffbf7..0ee2a5e12 100644 --- a/crates/common/src/fuzz/mod.rs +++ b/crates/common/src/fuzz/mod.rs @@ -40,9 +40,11 @@ pub struct DeploymentDebugData { #[typeshare] #[derive(Debug, Serialize, Deserialize)] pub struct DeploymentDebugPairData { - pub order_name: String, + pub order: String, + pub scenario: String, pub pair: String, - pub fuzz_result: FuzzResultFlat, + pub result: Option, + pub error: Option, } #[derive(Debug)] @@ -311,8 +313,6 @@ impl FuzzRunner { ); let dotrain = Arc::new(self.dotrain.clone()); - let mut handles = vec![]; - self.forker.roll_fork(Some(block_number), None)?; let fork = Arc::new(self.forker.clone()); // Wrap in Arc for shared ownership let fork_clone = Arc::clone(&fork); // Clone the Arc for each thread @@ -380,18 +380,10 @@ impl FuzzRunner { .map_err(FuzzRunnerError::ForkCallError) .await }); - handles.push(handle); - - let mut runs: Vec = Vec::new(); - - for handle in handles { - let res = handle.await??; - runs.push(res.into()); - } Ok(FuzzResult { scenario: scenario.name.clone(), - runs: runs.into(), + runs: vec![handle.await??.into()].into(), }) } @@ -458,18 +450,35 @@ impl FuzzRunner { output.token.symbol.clone().unwrap_or("UNKNOWN".to_string()) ); - let mut runner = self.clone(); - let fuzz_result = runner - .run_debug(block, input.clone(), output.clone(), &scenario) - .await? - .flatten_traces()?; - - let pair_data = DeploymentDebugPairData { - order_name: order_name.clone(), + let mut pair_data = DeploymentDebugPairData { + order: order_name.clone(), + scenario: scenario.name.clone(), pair, - fuzz_result, + result: None, + error: None, }; + let mut runner = self.clone(); + match runner + .run_debug(block, input.clone(), output.clone(), &scenario) + .await + { + Ok(fuzz_result) => match fuzz_result.flatten_traces() { + Ok(fuzz_result) => { + pair_data.result = Some(fuzz_result); + } + Err(e) => { + pair_data.error = Some(e.to_string()); + } + }, + Err(e) => { + if matches!(e, FuzzRunnerError::ComposeError(_)) { + return Err(e); + } + pair_data.error = Some(e.to_string()); + } + } + pair_datas .entry(deployment_name.clone()) .or_default() @@ -909,7 +918,13 @@ io-ratio: mul(0.99 20); .map_err(|e| println!("{:#?}", e)) .unwrap(); - let result_rows = res.result["sell-wflr"][0].fuzz_result.data.rows[0].clone(); + let result_rows = res.result["sell-wflr"][0] + .result + .as_ref() + .unwrap() + .data + .rows[0] + .clone(); assert_eq!( result_rows[0], U256::from_be_slice( From 39e7a3d140d1bccdb9e0addb71c01545ce969e22 Mon Sep 17 00:00:00 2001 From: findolor Date: Fri, 27 Sep 2024 17:19:58 +0300 Subject: [PATCH 12/15] refactor: handle the returned errors from debug function --- .../lib/components/ScenarioDebugTable.svelte | 61 +++++++++++-------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/tauri-app/src/lib/components/ScenarioDebugTable.svelte b/tauri-app/src/lib/components/ScenarioDebugTable.svelte index 55803eaf4..95eba008f 100644 --- a/tauri-app/src/lib/components/ScenarioDebugTable.svelte +++ b/tauri-app/src/lib/components/ScenarioDebugTable.svelte @@ -24,6 +24,10 @@ let debugError: string | undefined; const fetchData = async (dotrain: string, settings: string, blockNumber?: number) => { + if (dotrain === '' || settings === '') { + return; + } + try { debugError = undefined; loading = true; @@ -97,7 +101,7 @@
{#each Object.entries($data?.result ?? {}) as [deploymentName, results]} -

Deployment: {deploymentName}

+

Deployment: {deploymentName}

Order @@ -110,36 +114,41 @@ {#each results as item} - {@const data = transformData(item.fuzz_result)[0]} - {@const dataEntries = Object.entries(data)} - {item.order_name} - {item.fuzz_result.scenario} + {item.order} + {item.scenario} {item.pair} - {#if dataEntries.length === 1} - Missing stack data for max output and ratio - {:else} - {@const maxOutput = dataEntries[dataEntries.length - 2]} - {@const ioRatio = dataEntries[dataEntries.length - 1]} + {#if item.result} + {@const fuzzResult = item.result} + {@const data = transformData(fuzzResult)[0]} + {@const dataEntries = Object.entries(data)} + {#if dataEntries.length === 1} + Missing stack data for max output and ratio + {:else} + {@const maxOutput = dataEntries[dataEntries.length - 2]} + {@const ioRatio = dataEntries[dataEntries.length - 1]} + + {maxOutput[1][0]} + + + {ioRatio[1][0]} + + ({BigInt(ioRatio[1][1]) === 0n + ? '0' + : formatUnits(10n ** 36n / BigInt(ioRatio[1][1]), 18)}) + + + {/if} - {maxOutput[1][0]} - - - {ioRatio[1][0]} - - ({BigInt(ioRatio[1][1]) === 0n - ? '0' - : formatUnits(10n ** 36n / BigInt(ioRatio[1][1]), 18)}) - + + {:else} + {item.error} {/if} - - - {/each} From 1f91c0787f5434d6ed6b235a52e570e8781e426c Mon Sep 17 00:00:00 2001 From: findolor Date: Mon, 30 Sep 2024 13:23:36 +0300 Subject: [PATCH 13/15] refactor: move run all scenarios button to charts tab --- tauri-app/src/routes/orders/add/+page.svelte | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tauri-app/src/routes/orders/add/+page.svelte b/tauri-app/src/routes/orders/add/+page.svelte index 0319e63dc..f4b7e6c78 100644 --- a/tauri-app/src/routes/orders/add/+page.svelte +++ b/tauri-app/src/routes/orders/add/+page.svelte @@ -255,10 +255,6 @@ - - - +
+ + +
From 11aa031fea7636e5f005934222ff8fb451c42d8e Mon Sep 17 00:00:00 2001 From: findolor Date: Mon, 30 Sep 2024 18:35:42 +0300 Subject: [PATCH 14/15] feat: add entrypoint field to scenario --- crates/common/src/add_order.rs | 3 +++ crates/settings/src/chart.rs | 1 + crates/settings/src/config_source.rs | 2 ++ crates/settings/src/deployment.rs | 2 ++ crates/settings/src/scenario.rs | 6 ++++++ 5 files changed, 14 insertions(+) diff --git a/crates/common/src/add_order.rs b/crates/common/src/add_order.rs index 37be0fad8..df1301b57 100644 --- a/crates/common/src/add_order.rs +++ b/crates/common/src/add_order.rs @@ -397,6 +397,7 @@ price: 2e18; runs: None, blocks: None, deployer: deployer_arc.clone(), + entrypoint: None, }; let token1 = Token { address: Address::default(), @@ -495,6 +496,7 @@ _ _: 0 0; runs: None, blocks: None, deployer: deployer_arc.clone(), + entrypoint: None, }; let token1 = Token { address: Address::default(), @@ -627,6 +629,7 @@ _ _: 0 0; runs: None, blocks: None, deployer: deployer_arc.clone(), + entrypoint: None, }; let token1 = Token { address: Address::default(), diff --git a/crates/settings/src/chart.rs b/crates/settings/src/chart.rs index 8224cb2eb..2700bc880 100644 --- a/crates/settings/src/chart.rs +++ b/crates/settings/src/chart.rs @@ -93,6 +93,7 @@ mod tests { bindings: HashMap::from([(String::from("key"), String::from("value"))]), // Example binding runs, blocks: None, + entrypoint: None, deployer: mock_deployer(), }; (name.to_string(), Arc::new(scenario)) diff --git a/crates/settings/src/config_source.rs b/crates/settings/src/config_source.rs index 8d3a2eca0..016ad4298 100644 --- a/crates/settings/src/config_source.rs +++ b/crates/settings/src/config_source.rs @@ -171,6 +171,8 @@ pub struct ScenarioConfigSource { #[serde(skip_serializing_if = "Option::is_none")] pub blocks: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub entrypoint: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub deployer: Option, #[serde(skip_serializing_if = "Option::is_none")] pub scenarios: Option>, diff --git a/crates/settings/src/deployment.rs b/crates/settings/src/deployment.rs index a4e714616..c1827d071 100644 --- a/crates/settings/src/deployment.rs +++ b/crates/settings/src/deployment.rs @@ -70,6 +70,7 @@ mod tests { deployer: mock_deployer(), runs: None, blocks: None, + entrypoint: None, }; let order = Order { inputs: vec![], @@ -99,6 +100,7 @@ mod tests { deployer: mock_deployer(), runs: None, blocks: None, + entrypoint: None, }; let order = Order { inputs: vec![], diff --git a/crates/settings/src/scenario.rs b/crates/settings/src/scenario.rs index 701307dd3..3304b45ad 100644 --- a/crates/settings/src/scenario.rs +++ b/crates/settings/src/scenario.rs @@ -15,6 +15,7 @@ pub struct Scenario { pub runs: Option, #[typeshare(skip)] pub blocks: Option, + pub entrypoint: Option, #[typeshare(typescript(type = "Deployer"))] pub deployer: Arc, } @@ -96,6 +97,7 @@ impl ScenarioConfigSource { bindings: bindings.clone(), runs: self.runs, blocks: self.blocks.clone(), + entrypoint: self.entrypoint.clone(), deployer: deployer_ref.clone(), }); @@ -168,6 +170,7 @@ mod tests { bindings: HashMap::new(), // Assuming no bindings for simplification runs: Some(2), blocks: None, + entrypoint: None, deployer: None, scenarios: None, // No further nesting }, @@ -180,6 +183,7 @@ mod tests { bindings: HashMap::new(), // Assuming no bindings for simplification runs: Some(5), blocks: None, + entrypoint: None, deployer: None, scenarios: Some(nested_scenario2), // Include nested_scenario2 }, @@ -193,6 +197,7 @@ mod tests { bindings: HashMap::new(), // Assuming no bindings for simplification runs: Some(10), blocks: None, + entrypoint: None, deployer: Some("mainnet".to_string()), scenarios: Some(nested_scenario1), // Include nested_scenario1 }, @@ -266,6 +271,7 @@ mod tests { bindings: child_bindings, runs: None, blocks: None, + entrypoint: None, deployer: None, scenarios: None, }; From 7b882ce6c490290d278a32a4050d2208cf7256bb Mon Sep 17 00:00:00 2001 From: findolor Date: Mon, 30 Sep 2024 18:44:08 +0300 Subject: [PATCH 15/15] feat: implement the custom entry point logic in fuzzer --- crates/common/src/fuzz/mod.rs | 67 ++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/crates/common/src/fuzz/mod.rs b/crates/common/src/fuzz/mod.rs index 0ee2a5e12..c084f03fe 100644 --- a/crates/common/src/fuzz/mod.rs +++ b/crates/common/src/fuzz/mod.rs @@ -145,6 +145,17 @@ impl FuzzRunner { let deployer = scenario.deployer.clone(); + // Create entrypoints from the scenario's entrypoint field + let entrypoints: Vec = if let Some(entrypoint) = &scenario.entrypoint { + vec![entrypoint.clone()] + } else { + ORDERBOOK_ORDER_ENTRYPOINTS + .iter() + .map(|&s| s.to_string()) + .collect() + }; + let entrypoints = Arc::new(entrypoints); + // Fetch the latest block number let block_number = ReadableClientHttp::new_from_url(deployer.network.rpc.to_string())? .get_block_number() @@ -208,6 +219,7 @@ impl FuzzRunner { let deployer = Arc::clone(&deployer); let scenario_bindings = scenario_bindings.clone(); let dotrain = Arc::clone(&dotrain); + let entrypoints = Arc::clone(&entrypoints); let mut final_bindings: Vec = vec![]; @@ -224,7 +236,7 @@ impl FuzzRunner { let rainlang_string = RainDocument::compose_text( &dotrain, - &ORDERBOOK_ORDER_ENTRYPOINTS, + &entrypoints.iter().map(AsRef::as_ref).collect::>(), None, Some(final_bindings), )?; @@ -840,6 +852,59 @@ _: context<1 0>(); } } + #[tokio::test(flavor = "multi_thread", worker_threads = 10)] + async fn test_custom_entrypoint() { + let local_evm = LocalEvm::new().await; + let dotrain = format!( + r#" +deployers: + some-key: + address: {deployer} +networks: + some-key: + rpc: {rpc_url} + chain-id: 123 +scenarios: + some-key: + runs: 1 + entrypoint: "some-entrypoint" +charts: + some-key: + scenario: some-key +--- +#some-entrypoint +a: 20, +b: 30; + "#, + rpc_url = local_evm.url(), + deployer = local_evm.deployer.address() + ); + let frontmatter = RainDocument::get_front_matter(&dotrain).unwrap(); + let settings = serde_yaml::from_str::(frontmatter).unwrap(); + let config = settings + .try_into() + .map_err(|e| println!("{:?}", e)) + .unwrap(); + + let runner = FuzzRunner::new(&dotrain, config, None).await; + + let res = runner + .make_chart_data() + .await + .map_err(|e| println!("{:#?}", e)) + .unwrap(); + + let scenario_data = res.scenarios_data.get("some-key").unwrap(); + assert_eq!( + scenario_data.data.rows[0][0], + U256::from(20000000000000000000u128) + ); + assert_eq!( + scenario_data.data.rows[0][1], + U256::from(30000000000000000000u128) + ); + } + #[tokio::test(flavor = "multi_thread", worker_threads = 10)] async fn test_debug() { let local_evm = LocalEvm::new().await;