Skip to content

Conversation

@findolor
Copy link
Collaborator

@findolor findolor commented Jan 15, 2026

Chained PRs

Motivation

See issues:

Users need the ability to execute take orders against specific individual orders they've selected, rather than only using the auto-discovery mechanism that finds orders by token pair. This enables precise control over which order to trade against.

Solution

Adds single order take execution capability across the SDK, UI components, and webapp:

Rust SDK (crates/common):

  • getTakeCalldata method on RaindexOrder for targeting specific orders with fresh quote fetching
  • estimateTakeOrder method for calculating spend/receive estimates for UI preview
  • TakeOrderEstimate struct exposing expected spend, receive, and partial fill indicator
  • Refactored TakeOrdersCalldataResult with proper wasm getters

UI Components (packages/ui-components):

  • TAKE_ORDER transaction type
  • createTakeOrderTransaction in TransactionManager
  • Take Order button in OrderDetail component
  • Take button and Actions column in OrdersListTable

Webapp (packages/webapp):

  • TakeOrderModal with pair selection, direction (buy/sell), amount input, price cap
  • Live quote refresh with configurable interval
  • Real-time estimate preview showing expected spend/receive
  • handleTakeOrder service for processing submissions
Screenshot 2026-01-15 at 17 59 38 Screenshot 2026-01-15 at 17 59 45 Screenshot 2026-01-15 at 17 59 55

Checks

By submitting this for review, I'm confirming I've done the following:

  • made this PR as small as possible
  • unit-tested any new functionality
  • linked any relevant issues or PRs
  • included screenshots (if this involves a front-end change)

fix #2396

Summary by CodeRabbit

Release Notes

  • New Features

    • Added take order functionality: users can now execute orders directly from the orderbook with a dedicated modal for configuration
    • Integrated "Take" buttons in order listings and detail views for easy order execution
    • Added live quote refreshing and amount estimation within the take order modal
    • Enabled flexible order taking with customizable direction (buy/sell), amounts, and price caps
  • Documentation

    • Updated README with take orders API examples and usage guidance

✏️ Tip: You can customize this high-level summary in your review settings.

- Add `getTakeCalldata` method to RaindexOrder for targeting specific orders
- Add `estimateTakeOrder` method for calculating spend/receive estimates
- Add `single.rs` module with `build_candidate_from_quote` and `execute_single_take`
- Refactor TakeOrdersCalldataResult with proper wasm bindings (getters)
- Add TakeOrderEstimate struct for UI preview calculations
- Add TAKE_ORDER transaction type
- Add createTakeOrderTransaction to TransactionManager
- Add Take Order button to OrderDetail component
- Add Take button and Actions column to OrdersListTable
- Add onTakeOrder callback prop to OrderDetail
- Add handleTakeOrderModal prop to OrdersListTable
- Add TakeOrderModal component with pair selection, amount input, price cap
- Add handleTakeOrder service for processing take order submissions
- Add HandleTakeOrderModal type and handler to modal service
- Integrate take order modal into orders list and detail pages
- Support live quote refresh and estimate preview
- Add take orders feature to SDK capabilities
- Document getTakeOrdersCalldata for auto-discovery by token pair
- Document order.getTakeCalldata for targeting specific orders
- Explain take order modes (BuyExact, BuyUpTo, SpendExact, SpendUpTo)
Refactor tests to use getter methods instead of direct field access
following the wasm binding updates to TakeOrdersCalldataResult.
@findolor findolor self-assigned this Jan 15, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds comprehensive take-order execution functionality across SDK and UI layers. Introduces SDK methods for calldata generation and order estimation, a single-order execution module with quote validation, a UI modal for interactive order taking, transaction flow integration, and extensive test coverage.

Changes

Cohort / File(s) Summary
SDK Error & Type Definitions
crates/common/src/raindex_client/mod.rs, crates/common/src/raindex_client/take_orders/result.rs
Added QuoteDataMissing error variant; restructured TakeOrdersCalldataResult and new TakeOrderEstimate with private fields and wasm/non-wasm accessor methods instead of public fields
SDK Order Execution Methods
crates/common/src/raindex_client/orders.rs, crates/common/src/raindex_client/take_orders/single.rs
Added get_take_calldata and estimate_take_order public methods on RaindexOrder; introduced execute_single_take, build_candidate_from_quote, and estimate_take_order in new single module with preflight checks, liquidity estimation, and candidate building logic
SDK Module Exports
crates/common/src/raindex_client/take_orders/mod.rs
Added pub mod single; re-exported TakeOrderEstimate, build_candidate_from_quote, estimate_take_order, execute_single_take
SDK Tests
crates/common/src/raindex_client/take_orders/single_tests.rs, crates/common/src/raindex_client/take_orders/e2e_tests.rs
Added 1567 lines of comprehensive unit/integration tests for single take-order scenarios (modes, edge cases, preflight failures); updated e2e tests to use new accessor methods
UI Modal & Detail Components
packages/webapp/src/lib/components/TakeOrderModal.svelte, packages/ui-components/src/lib/components/detail/OrderDetail.svelte, packages/ui-components/src/lib/components/tables/OrdersListTable.svelte
Introduced TakeOrderModal with quote loading, mode selection, amount/price cap inputs; added onTakeOrder callback to OrderDetail; added handleTakeOrderModal prop and Take button to OrdersListTable
UI Services & Transaction Integration
packages/webapp/src/lib/services/handleTakeOrder.ts, packages/webapp/src/lib/services/modal.ts, packages/ui-components/src/lib/providers/transactions/TransactionManager.ts, packages/ui-components/src/lib/types/transaction.ts
Added handleTakeOrder service with calldata retrieval and transaction flow; added TakeOrderSubmitParams, TakeOrderModalProps, and handleTakeOrderModal types; added createTakeOrderTransaction method to TransactionManager; added TAKE_ORDER to TransactionName enum
Route Integration
packages/webapp/src/routes/orders/+page.svelte, packages/webapp/src/routes/orders/[chainId]-[orderbook]-[orderHash]/+page.svelte
Wired onTakeOrderCallback and handleTakeOrderModal handlers to OrdersListTable and OrderDetail components respectively
UI Tests
packages/ui-components/src/__tests__/OrderDetail.test.ts, packages/ui-components/src/__tests__/OrdersListTable.test.ts, packages/ui-components/src/__tests__/TransactionManager.test.ts, packages/webapp/src/__tests__/TakeOrderModal.test.ts, packages/webapp/src/__tests__/handleTakeOrder.test.ts
Added 205+ lines testing Take Order button visibility and interaction; 81 lines for OrdersListTable Take button; 139 lines for createTakeOrderTransaction; 666 lines for TakeOrderModal modal states, inputs, mode logic, refresh behavior; 295 lines for handleTakeOrder flow and token resolution
Configuration & Documentation
packages/webapp/src/lib/constants.ts, tauri-app/src/lib/services/loadRemoteSettings.ts, packages/orderbook/README.md
Updated REGISTRY_URL and REMOTE_SETTINGS_URL to newer commit hash; documented new take-orders API with TakeOrdersRequest, TakeOrdersCalldataResult, TakeOrderEstimate, GetOrdersTokenFilter

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant TakeOrderModal as TakeOrderModal<br/>(UI)
    participant handleTakeOrder as handleTakeOrder<br/>(Service)
    participant RaindexClient as RaindexClient<br/>(SDK)
    participant Single as single module<br/>(SDK)
    participant TransactionMgr as TransactionManager

    User->>TakeOrderModal: Open modal with order
    TakeOrderModal->>RaindexClient: Load quotes for order
    RaindexClient-->>TakeOrderModal: Quote data
    TakeOrderModal->>User: Display quote details, inputs

    User->>TakeOrderModal: Submit (quote, mode, amount, priceCap)
    TakeOrderModal->>handleTakeOrder: processSubmit(quote, mode, etc.)
    
    handleTakeOrder->>RaindexClient: order.getTakeCalldata()
    RaindexClient->>Single: execute_single_take(candidate, mode, price_cap)
    Single->>Single: Preflight checks (liquidity, price cap)
    Single->>RaindexClient: RPC simulation call
    RaindexClient-->>Single: Simulation result
    Single-->>RaindexClient: TakeOrdersCalldataResult
    RaindexClient-->>handleTakeOrder: Calldata result
    
    handleTakeOrder->>TransactionMgr: createTakeOrderTransaction(calldata)
    TransactionMgr->>User: Confirm transaction modal
    User->>TransactionMgr: Confirm
    TransactionMgr->>RaindexClient: Execute takeOrders4 call
    RaindexClient-->>TransactionMgr: Transaction hash
    TransactionMgr->>RaindexClient: Fetch updated order (via subgraph)
    RaindexClient-->>TransactionMgr: Updated order
    TransactionMgr->>User: Success toast
