Implement POST /v1/swap/calldata with approval-aware response#46
Implement POST /v1/swap/calldata with approval-aware response#46
Conversation
Brings in TakeOrdersRequest, TakeOrdersCalldataResult, and approval_info/take_orders_info APIs needed for the calldata endpoint.
Add pub(crate) CHAIN_ID constant in main.rs and use it in tokens.rs instead of a hardcoded value.
Clone is needed for the mock data source to return cloned error results. The taker field identifies who will execute the swap on-chain.
Add get_calldata to SwapDataSource trait backed by the library's get_take_orders_calldata. The handler builds a TakeOrdersRequest from the caller's parameters and returns either approval instructions or ready-to-submit takeOrders4 calldata. Includes 7 unit tests covering ready, needs-approval, not-found, bad-request, internal-error, 401 auth, and 500 client init scenarios.
📝 WalkthroughWalkthroughThis PR introduces calldata endpoint functionality for swap operations by integrating with TakeOrders, adding a configurable chain ID constant, and extending the API response types to support approval and calldata information. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as post_swap_calldata
participant Logic as process_swap_calldata
participant Trait as SwapDataSource
participant Impl as RaindexSwapDataSource
participant Raindex as Raindex Client
Client->>API: POST /swap/calldata (SwapCalldataRequest)
API->>Logic: process_swap_calldata(ds, request)
Logic->>Logic: Create TakeOrdersRequest from request fields
Logic->>Trait: get_calldata(take_req)
Trait->>Impl: get_calldata(take_req)
Impl->>Raindex: get_take_orders_calldata(request)
Raindex-->>Impl: Result (approvals or take_orders info)
Impl->>Impl: Map result and construct SwapCalldataResponse
Impl-->>Trait: SwapCalldataResponse
Trait-->>Logic: SwapCalldataResponse
Logic-->>API: SwapCalldataResponse
API-->>Client: 200 OK / 404 No liquidity / Error
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/routes/swap/mod.rs (1)
82-124: Approval response has emptysymbolfield, preventing clients from displaying token names.Line 103 sets
symbol: String::new()because the underlying library'sapproval_infoobject doesn't provide symbol information. TheApprovalstruct's OpenAPI schema showsexample = "USDC", but clients receive an empty string. Either populatesymbolby looking up the token address in a registry, document that clients must resolve symbols client-side, or update the schema example to reflect that approval symbols are empty.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/swap/mod.rs` around lines 82 - 124, The approval response currently sets Approval.symbol to an empty string in get_calldata when using approval_info(), which prevents clients from showing token names; fix by resolving the token symbol from a token registry/metadata source before constructing the Approval (e.g., call your token lookup function or TokenRegistry::get_symbol(approval_info.token()) and set symbol to the returned value, falling back to an empty string or None if not found), update the code that builds the Approval in get_calldata to use that resolved symbol, and if you prefer not to perform server-side lookups instead document this behavior or change the OpenAPI example for Approval to reflect that symbol may be empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/rain.orderbook`:
- Line 1: The PR references commit d9fff371 which is an untagged floating HEAD
and does not export the expected symbols (TakeOrdersRequest,
TakeOrdersCalldataResult, approval_info, take_orders_info); instead the module
exports BuyRequest, SellRequest, TakeOrdersApiResponse, ApprovalApiResponse, and
TakeOrdersReadyResponse. Verify and pin the correct revision: either switch the
dependency to the intended tagged commit or update the code to use the actual
exported symbols. Update any import/exports to match the real API (replace
references to
TakeOrdersRequest/TakeOrdersCalldataResult/approval_info/take_orders_info with
the corresponding real types or add adapter types/mappings), and ensure the
dependency reference is a stable tag or SHA to fix reproducibility.
---
Nitpick comments:
In `@src/routes/swap/mod.rs`:
- Around line 82-124: The approval response currently sets Approval.symbol to an
empty string in get_calldata when using approval_info(), which prevents clients
from showing token names; fix by resolving the token symbol from a token
registry/metadata source before constructing the Approval (e.g., call your token
lookup function or TokenRegistry::get_symbol(approval_info.token()) and set
symbol to the returned value, falling back to an empty string or None if not
found), update the code that builds the Approval in get_calldata to use that
resolved symbol, and if you prefer not to perform server-side lookups instead
document this behavior or change the OpenAPI example for Approval to reflect
that symbol may be empty.
| @@ -1 +1 @@ | |||
| Subproject commit 420bab786c49c3f2fb94e19d15826b0f79608525 | |||
| Subproject commit d9fff371d75d9d3d25087d20c77209b430a5c248 | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify the submodule commit and expected API surface.
# 1. Show what the submodule currently points to (commit + any associated tag)
git submodule status lib/rain.orderbook
# 2. Check whether d9fff371 is reachable from a named tag in the submodule
cd lib/rain.orderbook && git describe --tags d9fff371d75d9d3d25087d20c77209b430a5c248 2>/dev/null || echo "No tag found for this commit"
# 3. Confirm the four required symbols are publicly exported
rg -n "pub.*TakeOrdersRequest\|pub.*TakeOrdersCalldataResult\|pub fn approval_info\|pub fn take_orders_info" lib/rain.orderbookRepository: ST0x-Technology/st0x.rest.api
Length of output: 238
🏁 Script executed:
#!/bin/bash
# Check submodule configuration and verify commit in remote
# 1. Read .gitmodules to find the remote URL
echo "=== Submodule configuration ==="
cat .gitmodules | grep -A 3 "rain.orderbook"
# 2. Extract the submodule URL
SUBMODULE_URL=$(git config --file .gitmodules --get submodule.lib/rain.orderbook.url)
echo "Submodule URL: $SUBMODULE_URL"
# 3. Try to verify the commit exists in the remote using git ls-remote
if [ ! -z "$SUBMODULE_URL" ]; then
echo ""
echo "=== Verifying commit d9fff371 exists in remote ==="
git ls-remote "$SUBMODULE_URL" d9fff371d75d9d3d25087d20c77209b430a5c248
fi
# 4. Check if it's a GitHub URL and try to access the API
if [[ "$SUBMODULE_URL" == *"github.com"* ]]; then
echo ""
echo "=== Attempting to verify via GitHub API ==="
# Extract owner/repo from URL
REPO_PATH=$(echo "$SUBMODULE_URL" | sed 's|.*github.com[:/]\(.*\)\.git$|\1|')
echo "Repo path: $REPO_PATH"
# Try to fetch commit info from GitHub
curl -s "https://api.github.com/repos/$REPO_PATH/commits/d9fff371d75d9d3d25087d20c77209b430a5c248" | head -30
fiRepository: ST0x-Technology/st0x.rest.api
Length of output: 2817
🏁 Script executed:
#!/bin/bash
# Verify commit content and search for tags
# 1. Check if there are any tags pointing to or near this commit
echo "=== Checking for tags in rain.orderbook repo ==="
curl -s "https://api.github.com/repos/rainlanguage/rain.orderbook/git/refs/tags" | \
jq -r '.[] | select(.object.sha == "d9fff371d75d9d3d25087d20c77209b430a5c248") | .ref' || echo "No tags found for this commit"
# 2. List recent tags to see version scheme
echo ""
echo "=== Recent tags in rain.orderbook ==="
curl -s "https://api.github.com/repos/rainlanguage/rain.orderbook/git/refs/tags?per_page=10" | jq -r '.[].ref' | head -20
# 3. Search for the four expected symbols in the repository using raw GitHub content API
echo ""
echo "=== Searching for expected API symbols ==="
# Create a temp file for results
RESULTS_FILE="/tmp/api_search.txt"
> "$RESULTS_FILE"
# Search for TakeOrdersRequest
echo "Searching for TakeOrdersRequest..." >> "$RESULTS_FILE"
curl -s "https://api.github.com/search/code?q=repo:rainlanguage/rain.orderbook+TakeOrdersRequest+language:rust" | \
jq -r '.items[0:3] | .[] | "\(.path): \(.name)"' >> "$RESULTS_FILE" 2>&1
echo "" >> "$RESULTS_FILE"
echo "Searching for TakeOrdersCalldataResult..." >> "$RESULTS_FILE"
curl -s "https://api.github.com/search/code?q=repo:rainlanguage/rain.orderbook+TakeOrdersCalldataResult+language:rust" | \
jq -r '.items[0:3] | .[] | "\(.path): \(.name)"' >> "$RESULTS_FILE" 2>&1
echo "" >> "$RESULTS_FILE"
echo "Searching for approval_info..." >> "$RESULTS_FILE"
curl -s "https://api.github.com/search/code?q=repo:rainlanguage/rain.orderbook+approval_info+language:rust" | \
jq -r '.items[0:3] | .[] | "\(.path): \(.name)"' >> "$RESULTS_FILE" 2>&1
echo "" >> "$RESULTS_FILE"
echo "Searching for take_orders_info..." >> "$RESULTS_FILE"
curl -s "https://api.github.com/search/code?q=repo:rainlanguage/rain.orderbook+take_orders_info+language:rust" | \
jq -r '.items[0:3] | .[] | "\(.path): \(.name)"' >> "$RESULTS_FILE" 2>&1
cat "$RESULTS_FILE"Repository: ST0x-Technology/st0x.rest.api
Length of output: 1437
🏁 Script executed:
#!/bin/bash
# Direct inspection of commit tree and file content
# 1. Get the commit tree structure
echo "=== Commit d9fff371 tree structure ==="
curl -s "https://api.github.com/repos/rainlanguage/rain.orderbook/git/trees/1e4837b8f74fccb6c898c9142dec92104de809a0?recursive=1" | \
jq -r '.tree[] | select(.type == "blob") | .path' | grep -E "\.(rs|sol)$" | head -20
# 2. Search within the repo using a simpler pattern - check if files exist with these names
echo ""
echo "=== Checking main source files ==="
curl -s "https://api.github.com/repos/rainlanguage/rain.orderbook/contents/" | jq -r '.[] | select(.type == "dir") | .name'
# 3. Try a direct search on the main branch for the symbols
echo ""
echo "=== Searching main branch for symbols (using raw search) ==="
for symbol in "TakeOrdersRequest" "TakeOrdersCalldataResult" "approval_info" "take_orders_info"; do
echo "Searching for: $symbol"
curl -s -H "Accept: application/vnd.github.v3.raw" \
"https://api.github.com/search/code?q=$symbol+repo:rainlanguage/rain.orderbook&per_page=1" | \
head -5
echo ""
doneRepository: ST0x-Technology/st0x.rest.api
Length of output: 1951
🏁 Script executed:
#!/bin/bash
# Access raw file content directly without authentication
# 1. Get the main lib.rs to see what's exported
echo "=== Checking main crate lib.rs for API exports ==="
curl -s "https://raw.githubusercontent.com/rainlanguage/rain.orderbook/d9fff371d75d9d3d25087d20c77209b430a5c248/crates/bindings/src/lib.rs" | head -100
echo ""
echo "=== Searching for TakeOrdersRequest in the commit ==="
curl -s "https://raw.githubusercontent.com/rainlanguage/rain.orderbook/d9fff371d75d9d3d25087d20c77209b430a5c248/crates/bindings/src/lib.rs" | grep -i "takeorders"
# 2. Check if there's a root lib.rs or main module
echo ""
echo "=== Checking root src structure ==="
curl -s "https://api.github.com/repos/rainlanguage/rain.orderbook/contents/src?ref=d9fff371d75d9d3d25087d20c77209b430a5c248" | jq -r '.[] | .name' 2>/dev/null || echo "No src directory at root"
# 3. List crates to identify the main one
echo ""
echo "=== Listing crates directory ==="
curl -s "https://api.github.com/repos/rainlanguage/rain.orderbook/contents/crates?ref=d9fff371d75d9d3d25087d20c77209b430a5c248" | jq -r '.[] | .name'
# 4. Check the main orderbook crate if it exists
echo ""
echo "=== Checking for orderbook crate ==="
curl -s "https://api.github.com/repos/rainlanguage/rain.orderbook/contents/crates/orderbook/src/lib.rs?ref=d9fff371d75d9d3d25087d20c77209b430a5c248" | jq -r '.content' 2>/dev/null | base64 -d | grep -A 5 -B 5 -i "takeorders\|approval_info\|take_orders_info" | head -40Repository: ST0x-Technology/st0x.rest.api
Length of output: 1981
🏁 Script executed:
#!/bin/bash
# Check rest_api crate for the expected APIs
echo "=== Checking rest_api crate lib.rs ==="
curl -s "https://raw.githubusercontent.com/rainlanguage/rain.orderbook/d9fff371d75d9d3d25087d20c77209b430a5c248/crates/rest_api/src/lib.rs" | head -150
echo ""
echo "=== Searching for the four API symbols in rest_api ==="
for symbol in "TakeOrdersRequest" "TakeOrdersCalldataResult" "approval_info" "take_orders_info"; do
echo "--- Searching for: $symbol ---"
curl -s "https://raw.githubusercontent.com/rainlanguage/rain.orderbook/d9fff371d75d9d3d25087d20c77209b430a5c248/crates/rest_api/src/lib.rs" | grep -i "$symbol" && echo "FOUND" || echo "NOT FOUND"
done
# Also check if these might be in separate files
echo ""
echo "=== Listing rest_api/src files ==="
curl -s "https://api.github.com/repos/rainlanguage/rain.orderbook/contents/crates/rest_api/src?ref=d9fff371d75d9d3d25087d20c77209b430a5c248" | jq -r '.[] | .name'Repository: ST0x-Technology/st0x.rest.api
Length of output: 447
🏁 Script executed:
#!/bin/bash
# Check rest_api main.rs and routes for the APIs
echo "=== Checking rest_api main.rs ==="
curl -s "https://raw.githubusercontent.com/rainlanguage/rain.orderbook/d9fff371d75d9d3d25087d20c77209b430a5c248/crates/rest_api/src/main.rs" | head -100
echo ""
echo "=== Listing routes directory ==="
curl -s "https://api.github.com/repos/rainlanguage/rain.orderbook/contents/crates/rest_api/src/routes?ref=d9fff371d75d9d3d25087d20c77209b430a5c248" | jq -r '.[] | .name'
echo ""
echo "=== Searching all rest_api source files for target symbols ==="
# Try to get all rust files in rest_api and search them
for file in "crates/rest_api/src/main.rs" "crates/rest_api/src/error.rs"; do
echo "--- Checking $file ---"
curl -s "https://raw.githubusercontent.com/rainlanguage/rain.orderbook/d9fff371d75d9d3d25087d20c77209b430a5c248/$file" | grep -E "TakeOrdersRequest|TakeOrdersCalldataResult|approval_info|take_orders_info" && echo "FOUND IN $file" || echo "NOT IN $file"
done
# Check routes files
echo ""
echo "=== Checking routes files ==="
curl -s "https://api.github.com/repos/rainlanguage/rain.orderbook/git/trees/d9fff371d75d9d3d25087d20c77209b430a5c248?recursive=1" | \
jq -r '.tree[] | select(.path | contains("rest_api/src/routes")) | .path' | head -20Repository: ST0x-Technology/st0x.rest.api
Length of output: 3145
Commit d9fff371 does not contain the expected APIs and is untagged; verify this is the correct commit for the stated objectives.
The commit points to a registry URL update (not API additions) and does not export TakeOrdersRequest, TakeOrdersCalldataResult, approval_info, or take_orders_info. The actual exports are BuyRequest, SellRequest, TakeOrdersApiResponse, ApprovalApiResponse, and TakeOrdersReadyResponse. Additionally, the commit is untagged (floating HEAD), creating reproducibility concerns. Confirm whether this is the intended commit or if a different revision should be pinned.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/rain.orderbook` at line 1, The PR references commit d9fff371 which is an
untagged floating HEAD and does not export the expected symbols
(TakeOrdersRequest, TakeOrdersCalldataResult, approval_info, take_orders_info);
instead the module exports BuyRequest, SellRequest, TakeOrdersApiResponse,
ApprovalApiResponse, and TakeOrdersReadyResponse. Verify and pin the correct
revision: either switch the dependency to the intended tagged commit or update
the code to use the actual exported symbols. Update any import/exports to match
the real API (replace references to
TakeOrdersRequest/TakeOrdersCalldataResult/approval_info/take_orders_info with
the corresponding real types or add adapter types/mappings), and ensure the
dependency reference is a stable tag or SHA to fix reproducibility.
There was a problem hiding this comment.
This is the main commit hash for rain.orderbook which has those exports.
There was a problem hiding this comment.
@findolor, thank you for the clarification—I apologize for the confusion. If the commit contains the expected exports and the code compiles correctly with TakeOrdersRequest, TakeOrdersCalldataResult, approval_info, and take_orders_info, then my analysis was mistaken. The submodule update is fine as-is.
🧠 Learnings used
Learnt from: findolor
Repo: ST0x-Technology/st0x.rest.api PR: 38
File: src/routes/swap/mod.rs:115-129
Timestamp: 2026-02-17T09:36:00.616Z
Learning: In the swap calldata endpoint (Rust, `src/routes/swap/mod.rs`), when the response includes a non-empty `approvals` array, the two-phase flow is intentional: the client performs the approval transaction first, then calls the endpoint again to retrieve the actual swap calldata. In the approval response, `to` is set to `approval.spender()` to signal the eventual target, and `data` is `Bytes::new()` (empty) to indicate "no transaction yet." This design is preferred over using `Address::ZERO` for clarity.
Chained PRs
Motivation
See issues:
This is a standalone reimplementation of PR #38, which was originally part of a chained PR sequence. This PR carries the same functionality but is rebased onto the swap quote branch so it can be reviewed and merged independently.
The swap calldata endpoint was stubbed with
todo!(). Users need a way to get ABI-encodedtakeOrders4calldata for on-chain swap execution after obtaining a quote. The endpoint must handle two states: when the taker needs to approve an ERC20 token first, and when the swap is ready to execute.Solution
Updated the
rain.orderbooksubmodule to latest main to bring inTakeOrdersRequest,TakeOrdersCalldataResult, andapproval_info()/take_orders_info()APIs. Extracted a sharedCHAIN_IDconstant for Base network (8453) used across the codebase.Extended the existing
SwapDataSourcetrait withget_calldata, backed by the library'sget_take_orders_calldata. The handler builds aTakeOrdersRequestfrom the caller's parameters and returns either:to= spender (orderbook),data= empty, with approval details in theapprovalsarrayto= orderbook,data= encodedtakeOrders4calldata, emptyapprovalsError mapping from
RaindexErrorvariants:NoLiquidity/InsufficientLiquidity→ 404 Not FoundSameTokenPair/NonPositiveAmount/NegativePriceCap/FromHexError/Float→ 400 Bad Request7 unit tests covering ready, needs-approval, not-found, bad-request, internal-error, 401 auth, and 500 client init failure scenarios.
Checks
By submitting this for review, I'm confirming I've done the following:
Summary by CodeRabbit
New Features
Improvements