Skip to content

feat: add strategy-builder CLI subcommand#2544

Open
hardyjosh wants to merge 1 commit into
mainfrom
strategy-builder-cli
Open

feat: add strategy-builder CLI subcommand#2544
hardyjosh wants to merge 1 commit into
mainfrom
strategy-builder-cli

Conversation

@hardyjosh
Copy link
Copy Markdown
Contributor

@hardyjosh hardyjosh commented Apr 12, 2026

Motivation

The Raindex orderbook protocol has a rich GUI builder flow in the webapp for configuring and deploying strategies. There is currently no way to do the same from a terminal or from a non-interactive agent — the webapp is the only entry point. That forces anyone automating a deployment (CI, scripts, AI agents) to either drive the browser or hand-roll the calldata.

Solution

Add a strategy-builder subcommand to the raindex CLI that generates deployment calldata from a remote registry strategy.

raindex strategy-builder \
  --registry <url> \
  --strategy <key> \
  --deployment <key> \
  --owner <0x-address> \
  [--select-token KEY=ADDRESS ...] \
  [--set-field BINDING=VALUE ...] \
  [--set-deposit TOKEN=AMOUNT ...]

Outputs one <address>:<calldata> line per transaction on stdout — approvals first, then the deployment multicall, then optional metaboard meta emission. Each line is one signable transaction.

Implementation reuses RaindexOrderBuilder from the common crate (same object the webapp drives) and DotrainRegistry from the js_api crate, so the CLI and webapp use identical resolution semantics.

Follow-up PRs in this stack add --interactive (#2546), --tokens (#2549), the template-fallback operator (#2551), and --describe (#2548).

Checks

  • 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)

Summary by CodeRabbit

  • New Features
    • Added strategy-builder command to the CLI for generating deployment calldata from registry-based strategies. The command accepts required parameters for registry URL, strategy identifier, deployment address, and owner. Users can further customize strategy deployments with repeatable options to set field bindings, select tokens, and configure deposit amounts, enabling streamlined deployment workflows.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 2026

Warning

Rate limit exceeded

@graphite-app[bot] has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 56 minutes and 18 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a2adc1a3-e9cd-4263-8595-d9bf31ae3ce2

📥 Commits

Reviewing files that changed from the base of the PR and between a1e2ce7 and 50d4083.

📒 Files selected for processing (4)
  • crates/cli/Cargo.toml
  • crates/cli/src/commands/mod.rs
  • crates/cli/src/commands/strategy_builder.rs
  • crates/cli/src/lib.rs
📝 Walkthrough

Walkthrough

This PR adds a new strategy-builder CLI subcommand that enables users to load a strategy from a registry, apply custom field bindings and token configurations, and generate deployment calldata for execution. The implementation includes input validation, registry integration, and comprehensive unit tests.

Changes

Strategy Builder CLI Command

Layer / File(s) Summary
CLI Schema Definition
crates/cli/src/commands/strategy_builder.rs
StrategyBuilder struct with clap-derived options: required --registry, --strategy, --deployment, --owner; repeatable --set-field, --select-token, --set-deposit.
Input Validation Utility
crates/cli/src/commands/strategy_builder.rs
parse_key_value_pairs function parses KEY=VALUE strings into HashMap, validating for missing =, empty/whitespace keys, and duplicate keys.
Command Execution
crates/cli/src/commands/strategy_builder.rs
Execute::execute loads registry, retrieves strategy/order, builds RaindexOrderBuilder with parsed configs, generates deployment transaction args, and outputs approvals and calldata.
Unit Tests
crates/cli/src/commands/strategy_builder.rs
Tests verify CLI parsing and parse_key_value_pairs behavior for valid inputs and error cases (missing separator, empty keys, duplicate keys).
Module Exports
crates/cli/src/commands/mod.rs
Declares strategy_builder as public submodule; re-exports StrategyBuilder in module's public API.
CLI Dispatcher Integration
crates/cli/src/lib.rs
Adds StrategyBuilder variant to Orderbook enum; extends execute match dispatch to handle new subcommand.
Dependency Management
crates/cli/Cargo.toml
Adds rain_orderbook_js_api workspace dependency.

🎯 2 (Simple) | ⏱️ ~12 minutes

🐰 A strategy builder hops through code so clear,
With registries, configs, and calldata near,
Key-value pairs parsed with validation so keen,
The finest deployment command we've ever seen! 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 directly and clearly describes the main change: adding a new CLI subcommand called 'strategy-builder'.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch strategy-builder-cli

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.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 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.

Copy link
Copy Markdown
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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/cli/src/commands/strategy_builder.rs`:
- Around line 48-55: parse_key_value_pairs currently silently overwrites
duplicate keys; update it to detect duplicate keys and return an error instead
of replacing the previous entry. In the function parse_key_value_pairs(args:
&[String]) -> Result<HashMap<String, String>>, after splitting into key/value
check whether map.contains_key(&key.to_string()) (or lookups using &key) and if
present return an anyhow::anyhow! error indicating a duplicate override for that
key and the original arg; only insert when the key is new. Ensure the function
returns an Err on duplicates so callers cannot proceed with silent collisions.
- Around line 86-89: The parser currently uses line.split_once(' ') which fails
on tabs or multiple/mixed whitespace; change the logic that extracts key and
url_str from each line to use line.split_whitespace() (collect into fields),
validate that fields.len() == 2 and return the same anyhow::anyhow!("invalid
registry line (expected 'key url'): {line}') error if not, then set key =
fields[0] and url_str = fields[1] before calling
order_urls.insert(key.to_string(), url_str.trim().to_string()) so the code
accepts any whitespace delimiter.
- Around line 59-65: The fetch_text function currently calls reqwest::get
without a timeout and can block indefinitely; change it to build and reuse a
reqwest::Client with an explicit timeout (e.g., Duration::from_secs(10)) and
perform client.get(url.as_str()).send().await instead of reqwest::get, ensuring
you propagate errors as before; update fetch_text to accept or create that
client (or create a local client with Client::builder().timeout(...).build()?)
and use response.text().await? to return the body. Also add the necessary
std::time::Duration import and preserve the existing HTTP error handling around
response.status() and anyhow::bail! when non-success.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c75248ce-047a-4e27-b189-fdb73b5de209

📥 Commits

Reviewing files that changed from the base of the PR and between e6f03ef and 33fb668.

📒 Files selected for processing (4)
  • crates/cli/Cargo.toml
  • crates/cli/src/commands/mod.rs
  • crates/cli/src/commands/strategy_builder.rs
  • crates/cli/src/lib.rs

Comment thread crates/cli/src/commands/strategy_builder.rs
Comment thread crates/cli/src/commands/strategy_builder.rs Outdated
Comment thread crates/cli/src/commands/strategy_builder.rs Outdated
@hardyjosh
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 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.

Copy link
Copy Markdown
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.

♻️ Duplicate comments (1)
crates/cli/src/commands/strategy_builder.rs (1)

48-60: ⚠️ Potential issue | 🟡 Minor

Reject empty KEY values during KEY=VALUE parsing.

=value currently passes parsing and creates an empty key, which degrades CLI validation and error clarity. Validate non-empty trimmed keys at parse time and add a test for it.

Suggested patch
 fn parse_key_value_pairs(args: &[String]) -> Result<HashMap<String, String>> {
     let mut map = HashMap::new();
     for arg in args {
-        let (key, value) = arg
+        let (raw_key, value) = arg
             .split_once('=')
             .ok_or_else(|| anyhow::anyhow!("expected KEY=VALUE, got: {arg}"))?;
+        let key = raw_key.trim();
+        if key.is_empty() {
+            anyhow::bail!("expected non-empty KEY in KEY=VALUE, got: {arg}");
+        }
         if map.contains_key(key) {
             anyhow::bail!("duplicate key: {key}");
         }
         map.insert(key.to_string(), value.to_string());
     }
     Ok(map)
 }
@@
     fn parse_key_value_pairs_duplicate_key_fails() {
         let args = vec!["key=first".to_string(), "key=second".to_string()];
         let err = parse_key_value_pairs(&args).unwrap_err().to_string();
         assert!(err.contains("duplicate key: key"), "got: {err}");
     }
+
+    #[test]
+    fn parse_key_value_pairs_empty_key_fails() {
+        let args = vec!["=value".to_string()];
+        let err = parse_key_value_pairs(&args).unwrap_err().to_string();
+        assert!(err.contains("expected non-empty KEY"), "got: {err}");
+    }
 }

Also applies to: 165-202

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/cli/src/commands/strategy_builder.rs` around lines 48 - 60,
parse_key_value_pairs accepts inputs like "=value" producing empty keys; update
the function (parse_key_value_pairs) to trim the key part, reject empty keys by
returning an error (e.g., bail or anyhow::anyhow! with a clear message
referencing the original arg), and keep the duplicate-key check and insertion
behavior. Apply the same non-empty-trimmed-key validation to the other parsing
occurrence in this file (the duplicate block around the later function), and add
a unit test that asserts parsing "=value" (and keys with only whitespace)
returns an error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/cli/src/commands/strategy_builder.rs`:
- Around line 48-60: parse_key_value_pairs accepts inputs like "=value"
producing empty keys; update the function (parse_key_value_pairs) to trim the
key part, reject empty keys by returning an error (e.g., bail or anyhow::anyhow!
with a clear message referencing the original arg), and keep the duplicate-key
check and insertion behavior. Apply the same non-empty-trimmed-key validation to
the other parsing occurrence in this file (the duplicate block around the later
function), and add a unit test that asserts parsing "=value" (and keys with only
whitespace) returns an error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5ccb4e7f-ad28-409d-a17f-fce8982ea92f

📥 Commits

Reviewing files that changed from the base of the PR and between 33fb668 and ba92f20.

📒 Files selected for processing (2)
  • crates/cli/Cargo.toml
  • crates/cli/src/commands/strategy_builder.rs

@hardyjosh hardyjosh force-pushed the strategy-builder-cli branch from ba92f20 to 9a5eb91 Compare April 12, 2026 20:01
Copy link
Copy Markdown
Contributor Author

hardyjosh commented Apr 12, 2026


How to use the Graphite Merge Queue

Add the label Raindex-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@hardyjosh
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 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.

Copy link
Copy Markdown
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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/cli/src/commands/strategy_builder.rs`:
- Around line 77-82: The error message builds its "available" list from
registry.get_order_keys() which relies on map iteration order and yields
non-deterministic text; change the code that assigns available (from
registry.get_order_keys().unwrap_or_default()) to collect into a Vec, sort it
(e.g., sort_unstable or sort()), and then use that sorted Vec in the anyhow!
message so the "Available: {:?}" output is deterministic; update the variable
named available near the anyhow! call in strategy_builder.rs accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 81056df2-7093-4391-9355-e31e7fee657e

📥 Commits

Reviewing files that changed from the base of the PR and between ba92f20 and 875877e.

📒 Files selected for processing (5)
  • crates/cli/Cargo.toml
  • crates/cli/src/commands/mod.rs
  • crates/cli/src/commands/strategy_builder.rs
  • crates/cli/src/lib.rs
  • crates/settings/src/remote/tokens.rs

Comment thread crates/cli/src/commands/strategy_builder.rs Outdated
Copy link
Copy Markdown
Contributor Author

hardyjosh commented May 11, 2026

Merge activity

@hardyjosh hardyjosh changed the base branch from 2026-02-23-js-api-consolidation to graphite-base/2544 May 11, 2026 09:38
graphite-app Bot pushed a commit that referenced this pull request May 11, 2026
## Motivation

The Raindex orderbook protocol has a rich GUI builder flow in the webapp for configuring and deploying strategies. There is currently no way to do the same from a terminal or from a non-interactive agent — the webapp is the only entry point. That forces anyone automating a deployment (CI, scripts, AI agents) to either drive the browser or hand-roll the calldata.

## Solution

Add a `strategy-builder` subcommand to the raindex CLI that generates deployment calldata from a remote registry strategy.

```
raindex strategy-builder \
  --registry <url> \
  --strategy <key> \
  --deployment <key> \
  --owner <0x-address> \
  [--select-token KEY=ADDRESS ...] \
  [--set-field BINDING=VALUE ...] \
  [--set-deposit TOKEN=AMOUNT ...]
```

Outputs one `<address>:<calldata>` line per transaction on stdout — approvals first, then the deployment multicall, then optional metaboard meta emission. Each line is one signable transaction.

Implementation reuses `RaindexOrderBuilder` from the common crate (same object the webapp drives) and `DotrainRegistry` from the js_api crate, so the CLI and webapp use identical resolution semantics.

Follow-up PRs in this stack add `--interactive` (#2546), `--tokens` (#2549), the template-fallback operator (#2551), and `--describe` (#2548).

## Checks

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

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added a new `strategy-builder` command that generates deployment calldata from registry strategies with configurable field bindings, token selections, and deposit amounts.

* **Improvements**
  * Token logo URIs are now optional, improving compatibility with token data sources.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@graphite-app graphite-app Bot force-pushed the strategy-builder-cli branch from ea4b4dd to 70fbd42 Compare May 11, 2026 10:04
@graphite-app graphite-app Bot changed the base branch from graphite-base/2544 to 2026-02-23-js-api-consolidation May 11, 2026 10:04
@graphite-app graphite-app Bot changed the base branch from 2026-02-23-js-api-consolidation to main May 11, 2026 11:16
graphite-app Bot pushed a commit that referenced this pull request May 11, 2026
## Motivation

The Raindex orderbook protocol has a rich GUI builder flow in the webapp for configuring and deploying strategies. There is currently no way to do the same from a terminal or from a non-interactive agent — the webapp is the only entry point. That forces anyone automating a deployment (CI, scripts, AI agents) to either drive the browser or hand-roll the calldata.

## Solution

Add a `strategy-builder` subcommand to the raindex CLI that generates deployment calldata from a remote registry strategy.

```
raindex strategy-builder \
  --registry <url> \
  --strategy <key> \
  --deployment <key> \
  --owner <0x-address> \
  [--select-token KEY=ADDRESS ...] \
  [--set-field BINDING=VALUE ...] \
  [--set-deposit TOKEN=AMOUNT ...]
```

Outputs one `<address>:<calldata>` line per transaction on stdout — approvals first, then the deployment multicall, then optional metaboard meta emission. Each line is one signable transaction.

Implementation reuses `RaindexOrderBuilder` from the common crate (same object the webapp drives) and `DotrainRegistry` from the js_api crate, so the CLI and webapp use identical resolution semantics.

Follow-up PRs in this stack add `--interactive` (#2546), `--tokens` (#2549), the template-fallback operator (#2551), and `--describe` (#2548).

## Checks

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

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added a new `strategy-builder` command that generates deployment calldata from registry strategies with configurable field bindings, token selections, and deposit amounts.

* **Improvements**
  * Token logo URIs are now optional, improving compatibility with token data sources.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@graphite-app graphite-app Bot force-pushed the strategy-builder-cli branch from 70fbd42 to a1e2ce7 Compare May 11, 2026 12:27
## Motivation

The Raindex orderbook protocol has a rich GUI builder flow in the webapp for configuring and deploying strategies. There is currently no way to do the same from a terminal or from a non-interactive agent — the webapp is the only entry point. That forces anyone automating a deployment (CI, scripts, AI agents) to either drive the browser or hand-roll the calldata.

## Solution

Add a `strategy-builder` subcommand to the raindex CLI that generates deployment calldata from a remote registry strategy.

```
raindex strategy-builder \
  --registry <url> \
  --strategy <key> \
  --deployment <key> \
  --owner <0x-address> \
  [--select-token KEY=ADDRESS ...] \
  [--set-field BINDING=VALUE ...] \
  [--set-deposit TOKEN=AMOUNT ...]
```

Outputs one `<address>:<calldata>` line per transaction on stdout — approvals first, then the deployment multicall, then optional metaboard meta emission. Each line is one signable transaction.

Implementation reuses `RaindexOrderBuilder` from the common crate (same object the webapp drives) and `DotrainRegistry` from the js_api crate, so the CLI and webapp use identical resolution semantics.

Follow-up PRs in this stack add `--interactive` (#2546), `--tokens` (#2549), the template-fallback operator (#2551), and `--describe` (#2548).

## Checks

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

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Added `strategy-builder` command to the CLI for generating deployment calldata from registry-based strategies. The command accepts required parameters for registry URL, strategy identifier, deployment address, and owner. Users can further customize strategy deployments with repeatable options to set field bindings, select tokens, and configure deposit amounts, enabling streamlined deployment workflows.

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/rainlanguage/raindex/pull/2544)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@graphite-app graphite-app Bot force-pushed the strategy-builder-cli branch from a1e2ce7 to 50d4083 Compare May 11, 2026 12:31
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.

2 participants