Skip to content

Conversation

@vlntb
Copy link
Collaborator

@vlntb vlntb commented Dec 5, 2025

High Level Overview of Change

The freeze logic was incorrectly blocking IOU issuers from transacting with their own frozen currency. This violated the XRP Ledger protocol, which specifies that "Counterparties of the frozen issuer can still send and receive payments directly to and from the issuing address."

Context of Change

Three issues were fixed:

  1. isFrozen() function was returning true for issuers checking their own currency during global freeze, causing the issuer to be treated as frozen. The early return prevented reaching the later issuer exemption check.
  2. LoanBrokerCoverDeposit::preclaim() was using accountHolds() with FreezeHandling::fhZERO_IF_FROZEN for all accounts, including issuers. Since issuers don't have a "balance" of their own tokens (infinite issuance ability), the balance check would return 0 and fail with tecINSUFFICIENT_FUNDS.
  3. VaultWithdraw::preclaim() had checkFrozen() calls that were always executed, even when the destination or submitter was the asset issuer. Now skips freeze checks when either the destination or submitter is the IOU issuer.

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)
  • Performance (increase or change in throughput and/or latency)
  • Tests (you added tests for code that already exists, or your new feature included in this PR)
  • Documentation update
  • Chore (no impact to binary, e.g. .gitignore, formatting, dropping support for older tooling)
  • Release

API Impact

  • Public API: New feature (new methods and/or new fields)
  • Public API: Breaking change (in general, breaking changes should only impact the next api_version)
  • libxrpl change (any change that may affect libxrpl or dependents of libxrpl)
  • Peer protocol change (must be backward compatible or bump the peer protocol version)

@vlntb vlntb requested a review from ximinez December 5, 2025 10:53
@vlntb vlntb marked this pull request as ready for review December 5, 2025 10:53
@vlntb vlntb requested a review from Tapanito December 5, 2025 10:53
@codecov
Copy link

codecov bot commented Dec 5, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.1%. Comparing base (f7a5f35) to head (ee33367).

Additional details and impacted files

Impacted file tree graph

@@                       Coverage Diff                        @@
##           ximinez/lending-XLS-66-ongoing   #6113     +/-   ##
================================================================
- Coverage                            79.1%   79.1%   -0.0%     
================================================================
  Files                                 839     839             
  Lines                               71380   71394     +14     
  Branches                             8318    8320      +2     
================================================================
+ Hits                                56476   56486     +10     
- Misses                              14904   14908      +4     
Files with missing lines Coverage Δ
src/libxrpl/ledger/View.cpp 94.5% <100.0%> (+<0.1%) ⬆️
src/xrpld/app/tx/detail/LoanBrokerCoverDeposit.cpp 95.0% <100.0%> (+0.2%) ⬆️
src/xrpld/app/tx/detail/VaultWithdraw.cpp 99.2% <100.0%> (+0.1%) ⬆️

... and 3 files with indirect coverage changes

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vlntb vlntb changed the title Frozen assets should still be withdrawable Frozen IOUs should still be transferrable to the issuer Dec 5, 2025
@vlntb vlntb changed the title Frozen IOUs should still be transferrable to the issuer Frozen IOUs should still be transferable to the issuer Dec 5, 2025
@vlntb vlntb changed the title Frozen IOUs should still be transferable to the issuer Frozen IOUs should still be transferable to/from the issuer Dec 8, 2025
@vlntb vlntb requested a review from shawnxie999 December 8, 2025 14:05
return false;
auto sle = view.read(keylet::account(issuer));
if (sle && sle->isFlag(lsfGlobalFreeze))
if (sle && sle->isFlag(lsfGlobalFreeze) && issuer != account)
Copy link
Collaborator

@shawnxie999 shawnxie999 Dec 8, 2025

Choose a reason for hiding this comment

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

it looks like this helper function might have existed for some time (and being used in existing protocols). then we'd probably need to amendment gate this change with lending protocol.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agree. Added GlobalFreezeIssuer amendment.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Agree. Added GlobalFreezeIssuer amendment.

You don't need a new amendment - you can reuse LendingProtocol and/or SingleAssetVault. This bug only affects transactions related to those amendments.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@vlntb vlntb requested a review from a team as a code owner December 10, 2025 16:58

// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Does it really need a separate amendment? Since Lending Protocol is not supported, it can be part of the same LendingProtocol amendment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good question. Let's discuss this in the meeting in 15 minutes. My thinking was that since this is related to Freeze, which we happened to touch during Lending testing, it should be a freeze-related amendment. But I also see an argument including it with Lending.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good question. Let's discuss this in the meeting in 15 minutes. My thinking was that since this is related to Freeze, which we happened to touch during Lending testing, it should be a freeze-related amendment. But I also see an argument including it with Lending.

You don't need a new amendment - you can reuse LendingProtocol and/or SingleAssetVault. This bug only affects transactions related to those amendments.

// When withdrawing IOU to the issuer, ignore freeze since spec allows
// returning frozen IOU assets to their issuer. MPTs don't have this
// exemption - MPT locks function like "deep freeze" with no issuer
// exception.
Copy link
Collaborator

Choose a reason for hiding this comment

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

@vlntb, do I understand correctly that, if the depositor is the issuer of an MPT, and the asset is locked, the issue will still be able to withdraw from the vault?

Comment on lines 238 to 241
{
if (!view.rules().enabled(fixGlobalFreezeIssuer) || issuer != account)
return true;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

should it really be a separate amendment or should it just be fixed as part of lending? cc @ximinez

Copy link
Collaborator

Choose a reason for hiding this comment

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

We're thinking on the same line of thought: #6113 (comment)

Comment on lines +2045 to +2048
// Verify issuer can still receive payments (uses isFrozen internally)
env(pay(holder, issuer, USD(10)));
env.close();

Copy link
Collaborator

Choose a reason for hiding this comment

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

Should also verify that the issuer can still send payments.

        // Verify issuer can still send payments (uses isFrozen internally)
        env(pay(issuer, holder, USD(10)));
        env.close();


// Test pre-amendment behavior (issuer IS frozen under global freeze)
{
Env env_pre(*this, features - fixGlobalFreezeIssuer);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This test is still good, but will need to be rewritten to use LP and SAV.

Comment on lines +1481 to +1493
// Issuer CAN deposit to their own broker during global freeze
// This is the issuer exemption - issuer can always send (issue) their
// own tokens Per spec: "Counterparties of the frozen issuer can still
// send and receive payments directly to and from the issuing address."
env(coverDeposit(issuer, brokerKeylet.key, asset(10)));
env.close();

// Verify the deposit succeeded
broker = env.le(brokerKeylet);
if (BEAST_EXPECT(broker))
{
BEAST_EXPECT(broker->at(sfCoverAvailable) == asset(10).number());
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you also add tests to check that the broker can withdraw the cover, too?

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.

5 participants