Skip to content

Sponsorship xls 68#3238

Open
cybele-ripple wants to merge 13 commits intomainfrom
sponsorship-XLS-68
Open

Sponsorship xls 68#3238
cybele-ripple wants to merge 13 commits intomainfrom
sponsorship-XLS-68

Conversation

@cybele-ripple
Copy link
Copy Markdown
Collaborator

High Level Overview of Change

Support for XLS-68 (Sponsored Fees and Reserves)

Context of Change

  • New transaction types: SponsorshipSet (create/modify/delete sponsorships) and SponsorshipTransfer (transfer reserve sponsorship between accounts)
  • New ledger entry: Sponsorship to track active sponsorship relationships
  • Sponsor signing utilities: Functions to co-sign transactions as a sponsor (signAsSponsor, addPreFundedSponsor, combineSponsorSigners)
  • Validation helpers: validatePreFundedSponsorship to verify sponsorship coverage before submission
  • Autofill support: X-address conversion and fee calculation for sponsored transactions

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Tests (You added tests for code that already exists, or your new feature included in this PR)
  • Documentation Updates
  • Release

Did you update HISTORY.md?

  • Yes
  • No, this change does not impact library users

Test Plan

Added/updated unit tests, integration tests, and relevant autofill tests

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR introduces comprehensive XLS-68 sponsorship support across the xrpl.js library. Changes include new binary codec field definitions, transaction types (SponsorshipSet, SponsorshipTransfer), ledger entry models (Sponsorship), validation logic, wallet signing utilities, RPC methods, and extensive test coverage for sponsorship workflows.

Changes

Cohort / File(s) Summary
Binary codec definitions
packages/ripple-binary-codec/src/enums/definitions.json
Added 15 new field definitions (sponsor-related account/amount fields), 3 new ledger entry/transaction type enums (Sponsorship: 135, SponsorshipTransfer: 85, SponsorshipSet: 86).
Cryptographic signing
packages/ripple-keypairs/src/signing-schemes/secp256k1/index.ts
Reordered nobleSecp256k1.sign options; moved format: 'der' placement with clarifying comment; signing behavior unchanged.
Wallet exports and signing utilities
packages/xrpl/src/Wallet/index.ts, packages/xrpl/src/Wallet/sponsorSigner.ts
Added 3 sponsor signing helpers: signAsSponsor (single/multi-sign), combineSponsorSigners (merge multi-sig sponsor signatures), addPreFundedSponsor (augment transaction). Includes validation for self-sponsorship, field presence, and multi-signing deduplication.
Wallet utilities
packages/xrpl/src/Wallet/utils.ts
Updated compareSigners to return direct comparison result with ?? 0 fallback; removed error-throwing path for NaN comparisons.
Package-level exports
packages/xrpl/src/index.ts, packages/xrpl/src/sugar/index.ts
Added re-exports of ./sugar module and new ./validateSponsorship exports to public API surface.
Common types and base transaction
packages/xrpl/src/models/common/index.ts, packages/xrpl/src/models/transactions/common.ts
Added SponsorSignature type (single-sig or multi-sig shape), SponsorFlags enum, sponsor fields to BaseTransaction, areAddressesEqual() helper for address normalization, and comprehensive sponsor field validation (validateSponsorFields).
Ledger entry models
packages/xrpl/src/models/ledger/AccountRoot.ts, packages/xrpl/src/models/ledger/RippleState.ts, packages/xrpl/src/models/ledger/Sponsorship.ts, packages/xrpl/src/models/ledger/LedgerEntry.ts, packages/xrpl/src/models/ledger/index.ts
Added Sponsorship ledger entry model with SponsorshipFlags enum, updated AccountRoot with 4 sponsor fields, updated RippleState with 2 sponsor fields, integrated into LedgerEntry union and filter types.
Sponsorship transaction models
packages/xrpl/src/models/transactions/sponsorshipSet.ts, packages/xrpl/src/models/transactions/sponsorshipTransfer.ts
Added two transaction types with flags enums, boolean flag interfaces, and runtime validators enforcing scenario-specific field presence (e.g., Sponsee or CounterpartySponsor for SponsorshipSet; scenario flag mutual exclusivity for SponsorshipTransfer).
Transaction type routing
packages/xrpl/src/models/transactions/transaction.ts, packages/xrpl/src/models/transactions/index.ts, packages/xrpl/src/models/transactions/payment.ts
Extended SubmittableTransaction union and added dispatch cases for SponsorshipSet and SponsorshipTransfer validation. Added PaymentFlags.tfSponsorCreatedAccount flag and re-exported new sponsorship types/flags.
RPC method models
packages/xrpl/src/models/methods/accountObjects.ts, packages/xrpl/src/models/methods/accountSponsoring.ts, packages/xrpl/src/models/methods/ledger.ts, packages/xrpl/src/models/methods/ledgerEntry.ts, packages/xrpl/src/models/methods/index.ts
Added sponsored query filter for account_objects, introduced new account_sponsoring method (request/response models), extended ledger_entry to query sponsorship by sponsor/sponsee or hex ID, updated method unions and conditional type mappings.
Utility functions
packages/xrpl/src/models/utils/flags.ts, packages/xrpl/src/models/transactions/NFTokenCreateOffer.ts, packages/xrpl/src/models/transactions/depositPreauth.ts
Added flag mappings for new transaction types. Updated address equality checks in NFToken and DepositPreauth validators to use areAddressesEqual() for X-address normalization.
High-level sugar functions
packages/xrpl/src/sugar/autofill.ts, packages/xrpl/src/sugar/getOrderbook.ts, packages/xrpl/src/sugar/validateSponsorship.ts
Extended setValidAddresses() to convert sponsor fields; added calculateSponsorFee() for sponsor signature fee computation; integrated into fee calculation. Added validatePreFundedSponsorship() helper for pre-submission sponsorship validation. Fixed comparator null handling in sortOffers.
Test suites
packages/xrpl/test/models/sponsorshipSet.test.ts, packages/xrpl/test/models/sponsorshipTransfer.test.ts, packages/xrpl/test/wallet/sponsorSigner.test.ts, packages/xrpl/test/sugar/validateSponsorship.test.ts, packages/xrpl/test/integration/transactions/sponsorship.test.ts, packages/xrpl/test/client/autofill.test.ts
Added comprehensive unit and integration tests covering transaction validation (flag scenarios, field constraints, address normalization), wallet signing workflows (single/multi-signer, combination), pre-funded sponsorship validation, and end-to-end sponsorship flows with fee/reserve enforcement and ledger state verification.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • ckeshava
  • achowdhry-ripple
  • Patel-Raj11
  • mvadari

Poem

🐰 A rabbit hops through sponsorship's dance,
New signers merge, new fields advance,
Ledgers bloom with sponsor care,
Fees and reserves now fairly shared!
XLS-68 finds its way,

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Sponsorship xls 68' is vague and lacks specificity about the nature of the change. Consider using a more descriptive title such as 'Add XLS-68 sponsorship support with new transaction types and signing utilities' to clarify the scope and intent.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description covers the high-level overview, context, type of change, and test plan, meeting the template requirements.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sponsorship-XLS-68

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/xrpl/src/sugar/getOrderbook.ts (1)

15-22: ⚠️ Potential issue | 🟡 Minor

BigNumber.comparedTo() returns null for non-numeric values without explicit handling.

While lines 17–18 default nullish quality to 0, a non-numeric string (e.g., from a malformed server response) produces NaN in BigNumber, causing comparedTo() to return null. JavaScript coerces null to 0 in Array.sort(), so sorting behavior is functionally unchanged. However, explicitly handling this edge case with ?? 0 makes the code more defensive and clearer about intent. If the API contract guarantees valid numeric strings, consider adding that assumption as a comment; otherwise, restore the fallback or validate quality at the response parsing level.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/xrpl/src/sugar/getOrderbook.ts` around lines 15 - 22, sortOffers
currently constructs BigNumber from possibly non-numeric quality values so
comparedTo can return null; update sortOffers to defensively coerce/validate
qualities before comparing: inside sortOffers (function name) convert
qualityA/qualityB to BigNumber instances, check .isNaN() on each and replace NaN
instances with BigNumber(0), then call comparedTo on the validated BigNumber
instances and return that result; reference the variables qualityA, qualityB and
the function sortOffers when making the change.
🧹 Nitpick comments (3)
packages/xrpl/src/sugar/validateSponsorship.ts (1)

88-110: Consider requiring FeeAmount when validating fee sponsorship.

When tfSponsorFee is set (line 85), the validation checks sponsorship.FeeAmount only if it exists (line 98). However, if FeeAmount is undefined or missing on a sponsorship that's supposed to cover fees, this could silently pass validation and fail at submission time.

Consider adding explicit validation:

💡 Suggested enhancement
     if (!isSponsoringFee) {
       // Only reserve sponsorship, no fee validation needed
       return {
         valid: true,
         sponsorship,
         estimatedFee: fee,
       }
     }

+    // Fee sponsorship requires FeeAmount to be set
+    if (!sponsorship.FeeAmount) {
+      return {
+        valid: false,
+        error: 'Sponsorship does not have FeeAmount set for fee sponsorship',
+        sponsorship,
+        estimatedFee: fee,
+      }
+    }
+
     // Validate FeeAmount has sufficient balance
-    if (sponsorship.FeeAmount) {
       const feeAmount = new BigNumber(sponsorship.FeeAmount)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/xrpl/src/sugar/validateSponsorship.ts` around lines 88 - 110, When
