Skip to content

XLS-68d Sponsored Fees and Reserves#921

Open
ckeshava wants to merge 15 commits intoXRPLF:mainfrom
ckeshava:sponsoredFeesDraft1
Open

XLS-68d Sponsored Fees and Reserves#921
ckeshava wants to merge 15 commits intoXRPLF:mainfrom
ckeshava:sponsoredFeesDraft1

Conversation

@ckeshava
Copy link
Copy Markdown
Collaborator

High Level Overview of Change

This PR provides support for SponsoredFeesAndReserves amendment in xrpl-py library.
C++ Feature branch: XRPLF/rippled#5887
XLS specification: https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0068-sponsored-fees-and-reserves
XLS spec proposed updates: https://github.com/XRPLF/XRPL-Standards/pull/465/changes

If you would like to run the integ tests, please build the C++ feature branch linked above.

Context of Change

  • Unit tests updated/added
  • Integration tests updated/added
  • Transaction models updated
  • Ledger object models updated xrpl-py does not have ledger-object models
  • Request/RPC method models updated account_sponsoring RPC is expected to be implemented in Clio. I will include it in this implementation once Clio feature is ready.
  • Binary codec updated (e.g., definitions.json includes new types/fields)
  • Amendment enabled in test configuration (e.g., rippled.cfg)
  • Changelog/release notes updated
  • Utility functions added/updated/exported as needed did not find a need for new util methods

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 CHANGELOG.md?

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

Test Plan

Appropriate tests have been added

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 16, 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

Adds XLS-68 sponsorship support: new SponsorshipSet and SponsorshipTransfer transactions and flags, SponsorSignature inner object, sponsor fields on Transaction, sponsor-aware fee calculation, binary/ledger definitions for sponsorship, granular sponsor permissions, request/ledger filters, public exports, and many unit/integration tests.

Changes

Cohort / File(s) Summary
Config & Changelog
\.ci-config/rippled.cfg, CHANGELOG.md
Added Sponsor amendment entry and documented XLS-68d sponsorship support.
Binary codec & defs
xrpl/core/binarycodec/definitions/definitions.json, xrpl/core/binarycodec/definitions/definitions.py
Added sponsorship ledger/node fields, SponsorSignature STObject, Sponsorship ledger entry type, new tx types (SponsorshipSet, SponsorshipTransfer), and granular permission codes (65549/65550). Confirm nth indices, serialization/signing flags and cross-section consistency.
Request & Ledger models
xrpl/models/requests/account_objects.py, xrpl/models/requests/ledger_entry.py
Added AccountObjectType.SPONSORSHIP, AccountObjects.sponsored filter, LedgerEntryType.SPONSORSHIP, and Sponsorship dataclass plus sponsorship query support and validation inclusion.
Transaction core & types
xrpl/models/transactions/transaction.py, xrpl/models/transactions/types/transaction_type.py, xrpl/models/transactions/__init__.py
Added sponsor, sponsor_flags, sponsor_signature to Transaction with validation; new TransactionType members for sponsorship txs; exported sponsor-related models.
New transaction models
xrpl/models/transactions/sponsor_signature.py, xrpl/models/transactions/sponsorship_set.py, xrpl/models/transactions/sponsorship_transfer.py
Introduced SponsorSignature model with validation, SponsorshipSet and SponsorshipTransfer models, flags, interfaces, and cross-field validation logic.
Existing tx updates
xrpl/models/transactions/payment.py, xrpl/models/transactions/account_set.py, xrpl/models/transactions/delegate_set.py
Added TF_SPONSOR_CREATED_ACCOUNT flag and exclusivity checks in Payment; ASF_DISALLOW_INCOMING_SPONSOR ASF flag; added GranularPermission entries SPONSOR_FEE/SPONSOR_RESERVE.
Sponsor signing helpers
xrpl/transaction/sponsor_signer.py, xrpl/transaction/__init__.py
New sponsor co-signing primitives: sign_as_sponsor, combine_sponsor_signers, and result dataclasses; exposed in package API. Review multisig flow, signing preconditions, and canonical encoding choices.
Fee calculation
xrpl/asyncio/transaction/main.py
Augmented fee calc to factor sponsor signer count (warns on multisigner sponsors and scales base fee when sponsor has >1 signer).
Requests/queries & responses tests
tests/integration/reqs/test_account_objects_sponsored.py, tests/integration/reqs/test_ledger_entry_sponsorship.py
Integration tests for account_objects sponsored filter and ledger-entry Sponsorship retrieval.
Integration transaction tests
tests/integration/transactions/...
Many tests added for SponsorshipSet/Transfer, sponsor signing (single & multisig), sponsored AccountDelete, ASF_DISALLOW_INCOMING_SPONSOR behavior, sponsor permissions propagation, and multisig sponsor flows.
Unit tests
tests/unit/models/transactions/...
Comprehensive unit tests added for SponsorSignature, SponsorshipSet/Transfer validation, sponsor fields, sponsor permissions, sponsor flag rules for Payment, and codec round-trips.

Sequence Diagram(s)

sequenceDiagram
    participant Sponsee as Sponsee
    participant Sponsor as Sponsor
    participant Network as Network
    participant Ledger as Ledger

    Sponsee->>Sponsor: Request sponsorship / provide sponsee account
    Sponsor->>Sponsor: Build SponsorshipSet(tx)
    Sponsor->>Network: Submit SponsorshipSet
    Network->>Ledger: Validate & execute
    Ledger->>Ledger: Create Sponsorship ledger entry
    Ledger-->>Network: tesSUCCESS
    Network-->>Sponsor: Confirm sponsorship active
Loading
sequenceDiagram
    participant Sponsee as Sponsee
    participant Sponsor as Sponsor
    participant Network as Network
    participant Ledger as Ledger

    Sponsor->>Sponsor: Build SponsorshipTransfer(tx)
    Sponsor->>Sponsee: Request sponsor co-signature (SponsorSignature)
    Sponsee->>Sponsor: Return SponsorSignature (single or multisig chunks)
    Sponsor->>Network: Submit SponsorshipTransfer with SponsorSignature
    Network->>Ledger: Validate sponsor + sponsee signatures
    Ledger->>Ledger: Update sponsorship ownership
    Ledger-->>Network: tesSUCCESS
    Network-->>Sponsor: Confirm transfer
Loading
sequenceDiagram
    participant Txn as Transaction
    participant FeeCalc as FeeCalculator
    participant Network as Network
    participant SponsorAcc as SponsorAccount

    Txn->>FeeCalc: Request fee calc (includes sponsor?)
    alt sponsor present
        FeeCalc->>SponsorAcc: Fetch signer count
        SponsorAcc-->>FeeCalc: signer_count
        alt signer_count > 1
            FeeCalc->>FeeCalc: Warn multi-signer sponsor
            FeeCalc->>FeeCalc: base_fee *= (1 + signer_count)
        else
            FeeCalc->>FeeCalc: Use base fee
        end
    else
        FeeCalc->>FeeCalc: Standard fee calc
    end
    FeeCalc-->>Txn: Return calculated fee
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • pdp2121
  • Patel-Raj11
  • khancode

Poem

🐰 I nibble bytes and hop with glee,
Sponsor seeds take root in ledger tree,
Signatures stack in tidy rows,
Fees adjust as sponsorship grows,
A happy hop for ledger harmony.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'XLS-68d Sponsored Fees and Reserves' clearly and concisely identifies the main feature being added, matching the substantive changes throughout the PR.
Description check ✅ Passed The PR description follows the template structure, provides context linking to C++ implementation and XLS specification, documents all completed checklist items, and confirms changelog updates.
Docstring Coverage ✅ Passed Docstring coverage is 92.47% 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

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: 2

🧹 Nitpick comments (7)
xrpl/models/requests/account_objects.py (1)

69-69: Consider adding a docstring for the sponsored field.

Other fields in this class have docstrings explaining their purpose. Adding documentation would clarify the filtering behavior (e.g., whether True returns only sponsored objects, False returns non-sponsored, and None returns all).

📝 Suggested docstring
     method: RequestMethod = field(default=RequestMethod.ACCOUNT_OBJECTS, init=False)
     sponsored: Optional[bool] = None
+    """
+    If True, filter to objects that are sponsored. If False, filter to objects
+    that are not sponsored. If omitted, return all objects regardless of
+    sponsorship status.
+    """
     type: Optional[AccountObjectType] = None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xrpl/models/requests/account_objects.py` at line 69, Add a docstring for the
sponsored field in the AccountObjects request model (the sponsored:
Optional[bool] = None attribute) matching the style of the other field docs;
describe that sponsored=True filters to only sponsored objects, sponsored=False
to only non-sponsored objects, and sponsored=None (or omitted) returns both/all
objects, and place this explanatory string immediately above or inline with the
sponsored attribute to keep it consistent with the class's existing field
documentation.
tests/integration/transactions/test_account_set.py (1)

79-111: Consider adding assertion for the set transaction result.

Line 92 submits the set_tx but doesn't assert the result. If this fails silently, the subsequent clear and sponsorship tests could pass for the wrong reasons.

🛡️ Suggested improvement
-        await sign_and_reliable_submission_async(set_tx, sponsee_wallet, client)
+        set_resp = await sign_and_reliable_submission_async(set_tx, sponsee_wallet, client)
+        self.assertEqual(set_resp.result["engine_result"], "tesSUCCESS")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/transactions/test_account_set.py` around lines 79 - 111,
The test test_clear_disallow_incoming_sponsor submits the initial AccountSet
(variable set_tx) but never checks its result; after calling
sign_and_reliable_submission_async(set_tx, sponsee_wallet, client) capture the
response (e.g., set_resp) and assert set_resp.result["engine_result"] ==
"tesSUCCESS" before proceeding to clear_tx and sponsor_tx to ensure the flag was
actually set; update references to set_tx and sign_and_reliable_submission_async
in the test to perform and assert this check.
xrpl/asyncio/transaction/main.py (2)

594-606: Consider using warnings.warn() instead of print().

The warning uses print() which goes to stdout and may be missed in production environments. This is inconsistent with how proper warnings should be emitted. Consider using warnings.warn() for better integration with logging infrastructure, similar to how deprecation warnings are typically handled.

♻️ Suggested refactor
+import warnings
 ...
-            print(
-                (
-                    f"Warning: You are using autofill for a "
-                    f"sponsored transaction: "
-                    f"{transaction.to_dict()}. The fee "
-                    "estimation is based on the number of "
-                    "signers in the sponsor's SignerList. It "
-                    "might be possible to optimize the fee by "
-                    "considering the minimum quorum."
-                    "\nIf you prefer optimized transaction fee,"
-                    " please fill the fee field manually."
-                )
-            )
+            warnings.warn(
+                f"Autofill fee estimation for sponsored transaction uses the full "
+                f"SignerList count ({sponsor_signers_count}). You may optimize by "
+                f"using the minimum quorum and setting the fee field manually.",
+                stacklevel=2,
+            )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xrpl/asyncio/transaction/main.py` around lines 594 - 606, Replace the
print(...) call that emits the sponsored-transaction message with
warnings.warn(...) so the warning goes through Python's warning system;
specifically, construct the same multi-line message (using the existing f-string
with transaction.to_dict()) and call warnings.warn(message,
category=UserWarning, stacklevel=2). Also add an import warnings at the top of
the module if it isn't already present and remove the print(...) invocation in
the block where the transaction autofill for sponsored transactions is handled.

587-587: Minor: Replace ambiguous multiplication sign.

Static analysis flagged the × (MULTIPLICATION SIGN) as ambiguous. Consider replacing with x for consistency with other comments in the codebase.

-    # Fee = (1 + |tx.Signers| + |SponsorSignature.Signers|) × base
+    # Fee = (1 + |tx.Signers| + |SponsorSignature.Signers|) x base
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xrpl/asyncio/transaction/main.py` at line 587, Replace the ambiguous
multiplication sign in the comment "Fee = (1 + |tx.Signers| +
|SponsorSignature.Signers|) × base" with a plain ASCII "x" so it becomes "Fee =
(1 + |tx.Signers| + |SponsorSignature.Signers|) x base"; locate that comment in
xrpl/asyncio/transaction/main.py (the line containing the Fee formula) and
update the character only—no logic or formatting changes.
tests/integration/transactions/test_account_delete.py (1)

78-81: Consider using a more efficient approach for ledger advancement.

Advancing 260 ledgers with individual requests may be slow. However, this matches the AccountDelete sequence requirement (current_ledger_index >= account_sequence + 256), so the approach is functionally correct.

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

In `@tests/integration/transactions/test_account_delete.py` around lines 78 - 81,
The test currently advances _LEDGERS_TO_ADVANCE ledgers by looping and calling
await client.request(LEDGER_ACCEPT_REQUEST) repeatedly which is inefficient;
replace the loop with a single batched advance by using an existing helper or
extending the request to accept a count (e.g., call a helper like await
advance_ledgers(client, _LEDGERS_TO_ADVANCE) or send one client.request with
LEDGER_ACCEPT_REQUEST augmented to include the desired number of ledger
accepts), ensuring you reference and use the symbols _LEDGERS_TO_ADVANCE,
client.request, and LEDGER_ACCEPT_REQUEST when implementing the change.
tests/unit/models/transactions/test_sponsor_signature.py (1)

65-84: Consider consolidating duplicate test methods.

test_valid_sponsor_signature_single_sig (lines 65-71) duplicates test_valid_with_single_signature (lines 18-24), and test_valid_sponsor_signature_multi_sig (lines 73-84) duplicates test_valid_with_signers (lines 26-42). They test the same validation paths with slightly different test data.

Consider removing the duplicate methods or consolidating them to reduce maintenance overhead.

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

In `@tests/unit/models/transactions/test_sponsor_signature.py` around lines 65 -
84, The two tests test_valid_sponsor_signature_single_sig and
test_valid_sponsor_signature_multi_sig duplicate existing tests
test_valid_with_single_signature and test_valid_with_signers; remove the
redundant methods or consolidate them (e.g., keep only
test_valid_with_single_signature and test_valid_with_signers) so each validation
path for SponsorSignature (using signing_pub_key/txn_signature and using signers
list via Signer with account=_ACCOUNT2) is covered once; update any references
to the removed test names and ensure SponsorSignature and Signer usage remains
identical in the retained tests.
xrpl/models/transactions/sponsorship_set.py (1)

117-133: Multiple flag validation errors overwrite each other.

When multiple flag validation errors occur simultaneously, only the last one is preserved because they all use the same errors["flags"] key. For example, if a user sets both TF_SPONSORSHIP_SET_REQUIRE_SIGN_FOR_FEE and TF_SPONSORSHIP_CLEAR_REQUIRE_SIGN_FOR_FEE, AND also sets both reserve flags, only the reserve error will be shown.

This is a minor UX issue since validation still fails, but the user may not see all problems at once.

🔧 Option to accumulate multiple flag errors
+        flag_errors = []
         if set_fee and clear_fee:
