Skip to content

feat: add ows swap quote command via LI.FI - #192

Open
Sertug17 wants to merge 8 commits into
open-wallet-standard:mainfrom
Sertug17:feat/ows-swap
Open

feat: add ows swap quote command via LI.FI#192
Sertug17 wants to merge 8 commits into
open-wallet-standard:mainfrom
Sertug17:feat/ows-swap

Conversation

@Sertug17

@Sertug17 Sertug17 commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

Part of #125

Adds ows swap quote — a dry-run cross-chain swap route lookup powered by LI.FI's aggregation API (27 bridges, 31 DEXs, 58 chains).

Usage

# Same-chain swap quote
ows swap quote --wallet my-wallet --from ETH --to USDC --amount 0.1 --from-chain ethereum

# Cross-chain swap quote
ows swap quote --wallet my-wallet --from ETH --to USDC --amount 0.5 --from-chain ethereum --to-chain base

# Output example:
#   Swap Route
#   ----------
#   0.1 ETH -> 186.31 USDC
#   Min received:  185.44 USDC
#   Via:           1inch
#   Gas cost:      ~$0.02
#   Est. time:     30s
#
#   [dry-run — no transaction signed]

Changes

ows-pay: new swap.rs module

  • LI.FI /v1/quote API client (no SDK dependency, pure REST)
  • SwapParams / SwapResult types
  • swap_dry_run() — fetches route, formats amounts, returns result
  • Token amount formatting with decimal handling
  • 3 unit tests for format_amount

ows-cli: new swap subcommand

  • ows swap quote with flags: --wallet, --from, --to, --amount, --from-chain, --to-chain, --slippage, --order
  • Human-readable output with route details

Not included in this PR

  • Transaction signing (follow-up PR)
  • ows bridge alias (same as ows swap with --to-chain)
  • ows swap execute (after signing integration)
  • max_value_usd / allowed_tokens policy rules

Note

Medium Risk
Quotes send wallet addresses and swap parameters to a third-party API and rely on decimal heuristics for amounts; no on-chain execution yet, but mis-quotes could mislead users planning trades.

Overview
Adds cross-chain swap quoting to the CLI without signing or broadcasting transactions.

ows-pay gains a new swap module: a REST client for LI.FI’s /v1/quote endpoint, SwapParams / SwapResult types, human-readable format_amount, and swap_dry_run() that returns route details (amounts, min received, bridge/DEX tool, gas USD, ETA) while optionally retaining a transactionRequest for a future execute path.

ows-cli wires ows swap quote with flags for wallet, tokens, amount, --from-chain / --to-chain, slippage, and route order (CHEAPEST / FASTEST). The handler loads the vault wallet, maps OWS/CAIP-2 chain names to LI.FI IDs (EVM + Solana), picks from/to addresses for cross-VM routes, converts human amounts to raw units via token decimal heuristics, and prints a formatted route labeled dry-run.

Signing, swap execute, policy limits, and bridge aliases are explicitly out of scope for this change.

Reviewed by Cursor Bugbot for commit 5be2190. Bugbot is set up for automated code reviews on this repo. Configure here.

Part of open-wallet-standard#125

Adds ows swap quote — a dry-run cross-chain swap route lookup powered
by LI.FI's aggregation API (27 bridges, 31 DEXs, 58 chains).

- ows-pay: new swap.rs module with LI.FI quote client, token amount
  formatting, SwapParams/SwapResult types, swap_dry_run()
- ows-cli: new swap subcommand with ows swap quote
  --wallet --from --to --amount --from-chain --to-chain --slippage --order
- No signing in this PR — quote only, transaction_request returned for
  future signing integration
- 3 unit tests for amount formatting (format_amount)
@Sertug17
Sertug17 requested a review from njdawn as a code owner April 4, 2026 19:45
@vercel

vercel Bot commented Apr 4, 2026

Copy link
Copy Markdown

@Sertug17 is attempting to deploy a commit to the MoonPay Team on Vercel.

A member of the Team first needs to authorize it.

Comment thread ows/crates/ows-cli/src/commands/swap.rs
Comment thread ows/crates/ows-pay/src/swap.rs
- swap.rs: expand decimal heuristic to cover WBTC (8), GUSD (2) and
  BTC-family tokens alongside existing USDC/USDT (6) and default 18
- swap.rs: truncate LI.FI error body to 120 chars to avoid logging
  full third-party payloads in error messages
@Sertug17

Copy link
Copy Markdown
Contributor Author

Addressed both Cursor Bugbot findings:

Hardcoded decimals expanded the decimal heuristic to cover WBTC/BTC/SBTC/TBTC (8 decimals), GUSD (2 decimals), and USDC.E/USDT.E (6 decimals) alongside existing USDC/USDT. Default remains 18 for unlisted tokens.

