feat: Add price ratio functionality to DotrainOrderGuide#2084
feat: Add price ratio functionality to DotrainOrderGuide#2084big14way wants to merge 1 commit intorainlanguage:mainfrom
Conversation
- Add support for ${io-ratio(input, output)} expressions in YAML configurations
- Extend Context system to parse and resolve price ratio expressions
- Add price callback support to DotrainOrderGui for external price fetching
- Implement comprehensive error handling for price callback failures
- Add backward-compatible API with newWithDeploymentAndPriceCallback method
- Include complete documentation and examples for price ratio usage
- Support dynamic field names, descriptions, and default values with market prices
Resolves rainlanguage#1998: Ability to reference price ratios in field names/descriptions and for default field values
WalkthroughAdds dynamic price ratio support to DotrainOrderGui via a JavaScript price callback and YAML ${io-ratio(input, output)} expressions. Implements placeholder parsing/resolution, updates constructors and state restoration to accept a price callback, interpolates defaults in settings, and adds documentation, examples, and tests. Includes a submodule pointer update. Changes
Sequence Diagram(s)sequenceDiagram
participant App as App (JS)
participant GUI as DotrainOrderGui (WASM)
participant Ctx as YAML Context
participant CB as priceCallback (JS)
App->>GUI: newWithDeploymentAndPriceCallback(dotrain, deployment, stateCb, priceCb)
GUI->>Ctx: Build Context (with price_callback id)
Ctx-->>GUI: Parsed deployment with ${io-ratio(...)} placeholders
GUI->>GUI: resolve_price_ratios(...) on fields/names/descriptions/defaults
loop For each io-ratio(input, output)
GUI->>CB: priceCallback(inputAddr, outputAddr)
CB-->>GUI: Promise -> "priceString"
GUI->>GUI: Substitute placeholder with priceString
end
GUI-->>App: Initialized GUI instance
sequenceDiagram
participant App as App (JS)
participant GUI as DotrainOrderGui (WASM)
participant Restore as State Restoration
App->>GUI: newFromStateWithPriceCallback(dotrain, serialized, stateCb, priceCb)
GUI->>Restore: Decode + verify + rebuild GUI state
Restore-->>GUI: State with price_callback wired
GUI-->>App: Restored GUI instance
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested labels
Suggested reviewers
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 21
🔭 Outside diff range comments (5)
crates/settings/src/gui.rs (1)
842-858: Default interpolation should degrade gracefully when no price callback is providedParsing GUI YAML currently fails if a default contains an io-ratio expression and the Context has no price callback. Catch ContextError::NoPriceCallback and preserve the raw template (so non-GUI consumers can parse) while still propagating other interpolation errors.
Files to change:
- crates/settings/src/gui.rs — fields parsing (around the interpolation block, ~lines 815–860)
- Consider also guarding name/description interpolations in the same area (lines ~815–825) if you want to avoid premature failures there as well.
Suggested patch:
- let default = optional_string(field_yaml, "default"); - let interpolated_default = default.map(|default| context.interpolate(&default)).transpose()?; + let default = optional_string(field_yaml, "default"); + let interpolated_default = match default { + Some(d) if d.contains("${") => match context.interpolate(&d) { + Ok(s) => Some(s), + // If price callback isn't available at parse time, keep the raw template. + Err(crate::yaml::context::ContextError::NoPriceCallback) => Some(d), + Err(e) => return Err(e.into()), + }, + Some(d) => Some(d), + None => None, + };If desirable, apply the same pattern to interpolated_name/interpolated_description to avoid similar failures there.
crates/js_api/src/gui/state_management.rs (3)
1-11: Missing imports for base64 and flate2 types used in this module.URL_SAFE, GzEncoder, and GzDecoder are referenced but not imported here.
use super::*won’t bring these into scope because they are not re-exported from the parent module.Apply this diff near the top of the file:
use wasm_bindgen::JsValue; +use base64::{engine::general_purpose::URL_SAFE, Engine}; +use flate2::{read::GzDecoder, write::GzEncoder, Compression};Also applies to: 185-191, 266-268
331-337: Improve naming clarity for destructured tuple (nit).
is_inputis a VaultType, not a boolean. Consider renaming tovault_type, andindextotoken_keyto more clearly represent what’s passed to update_vault_id.- for ((is_input, index), vault_id) in state.vault_ids { + for ((vault_type, token_key), vault_id) in state.vault_ids { dotrain_order_gui .dotrain_order .dotrain_yaml() .get_order_for_gui_deployment(&order_key, &state.selected_deployment) - .and_then(|mut order| order.update_vault_id(is_input, index, vault_id))?; + .and_then(|mut order| order.update_vault_id(vault_type, token_key, vault_id))?; }
423-439: get_all_gui_config returns unresolved ${io-ratio(...)} — add a price-resolution variantgetAllGuiConfig currently calls self.get_current_deployment() (returns unresolved values). There is an async resolver get_current_deployment_with_price_resolution at crates/js_api/src/gui/mod.rs:881 that will resolve ${io-ratio(...)} when a price_callback is present.
Files/locations to update:
- crates/js_api/src/gui/state_management.rs — get_all_gui_config (lines ~423–439): either
- make this method async and call get_current_deployment_with_price_resolution().await when self.price_callback.is_some(), or
- add a new async wasm-exported method (e.g., getAllGuiConfigWithPriceResolution) that awaits get_current_deployment_with_price_resolution and returns the resolved AllGuiConfig (non-breaking).
- crates/js_api/src/gui/mod.rs:881 — contains existing async resolver to use.
- crates/js_api/src/gui/field_values.rs (get_all_field_definitions, ~line 345) — ensure it consumes the resolved deployment if you change get_all_gui_config to use the resolved deployment.
Recommendation: prefer adding a new async wasm method getAllGuiConfigWithPriceResolution (non-breaking). I can prepare a patch for either approach — tell me which you prefer.
crates/js_api/src/gui/mod.rs (1)
174-193: Signature mismatch: new_with_deployment doc/tests pass a price_callback, but the function accepts only 3 params.Tests and examples call new_with_deployment with four arguments (including price_callback), but the function signature exposes only three. Fix by adding the optional price_callback parameter and forwarding it to new_with_deployment_and_price_callback.
Apply this diff:
pub async fn new_with_deployment( #[wasm_export(param_description = "Complete dotrain YAML content with all configurations")] dotrain: String, #[wasm_export( param_description = "Key of the deployment to activate (must exist in YAML)" )] selected_deployment: String, #[wasm_export(param_description = "Optional function called on state changes. \ After a state change (deposit, field value, vault id, select token, etc.), the callback is called with the new state. \ This is useful for auto-saving the state of the GUI across sessions.")] state_update_callback: Option<js_sys::Function>, -) -> Result<DotrainOrderGui, GuiError> { - Self::new_with_deployment_and_price_callback( - dotrain, - selected_deployment, - state_update_callback, - None, - ) +) -> Result<DotrainOrderGui, GuiError> { + Self::new_with_deployment_and_price_callback( + dotrain, + selected_deployment, + state_update_callback, + price_callback, + ) .await }And add the new parameter:
- state_update_callback: Option<js_sys::Function>, + state_update_callback: Option<js_sys::Function>, + #[wasm_export(param_description = "Optional function for fetching price ratios. \ + Should accept (inputTokenAddress, outputTokenAddress) and return a Promise<string> with the price ratio.")] + price_callback: Option<js_sys::Function>,Also applies to: 1522-1551, 1533-1542
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (9)
-
README.md(1 hunks) -
crates/js_api/src/gui/mod.rs(12 hunks) -
crates/js_api/src/gui/state_management.rs(2 hunks) -
crates/settings/src/gui.rs(2 hunks) -
crates/settings/src/yaml/context.rs(9 hunks) -
docs/price-ratio-functionality.md(1 hunks) -
examples/price-callback-examples.js(1 hunks) -
examples/price-ratio-example.yaml(1 hunks) -
lib/rain.orderbook.interface(1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-07-04T09:02:57.301Z
Learnt from: findolor
PR: rainlanguage/rain.orderbook#1956
File: crates/common/src/fuzz/mod.rs:64-64
Timestamp: 2025-07-04T09:02:57.301Z
Learning: In rainlanguage/rain.orderbook, user findolor prefers to limit type consistency changes to only the parts directly related to the current work scope. For example, when updating chain_id fields from u64 to u32 in fuzz-related code, unrelated files like tauri-app wallet commands can remain as u64 if they serve different purposes and aren't part of the current changes.
Applied to files:
lib/rain.orderbook.interface
📚 Learning: 2025-07-16T10:40:05.717Z
Learnt from: findolor
PR: rainlanguage/rain.orderbook#2000
File: crates/common/src/raindex_client/vaults.rs:183-183
Timestamp: 2025-07-16T10:40:05.717Z
Learning: In the rainlanguage/rain.orderbook codebase, user findolor considers breaking changes from Option<U256> to U256 for required fields like decimals in RaindexVaultToken to be acceptable and safe, even when they affect multiple usage sites across the codebase.
Applied to files:
lib/rain.orderbook.interface
📚 Learning: 2025-06-18T12:54:47.807Z
Learnt from: findolor
PR: rainlanguage/rain.orderbook#1938
File: crates/js_api/src/raindex/orders.rs:1-11
Timestamp: 2025-06-18T12:54:47.807Z
Learning: In the rain.orderbook repository, findolor prefers to keep unused imports in place when they will be used in upcoming follow-up PRs, rather than removing and re-adding them during incremental development.
Applied to files:
lib/rain.orderbook.interface
📚 Learning: 2025-05-19T13:40:56.080Z
Learnt from: findolor
PR: rainlanguage/rain.orderbook#1858
File: crates/subgraph/src/orderbook_client/mod.rs:54-58
Timestamp: 2025-05-19T13:40:56.080Z
Learning: The `wasm_bindgen_utils` crate in the Rain Orderbook project handles conditional compilation for `JsValue` and `JsError` internally, allowing `impl From<Error> for JsValue` to work on non-WASM targets without explicit cfg guards.
Applied to files:
crates/js_api/src/gui/mod.rs
🧬 Code Graph Analysis (3)
crates/settings/src/yaml/context.rs (2)
crates/settings/src/yaml/dotrain.rs (1)
new(79-107)examples/price-callback-examples.js (1)
result(203-212)
crates/js_api/src/gui/state_management.rs (1)
crates/common/src/dotrain_order.rs (1)
dotrain(360-362)
crates/js_api/src/gui/mod.rs (2)
examples/price-callback-examples.js (1)
result(203-212)crates/settings/src/yaml/context.rs (1)
parts(108-111)
🪛 LanguageTool
docs/price-ratio-functionality.md
[grammar] ~1-~1: Use correct spacing
Context: ...e Ratio Functionality in DotrainOrderGui ## Overview The DotrainOrderGui now suppor...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~3-~3: Use correct spacing
Context: ...ionality in DotrainOrderGui ## Overview The DotrainOrderGui now supports dynamic...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~5-~5: Use correct spacing
Context: ...rategies more dynamic and user-friendly. ## Key Features - **Dynamic Price Resoluti...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~7-~7: Use correct spacing
Context: ...amic and user-friendly. ## Key Features - Dynamic Price Resolution: Automaticall...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~9-~9: There might be a mistake here.
Context: ...ch current market prices for token pairs - YAML Integration: Use `${io-ratio(inpu...
(QB_NEW_EN_OTHER)
[grammar] ~12-~12: There might be a mistake here.
Context: ...rations continue to work without changes ## Usage ### JavaScript Integration When ...
(QB_NEW_EN_OTHER)
[grammar] ~14-~14: Use correct spacing
Context: ...ntinue to work without changes ## Usage ### JavaScript Integration When creating a ...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~16-~16: Use correct spacing
Context: ...es ## Usage ### JavaScript Integration When creating a DotrainOrderGui instance...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~18-~18: Use correct spacing
Context: ...ance, provide a price callback function: javascript // Define your price callback function const priceCallback = async (inputTokenAddress, outputTokenAddress) => { try { // Example using SushiSwap API const response = await fetch( `https://api.sushi.com/price/${inputTokenAddress}/${outputTokenAddress}` ); const data = await response.json(); return data.ratio.toString(); } catch (error) { throw new Error(`Failed to fetch price: ${error.message}`); } }; // Create GUI instance with price callback const result = await DotrainOrderGui.newWithDeploymentAndPriceCallback( dotrainYaml, "mainnet-deployment", (serializedState) => { localStorage.setItem('orderState', serializedState); }, priceCallback ); if (result.error) { console.error("Failed to initialize GUI:", result.error.readableMsg); } else { const gui = result.value; // Use the GUI instance... } ### YAML Configuration Examples #### 1. Def...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~53-~53: Use correct spacing
Context: .... } ### YAML Configuration Examples #### 1. Default Field Values yaml deploym...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~55-~55: Use correct spacing
Context: ...n Examples #### 1. Default Field Values yaml deployments: auction-dca: name: Auction DCA Strategy description: Dollar-cost averaging using auctions fields: - binding: initial-io name: Kickoff ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} description: | The initial ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} to kickoff the first auction. This ratio is calculated in the same way as the baseline ratio. It must be greater than the baseline ratio, regardless of what you are selling or buying. default: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} #### 2. Field Names and Descriptions ```yaml...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~73-~73: Use correct spacing
Context: ...`` #### 2. Field Names and Descriptions yaml deployments: limit-order: name: Limit Order description: Deploy a limit order fields: - binding: fixed-io name: ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} description: | Fixed exchange rate (${order.inputs.0.token.symbol} received per 1 ${order.outputs.0.token.symbol} sold). Current market price: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} Set your desired rate above or below the current market price. #### 3. Complex Strategy Example ```yaml dep...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~91-~91: Use correct spacing
Context: ...e. #### 3. Complex Strategy Example yaml deployments: dsf-strategy: name: Dutch Auction Strategy description: Automated Dutch auction with dynamic pricing fields: - binding: start-ratio name: Starting ${order.inputs.0.token.symbol}/${order.outputs.0.token.symbol} Ratio description: | Starting price for the Dutch auction. Current market rate: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} Recommended: Set 5-10% above market rate for better execution. default: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} - binding: end-ratio name: Ending ${order.inputs.0.token.symbol}/${order.outputs.0.token.symbol} Ratio description: | Minimum acceptable price for the Dutch auction. Current market rate: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} Recommended: Set at or slightly below market rate. ``` ## Price Callback Implementation ### Requi...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~119-~119: Use correct spacing
Context: ...e. ``` ## Price Callback Implementation ### Requirements Your price callback functi...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~121-~121: Use correct spacing
Context: ...allback Implementation ### Requirements Your price callback function must: 1. *...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~123-~123: Use correct spacing
Context: ...ents Your price callback function must: 1. Accept two parameters: `inputTokenAddr...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~125-~125: There might be a mistake here.
Context: ... and outputTokenAddress (both strings) 2. Return a Promise: That resolves to a s...
(QB_NEW_EN_OTHER)
[grammar] ~127-~127: There might be a mistake here.
Context: ... meaningful error messages for debugging ### Example Implementations #### Using Sush...
(QB_NEW_EN_OTHER)
[grammar] ~129-~129: Use correct spacing
Context: ...r debugging ### Example Implementations #### Using SushiSwap API ```javascript const...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~131-~131: Use correct spacing
Context: ...mplementations #### Using SushiSwap API javascript const sushiPriceCallback = async (inputTokenAddress, outputTokenAddress) => { const response = await fetch( `https://api.sushi.com/price/${inputTokenAddress}/${outputTokenAddress}` ); if (!response.ok) { throw new Error(`SushiSwap API error: ${response.status}`); } const data = await response.json(); return data.price.toString(); }; #### Using 1inch API ```javascript const one...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~148-~148: Use correct spacing
Context: ...toString(); }; #### Using 1inch API javascript const oneInchPriceCallback = async (inputTokenAddress, outputTokenAddress) => { const response = await fetch( https://api.1inch.io/v5.0/1/quote?fromTokenAddress=${inputTokenAddress}&toTokenAddress=${outputTokenAddress}&amount=1000000000000000000 ); const data = await response.json(); const ratio = data.toTokenAmount / data.fromTokenAmount; return ratio.toString(); }; ``` #### Using Multiple Sources with Fallback ``...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~162-~162: Use correct spacing
Context: ...### Using Multiple Sources with Fallback javascript const robustPriceCallback = async (inputTokenAddress, outputTokenAddress) => { const sources = [ () => fetchFromSushiSwap(inputTokenAddress, outputTokenAddress), () => fetchFromUniswap(inputTokenAddress, outputTokenAddress), () => fetchFromOneInch(inputTokenAddress, outputTokenAddress), ]; for (const source of sources) { try { return await source(); } catch (error) { console.warn(`Price source failed: ${error.message}`); } } throw new Error('All price sources failed'); }; ## Error Handling The system provides comp...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~184-~184: Use correct spacing
Context: ...rces failed'); }; ``` ## Error Handling The system provides comprehensive error ...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~186-~186: Use correct spacing
Context: ...m provides comprehensive error handling: ### Common Error Types 1. **No Price Callba...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~188-~188: Use correct spacing
Context: ... error handling: ### Common Error Types 1. No Price Callback: When `${io-ratio(.....
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~190-~190: There might be a mistake here.
Context: ...)}` is used but no callback was provided 2. Invalid Token Addresses: When token ad...
(QB_NEW_EN)
[grammar] ~191-~191: There might be a mistake here.
Context: ...n token addresses are empty or malformed 3. Network Errors: When the price callbac...
(QB_NEW_EN)
[grammar] ~192-~192: There might be a mistake here.
Context: ...ice callback fails due to network issues 4. Invalid Response: When the callback re...
(QB_NEW_EN)
[grammar] ~193-~193: There might be a mistake here.
Context: ...lback returns non-string or empty values 5. Invalid Expression: When the `io-ratio...
(QB_NEW_EN)
[grammar] ~194-~194: Use correct spacing
Context: ... When the io-ratio syntax is incorrect ### Error Messages All errors include user-...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~196-~196: Use correct spacing
Context: ... syntax is incorrect ### Error Messages All errors include user-friendly message...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~198-~198: Use correct spacing
Context: ...l errors include user-friendly messages: javascript // Example error handling try { const gui = await DotrainOrderGui.newWithDeploymentAndPriceCallback( yaml, deployment, stateCallback, priceCallback ); } catch (error) { if (error.message.includes('Price callback')) { console.error('Price fetching failed:', error.message); // Show user-friendly message about network connectivity } else { console.error('GUI initialization failed:', error.message); } } ## Best Practices ### 1. Robust Price Call...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~216-~216: Use correct spacing
Context: ...r.message); } } ``` ## Best Practices ### 1. Robust Price Callbacks - Implement t...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~218-~218: Use correct spacing
Context: ...Practices ### 1. Robust Price Callbacks - Implement timeout handling - Use multipl...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~220-~220: There might be a mistake here.
Context: ... Callbacks - Implement timeout handling - Use multiple price sources with fallback...
(QB_NEW_EN_OTHER)
[grammar] ~223-~223: There might be a mistake here.
Context: ... token addresses before making API calls ### 2. YAML Configuration - Always provide ...
(QB_NEW_EN_OTHER)
[grammar] ~225-~225: Use correct spacing
Context: ...ing API calls ### 2. YAML Configuration - Always provide fallback values for criti...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~227-~227: There might be a mistake here.
Context: ...vide fallback values for critical fields - Use descriptive field names and descript...
(QB_NEW_EN_OTHER)
[grammar] ~230-~230: There might be a mistake here.
Context: ...imiting when using multiple price ratios ### 3. User Experience - Show loading state...
(QB_NEW_EN_OTHER)
[grammar] ~232-~232: Use correct spacing
Context: ...ple price ratios ### 3. User Experience - Show loading states while fetching price...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~234-~234: There might be a mistake here.
Context: ...how loading states while fetching prices - Provide clear error messages for price f...
(QB_NEW_EN_OTHER)
[grammar] ~235-~235: There might be a mistake here.
Context: ...- Provide clear error messages for price fetching failures - Allow users to manua...
(QB_NEW_EN_OTHER)
[grammar] ~235-~235: There might be a mistake here.
Context: ...ror messages for price fetching failures - Allow users to manually override price r...
(QB_NEW_EN_OTHER)
[grammar] ~237-~237: There might be a mistake here.
Context: ...ful price fetches to improve performance ## Migration Guide ### From Hardcoded Valu...
(QB_NEW_EN_OTHER)
[grammar] ~239-~239: Use correct spacing
Context: ... improve performance ## Migration Guide ### From Hardcoded Values Before: ```ya...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~241-~241: Use correct spacing
Context: ...gration Guide ### From Hardcoded Values Before: ```yaml - binding: initial-io ...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~243-~243: Use correct spacing
Context: ...de ### From Hardcoded Values Before: yaml - binding: initial-io name: Initial USDC per WETH default: "1800.0" After: ```yaml - binding: initial-io ...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~250-~250: Use correct spacing
Context: ...r WETH default: "1800.0" **After:**yaml - binding: initial-io name: Initial ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} description: Current market price: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} default: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} ``` ### Updating JavaScript Code Before: ``...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~258-~258: Use correct spacing
Context: ...ress)} ### Updating JavaScript Code **Before:**javascript const gui = awai...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~260-~260: Use correct spacing
Context: ... ### Updating JavaScript Code Before: javascript const gui = await DotrainOrderGui.newWithDeployment(yaml, deployment, stateCallback); After: ```javascript const gui = await...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~265-~265: Use correct spacing
Context: ...eployment, stateCallback); **After:**javascript const gui = await DotrainOrderGui.newWithDeploymentAndPriceCallback( yaml, deployment, stateCallback, priceCallback ); ``` ## Troubleshooting ### Common Issues 1. *...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~272-~272: Use correct spacing
Context: ...priceCallback ); ``` ## Troubleshooting ### Common Issues 1. **"Price callback not ...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~274-~274: Use correct spacing
Context: ...` ## Troubleshooting ### Common Issues 1. "Price callback not available": Ensure...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~276-~276: There might be a mistake here.
Context: ...hDeploymentAndPriceCallbackinstead ofnewWithDeployment` 2. "Invalid io-ratio expression": Check t...
(QB_NEW_EN)
[grammar] ~277-~277: There might be a mistake here.
Context: ...valid io-ratio expression"**: Check the syntax: ${io-ratio(input_path, output_path)} 3. "Price callback failed": Check network...
(QB_NEW_EN_OTHER)
[grammar] ~278-~278: There might be a mistake here.
Context: ...k network connectivity and API endpoints 4. Empty price values: Ensure your callba...
(QB_NEW_EN_OTHER)
[grammar] ~279-~279: There might be a mistake here.
Context: ...your callback returns a non-empty string ### Debug Tips - Test your price callback f...
(QB_NEW_EN_OTHER)
[grammar] ~281-~281: Use correct spacing
Context: ...turns a non-empty string ### Debug Tips - Test your price callback function indepe...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~283-~283: There might be a mistake here.
Context: ...ur price callback function independently - Use browser developer tools to inspect n...
(QB_NEW_EN_OTHER)
[grammar] ~285-~285: There might be a mistake here.
Context: ...per tools to inspect network requests - Check console for detailed error messages - V...
(QB_NEW_EN)
[grammar] ~285-~285: There might be a mistake here.
Context: ...heck console for detailed error messages - Validate token addresses are correct for...
(QB_NEW_EN_OTHER)
[grammar] ~286-~286: Insert the missing word
Context: ...ror messages - Validate token addresses are correct format (0x...)
(QB_NEW_EN_OTHER_ERROR_IDS_32)
[grammar] ~286-~286: There might be a mistake here.
Context: ...ken addresses are correct format (0x...)
(QB_NEW_EN_OTHER)
README.md
[grammar] ~26-~26: Use correct spacing
Context: ... frontends for Raindex. ## New Features ### 🎯 Dynamic Price Ratio Support The Dotr...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~28-~28: There might be a mistake here.
Context: ...ures ### 🎯 Dynamic Price Ratio Support The DotrainOrderGui now supports dynamic ...
(QB_NEW_EN)
[grammar] ~30-~30: Use correct spacing
Context: ...market prices without hardcoding values. Key Benefits: - **Automatic Price Fetc...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~32-~32: There might be a mistake here.
Context: ...hout hardcoding values. Key Benefits: - Automatic Price Fetching: Strategies c...
(QB_NEW_EN)
[grammar] ~33-~33: There might be a mistake here.
Context: ... automatically use current market prices - User-Friendly Defaults: No more manual...
(QB_NEW_EN_OTHER)
[grammar] ~34-~34: There might be a mistake here.
Context: ...: No more manual price lookups for users - Flexible Integration: Works with any p...
(QB_NEW_EN_OTHER)
[grammar] ~35-~35: There might be a mistake here.
Context: ... API (SushiSwap, 1inch, CoinGecko, etc.) - Error Handling: Comprehensive error ha...
(QB_NEW_EN_OTHER)
[grammar] ~36-~36: Use correct spacing
Context: ...ling for network issues and invalid data Quick Example: ```yaml fields: - bin...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~38-~38: Use correct spacing
Context: ...ssues and invalid data Quick Example: yaml fields: - binding: initial-io name: Kickoff ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} description: Current market price: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} default: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} javascript const priceCallback = async (inputTokenAddress, outputTokenAddress) => { const response = await fetch(`/api/price/${inputTokenAddress}/${outputTokenAddress}`); return (await response.json()).ratio.toString(); }; const gui = await DotrainOrderGui.newWithDeploymentAndPriceCallback( yaml, deployment, stateCallback, priceCallback ); 📚 Learn More: See [docs/price-ratio...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~58-~58: There might be a mistake here.
Context: ...(examples/) for implementation examples. ## Setup for local development ### Environm...
(QB_NEW_EN)
🪛 markdownlint-cli2 (0.17.2)
docs/price-ratio-functionality.md
244-244: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
251-251: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
261-261: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
266-266: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
README.md
33-33: Lists should be surrounded by blank lines
(MD032, blanks-around-lists)
39-39: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🪛 YAMLlint (1.37.1)
examples/price-ratio-example.yaml
[error] 47-47: trailing spaces
(trailing-spaces)
[error] 49-49: trailing spaces
(trailing-spaces)
[error] 50-50: trailing spaces
(trailing-spaces)
[error] 52-52: trailing spaces
(trailing-spaces)
[error] 60-60: trailing spaces
(trailing-spaces)
[error] 62-62: trailing spaces
(trailing-spaces)
[error] 83-83: trailing spaces
(trailing-spaces)
[error] 85-85: trailing spaces
(trailing-spaces)
[error] 87-87: trailing spaces
(trailing-spaces)
[error] 94-94: trailing spaces
(trailing-spaces)
[error] 95-95: trailing spaces
(trailing-spaces)
[error] 100-100: trailing spaces
(trailing-spaces)
[error] 110-110: trailing spaces
(trailing-spaces)
[error] 112-112: trailing spaces
(trailing-spaces)
[error] 120-120: trailing spaces
(trailing-spaces)
[error] 122-122: trailing spaces
(trailing-spaces)
[error] 130-130: trailing spaces
(trailing-spaces)
[error] 131-131: trailing spaces
(trailing-spaces)
[error] 147-147: trailing spaces
(trailing-spaces)
[error] 149-149: trailing spaces
(trailing-spaces)
[error] 157-157: trailing spaces
(trailing-spaces)
[error] 158-158: trailing spaces
(trailing-spaces)
[error] 174-174: trailing spaces
(trailing-spaces)
[error] 175-175: trailing spaces
(trailing-spaces)
[error] 182-182: trailing spaces
(trailing-spaces)
[error] 184-184: trailing spaces
(trailing-spaces)
[error] 193-193: trailing spaces
(trailing-spaces)
[error] 194-194: trailing spaces
(trailing-spaces)
[error] 202-202: trailing spaces
(trailing-spaces)
[error] 203-203: trailing spaces
(trailing-spaces)
[error] 205-205: trailing spaces
(trailing-spaces)
🔇 Additional comments (9)
lib/rain.orderbook.interface (2)
1-1: Submodule pointer bump only — LGTMThis is a straightforward submodule commit update with no direct code changes in the superproject.
1-1: Confirm the submodule bump is intentional and aligned with the ${io-ratio(...)} workI checked the lib/rain.orderbook.interface submodule and the referenced commit 9d6d8cc:
- .gitmodules contains lib/rain.orderbook.interface (submodule present).
- git submodule status shows lib/rain.orderbook.interface at 9d6d8cc2405031ff31ee2db75b15b74ef968c88c.
- GitHub commit: rainlanguage/rain.raindex.interface@9d6d8cc — Merge PR #38, message "bump i9r int". Files changed: lib/rain.interpreter.interface (lib/rain.math.float changed +1 −1).
I did not see explicit interface/docs changes for the ${io-ratio(...)} feature in that commit. Please confirm this bump was intentional and that the required interface/docs updates for the price-ratio/io-ratio work are included (or point me to the expected commit/tag). I can also cross-check the submodule commit vs the expected tag/branch and summarize relevant changes for the PR release notes if you’d like.
README.md (1)
53-56: API usage looks consistent with the new constructorThe example correctly uses DotrainOrderGui.newWithDeploymentAndPriceCallback and shows the expected priceCallback shape returning a string. Good addition.
crates/settings/src/yaml/context.rs (2)
23-29: Context now carries a price callback — good extension pointAdding PriceCallback and storing it in Context enables downstream layers (JS API) to drive dynamic resolution. The struct is minimal and forward-compatible.
301-305: LGTM: Early detection of io-ratio(...) expressionsShort-circuiting io-ratio(...) before the dot-path resolution is a clean way to extend the grammar without impacting existing paths.
crates/js_api/src/gui/state_management.rs (2)
220-229: Good: Backward-compatible delegating constructor preserved.new_from_state correctly delegates to new_from_state_with_price_callback with None, preserving the old API while enabling the new one.
255-297: Wiring price_callback during state restoration looks correct.The new
new_from_state_with_price_callbackaccepts an optional JS function and stores it on the restored DotrainOrderGui instance. This aligns with the new constructor path in mod.rs.crates/js_api/src/gui/mod.rs (2)
149-163: Docs for newWithDeployment are now accurate, provided the signature is updated.Once the extra parameter is added per earlier comment, the example passing priceCallback here will be correct and helpful.
1555-1570: Update tests to expect Err when PRICE_RATIO_PLACEHOLDER is present and no price callback is providedCurrent behavior (verified): resolve_price_ratios returns the input unchanged when self.price_callback is None and the tests reflect this lenient behavior. If you change resolve_price_ratios to return an error when placeholders exist and no callback is provided, update tests as follows.
File: crates/js_api/src/gui/mod.rs — test_price_ratio_placeholder_resolution (≈ lines 1555–1570)
- Replace:
let input_with_placeholder = "Price: PRICE_RATIO_PLACEHOLDER:0x123:0x456"; let result = gui.resolve_price_ratios(input_with_placeholder).await; assert!(result.is_ok()); assert_eq!(result.unwrap(), input_with_placeholder);- With:
let input_with_placeholder = "Price: PRICE_RATIO_PLACEHOLDER:0x123:0x456"; let result = gui.resolve_price_ratios(input_with_placeholder).await; assert!(result.is_err());File: crates/js_api/src/gui/mod.rs — test_price_ratio_error_handling (≈ lines 1588–1601)
- No change required; this test already asserts Err for malformed/empty placeholders.
| /// Resolves price ratio placeholders in a string by calling the JavaScript price callback. | ||
| /// | ||
| /// This method looks for PRICE_RATIO_PLACEHOLDER patterns and replaces them with actual | ||
| /// price data fetched via the JavaScript callback function. | ||
| pub async fn resolve_price_ratios(&self, input: &str) -> Result<String, GuiError> { | ||
| if let Some(price_callback) = &self.price_callback { | ||
| let mut result = input.to_string(); | ||
|
|
||
| // Find all PRICE_RATIO_PLACEHOLDER patterns | ||
| while let Some(start) = result.find("PRICE_RATIO_PLACEHOLDER:") { | ||
| if let Some(end) = result[start..].find(' ') { | ||
| let end = start + end; | ||
| let placeholder = &result[start..end]; | ||
|
|
||
| // Extract input and output addresses from placeholder | ||
| let parts: Vec<&str> = placeholder.split(':').collect(); | ||
| if parts.len() == 3 { | ||
| let input_address = parts[1]; | ||
| let output_address = parts[2]; | ||
|
|
||
| // Validate addresses before calling callback | ||
| if input_address.is_empty() || output_address.is_empty() { | ||
| return Err(GuiError::JsError( | ||
| "Invalid token addresses in price ratio expression".to_string(), | ||
| )); | ||
| } | ||
|
|
||
| // Call the JavaScript price callback with error handling | ||
| match self | ||
| .call_price_callback(input_address, output_address) | ||
| .await | ||
| { | ||
| Ok(price_ratio) => { | ||
| // Validate the returned price ratio | ||
| if price_ratio.is_empty() { | ||
| return Err(GuiError::JsError( | ||
| "Price callback returned empty value".to_string(), | ||
| )); | ||
| } | ||
|
|
||
| // Replace the placeholder with the actual price | ||
| result.replace_range(start..end, &price_ratio); | ||
| } | ||
| Err(e) => { | ||
| // Return the error instead of replacing with error text | ||
| return Err(e); | ||
| } | ||
| } | ||
| } else { | ||
| return Err(GuiError::JsError(format!( | ||
| "Invalid price ratio placeholder format: {}", | ||
| placeholder | ||
| ))); | ||
| } | ||
| } else { | ||
| // No space found, placeholder might be at the end of string | ||
| let placeholder = &result[start..]; | ||
| let parts: Vec<&str> = placeholder.split(':').collect(); | ||
| if parts.len() == 3 { | ||
| let input_address = parts[1]; | ||
| let output_address = parts[2]; | ||
|
|
||
| // Validate addresses | ||
| if input_address.is_empty() || output_address.is_empty() { | ||
| return Err(GuiError::JsError( | ||
| "Invalid token addresses in price ratio expression".to_string(), | ||
| )); | ||
| } | ||
|
|
||
| match self | ||
| .call_price_callback(input_address, output_address) | ||
| .await | ||
| { | ||
| Ok(price_ratio) => { | ||
| if price_ratio.is_empty() { | ||
| return Err(GuiError::JsError( | ||
| "Price callback returned empty value".to_string(), | ||
| )); | ||
| } | ||
| result.replace_range(start.., &price_ratio); | ||
| } | ||
| Err(e) => return Err(e), | ||
| } | ||
| } else { | ||
| return Err(GuiError::JsError(format!( | ||
| "Invalid price ratio placeholder format: {}", | ||
| placeholder | ||
| ))); | ||
| } | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| Ok(result) | ||
| } else { | ||
| // No price callback available, return input as-is | ||
| Ok(input.to_string()) | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Handle placeholders when no price callback is provided and make parsing more robust.
Current behavior silently returns the input unchanged when no callback is provided, even if placeholders exist. Docs specify an error should be raised in that case. Also, parsing assumes placeholders end at a space, which is brittle (punctuation, line breaks, or end-of-string aren’t handled well).
- If
PRICE_RATIO_PLACEHOLDER:is present and no callback: return a meaningful error. - Improve end-of-placeholder detection by stopping at whitespace or common punctuation.
Apply this diff:
- pub async fn resolve_price_ratios(&self, input: &str) -> Result<String, GuiError> {
- if let Some(price_callback) = &self.price_callback {
+ pub async fn resolve_price_ratios(&self, input: &str) -> Result<String, GuiError> {
+ let sentinel = "PRICE_RATIO_PLACEHOLDER:";
+ let has_placeholder = input.contains(sentinel);
+ if self.price_callback.is_none() {
+ return if has_placeholder {
+ Err(GuiError::JsError(
+ "Price callback not provided but price ratio placeholder found. \
+ Use newWithDeploymentAndPriceCallback or newFromStateWithPriceCallback."
+ .to_string(),
+ ))
+ } else {
+ Ok(input.to_string())
+ };
+ }
+ if let Some(_cb) = &self.price_callback {
let mut result = input.to_string();
// Find all PRICE_RATIO_PLACEHOLDER patterns
- while let Some(start) = result.find("PRICE_RATIO_PLACEHOLDER:") {
- if let Some(end) = result[start..].find(' ') {
- let end = start + end;
+ while let Some(start) = result.find(sentinel) {
+ // Find the end by scanning until whitespace or common punctuation
+ let rel_end = result[start..]
+ .char_indices()
+ .find(|(_, ch)| ch.is_whitespace() || matches!(ch, ')' | ']' | '}' | ',' | ';'))
+ .map(|(i, _)| i);
+ if let Some(rel_end) = rel_end {
+ let end = start + rel_end;
let placeholder = &result[start..end];
// Extract input and output addresses from placeholder
let parts: Vec<&str> = placeholder.split(':').collect();
if parts.len() == 3 {
let input_address = parts[1];
let output_address = parts[2];
// Validate addresses before calling callback
if input_address.is_empty() || output_address.is_empty() {
return Err(GuiError::JsError(
"Invalid token addresses in price ratio expression".to_string(),
));
}
// Call the JavaScript price callback with error handling
- match self
+ match self
.call_price_callback(input_address, output_address)
.await
{
Ok(price_ratio) => {
// Validate the returned price ratio
if price_ratio.is_empty() {
return Err(GuiError::JsError(
"Price callback returned empty value".to_string(),
));
}
// Replace the placeholder with the actual price
result.replace_range(start..end, &price_ratio);
}
Err(e) => {
// Return the error instead of replacing with error text
return Err(e);
}
}
} else {
return Err(GuiError::JsError(format!(
"Invalid price ratio placeholder format: {}",
placeholder
)));
}
} else {
- // No space found, placeholder might be at the end of string
+ // Placeholder extends to end of string
let placeholder = &result[start..];
let parts: Vec<&str> = placeholder.split(':').collect();
if parts.len() == 3 {
let input_address = parts[1];
let output_address = parts[2];
// Validate addresses
if input_address.is_empty() || output_address.is_empty() {
return Err(GuiError::JsError(
"Invalid token addresses in price ratio expression".to_string(),
));
}
match self
.call_price_callback(input_address, output_address)
.await
{
Ok(price_ratio) => {
if price_ratio.is_empty() {
return Err(GuiError::JsError(
"Price callback returned empty value".to_string(),
));
}
result.replace_range(start.., &price_ratio);
}
Err(e) => return Err(e),
}
} else {
return Err(GuiError::JsError(format!(
"Invalid price ratio placeholder format: {}",
placeholder
)));
}
- break;
+ break;
}
}
Ok(result)
- } else {
- // No price callback available, return input as-is
- Ok(input.to_string())
- }
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /// Resolves price ratio placeholders in a string by calling the JavaScript price callback. | |
| /// | |
| /// This method looks for PRICE_RATIO_PLACEHOLDER patterns and replaces them with actual | |
| /// price data fetched via the JavaScript callback function. | |
| pub async fn resolve_price_ratios(&self, input: &str) -> Result<String, GuiError> { | |
| if let Some(price_callback) = &self.price_callback { | |
| let mut result = input.to_string(); | |
| // Find all PRICE_RATIO_PLACEHOLDER patterns | |
| while let Some(start) = result.find("PRICE_RATIO_PLACEHOLDER:") { | |
| if let Some(end) = result[start..].find(' ') { | |
| let end = start + end; | |
| let placeholder = &result[start..end]; | |
| // Extract input and output addresses from placeholder | |
| let parts: Vec<&str> = placeholder.split(':').collect(); | |
| if parts.len() == 3 { | |
| let input_address = parts[1]; | |
| let output_address = parts[2]; | |
| // Validate addresses before calling callback | |
| if input_address.is_empty() || output_address.is_empty() { | |
| return Err(GuiError::JsError( | |
| "Invalid token addresses in price ratio expression".to_string(), | |
| )); | |
| } | |
| // Call the JavaScript price callback with error handling | |
| match self | |
| .call_price_callback(input_address, output_address) | |
| .await | |
| { | |
| Ok(price_ratio) => { | |
| // Validate the returned price ratio | |
| if price_ratio.is_empty() { | |
| return Err(GuiError::JsError( | |
| "Price callback returned empty value".to_string(), | |
| )); | |
| } | |
| // Replace the placeholder with the actual price | |
| result.replace_range(start..end, &price_ratio); | |
| } | |
| Err(e) => { | |
| // Return the error instead of replacing with error text | |
| return Err(e); | |
| } | |
| } | |
| } else { | |
| return Err(GuiError::JsError(format!( | |
| "Invalid price ratio placeholder format: {}", | |
| placeholder | |
| ))); | |
| } | |
| } else { | |
| // No space found, placeholder might be at the end of string | |
| let placeholder = &result[start..]; | |
| let parts: Vec<&str> = placeholder.split(':').collect(); | |
| if parts.len() == 3 { | |
| let input_address = parts[1]; | |
| let output_address = parts[2]; | |
| // Validate addresses | |
| if input_address.is_empty() || output_address.is_empty() { | |
| return Err(GuiError::JsError( | |
| "Invalid token addresses in price ratio expression".to_string(), | |
| )); | |
| } | |
| match self | |
| .call_price_callback(input_address, output_address) | |
| .await | |
| { | |
| Ok(price_ratio) => { | |
| if price_ratio.is_empty() { | |
| return Err(GuiError::JsError( | |
| "Price callback returned empty value".to_string(), | |
| )); | |
| } | |
| result.replace_range(start.., &price_ratio); | |
| } | |
| Err(e) => return Err(e), | |
| } | |
| } else { | |
| return Err(GuiError::JsError(format!( | |
| "Invalid price ratio placeholder format: {}", | |
| placeholder | |
| ))); | |
| } | |
| break; | |
| } | |
| } | |
| Ok(result) | |
| } else { | |
| // No price callback available, return input as-is | |
| Ok(input.to_string()) | |
| } | |
| } | |
| /// Resolves price ratio placeholders in a string by calling the JavaScript price callback. | |
| /// | |
| /// This method looks for PRICE_RATIO_PLACEHOLDER patterns and replaces them with actual | |
| /// price data fetched via the JavaScript callback function. | |
| pub async fn resolve_price_ratios(&self, input: &str) -> Result<String, GuiError> { | |
| let sentinel = "PRICE_RATIO_PLACEHOLDER:"; | |
| let has_placeholder = input.contains(sentinel); | |
| if self.price_callback.is_none() { | |
| return if has_placeholder { | |
| Err(GuiError::JsError( | |
| "Price callback not provided but price ratio placeholder found. \ | |
| Use newWithDeploymentAndPriceCallback or newFromStateWithPriceCallback." | |
| .to_string(), | |
| )) | |
| } else { | |
| Ok(input.to_string()) | |
| }; | |
| } | |
| if let Some(_cb) = &self.price_callback { | |
| let mut result = input.to_string(); | |
| // Find all PRICE_RATIO_PLACEHOLDER patterns | |
| while let Some(start) = result.find(sentinel) { | |
| // Find the end by scanning until whitespace or common punctuation | |
| let rel_end = result[start..] | |
| .char_indices() | |
| .find(|(_, ch)| ch.is_whitespace() || matches!(ch, ')' | ']' | '}' | ',' | ';')) | |
| .map(|(i, _)| i); | |
| if let Some(rel_end) = rel_end { | |
| let end = start + rel_end; | |
| let placeholder = &result[start..end]; | |
| // Extract input and output addresses from placeholder | |
| let parts: Vec<&str> = placeholder.split(':').collect(); | |
| if parts.len() == 3 { | |
| let input_address = parts[1]; | |
| let output_address = parts[2]; | |
| // Validate addresses before calling callback | |
| if input_address.is_empty() || output_address.is_empty() { | |
| return Err(GuiError::JsError( | |
| "Invalid token addresses in price ratio expression".to_string(), | |
| )); | |
| } | |
| // Call the JavaScript price callback with error handling | |
| match self | |
| .call_price_callback(input_address, output_address) | |
| .await | |
| { | |
| Ok(price_ratio) => { | |
| // Validate the returned price ratio | |
| if price_ratio.is_empty() { | |
| return Err(GuiError::JsError( | |
| "Price callback returned empty value".to_string(), | |
| )); | |
| } | |
| // Replace the placeholder with the actual price | |
| result.replace_range(start..end, &price_ratio); | |
| } | |
| Err(e) => { | |
| // Return the error instead of replacing with error text | |
| return Err(e); | |
| } | |
| } | |
| } else { | |
| return Err(GuiError::JsError(format!( | |
| "Invalid price ratio placeholder format: {}", | |
| placeholder | |
| ))); | |
| } | |
| } else { | |
| // Placeholder extends to end of string | |
| let placeholder = &result[start..]; | |
| let parts: Vec<&str> = placeholder.split(':').collect(); | |
| if parts.len() == 3 { | |
| let input_address = parts[1]; | |
| let output_address = parts[2]; | |
| // Validate addresses | |
| if input_address.is_empty() || output_address.is_empty() { | |
| return Err(GuiError::JsError( | |
| "Invalid token addresses in price ratio expression".to_string(), | |
| )); | |
| } | |
| match self | |
| .call_price_callback(input_address, output_address) | |
| .await | |
| { | |
| Ok(price_ratio) => { | |
| if price_ratio.is_empty() { | |
| return Err(GuiError::JsError( | |
| "Price callback returned empty value".to_string(), | |
| )); | |
| } | |
| result.replace_range(start.., &price_ratio); | |
| } | |
| Err(e) => return Err(e), | |
| } | |
| } else { | |
| return Err(GuiError::JsError(format!( | |
| "Invalid price ratio placeholder format: {}", | |
| placeholder | |
| ))); | |
| } | |
| break; | |
| } | |
| } | |
| Ok(result) | |
| } | |
| } |
| /// Calls the JavaScript price callback function to fetch a price ratio. | ||
| /// | ||
| /// This method handles all the complexity of calling JavaScript from Rust, including: | ||
| /// - Parameter validation | ||
| /// - Promise handling | ||
| /// - Error conversion and user-friendly messages | ||
| /// - Timeout handling (if the promise never resolves) | ||
| async fn call_price_callback( |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Docstring claims timeout handling that isn’t implemented.
The method comment lists “Timeout handling (if the promise never resolves)”, but there is no timeout. Either implement a timeout or remove the claim.
Minimal fix (remove the bullet):
/// - Parameter validation
/// - Promise handling
/// - Error conversion and user-friendly messages
- /// - Timeout handling (if the promise never resolves)Optional: I can add a Promise.race-based timeout using gloo_timers for WASM targets if desired.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /// Calls the JavaScript price callback function to fetch a price ratio. | |
| /// | |
| /// This method handles all the complexity of calling JavaScript from Rust, including: | |
| /// - Parameter validation | |
| /// - Promise handling | |
| /// - Error conversion and user-friendly messages | |
| /// - Timeout handling (if the promise never resolves) | |
| async fn call_price_callback( | |
| /// Calls the JavaScript price callback function to fetch a price ratio. | |
| /// | |
| /// This method handles all the complexity of calling JavaScript from Rust, including: | |
| /// - Parameter validation | |
| /// - Promise handling | |
| /// - Error conversion and user-friendly messages | |
| async fn call_price_callback( |
🤖 Prompt for AI Agents
In crates/js_api/src/gui/mod.rs around lines 786 to 793, the docstring
incorrectly claims "Timeout handling (if the promise never resolves)" though the
function does not implement any timeout; remove that bullet from the method
comment to keep docs accurate, or if you prefer to add timeout behavior
implement a Promise.race-based timeout for the JS Promise (e.g., use gloo_timers
for WASM to create a timeout future that races the callback promise and
rejects/returns an error on timeout), convert the timeout error into the same
Rust error path, and update the docstring to reflect the actual timeout
semantics.
| // Convert to Promise and await with error handling | ||
| let future = JsFuture::from(js_sys::Promise::from(promise)); | ||
| let result = future.await.map_err(|e| { | ||
| let error_msg = if let Some(js_error) = e.dyn_ref::<js_sys::Error>() { | ||
| format!("Price callback promise rejected: {}", js_error.message()) | ||
| } else if let Some(error_str) = e.as_string() { | ||
| format!("Price callback failed: {}", error_str) | ||
| } else { | ||
| format!("Price callback promise failed with unknown error: {:?}", e) | ||
| }; | ||
| GuiError::JsError(error_msg) | ||
| })?; | ||
|
|
||
| // Convert result to string with validation | ||
| let price_str = result.as_string().ok_or_else(|| { | ||
| GuiError::JsError(format!( | ||
| "Price callback must return a string, got: {:?}", | ||
| result | ||
| )) | ||
| })?; | ||
|
|
||
| // Validate the returned price string | ||
| if price_str.trim().is_empty() { | ||
| return Err(GuiError::JsError( | ||
| "Price callback returned empty or whitespace-only string".to_string(), | ||
| )); | ||
| } | ||
|
|
||
| // Basic validation that the result looks like a number | ||
| if price_str.parse::<f64>().is_err() { | ||
| return Err(GuiError::JsError(format!( | ||
| "Price callback returned invalid number format: '{}'", | ||
| price_str | ||
| ))); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Validation: enforcing string return type is good; consider accepting numbers too.
Today the callback must resolve to a string. Some users may naturally return a number. Consider accepting numbers by converting via as_f64 or to_string() if the value is numeric, then applying the same validation.
If you want, I can propose a patch that accepts both string and number returns with strict validation.
🤖 Prompt for AI Agents
In crates/js_api/src/gui/mod.rs around lines 833 to 867, the code only accepts a
JS string result from the price callback; update it to also accept numeric JS
values: first try result.as_string(), if Some use that; else if let Some(n) =
result.as_f64() convert n.to_string() to price_str; otherwise return the
existing JsError about expected string/number. After converting, run the same
trim/empty and parse::<f64>() validations as currently implemented. Keep the
existing Promise error mapping unchanged.
| /// Gets the current deployment with price ratio resolution applied to field definitions. | ||
| /// | ||
| /// This method processes the deployment configuration and resolves any price ratio | ||
| /// placeholders in field names, descriptions, and default values. | ||
| pub async fn get_current_deployment_with_price_resolution( | ||
| &self, | ||
| ) -> Result<GuiDeploymentCfg, GuiError> { | ||
| let mut deployment = self.get_current_deployment()?; | ||
|
|
||
| // Process each field to resolve price ratios | ||
| for field in &mut deployment.fields { | ||
| // Resolve price ratios in field name | ||
| field.name = self.resolve_price_ratios(&field.name).await?; | ||
|
|
||
| // Resolve price ratios in field description | ||
| if let Some(description) = &field.description { | ||
| field.description = Some(self.resolve_price_ratios(description).await?); | ||
| } | ||
|
|
||
| // Resolve price ratios in default value | ||
| if let Some(default) = &field.default { | ||
| field.default = Some(self.resolve_price_ratios(default).await?); | ||
| } | ||
| } | ||
|
|
||
| Ok(deployment) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Export get_current_deployment_with_price_resolution to JS.
This method is currently not wasm-exported, so UIs cannot consume the resolved configuration. Either export it or integrate price resolution into existing getters.
Apply this diff:
- pub async fn get_current_deployment_with_price_resolution(
+ #[wasm_export(
+ js_name = "getCurrentDeploymentWithPriceResolution",
+ unchecked_return_type = "GuiDeploymentCfg",
+ return_description = "Active deployment with price-ratio placeholders resolved"
+ )]
+ pub async fn get_current_deployment_with_price_resolution(
&self,
) -> Result<GuiDeploymentCfg, GuiError> {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /// Gets the current deployment with price ratio resolution applied to field definitions. | |
| /// | |
| /// This method processes the deployment configuration and resolves any price ratio | |
| /// placeholders in field names, descriptions, and default values. | |
| pub async fn get_current_deployment_with_price_resolution( | |
| &self, | |
| ) -> Result<GuiDeploymentCfg, GuiError> { | |
| let mut deployment = self.get_current_deployment()?; | |
| // Process each field to resolve price ratios | |
| for field in &mut deployment.fields { | |
| // Resolve price ratios in field name | |
| field.name = self.resolve_price_ratios(&field.name).await?; | |
| // Resolve price ratios in field description | |
| if let Some(description) = &field.description { | |
| field.description = Some(self.resolve_price_ratios(description).await?); | |
| } | |
| // Resolve price ratios in default value | |
| if let Some(default) = &field.default { | |
| field.default = Some(self.resolve_price_ratios(default).await?); | |
| } | |
| } | |
| Ok(deployment) | |
| } | |
| /// Gets the current deployment with price ratio resolution applied to field definitions. | |
| /// | |
| /// This method processes the deployment configuration and resolves any price ratio | |
| /// placeholders in field names, descriptions, and default values. | |
| #[wasm_export( | |
| js_name = "getCurrentDeploymentWithPriceResolution", | |
| unchecked_return_type = "GuiDeploymentCfg", | |
| return_description = "Active deployment with price-ratio placeholders resolved" | |
| )] | |
| pub async fn get_current_deployment_with_price_resolution( | |
| &self, | |
| ) -> Result<GuiDeploymentCfg, GuiError> { | |
| let mut deployment = self.get_current_deployment()?; | |
| // Process each field to resolve price ratios | |
| for field in &mut deployment.fields { | |
| // Resolve price ratios in field name | |
| field.name = self.resolve_price_ratios(&field.name).await?; | |
| // Resolve price ratios in field description | |
| if let Some(description) = &field.description { | |
| field.description = Some(self.resolve_price_ratios(description).await?); | |
| } | |
| // Resolve price ratios in default value | |
| if let Some(default) = &field.default { | |
| field.default = Some(self.resolve_price_ratios(default).await?); | |
| } | |
| } | |
| Ok(deployment) | |
| } |
🤖 Prompt for AI Agents
In crates/js_api/src/gui/mod.rs around lines 877 to 903, the async method
get_current_deployment_with_price_resolution is not exported to JS; add a
wasm-bindgen wrapper that returns a JS Promise/JsValue so the UI can call it.
Create a new #[wasm_bindgen] public function (or method on the exported JS
class) that calls the existing async Rust method via
wasm_bindgen_futures::future_to_promise (or by converting the async Result to a
JsValue with serde_wasm_bindgen), returning a Promise and mapping Rust errors to
JsValue/Error; do not change the existing internal logic—only add the small
exported wrapper to bridge async Result<GuiDeploymentCfg, GuiError> into a
JS-friendly Promise/JsValue.
| ContextError::NoPriceCallback => | ||
| "Price callback is not available. Please provide a price callback function when creating the DotrainOrderGui instance.".to_string(), | ||
| ContextError::PriceCallbackError(error) => | ||
| format!("Failed to fetch price data: {}. Please check your price callback implementation and network connectivity.", error), | ||
| ContextError::InvalidIoRatioExpression(expr) => | ||
| format!("Invalid io-ratio expression '{}'. Expected format: io-ratio(input_address, output_address)", expr), | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Clarify io-ratio error message to reflect accepted argument types
The message implies literal addresses are expected, but the implementation currently expects path expressions (e.g., order.inputs.0.token.address). Either accept literal 0x addresses in code (recommended) or adjust the message.
Proposed message update if keeping path-only:
-Invalid io-ratio expression '{}'. Expected format: io-ratio(input_address, output_address)
+Invalid io-ratio expression '{}'. Expected format: io-ratio(<path to input address>, <path to output address>), e.g., io-ratio(order.inputs.0.token.address, order.outputs.0.token.address)Alternatively, see the next comment for enabling literal 0x addresses in code.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ContextError::NoPriceCallback => | |
| "Price callback is not available. Please provide a price callback function when creating the DotrainOrderGui instance.".to_string(), | |
| ContextError::PriceCallbackError(error) => | |
| format!("Failed to fetch price data: {}. Please check your price callback implementation and network connectivity.", error), | |
| ContextError::InvalidIoRatioExpression(expr) => | |
| format!("Invalid io-ratio expression '{}'. Expected format: io-ratio(input_address, output_address)", expr), | |
| } | |
| ContextError::NoPriceCallback => | |
| "Price callback is not available. Please provide a price callback function when creating the DotrainOrderGui instance.".to_string(), | |
| ContextError::PriceCallbackError(error) => | |
| format!("Failed to fetch price data: {}. Please check your price callback implementation and network connectivity.", error), | |
| ContextError::InvalidIoRatioExpression(expr) => | |
| format!("Invalid io-ratio expression '{}'. Expected format: io-ratio(<path to input address>, <path to output address>), e.g., io-ratio(order.inputs.0.token.address, order.outputs.0.token.address)", expr), | |
| } |
🤖 Prompt for AI Agents
In crates/settings/src/yaml/context.rs around lines 60 to 66, the io-ratio error
message incorrectly implies literal 0x addresses are expected; update the
message to state that the expression must be a path expression (e.g.,
"order.inputs.0.token.address") and show the expected format
"io-ratio(input_path, output_path)"; alternatively (preferred) modify the
parsing logic to also accept literal 0x addresses by detecting hex-address
tokens and normalizing them into the same internal representation, then adjust
the error text to mention both supported forms and include short examples for
each.
| tokens: | ||
| usdc: | ||
| network: mainnet | ||
| address: 0xA0b86a33E6441b8e776f89d2b5B977c737C5e5b5 |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Replace incorrect USDC mainnet address
The USDC address on Ethereum mainnet should be 0xA0b86991C6218B36C1d19D4a2e9Eb0cE3606eB48 (checksummed). The current value is invalid and will cause price lookups to fail.
- address: 0xA0b86a33E6441b8e776f89d2b5B977c737C5e5b5
+ address: 0xA0b86991C6218B36C1d19D4a2e9Eb0cE3606eB48🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching repo for the addresses and USDC mentions..."
rg -n --hidden --no-ignore -S "A0b86a33|A0b86991|USDC" || true
echo
echo "If examples/price-ratio-example.yaml exists, show its contents (first 200 lines):"
if [ -f "examples/price-ratio-example.yaml" ]; then
sed -n '1,200p' examples/price-ratio-example.yaml
else
echo "examples/price-ratio-example.yaml not found"
fiLength of output: 14329
Replace incorrect USDC mainnet address in examples
The USDC address 0xA0b86a33E6441b8e776f89d2b5B977c737C5e5b5 is a typo and will break price lookups. Replace it with the correct checksummed mainnet address: 0xA0b86991C6218B36C1d19D4a2e9Eb0cE3606eB48.
Files to update:
- examples/price-ratio-example.yaml:13 — address field
- examples/price-callback-examples.js:232 — USDC constant
Note: the correct address is already used elsewhere (e.g., crates/js_api/src/gui/mod.rs:1462).
Diffs:
--- examples/price-ratio-example.yaml
- address: 0xA0b86a33E6441b8e776f89d2b5B977c737C5e5b5
+ address: 0xA0b86991C6218B36C1d19D4a2e9Eb0cE3606eB48--- examples/price-callback-examples.js
- const USDC = '0xA0b86a33E6441b8e776f89d2b5B977c737C5e5b5';
+ const USDC = '0xA0b86991C6218B36C1d19D4a2e9Eb0cE3606eB48';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| address: 0xA0b86a33E6441b8e776f89d2b5B977c737C5e5b5 | |
| address: 0xA0b86991C6218B36C1d19D4a2e9Eb0cE3606eB48 |
🤖 Prompt for AI Agents
In examples/price-ratio-example.yaml around line 13, the USDC mainnet address is
a typo (0xA0b86a33E6441b8e776f89d2b5B977c737C5e5b5); replace that value with the
correct checksummed mainnet address 0xA0b86991C6218B36C1d19D4a2e9Eb0cE3606eB48
so price lookups work correctly and match the address used elsewhere in the
repo.
| deployments: | ||
| # Example 1: Auction DCA Strategy with dynamic pricing | ||
| auction-dca: | ||
| name: Auction DCA Strategy | ||
| description: | | ||
| Dollar-cost averaging strategy using Dutch auctions. | ||
| Automatically starts auctions at current market prices. | ||
| order: auction-order | ||
| fields: | ||
| - binding: initial-io | ||
| name: Kickoff ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} | ||
| description: | | ||
| The initial ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} ratio to start the first auction. | ||
|
|
||
| Current market price: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} | ||
|
|
||
| This should be set higher than the baseline ratio to ensure the auction starts | ||
| at a favorable price and drops down to the baseline over time. | ||
|
|
||
| Recommended: Set 2-5% above current market price. | ||
| default: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} | ||
|
|
||
| - binding: baseline-io | ||
| name: Baseline ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} | ||
| description: | | ||
| The minimum acceptable ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} ratio. | ||
|
|
||
| Current market price: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} | ||
|
|
||
| Auctions will drop from the initial ratio down to this baseline ratio. | ||
| Set this at or slightly below current market price. | ||
|
|
||
| - binding: auction-duration | ||
| name: Auction Duration (seconds) | ||
| description: How long each auction should run before reaching the baseline price. | ||
| default: "3600" | ||
|
|
||
| # Example 2: Limit Order with price reference | ||
| limit-order: | ||
| name: Limit Order | ||
| description: | | ||
| Simple limit order with current market price reference. | ||
| Set your desired exchange rate above or below market price. | ||
| order: auction-order | ||
| fields: | ||
| - binding: fixed-io | ||
| name: ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} Rate | ||
| description: | | ||
| Fixed exchange rate for this limit order. | ||
|
|
||
| You will receive ${order.inputs.0.token.symbol} for every 1 ${order.outputs.0.token.symbol} you sell. | ||
|
|
||
| 📊 Current market price: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} | ||
|
|
||
| 💡 Tip: Set above market price to sell at a premium, or below to buy at a discount. | ||
|
|
||
| - binding: amount | ||
| name: ${order.outputs.0.token.symbol} Amount to Sell | ||
| description: | | ||
| Amount of ${order.outputs.0.token.symbol} you want to sell. | ||
|
|
||
| At current market price (${io-ratio(order.inputs.0.address, order.outputs.0.address)}), | ||
| this would get you approximately [amount × current_price] ${order.inputs.0.token.symbol}. | ||
|
|
||
| # Example 3: Advanced Dutch Auction Strategy | ||
| dutch-auction-strategy: | ||
| name: Dutch Auction Strategy | ||
| description: | | ||
| Advanced Dutch auction with multiple price points and dynamic pricing. | ||
| Perfect for large orders that need to be executed over time. | ||
| order: auction-order | ||
| fields: | ||
| - binding: start-premium | ||
| name: Starting Premium (%) | ||
| description: | | ||
| Percentage above current market price to start the auction. | ||
|
|
||
| Current market price: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} ${order.inputs.0.token.symbol}/${order.outputs.0.token.symbol} | ||
|
|
||
| Example: 10% means auction starts at 110% of current market price. | ||
| default: "5" | ||
|
|
||
| - binding: end-discount | ||
| name: Ending Discount (%) | ||
| description: | | ||
| Percentage below current market price to end the auction. | ||
|
|
||
| Current market price: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} ${order.inputs.0.token.symbol}/${order.outputs.0.token.symbol} | ||
|
|
||
| Example: 2% means auction ends at 98% of current market price. | ||
| default: "2" | ||
|
|
||
| - binding: auction-frequency | ||
| name: Auction Frequency (hours) | ||
| description: | | ||
| How often to start new auctions. | ||
|
|
||
| With current market price at ${io-ratio(order.inputs.0.address, order.outputs.0.address)}, | ||
| more frequent auctions may capture better price movements. | ||
| default: "24" | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Remove trailing spaces flagged by yamllint
There are numerous trailing spaces in multi-line descriptions (e.g., Lines 47, 49, 50, 52, 60, 62, 83, 85, 87, 94, 95, 100, 110, 112, 120, 122, 130, 131, 147, 149, 157, 158, 174, 175, 182, 184, 193, 194, 202, 203, 205). These can cause noisy diffs and lint failures.
Recommendation:
- Strip trailing whitespace from the entire file.
- If you intend to create blank lines in a block scalar (description: |), leave empty lines without spaces, not lines containing spaces.
If helpful, I can provide a script to normalize this.
🧰 Tools
🪛 YAMLlint (1.37.1)
[error] 47-47: trailing spaces
(trailing-spaces)
[error] 49-49: trailing spaces
(trailing-spaces)
[error] 50-50: trailing spaces
(trailing-spaces)
[error] 52-52: trailing spaces
(trailing-spaces)
[error] 60-60: trailing spaces
(trailing-spaces)
[error] 62-62: trailing spaces
(trailing-spaces)
[error] 83-83: trailing spaces
(trailing-spaces)
[error] 85-85: trailing spaces
(trailing-spaces)
[error] 87-87: trailing spaces
(trailing-spaces)
[error] 94-94: trailing spaces
(trailing-spaces)
[error] 95-95: trailing spaces
(trailing-spaces)
[error] 100-100: trailing spaces
(trailing-spaces)
[error] 110-110: trailing spaces
(trailing-spaces)
[error] 112-112: trailing spaces
(trailing-spaces)
[error] 120-120: trailing spaces
(trailing-spaces)
[error] 122-122: trailing spaces
(trailing-spaces)
[error] 130-130: trailing spaces
(trailing-spaces)
[error] 131-131: trailing spaces
(trailing-spaces)
🤖 Prompt for AI Agents
In examples/price-ratio-example.yaml around lines 34 to 134, multiple multi-line
description block lines contain trailing spaces which trigger yamllint failures;
remove all trailing whitespace across the file (especially inside every
description: | block), ensure intentionally blank lines contain zero characters
(no spaces), and then re-save the file so the diff only reflects substantive
changes; run a whitespace normalization (trim trailing spaces) before committing
to prevent future noisy diffs.
| gui: | ||
| name: Dynamic Pricing Strategies | ||
| description: | | ||
| Advanced trading strategies with real-time price integration. | ||
|
|
||
| All strategies automatically fetch current market prices to provide | ||
| intelligent defaults and helpful context for your trading decisions. | ||
|
|
||
| Current ${order.inputs.0.token.symbol}/${order.outputs.0.token.symbol} market rate: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Expressions in gui.description won’t be interpolated by current parser
The current implementation interpolates field-level name/description/defaults, but not the top-level gui.name/gui.description. The expressions here will remain literal strings at runtime.
Options:
- Update crates/settings/src/gui.rs to interpolate gui.name and gui.description similarly to field-level values.
- Or, remove expressions from gui.description in the example to avoid misleading users.
Would you like me to propose a code change to interpolate top-level GUI fields, or adjust the example?
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Repo root: $(pwd)"
echo
echo "Searching for files related to gui and interpolation..."
# Find gui.rs if present
fd --hidden --follow --max-depth 4 'gui.rs' || true
# Show the example file mentioned in the review (if present)
echo
echo "---- examples/price-ratio-example.yaml (if present) ----"
rg --line-number --hidden --no-ignore --max-filesize 1M "price-ratio-example.yaml" || true
if [ -f "examples/price-ratio-example.yaml" ]; then
sed -n '1,260p' examples/price-ratio-example.yaml
else
echo "examples/price-ratio-example.yaml not found"
fi
echo
echo "---- Searching for crates/settings/src/gui.rs and related code ----"
rg --line-number --hidden --no-ignore --max-filesize 1M "crates/settings/src/gui.rs" || true
if [ -f "crates/settings/src/gui.rs" ]; then
sed -n '1,240p' crates/settings/src/gui.rs
else
echo "crates/settings/src/gui.rs not found"
fi
echo
echo "---- Searching repository for interpolation-related symbols ----"
rg --line-number --hidden --no-ignore --max-filesize 1M "interpolat|interpolate|render_template|render_template|Template|expand_vars|expand_|\\$\\{" || true
echo
echo "---- Searching for code that handles field-level name/description/defaults ----"
rg --line-number --hidden --no-ignore --max-filesize 1M "description" | sed -n '1,200p' || true
echo
echo "---- Searching for io-ratio or similar example helpers ----"
rg --line-number --hidden --no-ignore --max-filesize 1M "io-ratio|io_ratio|ioRatio" || true
echo
echo "Done."Length of output: 130340
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Showing crates/settings/src/gui.rs lines 760-880"
nl -ba crates/settings/src/gui.rs | sed -n '760,880p'Length of output: 273
Top-level gui.name/gui.description are not interpolated — please fix or update the example
Short: field-level names/descriptions/defaults are run through the YAML Context.interpolate (see crates/settings/src/gui.rs where field interpolation uses context.interpolate), but GuiConfigSourceCfg::try_into_gui returns GuiCfg with name/description copied directly (no interpolation). As a result the ${...} expressions in examples/price-ratio-example.yaml (gui.name/gui.description) will be literal at runtime.
Files to inspect/fix:
- crates/settings/src/gui.rs
- Problem: GuiConfigSourceCfg::try_into_gui returns GuiCfg { name: self.name, description: self.description, ... } without calling Context::interpolate.
- Evidence: field-level interpolation exists in the same file (context.interpolate called for field.name/field.description/field.default around the field-processing code).
- Fix options:
- Interpolate top-level GUI fields in this conversion (call context.interpolate(&self.name) and interpolate description) — or accept/construct a Context here and reuse the same interpolation logic used for fields.
- OR perform interpolation higher where a Context is already available (e.g., in the YAML expansion/parse flow) before building GuiCfg.
- crates/settings/src/yaml/context.rs
- Use Context::interpolate / resolve_io_ratio (already supports ${io-ratio(...)}) for top-level fields.
- examples/price-ratio-example.yaml (lines ~198-206)
- The gui.description currently contains expressions that will not be evaluated. Either remove these expressions or keep them but ensure the code path interpolates top-level fields.
Problem snippet (from example):
gui:
name: Dynamic Pricing Strategies
description: |
Advanced trading strategies with real-time price integration.
All strategies automatically fetch current market prices to provide
intelligent defaults and helpful context for your trading decisions.
Current ${order.inputs.0.token.symbol}/${order.outputs.0.token.symbol} market rate: ${io-ratio(order.inputs.0.address, order.outputs.0.address)}
Would you like me to:
- propose the code change to interpolate top-level GUI fields (patch in crates/settings/src/gui.rs or the YAML parsing flow), or
- update the example to remove/clarify the interpolated expressions?
🧰 Tools
🪛 YAMLlint (1.37.1)
[error] 202-202: trailing spaces
(trailing-spaces)
[error] 203-203: trailing spaces
(trailing-spaces)
[error] 205-205: trailing spaces
(trailing-spaces)
| @@ -1 +1 @@ | |||
| Subproject commit 46ec53183a0ae214847e30bf0bf10ec819b34233 | |||
| Subproject commit 9d6d8cc2405031ff31ee2db75b15b74ef968c88c | |||
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Ensure CI and local workflows fetch submodules
If CI or dev setup doesn’t already fetch submodules, this bump will not take effect in builds/tests. Make sure actions/checkout (or equivalent) has submodules enabled.
For GitHub Actions, the typical configuration is:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Top-level files ==="
ls -la | sed -n '1,200p' || true
echo
echo "=== Check for .gitmodules ==="
if [ -f .gitmodules ]; then
echo ".gitmodules FOUND:"
sed -n '1,200p' .gitmodules
else
echo ".gitmodules NOT found"
fi
echo
echo "=== Search for 'actions/checkout' occurrences ==="
rg -n "actions/checkout" || true
echo
echo "=== Search for 'submodules' keyword occurrences ==="
rg -n "submodules" || true
echo
echo "=== Search for 'git submodule' occurrences ==="
rg -n "git submodule" || true
echo
echo "=== Search for 'fetch-depth' occurrences ==="
rg -n "fetch-depth" || true
echo
echo "=== Show GitHub workflow files if present ==="
if [ -d .github/workflows ]; then
for f in .github/workflows/*.{yml,yaml}; do
[ -e "$f" ] || continue
echo
echo "---- $f ----"
sed -n '1,300p' "$f"
done
else
echo ".github/workflows directory NOT present"
fi
echo
echo "=== Search for other CI configs (circleci, gitlab-ci) ==="
rg -n "gitlab-ci|circleci|workflows" || trueLength of output: 49142
Enable submodules on the remaining checkout step
Most workflows already fetch submodules (submodules: recursive + fetch-depth: 0). Update the checkout in the one workflow that doesn't.
Files to change:
- .github/workflows/npm-package-release.yml — the checkout step currently only sets ssh-key; add submodules and fetch-depth.
Replace this:
- uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.PUBLISH_PRIVATE_KEY }}
With this:
- uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.PUBLISH_PRIVATE_KEY }}
submodules: recursive
fetch-depth: 0
🤖 Prompt for AI Agents
In lib/rain.orderbook.interface around lines 1 to 1, update the GitHub Actions
checkout step in .github/workflows/npm-package-release.yml (currently only sets
ssh-key) to also enable submodules and full history; change the
actions/checkout@v4 step to include with: ssh-key: ${{
secrets.PUBLISH_PRIVATE_KEY }}, submodules: recursive, and fetch-depth: 0 so
submodules are fetched and not shallow-cloned.
| **Key Benefits:** | ||
| - **Automatic Price Fetching**: Strategies can automatically use current market prices | ||
| - **User-Friendly Defaults**: No more manual price lookups for users | ||
| - **Flexible Integration**: Works with any price API (SushiSwap, 1inch, CoinGecko, etc.) | ||
| - **Error Handling**: Comprehensive error handling for network issues and invalid data | ||
|
|
||
| **Quick Example:** | ||
| ```yaml | ||
| fields: | ||
| - binding: initial-io | ||
| name: Kickoff ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} | ||
| description: Current market price: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} | ||
| default: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} | ||
| ``` | ||
|
|
||
| ```javascript | ||
| const priceCallback = async (inputTokenAddress, outputTokenAddress) => { | ||
| const response = await fetch(`/api/price/${inputTokenAddress}/${outputTokenAddress}`); | ||
| return (await response.json()).ratio.toString(); | ||
| }; | ||
|
|
||
| const gui = await DotrainOrderGui.newWithDeploymentAndPriceCallback( | ||
| yaml, deployment, stateCallback, priceCallback | ||
| ); | ||
| ``` |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Fix markdown lint (MD031/MD032) and spacing around lists and code fences
Lists and fenced code blocks should be surrounded by blank lines. This also resolves the spacing issues flagged by LanguageTool.
Apply the following to improve formatting and lint compliance:
-**Key Benefits:**
-- **Automatic Price Fetching**: Strategies can automatically use current market prices
-- **User-Friendly Defaults**: No more manual price lookups for users
-- **Flexible Integration**: Works with any price API (SushiSwap, 1inch, CoinGecko, etc.)
-- **Error Handling**: Comprehensive error handling for network issues and invalid data
+**Key Benefits:**
+
+- **Automatic Price Fetching**: Strategies can automatically use current market prices
+- **User-Friendly Defaults**: No more manual price lookups for users
+- **Flexible Integration**: Works with any price API (SushiSwap, 1inch, CoinGecko, etc.)
+- **Error Handling**: Comprehensive error handling for network issues and invalid data-**Quick Example:**
-```yaml
+**Quick Example:**
+
+```yaml
fields:
- binding: initial-io
name: Kickoff ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol}
description: Current market price: ${io-ratio(order.inputs.0.address, order.outputs.0.address)}
default: ${io-ratio(order.inputs.0.address, order.outputs.0.address)}-javascript + +javascript
const priceCallback = async (inputTokenAddress, outputTokenAddress) => {
const response = await fetch(/api/price/${inputTokenAddress}/${outputTokenAddress});
return (await response.json()).ratio.toString();
};
const gui = await DotrainOrderGui.newWithDeploymentAndPriceCallback(
yaml, deployment, stateCallback, priceCallback
);
Also consider adding a brief note clarifying io-ratio direction (input per 1 output) to avoid ambiguity for users.
🧰 Tools
🪛 LanguageTool
[grammar] ~32-~32: There might be a mistake here.
Context: ...hout hardcoding values. Key Benefits: - Automatic Price Fetching: Strategies c...
(QB_NEW_EN)
[grammar] ~33-~33: There might be a mistake here.
Context: ... automatically use current market prices - User-Friendly Defaults: No more manual...
(QB_NEW_EN_OTHER)
[grammar] ~34-~34: There might be a mistake here.
Context: ...: No more manual price lookups for users - Flexible Integration: Works with any p...
(QB_NEW_EN_OTHER)
[grammar] ~35-~35: There might be a mistake here.
Context: ... API (SushiSwap, 1inch, CoinGecko, etc.) - Error Handling: Comprehensive error ha...
(QB_NEW_EN_OTHER)
[grammar] ~36-~36: Use correct spacing
Context: ...ling for network issues and invalid data Quick Example: ```yaml fields: - bin...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~38-~38: Use correct spacing
Context: ...ssues and invalid data Quick Example: yaml fields: - binding: initial-io name: Kickoff ${order.inputs.0.token.symbol} per ${order.outputs.0.token.symbol} description: Current market price: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} default: ${io-ratio(order.inputs.0.address, order.outputs.0.address)} javascript const priceCallback = async (inputTokenAddress, outputTokenAddress) => { const response = await fetch(`/api/price/${inputTokenAddress}/${outputTokenAddress}`); return (await response.json()).ratio.toString(); }; const gui = await DotrainOrderGui.newWithDeploymentAndPriceCallback( yaml, deployment, stateCallback, priceCallback ); 📚 Learn More: See [docs/price-ratio...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
🪛 markdownlint-cli2 (0.17.2)
33-33: Lists should be surrounded by blank lines
(MD032, blanks-around-lists)
39-39: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
In README.md around lines 32 to 56, the markdown blocks and lists lack
surrounding blank lines causing MD031/MD032 lint errors; fix by adding a blank
line before and after the "**Quick Example:**" heading, add a blank line between
the list of Key Benefits and the code fence, ensure there is a blank line
immediately before each ```yaml and ```javascript fence and immediately after
each closing fence, and optionally add a short single-line note clarifying the
io-ratio direction (e.g., "io-ratio returns input amount per 1 output") right
after the yaml example.
findolor
left a comment
There was a problem hiding this comment.
@big14way Thanks for the PR. We use AI analysis to apply improvements and catch issues so could you take a look at all the AI comments posted in this PR and reply to them?
You don't have to agree with every comment but sometimes it outlines issues that have been overlooked. If you can sweep and reply to each (not needed/will do) that would be great. I can review the PR after.
Resolves #1998: Ability to reference price ratios in field names/descriptions and for default field values
Summary by CodeRabbit
New Features
Documentation
Tests
Chores