-            errors["flags"] = (
+            flag_errors.append(
                 "`TF_SPONSORSHIP_SET_REQUIRE_SIGN_FOR_FEE` and "
                 "`TF_SPONSORSHIP_CLEAR_REQUIRE_SIGN_FOR_FEE` are mutually exclusive."
             )
         if set_res and clear_res:
-            errors["flags"] = (
+            flag_errors.append(
                 "`TF_SPONSORSHIP_SET_REQUIRE_SIGN_FOR_RESERVE`"
                 " and "
                 "`TF_SPONSORSHIP_CLEAR_REQUIRE_SIGN_FOR_RESERVE`"
                 " are mutually exclusive."
             )
         if delete_obj and (set_fee or clear_fee or set_res or clear_res):
-            errors["flags"] = (
+            flag_errors.append(
                 "`TF_DELETE_OBJECT` cannot be combined with any set/clear flags."
             )
+        if flag_errors:
+            errors["flags"] = " ".join(flag_errors)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xrpl/models/transactions/sponsorship_set.py` around lines 117 - 133, The
validation currently overwrites errors["flags"] when multiple mutually-exclusive
flag checks fail (see checks using set_fee, clear_fee, set_res, clear_res,
delete_obj), so change errors["flags"] to accumulate all messages instead of
replacing them: initialize errors["flags"] as a list (or append to it if already
present) and push each descriptive message for the three checks, or concatenate
messages with separators before finalizing; ensure any downstream consumers that
expect a string still receive a joined string if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/unit/models/transactions/test_sponsorship_transfer.py`:
- Around line 68-75: The test docstring for test_valid_reassign_flag is
incorrect: TF_SPONSORSHIP_REASSIGN does not forbid sponsee according to
SponsorshipTransfer._get_errors (only TF_SPONSORSHIP_CREATE forbids sponsee).
Update the docstring in test_valid_reassign_flag to accurately state that
TF_SPONSORSHIP_REASSIGN is a reassignment flag and does not impose the "no
sponsee" restriction (or otherwise describe expected behavior), referencing the
SponsorshipTransfer class and the TF_SPONSORSHIP_REASSIGN/TF_SPONSORSHIP_CREATE
flags.

In `@xrpl/asyncio/transaction/main.py`:
- Around line 586-607: The sponsor fee is only added when sponsor_signers_count
> 1, but per the comment/XLS‑68 it must be applied whenever transaction.sponsor
is not None; call _fetch_counterparty_signers_count(client, transaction.sponsor)
to get sponsor_signers_count and always add base_fee += net_fee * (1 +
sponsor_signers_count) when transaction.sponsor is present, while keeping the
multi‑sign warning (the print) only inside the sponsor_signers_count > 1 branch;
update the block around transaction.sponsor, sponsor_signers_count, base_fee and
net_fee so the fee math is executed unconditionally for any sponsor.

---

Nitpick comments:
In `@tests/integration/transactions/test_account_delete.py`:
- Around line 78-81: The test currently advances _LEDGERS_TO_ADVANCE ledgers by
looping and calling await client.request(LEDGER_ACCEPT_REQUEST) repeatedly which
is inefficient; replace the loop with a single batched advance by using an
existing helper or extending the request to accept a count (e.g., call a helper
like await advance_ledgers(client, _LEDGERS_TO_ADVANCE) or send one
client.request with LEDGER_ACCEPT_REQUEST augmented to include the desired
number of ledger accepts), ensuring you reference and use the symbols
_LEDGERS_TO_ADVANCE, client.request, and LEDGER_ACCEPT_REQUEST when implementing
the change.

In `@tests/integration/transactions/test_account_set.py`:
- Around line 79-111: The test test_clear_disallow_incoming_sponsor submits the
initial AccountSet (variable set_tx) but never checks its result; after calling
sign_and_reliable_submission_async(set_tx, sponsee_wallet, client) capture the
response (e.g., set_resp) and assert set_resp.result["engine_result"] ==
"tesSUCCESS" before proceeding to clear_tx and sponsor_tx to ensure the flag was
actually set; update references to set_tx and sign_and_reliable_submission_async
in the test to perform and assert this check.

In `@tests/unit/models/transactions/test_sponsor_signature.py`:
- Around line 65-84: The two tests test_valid_sponsor_signature_single_sig and
test_valid_sponsor_signature_multi_sig duplicate existing tests
test_valid_with_single_signature and test_valid_with_signers; remove the
redundant methods or consolidate them (e.g., keep only
test_valid_with_single_signature and test_valid_with_signers) so each validation
path for SponsorSignature (using signing_pub_key/txn_signature and using signers
list via Signer with account=_ACCOUNT2) is covered once; update any references
to the removed test names and ensure SponsorSignature and Signer usage remains
identical in the retained tests.

In `@xrpl/asyncio/transaction/main.py`:
- Around line 594-606: Replace the print(...) call that emits the
sponsored-transaction message with warnings.warn(...) so the warning goes
through Python's warning system; specifically, construct the same multi-line
message (using the existing f-string with transaction.to_dict()) and call
warnings.warn(message, category=UserWarning, stacklevel=2). Also add an import
warnings at the top of the module if it isn't already present and remove the
print(...) invocation in the block where the transaction autofill for sponsored
transactions is handled.
- Line 587: Replace the ambiguous multiplication sign in the comment "Fee = (1 +
|tx.Signers| + |SponsorSignature.Signers|) × base" with a plain ASCII "x" so it
becomes "Fee = (1 + |tx.Signers| + |SponsorSignature.Signers|) x base"; locate
that comment in xrpl/asyncio/transaction/main.py (the line containing the Fee
formula) and update the character only—no logic or formatting changes.

In `@xrpl/models/requests/account_objects.py`:
- Line 69: Add a docstring for the sponsored field in the AccountObjects request
model (the sponsored: Optional[bool] = None attribute) matching the style of the
other field docs; describe that sponsored=True filters to only sponsored
objects, sponsored=False to only non-sponsored objects, and sponsored=None (or
omitted) returns both/all objects, and place this explanatory string immediately
above or inline with the sponsored attribute to keep it consistent with the
class's existing field documentation.

In `@xrpl/models/transactions/sponsorship_set.py`:
- Around line 117-133: The validation currently overwrites errors["flags"] when
multiple mutually-exclusive flag checks fail (see checks using set_fee,
clear_fee, set_res, clear_res, delete_obj), so change errors["flags"] to
accumulate all messages instead of replacing them: initialize errors["flags"] as
a list (or append to it if already present) and push each descriptive message
for the three checks, or concatenate messages with separators before finalizing;
ensure any downstream consumers that expect a string still receive a joined
string if needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8854fa81-f26e-438d-8cdf-81c4ca860119

📥 Commits

Reviewing files that changed from the base of the PR and between 827baa6 and 20f1c60.

📒 Files selected for processing (30)
  • .ci-config/rippled.cfg
  • CHANGELOG.md
  • tests/integration/reqs/test_account_objects_sponsored.py
  • tests/integration/reqs/test_ledger_entry_sponsorship.py
  • tests/integration/transactions/test_account_delete.py
  • tests/integration/transactions/test_account_set.py
  • tests/integration/transactions/test_sponsor_permissions.py
  • tests/integration/transactions/test_sponsorship_set.py
  • tests/integration/transactions/test_sponsorship_transfer.py
  • tests/unit/models/transactions/test_better_transaction_flags.py
  • tests/unit/models/transactions/test_payment.py
  • tests/unit/models/transactions/test_sponsor_common_fields.py
  • tests/unit/models/transactions/test_sponsor_permissions.py
  • tests/unit/models/transactions/test_sponsor_signature.py
  • tests/unit/models/transactions/test_sponsorship_set.py
  • tests/unit/models/transactions/test_sponsorship_transfer.py
  • xrpl/asyncio/transaction/main.py
  • xrpl/core/binarycodec/definitions/definitions.json
  • xrpl/core/binarycodec/definitions/definitions.py
  • xrpl/models/requests/account_objects.py
  • xrpl/models/requests/ledger_entry.py
  • xrpl/models/transactions/__init__.py
  • xrpl/models/transactions/account_set.py
  • xrpl/models/transactions/delegate_set.py
  • xrpl/models/transactions/payment.py
  • xrpl/models/transactions/sponsor_signature.py
  • xrpl/models/transactions/sponsorship_set.py
  • xrpl/models/transactions/sponsorship_transfer.py
  • xrpl/models/transactions/transaction.py
  • xrpl/models/transactions/types/transaction_type.py

Comment thread tests/unit/models/transactions/test_sponsorship_transfer.py
Comment on lines +586 to +607
# SponsorSignature with multi-sign
# Fee = (1 + |tx.Signers| + |SponsorSignature.Signers|) × base
if transaction.sponsor is not None:
sponsor_signers_count = await _fetch_counterparty_signers_count(
client, transaction.sponsor
)

if sponsor_signers_count > 1:
print(
(
f"Warning: You are using autofill for a "
f"sponsored transaction: "
f"{transaction.to_dict()}. The fee "
"estimation is based on the number of "
"signers in the sponsor's SignerList. It "
"might be possible to optimize the fee by "
"considering the minimum quorum."
"\nIf you prefer optimized transaction fee,"
" please fill the fee field manually."
)
)
base_fee += net_fee * (1 + sponsor_signers_count)
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Mar 16, 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:

XLS-68 XRPL sponsored fees transaction cost formula

💡 Result:

Under XLS-68 (Sponsored Fees & Reserves), the fee amount a transaction must pay is still the normal XRPL transaction fee; sponsorship only changes which balance is decremented (a sponsor-funded Sponsorship.FeeAmount pool, or the sponsor’s XRP balance), subject to constraints like MaxFee, reserve protection, and (optionally) requiring a sponsor signature. [1]

1) Baseline XRPL transaction cost (network/load-based)

From XRPL’s transaction-cost rules, the current minimum fee you generally target is:

Current Tx Cost (in XRP) = base_fee_xrp × load_factor [2]

(Equivalent values also exist in drops via server_state / fee APIs, but the key formula is the same scaling concept.) [2]

2) XLS-68 signature-related fee formula (when sponsorship adds sponsor multi-signing)

XLS-68 specifies that if sponsor multi-signatures are present/needed, the fee must cover added signature-processing overhead, and the fee calculation becomes:

Total fee = (1 + |tx.Signers| + |tx.SponsorSignature.Signers|) × base_fee + (transaction-specific fees) [1]

And specifically, if SponsorSignature.Signers is necessary, the minimum fee is:

Minimum fee = (|signatures| + 1) × base_fee [1]

3) Who actually pays (sponsored fees)

If a Sponsorship object exists, the ledger decrements tx.Fee from Sponsorship.FeeAmount; otherwise it decrements tx.Fee from the sponsor’s AccountRoot.Balance. [1]

Sources: [1] XLS-0068 spec (Sponsored Fees & Reserves). [2] XRPL.org “Transaction Cost”.


🏁 Script executed:

# Search for other references to sponsored fee calculation and sponsor_signers_count
rg -n "sponsor_signers_count|SponsorSignature" xrpl/asyncio/transaction/main.py -A 2 -B 2

Repository: XRPLF/xrpl-py

Length of output: 677


🏁 Script executed:

# Look for tests related to sponsored fee calculation
fd -e py | xargs rg -l "sponsor.*fee\|SponsorSignature" | head -20

Repository: XRPLF/xrpl-py

Length of output: 39


🏁 Script executed:

# Check if there's documentation about the fee calculation
fd -e md -e txt | xargs rg "XLS-68\|sponsor.*fee\|SponsorSignature" 2>/dev/null | head -20

Repository: XRPLF/xrpl-py

Length of output: 39


🏁 Script executed:

# Look at the broader context around the sponsor fee calculation
cat -n xrpl/asyncio/transaction/main.py | sed -n '570,620p'

Repository: XRPLF/xrpl-py

Length of output: 2601


🏁 Script executed:

# Get the exact indentation and structure of the counterparty and sponsor fee blocks
python3 << 'EOF'
with open('xrpl/asyncio/transaction/main.py', 'r') as f:
    lines = f.readlines()
    # Show lines 570-615 with visible whitespace
    for i in range(569, 615):
        line = lines[i]
        # Replace spaces with dots and tabs with [TAB] for visibility
        visible = line.rstrip('\n').replace(' ', '·').replace('\t', '[TAB]')
        print(f"{i+1:3d}: {visible}")
EOF

Repository: XRPLF/xrpl-py

Length of output: 2259


🏁 Script executed:

# Also search for how the counterparty fee is handled earlier in the function
rg -n "counterparty_signers_count" xrpl/asyncio/transaction/main.py -B 5 -A 5

Repository: XRPLF/xrpl-py

Length of output: 1937


The sponsor fee is incorrectly omitted for single-signer sponsors.

The sponsor fee is only added when sponsor_signers_count > 1 (line 593), but should be calculated for all sponsors, even those with a single signer. This contradicts both the fee formula in the comment on line 587 and the XLS-68 specification, which states that the fee must include the sponsor's signer count regardless of whether it is 1, 2, or more.

Additionally, this behavior is inconsistent with the counterparty fee handling (lines 572–584), where the fee is added unconditionally outside the if counterparty_signers_count > 1: block, even for single-signer counterparties.

The fee calculation should be moved outside the conditional block and applied whenever transaction.sponsor is not None.

🧰 Tools
🪛 Ruff (0.15.6)

[warning] 587-587: Comment contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?

(RUF003)

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

In `@xrpl/asyncio/transaction/main.py` around lines 586 - 607, The sponsor fee is
only added when sponsor_signers_count > 1, but per the comment/XLS‑68 it must be
applied whenever transaction.sponsor is not None; call
_fetch_counterparty_signers_count(client, transaction.sponsor) to get
sponsor_signers_count and always add base_fee += net_fee * (1 +
sponsor_signers_count) when transaction.sponsor is present, while keeping the
multi‑sign warning (the print) only inside the sponsor_signers_count > 1 branch;
update the block around transaction.sponsor, sponsor_signers_count, base_fee and
net_fee so the fee math is executed unconditionally for any sponsor.

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.

The fee calculation in the case of single-signer-sponsors works correctly. The rippled node does not reject this transaction for insufficient fee.
Look at the integration tests for SponsorshipSet and SponsorshipTransfer transactions in this PR. They pass the integ tests.

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.

@ckeshava, thank you for the clarification. That makes sense — for a single-signer sponsor, the SponsorSignature uses signing_pub_key + txn_signature fields rather than a Signers array, so |SponsorSignature.Signers| = 0 in the XLS-68 formula (1 + |tx.Signers| + |SponsorSignature.Signers|) × base_fee, meaning no additional fee beyond the base is required. The guard sponsor_signers_count > 1 is therefore correct, and the integration tests validate this. I'll retract my earlier concern.

(∩`-´)⊃━☆゚.*・。゚


✏️ Learnings added
Learnt from: ckeshava
Repo: XRPLF/xrpl-py PR: 921
File: xrpl/asyncio/transaction/main.py:586-607
Timestamp: 2026-03-16T21:21:43.397Z
Learning: In xrpl/asyncio/transaction/main.py, for XLS-68 sponsored fee calculation, the additional fee for sponsor signatures is only required when the sponsor uses multi-signing (sponsor_signers_count > 1). For a single-signer sponsor, the SponsorSignature uses signing_pub_key + txn_signature fields (not a Signers array), so |SponsorSignature.Signers| = 0 in the XLS-68 formula (1 + |tx.Signers| + |SponsorSignature.Signers|) × base_fee, and no extra fee is needed. The guard `if sponsor_signers_count > 1` before `base_fee += net_fee * (1 + sponsor_signers_count)` is intentional and correct.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: ckeshava
Repo: XRPLF/xrpl-py PR: 757
File: xrpl/asyncio/transaction/main.py:505-509
Timestamp: 2025-06-05T20:52:31.099Z
Learning: The correct fee calculation for XRPL Batch transactions follows the C++ rippled implementation structure: (2 × base_fee) + sum of individual inner transaction fees + (signer_count × base_fee). Each inner transaction's fee should be calculated individually using the same fee calculation logic as standalone transactions, not assumed to be equal to base_fee.

Learnt from: ckeshava
Repo: XRPLF/xrpl-py PR: 759
File: xrpl/models/transactions/deposit_preauth.py:54-64
Timestamp: 2024-10-30T20:45:02.301Z
Learning: In the `DepositPreauth` class in `xrpl/models/transactions/deposit_preauth.py`, when validating that exactly one of the parameters `authorize`, `unauthorize`, `authorize_credentials`, or `unauthorize_credentials` is set, ensure that any refactoring maintains logical equivalence with the original condition to prevent altering the intended functionality.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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.

♻️ Duplicate comments (1)
tests/unit/models/transactions/test_sponsorship_transfer.py (1)

174-174: ⚠️ Potential issue | 🟡 Minor

Clarify REASSIGN docstring to avoid implying a sponsee restriction.

At Line 174, the phrase “(no sponsee)” suggests a rule that does not exist for TF_SPONSORSHIP_REASSIGN; only CREATE has that restriction in xrpl/models/transactions/sponsorship_transfer.py _get_errors.

📝 Suggested docstring fix
-        """FlagInterface dict with TF_SPONSORSHIP_REASSIGN (no sponsee)."""
+        """FlagInterface dict with TF_SPONSORSHIP_REASSIGN."""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/models/transactions/test_sponsorship_transfer.py` at line 174,
Update the docstring that currently reads "FlagInterface dict with
TF_SPONSORSHIP_REASSIGN (no sponsee)." to avoid implying a sponsee restriction;
change the wording to simply reference TF_SPONSORSHIP_REASSIGN without "(no
sponsee)". Ensure the docstring aligns with the validation logic in the
_get_errors method (which enforces the sponsee restriction only for CREATE) and
mention TF_SPONSORSHIP_REASSIGN and CREATE so reviewers can cross-check the
behavior.
🧹 Nitpick comments (1)
tests/unit/models/transactions/test_sponsorship_transfer.py (1)

297-416: Reduce coupling to exact validation message text in invalid-input tests.

These tests become brittle by asserting long message fragments; minor wording changes will fail tests without behavior changes. Prefer asserting exception type and a stable signal only.

♻️ Suggested pattern change
     def test_invalid_end_and_create_flags(self):
         """Setting TF_SPONSORSHIP_END and TF_SPONSORSHIP_CREATE together is rejected."""
-        with self.assertRaises(XRPLModelException) as cm:
+        with self.assertRaises(XRPLModelException):
             SponsorshipTransfer(
                 account=_ACCOUNT,
                 flags=(
                     SponsorshipTransferFlag.TF_SPONSORSHIP_END
                     | SponsorshipTransferFlag.TF_SPONSORSHIP_CREATE
                 ),
             )
-        self.assertIn(self._MULTI_FLAG_MSG, str(cm.exception))

Based on learnings: In tests/unit/models/transactions/test_credential_create.py, when testing invalid inputs, avoid adding assertions on specific error messages if it increases the complexity and maintenance overhead of the tests.

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

In `@tests/unit/models/transactions/test_sponsorship_transfer.py` around lines 297
- 416, The tests in test_sponsorship_transfer.py assert on long, exact
validation message fragments which makes them brittle; update the tests (e.g.,
test_invalid_end_and_create_flags, test_invalid_end_and_reassign_flags,
test_invalid_create_and_reassign_flags, test_invalid_sponsee_with_create_flag,
test_invalid_sponsor_equals_account, test_invalid_sponsor_flags_without_sponsor,
test_invalid_sponsor_signature_without_sponsor,
test_invalid_sponsor_flags_bad_bits,
test_invalid_sponsor_flags_combined_bad_bits) to stop asserting long message
text on XRPLModelException from SponsorshipTransfer and instead either only
assertRaises(XRPLModelException) or assert a minimal, stable signal (like the
presence of a short token such as the field name "sponsor" or "sponsee") to
verify the right validation path without coupling to exact wording.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/unit/models/transactions/test_sponsorship_transfer.py`:
- Line 174: Update the docstring that currently reads "FlagInterface dict with
TF_SPONSORSHIP_REASSIGN (no sponsee)." to avoid implying a sponsee restriction;
change the wording to simply reference TF_SPONSORSHIP_REASSIGN without "(no
sponsee)". Ensure the docstring aligns with the validation logic in the
_get_errors method (which enforces the sponsee restriction only for CREATE) and
mention TF_SPONSORSHIP_REASSIGN and CREATE so reviewers can cross-check the
behavior.

---

Nitpick comments:
In `@tests/unit/models/transactions/test_sponsorship_transfer.py`:
- Around line 297-416: The tests in test_sponsorship_transfer.py assert on long,
exact validation message fragments which makes them brittle; update the tests
(e.g., test_invalid_end_and_create_flags, test_invalid_end_and_reassign_flags,
test_invalid_create_and_reassign_flags, test_invalid_sponsee_with_create_flag,
test_invalid_sponsor_equals_account, test_invalid_sponsor_flags_without_sponsor,
test_invalid_sponsor_signature_without_sponsor,
test_invalid_sponsor_flags_bad_bits,
test_invalid_sponsor_flags_combined_bad_bits) to stop asserting long message
text on XRPLModelException from SponsorshipTransfer and instead either only
assertRaises(XRPLModelException) or assert a minimal, stable signal (like the
presence of a short token such as the field name "sponsor" or "sponsee") to
verify the right validation path without coupling to exact wording.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ecdf46ff-3449-41c7-8d3f-dfec7d583721

📥 Commits

Reviewing files that changed from the base of the PR and between 20f1c60 and 3f0b69b.

📒 Files selected for processing (1)
  • tests/unit/models/transactions/test_sponsorship_transfer.py

"`sponsor_signature` requires `sponsor` to be set."
)

# Pseudo-transactions and Batch cannot be sponsored (XLS-68 §8.3.4).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I like the comments referring to the exact section the edge case came from, very helpful

# Late import to avoid circular dependency (sponsor_signature imports Signer from this
# module). This makes SponsorSignature available in the module namespace so that
# get_type_hints() can resolve the forward reference in Transaction.sponsor_signature.
from xrpl.models.transactions.sponsor_signature import ( # noqa: E402, F811
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

what is noqa?

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.

Since this import is placed at the end of the file, I suspect it violates the linter configuration. However, this is necessary to avoid circular imports. noqa is used to disable the linter check on this line, it stands for No-Quality-Assurance

Explanation of noqa: https://stackoverflow.com/questions/45346575/what-does-noqa-mean-in-python-comments

Comment thread CHANGELOG.md Outdated
Comment on lines +17 to +19
- New transaction types: `SponsorshipSet` and `SponsorshipTransfer`
- New ledger object type: `Sponsorship` (type code 144)
- New inner object model: `SponsorSignature` for sponsor co-signing
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: I see the convention is to just list adding support, not any details for conciseness

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.

Copy link
Copy Markdown
Collaborator

@achowdhry-ripple achowdhry-ripple left a comment

Choose a reason for hiding this comment

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

good PR -- my favorite part of AI here is the detailed commenting, it makes the code very interpretable and draws directly from the spec

Comment thread tests/integration/transactions/test_sponsorship_transfer.py
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: 1

🧹 Nitpick comments (4)
tests/integration/transactions/test_sponsor_signer.py (2)

161-161: Move import to top of file.

The decode_classic_address import on line 161 is inside the test method. For consistency with the rest of the codebase and to avoid repeated import overhead, move it to the top-level imports.

♻️ Proposed fix

Add to the existing imports at the top:

 from xrpl.asyncio.transaction import autofill, submit
 from xrpl.asyncio.transaction.main import sign
+from xrpl.core.addresscodec import decode_classic_address
 from xrpl.models import Payment

Then remove line 161:

         # Signers must be sorted by canonical account ID bytes (ascending).
-        from xrpl.core.addresscodec import decode_classic_address
-
         ids = [
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/transactions/test_sponsor_signer.py` at line 161, The
import of decode_classic_address is inside a test body; move the statement
importing decode_classic_address from xrpl.core.addresscodec to the module's
top-level imports (alongside the other imports in
tests/integration/transactions/test_sponsor_signer.py) and remove the
now-redundant inline import on line 161 so the test uses the top-level symbol
instead.