Third-party payload logging LI.FI error body is now truncated to 120 chars before being included in the error message.

Comment thread ows/crates/ows-pay/src/swap.rs
Comment thread ows/crates/ows-cli/src/commands/swap.rs Outdated
- Add ows_chain_to_lifi() mapping (ethereum->1, polygon->137, base->8453, etc.)
- Use Solana account address for Solana chains, EVM address for others
- Prevents invalid LI.FI requests from unmapped chain names
@Sertug17

Copy link
Copy Markdown
Contributor Author

Addressed both Cursor Bugbot findings:

LI.FI chain identifiers added ows_chain_to_lifi() that maps OWS
chain names to LI.FI numeric IDs (ethereum→1, polygon→137, base→8453,
arbitrum→42161, optimism→10, avalanche→43114, bsc→56, solana→1151111081099592).

Non EVM address mismatch the handler now picks a Solana account
address for Solana chains and an EVM account address for all other chains,
preventing inconsistent LI.FI requests.

Comment thread ows/crates/ows-cli/src/commands/swap.rs
Comment thread ows/crates/ows-cli/src/commands/swap.rs
- ows_chain_to_lifi returns empty string for unknown chains; CLI now
  returns a clear validation error before making any API call
- SwapParams gains optional to_address for cross-VM routes
  (e.g. ETH->SOL supplies the Solana wallet address as toAddress)
- LI.FI quote URL includes toAddress when set
@Sertug17

Copy link
Copy Markdown
Contributor Author

Addressed both Cursor Bugbot findings:

Unknown chain sent to API ows_chain_to_lifi now returns an empty
string for unrecognized chains. The CLI validates both from-chain and
to-chain before making any HTTP call and returns a clear error listing
supported chains.

Missing cross-VM destination address SwapParams gains an optional
to_address field. For cross-VM routes (EVM→Solana or Solana→EVM), the
handler picks the destination chain's wallet address and passes it as
toAddress in the LI.FI quote URL.

Comment thread ows/crates/ows-cli/src/commands/swap.rs
let body = resp.text().await.unwrap_or_default();
let truncated = if body.len() > 120 {
&body[..120]
} else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

UTF-8 panic truncating errors

Medium Severity

On failed LI.FI responses, the error body is truncated with &body[..120]. If byte 120 falls inside a multibyte UTF-8 character, slicing the str panics and can crash the CLI instead of returning an API error.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit bcfd4f8. Configure here.

- ows_chain_to_lifi now accepts eip155:8453, bare numeric IDs (8453),
  and friendly aliases (ethereum, base, etc.)
- Error body truncation uses chars().take(120) to avoid UTF-8 panic
  on multibyte character boundaries
@Sertug17

Copy link
Copy Markdown
Contributor Author

Addressed both Cursor Bugbot findings:

Unsupported chain ID formats ows_chain_to_lifi now accepts CAIP-2
IDs (eip155:8453), bare numeric EVM IDs (8453), and friendly aliases
(ethereum, base, etc.) matching the formats other OWS CLI commands accept.

UTF-8 panic on truncation error body truncation now uses
chars().take(120).collect() instead of byte slicing, safe for multibyte
UTF-8 characters.

Comment thread ows/crates/ows-cli/src/commands/swap.rs Outdated
Comment thread ows/crates/ows-pay/src/swap.rs Outdated
- is_solana now checks lifi_from == '1151111081099592' instead of
  contains('solana'), preventing substring false positives
- Transport errors no longer include the request URL (which contains
  wallet addresses) — replaced with a generic connection error message
@Sertug17

Copy link
Copy Markdown
Contributor Author

Addressed both Cursor Bugbot findings:

Substring misclassifies Solana chains — is_solana and is_to_solana
now check lifi_from/lifi_to == "1151111081099592" directly instead of
contains("solana"), eliminating false positives from substrings.

Transport errors expose request URL — transport failures now return
a generic "failed to connect to LI.FI API" message instead of the full
reqwest error which could contain wallet addresses in the URL.

Comment thread ows/crates/ows-cli/src/commands/swap.rs
- Move lifi_from/lifi_to validation above from_address resolution
- Unsupported chain now returns dedicated error instead of misleading
  'no EVM/Solana account found in wallet' message

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a6a42e6. Configure here.

Comment thread ows/crates/ows-cli/src/commands/swap.rs
- solana:<ref> now only accepts 'mainnet' and the genesis hash prefix
- Any other solana:<ref> returns empty string, triggering the
  unsupported-chain validation error instead of being passed through
  as a bare numeric LI.FI chain ID
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.

1 participant