Sponsored fees reserves - #1335
Conversation
Surfaces XLS-68 sponsorship data on the account detail page: whether another account is sponsoring this account's base reserve (AccountRoot.Sponsor) and/or its transaction fees (Sponsorship ledger object), matching the Figma spec for this feature.
Build table rows from an array instead of duplicated JSX, use the existing AccountState type instead of any, and move the show-if-sponsored guard out to the call site to match how SignersCard/nftMinter/paychannels are conditionally rendered elsewhere on the account page.
# Conflicts: # src/containers/Accounts/index.tsx
An account can have more than one active Sponsorship object; getAccountSponsorship() previously used .find() and silently dropped all but the first, so the account page only ever showed one fee sponsor.
The Sponsored fees & reserves table truncated sponsor addresses via shortenAccount, but full addresses are more useful for verifying who is sponsoring an account.
…Sponsor field display Adds transaction detail rendering (Description/Simple/TableDetail) for the SponsorshipSet and SponsorshipTransfer transaction types, following the existing per-transaction-type component pattern, plus generic support for the transaction-common Sponsor/SponsorFlags/SponsorSignature fields so any co-sponsored transaction shows who is sponsoring its fee and/or reserve.
Match sibling account-page sections: capitalize the title, make it collapsible (collapsed by default) like Account Properties/Assets Held, drop the redundant Status column, and show "No Sponsors" instead of hiding the section entirely when an account has none. Also drop the sponsor-scope text from the transaction Simple tab's Sponsor row to keep the sidebar compact.
| "account_page_signers": "Signers", | ||
| "account_page_signer_weight": "Weight", | ||
| "account_page_details": "Details", | ||
| "account_page_current_sequence": "Current Sequence", |
There was a problem hiding this comment.
- Missing transaction display names. Every other type has transaction_type_name_. Without transaction_type_name_SponsorshipSet / _SponsorshipTransfer, TxLabel falls back to defaultValue: type and renders the raw camelCase SponsorshipSet instead of Sponsorship Set — in the ledger view, transaction tables, and search results.
- Missing keys for other language files
| account, | ||
| ledger_index: ledgerIndex, | ||
| type: 'sponsorship', | ||
| limit: 400, |
There was a problem hiding this comment.
The limit of rippled’s RPC methods does not mean “search all objects for this account and return at most 400 sponsorship objects.” Instead, it means “examine at most 400 account objects and return any sponsorship objects found within those 400.”
For example, if an account has 450 objects and its sponsorship objects happen to be the 405th and 420th objects, a request with limit: 400 could return zero sponsorship objects.
I think we’ll need to loop through the paginated results, either until we’ve examined all account objects or until we reach a predefined cap, such as 4,000 objects.
There was a problem hiding this comment.
getAccountSponsorship now pages through account_objects and examines up to 4000 objects. Added a test covering the multi-page case
| const rows: { scopeKey: ScopeKey; sponsor: string }[] = [ | ||
| ...(account.sponsorship ?? []).map(({ owner }) => ({ | ||
| scopeKey: 'account_page_sponsored_scope_transaction_fees' as ScopeKey, | ||
| sponsor: owner, |
There was a problem hiding this comment.
Should we check the values of FeeAmount and RemainingOwnerCount before claiming that the sponsor is sponsoring both fees and reserves? For example, if FeeAmount is 0 or RemainingOwnerCount is 0, do we still want to say that the sponsor is sponsoring both?
There was a problem hiding this comment.
Added a Number(feeAmount) > 0 filter before rendering a Transaction Fees row
| "sponsorship_operation_create": "Create", | ||
| "sponsorship_operation_reassign": "Reassign", | ||
| "sponsorship_operation_end": "End", | ||
| "sponsorship_set_description": "<Sponsor/> sponsors transaction fees for <Sponsee/>", |
There was a problem hiding this comment.
There is now a branch to account for whether feeAmountDelta/remainingOwnerCountDelta are present here
|
/ai-review set-config --profile=dge |
|
Repository |
|
/ai-review --adopt |
|
Repository |
|
/ai-review set-config --profile=dge |
|
Updated |
|
/ai-review |
There was a problem hiding this comment.
Solid, well-structured feature addition (sponsorship UI, transaction types, rippled API changes) with comprehensive test coverage. Found one likely bug in the new getAccountSponsorship error handling where new Error(message, code) won't actually attach a .code property to the error (the built-in Error constructor only accepts an options object as its second argument, not a status code), which contradicts what the accompanying test expects. Also found one hardcoded aria-label string that bypasses the translation function, per this repo's established i18n convention.
Cross-referenced against the pinned spec commit (XRPLF/XRPL-Standards@2733d3a, branch 68-updates) and found three inconsistencies: - SponsorshipTransfer's tfSponsorshipEnd/Create/Reassign flags reverted back to the upper-16-bit values (0x10000/0x20000/0x40000) per the latest spec revision. - SponsorshipSet's FeeAmount/ReserveCount fields are now FeeAmountDelta/RemainingOwnerCountDelta and represent deltas applied to the existing Sponsorship object, not absolute values — relabeled as "Fee Amount Change"/"Reserve Count Change" with explicit sign. - The Sponsorship ledger entry's own field is RemainingOwnerCount, not ReserveCount — fixes a pre-existing bug in the account page's fee sponsorship display from before this set of changes.
|
|
||
| export interface SponsorshipTransfer extends TransactionCommonFields { | ||
| ObjectID?: string | ||
| Sponsor?: string |
There was a problem hiding this comment.
Field name collision: Sponsor conflicts with generic tx-level field. Rename to NewSponsor (like SponsorshipSet's CounterpartySponsor); update parser, Simple, TableDetail, Description:
| Sponsor?: string | |
| NewSponsor?: string |
There was a problem hiding this comment.
Great call but incorrect suggested fix. Should gate the generic sponsor UI on a real co-sponsorship signal (data.tx.SponsorSignature / data.tx.SponsorFlags), or exclude the SponsorshipSet/SponsorshipTransfer types from it
There was a problem hiding this comment.
Fixed based on your comment (@pdp2121). The generic sponsor UI is now excluded only when SponsorshipTransfer's operation is create/reassign. This is now reflected in Transaction/SponsorshipTransfer/parser.ts, Transactions/SimpleTab.tsx, and Transactions/DetailTab/index.tsx
| @@ -0,0 +1,9 @@ | |||
| import { TransactionCommonFields } from '../types' | |||
There was a problem hiding this comment.
These types files are no longer needed. Please use xrpl.js latest for accuracy/consistency
There was a problem hiding this comment.
Agreed, and done. I upgraded xrpl from ^4.5.0 to ^5.1.0.
- Add transaction_type_name_SponsorshipSet/SponsorshipTransfer to en-US (translated) and all other locale files (null placeholders), fixing TxLabel's fallback to raw camelCase type names. - Add missing Payment.tfSponsorCreatedAccount flag to TX_FLAGS. - Fix SponsorshipTransfer's Description defaulting to sponsorship_transfer_end_self for any unrecognized operation, not just a genuine end-on-self case. - Show an explicit +/- sign on SponsorshipSet's fee amount delta, matching the reserve count delta's existing sign handling. - Rename accountState/rippled's reserveCount field to remainingOwnerCount to match the actual XRPL field name. - Fix getAccountSponsorship to page through account_objects via marker instead of only examining the first 400 objects, which could silently miss a sponsee's Sponsorship entries entirely. - Filter SponsoredFeesReserves' fee sponsor rows by an actual positive FeeAmount instead of assuming any sponsorship entry implies active fee sponsorship. - Vary SponsorshipSet's description text based on whether the transaction is adjusting the fee budget, the reserve budget, both, or neither, instead of always saying "sponsors transaction fees." - Remove clearRequireSignForFee/clearRequireSignForReserve from SponsorshipSet's parser output, which were computed but never rendered anywhere.
There was a problem hiding this comment.
The sponsored fees/reserves feature is well tested and mostly clean, but the flag-bit constants in SponsorshipSet/parser.ts and SponsorshipTransfer/parser.ts duplicate the same hex values already defined in transactionUtils.ts's TX_FLAGS map (a copy-paste-prone pattern this repo has been flagged for before). There's also a real correctness gap in getAccountSponsorship: paging silently stops after examining 4000 account_objects, which can make an account with many owned objects incorrectly show "No Sponsors" even when a Sponsorship object exists further down the list. A minor i18n gap: the new CollapsibleSection's ariaLabel is a hardcoded English string instead of going through t().
…or UI SponsorshipTransfer's Sponsor field is the same field as the generic transaction-common Sponsor field, but on create/reassign it means "new sponsor of the target object," not "who's co-sponsoring this outer transaction." Exclude the generic Sponsor row/section in that case. On tfSponsorshipEnd, the spec requires Sponsor to be omitted for the transfer's own purpose, so if it's present there it can only be genuine outer co-sponsorship and is still shown. Exports getOperation from SponsorshipTransfer/parser.ts for reuse in SimpleTab/DetailTab, and adds test coverage for all three cases.
There was a problem hiding this comment.
Overall this is a clean, well-tested addition of Sponsored Fees & Reserves support. The main things worth a look: bit-flag constants for SponsorshipSet/SponsorshipTransfer are re-declared in each parser file instead of reusing/sharing the single source of truth already defined in transactionUtils.ts's TX_FLAGS map (same values, three separate places), which risks silent drift if the flag values ever change. There's also a hardcoded safety cap (4000 objects) in getAccountSponsorship that can silently miss a sponsorship for accounts owning a very large number of objects, with no logging when the cap is hit. Minor: OPERATION_LABEL_KEYS is duplicated verbatim between SponsorshipTransfer/Simple.tsx and TableDetail.tsx.
xrpl@5.1.0 now ships real SponsorshipSet/SponsorshipTransfer types matching XLS-68, so the hand-rolled types.ts files in each of those transaction folders are removed in favor of importing directly from 'xrpl', matching the convention used by every other transaction type in this codebase (e.g. EscrowCreate). Explorer only ever uses xrpl.js for type-only imports (never Wallet or Client), so the 5.0.0 breaking changes around signing defaults and connection error handling don't apply here. Flags is now typed as `number | GlobalFlagsInterface` on BaseTransaction, so both parsers narrow it defensively (`typeof tx.Flags === 'number'`) before doing bitwise flag checks, matching the existing pattern in PaymentChannelClaim's parser.
| // A Sponsorship object is linked into both the sponsor's and sponsee's | ||
| // owner directories, so only keep the ones where this account is sponsored. | ||
| found.push( | ||
| ...resp.account_objects.filter((d: any) => d.Sponsee === account), |
There was a problem hiding this comment.
Safety cap broken: totalExamined counts filtered objects, not examined. Use:
| ...resp.account_objects.filter((d: any) => d.Sponsee === account), | |
| totalExamined += SPONSORSHIP_PAGE_SIZE |
| ...resp.account_objects.filter((d: any) => d.Sponsee === account), | |
| const totalExamined = examined + SPONSORSHIP_PAGE_SIZE |
High Level Overview of Change
Adds a "Sponsored fees & reserves" section to the account detail page
XLS spec - https://github.com/XRPLF/XRPL-Standards/tree/2733d3ad6f4814957fdbac635c057240064ec388/XLS-0068-sponsored-fees-and-reserves
Type of Change
Codebase Modernization
Preview
Note: this preview includes mock data which is not included in this PR.
Sponsored Fees & Reserves from the Accounts page
Minimized
Expanded
SponsorshipSet Transaction Types
Created
Deleted
SponsorshipTransfer Types
Create
Reassign
End
Standard Sponsor
Simple View
Detail View
Raw View
MPT Token Issuance
Create
Test Plan
Included tests in
src/rippled/lib/test/rippled.test.ts