60-60: Replace EN DASH with HYPHEN-MINUS in comments.

Ruff flagged ambiguous EN DASH characters () in step comments throughout the file. Replace them with standard HYPHEN-MINUS (-) for consistency (e.g., # Step 1 - instead of # Step 1 –).

Also applies to: 70-70, 76-76, 88-88, 123-123, 133-133, 137-137, 156-156, 169-169

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

In `@tests/integration/transactions/test_sponsor_signer.py` at line 60, Replace
ambiguous EN DASH characters (U+2013, "–") used in step comments with the
standard HYPHEN-MINUS (U+002D, "-") to satisfy Ruff; specifically update
comments like "# Step 1 – Sponsee builds and autofills the transaction." and the
other occurrences indicated (the comments at the other steps such as the ones
currently showing "–") so they read "# Step 1 - ..." (and likewise for Step 2,
Step 3, etc.) in tests/integration/transactions/test_sponsor_signer.py, ensuring
only the dash character in the comment text is changed.
xrpl/transaction/sponsor_signer.py (2)

139-140: Unnecessary round-trip conversion.

Lines 139-140 convert the transaction to a dict and back to a Transaction object, but tx is already a Transaction. This appears to be dead code that doesn't change anything.

♻️ Proposed fix
-    tx_dict = tx.to_dict()
-    tx = Transaction.from_dict(tx_dict)
-
     tx_json = tx.to_xrpl()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xrpl/transaction/sponsor_signer.py` around lines 139 - 140, The two-line
round-trip using tx.to_dict() and Transaction.from_dict(tx_dict) is unnecessary
because tx is already a Transaction; remove the lines "tx_dict = tx.to_dict()"
and "tx = Transaction.from_dict(tx_dict)" from sponsor_signer.py so the original
tx object is used directly (unless there is a documented need to normalize or
deep-copy, in which case replace with an explicit copy operation such as
tx.copy() or a dedicated normalization function instead of the round-trip).

98-99: Replace EN DASH with HYPHEN-MINUS in docstrings.

Ruff flagged ambiguous EN DASH characters () in the docstrings. Replace them with standard HYPHEN-MINUS (-) for consistency.

🔧 Proposed fix for lines 98-99
-        - ``tx`` – the transaction with ``sponsor_signature`` added.
-        - ``tx_blob`` – the serialised transaction blob (no sponsee sig yet).
+        - ``tx`` - the transaction with ``sponsor_signature`` added.
+        - ``tx_blob`` - the serialised transaction blob (no sponsee sig yet).
🔧 Proposed fix for lines 212-213
-        - ``tx`` – the combined transaction object.
-        - ``tx_blob`` – the serialised hex blob ready for the sponsee to sign
+        - ``tx`` - the combined transaction object.
+        - ``tx_blob`` - the serialised hex blob ready for the sponsee to sign

Also applies to: 212-213

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

In `@xrpl/transaction/sponsor_signer.py` around lines 98 - 99, Replace the
ambiguous EN DASH characters (U+2013) used in docstring bullet points with the
standard ASCII HYPHEN-MINUS ('-') in xrpl/transaction/sponsor_signer.py;
specifically update the bullets describing ``tx`` and ``tx_blob`` (the lines
referring to those symbols) and the other docstring occurrence near the 212-213
area so all hyphens are ASCII hyphen-minus characters, then run the linter to
ensure no other EN DASH instances remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@xrpl/transaction/sponsor_signer.py`:
- Around line 261-265: The function _strip_signers currently uses a bare dict
return type; update its annotation to return Dict[str, Any] (i.e., def
_strip_signers(tx: Transaction) -> Dict[str, Any]:) and add the required typing
imports (from typing import Any, Dict) at the top of the file so mypy no longer
reports "Missing type parameters for generic type 'dict'"; keep the function
body unchanged aside from the new return type annotation.

---

Nitpick comments:
In `@tests/integration/transactions/test_sponsor_signer.py`:
- Line 161: The import of decode_classic_address is inside a test body; move the
statement importing decode_classic_address from xrpl.core.addresscodec to the
module's top-level imports (alongside the other imports in
tests/integration/transactions/test_sponsor_signer.py) and remove the
now-redundant inline import on line 161 so the test uses the top-level symbol
instead.
- Line 60: Replace ambiguous EN DASH characters (U+2013, "–") used in step
comments with the standard HYPHEN-MINUS (U+002D, "-") to satisfy Ruff;
specifically update comments like "# Step 1 – Sponsee builds and autofills the
transaction." and the other occurrences indicated (the comments at the other
steps such as the ones currently showing "–") so they read "# Step 1 - ..." (and
likewise for Step 2, Step 3, etc.) in
tests/integration/transactions/test_sponsor_signer.py, ensuring only the dash
character in the comment text is changed.

