From 8143935f2eb691ff0756a4c7d5e4c188e5578983 Mon Sep 17 00:00:00 2001 From: Ashray Chowdhry Date: Wed, 11 Feb 2026 10:18:20 -0500 Subject: [PATCH 01/11] init draft --- .../xrpl/src/models/ledger/AccountRoot.ts | 22 +++ .../xrpl/src/models/ledger/LedgerEntry.ts | 3 + .../xrpl/src/models/ledger/RippleState.ts | 12 ++ .../xrpl/src/models/ledger/Sponsorship.ts | 81 ++++++++ packages/xrpl/src/models/ledger/index.ts | 7 + .../xrpl/src/models/transactions/common.ts | 119 ++++++++++++ .../xrpl/src/models/transactions/index.ts | 8 + .../xrpl/src/models/transactions/payment.ts | 14 ++ .../src/models/transactions/sponsorshipSet.ts | 175 ++++++++++++++++++ .../transactions/sponsorshipTransfer.ts | 69 +++++++ .../src/models/transactions/transaction.ts | 15 ++ 11 files changed, 525 insertions(+) create mode 100644 packages/xrpl/src/models/ledger/Sponsorship.ts create mode 100644 packages/xrpl/src/models/transactions/sponsorshipSet.ts create mode 100644 packages/xrpl/src/models/transactions/sponsorshipTransfer.ts diff --git a/packages/xrpl/src/models/ledger/AccountRoot.ts b/packages/xrpl/src/models/ledger/AccountRoot.ts index fdcc5e0f86..f9b989bfb5 100644 --- a/packages/xrpl/src/models/ledger/AccountRoot.ts +++ b/packages/xrpl/src/models/ledger/AccountRoot.ts @@ -78,6 +78,28 @@ export default interface AccountRoot extends BaseLedgerEntry, HasPreviousTxnID { MintedNFTokens?: number /** Another account that can mint NFTokens on behalf of this account. */ NFTokenMinter?: string + /** + * The address of the account sponsoring this account's reserve. Only present + * if this account was created via a sponsored Payment transaction. + */ + Sponsor?: string + /** + * The number of ledger objects whose reserves are being sponsored by other + * accounts on behalf of this account. This count does not contribute to the + * owner reserve. + */ + SponsoredOwnerCount?: number + /** + * The number of ledger objects for which this account is sponsoring reserves + * on behalf of other accounts. + */ + SponsoringOwnerCount?: number + /** + * The number of accounts for which this account is sponsoring the account + * reserve (i.e., accounts created via sponsored Payment transactions where + * this account was the sponsor). + */ + SponsoringAccountCount?: number } /** diff --git a/packages/xrpl/src/models/ledger/LedgerEntry.ts b/packages/xrpl/src/models/ledger/LedgerEntry.ts index abaa50ad07..5f72babf3f 100644 --- a/packages/xrpl/src/models/ledger/LedgerEntry.ts +++ b/packages/xrpl/src/models/ledger/LedgerEntry.ts @@ -19,6 +19,7 @@ import PayChannel from './PayChannel' import PermissionedDomain from './PermissionedDomain' import RippleState from './RippleState' import SignerList from './SignerList' +import Sponsorship from './Sponsorship' import Ticket from './Ticket' import Vault from './Vault' import XChainOwnedClaimID from './XChainOwnedClaimID' @@ -46,6 +47,7 @@ type LedgerEntry = | PermissionedDomain | RippleState | SignerList + | Sponsorship | Ticket | Vault | XChainOwnedClaimID @@ -76,6 +78,7 @@ type LedgerEntryFilter = | 'payment_channel' | 'permissioned_domain' | 'signer_list' + | 'sponsorship' | 'state' | 'ticket' | 'vault' diff --git a/packages/xrpl/src/models/ledger/RippleState.ts b/packages/xrpl/src/models/ledger/RippleState.ts index 0250e63c13..181fa95942 100644 --- a/packages/xrpl/src/models/ledger/RippleState.ts +++ b/packages/xrpl/src/models/ledger/RippleState.ts @@ -61,6 +61,18 @@ export default interface RippleState extends BaseLedgerEntry, HasPreviousTxnID { * equivalent to 1 billion, or face value. */ HighQualityOut?: number + /** + * The address of the account sponsoring the reserve for the high account's + * side of this trust line. Only present if the high account's reserve is + * being sponsored. + */ + HighSponsor?: string + /** + * The address of the account sponsoring the reserve for the low account's + * side of this trust line. Only present if the low account's reserve is + * being sponsored. + */ + LowSponsor?: string } export enum RippleStateFlags { diff --git a/packages/xrpl/src/models/ledger/Sponsorship.ts b/packages/xrpl/src/models/ledger/Sponsorship.ts new file mode 100644 index 0000000000..8d968a4566 --- /dev/null +++ b/packages/xrpl/src/models/ledger/Sponsorship.ts @@ -0,0 +1,81 @@ +import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry' + +/** + * Enum representing flags for a Sponsorship ledger entry. + * + * @category Ledger Entry Flags + */ +export enum SponsorshipFlags { + /** + * If set, each sponsored fee transaction requires a signature from the sponsor. + */ + lsfRequireSignForFee = 0x00010000, + /** + * If set, each sponsored reserve transaction requires a signature from the sponsor. + */ + lsfRequireSignForReserve = 0x00020000, +} + +/** + * A boolean map of SponsorshipFlags for simplified code checking Sponsorship settings. + * + * @category Ledger Entry Flags + */ +export interface SponsorshipFlagsInterface { + /** + * If set, each sponsored fee transaction requires a signature from the sponsor. + */ + lsfRequireSignForFee?: boolean + /** + * If set, each sponsored reserve transaction requires a signature from the sponsor. + */ + lsfRequireSignForReserve?: boolean +} + +/** + * The Sponsorship object represents a pre-funded sponsorship relationship + * between a sponsor and a sponsee. It allows the sponsor to pay transaction + * fees and/or reserves on behalf of the sponsee. + * + * @category Ledger Entries + */ +export default interface Sponsorship extends BaseLedgerEntry, HasPreviousTxnID { + LedgerEntryType: 'Sponsorship' + /** A bit-map of boolean flags enabled for this sponsorship. */ + Flags: number + /** + * The address of the sponsor account. This account pays for the reserve + * of this ledger entry. + */ + Owner: string + /** + * The address of the sponsee account (the account being sponsored). + */ + Sponsee: string + /** + * The remaining amount of XRP (in drops) that the sponsor has provided + * for the sponsee to use for transaction fees. + */ + FeeAmount: string + /** + * The maximum fee per transaction that will be sponsored. This is to + * prevent abuse/excessive draining of the sponsored fee pool. + */ + MaxFee?: string + /** + * The remaining number of reserves that the sponsor has provided for the + * sponsee to use. Each unit represents one object reserve. + */ + ReserveCount: number + /** + * A hint indicating which page of the sponsor's (owner's) directory links + * to this object, in case the directory consists of multiple pages. + */ + OwnerNode: string + /** + * A hint indicating which page of the sponsee's directory links to this + * object, in case the directory consists of multiple pages. + */ + SponseeNode: string +} + diff --git a/packages/xrpl/src/models/ledger/index.ts b/packages/xrpl/src/models/ledger/index.ts index 3988698576..57633b5f07 100644 --- a/packages/xrpl/src/models/ledger/index.ts +++ b/packages/xrpl/src/models/ledger/index.ts @@ -32,6 +32,10 @@ import Oracle from './Oracle' import PayChannel from './PayChannel' import RippleState, { RippleStateFlags } from './RippleState' import SignerList, { SignerListFlags } from './SignerList' +import Sponsorship, { + SponsorshipFlags, + SponsorshipFlagsInterface, +} from './Sponsorship' import Ticket from './Ticket' import Vault, { VaultFlags } from './Vault' import XChainOwnedClaimID from './XChainOwnedClaimID' @@ -80,6 +84,9 @@ export { RippleStateFlags, SignerList, SignerListFlags, + Sponsorship, + SponsorshipFlags, + SponsorshipFlagsInterface, Ticket, Vault, VaultFlags, diff --git a/packages/xrpl/src/models/transactions/common.ts b/packages/xrpl/src/models/transactions/common.ts index 42dfe47cbf..04f27b8eb3 100644 --- a/packages/xrpl/src/models/transactions/common.ts +++ b/packages/xrpl/src/models/transactions/common.ts @@ -456,6 +456,45 @@ export interface GlobalFlagsInterface { tfInnerBatchTxn?: boolean } +/** + * Enum representing sponsor flags for transaction sponsorship. + * These flags indicate what type of sponsorship is being applied to a transaction. + * + * @category Transaction Flags + */ +export enum SponsorFlags { + /** + * Indicates that the sponsor is paying the transaction fee. + */ + tfSponsorFee = 0x00000001, + /** + * Indicates that the sponsor is paying for the reserve of any objects + * created in the transaction. + */ + tfSponsorReserve = 0x00000002, +} + +/** + * The SponsorSignature object contains signing information for sponsored + * transactions. It holds the sponsor's signature to authorize the sponsorship. + */ +export interface SponsorSignature { + /** + * The public key for the sponsor's account, if single-signing. + */ + SigningPubKey?: string + /** + * A signature from the sponsor to indicate their approval of the transaction, + * if single-signing. + */ + TxnSignature?: string + /** + * An array of signatures from the sponsor's signers to indicate their approval + * of the transaction, if the sponsor is multi-signing. + */ + Signers?: Signer[] +} + /** * Every transaction has the same set of common fields. */ @@ -534,6 +573,23 @@ export interface BaseTransaction extends Record { * The delegate account that is sending the transaction. */ Delegate?: Account + /** + * The account ID of the sponsor for this transaction. If this field is + * included, the sponsor is co-signing the transaction to pay for the fee + * and/or reserve on behalf of the transaction sender. + */ + Sponsor?: Account + /** + * Flags indicating what type of sponsorship this is. Must be included if + * Sponsor is included. Valid values are tfSponsorFee (1) and/or + * tfSponsorReserve (2). + */ + SponsorFlags?: number + /** + * An object containing the sponsor's signature information to authorize + * the sponsorship. Must be included if Sponsor is included. + */ + SponsorSignature?: SponsorSignature } /** @@ -610,6 +666,69 @@ export function validateBaseTransaction( 'BaseTransaction: Account and Delegate addresses cannot be the same', ) } + + // Sponsor fields validation + validateOptionalField(common, 'Sponsor', isAccount) + validateOptionalField(common, 'SponsorFlags', isNumber) + + // Validate SponsorSignature if present + if (common.SponsorSignature !== undefined) { + if (!isRecord(common.SponsorSignature)) { + throw new ValidationError('BaseTransaction: invalid SponsorSignature') + } + const sponsorSig = common.SponsorSignature + if ( + sponsorSig.SigningPubKey !== undefined && + !isString(sponsorSig.SigningPubKey) + ) { + throw new ValidationError( + 'BaseTransaction: invalid SponsorSignature.SigningPubKey', + ) + } + if ( + sponsorSig.TxnSignature !== undefined && + !isString(sponsorSig.TxnSignature) + ) { + throw new ValidationError( + 'BaseTransaction: invalid SponsorSignature.TxnSignature', + ) + } + if ( + sponsorSig.Signers !== undefined && + (!isArray(sponsorSig.Signers) || + sponsorSig.Signers.length === 0 || + !sponsorSig.Signers.every(isSigner)) + ) { + throw new ValidationError( + 'BaseTransaction: invalid SponsorSignature.Signers', + ) + } + } + + // Validate that Sponsor, SponsorFlags, and SponsorSignature are all present or all absent + const hasSponsor = common.Sponsor !== undefined + const hasSponsorFlags = common.SponsorFlags !== undefined + const hasSponsorSignature = common.SponsorSignature !== undefined + + if (hasSponsor || hasSponsorFlags || hasSponsorSignature) { + if (!hasSponsor || !hasSponsorFlags) { + throw new ValidationError( + 'BaseTransaction: Sponsor and SponsorFlags must both be included if either is present', + ) + } + // SponsorSignature may be omitted if using pre-funded sponsorship without signature requirement + } + + // Validate SponsorFlags has valid values + if (hasSponsorFlags) { + const validFlags = SponsorFlags.tfSponsorFee | SponsorFlags.tfSponsorReserve + const flags = common.SponsorFlags as number + if ((flags & ~validFlags) !== 0 || flags === 0) { + throw new ValidationError( + 'BaseTransaction: invalid SponsorFlags - must include tfSponsorFee and/or tfSponsorReserve', + ) + } + } } /** diff --git a/packages/xrpl/src/models/transactions/index.ts b/packages/xrpl/src/models/transactions/index.ts index 984a29438c..0804c4ac27 100644 --- a/packages/xrpl/src/models/transactions/index.ts +++ b/packages/xrpl/src/models/transactions/index.ts @@ -3,6 +3,8 @@ export { GlobalFlags, GlobalFlagsInterface, isMPTAmount, + SponsorFlags, + SponsorSignature, } from './common' export { validate, @@ -118,6 +120,12 @@ export { PermissionedDomainDelete } from './permissionedDomainDelete' export { SetFee, SetFeePreAmendment, SetFeePostAmendment } from './setFee' export { SetRegularKey } from './setRegularKey' export { SignerListSet } from './signerListSet' +export { + SponsorshipSet, + SponsorshipSetFlags, + SponsorshipSetFlagsInterface, +} from './sponsorshipSet' +export { SponsorshipTransfer } from './sponsorshipTransfer' export { TicketCreate } from './ticketCreate' export { TrustSetFlagsInterface, TrustSetFlags, TrustSet } from './trustSet' export { UNLModify } from './UNLModify' diff --git a/packages/xrpl/src/models/transactions/payment.ts b/packages/xrpl/src/models/transactions/payment.ts index cf5ee4f884..6ec3f3effd 100644 --- a/packages/xrpl/src/models/transactions/payment.ts +++ b/packages/xrpl/src/models/transactions/payment.ts @@ -43,6 +43,13 @@ export enum PaymentFlags { * details. */ tfLimitQuality = 0x00040000, + /** + * If the Payment is used to create an account, the created account will be + * sponsored by the transaction sender (Account). When this flag is enabled, + * the XRP amount does not need to be greater than or equal to the account + * reserve requirement. + */ + tfSponsorCreatedAccount = 0x00080000, } /** @@ -105,6 +112,13 @@ export interface PaymentFlagsInterface extends GlobalFlagsInterface { * details. */ tfLimitQuality?: boolean + /** + * If the Payment is used to create an account, the created account will be + * sponsored by the transaction sender (Account). When this flag is enabled, + * the XRP amount does not need to be greater than or equal to the account + * reserve requirement. + */ + tfSponsorCreatedAccount?: boolean } /** diff --git a/packages/xrpl/src/models/transactions/sponsorshipSet.ts b/packages/xrpl/src/models/transactions/sponsorshipSet.ts new file mode 100644 index 0000000000..f489b70ed9 --- /dev/null +++ b/packages/xrpl/src/models/transactions/sponsorshipSet.ts @@ -0,0 +1,175 @@ +import { ValidationError } from '../../errors' + +import { + Account, + BaseTransaction, + GlobalFlagsInterface, + isAccount, + isNumber, + isString, + validateBaseTransaction, + validateOptionalField, +} from './common' + +/** + * Enum representing flags for the SponsorshipSet transaction. + * + * @category Transaction Flags + */ +export enum SponsorshipSetFlags { + /** + * Adds the restriction that every use of this sponsor for sponsoring fees + * requires a signature from the sponsor. + */ + tfSponsorshipSetRequireSignForFee = 0x00010000, + /** + * Removes the restriction that every use of this sponsor for sponsoring fees + * requires a signature from the sponsor. + */ + tfSponsorshipClearRequireSignForFee = 0x00020000, + /** + * Adds the restriction that every use of this sponsor for sponsoring reserves + * requires a signature from the sponsor. + */ + tfSponsorshipSetRequireSignForReserve = 0x00040000, + /** + * Removes the restriction that every use of this sponsor for sponsoring + * reserves requires a signature from the sponsor. + */ + tfSponsorshipClearRequireSignForReserve = 0x00080000, + /** + * Removes the Sponsorship ledger object. + */ + tfDeleteObject = 0x00100000, +} + +/** + * Map of flags to boolean values representing {@link SponsorshipSet} transaction + * flags. + * + * @category Transaction Flags + */ +export interface SponsorshipSetFlagsInterface extends GlobalFlagsInterface { + /** + * Adds the restriction that every use of this sponsor for sponsoring fees + * requires a signature from the sponsor. + */ + tfSponsorshipSetRequireSignForFee?: boolean + /** + * Removes the restriction that every use of this sponsor for sponsoring fees + * requires a signature from the sponsor. + */ + tfSponsorshipClearRequireSignForFee?: boolean + /** + * Adds the restriction that every use of this sponsor for sponsoring reserves + * requires a signature from the sponsor. + */ + tfSponsorshipSetRequireSignForReserve?: boolean + /** + * Removes the restriction that every use of this sponsor for sponsoring + * reserves requires a signature from the sponsor. + */ + tfSponsorshipClearRequireSignForReserve?: boolean + /** + * Removes the Sponsorship ledger object. + */ + tfDeleteObject?: boolean +} + +/** + * A SponsorshipSet transaction creates, updates, or deletes a Sponsorship + * ledger object. The Sponsorship object represents a pre-funded sponsorship + * relationship between a sponsor and a sponsee. + * + * @category Transaction Models + */ +export interface SponsorshipSet extends BaseTransaction { + TransactionType: 'SponsorshipSet' + /** + * The sponsor associated with this relationship. This account also pays for + * the reserve of this object. If this field is included, the Account is + * assumed to be the Sponsee. + */ + Sponsor?: Account + /** + * The sponsee associated with this relationship. If this field is included, + * the Account is assumed to be the Sponsor. + */ + Sponsee?: Account + /** + * The (remaining) amount of XRP (in drops) that the sponsor has provided for + * the sponsee to use for fees. This value will replace what is currently in + * the Sponsorship.FeeAmount field (if it exists). + */ + FeeAmount?: string + /** + * The maximum fee per transaction that will be sponsored. This is to prevent + * abuse/excessive draining of the sponsored fee pool. + */ + MaxFee?: string + /** + * The (remaining) amount of reserves that the sponsor has provided for the + * sponsee to use. This value will replace what is currently in the + * Sponsorship.ReserveCount field (if it exists). + */ + ReserveCount?: number + Flags?: number | SponsorshipSetFlagsInterface +} + +/** + * Verify the form and type of a SponsorshipSet at runtime. + * + * @param tx - A SponsorshipSet Transaction. + * @throws When the SponsorshipSet is malformed. + */ +export function validateSponsorshipSet(tx: Record): void { + validateBaseTransaction(tx) + + // Must have exactly one of Sponsor or Sponsee + const hasSponsor = tx.Sponsor !== undefined + const hasSponsee = tx.Sponsee !== undefined + + if (hasSponsor && hasSponsee) { + throw new ValidationError( + 'SponsorshipSet: cannot specify both Sponsor and Sponsee', + ) + } + + if (!hasSponsor && !hasSponsee) { + throw new ValidationError( + 'SponsorshipSet: must specify either Sponsor or Sponsee', + ) + } + + validateOptionalField(tx, 'Sponsor', isAccount) + validateOptionalField(tx, 'Sponsee', isAccount) + validateOptionalField(tx, 'FeeAmount', isString) + validateOptionalField(tx, 'MaxFee', isString) + validateOptionalField(tx, 'ReserveCount', isNumber) + + // Validate flag combinations with tfDeleteObject + const flags = + typeof tx.Flags === 'number' ? tx.Flags : 0 + + const tfDeleteObject = SponsorshipSetFlags.tfDeleteObject + + if ((flags & tfDeleteObject) !== 0) { + // When deleting, cannot specify FeeAmount, MaxFee, or ReserveCount + if (tx.FeeAmount !== undefined) { + throw new ValidationError( + 'SponsorshipSet: FeeAmount cannot be specified with tfDeleteObject', + ) + } + if (tx.MaxFee !== undefined) { + throw new ValidationError( + 'SponsorshipSet: MaxFee cannot be specified with tfDeleteObject', + ) + } + if (tx.ReserveCount !== undefined) { + throw new ValidationError( + 'SponsorshipSet: ReserveCount cannot be specified with tfDeleteObject', + ) + } + } +} + diff --git a/packages/xrpl/src/models/transactions/sponsorshipTransfer.ts b/packages/xrpl/src/models/transactions/sponsorshipTransfer.ts new file mode 100644 index 0000000000..7d950b78f0 --- /dev/null +++ b/packages/xrpl/src/models/transactions/sponsorshipTransfer.ts @@ -0,0 +1,69 @@ +import { + Account, + BaseTransaction, + isAccount, + isNumber, + isString, + SponsorSignature, + validateBaseTransaction, + validateOptionalField, +} from './common' + +/** + * A SponsorshipTransfer transaction transfers a sponsor relationship for a + * particular ledger object's reserve. The sponsor relationship can either be + * passed on to a new sponsor, or dissolved entirely (with the sponsee taking + * on the reserve). Either the sponsor or sponsee may submit this transaction + * at any point in time. + * + * There are three valid transfer scenarios: + * 1. Transferring from sponsor to sponsee (sponsored to unsponsored) + * - Either the sponsor or sponsee may submit this transaction. + * 2. Transferring from sponsee to sponsor (unsponsored to sponsored) + * - Only the sponsee may submit this transaction. + * 3. Transferring from sponsor to new sponsor + * - Only the sponsee may submit this transaction. + * + * @category Transaction Models + */ +export interface SponsorshipTransfer extends BaseTransaction { + TransactionType: 'SponsorshipTransfer' + /** + * The ID of the ledger object to transfer sponsorship for. If not included, + * refers to the account sending the transaction. + */ + ObjectID?: string + /** + * The new sponsor of the object. If not included (or if tfSponsorReserve is + * not set in SponsorFlags), the burden of the reserve will be passed back + * to the ledger object's owner (the former sponsee). + */ + Sponsor?: Account + /** + * Flags indicating what type of sponsorship this is. Should include + * tfSponsorReserve (2) if transferring to a new sponsor. + */ + SponsorFlags?: number + /** + * The sponsor's signature information to authorize the sponsorship transfer. + * Required when transferring to a new sponsor. + */ + SponsorSignature?: SponsorSignature +} + +/** + * Verify the form and type of a SponsorshipTransfer at runtime. + * + * @param tx - A SponsorshipTransfer Transaction. + * @throws When the SponsorshipTransfer is malformed. + */ +export function validateSponsorshipTransfer( + tx: Record, +): void { + validateBaseTransaction(tx) + + validateOptionalField(tx, 'ObjectID', isString) + validateOptionalField(tx, 'Sponsor', isAccount) + validateOptionalField(tx, 'SponsorFlags', isNumber) +} + diff --git a/packages/xrpl/src/models/transactions/transaction.ts b/packages/xrpl/src/models/transactions/transaction.ts index 97f710a2ea..7c3d37a795 100644 --- a/packages/xrpl/src/models/transactions/transaction.ts +++ b/packages/xrpl/src/models/transactions/transaction.ts @@ -109,6 +109,11 @@ import { import { SetFee } from './setFee' import { SetRegularKey, validateSetRegularKey } from './setRegularKey' import { SignerListSet, validateSignerListSet } from './signerListSet' +import { SponsorshipSet, validateSponsorshipSet } from './sponsorshipSet' +import { + SponsorshipTransfer, + validateSponsorshipTransfer, +} from './sponsorshipTransfer' import { TicketCreate, validateTicketCreate } from './ticketCreate' import { TrustSet, validateTrustSet } from './trustSet' import { UNLModify } from './UNLModify' @@ -206,6 +211,8 @@ export type SubmittableTransaction = | PermissionedDomainDelete | SetRegularKey | SignerListSet + | SponsorshipSet + | SponsorshipTransfer | TicketCreate | TrustSet | VaultClawback @@ -510,6 +517,14 @@ export function validate(transaction: Record): void { validateSignerListSet(tx) break + case 'SponsorshipSet': + validateSponsorshipSet(tx) + break + + case 'SponsorshipTransfer': + validateSponsorshipTransfer(tx) + break + case 'TicketCreate': validateTicketCreate(tx) break From 3fa4bd8af70e2f274dccb1aa09ea4d91133dbe6a Mon Sep 17 00:00:00 2001 From: Ashray Chowdhry Date: Wed, 11 Feb 2026 10:51:47 -0500 Subject: [PATCH 02/11] codec changes --- .../src/enums/definitions.json | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/packages/ripple-binary-codec/src/enums/definitions.json b/packages/ripple-binary-codec/src/enums/definitions.json index ccd7b7990f..44358c947e 100644 --- a/packages/ripple-binary-codec/src/enums/definitions.json +++ b/packages/ripple-binary-codec/src/enums/definitions.json @@ -850,6 +850,56 @@ "type": "UInt32" } ], + [ + "SponsoredOwnerCount", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 69, + "type": "UInt32" + } + ], + [ + "SponsoringOwnerCount", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 70, + "type": "UInt32" + } + ], + [ + "SponsoringAccountCount", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 71, + "type": "UInt32" + } + ], + [ + "ReserveCount", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 72, + "type": "UInt32" + } + ], + [ + "SponsorFlags", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 73, + "type": "UInt32" + } + ], [ "IndexNext", { @@ -1140,6 +1190,16 @@ "type": "UInt64" } ], + [ + "SponseeNode", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 32, + "type": "UInt64" + } + ], [ "EmailHash", { @@ -1510,6 +1570,16 @@ "type": "Hash256" } ], + [ + "ObjectID", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 39, + "type": "Hash256" + } + ], [ "hash", { @@ -1800,6 +1870,26 @@ "type": "Amount" } ], + [ + "FeeAmount", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 32, + "type": "Amount" + } + ], + [ + "MaxFee", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 33, + "type": "Amount" + } + ], [ "PublicKey", { @@ -2310,6 +2400,56 @@ "type": "AccountID" } ], + [ + "Sponsor", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 27, + "type": "AccountID" + } + ], + [ + "HighSponsor", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 28, + "type": "AccountID" + } + ], + [ + "LowSponsor", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 29, + "type": "AccountID" + } + ], + [ + "CounterpartySponsor", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 30, + "type": "AccountID" + } + ], + [ + "Sponsee", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 31, + "type": "AccountID" + } + ], [ "Number", { @@ -2840,6 +2980,16 @@ "type": "STObject" } ], + [ + "SponsorSignature", + { + "isSerialized": true, + "isSigningField": false, + "isVLEncoded": false, + "nth": 38, + "type": "STObject" + } + ], [ "Signers", { @@ -3459,6 +3609,7 @@ "PermissionedDomain": 130, "RippleState": 114, "SignerList": 83, + "Sponsorship": 144, "Ticket": 84, "Vault": 132, "XChainOwnedClaimID": 113, @@ -3720,6 +3871,8 @@ "SetFee": 101, "SetRegularKey": 5, "SignerListSet": 12, + "SponsorshipSet": 86, + "SponsorshipTransfer": 85, "TicketCreate": 10, "TrustSet": 20, "UNLModify": 102, From 0c3762eff5828f025e4d309140f572cd66f9b651 Mon Sep 17 00:00:00 2001 From: Ashray Chowdhry Date: Wed, 11 Feb 2026 11:04:37 -0500 Subject: [PATCH 03/11] regen definitions --- packages/ripple-binary-codec/src/enums/definitions.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/ripple-binary-codec/src/enums/definitions.json b/packages/ripple-binary-codec/src/enums/definitions.json index 44358c947e..17d5d12bd1 100644 --- a/packages/ripple-binary-codec/src/enums/definitions.json +++ b/packages/ripple-binary-codec/src/enums/definitions.json @@ -3666,6 +3666,7 @@ "tecNO_LINE_REDUNDANT": 127, "tecNO_PERMISSION": 139, "tecNO_REGULAR_KEY": 131, + "tecNO_SPONSOR_PERMISSION": 199, "tecNO_SUITABLE_NFTOKEN_PAGE": 155, "tecNO_TARGET": 138, "tecOBJECT_NOT_FOUND": 160, @@ -3803,6 +3804,7 @@ "terNO_DELEGATE_PERMISSION": -85, "terNO_LINE": -94, "terNO_RIPPLE": -90, + "terNO_SPONSORSHIP": -84, "terOWNERS": -93, "terPRE_SEQ": -92, "terPRE_TICKET": -88, From 52ba681b0c01f08ce5d7c4c703674db4eccb5f63 Mon Sep 17 00:00:00 2001 From: Ashray Chowdhry Date: Wed, 11 Feb 2026 11:21:48 -0500 Subject: [PATCH 04/11] rpc and granular --- .../src/enums/xrpl-definitions-base.ts | 2 + .../xrpl/src/models/methods/accountObjects.ts | 5 + .../src/models/methods/accountSponsoring.ts | 98 +++++ packages/xrpl/src/models/methods/index.ts | 346 +++++++++--------- 4 files changed, 285 insertions(+), 166 deletions(-) create mode 100644 packages/xrpl/src/models/methods/accountSponsoring.ts diff --git a/packages/ripple-binary-codec/src/enums/xrpl-definitions-base.ts b/packages/ripple-binary-codec/src/enums/xrpl-definitions-base.ts index 0f1b0d679a..6caa45c6fa 100644 --- a/packages/ripple-binary-codec/src/enums/xrpl-definitions-base.ts +++ b/packages/ripple-binary-codec/src/enums/xrpl-definitions-base.ts @@ -94,6 +94,8 @@ class XrplDefinitionsBase { PaymentBurn: 65546, MPTokenIssuanceLock: 65547, MPTokenIssuanceUnlock: 65548, + SponsorFee: 65549, + SponsorReserve: 65550, } const incrementedTransactionTypes = Object.fromEntries( diff --git a/packages/xrpl/src/models/methods/accountObjects.ts b/packages/xrpl/src/models/methods/accountObjects.ts index 90092847ce..561f868f7d 100644 --- a/packages/xrpl/src/models/methods/accountObjects.ts +++ b/packages/xrpl/src/models/methods/accountObjects.ts @@ -30,6 +30,11 @@ export interface AccountObjectsRequest * from being deleted. The default is false. */ deletion_blockers_only?: boolean + /** + * If true, only return ledger entries that are sponsored. If false, only + * return ledger entries that are not sponsored. If omitted, return all objects. + */ + sponsored?: boolean /** * The maximum number of objects to include in the results. Must be within * the inclusive range 10 to 400 on non-admin connections. The default is 200. diff --git a/packages/xrpl/src/models/methods/accountSponsoring.ts b/packages/xrpl/src/models/methods/accountSponsoring.ts new file mode 100644 index 0000000000..d70bf8614f --- /dev/null +++ b/packages/xrpl/src/models/methods/accountSponsoring.ts @@ -0,0 +1,98 @@ +import { Amendments, FeeSettings, LedgerHashes } from '../ledger' +import { LedgerEntry, LedgerEntryFilter } from '../ledger/LedgerEntry' + +import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod' + +export type AccountSponsoringObjectType = Exclude< + LedgerEntryFilter, + 'amendments' | 'fee' | 'hashes' +> + +/** + * The account_sponsoring command returns a list of objects that an account is + * sponsoring; namely, a list of objects where the Sponsor is the given account. + * This is a Clio-specific RPC method. + * + * @category Requests + */ +export interface AccountSponsoringRequest + extends BaseRequest, + LookupByLedgerRequest { + command: 'account_sponsoring' + /** The sponsor account to get sponsored objects for. */ + account: string + /** + * If true, the response only includes objects that would block this account + * from being deleted. The default is false. + */ + deletion_blockers_only?: boolean + /** + * Filter results by a ledger entry type. Some examples are 'offer' and 'escrow'. + */ + type?: AccountSponsoringObjectType + /** + * The maximum number of objects to include in the results. + */ + limit?: number + /** + * Value from a previous paginated response. Resume retrieving data where + * that response left off. + */ + marker?: unknown +} + +/** + * Sponsored objects can be any ledger entry type except Amendments, FeeSettings, + * and LedgerHashes. + */ +export type SponsoredObject = Exclude< + LedgerEntry, + Amendments | FeeSettings | LedgerHashes +> + +/** + * Response expected from an {@link AccountSponsoringRequest}. + * + * @category Responses + */ +export interface AccountSponsoringResponse extends BaseResponse { + result: { + /** The account this request corresponds to. */ + account: string + /** + * Array of ledger entries that this account is sponsoring. Each object + * is in its raw ledger format. + */ + sponsored_objects: SponsoredObject[] + /** + * The identifying hash of the ledger that was used to generate this + * response. + */ + ledger_hash?: string + /** + * The ledger index of the ledger version that was used to generate this + * response. + */ + ledger_index?: number + /** + * The ledger index of the current in-progress ledger version, which was + * used to generate this response. + */ + ledger_current_index?: number + /** The limit that was used in this request, if any. */ + limit?: number + /** + * Server-defined value indicating the response is paginated. Pass this to + * the next call to resume where this call left off. Omitted when there are + * no additional pages after this one. + */ + marker?: unknown + /** + * If included and set to true, the information in this response comes from + * a validated ledger version. Otherwise, the information is subject to + * change. + */ + validated?: boolean + } +} + diff --git a/packages/xrpl/src/models/methods/index.ts b/packages/xrpl/src/models/methods/index.ts index 5332c8c52a..153fa8b16a 100644 --- a/packages/xrpl/src/models/methods/index.ts +++ b/packages/xrpl/src/models/methods/index.ts @@ -37,6 +37,12 @@ import { AccountObjectsResponse, AccountObjectType, } from './accountObjects' +import { + AccountSponsoringObjectType, + AccountSponsoringRequest, + AccountSponsoringResponse, + SponsoredObject, +} from './accountSponsoring' import { AccountOffer, AccountOffersRequest, @@ -242,6 +248,7 @@ type Request = | NFTBuyOffersRequest | NFTSellOffersRequest // clio only methods + | AccountSponsoringRequest | NFTInfoRequest | NFTHistoryRequest | NFTsByIssuerRequest @@ -303,6 +310,7 @@ type Response = | NFTBuyOffersResponse | NFTSellOffersResponse // clio only methods + | AccountSponsoringResponse | NFTInfoResponse | NFTHistoryResponse | NFTsByIssuerResponse @@ -319,160 +327,162 @@ export type RequestResponseMap< > = T extends AccountChannelsRequest ? AccountChannelsResponse : T extends AccountCurrenciesRequest - ? AccountCurrenciesResponse - : T extends AccountInfoRequest - ? AccountInfoVersionResponseMap - : T extends AccountLinesRequest - ? AccountLinesResponse - : T extends AccountNFTsRequest - ? AccountNFTsResponse - : T extends AccountObjectsRequest - ? AccountObjectsResponse - : T extends AccountOffersRequest - ? AccountOffersResponse - : T extends AccountTxRequest - ? AccountTxVersionResponseMap - : T extends AMMInfoRequest - ? AMMInfoResponse - : T extends GatewayBalancesRequest - ? GatewayBalancesResponse - : T extends GetAggregatePriceRequest - ? GetAggregatePriceResponse - : T extends NoRippleCheckRequest - ? NoRippleCheckResponse - : // NOTE: The order of these LedgerRequest types is important - // to get the proper type matching overrides based on parameters set - // in the request. For example LedgerRequestExpandedTransactionsBinary - // should match LedgerRequestExpandedTransactionsOnly, but not - // LedgerRequestExpandedAccountsOnly. This is because the - // LedgerRequestExpandedTransactionsBinary type is a superset of - // LedgerRequestExpandedTransactionsOnly, but not of the other. - // This is why LedgerRequestExpandedTransactionsBinary is listed - // first in the type list. - // - // Here is an example using real data: - // LedgerRequestExpandedTransactionsBinary = { - // command: 'ledger', - // ledger_index: 'validated', - // expand: true, - // transactions: true, - // binary: true, - // } - // LedgerRequestExpandedTransactionsOnly = { - // command: 'ledger', - // ledger_index: 'validated', - // expand: true, - // transactions: true, - // } - // LedgerRequestExpandedAccountsOnly = { - // command: 'ledger', - // ledger_index: 'validated', - // accounts: true, - // expand: true, - // } - // LedgerRequest = { - // command: 'ledger', - // ledger_index: 'validated', - // } - // - // The type with the most parameters set should be listed first. In this - // case LedgerRequestExpandedTransactionsBinary has the most parameters (`expand`, `transactions`, and `binary`) - // set, so it is listed first. When TypeScript tries to match the type of - // a request to a response, it will try to match the request type to the - // response type in the order they are listed. So, if we have a request - // with the following parameters: - // { - // command: 'ledger', - // ledger_index: 'validated', - // expand: true, - // transactions: true, - // binary: true, - // } - // TypeScript will first try to match the request type to - // LedgerRequestExpandedTransactionsBinary, which will succeed. It will - // then try to match the response type to LedgerResponseExpanded, which - // will also succeed. If we had listed LedgerRequestExpandedTransactionsOnly - // first, TypeScript would have tried to match the request type to - // LedgerRequestExpandedTransactionsOnly, which would have succeeded, but - // then we'd get the wrong response type, LedgerResponse, instead of - // LedgerResponseExpanded. - T extends LedgerRequestExpandedTransactionsBinary - ? LedgerVersionResponseMap - : T extends LedgerRequestExpandedAccountsAndTransactions - ? LedgerResponseExpanded - : T extends LedgerRequestExpandedTransactionsOnly - ? LedgerResponseExpanded - : T extends LedgerRequestExpandedAccountsOnly - ? LedgerResponseExpanded - : T extends LedgerRequest - ? LedgerVersionResponseMap - : T extends LedgerClosedRequest - ? LedgerClosedResponse - : T extends LedgerCurrentRequest - ? LedgerCurrentResponse - : T extends LedgerDataRequest - ? LedgerDataResponse - : T extends LedgerEntryRequest - ? LedgerEntryResponse - : T extends SimulateBinaryRequest - ? SimulateBinaryResponse - : T extends SimulateJsonRequest - ? SimulateJsonResponse - : T extends SimulateRequest - ? SimulateJsonResponse - : T extends SubmitRequest - ? SubmitResponse - : T extends SubmitMultisignedRequest - ? SubmitMultisignedVersionResponseMap - : T extends TransactionEntryRequest - ? TransactionEntryResponse - : T extends TxRequest - ? TxVersionResponseMap - : T extends BookOffersRequest - ? BookOffersResponse - : T extends DepositAuthorizedRequest - ? DepositAuthorizedResponse - : T extends PathFindRequest - ? PathFindResponse - : T extends RipplePathFindRequest - ? RipplePathFindResponse - : T extends ChannelVerifyRequest - ? ChannelVerifyResponse - : T extends SubscribeRequest - ? SubscribeResponse - : T extends UnsubscribeRequest - ? UnsubscribeResponse - : T extends FeeRequest - ? FeeResponse - : T extends ManifestRequest - ? ManifestResponse - : T extends ServerInfoRequest - ? ServerInfoResponse - : T extends ServerStateRequest - ? ServerStateResponse - : T extends ServerDefinitionsRequest - ? ServerDefinitionsResponse - : T extends FeatureAllRequest - ? FeatureAllResponse - : T extends FeatureOneRequest - ? FeatureOneResponse - : T extends PingRequest - ? PingResponse - : T extends RandomRequest - ? RandomResponse - : T extends NFTBuyOffersRequest - ? NFTBuyOffersResponse - : T extends NFTSellOffersRequest - ? NFTSellOffersResponse - : T extends NFTInfoRequest - ? NFTInfoResponse - : T extends NFTsByIssuerRequest - ? NFTsByIssuerResponse - : T extends NFTHistoryRequest - ? NFTHistoryResponse - : T extends VaultInfoRequest - ? VaultInfoResponse - : Response + ? AccountCurrenciesResponse + : T extends AccountInfoRequest + ? AccountInfoVersionResponseMap + : T extends AccountLinesRequest + ? AccountLinesResponse + : T extends AccountNFTsRequest + ? AccountNFTsResponse + : T extends AccountObjectsRequest + ? AccountObjectsResponse + : T extends AccountOffersRequest + ? AccountOffersResponse + : T extends AccountTxRequest + ? AccountTxVersionResponseMap + : T extends AMMInfoRequest + ? AMMInfoResponse + : T extends GatewayBalancesRequest + ? GatewayBalancesResponse + : T extends GetAggregatePriceRequest + ? GetAggregatePriceResponse + : T extends NoRippleCheckRequest + ? NoRippleCheckResponse + : // NOTE: The order of these LedgerRequest types is important + // to get the proper type matching overrides based on parameters set + // in the request. For example LedgerRequestExpandedTransactionsBinary + // should match LedgerRequestExpandedTransactionsOnly, but not + // LedgerRequestExpandedAccountsOnly. This is because the + // LedgerRequestExpandedTransactionsBinary type is a superset of + // LedgerRequestExpandedTransactionsOnly, but not of the other. + // This is why LedgerRequestExpandedTransactionsBinary is listed + // first in the type list. + // + // Here is an example using real data: + // LedgerRequestExpandedTransactionsBinary = { + // command: 'ledger', + // ledger_index: 'validated', + // expand: true, + // transactions: true, + // binary: true, + // } + // LedgerRequestExpandedTransactionsOnly = { + // command: 'ledger', + // ledger_index: 'validated', + // expand: true, + // transactions: true, + // } + // LedgerRequestExpandedAccountsOnly = { + // command: 'ledger', + // ledger_index: 'validated', + // accounts: true, + // expand: true, + // } + // LedgerRequest = { + // command: 'ledger', + // ledger_index: 'validated', + // } + // + // The type with the most parameters set should be listed first. In this + // case LedgerRequestExpandedTransactionsBinary has the most parameters (`expand`, `transactions`, and `binary`) + // set, so it is listed first. When TypeScript tries to match the type of + // a request to a response, it will try to match the request type to the + // response type in the order they are listed. So, if we have a request + // with the following parameters: + // { + // command: 'ledger', + // ledger_index: 'validated', + // expand: true, + // transactions: true, + // binary: true, + // } + // TypeScript will first try to match the request type to + // LedgerRequestExpandedTransactionsBinary, which will succeed. It will + // then try to match the response type to LedgerResponseExpanded, which + // will also succeed. If we had listed LedgerRequestExpandedTransactionsOnly + // first, TypeScript would have tried to match the request type to + // LedgerRequestExpandedTransactionsOnly, which would have succeeded, but + // then we'd get the wrong response type, LedgerResponse, instead of + // LedgerResponseExpanded. + T extends LedgerRequestExpandedTransactionsBinary + ? LedgerVersionResponseMap + : T extends LedgerRequestExpandedAccountsAndTransactions + ? LedgerResponseExpanded + : T extends LedgerRequestExpandedTransactionsOnly + ? LedgerResponseExpanded + : T extends LedgerRequestExpandedAccountsOnly + ? LedgerResponseExpanded + : T extends LedgerRequest + ? LedgerVersionResponseMap + : T extends LedgerClosedRequest + ? LedgerClosedResponse + : T extends LedgerCurrentRequest + ? LedgerCurrentResponse + : T extends LedgerDataRequest + ? LedgerDataResponse + : T extends LedgerEntryRequest + ? LedgerEntryResponse + : T extends SimulateBinaryRequest + ? SimulateBinaryResponse + : T extends SimulateJsonRequest + ? SimulateJsonResponse + : T extends SimulateRequest + ? SimulateJsonResponse + : T extends SubmitRequest + ? SubmitResponse + : T extends SubmitMultisignedRequest + ? SubmitMultisignedVersionResponseMap + : T extends TransactionEntryRequest + ? TransactionEntryResponse + : T extends TxRequest + ? TxVersionResponseMap + : T extends BookOffersRequest + ? BookOffersResponse + : T extends DepositAuthorizedRequest + ? DepositAuthorizedResponse + : T extends PathFindRequest + ? PathFindResponse + : T extends RipplePathFindRequest + ? RipplePathFindResponse + : T extends ChannelVerifyRequest + ? ChannelVerifyResponse + : T extends SubscribeRequest + ? SubscribeResponse + : T extends UnsubscribeRequest + ? UnsubscribeResponse + : T extends FeeRequest + ? FeeResponse + : T extends ManifestRequest + ? ManifestResponse + : T extends ServerInfoRequest + ? ServerInfoResponse + : T extends ServerStateRequest + ? ServerStateResponse + : T extends ServerDefinitionsRequest + ? ServerDefinitionsResponse + : T extends FeatureAllRequest + ? FeatureAllResponse + : T extends FeatureOneRequest + ? FeatureOneResponse + : T extends PingRequest + ? PingResponse + : T extends RandomRequest + ? RandomResponse + : T extends NFTBuyOffersRequest + ? NFTBuyOffersResponse + : T extends NFTSellOffersRequest + ? NFTSellOffersResponse + : T extends NFTInfoRequest + ? NFTInfoResponse + : T extends NFTsByIssuerRequest + ? NFTsByIssuerResponse + : T extends NFTHistoryRequest + ? NFTHistoryResponse + : T extends VaultInfoRequest + ? VaultInfoResponse + : T extends AccountSponsoringRequest + ? AccountSponsoringResponse + : Response export type MarkerRequest = Request & { limit?: number @@ -493,18 +503,18 @@ export type RequestAllResponseMap< > = T extends AccountChannelsRequest ? AccountChannelsResponse : T extends AccountLinesRequest - ? AccountLinesResponse - : T extends AccountObjectsRequest - ? AccountObjectsResponse - : T extends AccountOffersRequest - ? AccountOffersResponse - : T extends AccountTxRequest - ? AccountTxVersionResponseMap - : T extends LedgerDataRequest - ? LedgerDataResponse - : T extends BookOffersRequest - ? BookOffersResponse - : MarkerResponse + ? AccountLinesResponse + : T extends AccountObjectsRequest + ? AccountObjectsResponse + : T extends AccountOffersRequest + ? AccountOffersResponse + : T extends AccountTxRequest + ? AccountTxVersionResponseMap + : T extends LedgerDataRequest + ? LedgerDataResponse + : T extends BookOffersRequest + ? BookOffersResponse + : MarkerResponse export { // Allow users to define their own requests and responses. This is useful for releasing experimental versions @@ -647,6 +657,10 @@ export { NFTSellOffersRequest, NFTSellOffersResponse, // clio only methods + AccountSponsoringObjectType, + AccountSponsoringRequest, + AccountSponsoringResponse, + SponsoredObject, NFTInfoRequest, NFTInfoResponse, NFTHistoryRequest, From 059588d573b72f805bbe6b22104b577f84a1e0a0 Mon Sep 17 00:00:00 2001 From: Ashray Chowdhry Date: Wed, 11 Feb 2026 11:32:32 -0500 Subject: [PATCH 05/11] add base entry Sponsor field --- packages/xrpl/src/models/ledger/BaseLedgerEntry.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/xrpl/src/models/ledger/BaseLedgerEntry.ts b/packages/xrpl/src/models/ledger/BaseLedgerEntry.ts index abc474c1c6..2d06afbbb5 100644 --- a/packages/xrpl/src/models/ledger/BaseLedgerEntry.ts +++ b/packages/xrpl/src/models/ledger/BaseLedgerEntry.ts @@ -1,5 +1,16 @@ export interface BaseLedgerEntry { index: string + /** + * The address of the account sponsoring the reserve for this ledger object. + * Only present if the object's reserve is being sponsored by another account. + * This field is added when sponsorship is established and removed when + * sponsorship is dissolved. + * + * Note: For RippleState objects, use HighSponsor and LowSponsor instead. + * This field must not appear on DirectoryNode, Amendments, FeeSettings, + * or NegativeUNL objects. + */ + Sponsor?: string } export interface HasPreviousTxnID { From 525cd7a5eb8d049b839234d502d2f89482b0b092 Mon Sep 17 00:00:00 2001 From: Ashray Chowdhry Date: Wed, 11 Feb 2026 11:45:54 -0500 Subject: [PATCH 06/11] unit testts --- .../test/granularPermissions.test.ts | 140 +++++++ packages/xrpl/test/models/sponsorship.test.ts | 385 ++++++++++++++++++ 2 files changed, 525 insertions(+) create mode 100644 packages/ripple-binary-codec/test/granularPermissions.test.ts create mode 100644 packages/xrpl/test/models/sponsorship.test.ts diff --git a/packages/ripple-binary-codec/test/granularPermissions.test.ts b/packages/ripple-binary-codec/test/granularPermissions.test.ts new file mode 100644 index 0000000000..9fcf8b2451 --- /dev/null +++ b/packages/ripple-binary-codec/test/granularPermissions.test.ts @@ -0,0 +1,140 @@ +import { XrplDefinitions } from '../src' +import normalDefinitionsJson from '../src/enums/definitions.json' + +describe('Granular Permissions', function () { + describe('SponsorFee and SponsorReserve permissions', function () { + it('should have SponsorFee defined with value 65549', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + expect(definitions.granularPermissions.SponsorFee).toBe(65549) + }) + + it('should have SponsorReserve defined with value 65550', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + expect(definitions.granularPermissions.SponsorReserve).toBe(65550) + }) + + it('should have all expected granular permissions defined', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + + // Verify all granular permissions are present + const expectedPermissions = { + TrustlineAuthorize: 65537, + TrustlineFreeze: 65538, + TrustlineUnfreeze: 65539, + AccountDomainSet: 65540, + AccountEmailHashSet: 65541, + AccountMessageKeySet: 65542, + AccountTransferRateSet: 65543, + AccountTickSizeSet: 65544, + PaymentMint: 65545, + PaymentBurn: 65546, + MPTokenIssuanceLock: 65547, + MPTokenIssuanceUnlock: 65548, + SponsorFee: 65549, + SponsorReserve: 65550, + } + + expect(definitions.granularPermissions).toEqual(expectedPermissions) + }) + + it('should have granular permissions with values greater than UINT16_MAX (65536)', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + const UINT16_MAX = 65536 + + // All granular permissions should have values > 65536 + Object.entries(definitions.granularPermissions).forEach( + ([_name, value]) => { + expect(value).toBeGreaterThan(UINT16_MAX) + }, + ) + }) + + it('should have SponsorFee and SponsorReserve as the highest permission values', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + + const allValues = Object.values(definitions.granularPermissions) + const maxValue = Math.max(...allValues) + + // SponsorReserve should be the highest value + expect(definitions.granularPermissions.SponsorReserve).toBe(maxValue) + // SponsorFee should be second highest + expect(definitions.granularPermissions.SponsorFee).toBe(maxValue - 1) + }) + }) + + /** + * Edge cases and boundary conditions for granular permissions. + * Per XLS-0068 Sections 13-14, granular permissions are values > UINT16_MAX (65536). + */ + describe('Edge cases and boundary conditions', function () { + it('should have exactly 14 granular permissions defined', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + const permissionCount = Object.keys( + definitions.granularPermissions, + ).length + + expect(permissionCount).toBe(14) + }) + + it('should have no granular permissions with value <= UINT16_MAX', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + const UINT16_MAX = 65536 + + Object.entries(definitions.granularPermissions).forEach( + ([_name, value]) => { + expect(value).toBeGreaterThan(UINT16_MAX) + }, + ) + }) + + it('should have granular permissions starting at 65537 (UINT16_MAX + 1)', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + const UINT16_MAX_PLUS_ONE = 65537 + + const allValues = Object.values(definitions.granularPermissions) + const minValue = Math.min(...allValues) + + expect(minValue).toBe(UINT16_MAX_PLUS_ONE) + }) + + it('should have SponsorFee value exactly one less than SponsorReserve', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + + const sponsorFee = definitions.granularPermissions.SponsorFee + const sponsorReserve = definitions.granularPermissions.SponsorReserve + + expect(sponsorReserve - sponsorFee).toBe(1) + }) + + it('should have unique values for all granular permissions', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + + const allValues = Object.values(definitions.granularPermissions) + const uniqueValues = new Set(allValues) + + expect(uniqueValues.size).toBe(allValues.length) + }) + + it('should have consecutive values for all granular permissions', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + + const allValues = Object.values(definitions.granularPermissions).sort( + (a, b) => a - b, + ) + + for (let i = 1; i < allValues.length; i++) { + expect(allValues[i] - allValues[i - 1]).toBe(1) + } + }) + + it('should not allow undefined permission names to be accessed', function () { + const definitions = new XrplDefinitions(normalDefinitionsJson) + + // Accessing an undefined permission should return undefined + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect( + (definitions.granularPermissions as any).NonExistentPermission, + ).toBeUndefined() + }) + }) +}) diff --git a/packages/xrpl/test/models/sponsorship.test.ts b/packages/xrpl/test/models/sponsorship.test.ts new file mode 100644 index 0000000000..65d80e2639 --- /dev/null +++ b/packages/xrpl/test/models/sponsorship.test.ts @@ -0,0 +1,385 @@ +import { assert } from 'chai' + +import { + AccountObjectsRequest, + AccountSponsoringRequest, + AccountSponsoringResponse, +} from '../../src/models/methods' +import Check from '../../src/models/ledger/Check' +import Escrow from '../../src/models/ledger/Escrow' +import Offer from '../../src/models/ledger/Offer' +import AccountRoot from '../../src/models/ledger/AccountRoot' +import { BaseLedgerEntry } from '../../src/models/ledger/BaseLedgerEntry' + +/** + * Tests for XLS-0068 Sponsorship Feature Changes. + * + * These tests verify that the TypeScript interfaces are correctly defined + * for the sponsorship-related RPC methods and ledger entries. + */ +describe('Sponsorship', function () { + /** + * Note: xrpl.js uses TypeScript for compile-time type checking and relies on + * server-side validation for runtime checks. The following tests document + * the expected behavior and constraints per XLS-0068 specification. + * + * Server-side validations include: + * - sponsored parameter must be a boolean (Section 15) + * - account must be a valid AccountID (Section 16) + * - Sponsor != Owner (Section 4.3.2) + * - FeeAmount >= 0 (Section 5.7) + * - ReserveCount >= 0 (Section 5.7) + */ + + describe('AccountObjectsRequest with sponsored filter', function () { + it('should accept request with sponsored: true', function () { + const request: AccountObjectsRequest = { + command: 'account_objects', + account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + sponsored: true, + } + + assert.strictEqual(request.command, 'account_objects') + assert.strictEqual(request.sponsored, true) + }) + + it('should accept request with sponsored: false', function () { + const request: AccountObjectsRequest = { + command: 'account_objects', + account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + sponsored: false, + } + + assert.strictEqual(request.command, 'account_objects') + assert.strictEqual(request.sponsored, false) + }) + + it('should accept request without sponsored (optional field)', function () { + const request: AccountObjectsRequest = { + command: 'account_objects', + account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + } + + assert.strictEqual(request.command, 'account_objects') + assert.isUndefined(request.sponsored) + }) + + it('should accept request with all optional fields', function () { + const request: AccountObjectsRequest = { + command: 'account_objects', + account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + type: 'check', + deletion_blockers_only: true, + sponsored: true, + limit: 100, + ledger_index: 'validated', + } + + assert.strictEqual(request.command, 'account_objects') + assert.strictEqual(request.type, 'check') + assert.strictEqual(request.deletion_blockers_only, true) + assert.strictEqual(request.sponsored, true) + assert.strictEqual(request.limit, 100) + assert.strictEqual(request.ledger_index, 'validated') + }) + }) + + describe('AccountSponsoringRequest (Clio RPC method)', function () { + it('should accept basic account_sponsoring request', function () { + const request: AccountSponsoringRequest = { + command: 'account_sponsoring', + account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + } + + assert.strictEqual(request.command, 'account_sponsoring') + assert.strictEqual(request.account, 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9') + }) + + it('should accept request with all optional fields', function () { + const request: AccountSponsoringRequest = { + command: 'account_sponsoring', + account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + type: 'escrow', + deletion_blockers_only: true, + limit: 50, + ledger_index: 'validated', + } + + assert.strictEqual(request.command, 'account_sponsoring') + assert.strictEqual(request.type, 'escrow') + assert.strictEqual(request.deletion_blockers_only, true) + assert.strictEqual(request.limit, 50) + }) + + it('should have correct response structure', function () { + // Create a mock response that matches AccountSponsoringResponse + const response: AccountSponsoringResponse = { + id: 1, + type: 'response', + result: { + account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + sponsored_objects: [], + ledger_hash: + 'C8BFA74A740AA22AD9BD724781589319052398B0C6C817B88D55628E07B7B4A1', + ledger_index: 150, + validated: true, + }, + } + + assert.strictEqual(response.type, 'response') + assert.strictEqual( + response.result.account, + 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + ) + assert.isArray(response.result.sponsored_objects) + assert.strictEqual(response.result.validated, true) + }) + }) + + describe('BaseLedgerEntry Sponsor field', function () { + it('should allow Sponsor field on Check ledger entry', function () { + const check: Check = { + LedgerEntryType: 'Check', + index: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + Account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + Destination: 'rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy', + Flags: 0, + OwnerNode: '0', + PreviousTxnID: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + PreviousTxnLgrSeq: 12345, + SendMax: '1000000', + Sequence: 1, + Sponsor: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe', + } + + assert.strictEqual(check.Sponsor, 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe') + }) + + it('should allow Sponsor field on Escrow ledger entry', function () { + const escrow: Escrow = { + LedgerEntryType: 'Escrow', + index: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + Account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + Destination: 'rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy', + Amount: '1000000', + Flags: 0, + OwnerNode: '0', + PreviousTxnID: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + PreviousTxnLgrSeq: 12345, + Sponsor: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe', + } + + assert.strictEqual(escrow.Sponsor, 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe') + }) + + it('should allow Sponsor field on Offer ledger entry', function () { + const offer: Offer = { + LedgerEntryType: 'Offer', + index: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + Account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + BookDirectory: + 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4C1AA535D3D0C000', + BookNode: '0', + Flags: 0, + OwnerNode: '0', + Sequence: 1, + TakerGets: '1000000', + TakerPays: { + currency: 'USD', + issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', + value: '10', + }, + PreviousTxnID: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + PreviousTxnLgrSeq: 12345, + Sponsor: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe', + } + + assert.strictEqual(offer.Sponsor, 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe') + }) + + it('should allow Sponsor field to be optional on ledger entries', function () { + const escrowWithoutSponsor: Escrow = { + LedgerEntryType: 'Escrow', + index: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + Account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + Destination: 'rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy', + Amount: '1000000', + Flags: 0, + OwnerNode: '0', + PreviousTxnID: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + PreviousTxnLgrSeq: 12345, + } + + assert.isUndefined(escrowWithoutSponsor.Sponsor) + }) + + it('should preserve AccountRoot Sponsor field with specific documentation', function () { + const accountRoot: AccountRoot = { + LedgerEntryType: 'AccountRoot', + index: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + Account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + Balance: '1000000000', + Flags: 0, + OwnerCount: 0, + Sequence: 1, + PreviousTxnID: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + PreviousTxnLgrSeq: 12345, + Sponsor: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe', + SponsoredOwnerCount: 5, + SponsoringOwnerCount: 10, + SponsoringAccountCount: 2, + } + + assert.strictEqual( + accountRoot.Sponsor, + 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe', + ) + assert.strictEqual(accountRoot.SponsoredOwnerCount, 5) + assert.strictEqual(accountRoot.SponsoringOwnerCount, 10) + assert.strictEqual(accountRoot.SponsoringAccountCount, 2) + }) + + it('should have Sponsor defined on BaseLedgerEntry interface', function () { + // Verify that BaseLedgerEntry includes optional Sponsor field + const baseLedgerEntry: BaseLedgerEntry = { + index: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + Sponsor: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe', + } + + assert.strictEqual( + baseLedgerEntry.Sponsor, + 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe', + ) + }) + }) + + /** + * Edge cases and boundary conditions. + * Note: These tests document constraints that are enforced server-side. + * TypeScript provides compile-time type checking for structure. + */ + describe('Edge cases and constraints (server-side validated)', function () { + it('should allow empty sponsored_objects array in AccountSponsoringResponse', function () { + const response: AccountSponsoringResponse = { + id: 1, + type: 'response', + result: { + account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + sponsored_objects: [], + ledger_hash: + 'C8BFA74A740AA22AD9BD724781589319052398B0C6C817B88D55628E07B7B4A1', + ledger_index: 150, + validated: true, + }, + } + + assert.isArray(response.result.sponsored_objects) + assert.lengthOf(response.result.sponsored_objects, 0) + }) + + it('should handle AccountObjectsRequest without optional fields', function () { + // Minimum valid request - only required fields + const request: AccountObjectsRequest = { + command: 'account_objects', + account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + } + + assert.strictEqual(request.command, 'account_objects') + assert.isUndefined(request.sponsored) + assert.isUndefined(request.type) + assert.isUndefined(request.limit) + assert.isUndefined(request.deletion_blockers_only) + }) + + it('should handle AccountSponsoringRequest without optional fields', function () { + // Minimum valid request - only required fields + const request: AccountSponsoringRequest = { + command: 'account_sponsoring', + account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + } + + assert.strictEqual(request.command, 'account_sponsoring') + assert.isUndefined(request.type) + assert.isUndefined(request.limit) + assert.isUndefined(request.deletion_blockers_only) + }) + + it('should allow ledger entries without Sponsor field (unsponsored objects)', function () { + const escrow: Escrow = { + LedgerEntryType: 'Escrow', + index: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + Account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + Destination: 'rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy', + Amount: '1000000', + Flags: 0, + OwnerNode: '0', + PreviousTxnID: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + PreviousTxnLgrSeq: 12345, + } + + // Per XLS-0068 Section 4.3.2: "The field must be omitted when there is no sponsor" + assert.isUndefined(escrow.Sponsor) + }) + + it('should allow AccountRoot with sponsorship count fields', function () { + // Per XLS-0068 Section 6.1: AccountRoot has SponsoredOwnerCount, SponsoringOwnerCount, SponsoringAccountCount + const accountRoot: AccountRoot = { + LedgerEntryType: 'AccountRoot', + index: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + Account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + Balance: '1000000000', + Flags: 0, + OwnerCount: 10, + Sequence: 1, + PreviousTxnID: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + PreviousTxnLgrSeq: 12345, + SponsoredOwnerCount: 3, + SponsoringOwnerCount: 5, + SponsoringAccountCount: 1, + } + + // Per XLS-0068 Section 6.3: SponsoredOwnerCount <= OwnerCount + assert.isAtMost( + accountRoot.SponsoredOwnerCount!, + accountRoot.OwnerCount, + 'SponsoredOwnerCount should be <= OwnerCount per XLS-0068 Section 6.3', + ) + }) + + it('should allow AccountRoot without any sponsorship fields (non-sponsored account)', function () { + const accountRoot: AccountRoot = { + LedgerEntryType: 'AccountRoot', + index: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + Account: 'rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9', + Balance: '1000000000', + Flags: 0, + OwnerCount: 0, + Sequence: 1, + PreviousTxnID: + '5463C6E08862A1FAE5EDAC12D70ADB16546A1F674930521295BC082494B62924', + PreviousTxnLgrSeq: 12345, + } + + assert.isUndefined(accountRoot.Sponsor) + assert.isUndefined(accountRoot.SponsoredOwnerCount) + assert.isUndefined(accountRoot.SponsoringOwnerCount) + assert.isUndefined(accountRoot.SponsoringAccountCount) + }) + }) +}) From ef25f630b8765980547f571d89bc0c0a35a7cfd0 Mon Sep 17 00:00:00 2001 From: Ashray Chowdhry Date: Wed, 11 Feb 2026 11:54:46 -0500 Subject: [PATCH 07/11] update history.md --- packages/ripple-binary-codec/HISTORY.md | 5 +++++ packages/xrpl/HISTORY.md | 1 + 2 files changed, 6 insertions(+) diff --git a/packages/ripple-binary-codec/HISTORY.md b/packages/ripple-binary-codec/HISTORY.md index 3d03aebd99..8627d576c5 100644 --- a/packages/ripple-binary-codec/HISTORY.md +++ b/packages/ripple-binary-codec/HISTORY.md @@ -2,6 +2,11 @@ ## Unreleased +### Added +* Support for `Sponsored Fees and Reserves` (XLS-68d): + * Add `SponsorFee` granular permission for fee sponsorship delegation + * Add `SponsorReserve` granular permission for reserve sponsorship delegation + ### Fixed * Fix STNumber serialization logic to work with large mantissa scale [10^18, 10^19-1]. * Error if a decimal is passed into a `UInt`-typed field. diff --git a/packages/xrpl/HISTORY.md b/packages/xrpl/HISTORY.md index 93ec06dca8..85d34774cf 100644 --- a/packages/xrpl/HISTORY.md +++ b/packages/xrpl/HISTORY.md @@ -7,6 +7,7 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr ### Added * Add `faucetProtocol` (http or https) option to `fundWallet` method. Makes `fundWallet` work with locally running faucet servers. * Add `signLoanSetByCounterparty` and `combineLoanSetCounterpartySigners` helper functions to sign and combine LoanSet transactions signed by the counterparty. +* Support for `Sponsored Fees and Reserves` (XLS-68d) ## 4.5.0 (2025-12-16) From 032902bb6825207fdb1f2741f627a51a96badce4 Mon Sep 17 00:00:00 2001 From: Ashray Chowdhry Date: Wed, 11 Feb 2026 11:59:28 -0500 Subject: [PATCH 08/11] lint fix --- .../ripple-binary-codec/test/granularPermissions.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/ripple-binary-codec/test/granularPermissions.test.ts b/packages/ripple-binary-codec/test/granularPermissions.test.ts index 9fcf8b2451..c3aad8260c 100644 --- a/packages/ripple-binary-codec/test/granularPermissions.test.ts +++ b/packages/ripple-binary-codec/test/granularPermissions.test.ts @@ -131,9 +131,10 @@ describe('Granular Permissions', function () { const definitions = new XrplDefinitions(normalDefinitionsJson) // Accessing an undefined permission should return undefined - // eslint-disable-next-line @typescript-eslint/no-explicit-any expect( - (definitions.granularPermissions as any).NonExistentPermission, + (definitions.granularPermissions as Record)[ + 'NonExistentPermission' + ], ).toBeUndefined() }) }) From 2ffef8c73ff9537951f7d66055cb97343474ed91 Mon Sep 17 00:00:00 2001 From: Ashray Chowdhry Date: Wed, 11 Feb 2026 12:04:33 -0500 Subject: [PATCH 09/11] lint --- packages/ripple-binary-codec/test/granularPermissions.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/ripple-binary-codec/test/granularPermissions.test.ts b/packages/ripple-binary-codec/test/granularPermissions.test.ts index c3aad8260c..67f00b571b 100644 --- a/packages/ripple-binary-codec/test/granularPermissions.test.ts +++ b/packages/ripple-binary-codec/test/granularPermissions.test.ts @@ -132,9 +132,7 @@ describe('Granular Permissions', function () { // Accessing an undefined permission should return undefined expect( - (definitions.granularPermissions as Record)[ - 'NonExistentPermission' - ], + definitions.granularPermissions['NonExistentPermission'], ).toBeUndefined() }) }) From 50621a7c86efcda684b551d9003c9e5f193fc6e9 Mon Sep 17 00:00:00 2001 From: Ashray Chowdhry Date: Wed, 11 Feb 2026 12:24:15 -0500 Subject: [PATCH 10/11] lint --- packages/xrpl/src/models/methods/index.ts | 11 +++++------ packages/xrpl/src/models/transactions/common.ts | 9 ++++++--- .../xrpl/src/models/transactions/sponsorshipSet.ts | 7 ++----- .../src/models/transactions/sponsorshipTransfer.ts | 5 +---- packages/xrpl/test/models/sponsorship.test.ts | 10 +++++----- 5 files changed, 19 insertions(+), 23 deletions(-) diff --git a/packages/xrpl/src/models/methods/index.ts b/packages/xrpl/src/models/methods/index.ts index 153fa8b16a..6433ec9beb 100644 --- a/packages/xrpl/src/models/methods/index.ts +++ b/packages/xrpl/src/models/methods/index.ts @@ -1,6 +1,5 @@ /* eslint-disable no-inline-comments -- Necessary for important note */ /* eslint-disable max-lines -- There is a lot to export */ -/* eslint-disable prettier/prettier -- Required here to keep formatting in line */ import type { APIVersion, DEFAULT_API_VERSION } from '../common' import { @@ -37,17 +36,17 @@ import { AccountObjectsResponse, AccountObjectType, } from './accountObjects' +import { + AccountOffer, + AccountOffersRequest, + AccountOffersResponse, +} from './accountOffers' import { AccountSponsoringObjectType, AccountSponsoringRequest, AccountSponsoringResponse, SponsoredObject, } from './accountSponsoring' -import { - AccountOffer, - AccountOffersRequest, - AccountOffersResponse, -} from './accountOffers' import { AccountTxRequest, AccountTxResponse, diff --git a/packages/xrpl/src/models/transactions/common.ts b/packages/xrpl/src/models/transactions/common.ts index 04f27b8eb3..37d3d5d830 100644 --- a/packages/xrpl/src/models/transactions/common.ts +++ b/packages/xrpl/src/models/transactions/common.ts @@ -721,9 +721,12 @@ export function validateBaseTransaction( // Validate SponsorFlags has valid values if (hasSponsorFlags) { - const validFlags = SponsorFlags.tfSponsorFee | SponsorFlags.tfSponsorReserve - const flags = common.SponsorFlags as number - if ((flags & ~validFlags) !== 0 || flags === 0) { + const flags = Number(common.SponsorFlags) + const isValidFlag = + flags === SponsorFlags.tfSponsorFee || + flags === SponsorFlags.tfSponsorReserve || + flags === SponsorFlags.tfSponsorFee + SponsorFlags.tfSponsorReserve + if (!isValidFlag) { throw new ValidationError( 'BaseTransaction: invalid SponsorFlags - must include tfSponsorFee and/or tfSponsorReserve', ) diff --git a/packages/xrpl/src/models/transactions/sponsorshipSet.ts b/packages/xrpl/src/models/transactions/sponsorshipSet.ts index f489b70ed9..3c37ae0621 100644 --- a/packages/xrpl/src/models/transactions/sponsorshipSet.ts +++ b/packages/xrpl/src/models/transactions/sponsorshipSet.ts @@ -148,12 +148,10 @@ export function validateSponsorshipSet(tx: Record): void { validateOptionalField(tx, 'ReserveCount', isNumber) // Validate flag combinations with tfDeleteObject - const flags = - typeof tx.Flags === 'number' ? tx.Flags : 0 - + const flags = typeof tx.Flags === 'number' ? tx.Flags : 0 const tfDeleteObject = SponsorshipSetFlags.tfDeleteObject - if ((flags & tfDeleteObject) !== 0) { + if (flags === tfDeleteObject) { // When deleting, cannot specify FeeAmount, MaxFee, or ReserveCount if (tx.FeeAmount !== undefined) { throw new ValidationError( @@ -172,4 +170,3 @@ export function validateSponsorshipSet(tx: Record): void { } } } - diff --git a/packages/xrpl/src/models/transactions/sponsorshipTransfer.ts b/packages/xrpl/src/models/transactions/sponsorshipTransfer.ts index 7d950b78f0..760c9e8760 100644 --- a/packages/xrpl/src/models/transactions/sponsorshipTransfer.ts +++ b/packages/xrpl/src/models/transactions/sponsorshipTransfer.ts @@ -57,13 +57,10 @@ export interface SponsorshipTransfer extends BaseTransaction { * @param tx - A SponsorshipTransfer Transaction. * @throws When the SponsorshipTransfer is malformed. */ -export function validateSponsorshipTransfer( - tx: Record, -): void { +export function validateSponsorshipTransfer(tx: Record): void { validateBaseTransaction(tx) validateOptionalField(tx, 'ObjectID', isString) validateOptionalField(tx, 'Sponsor', isAccount) validateOptionalField(tx, 'SponsorFlags', isNumber) } - diff --git a/packages/xrpl/test/models/sponsorship.test.ts b/packages/xrpl/test/models/sponsorship.test.ts index 65d80e2639..171b0640e7 100644 --- a/packages/xrpl/test/models/sponsorship.test.ts +++ b/packages/xrpl/test/models/sponsorship.test.ts @@ -1,15 +1,15 @@ import { assert } from 'chai' +import AccountRoot from '../../src/models/ledger/AccountRoot' +import { BaseLedgerEntry } from '../../src/models/ledger/BaseLedgerEntry' +import Check from '../../src/models/ledger/Check' +import Escrow from '../../src/models/ledger/Escrow' +import Offer from '../../src/models/ledger/Offer' import { AccountObjectsRequest, AccountSponsoringRequest, AccountSponsoringResponse, } from '../../src/models/methods' -import Check from '../../src/models/ledger/Check' -import Escrow from '../../src/models/ledger/Escrow' -import Offer from '../../src/models/ledger/Offer' -import AccountRoot from '../../src/models/ledger/AccountRoot' -import { BaseLedgerEntry } from '../../src/models/ledger/BaseLedgerEntry' /** * Tests for XLS-0068 Sponsorship Feature Changes. From 469702c73aa09ba32997f3b719f2082e9ffab9c1 Mon Sep 17 00:00:00 2001 From: Ashray Chowdhry Date: Wed, 11 Feb 2026 12:29:40 -0500 Subject: [PATCH 11/11] lint --- packages/xrpl/src/models/ledger/Sponsorship.ts | 1 - packages/xrpl/src/models/methods/accountSponsoring.ts | 1 - packages/xrpl/src/models/methods/index.ts | 6 ++++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/xrpl/src/models/ledger/Sponsorship.ts b/packages/xrpl/src/models/ledger/Sponsorship.ts index 8d968a4566..d4960937d5 100644 --- a/packages/xrpl/src/models/ledger/Sponsorship.ts +++ b/packages/xrpl/src/models/ledger/Sponsorship.ts @@ -78,4 +78,3 @@ export default interface Sponsorship extends BaseLedgerEntry, HasPreviousTxnID { */ SponseeNode: string } - diff --git a/packages/xrpl/src/models/methods/accountSponsoring.ts b/packages/xrpl/src/models/methods/accountSponsoring.ts index d70bf8614f..556b4af9a3 100644 --- a/packages/xrpl/src/models/methods/accountSponsoring.ts +++ b/packages/xrpl/src/models/methods/accountSponsoring.ts @@ -95,4 +95,3 @@ export interface AccountSponsoringResponse extends BaseResponse { validated?: boolean } } - diff --git a/packages/xrpl/src/models/methods/index.ts b/packages/xrpl/src/models/methods/index.ts index 6433ec9beb..65968b32f2 100644 --- a/packages/xrpl/src/models/methods/index.ts +++ b/packages/xrpl/src/models/methods/index.ts @@ -1,5 +1,6 @@ /* eslint-disable no-inline-comments -- Necessary for important note */ /* eslint-disable max-lines -- There is a lot to export */ +/* eslint-disable max-len -- Deeply nested ternary types require long lines */ import type { APIVersion, DEFAULT_API_VERSION } from '../common' import { @@ -383,8 +384,9 @@ export type RequestResponseMap< // } // // The type with the most parameters set should be listed first. In this - // case LedgerRequestExpandedTransactionsBinary has the most parameters (`expand`, `transactions`, and `binary`) - // set, so it is listed first. When TypeScript tries to match the type of + // case LedgerRequestExpandedTransactionsBinary has the most parameters + // (`expand`, `transactions`, and `binary`)set, so it is listed first. + // When TypeScript tries to match the type of // a request to a response, it will try to match the request type to the // response type in the order they are listed. So, if we have a request // with the following parameters: