Skip to content

feat(cli): add exchange-rates, crypto, discoveries, uma-providers, tokens, and internal-account update/export#705

Closed
ls-bolt[bot] wants to merge 1 commit into
07-19-grid-cli-cards-auth-planfrom
07-19-grid-cli-quick-wins
Closed

feat(cli): add exchange-rates, crypto, discoveries, uma-providers, tokens, and internal-account update/export#705
ls-bolt[bot] wants to merge 1 commit into
07-19-grid-cli-cards-auth-planfrom
07-19-grid-cli-quick-wins

Conversation

@ls-bolt

@ls-bolt ls-bolt Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

This PR has been claimed. The active PR is now #712.

Summary

Rounds out the Grid CLI with the high-value "quick win" endpoints from the audit. Stacked on #704.

New commands

  • grid exchange-ratesGET /exchange-rates (--source-currency, repeatable --destination-currency, --sending-amount)
  • grid crypto estimate-feePOST /crypto/estimate-withdrawal-fee
  • grid discoveriesGET /discoveries (receiving-institution lookup)
  • grid uma-providersGET /uma-providers (counterparty providers, paginated)
  • grid tokens list|get|create|revoke — API token management (create returns the clientSecret once — noted in help/output)
  • grid internal-accounts update|export — the two signed internal-account operations, reusing the --wallet-signature / --request-id header support (they surface the 202 challenge like the other signed ops; the CLI does not compute the stamp, generate keys, or decrypt)

Supporting change

  • buildUrl now sends array query params as repeated keys (for exchange-rates --destination-currency).

Test plan

  • cd cli && npm run build — clean compile.
  • npm test — 9 new boundary tests (67 total with the rest of the stack), asserting each command's method/path/query/body/headers.

Follow-up (noted, not in this PR)

customers update can hit a signed-retry (202) when changing email/phone for Embedded-Wallet customers; it doesn't yet accept --wallet-signature/--request-id. Worth wiring up in a follow-up (needs the signed helper positioned in the stack).

@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
grid-flow-builder Ignored Ignored Preview Jul 19, 2026 1:36am
grid-wallet-demo Ignored Ignored Preview Jul 19, 2026 1:36am

Request Review

akanter commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

@ls-bolt

ls-bolt Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai review

@greptile-apps

greptile-apps Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR rounds out the Grid CLI with six new command groups: exchange-rates, crypto estimate-fee, discoveries, uma-providers, tokens, and internal-accounts update/export. It also extends buildUrl in the client to serialize string[] query params as repeated keys, enabling multi-value filters like --destination-currency.

  • Client: QueryParams type alias + array-aware buildUrl — correctly no-ops on empty arrays and skips undefined values.
  • Commands: All six groups follow the existing patterns (signed-header helpers for internal-accounts, parseList shared util for tokens, confirmation prompt for revoke). The crypto estimate-fee amount validation is strict (/^\\d+$/ + Number.isSafeInteger), while exchange-rates --sending-amount uses a looser parseInt that silently truncates floats and accepts negatives.
  • Tests: 9 new boundary tests covering method, path, query params, body, and headers for every new command.

Confidence Score: 4/5

Safe to merge with one fix: the --sending-amount validator in exchange-rates.ts should be hardened to match the stricter input guard used by crypto estimate-fee in the same PR.

All six command groups are well-structured and follow existing CLI conventions. The one gap is in exchange-rates.ts: parseInt("10.5", 10) silently produces 10 and parseInt("-50", 10) forwards a negative amount, giving users wrong rate quotes without any error. Every other numeric input in this PR (and in crypto.ts from the same changeset) guards against this with a regex check. The rest of the change — the buildUrl array serialization, the signed-header flow for internal-accounts, token management, and the test suite — looks solid.

cli/src/commands/exchange-rates.ts — the --sending-amount input validation.

Important Files Changed

