Skip to content

Commit 3248433

Browse files
gregtatcamximinez
authored andcommitted
Check if a withdrawal amount exceeds any applicable receiving limit. (#6117)
- Check the trust line limit is not exceeded for a withdraw to a third party Destination account.
1 parent 5835dff commit 3248433

File tree

9 files changed

+345
-9
lines changed

9 files changed

+345
-9
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
*/
1818
//==============================================================================
1919

20-
#ifndef RIPPLE_APP_PATHS_CREDIT_H_INCLUDED
21-
#define RIPPLE_APP_PATHS_CREDIT_H_INCLUDED
20+
#ifndef RIPPLE_LEDGER_CREDIT_H_INCLUDED
21+
#define RIPPLE_LEDGER_CREDIT_H_INCLUDED
2222

2323
#include <xrpl/ledger/View.h>
2424
#include <xrpl/protocol/IOUAmount.h>

include/xrpl/ledger/View.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,13 +729,16 @@ checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag);
729729
* - If withdrawing to self, succeed.
730730
* - If not, checks if the receiver requires deposit authorization, and if
731731
* the sender has it.
732+
* - Checks that the receiver will not exceed the limit (IOU trustline limit
733+
* or MPT MaximumAmount).
732734
*/
733735
[[nodiscard]] TER
734736
canWithdraw(
735737
AccountID const& from,
736738
ReadView const& view,
737739
AccountID const& to,
738740
SLE::const_ref toSle,
741+
STAmount const& amount,
739742
bool hasDestinationTag);
740743

741744
/** Checks that can withdraw funds from an object to itself or a destination.
@@ -749,12 +752,15 @@ canWithdraw(
749752
* - If withdrawing to self, succeed.
750753
* - If not, checks if the receiver requires deposit authorization, and if
751754
* the sender has it.
755+
* - Checks that the receiver will not exceed the limit (IOU trustline limit
756+
* or MPT MaximumAmount).
752757
*/
753758
[[nodiscard]] TER
754759
canWithdraw(
755760
AccountID const& from,
756761
ReadView const& view,
757762
AccountID const& to,
763+
STAmount const& amount,
758764
bool hasDestinationTag);
759765

760766
/** Checks that can withdraw funds from an object to itself or a destination.
@@ -768,6 +774,8 @@ canWithdraw(
768774
* - If withdrawing to self, succeed.
769775
* - If not, checks if the receiver requires deposit authorization, and if
770776
* the sender has it.
777+
* - Checks that the receiver will not exceed the limit (IOU trustline limit
778+
* or MPT MaximumAmount).
771779
*/
772780
[[nodiscard]] TER
773781
canWithdraw(ReadView const& view, STTx const& tx);

src/libxrpl/ledger/View.cpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <xrpl/basics/chrono.h>
2323
#include <xrpl/beast/utility/instrumentation.h>
2424
#include <xrpl/ledger/CredentialHelpers.h>
25+
#include <xrpl/ledger/Credit.h>
2526
#include <xrpl/ledger/ReadView.h>
2627
#include <xrpl/ledger/View.h>
2728
#include <xrpl/protocol/Feature.h>
@@ -1361,12 +1362,58 @@ checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag)
13611362
return tesSUCCESS;
13621363
}
13631364

1365+
/*
1366+
* Checks if a withdrawal amount into the destination account exceeds
1367+
* any applicable receiving limit.
1368+
* Called by VaultWithdraw and LoanBrokerCoverWithdraw.
1369+
*
1370+
* IOU : Performs the trustline check against the destination account's
1371+
* credit limit to ensure the account's trust maximum is not exceeded.
1372+
*
1373+
* MPT: The limit check is effectively skipped (returns true). This is
1374+
* because MPT MaximumAmount relates to token supply, and withdrawal does not
1375+
* involve minting new tokens that could exceed the global cap.
1376+
* On withdrawal, tokens are simply transferred from the vault's pseudo-account
1377+
* to the destination account. Since no new MPT tokens are minted during this
1378+
* transfer, the withdrawal cannot violate the MPT MaximumAmount/supply cap
1379+
* even if `from` is the issuer.
1380+
*/
1381+
static TER
1382+
withdrawToDestExceedsLimit(
1383+
ReadView const& view,
1384+
AccountID const& from,
1385+
AccountID const& to,
1386+
STAmount const& amount)
1387+
{
1388+
auto const& issuer = amount.getIssuer();
1389+
if (from == to || to == issuer || isXRP(issuer))
1390+
return tesSUCCESS;
1391+
1392+
return std::visit(
1393+
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
1394+
if constexpr (std::is_same_v<TIss, Issue>)
1395+
{
1396+
auto const& currency = issue.currency;
1397+
auto const owed = creditBalance(view, to, issuer, currency);
1398+
if (owed <= beast::zero)
1399+
{
1400+
auto const limit = creditLimit(view, to, issuer, currency);
1401+
if (-owed >= limit || amount > (limit + owed))
1402+
return tecNO_LINE;
1403+
}
1404+
}
1405+
return tesSUCCESS;
1406+
},
1407+
amount.asset().value());
1408+
}
1409+
13641410
[[nodiscard]] TER
13651411
canWithdraw(
13661412
AccountID const& from,
13671413
ReadView const& view,
13681414
AccountID const& to,
13691415
SLE::const_ref toSle,
1416+
STAmount const& amount,
13701417
bool hasDestinationTag)
13711418
{
13721419
if (auto const ret = checkDestinationAndTag(toSle, hasDestinationTag))
@@ -1381,19 +1428,20 @@ canWithdraw(
13811428
return tecNO_PERMISSION;
13821429
}
13831430

1384-
return tesSUCCESS;
1431+
return withdrawToDestExceedsLimit(view, from, to, amount);
13851432
}
13861433

13871434
[[nodiscard]] TER
13881435
canWithdraw(
13891436
AccountID const& from,
13901437
ReadView const& view,
13911438
AccountID const& to,
1439+
STAmount const& amount,
13921440
bool hasDestinationTag)
13931441
{
13941442
auto const toSle = view.read(keylet::account(to));
13951443

1396-
return canWithdraw(from, view, to, toSle, hasDestinationTag);
1444+
return canWithdraw(from, view, to, toSle, amount, hasDestinationTag);
13971445
}
13981446

13991447
[[nodiscard]] TER
@@ -1402,7 +1450,8 @@ canWithdraw(ReadView const& view, STTx const& tx)
14021450
auto const from = tx[sfAccount];
14031451
auto const to = tx[~sfDestination].value_or(from);
14041452

1405-
return canWithdraw(from, view, to, tx.isFieldPresent(sfDestinationTag));
1453+
return canWithdraw(
1454+
from, view, to, tx[sfAmount], tx.isFieldPresent(sfDestinationTag));
14061455
}
14071456

14081457
TER

0 commit comments

Comments
 (0)