tfSponsorFee/isSponsoringFee is true, ensure FeeAmount is required: update
validateSponsorship (around isSponsoringFee and the sponsorship.FeeAmount check)
to return valid:false with a clear error if sponsorship.FeeAmount is undefined
or null (instead of skipping validation), and keep returning sponsorship and
estimatedFee in that error response; otherwise continue to parse FeeAmount and
compare it to fee as currently implemented.
packages/xrpl/test/integration/transactions/sponsorship.test.ts (1)

776-781: Consider using enum values for combined flags.

Similar to the unit test, the magic number 3 could be replaced with explicit enum combination for better readability.

♻️ Suggested improvement
         Sponsor: sponsorWallet.classicAddress,
-        // SponsorFlags: fee (1) + reserve (2) = 3
-        SponsorFlags: 3,
+        /* eslint-disable no-bitwise -- combining flags */
+        SponsorFlags: SponsorFlags.tfSponsorFee | SponsorFlags.tfSponsorReserve,
+        /* eslint-enable no-bitwise */
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/xrpl/test/integration/transactions/sponsorship.test.ts` around lines
776 - 781, Replace the magic literal 3 assigned to SponsorFlags with the
explicit enum combination of the two sponsorship flag values; update the object
so SponsorFlags is set using the bitwise OR of the enum members (e.g.,
SponsorFlag.FEE | SponsorFlag.RESERVE) instead of the number 3 so the intent is
clear when creating the transaction that includes sponsorWallet.classicAddress.
packages/xrpl/test/wallet/sponsorSigner.test.ts (1)

316-334: Consider using enum values instead of magic number for combined flags.

The combined flags value 3 is correct but less readable than using the enum values explicitly.

♻️ Suggested improvement for readability
       const sponsorAddress = 'rBJMcbqnAaxcUeEPF7WiaoHCtFiTmga7un'
-      // SponsorFlags.tfSponsorFee (1) + SponsorFlags.tfSponsorReserve (2) = 3
-      const sponsorFlags = 3
+      /* eslint-disable no-bitwise -- combining flags */
+      const sponsorFlags =
+        SponsorFlags.tfSponsorFee | SponsorFlags.tfSponsorReserve
+      /* eslint-enable no-bitwise */

       const result = addPreFundedSponsor(payment, sponsorAddress, sponsorFlags)

       assert.equal(result.SponsorFlags, sponsorFlags)
-      assert.equal(result.SponsorFlags, 3)
+      assert.equal(
+        result.SponsorFlags,
+        SponsorFlags.tfSponsorFee | SponsorFlags.tfSponsorReserve,
+      )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/xrpl/test/wallet/sponsorSigner.test.ts` around lines 316 - 334,
Replace the magic number 3 with the explicit enum combination when testing
addPreFundedSponsor: compute sponsorFlags using the SponsorFlags enum (e.g.,
SponsorFlags.tfSponsorFee | SponsorFlags.tfSponsorReserve) and use that variable
in assertions instead of the literal 3; update the test in sponsorSigner.test.ts
to reference the SponsorFlags symbol so it’s clearer which flags are combined
when calling addPreFundedSponsor and asserting result.SponsorFlags.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ripple-keypairs/src/signing-schemes/secp256k1/index.ts`:
- Around line 45-57: The call to nobleSecp256k1.sign(...).toBytes('der') is
invalid because Signature objects use toDERRawBytes() for DER encoding; update
the expression in the signing function to call
nobleSecp256k1.sign(Sha512.half(message), hexToBytes(normedPrivateKey),
{...}).toDERRawBytes(), then pass that result into bytesToHex(...).toUpperCase()
so the function continues returning the same hex DER signature format.

In `@packages/xrpl/src/models/methods/index.ts`:
- Line 211: RequestResponseMap is missing a specific mapping for
AccountSponsoringRequest, so calls to client.request({ command:
'account_sponsoring', ... }) resolve to the generic fallback; update the
RequestResponseMap type (the conditional chain in index.ts) to add a branch
mapping "T extends AccountSponsoringRequest ? AccountSponsoringResponse"
immediately before the final ": Response<Version>" fallback so the compiler
resolves the concrete AccountSponsoringResponse for that request type.

In `@packages/xrpl/src/models/transactions/common.ts`:
- Around line 736-742: The self-sponsorship check using string equality (sponsor
=== tx.Account) can miss equivalence between X-address and classic address
formats; update the check in the function containing hasSponsor, sponsor and
tx.Account to normalize both addresses to their classic form using the existing
xAddressToClassicAddress (or a helper that falls back to the original if
conversion fails) and then compare the resulting classic addresses, catching any
conversion errors and still throwing the same ValidationError ('Transaction:
Sponsor and Account cannot be the same (self-sponsorship not allowed)') when
they match; apply the same normalization approach to other address-equality
spots noted (e.g., NFTokenCreateOffer, depositPreauth, sponsorshipTransfer) to
avoid cross-format false negatives.

In `@packages/xrpl/src/models/transactions/sponsorshipSet.ts`:
- Around line 181-207: The FeeAmount and MaxFee validation in the SponsorshipSet
transaction currently uses Number() coercion which accepts non-canonical drop
strings; update the validation in the SponsorshipSet validator to reject
anything that is not a canonical unsigned integer string (no whitespace, no
scientific notation, no decimals, not "Infinity" or empty) by using the existing
isXRPLNumber helper or the same drops / unsigned integer regex used elsewhere in
the codebase instead of Number(), and keep the same error messages thrown from
the SponsorshipSet validation for tx.FeeAmount and tx.MaxFee when the new strict
check fails.

In `@packages/xrpl/src/sugar/autofill.ts`:
- Around line 322-373: The code currently treats any transaction with tx.Sponsor
!= null as requiring sponsor-signature fees; change calculateSponsorFee to only
add sponsor signer fees when a SponsorSignature is present (tx.SponsorSignature
!= null) or when an explicit opt-in is provided (e.g., a new flag/field such as
tx.EstimateSponsorSigners or a function parameter), and remove the automatic fee
scaling for the plain tx.Sponsor case; update any other places that mirror this
behavior (the similar branch referenced around the later lines) to use the same
explicit check so pre-funded Sponsor/SponsorFlags flows without SponsorSignature
are not overcharged.

In `@packages/xrpl/src/Wallet/sponsorSigner.ts`:
- Around line 218-229: getTransactionWithAllSponsorSigners currently
concatenates and sorts all SponsorSignature.Signers but doesn't remove
duplicates, so identical Signer.Account entries can appear twice; change the
function to deduplicate the sortedSigners list by Signer.Account (e.g., iterate
sortedSigners and keep a single Signer per unique Signer.Account, preserving the
ordering established by compareSigners) before returning the combined
Transaction with SponsorSignature.Signers; ensure you still call
compareSigners(signer.Signer, ...) to build the initial sort and then collapse
duplicates (keeping either the first occurrence) so the returned blob won’t
contain duplicate signer entries.
- Around line 91-124: Update the self-sponsorship guard and validation order in
sponsorSigner: check self-sponsorship using tx.Sponsor (i.e., throw if
tx.Account === tx.Sponsor) instead of comparing to wallet.classicAddress, then
perform validate(...) after you attach SponsorSignature so the final signed
payload is validated; ensure both the multisign branch (where you set
tx.SponsorSignature with Signers/TxnSignature via computeSignature(tx,
wallet.privateKey, multisignAddress)) and the single-signer branch (Setting
SigningPubKey/TxnSignature via computeSignature(tx, wallet.privateKey)) are
followed by a call to validate(tx) before calling encode(tx).
- Around line 272-291: The sponsorFlags validation currently uses typeof
sponsorFlags !== 'number', which permits NaN/Infinity/floats; change the check
to use Number.isInteger(sponsorFlags) (e.g., if
(!Number.isInteger(sponsorFlags)) throw ValidationError) inside the
addPreFundedSponsor validation block that references sponsorFlags and
SponsorFlags.tfSponsorFee|tfSponsorReserve so non-integer and non-number values
are rejected before the bitwise flag checks.

In `@packages/xrpl/test/client/autofill.test.ts`:
- Around line 669-769: Rename and split the existing sponsorship tests to
separate "sponsor-signed" from "pre-funded" scenarios: ensure the sponsor-signed
tests supply a SponsorSignature (or signer list for multisig sponsor) in the tx
object so autofill() will detect sponsor signatures and add the correct extra
signature fees (e.g., modify the single-sponsor test to include SponsorSignature
and the multisig test to mock signer_lists for the Sponsor account), and add a
new pre-funded test that only sets Sponsor (no SponsorSignature and no
signer_lists) and asserts the base fee (12) remains unchanged; update test names
(e.g., tests around Payment tx, txResult from client.autofill) and expectations
accordingly so fees reflect the correct path.

---

Outside diff comments:
In `@packages/xrpl/src/sugar/getOrderbook.ts`:
- Around line 15-22: sortOffers currently constructs BigNumber from possibly
non-numeric quality values so comparedTo can return null; update sortOffers to
defensively coerce/validate qualities before comparing: inside sortOffers
(function name) convert qualityA/qualityB to BigNumber instances, check .isNaN()
on each and replace NaN instances with BigNumber(0), then call comparedTo on the
validated BigNumber instances and return that result; reference the variables
qualityA, qualityB and the function sortOffers when making the change.

---

Nitpick comments:
In `@packages/xrpl/src/sugar/validateSponsorship.ts`:
- Around line 88-110: When tfSponsorFee/isSponsoringFee is true, ensure
FeeAmount is required: update validateSponsorship (around isSponsoringFee and
the sponsorship.FeeAmount check) to return valid:false with a clear error if
sponsorship.FeeAmount is undefined or null (instead of skipping validation), and
keep returning sponsorship and estimatedFee in that error response; otherwise
continue to parse FeeAmount and compare it to fee as currently implemented.

In `@packages/xrpl/test/integration/transactions/sponsorship.test.ts`:
- Around line 776-781: Replace the magic literal 3 assigned to SponsorFlags with
the explicit enum combination of the two sponsorship flag values; update the
object so SponsorFlags is set using the bitwise OR of the enum members (e.g.,
SponsorFlag.FEE | SponsorFlag.RESERVE) instead of the number 3 so the intent is
clear when creating the transaction that includes sponsorWallet.classicAddress.

In `@packages/xrpl/test/wallet/sponsorSigner.test.ts`:
- Around line 316-334: Replace the magic number 3 with the explicit enum
combination when testing addPreFundedSponsor: compute sponsorFlags using the
SponsorFlags enum (e.g., SponsorFlags.tfSponsorFee |
SponsorFlags.tfSponsorReserve) and use that variable in assertions instead of
the literal 3; update the test in sponsorSigner.test.ts to reference the
SponsorFlags symbol so it’s clearer which flags are combined when calling
addPreFundedSponsor and asserting result.SponsorFlags.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 20082e83-297d-4ea2-a1ae-f2f99a55ff62

📥 Commits

Reviewing files that changed from the base of the PR and between e0fd402 and 22a0ecf.

📒 Files selected for processing (36)
  • packages/ripple-binary-codec/src/enums/definitions.json
  • packages/ripple-keypairs/src/signing-schemes/secp256k1/index.ts
  • packages/xrpl/src/Wallet/index.ts
  • packages/xrpl/src/Wallet/sponsorSigner.ts
  • packages/xrpl/src/Wallet/utils.ts
  • packages/xrpl/src/index.ts
  • packages/xrpl/src/models/common/index.ts
  • packages/xrpl/src/models/ledger/AccountRoot.ts
  • packages/xrpl/src/models/ledger/LedgerEntry.ts
  • packages/xrpl/src/models/ledger/RippleState.ts
  • packages/xrpl/src/models/ledger/Sponsorship.ts
  • packages/xrpl/src/models/ledger/index.ts
  • packages/xrpl/src/models/methods/accountObjects.ts
  • packages/xrpl/src/models/methods/accountSponsoring.ts
  • packages/xrpl/src/models/methods/index.ts
  • packages/xrpl/src/models/methods/ledger.ts
  • packages/xrpl/src/models/methods/ledgerEntry.ts
  • packages/xrpl/src/models/methods/simulate.ts
  • packages/xrpl/src/models/methods/tx.ts
  • packages/xrpl/src/models/transactions/common.ts
  • packages/xrpl/src/models/transactions/index.ts
  • packages/xrpl/src/models/transactions/payment.ts
  • packages/xrpl/src/models/transactions/sponsorshipSet.ts
  • packages/xrpl/src/models/transactions/sponsorshipTransfer.ts
  • packages/xrpl/src/models/transactions/transaction.ts
  • packages/xrpl/src/models/utils/flags.ts
  • packages/xrpl/src/sugar/autofill.ts
  • packages/xrpl/src/sugar/getOrderbook.ts
  • packages/xrpl/src/sugar/index.ts
  • packages/xrpl/src/sugar/validateSponsorship.ts
  • packages/xrpl/test/client/autofill.test.ts
  • packages/xrpl/test/integration/transactions/sponsorship.test.ts
  • packages/xrpl/test/models/sponsorshipSet.test.ts
  • packages/xrpl/test/models/sponsorshipTransfer.test.ts
  • packages/xrpl/test/sugar/validateSponsorship.test.ts
  • packages/xrpl/test/wallet/sponsorSigner.test.ts

Comment on lines 45 to 57
return bytesToHex(
nobleSecp256k1.sign(Sha512.half(message), hexToBytes(normedPrivateKey), {
// "Canonical" signatures
lowS: true,
// Would fail tests if signatures aren't deterministic
extraEntropy: undefined,
format: 'der',
// We pass a pre-hashed message (Sha512Half), so disable secp256k1's
// default SHA-256 prehashing (added as default in @noble/curves 2.0.0)
prehash: false,
}),
nobleSecp256k1
.sign(Sha512.half(message), hexToBytes(normedPrivateKey), {
// "Canonical" signatures
lowS: true,
// Would fail tests if signatures aren't deterministic
extraEntropy: undefined,
// We pass a pre-hashed message (Sha512Half), so disable secp256k1's
// default SHA-256 prehashing (added as default in @noble/curves 2.0.0)
prehash: false,
})
.toBytes('der'),
).toUpperCase()
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Mar 30, 2026

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

🌐 Web query:

What is the return type of secp256k1.sign() in @noble/curves when options are passed but no format is specified? Does the Signature object have toBytes or toDERRawBytes method?

💡 Result:

The return type of secp256k1.sign in @noble/curves, when options are passed but no format is specified, is a Signature object. This is evidenced by documentation examples where secp256k1.sign(msg, secretKey) without format returns a Signature instance, as shown in code snippets like const sigInstance = secp256k1.Signature.fromBytes(sigRec, 'recovered'); and historical notes that sign returns Signature with { r, s, recovery }. The default format when unspecified is 'compact' (64-byte RS), but the primary return is the Signature class instance. The Signature object has toCompactRawBytes and toDERRawBytes methods (or equivalent toBytes('compact')/'der' in some versions), as explicitly stated in interface definitions: interface ECDSASignature { r: bigint; s: bigint; recovery?: number; toCompactRawBytes: Uint8Array; toDERRawBytes: Uint8Array; }. Older versions used toBytes, but raw bytes methods are confirmed. It does not have a plain toBytes method without arguments based on available source snippets, but the queried methods exist.

Citations:


Build failure: toBytes is not the correct method for DER encoding.

In @noble/curves, secp256k1.sign() returns a Signature object. To export it as DER-encoded bytes, use .toDERRawBytes() instead of .toBytes('der'), which does not exist on the Signature class.

Fix
       return bytesToHex(
         nobleSecp256k1
           .sign(Sha512.half(message), hexToBytes(normedPrivateKey), {
             lowS: true,
             extraEntropy: undefined,
             prehash: false,
           })
-          .toBytes('der'),
+          .toDERRawBytes(),
       ).toUpperCase()
🧰 Tools
🪛 GitHub Actions: Node.js CI

[error] 56-56: TypeScript build failed in ripple-keypairs. TS2339: Property 'toBytes' does not exist on type 'Uint8Array'.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ripple-keypairs/src/signing-schemes/secp256k1/index.ts` around lines
45 - 57, The call to nobleSecp256k1.sign(...).toBytes('der') is invalid because
Signature objects use toDERRawBytes() for DER encoding; update the expression in
the signing function to call nobleSecp256k1.sign(Sha512.half(message),
hexToBytes(normedPrivateKey), {...}).toDERRawBytes(), then pass that result into
bytesToHex(...).toUpperCase() so the function continues returning the same hex
DER signature format.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This finding is based on an outdated version of the @noble/curves api. We are using version 2.x which includes a change from toDERRawBytes to toBytes('der').

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread packages/xrpl/src/models/methods/index.ts
Comment thread packages/xrpl/src/models/transactions/common.ts
Comment thread packages/xrpl/src/models/transactions/sponsorshipSet.ts
Comment thread packages/xrpl/src/sugar/autofill.ts Outdated
Comment thread packages/xrpl/src/Wallet/sponsorSigner.ts Outdated
Comment thread packages/xrpl/src/Wallet/sponsorSigner.ts
Comment thread packages/xrpl/src/Wallet/sponsorSigner.ts Outdated
Comment thread packages/xrpl/test/client/autofill.test.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/xrpl/src/models/transactions/depositPreauth.ts (1)