Filename Overview
cli/src/client.ts Introduces QueryParams type alias and extends buildUrl to handle string[] values as repeated query params. Logic is clean and correctly handles empty arrays (no-op) and undefined (skip).
cli/src/commands/exchange-rates.ts Adds exchange-rates command with repeatable --destination-currency. The --sending-amount validation uses parseInt which silently truncates floats and accepts negatives — inconsistent with the strict /^\d+$/ + Number.isSafeInteger guard used in crypto.ts for the same class of input.
cli/src/commands/crypto.ts Adds crypto estimate-fee subcommand with rigorous amount validation (regex + safe-integer check). Clean and consistent with the rest of the CLI.
cli/src/commands/tokens.ts Adds tokens list/get/create/revoke commands. Imports the shared parseList utility, includes a confirmation prompt for revoke, and correctly notes the one-time clientSecret in the command description.
cli/src/commands/internal-accounts.ts Adds internal-accounts update/export subcommands using the existing addSignedOptions/signedHeaders helpers. The --private-enabled boolean-string validation pattern is straightforward and correct.
cli/src/commands/uma-providers.ts Adds paginated uma-providers command. --has-blocked-providers boolean flag correctly maps to `true
cli/src/commands/discoveries.ts Minimal, correct discoveries command. No issues.
cli/src/index.ts Wires up all six new command registrations. Straightforward additions, no issues.
cli/test/quick-wins.test.ts 9 new boundary tests covering method, path, query params, body, and headers for all new commands, including validation-rejection cases.
cli/README.md Documents all new commands with accurate examples and the signed-retry note for internal-accounts.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    CLI["grid CLI"] --> ER["exchange-rates\nGET /exchange-rates"]
    CLI --> CR["crypto estimate-fee\nPOST /crypto/estimate-withdrawal-fee"]
    CLI --> DI["discoveries\nGET /discoveries"]
    CLI --> UP["uma-providers\nGET /uma-providers"]
    CLI --> TK["tokens\nlist / get / create / revoke"]
    CLI --> IA["internal-accounts\nupdate / export"]

    ER -->|"buildUrl: string[] → repeated params"| API["Grid API"]
    CR -->|"strict /^\\d+$/ + isSafeInteger"| API
    DI --> API
    UP --> API
    TK -->|"parseList shared util"| API
    IA -->|"addSignedOptions + signedHeaders\n(202 challenge flow)"| API

    subgraph "Signed ops (202 challenge)"
        IA
    end

    subgraph "Input validation"
        CR
        ER
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    CLI["grid CLI"] --> ER["exchange-rates\nGET /exchange-rates"]
    CLI --> CR["crypto estimate-fee\nPOST /crypto/estimate-withdrawal-fee"]
    CLI --> DI["discoveries\nGET /discoveries"]
    CLI --> UP["uma-providers\nGET /uma-providers"]
    CLI --> TK["tokens\nlist / get / create / revoke"]
    CLI --> IA["internal-accounts\nupdate / export"]

    ER -->|"buildUrl: string[] → repeated params"| API["Grid API"]
    CR -->|"strict /^\\d+$/ + isSafeInteger"| API
    DI --> API
    UP --> API
    TK -->|"parseList shared util"| API
    IA -->|"addSignedOptions + signedHeaders\n(202 challenge flow)"| API

    subgraph "Signed ops (202 challenge)"
        IA
    end

    subgraph "Input validation"
        CR
        ER
    end
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
cli/src/commands/exchange-rates.ts:47-53
**`parseInt` silently truncates non-integer inputs**

`parseInt("10.5", 10)` returns `10` and `parseInt("-50", 10)` returns `-50` — both are accepted without any error, even though the help text says "Sending amount in the smallest unit" (implying a positive integer). The result is that a user passing `--sending-amount 10.5` gets rate quotes for `10` instead of an error, and a negative amount is forwarded to the API. The `crypto estimate-fee` command in the same PR guards against exactly this with a `/^\d+$/` regex plus an `amount >= 1` check — the same approach should be used here for consistency and correctness.

Reviews (2): Last reviewed commit: "feat(cli): add exchange-rates, crypto, d..." | Re-trigger Greptile

Comment thread cli/src/commands/tokens.ts Outdated
Comment thread cli/src/commands/tokens.ts
@ls-bolt
ls-bolt Bot force-pushed the 07-19-grid-cli-quick-wins branch from 2c855a7 to bc1e0a6 Compare July 19, 2026 01:25
@ls-bolt
ls-bolt Bot force-pushed the 07-19-grid-cli-cards-auth-plan branch from fc569a6 to da61871 Compare July 19, 2026 01:25
@ls-bolt

ls-bolt Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Revision — addressed Greptile + Codex

  • Extracted the duplicated parseList into a shared src/parse.ts (used by customers/cards/tokens).
  • crypto estimate-fee: strict positive-integer validation on --amount (rejects 10foo/out-of-range instead of truncating).
  • cards create: reject an empty --funding-sources before sending (was omitting the required field).
  • Test helper now restores env vars and process.exitCode between runs.
  • Kept the console.log abort path for consistency with every other revoke/delete command (declined in-thread).

npm test green (68).

@ls-bolt

ls-bolt Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment on lines +47 to +53
if (options.sendingAmount !== undefined) {
sendingAmount = parseInt(options.sendingAmount, 10);
if (Number.isNaN(sendingAmount)) {
output(formatError("--sending-amount must be a number"));
process.exitCode = 1;
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 parseInt silently truncates non-integer inputs

parseInt("10.5", 10) returns 10 and parseInt("-50", 10) returns -50 — both are accepted without any error, even though the help text says "Sending amount in the smallest unit" (implying a positive integer). The result is that a user passing --sending-amount 10.5 gets rate quotes for 10 instead of an error, and a negative amount is forwarded to the API. The crypto estimate-fee command in the same PR guards against exactly this with a /^\d+$/ regex plus an amount >= 1 check — the same approach should be used here for consistency and correctness.

Prompt To Fix With AI
This is a comment left during a code review.
Path: cli/src/commands/exchange-rates.ts
Line: 47-53

Comment:
**`parseInt` silently truncates non-integer inputs**

`parseInt("10.5", 10)` returns `10` and `parseInt("-50", 10)` returns `-50` — both are accepted without any error, even though the help text says "Sending amount in the smallest unit" (implying a positive integer). The result is that a user passing `--sending-amount 10.5` gets rate quotes for `10` instead of an error, and a negative amount is forwarded to the API. The `crypto estimate-fee` command in the same PR guards against exactly this with a `/^\d+$/` regex plus an `amount >= 1` check — the same approach should be used here for consistency and correctness.

How can I resolve this? If you propose a fix, please make it concise.

…kens, internal-account update/export

Read/lookup commands (exchange-rates, crypto estimate-fee, discoveries,
uma-providers), API token CRUD (tokens list/get/create/revoke), and the two
signed internal-account operations (update, export) reusing the
--wallet-signature/--request-id header support. Extends buildUrl to send array
query params (exchange-rates --destination-currency). vitest boundary tests
cover each.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants