diff --git a/crates/common/src/add_order.rs b/crates/common/src/add_order.rs index 72ecf9226..8524e7a35 100644 --- a/crates/common/src/add_order.rs +++ b/crates/common/src/add_order.rs @@ -70,8 +70,7 @@ pub struct AddOrderArgs { impl AddOrderArgs { /// Parse an Io array from from frontmatter field (i.e. validInputs or validOutputs) - fn try_parse_frontmatter_io( - &self, + pub(crate) fn try_parse_frontmatter_io( io_yamls: StrictYaml, io_field_name: &str, ) -> Result, AddOrderArgsError> { @@ -129,8 +128,7 @@ impl AddOrderArgs { } /// Parse dotrain frontmatter to extract deployer, validInputs and validOutputs - fn try_parse_frontmatter( - &self, + pub(crate) fn try_parse_frontmatter( frontmatter: &str, ) -> Result<(Address, Vec, Vec), AddOrderArgsError> { // Parse dotrain document frontmatter @@ -147,11 +145,11 @@ impl AddOrderArgs { AddOrderArgsError::FrontmatterFieldInvalid("orderbook.order.deployer".into()) })?; - let valid_inputs: Vec = self.try_parse_frontmatter_io( + let valid_inputs: Vec = Self::try_parse_frontmatter_io( frontmatter_yaml[0]["orderbook"]["order"]["validInputs"].clone(), "validInputs", )?; - let valid_outputs: Vec = self.try_parse_frontmatter_io( + let valid_outputs: Vec = Self::try_parse_frontmatter_io( frontmatter_yaml[0]["orderbook"]["order"]["validOutputs"].clone(), "validOutputs", )?; @@ -208,7 +206,7 @@ impl AddOrderArgs { // Prepare call let (deployer, valid_inputs, valid_outputs) = - self.try_parse_frontmatter(dotrain_doc.front_matter().as_str())?; + Self::try_parse_frontmatter(dotrain_doc.front_matter().as_str())?; let (bytecode, constants) = self .try_parse_rainlang(rpc_url, deployer, rainlang.clone()) .await?; @@ -267,10 +265,9 @@ orderbook: decimals: 18 vaultId: 0x2 "; - let args = AddOrderArgs { dotrain: "".into() }; let (deployer, valid_inputs, valid_outputs) = - args.try_parse_frontmatter(frontmatter).unwrap(); + AddOrderArgs::try_parse_frontmatter(frontmatter).unwrap(); assert_eq!( deployer, diff --git a/crates/common/src/error.rs b/crates/common/src/error.rs index a93438455..869eb718a 100644 --- a/crates/common/src/error.rs +++ b/crates/common/src/error.rs @@ -1,4 +1,4 @@ -use crate::transaction::TransactionArgsError; +use crate::{add_order::AddOrderArgsError, transaction::TransactionArgsError}; use alloy_dyn_abi::JsonAbiExt; use alloy_ethers_typecast::{client::LedgerClientError, transaction::WritableClientError}; use alloy_json_abi::Error as AlloyError; @@ -40,6 +40,27 @@ pub enum AbiDecodedErrorType { }, } +impl From for String { + fn from(value: AbiDecodedErrorType) -> Self { + value.to_string() + } +} + +impl std::fmt::Display for AbiDecodedErrorType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AbiDecodedErrorType::Unknown => { + f.write_str("native parser panicked with unknown error!") + } + AbiDecodedErrorType::Known { name, args, .. } => f.write_str(&format!( + "native parser panicked with: {}\n{}", + name, + args.join("\n") + )), + } + } +} + /// decodes an error returned from calling a contract by searching its selector in registry pub async fn abi_decode_error<'a>( error_data: &[u8], @@ -148,6 +169,28 @@ impl<'a> From>>> for ForkC } } +#[derive(Debug, Error)] +pub enum ForkParseError { + #[error("ForkCall error: {0}")] + ForkCallFailed(ForkCallError<'static>), + #[error("{0}")] + AbiDecodedError(AbiDecodedErrorType), + #[error("Invalid Front Matter error: {0}")] + InvalidFrontMatter(#[from] AddOrderArgsError), +} + +impl From for ForkParseError { + fn from(value: AbiDecodedErrorType) -> Self { + Self::AbiDecodedError(value) + } +} + +impl From> for ForkParseError { + fn from(value: ForkCallError<'static>) -> Self { + Self::ForkCallFailed(value) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/common/src/fork.rs b/crates/common/src/fork.rs index bbb13efbc..e936c902c 100644 --- a/crates/common/src/fork.rs +++ b/crates/common/src/fork.rs @@ -1,10 +1,19 @@ use super::error::ForkCallError; use super::error::{abi_decode_error, AbiDecodedErrorType}; +use crate::add_order::AddOrderArgs; +use crate::error::ForkParseError; +use alloy_primitives::hex::decode; +use alloy_sol_types::SolCall; use forker::*; use once_cell::sync::Lazy; +use rain_interpreter_bindings::DeployerISP::iParserCall; +use rain_interpreter_bindings::IExpressionDeployerV3::deployExpression2Call; +use rain_interpreter_bindings::IParserV1::parseCall; use revm::primitives::Bytes; use std::{collections::HashMap, sync::Mutex}; +const FROM_ADDRESS: &str = "0x5855A7b48a1f9811392B89F18A8e27347EF84E42"; + /// static hashmap of fork evm instances, used for caching instances between runs pub static FORKS: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); @@ -54,6 +63,54 @@ pub async fn fork_call<'a>( } } +/// checks the front matter validity and parses the given rainlang string +/// with the deployer parsed from the front matter +/// returns abi encoded expression config on Ok variant +pub async fn fork_parse_rainlang( + rainlang_string: &str, + front_matter: &str, + fork_url: &str, + fork_block_number: u64, +) -> Result { + let deployer = AddOrderArgs::try_parse_frontmatter(front_matter)?.0; + + let calldata = iParserCall {}.abi_encode(); + let parser_address = fork_call( + fork_url, + fork_block_number, + &decode(FROM_ADDRESS).unwrap(), + deployer.as_slice(), + &calldata, + ) + .await??; + + let calldata = parseCall { + data: rainlang_string.as_bytes().to_vec(), + } + .abi_encode(); + let expression_config = fork_call( + fork_url, + fork_block_number, + &decode(FROM_ADDRESS).unwrap(), + &parser_address, + &calldata, + ) + .await??; + + let mut calldata = deployExpression2Call::SELECTOR.to_vec(); + calldata.extend_from_slice(&expression_config); + fork_call( + fork_url, + fork_block_number, + &decode(FROM_ADDRESS).unwrap(), + deployer.as_slice(), + &calldata, + ) + .await??; + + Ok(expression_config) +} + #[cfg(test)] mod tests { use super::*; diff --git a/flake.nix b/flake.nix index 09cd9d114..f582f403a 100644 --- a/flake.nix +++ b/flake.nix @@ -38,6 +38,9 @@ ''; additionalBuildInputs = [ pkgs.typeshare + pkgs.wasm-bindgen-cli + rainix.rust-toolchain.${system} + rainix.rust-build-inputs.${system} ]; }; ob-tauri-test = rainix.mkTask.${system} { diff --git a/tauri-app/package-lock.json b/tauri-app/package-lock.json index 512f376ef..b990a3f92 100644 --- a/tauri-app/package-lock.json +++ b/tauri-app/package-lock.json @@ -11,6 +11,7 @@ "@fontsource/dm-sans": "^5.0.18", "@imask/svelte": "^7.3.0", "@tauri-apps/api": "^1.5.3", + "codemirror-rainlang": "github:rainlanguage/rainlang-codemirror#1f003726faa1523073bcff68781631c847f41f17", "dayjs": "^1.11.10", "lodash": "^4.17.21", "uuid": "^9.0.1", @@ -40,7 +41,9 @@ "prettier-plugin-tailwindcss": "^0.5.9", "svelte": "^4.2.7", "svelte-check": "^3.6.0", + "svelte-codemirror-editor": "^1.2.0", "tailwindcss": "^3.3.6", + "thememirror": "^2.0.1", "tslib": "^2.4.1", "typescript": "^5.0.0", "vite": "^5.0.3", @@ -288,6 +291,98 @@ "node": ">=6.9.0" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.12.0.tgz", + "integrity": "sha512-r4IjdYFthwbCQyvqnSlx0WBHRHi8nBvU+WjJxFUij81qsBfhNudf/XKKmmC2j3m0LaOYUQTf3qiEK1J8lO1sdg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + }, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.3.tgz", + "integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==", + "dev": true, + "peer": true, + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-yaml": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.0.0.tgz", + "integrity": "sha512-fVPapdX1oYr5HMC5bou1MHscGnNCvOHuhUW6C+V2gfIeIRcughvVfznV0OuUyHy0AdXoBCjOehjzFcmLRumu2Q==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/yaml": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.0.tgz", + "integrity": "sha512-2vaNn9aPGCRFKWcHPFksctzJ8yS5p7YoaT+jHpc0UGKzNuAIx4qy6R5wiqbP+heEEdyaABA582mNqSHzSoYdmg==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.5.0.tgz", + "integrity": "sha512-+5YyicIaaAZKU8K43IQi8TBy6mF6giGeWAH7N96Z5LC30Wm5JMjqxOYIE9mxwMG1NbhT2mA3l9hA4uuKUM3E5g==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.5.tgz", + "integrity": "sha512-PIEN3Ke1buPod2EHbJsoQwlbpkz30qGZKcnmH1eihq9+bPQx8gelauUwLYaY4vBOuBAuEhmpDLii4rj/uO0yMA==", + "dev": true, + "peer": true, + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.0.tgz", + "integrity": "sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A==" + }, + "node_modules/@codemirror/view": { + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.23.1.tgz", + "integrity": "sha512-J2Xnn5lFYT1ZN/5ewEoMBCmLlL71lZ3mBdb7cUEuHhX2ESoSrNEucpsDXpX22EuTGm9LOgC9v4Z0wx+Ez8QmGA==", + "dependencies": { + "@codemirror/state": "^6.4.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.11", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", @@ -929,6 +1024,37 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lezer/common": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", + "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", + "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz", + "integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/yaml": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.2.tgz", + "integrity": "sha512-XCkwuxe+eumJ28nA9e1S6XKsXz9W7V/AG+WBiWOtiIuUpKcZ/bHuvN8bLxSDREIcybSRpEd/jvphh4vgm6Ed2g==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.4.0" + } + }, "node_modules/@noble/curves": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", @@ -1012,6 +1138,42 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rainlanguage/dotrain": { + "version": "6.0.0", + "resolved": "git+ssh://git@github.com/rainlanguage/dotrain.git#3a0f4e8677033a4588fdcdf6f2af09f081e710fb", + "integrity": "sha512-M7o8eDZxDRMwwi7G3QOs/G+eiZNQttsolNpgutb1l58buIguBdH6oJsdnfXpJvjPnGHLPsYnx1QMBQTLqPFxfw==", + "license": "CAL-1.0", + "dependencies": { + "buffer": "^6.0.3", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-types": "^3.17.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rainlanguage/dotrain/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rainlanguage/dotrain/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/@rainlanguage/dotrain/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.9.4", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.4.tgz", @@ -1914,6 +2076,25 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1976,6 +2157,29 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -2129,6 +2333,39 @@ "periscopic": "^3.1.0" } }, + "node_modules/codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dev": true, + "peer": true, + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/codemirror-rainlang": { + "version": "2.0.3", + "resolved": "git+ssh://git@github.com/rainlanguage/rainlang-codemirror.git#1f003726faa1523073bcff68781631c847f41f17", + "integrity": "sha512-pr/TsjGCtCS7x+CfeVBHXSDpOGFACYY5JL4bBFexOn/Mtrnzu0h+R3/QvhR2bsRFxrsg7Go33zsehTaRywRnUw==", + "license": "CAL-1.0", + "dependencies": { + "@codemirror/autocomplete": "^6.4.2", + "@codemirror/lang-yaml": "^6.0.0", + "@codemirror/lint": "^6.2.0", + "@codemirror/state": "^6.2.0", + "@codemirror/view": "^6.9.3", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.3", + "@rainlanguage/dotrain": "github:rainlanguage/dotrain#3a0f4e8677033a4588fdcdf6f2af09f081e710fb", + "vscode-languageserver-textdocument": "^1.0.11" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2181,6 +2418,11 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3144,6 +3386,25 @@ "node": ">=16.17.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -4773,6 +5034,11 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/style-mod": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", + "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -4886,6 +5152,16 @@ "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" } }, + "node_modules/svelte-codemirror-editor": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/svelte-codemirror-editor/-/svelte-codemirror-editor-1.2.0.tgz", + "integrity": "sha512-OEf52U4bBeyDFCMtPH+ZYCtYT+KdyjUw+l19V72Bxeadh2LdPOWC0WSEP+hXRleg/94VC5NgfgqHXf3DqoVZUA==", + "dev": true, + "peerDependencies": { + "codemirror": "^6.0.0", + "svelte": "^3.0.0 || ^4.0.0" + } + }, "node_modules/svelte-eslint-parser": { "version": "0.33.1", "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.33.1.tgz", @@ -5188,6 +5464,17 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thememirror": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/thememirror/-/thememirror-2.0.1.tgz", + "integrity": "sha512-d5i6FVvWWPkwrm4cHLI3t9AT1OrkAt7Ig8dtdYSofgF7C/eiyNuq6zQzSTusWTde3jpW9WLvA9J/fzNKMUsd0w==", + "dev": true, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -5742,8 +6029,7 @@ "node_modules/vscode-languageserver-textdocument": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", - "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==", - "dev": true + "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==" }, "node_modules/vscode-languageserver-types": { "version": "3.16.0", @@ -5757,6 +6043,11 @@ "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", "dev": true }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/tauri-app/package.json b/tauri-app/package.json index 0caced696..f96c33cce 100644 --- a/tauri-app/package.json +++ b/tauri-app/package.json @@ -35,7 +35,9 @@ "prettier-plugin-tailwindcss": "^0.5.9", "svelte": "^4.2.7", "svelte-check": "^3.6.0", + "svelte-codemirror-editor": "^1.2.0", "tailwindcss": "^3.3.6", + "thememirror": "^2.0.1", "tslib": "^2.4.1", "typescript": "^5.0.0", "vite": "^5.0.3", @@ -47,6 +49,7 @@ "@fontsource/dm-sans": "^5.0.18", "@imask/svelte": "^7.3.0", "@tauri-apps/api": "^1.5.3", + "codemirror-rainlang": "github:rainlanguage/rainlang-codemirror#1f003726faa1523073bcff68781631c847f41f17", "dayjs": "^1.11.10", "lodash": "^4.17.21", "uuid": "^9.0.1", diff --git a/tauri-app/src-tauri/src/commands/fork.rs b/tauri-app/src-tauri/src/commands/fork.rs new file mode 100644 index 000000000..b8b39a76a --- /dev/null +++ b/tauri-app/src-tauri/src/commands/fork.rs @@ -0,0 +1,17 @@ +use super::super::error::CommandError; +use alloy_primitives::bytes::Bytes; +use rain_orderbook_common::fork::fork_parse_rainlang; + +#[tauri::command] +pub async fn fork_parse( + rainlang: &str, + front_matter: &str, + fork_url: &str, + fork_block_number: u64, +) -> Result { + Ok( + fork_parse_rainlang(rainlang, front_matter, fork_url, fork_block_number) + .await + .map(Bytes::from)?, + ) +} diff --git a/tauri-app/src-tauri/src/commands/mod.rs b/tauri-app/src-tauri/src/commands/mod.rs index 93e0df53e..abe34cef8 100644 --- a/tauri-app/src-tauri/src/commands/mod.rs +++ b/tauri-app/src-tauri/src/commands/mod.rs @@ -1,4 +1,5 @@ pub mod chain; +pub mod fork; pub mod order; pub mod vault; pub mod wallet; diff --git a/tauri-app/src-tauri/src/commands/wallet.rs b/tauri-app/src-tauri/src/commands/wallet.rs index 7879b5936..74d34b4e9 100644 --- a/tauri-app/src-tauri/src/commands/wallet.rs +++ b/tauri-app/src-tauri/src/commands/wallet.rs @@ -1,8 +1,5 @@ use crate::error::CommandResult; -use alloy_ethers_typecast::{ - client::LedgerClient, - ethers_address_to_alloy, -}; +use alloy_ethers_typecast::{client::LedgerClient, ethers_address_to_alloy}; use alloy_primitives::Address; #[tauri::command] @@ -11,7 +8,8 @@ pub async fn get_address_from_ledger( chain_id: u64, rpc_url: String, ) -> CommandResult
{ - let ledger_client = LedgerClient::new(derivation_index, chain_id, rpc_url.clone(), None).await?; + let ledger_client = + LedgerClient::new(derivation_index, chain_id, rpc_url.clone(), None).await?; let ledger_address = ethers_address_to_alloy(ledger_client.client.address()); Ok(ledger_address) diff --git a/tauri-app/src-tauri/src/error.rs b/tauri-app/src-tauri/src/error.rs index eb3e202cb..9b8e5b110 100644 --- a/tauri-app/src-tauri/src/error.rs +++ b/tauri-app/src-tauri/src/error.rs @@ -1,5 +1,6 @@ use alloy_ethers_typecast::{client::LedgerClientError, transaction::ReadableClientError}; use alloy_primitives::ruint::FromUintError; +use rain_orderbook_common::error::ForkParseError; use rain_orderbook_subgraph_client::{OrderbookSubgraphClientError, WriteCsvError}; use serde::{ser::Serializer, Serialize}; use thiserror::Error; @@ -24,6 +25,9 @@ pub enum CommandError { #[error(transparent)] WriteCsvError(#[from] WriteCsvError), + + #[error(transparent)] + ForkParseRainlangError(ForkParseError), } impl Serialize for CommandError { @@ -36,3 +40,9 @@ impl Serialize for CommandError { } pub type CommandResult = Result; + +impl From for CommandError { + fn from(value: ForkParseError) -> Self { + Self::ForkParseRainlangError(value) + } +} diff --git a/tauri-app/src-tauri/src/main.rs b/tauri-app/src-tauri/src/main.rs index 93c6c7774..1b8f8fabc 100644 --- a/tauri-app/src-tauri/src/main.rs +++ b/tauri-app/src-tauri/src/main.rs @@ -7,11 +7,11 @@ pub mod transaction_status; mod commands; use commands::chain::get_chainid; +use commands::fork::fork_parse; use commands::order::{order_detail, order_remove, orders_list, orders_list_write_csv}; use commands::vault::{ - vault_deposit, vault_detail, vault_withdraw, vaults_list, vaults_list_write_csv, - vault_list_balance_changes_write_csv, - vault_list_balance_changes, + vault_deposit, vault_detail, vault_list_balance_changes, vault_list_balance_changes_write_csv, + vault_withdraw, vaults_list, vaults_list_write_csv, }; use commands::wallet::get_address_from_ledger; @@ -31,6 +31,7 @@ fn main() { order_remove, get_address_from_ledger, get_chainid, + fork_parse, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/tauri-app/src/lib/texteditor/TextEditor.svelte b/tauri-app/src/lib/texteditor/TextEditor.svelte new file mode 100644 index 000000000..4c408ef55 --- /dev/null +++ b/tauri-app/src/lib/texteditor/TextEditor.svelte @@ -0,0 +1,69 @@ + + +
+ +
+ + \ No newline at end of file diff --git a/tauri-app/src/lib/texteditor/forkParse.ts b/tauri-app/src/lib/texteditor/forkParse.ts new file mode 100644 index 000000000..073cb5f41 --- /dev/null +++ b/tauri-app/src/lib/texteditor/forkParse.ts @@ -0,0 +1,59 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { invoke } from '@tauri-apps/api'; +import { RainDocument, ErrorCode, type Problem } from "codemirror-rainlang"; + +/** + * Parses a RainDocument with native parser with hardcoded entrypoints + * @param dotrain - The RainDocument instance + * @param forkUrl - frok url + * @param forkBlockNumber - fork block number + * @returns Resolves with empty array or with array of Problems if encountered an error + */ +export async function forkParseDotrain(dotrain: RainDocument, forkUrl: string, forkBlockNumber: number): Promise { + let rainlang: string; + const frontMatter = dotrain.frontMatter; + try { + // set the hardcoded entrypoints + rainlang = await dotrain.compose(["calculate-io", "handle-io"]); + } catch(err) { + // if compose fails, reject with the caught error + if (err && typeof err === "object") { + if ("Reject" in err) { + return [{ + msg: err.Reject as string, + position: [0, 0], + code: ErrorCode.NativeParserError + }] + } else if ("Problems" in err) { + return err.Problems as Problem[] + } else { + // in case of unexpected panic with uknown error type + return [{ + msg: "something went wrong: " + (typeof err === "string" ? err : err instanceof Error ? err.message : ""), + position: [0, 0], + code: ErrorCode.NativeParserError + }] + } + } else { + // in case of unexpected panic with uknown error type + return [{ + msg: "something went wrong: " + (typeof err === "string" ? err : ""), + position: [0, 0], + code: ErrorCode.NativeParserError + }] + } + } + + try { + // invoke tauri fork parse command + await invoke('fork_parse', { rainlang, frontMatter, forkUrl, forkBlockNumber }); + return []; + } catch(err) { + // if the fork call fails, reject with the caught errors + return [{ + msg: typeof err === "string" ? err : err instanceof Error ? err.message : "", + position: [0, 0], // default position for native parser errors without knowing offset + code: ErrorCode.NativeParserError + }] + } +} \ No newline at end of file diff --git a/tauri-app/src/lib/texteditor/themes.ts b/tauri-app/src/lib/texteditor/themes.ts new file mode 100644 index 000000000..e76f7262c --- /dev/null +++ b/tauri-app/src/lib/texteditor/themes.ts @@ -0,0 +1,210 @@ +import { createTheme } from 'thememirror'; +import { tags } from '@lezer/highlight'; + +// configured to resemble vscode default light theme +export const lightTheme = createTheme({ + variant: 'light', + settings: { + background: '#ffffff', + foreground: '#001080', + caret: '#000000', + selection: '#add6ff', + lineHighlight: '#dadada', + gutterBackground: '#eeeeee', + gutterForeground: '#237893' + }, + styles: [ + { + tag: tags.comment, + color: '#008001' + }, + { + tag: tags.variableName, + color: '#0070c1' + }, + { + tag: [tags.string, tags.special(tags.brace)], + color: '#a31515' + }, + { + tag: tags.number, + color: '#00b97b' + }, + { + tag: tags.bool, + color: '#0000ff' + }, + { + tag: tags.null, + color: '#0000ff' + }, + { + tag: tags.unit, + color: '#0000ff' + }, + { + tag: tags.keyword, + color: '#af01db' + }, + { + tag: tags.operator, + color: '#000000' + }, + { + tag: tags.className, + color: '#257f99' + }, + { + tag: tags.meta, + color: '#0950a9' + }, + { + tag: tags.definition(tags.typeName), + color: '#257f99' + }, + { + tag: tags.angleBracket, + color: '#213CF1' + }, + { + tag: tags.brace, + color: '#213CF1' + }, + { + tag: tags.bracket, + color: '#213CF1' + }, + { + tag: tags.squareBracket, + color: '#213CF1' + }, + { + tag: tags.paren, + color: '#213CF1' + }, + { + tag: tags.punctuation, + color: '#464646' + }, + { + tag: tags.separator, + color: '#464646' + }, + { + tag: tags.typeName, + color: '#257f99' + }, + { + tag: tags.tagName, + color: '#800000' + }, + { + tag: tags.attributeName, + color: '#eb3d36' + } + ] +}); + +// configured to resemble vscode default dark theme +export const darkTheme = createTheme({ + variant: 'dark', + settings: { + background: '#1e1e1e', + foreground: '#d4d4d4', + caret: '#d4d4d4', + selection: '#5A7590', + lineHighlight: '#3c3c41', + gutterBackground: '#282828', + gutterForeground: '#858585' + }, + styles: [ + { + tag: tags.comment, + color: '#6c9e57' + }, + { + tag: tags.variableName, + color: '#9cdcfe' + }, + { + tag: [tags.string, tags.special(tags.brace)], + color: '#ce9178' + }, + { + tag: tags.number, + color: '#B6CFA9' + }, + { + tag: tags.bool, + color: '#4fc4ff' + }, + { + tag: tags.null, + color: '#4fc4ff' + }, + { + tag: tags.unit, + color: '#608FC2' + }, + { + tag: tags.keyword, + color: '#d18dcc' + }, + { + tag: tags.operator, + color: '#d4d4d4' + }, + { + tag: tags.className, + color: '#4dcab1' + }, + { + tag: tags.meta, + color: '#608FC2' + }, + { + tag: tags.definition(tags.typeName), + color: '#4fcfb5' + }, + { + tag: tags.angleBracket, + color: '#F9D849' + }, + { + tag: tags.brace, + color: '#F9D849' + }, + { + tag: tags.bracket, + color: '#F9D849' + }, + { + tag: tags.squareBracket, + color: '#F9D849' + }, + { + tag: tags.paren, + color: '#F9D849' + }, + { + tag: tags.punctuation, + color: '#d4d4d4' + }, + { + tag: tags.separator, + color: '#d4d4d4' + }, + { + tag: tags.typeName, + color: '#4ecdb4' + }, + { + tag: tags.tagName, + color: '#59a3df' + }, + { + tag: tags.attributeName, + color: '#a0e1ff' + } + ] +}); \ No newline at end of file