In `@xrpl/transaction/sponsor_signer.py`:
- Around line 139-140: The two-line round-trip using tx.to_dict() and
Transaction.from_dict(tx_dict) is unnecessary because tx is already a
Transaction; remove the lines "tx_dict = tx.to_dict()" and "tx =
Transaction.from_dict(tx_dict)" from sponsor_signer.py so the original tx object
is used directly (unless there is a documented need to normalize or deep-copy,
in which case replace with an explicit copy operation such as tx.copy() or a
dedicated normalization function instead of the round-trip).
- Around line 98-99: Replace the ambiguous EN DASH characters (U+2013) used in
docstring bullet points with the standard ASCII HYPHEN-MINUS ('-') in
xrpl/transaction/sponsor_signer.py; specifically update the bullets describing
``tx`` and ``tx_blob`` (the lines referring to those symbols) and the other
docstring occurrence near the 212-213 area so all hyphens are ASCII hyphen-minus
characters, then run the linter to ensure no other EN DASH instances remain.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 22999a35-d3bf-4309-8d32-45f702800941

📥 Commits

Reviewing files that changed from the base of the PR and between ace327c and bc52ae3.

📒 Files selected for processing (3)
  • tests/integration/transactions/test_sponsor_signer.py
  • xrpl/transaction/__init__.py
  • xrpl/transaction/sponsor_signer.py

Comment thread xrpl/transaction/sponsor_signer.py Outdated
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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@xrpl/transaction/sponsor_signer.py`:
- Around line 98-99: Docstring uses Unicode en dashes for the bullet lines
describing ``tx`` and ``tx_blob``; replace each EN DASH (U+2013) with a standard
ASCII hyphen-minus (U+002D) so Ruff RUF002 is satisfied. Locate the
return/descriptive section in xrpl/transaction/sponsor_signer.py that lists
``tx`` – the transaction... and ``tx_blob`` – the serialised..., and change the
dash characters to "-"; apply the same replacement to the other occurrences
noted around the section that also lists those items (the other instance at the
later docstring lines mentioned).
- Line 26: The module docstring contains a malformed Sphinx function reference
`:func:\`combine_sponsor_signers\`\``; open the module docstring in
sponsor_signer.py and replace that token with a proper Sphinx role
`:func:`combine_sponsor_signers`` (i.e., remove the extra backtick) so the
reference to the combine_sponsor_signers function renders correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 59236108-2582-4ff2-b17e-0cfdfed5c1b7

📥 Commits

Reviewing files that changed from the base of the PR and between bc52ae3 and 67b08d8.

📒 Files selected for processing (1)
  • xrpl/transaction/sponsor_signer.py


For sponsor accounts that require multiple keys (multi-sig), each key holder
calls :func:`sign_as_sponsor` with ``multisign=True``, then all contributions
are merged with :func:`combine_sponsor_signers`` before the sponsee signs.
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.

⚠️ Potential issue | 🟡 Minor

Fix malformed Sphinx function reference in the module docstring.

There’s an extra backtick in :func:\combine_sponsor_signers``` which can break docs rendering.

Suggested fix
-calls :func:`sign_as_sponsor` with ``multisign=True``, then all contributions
-are merged with :func:`combine_sponsor_signers`` before the sponsee signs.
+calls :func:`sign_as_sponsor` with ``multisign=True``, then all contributions
+are merged with :func:`combine_sponsor_signers` before the sponsee signs.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
are merged with :func:`combine_sponsor_signers`` before the sponsee signs.
are merged with :func:`combine_sponsor_signers` before the sponsee signs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xrpl/transaction/sponsor_signer.py` at line 26, The module docstring contains
a malformed Sphinx function reference `:func:\`combine_sponsor_signers\`\``;
open the module docstring in sponsor_signer.py and replace that token with a
proper Sphinx role `:func:`combine_sponsor_signers`` (i.e., remove the extra
backtick) so the reference to the combine_sponsor_signers function renders
correctly.

Comment on lines +98 to +99
- ``tx`` – the transaction with ``sponsor_signature`` added.
- ``tx_blob`` – the serialised transaction blob (no sponsee sig yet).
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.

⚠️ Potential issue | 🟡 Minor

Replace EN DASH with standard hyphen to satisfy Ruff (RUF002).

These lines currently use ambiguous Unicode dashes.

Suggested fix
-        - ``tx`` – the transaction with ``sponsor_signature`` added.
-        - ``tx_blob`` – the serialised transaction blob (no sponsee sig yet).
+        - ``tx`` - the transaction with ``sponsor_signature`` added.
+        - ``tx_blob`` - the serialised transaction blob (no sponsee sig yet).
...
-        - ``tx`` – the combined transaction object.
-        - ``tx_blob`` – the serialised hex blob ready for the sponsee to sign
+        - ``tx`` - the combined transaction object.
+        - ``tx_blob`` - the serialised hex blob ready for the sponsee to sign

Also applies to: 212-213

🧰 Tools
🪛 Ruff (0.15.6)

[warning] 98-98: Docstring contains ambiguous (EN DASH). Did you mean - (HYPHEN-MINUS)?

(RUF002)


[warning] 99-99: Docstring contains ambiguous (EN DASH). Did you mean - (HYPHEN-MINUS)?

(RUF002)

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

In `@xrpl/transaction/sponsor_signer.py` around lines 98 - 99, Docstring uses
Unicode en dashes for the bullet lines describing ``tx`` and ``tx_blob``;
replace each EN DASH (U+2013) with a standard ASCII hyphen-minus (U+002D) so
Ruff RUF002 is satisfied. Locate the return/descriptive section in
xrpl/transaction/sponsor_signer.py that lists ``tx`` – the transaction... and
``tx_blob`` – the serialised..., and change the dash characters to "-"; apply
the same replacement to the other occurrences noted around the section that also
lists those items (the other instance at the later docstring lines mentioned).

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.

3 participants