Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR adds a complete developer guide and helper script for the USDB embedded wallet → USD bank offramp flow, including a step-by-step Confidence Score: 4/5Safe to merge; all findings are P2 style suggestions with no runtime impact. Only P2 findings (dead variable, implicit transitive dependency). No logic errors, security issues, or breaking changes. scripts/embedded-wallet-sign.js — dead variable and implicit @noble/curves dependency worth addressing before the script is widely distributed.
|
| Filename | Overview |
|---|---|
| scripts/embedded-wallet-sign.js | New helper script for USDB offramp signing (gen-keypair, decrypt-bundle, stamp); a dead variable and an implicit transitive dependency on @noble/curves are minor issues. |
| scripts/README.md | New step-by-step guide for the USDB embedded-wallet offramp flow; copy-pasteable curl and polling loops look correct. |
| scripts/package.json | New package manifest; @noble/curves is used in embedded-wallet-sign.js but not declared as a direct dependency here. |
| scripts/package-lock.json | Generated lockfile for the new scripts package; all versions are consistent with the declared dependencies. |
| CLAUDE.md | Documents the new scripts/ section with the USDB offramp gotcha; straightforward documentation addition. |
| .claude/skills/grid-api/SKILL.md | Adds USDB / Turnkey / embedded-wallet trigger keywords and a new prose section pointing at the new scripts. |
Sequence Diagram
sequenceDiagram
participant Dev as Developer
participant Grid as Grid API
participant Sign as embedded-wallet-sign.js
Note over Dev,Sign: 1. Onboarding
Dev->>Grid: POST /customers
Grid-->>Dev: customerId
Dev->>Grid: POST /auth/credentials (EMAIL_OTP)
Grid-->>Dev: credId — bootstraps Turnkey sub-org
Note over Dev,Sign: 2. On-ramp
Dev->>Grid: POST /quotes (platform USD to USDB account)
Dev->>Grid: POST /quotes/{id}/execute
Grid-->>Dev: transactionId COMPLETED
Note over Dev,Sign: 3. Off-ramp
Dev->>Sign: gen-keypair
Sign-->>Dev: pubHex + privHex
Dev->>Grid: POST /auth/credentials/{id}/challenge
Dev->>Grid: POST /auth/credentials/{id}/verify (clientPublicKey)
Grid-->>Dev: encryptedSessionSigningKey
Dev->>Sign: decrypt-bundle (encryptedBundle, privHex)
Sign-->>Dev: sessionPrivHex
Dev->>Grid: POST /quotes (USDB account to bank)
Grid-->>Dev: quoteId + payloadToSign
Dev->>Sign: stamp (sessionPrivHex, payloadToSign)
Sign-->>Dev: Grid-Wallet-Signature value
Dev->>Grid: POST /quotes/{id}/execute with Grid-Wallet-Signature
Grid-->>Dev: transactionId COMPLETED
Comments Outside Diff (1)
-
scripts/embedded-wallet-sign.js, line 377-385 (link)Dead variable and implicit transitive dependency
dB64is computed but never read — the comment above it describes a workaround that was apparently abandoned in favour of callingp256.getPublicKeydirectly. Also,@noble/curvesis used here but is not listed as a direct dependency inpackage.json; it's only available transitively through@turnkey/crypto. If a future version of that package pins or drops@noble/curves, this require will fail at runtime.Consider adding
"@noble/curves": "^1.9.0"toscripts/package.jsonso the dependency is explicit.Prompt To Fix With AI
This is a comment left during a code review. Path: scripts/embedded-wallet-sign.js Line: 377-385 Comment: **Dead variable and implicit transitive dependency** `dB64` is computed but never read — the comment above it describes a workaround that was apparently abandoned in favour of calling `p256.getPublicKey` directly. Also, `@noble/curves` is used here but is not listed as a direct dependency in `package.json`; it's only available transitively through `@turnkey/crypto`. If a future version of that package pins or drops `@noble/curves`, this require will fail at runtime. Consider adding `"@noble/curves": "^1.9.0"` to `scripts/package.json` so the dependency is explicit. How can I resolve this? If you propose a fix, please make it concise.
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
scripts/embedded-wallet-sign.js:377-385
**Dead variable and implicit transitive dependency**
`dB64` is computed but never read — the comment above it describes a workaround that was apparently abandoned in favour of calling `p256.getPublicKey` directly. Also, `@noble/curves` is used here but is not listed as a direct dependency in `package.json`; it's only available transitively through `@turnkey/crypto`. If a future version of that package pins or drops `@noble/curves`, this require will fail at runtime.
Consider adding `"@noble/curves": "^1.9.0"` to `scripts/package.json` so the dependency is explicit.
```suggestion
function privHexToCompressedPubHex(privHex) {
// Re-derive the public point in compressed SEC1 form (the format the
// Turnkey stamp expects).
const { p256 } = require("@noble/curves/p256");
const compressed = p256.getPublicKey(privHex, true); // Uint8Array
return Buffer.from(compressed).toString("hex");
}
```
Reviews (1): Last reviewed commit: "Add Turnkey USDB offramp instructions" | Re-trigger Greptile
|
I don't think we want to mention Turnkey publicly? |
| are HPKE bundle decrypt and Turnkey API stamp construction, which live in | ||
| [`embedded-wallet-sign.js`](./embedded-wallet-sign.js) (using | ||
| `@turnkey/crypto` and `@turnkey/api-key-stamper`). | ||
|
|
There was a problem hiding this comment.
do you want to point to one of the sdks?
There was a problem hiding this comment.
Applied. Added a callout near the top pointing production integrators at the official Grid SDKs (https://grid.lightspark.com), and noting this README is intended for hands-on tinkering / debugging.
Review response generated using /pr-review-respond Claude command.
|
|
||
| Customer creation returns `kybStatus: "UNVERIFIED"`. Internal accounts | ||
| auto-provision **after** approval. Poll: | ||
|
|
There was a problem hiding this comment.
currently individuals are auto kyc'd
There was a problem hiding this comment.
Applied. Reworked section 1.2 to call out that INDIVIDUAL customers are auto-KYC'd (no wait), while only BUSINESS customers go through the manual KYB poll.
Review response generated using /pr-review-respond Claude command.
| ``` | ||
|
|
||
| Read the OTP code from the email and assign to `$OTP`. | ||
|
|
There was a problem hiding this comment.
could probably add something here about in sandbox otp is 000000
There was a problem hiding this comment.
Applied. Added a sandbox tip noting that no email is sent in sandbox mode and the fixed OTP is 000000.
Review response generated using /pr-review-respond Claude command.
| | select(.accountType=="EMBEDDED_WALLET").payloadToSign') | ||
| ``` | ||
|
|
||
| The quote response also includes a `SPARK_WALLET` invoice — that's the |
There was a problem hiding this comment.
Applied. Yes — the quote response includes both: paymentInstructions is a 2-element array with one SPARK_WALLET entry (address + invoice fields) and one EMBEDDED_WALLET entry (payloadToSign). Reworked the section to show the actual response shape and explicitly call out that the two are alternatives — pick one.
Review response generated using /pr-review-respond Claude command.
| ### 3.5 Stamp the payload | ||
|
|
||
| ```bash | ||
| STAMP=$($SIGN stamp "$SESSION_PRIV_HEX" "$PAYLOAD") |
There was a problem hiding this comment.
do you wanna have sandbox or prod instructions here?
There was a problem hiding this comment.
Applied. Added a sandbox tip noting you can skip the keypair / decrypt / stamp dance and use Grid-Wallet-Signature: sandbox-valid-signature instead; any other value gets rejected the same way a bad real stamp would.
Review response generated using /pr-review-respond Claude command.
|
Not applicable. Per offline confirmation, mentioning Turnkey is fine, so I've left the references in. Happy to genericize if that ever changes — Review response generated using |
- Add SDK pointer for production integrators (https://grid.lightspark.com) - Clarify INDIVIDUAL auto-KYC vs BUSINESS manual KYB in section 1.2 - Note sandbox OTP `000000` in step 3.2 - Show paymentInstructions response shape; call out SPARK_WALLET vs EMBEDDED_WALLET as alternatives - Note sandbox `Grid-Wallet-Signature: sandbox-valid-signature` shortcut in step 3.6 Co-Authored-By: Claude <noreply@anthropic.com>

TL;DR
Adds a
scripts/directory with a step-by-step offramp guide and a Node.js signing helper for the USDB embedded wallet → USD bank flow, and updates Claude skill/project documentation to reference them.What changed?
scripts/README.md— A complete walkthrough of the USDB embedded wallet offramp flow covering:EMAIL_OTPcredential registration, add a destination bank)Grid-Wallet-Signature)to_network INTERNAL_FUNDED_FIAT does not support USDB, expired OTPs, and insufficient fundsscripts/embedded-wallet-sign.js— A Node.js CLI with three subcommands that handle the cryptographic operations not expressible in plain curl:gen-keypair— generates an ephemeral P-256 keypair (pubHex/privHex) to supply asclientPublicKeyto/auth/credentials/{id}/verifydecrypt-bundle— HPKE-opens theencryptedSessionSigningKeyfrom the verify response using@turnkey/cryptostamp— builds a Turnkey API stamp over apayloadToSignusing@turnkey/api-key-stamper, producing the value for theGrid-Wallet-Signatureheaderscripts/package.json/package-lock.json— Package manifest with@turnkey/api-key-stamperand@turnkey/cryptoas dependencies.CLAUDE.mdand.claude/skills/grid-api/SKILL.md— Updated to point Claude atscripts/README.mdwhenever tasks involve Turnkey signing, offramp,Grid-Wallet-Signature, HPKE bundle decryption, or theEMAIL_OTPcredential flow. Also documents the bootstrapping gotcha: the Turnkey sub-org and Spark network wallet aren't provisioned until anEMAIL_OTPcredential is registered, so this must happen before the first on-ramp quote.How to test?
cd scripts && npm installGRID_BASE_URL,GRID_CLIENT_ID, andGRID_CLIENT_SECRETscripts/README.mdend-to-end: create a customer, register anEMAIL_OTPcredential against the USDB account, run an on-ramp quote, then execute the offramp using the threeembedded-wallet-sign.jssubcommands (gen-keypair→decrypt-bundle→stamp)COMPLETEDand the destination bank receives USDWhy make this change?
The USDB embedded wallet offramp requires two cryptographic operations (HPKE decryption and Turnkey API stamp construction) that cannot be performed with curl alone. Without tooling and documentation for these steps, integrators have no clear path to complete the flow. The bootstrapping requirement (registering an
EMAIL_OTPcredential before the first quote) is also a non-obvious gotcha that causes confusing failures. This PR provides both the reference documentation and the minimal Node.js tooling needed to drive the full flow end-to-end.