Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/ripple-binary-codec/HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
155 changes: 155 additions & 0 deletions packages/ripple-binary-codec/src/enums/definitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
{
Expand Down Expand Up @@ -1140,6 +1190,16 @@
"type": "UInt64"
}
],
[
"SponseeNode",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 32,
"type": "UInt64"
}
],
[
"EmailHash",
{
Expand Down Expand Up @@ -1510,6 +1570,16 @@
"type": "Hash256"
}
],
[
"ObjectID",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 39,
"type": "Hash256"
}
],
[
"hash",
{
Expand Down Expand Up @@ -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",
{
Expand Down Expand Up @@ -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",
{
Expand Down Expand Up @@ -2840,6 +2980,16 @@
"type": "STObject"
}
],
[
"SponsorSignature",
{
"isSerialized": true,
"isSigningField": false,
"isVLEncoded": false,
"nth": 38,
"type": "STObject"
}
],
[
"Signers",
{
Expand Down Expand Up @@ -3459,6 +3609,7 @@
"PermissionedDomain": 130,
"RippleState": 114,
"SignerList": 83,
"Sponsorship": 144,
"Ticket": 84,
"Vault": 132,
"XChainOwnedClaimID": 113,
Expand Down Expand Up @@ -3515,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,
Expand Down Expand Up @@ -3652,6 +3804,7 @@
"terNO_DELEGATE_PERMISSION": -85,
"terNO_LINE": -94,
"terNO_RIPPLE": -90,
"terNO_SPONSORSHIP": -84,
"terOWNERS": -93,
"terPRE_SEQ": -92,
"terPRE_TICKET": -88,
Expand Down Expand Up @@ -3720,6 +3873,8 @@
"SetFee": 101,
"SetRegularKey": 5,
"SignerListSet": 12,
"SponsorshipSet": 86,
"SponsorshipTransfer": 85,
Comment on lines +3876 to +3877
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, check if the file exists and show the relevant section
echo "=== Reading definitions.json for Sponsorship transaction types ==="
cat packages/ripple-binary-codec/src/enums/definitions.json | python3 -c "
import json, sys
try:
    data = json.load(sys.stdin)
    tx_types = data.get('TRANSACTION_TYPES', {})
    
    # Check for SponsorshipSet and SponsorshipTransfer
    print('Current values in definitions.json:')
    print(f'  SponsorshipSet = {tx_types.get(\"SponsorshipSet\", \"NOT FOUND\")}')
    print(f'  SponsorshipTransfer = {tx_types.get(\"SponsorshipTransfer\", \"NOT FOUND\")}')
    
    # Check for any duplicates
    print('\nChecking for duplicate type codes:')
    values_map = {}
    for k, v in tx_types.items():
        if v in values_map:
            print(f'  CONFLICT: {k} ({v}) and {values_map[v]} ({v}) share same code!')
        else:
            values_map[v] = k
    
    # Look for nearby type codes
    print('\nNearby transaction types (codes 80-90):')
    for k, v in sorted(tx_types.items(), key=lambda x: x[1]):
        if 80 <= v <= 90:
            print(f'  {k} = {v}')
except json.JSONDecodeError as e:
    print(f'JSON parsing error: {e}')
"

Repository: XRPLF/xrpl.js

Length of output: 400


🌐 Web query:

rippled transaction type codes SponsorshipSet SponsorshipTransfer

💡 Result:

In XRPL’s rippled (mainnet/mainline) there are no TransactionType entries named SponsorshipSet or SponsorshipTransfer, so there are no corresponding UInt16 “transaction type codes” for them in the canonical TxType enum / transaction-type identifiers list. The published TxType identifiers cover types like ttPAYMENT, ttACCOUNT_SET, ttTRUST_SET, etc., but not those sponsorship names. [1]

If you’re trying to get the numeric transaction type code (UInt16) for any TransactionType name on a given network/build, the authoritative way is to query the server’s definitions and read the TRANSACTION_TYPES map (string → UInt16). [2]

  • RPC method: server_definitions
  • Then check: result.TRANSACTION_TYPES.SponsorshipSet and result.TRANSACTION_TYPES.SponsorshipTransfer
  • If those keys are missing, that server/network does not support those transaction types. [2]

Related note: in proposed “sponsorship” designs for XRPL, sponsorship is described as behavior attached to an existing Payment via a flag (e.g., tfSponsor), not as new SponsorshipSet/SponsorshipTransfer transaction types. [3]

Sources
[1] rippled TxType (transaction type identifiers / enum list) https://thejohnfreeman.github.io/rippled/namespaceripple.html
[2] XRPL Binary Format docs (definitions file + TRANSACTION_TYPES + server_definitions) https://xrpl.org/docs/references/protocol/binary-format
[3] XRPL Standards discussion (sponsorship via ttPAYMENT + tfSponsor) XRPLF/XRPL-Standards#56


Remove SponsorshipSet and SponsorshipTransfer from TRANSACTION_TYPES—they do not exist in rippled.

These transaction types are not defined in rippled's TxType enum. Sponsorship in XRPL is implemented via a flag (tfSponsor) on Payment transactions, not as separate transaction types. Including codes 85 and 86 for non-existent types creates incorrect serialization mapping and will cause deserialization failures against actual rippled nodes.

🤖 Prompt for AI Agents
In `@packages/ripple-binary-codec/src/enums/definitions.json` around lines 3876 -
3877, The TRANSACTION_TYPES mapping includes two invalid entries
"SponsorshipSet" and "SponsorshipTransfer" with codes 86 and 85; remove these
keys from the TRANSACTION_TYPES object in definitions.json to avoid creating
invalid serialization IDs, and search for any usages of the symbols
SponsorshipSet or SponsorshipTransfer elsewhere (e.g., any lookup code that
expects those enum keys) and update or remove those references so
deserialization/serialization only uses real rippled TxType names; ensure the
JSON remains valid after deletion and run unit/serialization tests to confirm no
remaining dependencies.

"TicketCreate": 10,
"TrustSet": 20,
"UNLModify": 102,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class XrplDefinitionsBase {
PaymentBurn: 65546,
MPTokenIssuanceLock: 65547,
MPTokenIssuanceUnlock: 65548,
SponsorFee: 65549,
SponsorReserve: 65550,
}

const incrementedTransactionTypes = Object.fromEntries(
Expand Down
139 changes: 139 additions & 0 deletions packages/ripple-binary-codec/test/granularPermissions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
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
expect(
definitions.granularPermissions['NonExistentPermission'],
).toBeUndefined()
})
})
})
1 change: 1 addition & 0 deletions packages/xrpl/HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading
Loading