56-72: Consider using isString helper for consistency with NFTokenCreateOffer.

The X-address equality checks are correct. However, NFTokenCreateOffer imports and uses the isString helper, while this file uses inline typeof ... === 'string'. Consider importing isString from ./common for consistency across transaction validators.

♻️ Suggested refactor for consistency
 import {
   BaseTransaction,
   validateBaseTransaction,
   validateCredentialsList,
   MAX_AUTHORIZED_CREDENTIALS,
   areAddressesEqual,
+  isString,
 } from './common'

Then update the checks:

     if (
-      typeof tx.Account === 'string' &&
+      isString(tx.Account) &&
       areAddressesEqual(tx.Account, tx.Authorize)
     ) {
     if (
-      typeof tx.Account === 'string' &&
+      isString(tx.Account) &&
       areAddressesEqual(tx.Account, tx.Unauthorize)
     ) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/xrpl/src/models/transactions/depositPreauth.ts` around lines 56 -
72, Replace inline typeof string checks in the DepositPreauth validator with the
shared isString helper for consistency: import isString from ./common at the
top, then use isString(tx.Account), isString(tx.Unauthorize) (and any other
typeof ... === 'string' checks in this file) instead of typeof ... === 'string';
keep the existing areAddressesEqual logic and ValidationError messages
unchanged. This mirrors how NFTokenCreateOffer uses isString and ensures
consistent validation helpers across validators.
packages/xrpl/src/sugar/autofill.ts (1)

331-337: Consider using || 1 for defensive handling of edge cases.

Using the nullish coalescing operator (?? 1) won't catch an empty Signers array because 0 is not nullish. If Signers is [], sponsorSignersCount would be 0, leading to no additional fee. While the protocol likely rejects transactions with empty Signers arrays, using || 1 would be more defensive:

-    const sponsorSignersCount = tx.SponsorSignature.Signers?.length ?? 1
+    const sponsorSignersCount = tx.SponsorSignature.Signers?.length || 1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/xrpl/src/sugar/autofill.ts` around lines 331 - 337, The code
currently sets sponsorSignersCount with "tx.SponsorSignature.Signers?.length ??
1", which treats an empty Signers array as 0; change this defensive handling to
use logical OR so an empty array yields at least 1 signer: compute
sponsorSignersCount from tx.SponsorSignature.Signers?.length || 1 (referencing
the SponsorSignature.Signers access and the sponsorSignersCount variable) and
keep returning new BigNumber(scaleValue(netFeeDrops, sponsorSignersCount)) so
fee calculation still uses scaleValue and netFeeDrops.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/xrpl/src/models/transactions/depositPreauth.ts`:
- Around line 56-72: Replace inline typeof string checks in the DepositPreauth
validator with the shared isString helper for consistency: import isString from
./common at the top, then use isString(tx.Account), isString(tx.Unauthorize)
(and any other typeof ... === 'string' checks in this file) instead of typeof
... === 'string'; keep the existing areAddressesEqual logic and ValidationError
messages unchanged. This mirrors how NFTokenCreateOffer uses isString and
ensures consistent validation helpers across validators.

In `@packages/xrpl/src/sugar/autofill.ts`:
- Around line 331-337: The code currently sets sponsorSignersCount with
"tx.SponsorSignature.Signers?.length ?? 1", which treats an empty Signers array
as 0; change this defensive handling to use logical OR so an empty array yields
at least 1 signer: compute sponsorSignersCount from
tx.SponsorSignature.Signers?.length || 1 (referencing the
SponsorSignature.Signers access and the sponsorSignersCount variable) and keep
returning new BigNumber(scaleValue(netFeeDrops, sponsorSignersCount)) so fee
calculation still uses scaleValue and netFeeDrops.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 227becb1-42ac-495b-9b1e-3bfce350d6f7

📥 Commits

Reviewing files that changed from the base of the PR and between 22a0ecf and 5dbd855.

📒 Files selected for processing (9)
  • packages/xrpl/src/Wallet/sponsorSigner.ts
  • packages/xrpl/src/models/methods/index.ts
  • packages/xrpl/src/models/transactions/NFTokenCreateOffer.ts
  • packages/xrpl/src/models/transactions/common.ts
  • packages/xrpl/src/models/transactions/depositPreauth.ts
  • packages/xrpl/src/models/transactions/sponsorshipSet.ts
  • packages/xrpl/src/models/transactions/sponsorshipTransfer.ts
  • packages/xrpl/src/sugar/autofill.ts
  • packages/xrpl/test/client/autofill.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/xrpl/src/models/methods/index.ts
  • packages/xrpl/test/client/autofill.test.ts
  • packages/xrpl/src/models/transactions/sponsorshipTransfer.ts
  • packages/xrpl/src/models/transactions/sponsorshipSet.ts
  • packages/xrpl/src/models/transactions/common.ts
  • packages/xrpl/src/Wallet/sponsorSigner.ts

… xrpl4j

- Add optional Sponsor field to Check, DepositPreauth, Escrow, NFTokenOffer,
  Offer, PayChannel, and Ticket ledger entry types so consumers can read the
  sponsor address from returned ledger objects.
- Add deletion_blockers_only and type params to AccountSponsoringRequest to
  match the account_sponsoring RPC shape supported by xrpl4j.
…onsor rippled

- Sponsorship LedgerEntryType was 135; rippled's XLS-68 branch defines
  ltSPONSORSHIP = 0x0090 = 144. Update definitions.json so parsed Sponsorship
  ledger entries decode correctly.
- Switch CI rippled image from rippleci/rippled:develop (no XLS-68) to
  legleux/xrpld:sponsor so integration tests can actually exercise
  SponsorshipSet / SponsorshipTransfer instead of being rejected with
  "Unknown field". Matches the image xrpl4j uses.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant