1#include <xrpl/ledger/helpers/TokenHelpers.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/beast/utility/instrumentation.h>
5#include <xrpl/ledger/helpers/AccountRootHelpers.h>
6#include <xrpl/ledger/helpers/MPTokenHelpers.h>
7#include <xrpl/ledger/helpers/RippleStateHelpers.h>
8#include <xrpl/protocol/Feature.h>
9#include <xrpl/protocol/Indexes.h>
10#include <xrpl/protocol/LedgerFormats.h>
11#include <xrpl/protocol/Protocol.h>
12#include <xrpl/protocol/Quality.h>
13#include <xrpl/protocol/TER.h>
14#include <xrpl/protocol/st.h>
63 [&](
auto const& issue) {
return isFrozen(view, account, issue, depth); }, asset.
value());
82 [&](
auto const& issue) {
return checkFrozen(view, account, issue); }, asset.
value());
91 for (
auto const& account : accounts)
125 return isFrozen(view, account, mptIssue, depth);
132 [&](
auto const& issue) {
return isDeepFrozen(view, account, issue, depth); },
173 if (
isFrozen(view, account, currency, issuer) ||
188 if (sleIssuer->isFieldPresent(sfAMMID))
196 (*sleAmm)[sfAsset].get<Issue>(),
197 (*sleAmm)[sfAsset2].get<Issue>()))
215 bool includeOppositeLimit,
221 amount = sle->getFieldAmount(sfBalance);
222 bool const accountHigh = account > issuer;
223 auto const& oppositeField = accountHigh ? sfLowLimit : sfHighLimit;
229 if (includeOppositeLimit)
231 amount += sle->getFieldAmount(oppositeField);
233 amount.setIssuer(issuer);
237 amount.clear(
Issue{currency, issuer});
240 JLOG(j.
trace()) <<
"getTrustLineBalance:" <<
" account=" <<
to_string(account)
241 <<
" amount=" << amount.getFullText();
262 bool const returnSpendable = (includeFullBalance ==
shFULL_BALANCE);
263 if (returnSpendable && account == issuer)
287 view, account, issue.
currency, issue.
account, zeroIfFrozen, j, includeFullBalance);
300 bool const returnSpendable = (includeFullBalance ==
shFULL_BALANCE);
302 if (returnSpendable && account == mptIssue.
getIssuer())
315 issuance->at(sfOutstandingAmount)};
324 amount.clear(mptIssue);
328 amount.clear(mptIssue);
332 amount =
STAmount{mptIssue, sleMpt->getFieldU64(sfMPTAmount)};
341 amount.clear(mptIssue);
349 if (sleIssuance && sleIssuance->isFlag(lsfMPTRequireAuth) &&
350 !sleMpt->isFlag(lsfMPTAuthorized))
351 amount.clear(mptIssue);
358[[nodiscard]] STAmount
372 return accountHolds(view, account, value, zeroIfFrozen, j, includeFullBalance);
377 view, account, value, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance);
412 amount.asset().value());
434 if (!issuer->isFlag(lsfDefaultRipple))
490 return requireAuth(view, issue_, account, authType);
529 !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer,
530 "xrpl::rippleCreditIOU : matching issuer or don't care");
534 XRPL_ASSERT(uSenderID != uReceiverID,
"xrpl::rippleCreditIOU : sender is not receiver");
536 bool const bSenderHigh = uSenderID > uReceiverID;
537 auto const index =
keylet::line(uSenderID, uReceiverID, currency);
540 !
isXRP(uSenderID) && uSenderID !=
noAccount(),
"xrpl::rippleCreditIOU : sender is not XRP");
543 "xrpl::rippleCreditIOU : receiver is not XRP");
546 if (
auto const sleRippleState = view.
peek(index))
548 STAmount saBalance = sleRippleState->getFieldAmount(sfBalance);
553 view.
creditHook(uSenderID, uReceiverID, saAmount, saBalance);
555 STAmount const saBefore = saBalance;
557 saBalance -= saAmount;
559 JLOG(j.
trace()) <<
"rippleCreditIOU: " <<
to_string(uSenderID) <<
" -> "
564 std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags));
565 bool bDelete =
false;
569 if (saBefore > beast::zero
571 && saBalance <= beast::zero
573 && ((uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) != 0u)
575 &&
static_cast<bool>(uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
578 ((uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) == 0u) &&
579 !sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
581 && (sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) == 0u)
584 (sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut) == 0u))
591 sleRippleState->setFieldU32(
592 sfFlags, uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
596 && ((uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)) == 0u);
604 sleRippleState->setFieldAmount(sfBalance, saBalance);
612 bSenderHigh ? uReceiverID : uSenderID,
613 !bSenderHigh ? uReceiverID : uSenderID,
617 view.
update(sleRippleState);
621 STAmount const saReceiverLimit(
Issue{currency, uReceiverID});
626 JLOG(j.
debug()) <<
"rippleCreditIOU: "
635 bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0;
668 auto const& issuer = saAmount.
getIssuer();
672 "xrpl::rippleSendIOU : neither sender nor receiver is XRP");
673 XRPL_ASSERT(uSenderID != uReceiverID,
"xrpl::rippleSendIOU : sender is not receiver");
675 if (uSenderID == issuer || uReceiverID == issuer || issuer ==
noAccount())
678 auto const ter =
rippleCreditIOU(view, uSenderID, uReceiverID, saAmount,
false, j);
692 JLOG(j.
debug()) <<
"rippleSendIOU> " <<
to_string(uSenderID) <<
" - > "
699 terResult =
rippleCreditIOU(view, uSenderID, issuer, saActual,
true, j);
719 XRPL_ASSERT(!
isXRP(senderID),
"xrpl::rippleSendMultiIOU : sender is not XRP");
723 actual = takeFromSender;
726 for (
auto const& r : receivers)
728 auto const& receiverID = r.first;
729 STAmount const amount{issue, r.second};
734 if (!amount || (senderID == receiverID))
737 XRPL_ASSERT(!
isXRP(receiverID),
"xrpl::rippleSendMultiIOU : receiver is not XRP");
739 if (senderID == issuer || receiverID == issuer || issuer ==
noAccount())
742 if (
auto const ter =
rippleCreditIOU(view, senderID, receiverID, amount,
false, j))
758 actual += actualSend;
759 takeFromSender += actualSend;
761 JLOG(j.
debug()) <<
"rippleSendMultiIOU> " <<
to_string(senderID) <<
" - > "
762 <<
to_string(receiverID) <<
" : deliver=" << amount.getFullText()
769 if (senderID != issuer && takeFromSender)
771 if (
TER const terResult =
rippleCreditIOU(view, senderID, issuer, takeFromSender,
true, j))
799 "xrpl::accountSendIOU : minimum amount and not MPT");
806 if (!saAmount || (uSenderID == uReceiverID))
813 JLOG(j.
trace()) <<
"accountSendIOU: " <<
to_string(uSenderID) <<
" -> "
816 return rippleSendIOU(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
831 if (
auto stream = j.
trace())
842 stream <<
"accountSendIOU> " <<
to_string(uSenderID) <<
" (" << sender_bal <<
") -> "
843 <<
to_string(uReceiverID) <<
" (" << receiver_bal
878 if (
auto stream = j.
trace())
889 stream <<
"accountSendIOU< " <<
to_string(uSenderID) <<
" (" << sender_bal <<
") -> "
890 <<
to_string(uReceiverID) <<
" (" << receiver_bal
907 receivers.
size() > 1,
"xrpl::accountSendMultiIOU",
"multiple recipients provided");
912 JLOG(j.
trace()) <<
"accountSendMultiIOU: " <<
to_string(senderID) <<
" sending "
913 << receivers.
size() <<
" IOUs";
927 if (
auto stream = j.
trace())
934 stream <<
"accountSendMultiIOU> " <<
to_string(senderID) <<
" (" << sender_bal <<
") -> "
935 << receivers.
size() <<
" receivers.";
940 for (
auto const& r : receivers)
942 auto const& receiverID = r.first;
943 STAmount const amount{issue, r.second};
945 if (amount < beast::zero)
953 if (!amount || (senderID == receiverID))
959 if (
auto stream = j.
trace())
966 stream <<
"accountSendMultiIOU> " <<
to_string(senderID) <<
" -> "
967 <<
to_string(receiverID) <<
" (" << receiver_bal
968 <<
") : " << amount.getFullText();
981 takeFromSender += amount;
984 if (
auto stream = j.
trace())
991 stream <<
"accountSendMultiIOU< " <<
to_string(senderID) <<
" -> "
992 <<
to_string(receiverID) <<
" (" << receiver_bal
993 <<
") : " << amount.getFullText();
1011 if (
auto stream = j.
trace())
1018 stream <<
"accountSendMultiIOU< " <<
to_string(senderID) <<
" (" << sender_bal <<
") -> "
1019 << receivers.
size() <<
" receivers.";
1034 auto const& issuer = saAmount.
getIssuer();
1035 auto sleIssuance = view.
peek(mptID);
1038 if (uSenderID == issuer)
1040 (*sleIssuance)[sfOutstandingAmount] += saAmount.
mpt().
value();
1041 view.
update(sleIssuance);
1046 if (
auto sle = view.
peek(mptokenID))
1048 auto const amt = sle->getFieldU64(sfMPTAmount);
1049 auto const pay = saAmount.
mpt().
value();
1052 (*sle)[sfMPTAmount] = amt - pay;
1061 if (uReceiverID == issuer)
1063 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
1064 auto const redeem = saAmount.
mpt().
value();
1065 if (outstanding >= redeem)
1067 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
1068 view.
update(sleIssuance);
1078 if (
auto sle = view.
peek(mptokenID))
1080 (*sle)[sfMPTAmount] += saAmount.
mpt().
value();
1102 XRPL_ASSERT(uSenderID != uReceiverID,
"xrpl::rippleSendMPT : sender is not receiver");
1105 auto const& issuer = saAmount.
getIssuer();
1111 if (uSenderID == issuer || uReceiverID == issuer)
1115 if (uSenderID == issuer)
1117 auto const sendAmount = saAmount.
mpt().
value();
1118 auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(
maxMPTokenAmount);
1119 if (sendAmount > maximumAmount ||
1120 sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
1125 auto const ter =
rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j);
1128 saActual = saAmount;
1137 JLOG(j.
debug()) <<
"rippleSendMPT> " <<
to_string(uSenderID) <<
" - > "
1141 if (
auto const terResult =
rippleCreditMPT(view, issuer, uReceiverID, saAmount, j);
1158 auto const& issuer = mptIssue.
getIssuer();
1173 std::uint64_t const outstandingAmount = sle->getFieldU64(sfOutstandingAmount);
1180 actual = takeFromSender;
1182 for (
auto const& [receiverID, amt] : receivers)
1184 STAmount const amount{mptIssue, amt};
1186 if (amount < beast::zero)
1189 if (!amount || senderID == receiverID)
1192 if (senderID == issuer || receiverID == issuer)
1194 if (senderID == issuer)
1197 takeFromSender == beast::zero,
1198 "xrpl::rippleSendMultiMPT",
1199 "sender == issuer, takeFromSender == zero");
1209 bool const exceedsMaximumAmount =
1211 sendAmount > maximumAmount ||
1213 totalSendAmount > maximumAmount - sendAmount ||
1215 outstandingAmount > maximumAmount - sendAmount - totalSendAmount;
1217 if (exceedsMaximumAmount)
1219 totalSendAmount += sendAmount;
1227 if (sendAmount > maximumAmount ||
1228 outstandingAmount > maximumAmount - sendAmount)
1234 if (
auto const ter =
rippleCreditMPT(view, senderID, receiverID, amount, j))
1247 actual += actualSend;
1248 takeFromSender += actualSend;
1250 JLOG(j.
debug()) <<
"rippleSendMultiMPT> " <<
to_string(senderID) <<
" - > "
1251 <<
to_string(receiverID) <<
" : deliver=" << amount.getFullText()
1254 if (
auto const terResult =
rippleCreditMPT(view, issuer, receiverID, amount, j))
1257 if (senderID != issuer && takeFromSender)
1259 if (
TER const terResult =
rippleCreditMPT(view, senderID, issuer, takeFromSender, j))
1277 "xrpl::accountSendMPT : minimum amount and MPT");
1282 if (!saAmount || (uSenderID == uReceiverID))
1287 return rippleSendMPT(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
1323 return rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j);
1327 XRPL_ASSERT(!bCheckIssuer,
"xrpl::rippleCredit : not checking issuer");
1347 return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, waiveFee);
1351 return accountSendMPT(view, uSenderID, uReceiverID, saAmount, j, waiveFee);
1367 receivers.
size() > 1,
"xrpl::accountSendMulti",
"multiple recipients provided");
1390 XRPL_ASSERT(from != beast::zero,
"xrpl::transferXRP : nonzero from account");
1391 XRPL_ASSERT(to != beast::zero,
"xrpl::transferXRP : nonzero to account");
1392 XRPL_ASSERT(from != to,
"xrpl::transferXRP : sender is not receiver");
1393 XRPL_ASSERT(amount.native(),
"xrpl::transferXRP : amount is XRP");
1397 if (!sender || !receiver)
1401 <<
") : " << amount.getFullText();
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
Writeable view to a ledger, for applying a transaction.
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual void creditHook(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance)
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
constexpr value_type const & value() const
A currency issued by an account.
AccountID const & getIssuer() const
constexpr value_type value() const
Returns the underlying value.
constexpr MPTID const & getMptID() const
AccountID const & getIssuer() const
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual STAmount balanceHook(AccountID const &account, AccountID const &issuer, STAmount const &amount) const
virtual bool open() const =0
Returns true if this reflects an open ledger.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
constexpr bool holds() const noexcept
constexpr TIss const & get() const
std::string getFullText() const override
static constexpr std::uint64_t cMaxValue
void setIssuer(AccountID const &uIssuer)
static int const cMaxOffset
Currency const & getCurrency() const
bool native() const noexcept
Asset const & asset() const
AccountID const & getIssuer() const
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const_pointer
void setFieldAmount(SField const &field, STAmount const &)
STAmount const & getFieldAmount(SField const &field) const
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Keylet line(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
TER checkDeepFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
FreezeHandling
Controls the treatment of frozen account balances.
SpendableHandling
Controls whether to include the account's full spendable balance.
bool isXRP(AccountID const &c)
bool isIndividualFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue)
std::string to_string(base_uint< Bits, Tag > const &a)
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uQualityIn, std::uint32_t uQualityOut, beast::Journal j)
Create a trust line.
static TER accountSendMultiIOU(ApplyView &view, AccountID const &senderID, Issue const &issue, MultiplePaymentDestinations const &receivers, beast::Journal j, WaiveTransferFee waiveFee)
static TER rippleSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
TER addEmptyHolding(ApplyView &view, AccountID const &accountID, XRPAmount priorBalance, MPTIssue const &mptIssue, beast::Journal journal)
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
static TER accountSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
static TER rippleCreditMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j)
STAmount multiply(STAmount const &amount, Rate const &rate)
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
static TER rippleCreditIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
bool isDeepFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
static TER rippleSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
static TER rippleSendMultiMPT(ApplyView &view, AccountID const &senderID, MPTIssue const &mptIssue, MultiplePaymentDestinations const &receivers, STAmount &actual, beast::Journal j, WaiveTransferFee waiveFee)
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Check if the issuer has the global freeze flag set.
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, int depth=0)
static TER rippleSendMultiIOU(ApplyView &view, AccountID const &senderID, Issue const &issue, MultiplePaymentDestinations const &receivers, STAmount &actual, beast::Journal j, WaiveTransferFee waiveFee)
TERSubset< CanCvtToTER > TER
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
TER canAddHolding(ReadView const &view, MPTIssue const &mptIssue)
static STAmount getTrustLineBalance(ReadView const &view, SLE::const_ref sle, AccountID const &account, Currency const ¤cy, AccountID const &issuer, bool includeOppositeLimit, beast::Journal j)
AuthHandling
Controls the treatment of unauthorized MPT balances.
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
static SLE::const_pointer getLineIfUsable(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
AccountID const & noAccount()
A placeholder for empty accounts.
static TER accountSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
bool isTesSuccess(TER x) noexcept
AccountID const & xrpAccount()
Compute AccountID from public key.
static TER accountSendMultiMPT(ApplyView &view, AccountID const &senderID, MPTIssue const &mptIssue, MultiplePaymentDestinations const &receivers, beast::Journal j, WaiveTransferFee waiveFee)
TER accountSendMulti(ApplyView &view, AccountID const &senderID, Asset const &asset, MultiplePaymentDestinations const &receivers, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Like accountSend, except one account is sending multiple payments (with the same asset!...
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, MPTIssue const &mptIssue, beast::Journal journal)
bool isLPTokenFrozen(ReadView const &view, AccountID const &account, Issue const &asset, Issue const &asset2)
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, int depth=0)
Check if the account lacks required authorization for MPT.
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.