xrpld
Loading...
Searching...
No Matches
EscrowHelpers.h
1#pragma once
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/beast/utility/Journal.h>
5#include <xrpl/beast/utility/instrumentation.h>
6#include <xrpl/ledger/ApplyView.h>
7#include <xrpl/ledger/helpers/AccountRootHelpers.h>
8#include <xrpl/ledger/helpers/MPTokenHelpers.h>
9#include <xrpl/ledger/helpers/RippleStateHelpers.h>
10#include <xrpl/ledger/helpers/SponsorHelpers.h>
11#include <xrpl/ledger/helpers/TokenHelpers.h>
12#include <xrpl/protocol/AccountID.h>
13#include <xrpl/protocol/Concepts.h>
14#include <xrpl/protocol/Feature.h>
15#include <xrpl/protocol/Indexes.h>
16#include <xrpl/protocol/Issue.h>
17#include <xrpl/protocol/Keylet.h>
18#include <xrpl/protocol/LedgerFormats.h>
19#include <xrpl/protocol/MPTAmount.h>
20#include <xrpl/protocol/MPTIssue.h>
21#include <xrpl/protocol/Rate.h>
22#include <xrpl/protocol/SField.h>
23#include <xrpl/protocol/STAmount.h>
24#include <xrpl/protocol/STLedgerEntry.h>
25#include <xrpl/protocol/TER.h>
26#include <xrpl/protocol/UintTypes.h>
27
28namespace xrpl {
29
30template <ValidIssueType T>
31TER
34 Rate lockedRate,
35 SLE::ref sleDest,
36 XRPAmount xrpBalance,
37 STAmount const& amount,
38 AccountID const& issuer,
39 AccountID const& sender,
40 AccountID const& receiver,
41 bool createAsset,
42 beast::Journal journal);
43
44template <>
45inline TER
48 Rate lockedRate,
49 SLE::ref sleDest,
50 XRPAmount xrpBalance,
51 STAmount const& amount,
52 AccountID const& issuer,
53 AccountID const& sender,
54 AccountID const& receiver,
55 bool createAsset,
56 beast::Journal journal)
57{
58 auto const& issue = amount.get<Issue>();
59 Keylet const trustLineKey = keylet::trustLine(receiver, issue);
60 bool const recvLow = issuer > receiver;
61 bool const senderIssuer = issuer == sender;
62 bool const receiverIssuer = issuer == receiver;
63
64 if (senderIssuer)
65 return tecINTERNAL; // LCOV_EXCL_LINE
66
67 if (receiverIssuer)
68 return tesSUCCESS;
69
70 if (!ctx.view.exists(trustLineKey) && createAsset)
71 {
72 // Can the account cover the trust line's reserve?
73 auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
74 if (!sponsorSle)
75 return sponsorSle.error(); // LCOV_EXCL_LINE
76
77 if (auto const ret = checkReserve(
78 ctx,
79 sleDest,
80 xrpBalance,
81 *sponsorSle,
82 {.ownerCountDelta = 1},
83 journal,
85 !isTesSuccess(ret))
86 {
87 JLOG(journal.trace()) << "Trust line does not exist. "
88 "Insufficient reserve to create line.";
89 return ret;
90 }
91
92 Currency const currency = issue.currency;
93 STAmount initialBalance(issue);
94 initialBalance.get<Issue>().account = noAccount();
95
96 if (TER const ter = trustCreate(
97 ctx.view, // payment sandbox
98 recvLow, // is dest low?
99 issuer, // source
100 receiver, // destination
101 trustLineKey.key, // ledger index
102 sleDest, // Account to add to
103 false, // authorize account
104 !sleDest->isFlag(lsfDefaultRipple), //
105 false, // freeze trust line
106 false, // deep freeze trust line
107 initialBalance, // zero initial balance
108 Issue(currency, receiver), // limit of zero
109 0, // quality in
110 0, // quality out
111 *sponsorSle, // sponsor
112 journal); // journal
113 !isTesSuccess(ter))
114 {
115 return ter; // LCOV_EXCL_LINE
116 }
117
118 ctx.view.update(sleDest);
119 }
120
121 if (!ctx.view.exists(trustLineKey) && !receiverIssuer)
122 return tecNO_LINE;
123
124 auto const xferRate = transferRate(ctx.view, amount);
125 // update if issuer rate is less than locked rate
126 if (xferRate < lockedRate)
127 lockedRate = xferRate;
128
129 // Transfer Rate only applies when:
130 // 1. Issuer is not involved in the transfer (senderIssuer or
131 // receiverIssuer)
132 // 2. The locked rate is different from the parity rate
133
134 // NOTE: Transfer fee in escrow works a bit differently from a normal
135 // payment. In escrow, the fee is deducted from the locked/sending amount,
136 // whereas in a normal payment, the transfer fee is taken on top of the
137 // sending amount.
138 auto finalAmt = amount;
139 if ((!senderIssuer && !receiverIssuer) && lockedRate != kParityRate)
140 {
141 // compute transfer fee, if any
142 auto const xferFee =
143 amount.value() - divideRound(amount, lockedRate, amount.get<Issue>(), true);
144 // compute balance to transfer
145 finalAmt = amount.value() - xferFee;
146 }
147
148 // validate the line limit if the account submitting txn is not the receiver
149 // of the funds
150 if (!createAsset)
151 {
152 auto const sleRippleState = ctx.view.peek(trustLineKey);
153 if (!sleRippleState)
154 return tecINTERNAL; // LCOV_EXCL_LINE
155
156 // if the issuer is the high, then we use the low limit
157 // otherwise we use the high limit
158 STAmount const lineLimit =
159 sleRippleState->getFieldAmount(recvLow ? sfLowLimit : sfHighLimit);
160
161 STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance);
162
163 // flip the sign of the line balance if the issuer is not high
164 if (!recvLow)
165 lineBalance.negate();
166
167 // add the final amount to the line balance
168 lineBalance += finalAmt;
169
170 // if the transfer would exceed the line limit return tecLIMIT_EXCEEDED
171 if (lineLimit < lineBalance)
172 return tecLIMIT_EXCEEDED;
173 }
174
175 // if destination is not the issuer then transfer funds
176 if (!receiverIssuer)
177 {
178 auto const ter = directSendNoFee(ctx.view, issuer, receiver, finalAmt, true, journal);
179 if (!isTesSuccess(ter))
180 return ter; // LCOV_EXCL_LINE
181 }
182 return tesSUCCESS;
183}
184
185template <>
186inline TER
189 Rate lockedRate,
190 SLE::ref sleDest,
191 XRPAmount xrpBalance,
192 STAmount const& amount,
193 AccountID const& issuer,
194 AccountID const& sender,
195 AccountID const& receiver,
196 bool createAsset,
197 beast::Journal journal)
198{
199 bool const senderIssuer = issuer == sender;
200 bool const receiverIssuer = issuer == receiver;
201
202 auto const mptID = amount.get<MPTIssue>().getMptID();
203 auto const issuanceKey = keylet::mptokenIssuance(mptID);
204 auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver);
205 if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer)
206 {
207 auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
208 if (!sponsorSle)
209 return sponsorSle.error(); // LCOV_EXCL_LINE
210
211 if (auto const ret = checkReserve(
212 ctx, sleDest, xrpBalance, *sponsorSle, {.ownerCountDelta = 1}, journal);
213 !isTesSuccess(ret))
214 return ret;
215
216 if (auto const ter = createMPToken(ctx.view, mptID, receiver, *sponsorSle, 0);
217 !isTesSuccess(ter))
218 {
219 return ter; // LCOV_EXCL_LINE
220 }
221
222 // update owner count.
223 increaseOwnerCount(ctx.view, sleDest, *sponsorSle, 1, journal);
224 }
225
226 if (!ctx.view.exists(mptKeylet) && !receiverIssuer)
227 return tecNO_PERMISSION;
228
229 auto const xferRate = transferRate(ctx.view, amount);
230 // update if issuer rate is less than locked rate
231 if (xferRate < lockedRate)
232 lockedRate = xferRate;
233
234 // Transfer Rate only applies when:
235 // 1. Issuer is not involved in the transfer (senderIssuer or
236 // receiverIssuer)
237 // 2. The locked rate is different from the parity rate
238
239 // NOTE: Transfer fee in escrow works a bit differently from a normal
240 // payment. In escrow, the fee is deducted from the locked/sending amount,
241 // whereas in a normal payment, the transfer fee is taken on top of the
242 // sending amount.
243 auto finalAmt = amount;
244 if ((!senderIssuer && !receiverIssuer) && lockedRate != kParityRate)
245 {
246 if (ctx.view.rules().enabled(fixCleanup3_4_0))
247 {
248 XRPL_ASSERT(
249 lockedRate >= kParityRate,
250 "xrpl::escrowUnlockApplyHelper<MPTIssue> : lockedRate is at least parity");
251 // MPTs are integral, so round the delivered amount down and
252 // charge any fractional transfer fee to the escrowed amount.
253 auto const delivered =
254 mulRatio(amount.mpt(), kParityRate.value, lockedRate.value, false);
255 finalAmt = STAmount(amount.asset(), delivered.value());
256 }
257 else
258 {
259 // compute transfer fee, if any
260 auto const xferFee =
261 amount.value() - divideRound(amount, lockedRate, amount.asset(), true);
262 // compute balance to transfer
263 finalAmt = amount.value() - xferFee;
264 }
265 }
266 return unlockEscrowMPT(
267 ctx.view,
268 sender,
269 receiver,
270 finalAmt,
271 ctx.view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt,
272 journal);
273}
274
275} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream trace() const
Severity stream access functions.
Definition Journal.h:338
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void update(SLE::ref sle)=0
Indicate changes to a peeked SLE.
constexpr value_type const & value() const
Definition Asset.h:201
A currency issued by an account.
Definition Issue.h:18
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:180
constexpr TIss const & get() const
void negate()
Definition STAmount.h:586
Asset const & asset() const
Definition STAmount.h:496
MPTAmount mpt() const
Definition STAmount.cpp:301
STAmount const & value() const noexcept
Definition STAmount.h:610
std::shared_ptr< STLedgerEntry > const & ref
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:543
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Definition Indexes.cpp:537
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:253
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
TER createMPToken(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, SLE::ref sponsorSle, std::uint32_t const flags)
TER escrowUnlockApplyHelper< Issue >(ApplyViewContext ctx, Rate lockedRate, SLE::ref sleDest, XRPAmount xrpBalance, STAmount const &amount, AccountID const &issuer, AccountID const &sender, AccountID const &receiver, bool createAsset, beast::Journal journal)
void increaseOwnerCount(ApplyView &view, SLE::ref accountSle, SLE::ref sponsorSle, std::uint32_t count, beast::Journal j)
Increase owner-count fields when the caller supplies the sponsor.
TER escrowUnlockApplyHelper(ApplyViewContext ctx, Rate lockedRate, SLE::ref sleDest, XRPAmount xrpBalance, STAmount const &amount, AccountID const &issuer, AccountID const &sender, AccountID const &receiver, bool createAsset, beast::Journal journal)
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:42
TER escrowUnlockApplyHelper< MPTIssue >(ApplyViewContext ctx, Rate lockedRate, SLE::ref sleDest, XRPAmount xrpBalance, STAmount const &amount, AccountID const &issuer, AccountID const &sender, AccountID const &receiver, bool createAsset, beast::Journal journal)
TER unlockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, AccountID const &uGranteeID, STAmount const &netAmount, STAmount const &grossAmount, beast::Journal j)
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, SLE::ref sponsorSle, beast::Journal j)
Create a trust line.
std::expected< SLE::pointer, TER > getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::const_ref accountSle)
The transaction's reserve sponsor for the given account, if applicable.
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Rate const kParityRate
A transfer rate signifying a 1:1 exchange.
TER directSendNoFee(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static directSendNoFeeIOU if saAmount represents Issue.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
AccountID const & noAccount()
A placeholder for empty accounts.
TER checkReserve(ApplyViewContext ctx, SLE::const_ref accSle, XRPAmount accBalance, SLE::const_ref sponsorSle, Adjustment adj, beast::Journal j, TER insufReserveCode=tecINSUFFICIENT_RESERVE)
Check if an account has sufficient reserve.
bool isTesSuccess(TER x) noexcept
Definition TER.h:676
TERSubset< CanCvtToTER > TER
Definition TER.h:647
@ tecNO_LINE_INSUF_RESERVE
Definition TER.h:295
@ tecINTERNAL
Definition TER.h:313
@ tecNO_LINE
Definition TER.h:304
@ tecLIMIT_EXCEEDED
Definition TER.h:364
@ tecNO_PERMISSION
Definition TER.h:308
STAmount divideRound(STAmount const &amount, Rate const &rate, bool roundUp)
Definition Rate2.cpp:80
@ tesSUCCESS
Definition TER.h:245
Bundles the mutable ledger view and the transaction being applied.
Definition ApplyView.h:444
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:20
Represents a transfer rate.
Definition Rate.h:21
std::uint32_t value
Definition Rate.h:22