Skip to content

feat: add TransactionFeeConfig schema (AT-5613)#613

Open
jacklatourette wants to merge 3 commits into
mainfrom
06-23-feat_grid-api_add_transactionfeeconfig_schema_at-5613_
Open

feat: add TransactionFeeConfig schema (AT-5613)#613
jacklatourette wants to merge 3 commits into
mainfrom
06-23-feat_grid-api_add_transactionfeeconfig_schema_at-5613_

Conversation

@jacklatourette

@jacklatourette jacklatourette commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Reason

Ships AT-5613 (Xc) of the Platform Fee Config: Cross Currency epic — Grid API OpenAPI schemas for platform-collected transaction fees.

This is the API-surface ticket that the sparkcore PATCH /config handler (AT-5614) and the Grid API SDKs will consume.

Overview

Two new schemas + extensions to two existing ones:

  • TransactionFeeConfig.yaml{ id, feeEventType, sourceCurrency, variableFeeBps, fixedFee, isActive }. id and isActive are read-only. Required: feeEventType, sourceCurrency, variableFeeBps, fixedFee.
  • TransactionFeeEventType.yaml — enum { CROSS_CURRENCY_TRANSACTION, TRANSFER_OUT_TRANSACTION }. Intentionally broad — sparkcore enforces a narrower v0 whitelist (CROSS_CURRENCY_TRANSACTION only) at the handler layer (AT-5614) so the Transfer Out fast-follow does not need an SDK regen.
  • PlatformConfig.yaml — adds optional transactionFeeConfigs: array. Reads return all active rows regardless of feeEventType, so operator-planted Transfer Out rows are visible to platforms before the write path is enabled.
  • PlatformConfigUpdateRequest.yaml — same field. Documented as merge-by-key upsert on (feeEventType, sourceCurrency); "deactivate" = same key with variableFeeBps: 0 and fixedFee.amount: 0.

Test Plan

  • Bundle: npm run build:openapi clean — both new schemas auto-discovered into openapi.yaml (lines 8857 + 8904) and the mintlify/openapi.yaml mirror.
  • Redocly lint: validates clean (Your API description is valid 🎉). The pre-existing 33 warnings are all on unrelated webhook example schemas (none on the files this PR touches). Confirmed by running npm run lint against main first — same 33.
  • Spectral: pre-existing spectral: command not found issue on main — the npm script tries npx spectral which resolves to a 0.0.0 placeholder package, not @stoplight/spectral-cli. Not introduced by this PR; tracking separately.
  • Stainless: regen kicks in on merge.

Manual SDK-shape check (post-Stainless regen)

After Stainless picks this up, the generated TS SDK should expose:

  • TransactionFeeConfig type with the shape above
  • TransactionFeeEventType union including both CROSS_CURRENCY_TRANSACTION and TRANSFER_OUT_TRANSACTION
  • PlatformConfig.transactionFeeConfigs?: TransactionFeeConfig[]
  • PlatformConfigUpdateRequest.transactionFeeConfigs?: TransactionFeeConfig[]

Blocked on

Nothing — this lands in parallel with AT-5611/AT-5612 sparkcore work.

Unblocks

  • AT-5614 — sparkcore PATCH /config handler that consumes transactionFeeConfigs and enforces v0 validation
  • All downstream API consumers

@vercel

vercel Bot commented Jun 23, 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 Jun 25, 2026 9:19pm
grid-wallet-demo Ignored Ignored Preview Jun 25, 2026 9:19pm

Request Review

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

✱ Stainless preview builds for grid

This PR will update the grid SDKs with the following commit messages.

cli

feat(api): add transaction-fee-config parameter to config update method

csharp

feat(api): add transactionFeeConfigs field and types to config models

go

feat(api): add transaction fee configs to platform config and CurrencyAmountParam type

kotlin

feat(api): add transactionFeeConfigs to PlatformConfig request/response

openapi

feat(api): add transaction fee config types and fields to PlatformConfig

php

feat(api): add transactionFeeConfigs field and TransactionFeeConfig type to config

python

feat(api): add transaction_fee_configs parameter to config update method

ruby

feat(api): add transaction_fee_configs field and nested model to platform config

typescript

feat(api): add transactionFeeConfigs parameter and field to config

Edit this comment to update them. They will appear in their respective SDK's changelogs.

grid-openapi studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅

grid-ruby studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅build ✅lint ✅test ✅

grid-go studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅build ✅lint ❗test ❗

go get github.com/stainless-sdks/grid-go@5f2dd4f945c4bedcfb8c4a7071c7d20226436db0
grid-typescript studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅build ✅lint ✅test ✅

npm install https://pkg.stainless.com/s/grid-typescript/34306db66d83cb801716dc97d1f2a556e94ee395/dist.tar.gz
grid-kotlin studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅build ✅lint ✅test ✅

grid-python studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅build ✅lint ❗test ❗

pip install https://pkg.stainless.com/s/grid-python/3dd7fd70ffa55780f9c4658fb9f75fb2841e205a/grid-0.0.1-py3-none-any.whl
grid-csharp studio · code · diff

Your SDK build had at least one "warning" diagnostic, but this did not represent a regression.
generate ⚠️build ❗lint ✅test ❗

grid-php studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅lint ✅test ✅

grid-cli studio · code · diff

Your SDK build had at least one "warning" diagnostic, but this did not represent a regression.
generate ⚠️build ❗lint ❗test ❗


This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-06-25 21:25:56 UTC

@greptile-apps

greptile-apps Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds OpenAPI 3.1 schemas for platform-collected transaction fees: TransactionFeeConfig (read), TransactionFeeConfigInput (write), and TransactionFeeEventType enum. The design correctly separates read and write shapes and extends PlatformConfig / PlatformConfigUpdateRequest with an optional transactionFeeConfigs array.

  • Two new schemas + enumTransactionFeeConfig for GET responses, TransactionFeeConfigInput for PATCH requests, and a TransactionFeeEventType enum that intentionally includes TRANSFER_OUT_TRANSACTION ahead of the server-side whitelist, avoiding a future SDK regen.
  • fixedFee.amount minimum not enforced — both schemas document "must be non-negative" but CurrencyAmount.amount has no minimum: 0, so schema validators accept negative values that the server will reject with a 400.
  • id and isActive missing from required — these readOnly fields are always present in read responses, but omitting them from required causes Stainless (and other generators) to emit optional types for identifiers that are never actually absent.

Confidence Score: 4/5

Safe to merge after addressing the missing minimum constraint on fixedFee.amount in the write schema.

The write schema documents that fixedFee.amount must be non-negative but CurrencyAmount.amount has no minimum: 0, so a client can craft a request that passes schema validation and hits an undocumented server-side rejection. All other changes are additive schema extensions with no breaking surface.

openapi/components/schemas/config/TransactionFeeConfigInput.yaml — the fixedFee property needs a minimum:0 constraint; openapi/components/schemas/config/TransactionFeeConfig.yaml — id and isActive should be in required for accurate SDK type generation.

Important Files Changed

Filename Overview
openapi/components/schemas/config/TransactionFeeConfig.yaml New read-side fee schema; id and isActive are readOnly and always present in responses but not in the required list, so generated SDK types will mark them optional.
openapi/components/schemas/config/TransactionFeeConfigInput.yaml New write-side fee schema; fixedFee description says amount must be non-negative but CurrencyAmount.amount has no minimum: 0 — schema validators will accept negative values that the server rejects.
openapi/components/schemas/config/TransactionFeeEventType.yaml New enum schema; intentionally broad (includes TRANSFER_OUT_TRANSACTION) with runtime enforcement of v0 whitelist documented inline.
openapi/components/schemas/config/PlatformConfig.yaml Adds optional transactionFeeConfigs array (TransactionFeeConfig[]) to the GET response shape; change is additive and non-breaking.
openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml Adds optional transactionFeeConfigs array (TransactionFeeConfigInput[]) to the PATCH request; merge-by-key upsert semantics and v0 restrictions documented inline.
.stainless/stainless.yml Registers three new schema models in the config resource; consistent with existing pattern for other config schemas.

Entity Relationship Diagram

%%{init: {'theme': 'neutral'}}%%
erDiagram
    PlatformConfig {
        string umaDomain
        string webhookEndpoint
        array supportedCurrencies
        object embeddedWalletConfig
        array transactionFeeConfigs
        string createdAt
    }
    PlatformConfigUpdateRequest {
        string umaDomain
        string webhookEndpoint
        array supportedCurrencies
        object embeddedWalletConfig
        array transactionFeeConfigs
    }
    TransactionFeeConfig {
        string id readOnly
        string feeEventType
        string sourceCurrency
        integer variableFeeBps
        object fixedFee
        boolean isActive readOnly
    }
    TransactionFeeConfigInput {
        string feeEventType
        string sourceCurrency
        integer variableFeeBps
        object fixedFee
    }
    TransactionFeeEventType {
        string CROSS_CURRENCY_TRANSACTION
        string TRANSFER_OUT_TRANSACTION
    }
    CurrencyAmount {
        integer amount
        object currency
    }
    PlatformConfig ||--o{ TransactionFeeConfig : "transactionFeeConfigs (GET response)"
    PlatformConfigUpdateRequest ||--o{ TransactionFeeConfigInput : "transactionFeeConfigs (PATCH request)"
    TransactionFeeConfig }o--|| TransactionFeeEventType : feeEventType
    TransactionFeeConfigInput }o--|| TransactionFeeEventType : feeEventType
    TransactionFeeConfig }o--|| CurrencyAmount : fixedFee
    TransactionFeeConfigInput }o--|| CurrencyAmount : fixedFee
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"}}}%%
erDiagram
    PlatformConfig {
        string umaDomain
        string webhookEndpoint
        array supportedCurrencies
        object embeddedWalletConfig
        array transactionFeeConfigs
        string createdAt
    }
    PlatformConfigUpdateRequest {
        string umaDomain
        string webhookEndpoint
        array supportedCurrencies
        object embeddedWalletConfig
        array transactionFeeConfigs
    }
    TransactionFeeConfig {
        string id readOnly
        string feeEventType
        string sourceCurrency
        integer variableFeeBps
        object fixedFee
        boolean isActive readOnly
    }
    TransactionFeeConfigInput {
        string feeEventType
        string sourceCurrency
        integer variableFeeBps
        object fixedFee
    }
    TransactionFeeEventType {
        string CROSS_CURRENCY_TRANSACTION
        string TRANSFER_OUT_TRANSACTION
    }
    CurrencyAmount {
        integer amount
        object currency
    }
    PlatformConfig ||--o{ TransactionFeeConfig : "transactionFeeConfigs (GET response)"
    PlatformConfigUpdateRequest ||--o{ TransactionFeeConfigInput : "transactionFeeConfigs (PATCH request)"
    TransactionFeeConfig }o--|| TransactionFeeEventType : feeEventType
    TransactionFeeConfigInput }o--|| TransactionFeeEventType : feeEventType
    TransactionFeeConfig }o--|| CurrencyAmount : fixedFee
    TransactionFeeConfigInput }o--|| CurrencyAmount : fixedFee
Loading

Reviews (5): Last reviewed commit: "fix(config): split TransactionFeeConfig ..." | Re-trigger Greptile

Comment thread openapi/components/schemas/config/TransactionFeeConfig.yaml
Comment on lines +1 to +10
type: object
description: >-
Platform-collected transaction fee charged on top of corridor fees. Keyed
uniquely by `(feeEventType, sourceCurrency)` within a platform. To deactivate
a fee, send the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`.
required:
- feeEventType
- sourceCurrency
- variableFeeBps
- fixedFee

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.

P2 Single schema reused for read and write contexts

TransactionFeeConfig is referenced by both PlatformConfig (GET response) and PlatformConfigUpdateRequest (PATCH body). In the write context, readOnly fields (id, isActive) appear in generated SDK request types — even though spec-compliant generators should strip them, some won't. Consider a dedicated TransactionFeeConfigInput schema for the write path that omits id and isActive entirely, so write-side types are unambiguous without relying on readOnly semantics being respected by all toolchains.

Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/config/TransactionFeeConfig.yaml
Line: 1-10

Comment:
**Single schema reused for read and write contexts**

`TransactionFeeConfig` is referenced by both `PlatformConfig` (GET response) and `PlatformConfigUpdateRequest` (PATCH body). In the write context, `readOnly` fields (`id`, `isActive`) appear in generated SDK request types — even though spec-compliant generators should strip them, some won't. Consider a dedicated `TransactionFeeConfigInput` schema for the write path that omits `id` and `isActive` entirely, so write-side types are unambiguous without relying on `readOnly` semantics being respected by all toolchains.

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

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@jacklatourette jacklatourette force-pushed the 06-23-feat_grid-api_add_transactionfeeconfig_schema_at-5613_ branch from 6f07fa7 to c2ddd74 Compare June 23, 2026 20:42
jacklatourette and others added 3 commits June 25, 2026 14:19
Adds platform-collected transaction-fee configuration to the Grid API
PlatformConfig surface.

- New TransactionFeeConfig schema: { id, feeEventType, sourceCurrency,
  variableFeeBps, fixedFee, isActive }. `id` and `isActive` are
  read-only.
- New TransactionFeeEventType enum: { CROSS_CURRENCY_TRANSACTION,
  TRANSFER_OUT_TRANSACTION }.
- PlatformConfig: optional `transactionFeeConfigs: array`. Reads return
  all active rows regardless of feeEventType so operator-created Transfer
  Out rows are visible before the corresponding write path is enabled.
- PlatformConfigUpdateRequest: same field, with semantics documented as
  merge-by-key upsert on (feeEventType, sourceCurrency). To deactivate a
  fee, send the same key with variableFeeBps=0 and fixedFee.amount=0.
  Sparkcore's PATCH /config handler (AT-5614) will accept only
  feeEventType=CROSS_CURRENCY_TRANSACTION + sourceCurrency=USD for v0;
  the OpenAPI enum is intentionally broader so the Transfer Out
  fast-follow does not need an SDK regen.
The platform transaction fee's fixedFee.amount is enforced non-negative
server-side in sparkcore (PATCH /config); document it on the schema so
integrators see the constraint. CurrencyAmount stays unconstrained since
negative amounts are valid elsewhere in the API.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…5613)

Add TransactionFeeConfigInput for the PATCH /config write path, omitting the
read-only id/isActive fields, so generated SDK request types are unambiguous and
do not rely on every toolchain stripping readOnly. This fixes the Stainless SDK
build regressions (csharp build, ruby/python lint, kotlin test) caused by the
read-only fields appearing in request types.

Also clarify the isActive read semantics (no filter param exists).
Addresses Greptile review feedback.
@jacklatourette jacklatourette force-pushed the 06-23-feat_grid-api_add_transactionfeeconfig_schema_at-5613_ branch from 6de5f40 to 9248a32 Compare June 25, 2026 21:19
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