Loading
sequenceDiagram
    participant Caller as Caller<br/>(App/Test)
    participant RaindexOrder as RaindexOrder<br/>(orders.rs)
    participant Single as single module
    participant Estimate as estimate_take_order
    participant CandidateBuilder as build_candidate_from_quote

    Caller->>RaindexOrder: get_take_calldata(mode, amount, price_cap)
    RaindexOrder->>RaindexOrder: Load fresh quote for order
    RaindexOrder->>CandidateBuilder: build_candidate_from_quote(order, quote)
    CandidateBuilder->>CandidateBuilder: Validate indices, check capacity
    CandidateBuilder-->>RaindexOrder: TakeOrderCandidate (or None/Error)
    
    RaindexOrder->>Single: execute_single_take(candidate, mode, price_cap)
    Single->>Single: Preflight liquidity checks
    Single->>Single: Compute target input/output from mode
    Single->>Single: Build simulation config
    Single->>Single: Run RPC simulation
    Single-->>RaindexOrder: TakeOrdersCalldataResult
    RaindexOrder-->>Caller: Result with calldata, prices, effective_price

    Caller->>RaindexOrder: estimate_take_order_method(quote)
    RaindexOrder->>Estimate: estimate_take_order(max_output, ratio, is_buy, amount)
    Estimate->>Estimate: Compute expected_spend, expected_receive, is_partial
    Estimate-->>RaindexOrder: TakeOrderEstimate
    RaindexOrder-->>Caller: Estimate result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #2386: Implements core take-order SDK functionality (calldata generation, execution, estimation) required for takeOrders4/V6 exact-swap execution.
  • #2355: Implements take-order candidate building from quotes (build_candidate_from_quote) and quote validation layer.
  • #2353: Implements Phase 2/3 take-orders SDK including quote-driven candidate building, estimation, and execution with wasm/JS API bindings.

Possibly related PRs

  • #1891: Adds createAddOrderTransaction and related transaction manager infrastructure; this PR extends the same pattern with createTakeOrderTransaction.
  • #1597: Modifies OrderDetail component tests and test IDs; this PR adds onTakeOrder prop and corresponding tests to the same component.
  • #2001: Introduces/modifies RaindexOrderQuote types that are consumed by the take-order execution logic in this PR.

Suggested labels

rust, webapp, enhancement, test

Suggested reviewers

  • hardyjosh
  • 0xgleb
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Minor out-of-scope changes detected: updates to REGISTRY_URL and REMOTE_SETTINGS_URL constants point to a different rain.strategies commit, which is unrelated to the take-order execution feature. Remove or separate the settings URL updates (REGISTRY_URL, REMOTE_SETTINGS_URL in constants.ts and loadRemoteSettings.ts) into a dedicated PR, as these are infrastructure updates unrelated to the take-order UI feature.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add single order take execution UI' clearly and concisely summarizes the main change: adding UI functionality to execute take orders for individual orders.
Linked Issues check ✅ Passed The PR fulfills all coding requirements from issue #2396: adds a Take Order button in OrderDetail and OrdersListTable, implements take-order execution for specific orders, and integrates with the Take Orders SDK via new methods and types.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@findolor findolor requested review from 0xgleb and hardyjosh January 15, 2026 15:01
@findolor findolor linked an issue Jan 15, 2026 that may be closed by this pull request
3 tasks
…-execution

# Conflicts:
#	crates/common/src/raindex_client/take_orders/result.rs
@findolor
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@findolor
Copy link
Collaborator Author

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

🤖 Generated with Claude Code

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@crates/common/src/raindex_client/take_orders/single_tests.rs`:
- Around line 58-133: The tests use LocalEvm via base_setup_test() and must run
with Tokio's multi_thread runtime to avoid stalls; update each test attribute
(e.g., #[tokio::test] on functions like
test_single_order_take_happy_path_buy_up_to and the other 15 tests in this file)
to #[tokio::test(flavor = "multi_thread")], keeping the function bodies
unchanged so LocalEvm from base_setup_test() runs on the multi-threaded flavor.

In `@crates/common/src/raindex_client/take_orders/single.rs`:
- Around line 104-187: The code currently calls find_failing_order_index (which
re-simulates) even for single-order cases; inside execute_single_take's
Err(sim_error) branch, check if the built config contains only one order (e.g.
built.config.orders.len() == 1) and if so immediately return a PreflightError
with the simulation error (no extra RPC/simulation); otherwise keep the existing
logic that calls find_failing_order_index(...) and formats the error based on
whether it returns Some or None.

In `@packages/ui-components/src/lib/components/tables/OrdersListTable.svelte`:
- Around line 223-234: The icon-only menu Button (the Button component rendering
DotsVerticalOutline with id/data-testid like `order-menu-${item.id}`) lacks an
accessible name; add one by providing an aria-label (e.g., aria-label={`Open
order actions for ${item.id}`}) or an aria-labelledby that points to a visually
hidden text element inside the same component so screen readers get context;
ensure the attribute is added to the Button element that has on:click and the
DotsVerticalOutline so the button is announced meaningfully.

In `@packages/webapp/src/lib/components/TakeOrderModal.svelte`:
- Around line 43-70: In loadQuotes, ensure stale errors are cleared and
selectedPairIndex is clamped after a successful refresh: when order.getQuotes()
succeeds (after filtering into quotes) reset errorMessage to an empty string and
then adjust selectedPairIndex so it is within [0, quotes.length-1] (or set to
-1/0 if you represent "none selected") so it cannot point past the end when
quotes shrank; do this in the success path right after assigning quotes (and
before returning) so both normal loads and isRefresh loads clear errors and
clamp selection.

Comment on lines 58 to 133
#[tokio::test]
async fn test_single_order_take_happy_path_buy_up_to() {
let setup = base_setup_test().await;
let sg_server = MockServer::start_async().await;

let vault_id = B256::from(U256::from(1u64));
fund_standard_two_token_vault(&setup, vault_id).await;

let vault1 = create_vault(vault_id, &setup, &setup.token1_sg);
let vault2 = create_vault(vault_id, &setup, &setup.token2_sg);

let dotrain = create_dotrain_config_with_params(&setup, "100", "2");
let (order_bytes, order_hash) = deploy_order(&setup, dotrain).await;

let order_json = create_sg_order_json(
&setup,
&order_bytes,
order_hash,
vec![vault1.clone(), vault2.clone()],
vec![vault1.clone(), vault2.clone()],
);

sg_server.mock(|when, then| {
when.path("/sg");
then.status(200).json_body_obj(&json!({
"data": {
"orders": [order_json]
}
}));
});

let yaml = get_minimal_yaml_for_chain(
123,
&setup.local_evm.url().to_string(),
&sg_server.url("/sg"),
&setup.orderbook.to_string(),
);

let client = RaindexClient::new(vec![yaml], None).unwrap();

let order = client
.get_order_by_hash(&OrderbookIdentifier::new(123, setup.orderbook), order_hash)
.await
.unwrap();

let taker = setup.local_evm.signer_wallets[1].default_signer().address();
fund_and_approve_taker(
&setup,
setup.token1,
taker,
setup.orderbook,
U256::from(10).pow(U256::from(22)),
)
.await;

let max_output = Float::parse("100".to_string()).unwrap();
let ratio = Float::parse("2".to_string()).unwrap();
let max_input = max_output.mul(ratio).unwrap();
let quote = make_quote(
0,
1,
Some(make_quote_value(max_output, max_input, ratio)),
true,
);

let candidate = build_candidate_from_quote(&order, &quote)
.unwrap()
.expect("Should build candidate from quote");

let mode = parse_buy_up_to("50");
let price_cap = Float::parse(high_price_cap()).unwrap();
let rpc_urls = vec![url::Url::parse(&setup.local_evm.url()).unwrap()];

let result = execute_single_take(candidate, mode, price_cap, taker, &rpc_urls, None)
.await
.expect("Should succeed with BuyUpTo mode");
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check the actual test decorator at lines 58-133
cat -n crates/common/src/raindex_client/take_orders/single_tests.rs | sed -n '50,135p'

Repository: rainlanguage/rain.orderbook

Length of output: 3297


🏁 Script executed:

# Find base_setup_test() definition to confirm it provides LocalEvm
rg -A 10 "async fn base_setup_test" crates/common/src/raindex_client/take_orders/

Repository: rainlanguage/rain.orderbook

Length of output: 53


🏁 Script executed:

# List all test functions in this file to see which ones use base_setup_test()
rg "#\[tokio::test" crates/common/src/raindex_client/take_orders/single_tests.rs -A 1

Repository: rainlanguage/rain.orderbook

Length of output: 1357


🏁 Script executed:

# Search for base_setup_test definition in the entire crates/common directory
rg "fn base_setup_test" crates/common/ -A 5

Repository: rainlanguage/rain.orderbook

Length of output: 53


🏁 Script executed:

# Get the full test file to see setup patterns
wc -l crates/common/src/raindex_client/take_orders/single_tests.rs

Repository: rainlanguage/rain.orderbook

Length of output: 136


🏁 Script executed:

# Count which tests call base_setup_test
rg "base_setup_test()" crates/common/src/raindex_client/take_orders/single_tests.rs | wc -l

Repository: rainlanguage/rain.orderbook

Length of output: 73


🏁 Script executed:

# Check imports and module structure at start of the file
head -50 crates/common/src/raindex_client/take_orders/single_tests.rs

Repository: rainlanguage/rain.orderbook

Length of output: 1779


🏁 Script executed:

# Search repo-wide for base_setup_test definition
rg "fn base_setup_test" . -A 5

Repository: rainlanguage/rain.orderbook

Length of output: 53


🏁 Script executed:

# Find the setup_test function in local_evm module
rg "fn setup_test" crates/common/src/test_helpers/ -A 10

Repository: rainlanguage/rain.orderbook

Length of output: 142


🏁 Script executed:

# Also check if there are any multi_thread decorated tests already in the codebase for reference
rg "tokio::test.*multi_thread" crates/common/src/ -B 1 | head -20

Repository: rainlanguage/rain.orderbook

Length of output: 977


🏁 Script executed:

# Find test_helpers directory
fd "test_helpers" crates/ -type d

Repository: rainlanguage/rain.orderbook

Length of output: 241


🏁 Script executed:

# Also check the parent module structure
ls -la crates/common/src/ | grep -E "^d"

Repository: rainlanguage/rain.orderbook

Length of output: 528


🏁 Script executed:

# Find test_helpers in the entire crates/common
find crates/common -name "test_helpers*" -o -name "local_evm*"

Repository: rainlanguage/rain.orderbook

Length of output: 104


🏁 Script executed:

# Search for where setup_test or test_helpers is defined
rg "mod test_helpers" crates/common/

Repository: rainlanguage/rain.orderbook

Length of output: 303


🏁 Script executed:

# Try searching in the root crates/common/src
ls -la crates/common/src/*.rs | head -20

Repository: rainlanguage/rain.orderbook

Length of output: 1522


🏁 Script executed:

# Check the test_helpers.rs file for local_evm and setup_test
rg "pub mod local_evm|fn setup_test" crates/common/src/test_helpers.rs -A 5

Repository: rainlanguage/rain.orderbook

Length of output: 573


🏁 Script executed:

# Get the LocalEvm structure to confirm it provides local_evm field
rg "struct LocalEvm|pub local_evm" crates/common/src/test_helpers.rs -B 2 -A 5

Repository: rainlanguage/rain.orderbook

Length of output: 509


Use multi_thread tokio flavor for LocalEvm-backed tests.

All tests in this file call base_setup_test(), which provides a LocalEvm instance. They should be annotated with #[tokio::test(flavor = "multi_thread")] to avoid runtime stalls. This pattern is already established in other test files in the codebase (e.g., replays.rs, add_order.rs, unit_tests.rs).

Suggested fix (apply to all 16 tests)
-#[tokio::test]
+#[tokio::test(flavor = "multi_thread")]
 async fn test_single_order_take_happy_path_buy_up_to() {
📝 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.

Suggested change
#[tokio::test]
async fn test_single_order_take_happy_path_buy_up_to() {
let setup = base_setup_test().await;
let sg_server = MockServer::start_async().await;
let vault_id = B256::from(U256::from(1u64));
fund_standard_two_token_vault(&setup, vault_id).await;
let vault1 = create_vault(vault_id, &setup, &setup.token1_sg);
let vault2 = create_vault(vault_id, &setup, &setup.token2_sg);
let dotrain = create_dotrain_config_with_params(&setup, "100", "2");
let (order_bytes, order_hash) = deploy_order(&setup, dotrain).await;
let order_json = create_sg_order_json(
&setup,
&order_bytes,
order_hash,
vec![vault1.clone(), vault2.clone()],
vec![vault1.clone(), vault2.clone()],
);
sg_server.mock(|when, then| {
when.path("/sg");
then.status(200).json_body_obj(&json!({
"data": {
"orders": [order_json]
}
}));
});
let yaml = get_minimal_yaml_for_chain(
123,
&setup.local_evm.url().to_string(),
&sg_server.url("/sg"),
&setup.orderbook.to_string(),
);
let client = RaindexClient::new(vec![yaml], None).unwrap();
let order = client
.get_order_by_hash(&OrderbookIdentifier::new(123, setup.orderbook), order_hash)
.await
.unwrap();
let taker = setup.local_evm.signer_wallets[1].default_signer().address();
fund_and_approve_taker(
&setup,
setup.token1,
taker,
setup.orderbook,
U256::from(10).pow(U256::from(22)),
)
.await;
let max_output = Float::parse("100".to_string()).unwrap();
let ratio = Float::parse("2".to_string()).unwrap();
let max_input = max_output.mul(ratio).unwrap();
let quote = make_quote(
0,
1,
Some(make_quote_value(max_output, max_input, ratio)),
true,
);
let candidate = build_candidate_from_quote(&order, &quote)
.unwrap()
.expect("Should build candidate from quote");
let mode = parse_buy_up_to("50");
let price_cap = Float::parse(high_price_cap()).unwrap();
let rpc_urls = vec![url::Url::parse(&setup.local_evm.url()).unwrap()];
let result = execute_single_take(candidate, mode, price_cap, taker, &rpc_urls, None)
.await
.expect("Should succeed with BuyUpTo mode");
#[tokio::test(flavor = "multi_thread")]
async fn test_single_order_take_happy_path_buy_up_to() {
let setup = base_setup_test().await;
let sg_server = MockServer::start_async().await;
let vault_id = B256::from(U256::from(1u64));
fund_standard_two_token_vault(&setup, vault_id).await;
let vault1 = create_vault(vault_id, &setup, &setup.token1_sg);
let vault2 = create_vault(vault_id, &setup, &setup.token2_sg);
let dotrain = create_dotrain_config_with_params(&setup, "100", "2");
let (order_bytes, order_hash) = deploy_order(&setup, dotrain).await;
let order_json = create_sg_order_json(
&setup,
&order_bytes,
order_hash,
vec![vault1.clone(), vault2.clone()],
vec![vault1.clone(), vault2.clone()],
);
sg_server.mock(|when, then| {
when.path("/sg");
then.status(200).json_body_obj(&json!({
"data": {
"orders": [order_json]
}
}));
});
let yaml = get_minimal_yaml_for_chain(
123,
&setup.local_evm.url().to_string(),
&sg_server.url("/sg"),
&setup.orderbook.to_string(),
);
let client = RaindexClient::new(vec![yaml], None).unwrap();
let order = client
.get_order_by_hash(&OrderbookIdentifier::new(123, setup.orderbook), order_hash)
.await
.unwrap();
let taker = setup.local_evm.signer_wallets[1].default_signer().address();
fund_and_approve_taker(
&setup,
setup.token1,
taker,
setup.orderbook,
U256::from(10).pow(U256::from(22)),
)
.await;
let max_output = Float::parse("100".to_string()).unwrap();
let ratio = Float::parse("2".to_string()).unwrap();
let max_input = max_output.mul(ratio).unwrap();
let quote = make_quote(
0,
1,
Some(make_quote_value(max_output, max_input, ratio)),
true,
);
let candidate = build_candidate_from_quote(&order, &quote)
.unwrap()
.expect("Should build candidate from quote");
let mode = parse_buy_up_to("50");
let price_cap = Float::parse(high_price_cap()).unwrap();
let rpc_urls = vec![url::Url::parse(&setup.local_evm.url()).unwrap()];
let result = execute_single_take(candidate, mode, price_cap, taker, &rpc_urls, None)
.await
.expect("Should succeed with BuyUpTo mode");
🤖 Prompt for AI Agents
In `@crates/common/src/raindex_client/take_orders/single_tests.rs` around lines 58
- 133, The tests use LocalEvm via base_setup_test() and must run with Tokio's
multi_thread runtime to avoid stalls; update each test attribute (e.g.,
#[tokio::test] on functions like test_single_order_take_happy_path_buy_up_to and
the other 15 tests in this file) to #[tokio::test(flavor = "multi_thread")],
keeping the function bodies unchanged so LocalEvm from base_setup_test() runs on
the multi-threaded flavor.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have any issues with the testing at the moment. If we have issues, we can implement this but for now we'll leave this as it is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@findolor, understood! I'll defer this suggestion. If you encounter any runtime issues with these tests in the future, the multi_thread flavor annotation is available as a potential solution.


🧠 Learnings used
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2363
File: crates/common/src/take_orders/preflight.rs:80-107
Timestamp: 2025-12-18T08:19:55.397Z
Learning: In crates/common/src/take_orders/preflight.rs, the simulate_take_orders function intentionally returns () and discards the simulation result data because it only needs to verify that the transaction doesn't revert with the given parameters, without requiring access to output amounts or other returndata.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2391
File: crates/rest_api/src/main.rs:66-201
Timestamp: 2026-01-16T11:13:10.390Z
Learning: In crates/rest_api, happy-path integration tests for the /take-orders endpoint are intentionally omitted because they would require mocking blockchain and subgraph infrastructure, which is considered too complex for the value provided. The test suite focuses on error-path validation instead.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1903
File: crates/settings/src/yaml/orderbook.rs:371-377
Timestamp: 2025-06-17T16:21:24.384Z
Learning: In crates/settings/src/yaml/orderbook.rs tests, the user findolor considers RPC ordering in Vec<Url> assertions to be intentional and not a test brittleness issue. The ordering of RPCs in tests should be preserved as specified.

Learnt from: 0xgleb
Repo: rainlanguage/rain.orderbook PR: 1911
File: crates/common/src/raindex_client/orders.rs:720-720
Timestamp: 2025-07-21T16:34:04.947Z
Learning: In the rainlanguage/rain.orderbook codebase, user 0xgleb prefers using `.unwrap()` in test code rather than `.expect()` with descriptive messages, considering the direct unwrap approach acceptable for test contexts where failures should be fast and clear.

Learnt from: brusherru
Repo: rainlanguage/rain.orderbook PR: 2036
File: packages/orderbook/test/js_api/filters.test.ts:19-36
Timestamp: 2025-08-02T03:55:25.215Z
Learning: In the rainlanguage/rain.orderbook project's WASM tests, the pattern of chaining `.value!` calls on WASM result types (like from VaultsFilterBuilder methods) is the established and preferred approach for handling WASM results, and should not be refactored into intermediate variables as it would add unnecessary verbosity without improving the code.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2391
File: crates/rest_api/src/routes/take_orders.rs:75-93
Timestamp: 2026-01-16T11:17:10.568Z
Learning: In crates/rest_api, the take_orders handler uses spawn_blocking with a dedicated single-threaded Tokio runtime per request because RaindexClient contains Rc<RefCell<...>> internally which is not Send, while Rocket requires handler futures to be Send. This pattern is intentional and required to satisfy Rust's thread-safety requirements.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2360
File: crates/common/src/test_helpers.rs:329-367
Timestamp: 2025-12-17T13:17:58.487Z
Learning: In the rainlanguage/rain.orderbook codebase, findolor considers hardcoded values (like decimals) acceptable in test helper functions such as deposit_to_orderbook in crates/common/src/test_helpers.rs, as test infrastructure doesn't need the same level of parameterization as production code.

Learnt from: 0xgleb
Repo: rainlanguage/rain.orderbook PR: 1859
File: crates/quote/src/quote_debug.rs:472-492
Timestamp: 2025-05-20T10:20:08.206Z
Learning: In the Rain Orderbook codebase, the `#[tokio::test(flavor = "multi_thread")]` annotation is specifically needed for tests that use `LocalEvm`, not just for consistency across all async tests.

Learnt from: brusherru
Repo: rainlanguage/rain.orderbook PR: 2036
File: crates/js_api/src/filters/raindex_filter_store.rs:446-533
Timestamp: 2025-08-02T02:34:32.237Z
Learning: In the rainlanguage/rain.orderbook project's RaindexFilterStore WASM tests (crates/js_api/src/filters/raindex_filter_store.rs), brusherru decided to focus on testing only methods without side effects (that don't use web_sys) due to difficulties with mocking localStorage and window APIs in the WASM test environment. This pragmatic approach tests pure logic separately from browser integration.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2117
File: crates/js_api/src/gui/select_tokens.rs:421-424
Timestamp: 2025-09-02T08:04:34.729Z
Learning: User findolor in the rainlanguage/rain.orderbook codebase prefers not to change test code when tests are already passing, even if there appear to be type mismatches or potential issues. Working tests should be left alone unless there are actual errors.

Learnt from: 0xgleb
Repo: rainlanguage/rain.orderbook PR: 1911
File: crates/subgraph/src/vault_balance_changes_query.rs:133-135
Timestamp: 2025-07-23T13:03:40.669Z
Learning: User 0xgleb prefers not to make changes to test code when tests are already passing, even if the code format could be standardized differently. Working test code should be left alone unless there's a functional issue.

