feat: add Bitcoin L1 deposit-address instruction + refundAddress to quotes#736
feat: add Bitcoin L1 deposit-address instruction + refundAddress to quotes#736peterrojs wants to merge 1 commit into
Conversation
Add a Bitcoin L1 on/off-ramp surface to the quote/execute flow: - New self-contained PaymentBitcoinDepositAddressInfo member in the PaymentInstructions oneOf (accountType=BITCOIN_L1 discriminator value, on-chain address, optional network fixed to BITCOIN). - Optional refundAddress on RealtimeFundingQuoteSource (on-ramp crypto funding) and AccountDestination (off-ramp crypto payout). The member is self-contained rather than composing BasePaymentAccountInfo, so it does not extend the shared PaymentAccountType enum; the discriminator mapping is the source of truth for the BITCOIN_L1 value. All additions are optional / additive, so no version bump. SDKs are regenerated by the Stainless CI action from the bundled spec + config. Co-Authored-By: peterrojs <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
|
This stack of pull requests is managed by Graphite. Learn more about stacking. |
✱ Stainless preview builds for gridThis PR will update the cli go kotlin openapi php python ruby typescript Edit this comment to update them. They will appear in their respective SDK's changelogs. ✅ grid-typescript studio · code · diff
✅ grid-openapi studio · code · diff
✅ grid-ruby studio · code · diff
✅ grid-go studio · code · diff
✅ grid-kotlin studio · code · diff
✅ grid-python studio · code · diff
✅ grid-php studio · code · diff
✅ grid-cli studio · code · diff
This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push. |
Greptile SummaryThis PR adds the public API types for Bitcoin L1 quote and execution flows. The main changes are:
Confidence Score: 5/5The API additions look mergeable after aligning the Bitcoin discriminator with the shared account-type contract.
openapi/components/schemas/common/PaymentBitcoinDepositAddressInfo.yaml and the shared PaymentAccountType schema
|
| Filename | Overview |
|---|---|
| openapi/components/schemas/common/PaymentBitcoinDepositAddressInfo.yaml | Adds the Bitcoin L1 instruction but defines its discriminator outside the shared payment account-type enum. |
| openapi/components/schemas/common/PaymentInstructions.yaml | Adds the Bitcoin L1 schema to the instruction union and discriminator mapping. |
| openapi/components/schemas/quotes/RealtimeFundingQuoteSource.yaml | Adds an optional refund address for on-chain real-time funding. |
| openapi/components/schemas/quotes/AccountDestination.yaml | Adds an optional refund address for crypto payout destinations. |
| openapi.yaml | Updates the generated root bundle with the new public schema fields. |
| mintlify/openapi.yaml | Updates the Mintlify bundle to match the source OpenAPI changes. |
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
openapi/components/schemas/common/PaymentBitcoinDepositAddressInfo.yaml:7-12
**Discriminator Bypasses Shared Enum**
This defines `BITCOIN_L1` only in a local enum while the shared `PaymentAccountType` used by the other payment-info models does not include it. SDK generators or consumers that expose `accountType` through that shared enum cannot represent a valid Bitcoin L1 instruction, causing compile-time rejection or failed response deserialization.
Reviews (1): Last reviewed commit: "feat(quotes): add Bitcoin L1 deposit-add..." | Re-trigger Greptile
Revision — align Bitcoin L1 with the shared account-type contractAddressed Greptile's inline finding (Discriminator Bypasses Shared Enum) in
Net effect: generated SDK clients that surface |
847f94c to
699ff8c
Compare
Correction — reverted the Greptile "shared enum" changeMy previous revision comment is superseded. I applied Greptile's Discriminator Bypasses Shared Enum suggestion in Why it broke: moving Why the original design is right: Full reasoning is in the inline thread. No code change vs. what you published — CI should return to green. |
| @@ -0,0 +1,21 @@ | |||
| title: Bitcoin L1 Deposit Address | |||
There was a problem hiding this comment.
Should this be using BasePaymentAccountInfo.yaml ?
There was a problem hiding this comment.
You're right on consistency grounds — every cousin model does allOf: [BasePaymentAccountInfo, { narrow accountType to its discriminator value }], and this is the only member that rolls its own standalone accountType. I actually made exactly this change earlier (commit 847f94c: inherit BasePaymentAccountInfo + add BITCOIN_L1 to the shared PaymentAccountType enum), but reverted it because it broke the Detect breaking changes job — and I want to flag that trade-off before redoing it, because the two are inseparable.
Why they're coupled: inheriting BasePaymentAccountInfo pins accountType to the shared PaymentAccountType enum, so narrowing to BITCOIN_L1 is only valid if BITCOIN_L1 is in that shared enum (otherwise the allOf intersection is empty). So "use BasePaymentAccountInfo" necessarily means "add BITCOIN_L1 to PaymentAccountType."
Why it broke CI: adding the value to the shared enum makes oasdiff report a new enum value on the accountType of every cousin that inherits the base (~50 currencies × the quote + transaction response paths = 100+ findings). The breaking detection is fine — that step runs --fail-on ERR and these are all WARN — but the downstream marocchino/sticky-pull-request-comment step then fails with Argument list too long trying to post that 100+-line body. The standalone enum keeps it to the single "added oneOf member" finding, so the job stays green.
I can't fix that workflow myself (the bot lacks the GitHub Actions workflows permission).
So it's a real trade-off:
- Your approach (inherit base + shared enum): matches the cousin convention, but the
Detect breaking changescheck goes red on the comment step until the workflow is made large-body-safe (or that check is overridden/not required for merge). - Standalone enum (current): the lone structural deviation, but CI stays green — and it's arguably a tighter contract, since a discriminated
oneOfvariant carries its ownaccountTypeliteral (generators key off the discriminator), and it avoids implying a USD/EUR/etc. account could ever returnaccountType: BITCOIN_L1.
Happy to push the BasePaymentAccountInfo version if you'd prefer consistency and are OK with that check being red pending a workflow fix — just say the word and I'll redo it. Which way do you want to go?
| accountType: | ||
| type: string | ||
| description: Type of payment account or wallet | ||
| enum: | ||
| - BITCOIN_L1 | ||
| example: BITCOIN_L1 |
There was a problem hiding this comment.
I think we don't need this if we're using PaymentAccountType.yaml? and I think we should be doing that based on my read of the cousin models
There was a problem hiding this comment.
Same underlying decision as the thread above — full context there. Short version: dropping this local accountType enum in favor of the shared PaymentAccountType requires adding BITCOIN_L1 to that shared enum, which is what turns the Detect breaking changes job red (100+ oasdiff findings overflow the sticky-comment step with Argument list too long). It's a consistency-vs-green-CI trade-off; let me know which you'd like and I'll push it.
|
📌 Bolt Status 2026-07-24 20:29:53 UTC — ⚡ Agent |

Summary
Adds the public API surface for a Bitcoin L1 crypto on/off-ramp on the quote/execute flow. All changes are additive and optional (non-breaking), so no version bump.
Changes
PaymentBitcoinDepositAddressInfopayment instruction — a member of thePaymentInstructions.accountOrWalletInfooneOf with a newBITCOIN_L1discriminator value. Carries the on-chain depositaddressand an optionalnetwork(fixed toBITCOIN). The member is self-contained (itsaccountTypeis a local single-value enum), so the sharedPaymentAccountTypeenum is left unchanged and the discriminator mapping remains the source of truth forBITCOIN_L1.refundAddressonRealtimeFundingQuoteSource— the caller's address to return funds to if a crypto deposit cannot be routed to complete the payment (on-ramp).refundAddressonAccountDestination— the address to return funds to if a crypto payout cannot be completed (off-ramp).BITCOINis already a validcryptoNetworkforRealtimeFundingQuoteSource(currency=BTC, cryptoNetwork=BITCOIN); no change needed.Notes
Detect breaking changes(oasdiff) check flags the new oneOf member as a breaking response change and applies thebreaking-changelabel — this is expected for any additive payment-instruction member and is gated by API-reviewer approval, per the repo's workflow design.Test plan
npm run lint:openapi(redocly bundle + redocly lint + spectral lint): passes with 0 errors.oasdiff breaking(v1.16.0, base=main): only the intended additive oneOf-member findings remain; no property removals.Original PR: #732