Learnt from: brusherru
Repo: rainlanguage/rain.orderbook PR: 2044
File: tauri-app/src/routes/orders/[chainId]-[orderbook]-[orderHash]/+page.svelte:76-78
Timestamp: 2025-07-31T19:03:56.594Z
Learning: In the rainlanguage/rain.orderbook project, when the Tauri app has issues preventing proper testing, the team prefers to defer Tauri-specific implementations to separate PRs rather than including untested code, especially for financial operations like withdrawals.

Learnt from: 0xgleb
Repo: rainlanguage/rain.orderbook PR: 1911
File: crates/common/src/erc20.rs:336-350
Timestamp: 2025-07-21T16:33:12.129Z
Learning: In the rainlanguage/rain.orderbook codebase, user 0xgleb considers both LocalEvm-based tests and mocked tests to be "unit tests" - they don't distinguish between them as "unit" vs "integration" tests based on their implementation approach.

Learnt from: brusherru
Repo: rainlanguage/rain.orderbook PR: 2044
File: crates/common/src/raindex_client/vaults_list.rs:363-423
Timestamp: 2025-07-31T19:34:11.716Z
Learning: In the rainlanguage/rain.orderbook project, for WASM-exposed functionality like VaultsList, the team prefers to keep comprehensive tests in the non-WASM environment due to the complexity of recreating objects like RaindexVaults in WASM. WASM tests focus on basic functionality and error cases since the WASM code reuses the already-tested non-WASM implementation.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1975
File: packages/orderbook/test/js_api/gui.test.ts:958-960
Timestamp: 2025-07-09T14:00:09.618Z
Learning: In test files for the rain.orderbook project, maintainer findolor prefers to keep hard-coded serialized state strings (like gzip/base64 blobs) rather than replacing them with semantic assertions or round-trip testing, even when the hard-coded approach may be more brittle to changes.

Learnt from: 0xgleb
Repo: rainlanguage/rain.orderbook PR: 1790
File: tauri-app/src-tauri/src/commands/vault.rs:67-67
Timestamp: 2025-05-17T15:32:28.733Z
Learning: For the PR focused on testing Tauri commands::order module, the generic type parameter R: Runtime was selectively added where needed for the PR scope, applying the changes primarily to order.rs and related files while leaving other modules like vault.rs for potential future refactoring.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1907
File: packages/orderbook/test/common/test.test.ts:75-77
Timestamp: 2025-06-04T10:21:01.388Z
Learning: The DotrainOrder.create API in packages/orderbook/test/common/test.test.ts is internal and not used directly in consumer applications, so API changes here don't require external breaking change documentation.

Learnt from: CR
Repo: rainlanguage/rain.orderbook PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T16:50:31.752Z
Learning: Applies to crates/integration_tests/**/*.rs : Rust: write tests using `cargo test`; integration tests live in `crates/integration_tests`. Prefer `insta` snapshots and `proptest` where helpful

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T21:24:53.708Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, provide a separate comment after each review with PR size statistics in the format: `TOTAL=number ADDITIONS=number DELETIONS=number`, excluding lock files like package-lock.json and cargo.lock.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T21:24:53.708Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, provide a separate comment after each review with PR size statistics in the format: `TOTAL=number ADDITIONS=number DELETIONS=number`, excluding lock files like package-lock.json and cargo.lock.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T21:24:53.708Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, provide a separate comment after each review with PR size statistics in the format: `TOTAL=number ADDITIONS=number DELETIONS=number`, excluding lock files like package-lock.json and cargo.lock.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T21:24:53.708Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, provide a separate comment after each review with PR size statistics in the format: `TOTAL=number ADDITIONS=number DELETIONS=number`, excluding lock files like package-lock.json and cargo.lock.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T21:26:31.566Z
Learning: For PR `#1884` and future PRs in rainlanguage/rain.orderbook repository, provide PR size reports in a separate comment with ONLY the exact format "TOTAL=number\nADDITIONS=number\nDELETIONS=number", without any additional text or explanation. Reports should exclude irrelevant files such as lock files (package-lock.json, yarn.lock, pnpm-lock.yaml, cargo.lock).

Learnt from: hardingjam
Repo: rainlanguage/rain.orderbook PR: 1512
File: packages/ui-components/src/__tests__/OrderDetail.test.ts:125-143
Timestamp: 2025-04-08T12:56:03.272Z
Learning: The OrderDetail component in the Rain orderbook UI doesn't currently have error handling tests, but issue `#1605` has been created to address this in the future.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T22:14:22.646Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, I must consistently report the PR size after EVERY new commit in the exact format "TOTAL=number\nADDITIONS=number\nDELETIONS=number" without any additional text or formatting blocks. The report must exclude lock files (package-lock.json, yarn.lock, pnpm-lock.yaml, cargo.lock) and can be included either as part of the review or as a separate comment.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T22:14:22.646Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, I must consistently report the PR size after EVERY new commit in the exact format "TOTAL=number\nADDITIONS=number\nDELETIONS=number" without any formatting blocks. The report must exclude lock files (package-lock.json, yarn.lock, pnpm-lock.yaml, cargo.lock) and can be included either as part of the review or as a separate comment.

Comment on lines 104 to 187
pub async fn execute_single_take(
candidate: TakeOrderCandidate,
mode: ParsedTakeOrdersMode,
price_cap: Float,
taker: Address,
rpc_urls: &[Url],
block_number: Option<u64>,
) -> Result<TakeOrdersCalldataResult, RaindexError> {
let zero = Float::zero()?;

if candidate.ratio.gt(price_cap)? {
return Err(RaindexError::NoLiquidity);
}

let target = mode.target_amount();
let is_buy_mode = mode.is_buy_mode();

let (output, input) = if is_buy_mode {
let output = if candidate.max_output.lte(target)? {
candidate.max_output
} else {
target
};
let input = output.mul(candidate.ratio)?;
(output, input)
} else {
let max_input = candidate.max_output.mul(candidate.ratio)?;
let input = if max_input.lte(target)? {
max_input
} else {
target
};
let output = if candidate.ratio.eq(zero)? {
candidate.max_output
} else {
input.div(candidate.ratio)?
};
(output, input)
};

if output.lte(zero)? {
return Err(RaindexError::NoLiquidity);
}

let sim = SimulationResult {
legs: vec![SelectedTakeOrderLeg {
candidate: candidate.clone(),
input,
output,
}],
total_input: input,
total_output: output,
};

let built = build_take_orders_config_from_simulation(sim, mode, price_cap)?
.ok_or(RaindexError::NoLiquidity)?;

let provider =
mk_read_provider(rpc_urls).map_err(|e| RaindexError::PreflightError(e.to_string()))?;

let orderbook = candidate.orderbook;

let sim_result =
simulate_take_orders(&provider, orderbook, taker, &built.config, block_number).await;

match sim_result {
Ok(()) => build_calldata_result(orderbook, built, mode, price_cap),
Err(sim_error) => {
if let Some(_failing_idx) =
find_failing_order_index(&provider, orderbook, taker, &built.config, block_number)
.await
{
Err(RaindexError::PreflightError(format!(
"Order failed simulation: {}",
sim_error
)))
} else {
Err(RaindexError::PreflightError(format!(
"Simulation failed: {}",
sim_error
)))
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Avoid double simulation for single‑order preflight failures.

find_failing_order_index re‑simulates even when only one order is present. Short‑circuiting avoids an extra RPC call.

♻️ Proposed refactor
     match sim_result {
         Ok(()) => build_calldata_result(orderbook, built, mode, price_cap),
         Err(sim_error) => {
-            if let Some(_failing_idx) =
-                find_failing_order_index(&provider, orderbook, taker, &built.config, block_number)
-                    .await
-            {
-                Err(RaindexError::PreflightError(format!(
-                    "Order failed simulation: {}",
-                    sim_error
-                )))
-            } else {
-                Err(RaindexError::PreflightError(format!(
-                    "Simulation failed: {}",
-                    sim_error
-                )))
-            }
+            if built.config.orders.len() == 1 {
+                Err(RaindexError::PreflightError(format!(
+                    "Order failed simulation: {}",
+                    sim_error
+                )))
+            } else if let Some(_failing_idx) = find_failing_order_index(
+                &provider,
+                orderbook,
+                taker,
+                &built.config,
+                block_number,
+            )
+            .await
+            {
+                Err(RaindexError::PreflightError(format!(
+                    "Order failed simulation: {}",
+                    sim_error
+                )))
+            } else {
+                Err(RaindexError::PreflightError(format!(
+                    "Simulation failed: {}",
+                    sim_error
+                )))
+            }
         }
     }
🤖 Prompt for AI Agents
In `@crates/common/src/raindex_client/take_orders/single.rs` around lines 104 -
187, The code currently calls find_failing_order_index (which re-simulates) even
for single-order cases; inside execute_single_take's Err(sim_error) branch,
check if the built config contains only one order (e.g.
built.config.orders.len() == 1) and if so immediately return a PreflightError
with the simulation error (no extra RPC/simulation); otherwise keep the existing
logic that calls find_failing_order_index(...) and formats the error based on
whether it returns Some or None.

Comment on lines +223 to +234
<Button
color="alternative"
outline={false}
data-testid={`order-menu-${item.id}`}
id={`order-menu-${item.id}`}
class="border-none px-2"
on:click={(e) => {
e.stopPropagation();
}}
>
<DotsVerticalOutline class="dark:text-white" />
</Button>
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add an accessible label to the icon-only actions menu button.

Line 223-234: Screen readers will announce only “button” without context. Provide an accessible name.

Suggested fix
-							<Button
+							<Button
 								color="alternative"
 								outline={false}
 								data-testid={`order-menu-${item.id}`}
 								id={`order-menu-${item.id}`}
 								class="border-none px-2"
+								aria-label="Order actions"
+								title="Order actions"
 								on:click={(e) => {
 									e.stopPropagation();
 								}}
 							>
🤖 Prompt for AI Agents
In `@packages/ui-components/src/lib/components/tables/OrdersListTable.svelte`
around lines 223 - 234, The icon-only menu Button (the Button component
rendering DotsVerticalOutline with id/data-testid like `order-menu-${item.id}`)
lacks an accessible name; add one by providing an aria-label (e.g.,
aria-label={`Open order actions for ${item.id}`}) or an aria-labelledby that
points to a visually hidden text element inside the same component so screen
readers get context; ensure the attribute is added to the Button element that
has on:click and the DotsVerticalOutline so the button is announced
meaningfully.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed at the moment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@findolor, understood! I'll skip this for now.


🧠 Learnings used
Learnt from: hardingjam
Repo: rainlanguage/rain.orderbook PR: 1512
File: packages/ui-components/src/__tests__/OrderDetail.test.ts:0-0
Timestamp: 2025-04-09T09:28:05.097Z
Learning: The OrderDetail component has been refactored to use an `onRemove` callback approach instead of the previous `handleOrderRemoveModal` pattern for order removal functionality, as part of PR `#1512`.

Learnt from: hardyjosh
Repo: rainlanguage/rain.orderbook PR: 2035
File: packages/ui-components/src/lib/components/tables/OrdersListTable.svelte:167-174
Timestamp: 2025-07-26T06:56:28.207Z
Learning: In OrdersListTable.svelte, maintainer hardyjosh considers keyed each-blocks unnecessary for item.inputs and item.outputs arrays because the entire query result updates at once during refresh, making DOM diffing optimizations negligible when the whole dataset is replaced rather than individual items being modified.

Learnt from: brusherru
Repo: rainlanguage/rain.orderbook PR: 2083
File: packages/ui-components/src/lib/components/tables/VaultsListTable.svelte:205-205
Timestamp: 2025-08-14T14:16:34.044Z
Learning: In the VaultsListTable component, the `matchesAccount` function from the wallet provider may not properly update on wallet connect/disconnect events, making direct comparison with the reactive `$account` variable more reliable for UI visibility checks. The `matchesAccount` function needs future refactoring to handle wallet state changes properly.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2358
File: packages/ui-components/src/lib/components/tables/OrdersListTable.svelte:82-85
Timestamp: 2025-12-11T07:46:32.817Z
Learning: In packages/ui-components/src/lib/components/tables/OrdersListTable.svelte, the token filter intentionally sets both inputs and outputs to the same selectedTokens array (lines 82-85) to achieve OR filtering behavior. This is by design: the single UI token filter should match orders containing the selected token(s) in either inputs OR outputs, not pair-specific filtering. Even though the API supports more granular directional filtering, the UI uses the same tokens in both fields for broader matching.

Learnt from: brusherru
Repo: rainlanguage/rain.orderbook PR: 2002
File: tauri-app/src/lib/components/ModalMultipleVaultsWithdraw.svelte:20-25
Timestamp: 2025-07-24T08:16:47.393Z
Learning: In the Rain OrderBook Tauri app, modal components directly mutate the `open` prop (setting `open = false`) as a consistent pattern across the codebase. This approach is maintained for consistency even though it violates typical Svelte one-way data flow, with plans to refactor all modals together in the future rather than making isolated changes.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2384
File: packages/ui-components/src/lib/components/dropdown/DropdownOrderbooksFilter.svelte:95-107
Timestamp: 2026-01-07T10:38:00.196Z
Learning: In DropdownOrderbooksFilter.svelte, selectedOrderbookAddresses is a filtered view (by selected chain IDs) while the activeOrderbookAddresses store holds all user selections across all chains. The toggleOrderbook function intentionally checks selection state against the filtered view (selectedOrderbookAddresses) but modifies the full store ($activeOrderbookAddresses) to preserve selections even when chain filters change.

Learnt from: brusherru
Repo: rainlanguage/rain.orderbook PR: 1957
File: packages/ui-components/src/lib/components/tables/VaultsListTable.svelte:60-60
Timestamp: 2025-06-27T17:34:15.825Z
Learning: In the VaultsListTable.svelte component, the `activeAccounts` store is redundant in query keys when the `owners` variable (derived from `activeAccountsItems`) is already included, as `owners` is what's actually used in the query function while `activeAccounts` is just a transformed version of the same data.

Learnt from: hardingjam
Repo: rainlanguage/rain.orderbook PR: 1504
File: packages/ui-components/src/lib/components/detail/VaultDetail.svelte:85-102
Timestamp: 2025-04-08T08:53:50.707Z
Learning: For icon-only buttons in the Rain UI components, aria-labels should be added to ensure accessibility for screen reader users.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T21:24:53.708Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, provide a separate comment after each review with PR size statistics in the format: `TOTAL=number ADDITIONS=number DELETIONS=number`, excluding lock files like package-lock.json and cargo.lock.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T21:24:53.708Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, provide a separate comment after each review with PR size statistics in the format: `TOTAL=number ADDITIONS=number DELETIONS=number`, excluding lock files like package-lock.json and cargo.lock.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T21:24:53.708Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, provide a separate comment after each review with PR size statistics in the format: `TOTAL=number ADDITIONS=number DELETIONS=number`, excluding lock files like package-lock.json and cargo.lock.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T21:24:53.708Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, provide a separate comment after each review with PR size statistics in the format: `TOTAL=number ADDITIONS=number DELETIONS=number`, excluding lock files like package-lock.json and cargo.lock.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T21:26:31.566Z
Learning: For PR `#1884` and future PRs in rainlanguage/rain.orderbook repository, provide PR size reports in a separate comment with ONLY the exact format "TOTAL=number\nADDITIONS=number\nDELETIONS=number", without any additional text or explanation. Reports should exclude irrelevant files such as lock files (package-lock.json, yarn.lock, pnpm-lock.yaml, cargo.lock).

Learnt from: hardingjam
Repo: rainlanguage/rain.orderbook PR: 1512
File: packages/ui-components/src/__tests__/OrderDetail.test.ts:125-143
Timestamp: 2025-04-08T12:56:03.272Z
Learning: The OrderDetail component in the Rain orderbook UI doesn't currently have error handling tests, but issue `#1605` has been created to address this in the future.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T22:14:22.646Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, I must consistently report the PR size after EVERY new commit in the exact format "TOTAL=number\nADDITIONS=number\nDELETIONS=number" without any additional text or formatting blocks. The report must exclude lock files (package-lock.json, yarn.lock, pnpm-lock.yaml, cargo.lock) and can be included either as part of the review or as a separate comment.

Learnt from: rouzwelt
Repo: rainlanguage/rain.orderbook PR: 0
File: :0-0
Timestamp: 2025-05-21T22:14:22.646Z
Learning: For PR `#1884` in rainlanguage/rain.orderbook, I must consistently report the PR size after EVERY new commit in the exact format "TOTAL=number\nADDITIONS=number\nDELETIONS=number" without any formatting blocks. The report must exclude lock files (package-lock.json, yarn.lock, pnpm-lock.yaml, cargo.lock) and can be included either as part of the review or as a separate comment.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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.

Learnt from: brusherru
Repo: rainlanguage/rain.orderbook PR: 2044
File: tauri-app/src/routes/orders/[chainId]-[orderbook]-[orderHash]/+page.svelte:76-78
Timestamp: 2025-07-31T19:03:56.594Z
Learning: In the rainlanguage/rain.orderbook project, when the Tauri app has issues preventing proper testing, the team prefers to defer Tauri-specific implementations to separate PRs rather than including untested code, especially for financial operations like withdrawals.

Comment on lines 43 to 70
const loadQuotes = async (isRefresh = false) => {
if (isRefresh) {
isFetchingQuotes = true;
} else {
isLoadingQuotes = true;
}

try {
const result = await order.getQuotes();
if (result.error) {
if (!isRefresh) {
errorMessage = result.error.readableMsg;
}
return;
}
quotes = result.value.filter((q: RaindexOrderQuote) => q.success && q.data);
if (quotes.length === 0 && !isRefresh) {
errorMessage = 'No valid quotes available for this order';
}
} catch (e) {
if (!isRefresh) {
errorMessage = e instanceof Error ? e.message : 'Failed to load quotes';
}
} finally {
isLoadingQuotes = false;
isFetchingQuotes = false;
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clear stale errors and clamp pair selection after quote refresh.

Line 43-70: if a refresh succeeds after an earlier error, errorMessage remains visible; and selectedPairIndex can point past quotes.length if the list shrinks. Reset/clamp on successful load.

Suggested fix
 		try {
 			const result = await order.getQuotes();
 			if (result.error) {
 				if (!isRefresh) {
 					errorMessage = result.error.readableMsg;
 				}
 				return;
 			}
 			quotes = result.value.filter((q: RaindexOrderQuote) => q.success && q.data);
+			if (quotes.length > 0) {
+				errorMessage = '';
+			}
+			if (selectedPairIndex >= quotes.length) {
+				selectedPairIndex = 0;
+			}
 			if (quotes.length === 0 && !isRefresh) {
 				errorMessage = 'No valid quotes available for this order';
 			}
 		} catch (e) {
📝 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.

Suggested change
const loadQuotes = async (isRefresh = false) => {
if (isRefresh) {
isFetchingQuotes = true;
} else {
isLoadingQuotes = true;
}
try {
const result = await order.getQuotes();
if (result.error) {
if (!isRefresh) {
errorMessage = result.error.readableMsg;
}
return;
}
quotes = result.value.filter((q: RaindexOrderQuote) => q.success && q.data);
if (quotes.length === 0 && !isRefresh) {
errorMessage = 'No valid quotes available for this order';
}
} catch (e) {
if (!isRefresh) {
errorMessage = e instanceof Error ? e.message : 'Failed to load quotes';
}
} finally {
isLoadingQuotes = false;
isFetchingQuotes = false;
}
};
const loadQuotes = async (isRefresh = false) => {
if (isRefresh) {
isFetchingQuotes = true;
} else {
isLoadingQuotes = true;
}
try {
const result = await order.getQuotes();
if (result.error) {
if (!isRefresh) {
errorMessage = result.error.readableMsg;
}
return;
}
quotes = result.value.filter((q: RaindexOrderQuote) => q.success && q.data);
if (quotes.length > 0) {
errorMessage = '';
}
if (selectedPairIndex >= quotes.length) {
selectedPairIndex = 0;
}
if (quotes.length === 0 && !isRefresh) {
errorMessage = 'No valid quotes available for this order';
}
} catch (e) {
if (!isRefresh) {
errorMessage = e instanceof Error ? e.message : 'Failed to load quotes';
}
} finally {
isLoadingQuotes = false;
isFetchingQuotes = false;
}
};
🤖 Prompt for AI Agents
In `@packages/webapp/src/lib/components/TakeOrderModal.svelte` around lines 43 -
70, In loadQuotes, ensure stale errors are cleared and selectedPairIndex is
clamped after a successful refresh: when order.getQuotes() succeeds (after
filtering into quotes) reset errorMessage to an empty string and then adjust
selectedPairIndex so it is within [0, quotes.length-1] (or set to -1/0 if you
represent "none selected") so it cannot point past the end when quotes shrank;
do this in the success path right after assigning quotes (and before returning)
so both normal loads and isRefresh loads clear errors and clamp selection.

When a single order fails simulation, skip the redundant call to
find_failing_order_index since we already know the failing order is
index 0. This saves an unnecessary RPC call.
- Clear errorMessage when quotes load successfully
- Reset selectedPairIndex to 0 if it exceeds new quotes length
- Add tests for both behaviors
Merge commit 9a3244c which updates e2e tests to use
TakeOrdersCalldataResult enum pattern for approval/ready states.
Add approval flow to execute_single_take:
- Check token allowance before building take order calldata
- Return NeedsApproval result with approval calldata when insufficient
- Update tests to use getter methods on result types
- Export approval module as pub(crate) for internal use
- processSubmit now checks isNeedsApproval and shows approval modal
- Returns boolean to indicate whether to close TakeOrderModal
- Modal stays open after successful approval with toast message
- User clicks "Take Order" again to proceed with fresh quotes
- Update tests with approval flow mocks and assertions
- Wire up addToast in order pages
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add UI button to execute take orders for individual orders

2 participants