rippled
Loading...
Searching...
No Matches
VaultDeposit.cpp
1#include <xrpld/app/tx/detail/MPTokenAuthorize.h>
2#include <xrpld/app/tx/detail/VaultDeposit.h>
3
4#include <xrpl/ledger/CredentialHelpers.h>
5#include <xrpl/ledger/View.h>
6#include <xrpl/protocol/Feature.h>
7#include <xrpl/protocol/Indexes.h>
8#include <xrpl/protocol/LedgerFormats.h>
9#include <xrpl/protocol/MPTIssue.h>
10#include <xrpl/protocol/SField.h>
11#include <xrpl/protocol/STNumber.h>
12#include <xrpl/protocol/TER.h>
13#include <xrpl/protocol/TxFlags.h>
14
15namespace xrpl {
16
19{
20 if (ctx.tx[sfVaultID] == beast::zero)
21 {
22 JLOG(ctx.j.debug()) << "VaultDeposit: zero/empty vault ID.";
23 return temMALFORMED;
24 }
25
26 if (ctx.tx[sfAmount] <= beast::zero)
27 return temBAD_AMOUNT;
28
29 return tesSUCCESS;
30}
31
32TER
34{
35 auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
36 if (!vault)
37 return tecNO_ENTRY;
38
39 auto const& account = ctx.tx[sfAccount];
40 auto const assets = ctx.tx[sfAmount];
41 auto const vaultAsset = vault->at(sfAsset);
42 if (assets.asset() != vaultAsset)
43 return tecWRONG_ASSET;
44
45 auto const& vaultAccount = vault->at(sfAccount);
46 if (auto ter = canTransfer(ctx.view, vaultAsset, account, vaultAccount);
47 !isTesSuccess(ter))
48 {
49 JLOG(ctx.j.debug())
50 << "VaultDeposit: vault assets are non-transferable.";
51 return ter;
52 }
53
54 auto const mptIssuanceID = vault->at(sfShareMPTID);
55 auto const vaultShare = MPTIssue(mptIssuanceID);
56 if (vaultShare == assets.asset())
57 {
58 // LCOV_EXCL_START
59 JLOG(ctx.j.error())
60 << "VaultDeposit: vault shares and assets cannot be same.";
61 return tefINTERNAL;
62 // LCOV_EXCL_STOP
63 }
64
65 auto const sleIssuance = ctx.view.read(keylet::mptIssuance(mptIssuanceID));
66 if (!sleIssuance)
67 {
68 // LCOV_EXCL_START
69 JLOG(ctx.j.error())
70 << "VaultDeposit: missing issuance of vault shares.";
71 return tefINTERNAL;
72 // LCOV_EXCL_STOP
73 }
74
75 if (sleIssuance->isFlag(lsfMPTLocked))
76 {
77 // LCOV_EXCL_START
78 JLOG(ctx.j.error())
79 << "VaultDeposit: issuance of vault shares is locked.";
80 return tefINTERNAL;
81 // LCOV_EXCL_STOP
82 }
83
84 // Cannot deposit inside Vault an Asset frozen for the depositor
85 if (isFrozen(ctx.view, account, vaultAsset))
86 return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
87
88 // Cannot deposit if the shares of the vault are frozen
89 if (isFrozen(ctx.view, account, vaultShare))
90 return tecLOCKED;
91
92 if (vault->isFlag(lsfVaultPrivate) && account != vault->at(sfOwner))
93 {
94 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
95 // Since this is a private vault and the account is not its owner, we
96 // perform authorization check based on DomainID read from sleIssuance.
97 // Had the vault shares been a regular MPToken, we would allow
98 // authorization granted by the Issuer explicitly, but Vault uses Issuer
99 // pseudo-account, which cannot grant an authorization.
100 if (maybeDomainID)
101 {
102 // As per validDomain documentation, we suppress tecEXPIRED error
103 // here, so we can delete any expired credentials inside doApply.
104 if (auto const err =
105 credentials::validDomain(ctx.view, *maybeDomainID, account);
106 !isTesSuccess(err) && err != tecEXPIRED)
107 return err;
108 }
109 else
110 return tecNO_AUTH;
111 }
112
113 // Source MPToken must exist (if asset is an MPT)
114 if (auto const ter = requireAuth(ctx.view, vaultAsset, account);
115 !isTesSuccess(ter))
116 return ter;
117
118 // Asset issuer does not have any balance, they can just create funds by
119 // depositing in the vault.
120 if ((vaultAsset.native() || vaultAsset.getIssuer() != account) &&
122 ctx.view,
123 account,
124 vaultAsset,
127 ctx.j) < assets)
129
130 return tesSUCCESS;
131}
132
133TER
135{
136 auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID]));
137 if (!vault)
138 return tefINTERNAL; // LCOV_EXCL_LINE
139
140 auto const amount = ctx_.tx[sfAmount];
141 // Make sure the depositor can hold shares.
142 auto const mptIssuanceID = (*vault)[sfShareMPTID];
143 auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID));
144 if (!sleIssuance)
145 {
146 // LCOV_EXCL_START
147 JLOG(j_.error()) << "VaultDeposit: missing issuance of vault shares.";
148 return tefINTERNAL;
149 // LCOV_EXCL_STOP
150 }
151
152 auto const& vaultAccount = vault->at(sfAccount);
153 // Note, vault owner is always authorized
154 if (vault->isFlag(lsfVaultPrivate) && account_ != vault->at(sfOwner))
155 {
156 if (auto const err = enforceMPTokenAuthorization(
157 ctx_.view(), mptIssuanceID, account_, mPriorBalance, j_);
158 !isTesSuccess(err))
159 return err;
160 }
161 else // !vault->isFlag(lsfVaultPrivate) || account_ == vault->at(sfOwner)
162 {
163 // No authorization needed, but must ensure there is MPToken
164 if (!view().exists(keylet::mptoken(mptIssuanceID, account_)))
165 {
166 if (auto const err = authorizeMPToken(
167 view(),
169 mptIssuanceID->value(),
170 account_,
171 ctx_.journal);
172 !isTesSuccess(err))
173 return err;
174 }
175
176 // If the vault is private, set the authorized flag for the vault owner
177 if (vault->isFlag(lsfVaultPrivate))
178 {
179 // This follows from the reverse of the outer enclosing if condition
180 XRPL_ASSERT(
181 account_ == vault->at(sfOwner),
182 "xrpl::VaultDeposit::doApply : account is owner");
183 if (auto const err = authorizeMPToken(
184 view(),
185 mPriorBalance, // priorBalance
186 mptIssuanceID->value(), // mptIssuanceID
187 sleIssuance->at(sfIssuer), // account
189 {}, // flags
190 account_ // holderID
191 );
192 !isTesSuccess(err))
193 return err;
194 }
195 }
196
197 STAmount sharesCreated = {vault->at(sfShareMPTID)}, assetsDeposited;
198 try
199 {
200 // Compute exchange before transferring any amounts.
201 {
202 auto const maybeShares =
203 assetsToSharesDeposit(vault, sleIssuance, amount);
204 if (!maybeShares)
205 return tecINTERNAL; // LCOV_EXCL_LINE
206 sharesCreated = *maybeShares;
207 }
208 if (sharesCreated == beast::zero)
209 return tecPRECISION_LOSS;
210
211 auto const maybeAssets =
212 sharesToAssetsDeposit(vault, sleIssuance, sharesCreated);
213 if (!maybeAssets)
214 return tecINTERNAL; // LCOV_EXCL_LINE
215 else if (*maybeAssets > amount)
216 {
217 // LCOV_EXCL_START
218 JLOG(j_.error()) << "VaultDeposit: would take more than offered.";
219 return tecINTERNAL;
220 // LCOV_EXCL_STOP
221 }
222 assetsDeposited = *maybeAssets;
223 }
224 catch (std::overflow_error const&)
225 {
226 // It's easy to hit this exception from Number with large enough Scale
227 // so we avoid spamming the log and only use debug here.
228 JLOG(j_.debug()) //
229 << "VaultDeposit: overflow error with"
230 << " scale=" << (int)vault->at(sfScale).value() //
231 << ", assetsTotal=" << vault->at(sfAssetsTotal).value()
232 << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount)
233 << ", amount=" << amount;
234 return tecPATH_DRY;
235 }
236
237 XRPL_ASSERT(
238 sharesCreated.asset() != assetsDeposited.asset(),
239 "xrpl::VaultDeposit::doApply : assets are not shares");
240
241 vault->at(sfAssetsTotal) += assetsDeposited;
242 vault->at(sfAssetsAvailable) += assetsDeposited;
243 view().update(vault);
244
245 // A deposit must not push the vault over its limit.
246 auto const maximum = *vault->at(sfAssetsMaximum);
247 if (maximum != 0 && *vault->at(sfAssetsTotal) > maximum)
248 return tecLIMIT_EXCEEDED;
249
250 // Transfer assets from depositor to vault.
251 if (auto const ter = accountSend(
252 view(),
253 account_,
254 vaultAccount,
255 assetsDeposited,
256 j_,
258 !isTesSuccess(ter))
259 return ter;
260
261 // Sanity check
262 if (accountHolds(
263 view(),
264 account_,
265 assetsDeposited.asset(),
268 j_) < beast::zero)
269 {
270 // LCOV_EXCL_START
271 JLOG(j_.error()) << "VaultDeposit: negative balance of account assets.";
272 return tefINTERNAL;
273 // LCOV_EXCL_STOP
274 }
275
276 // Transfer shares from vault to depositor.
277 if (auto const ter = accountSend(
278 view(),
279 vaultAccount,
280 account_,
281 sharesCreated,
282 j_,
284 !isTesSuccess(ter))
285 return ter;
286
287 return tesSUCCESS;
288}
289
290} // namespace xrpl
Stream error() const
Definition Journal.h:327
Stream debug() const
Definition Journal.h:309
STTx const & tx
beast::Journal const journal
ApplyView & view()
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
A currency issued by an account.
Definition Issue.h:14
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
Asset const & asset() const
Definition STAmount.h:475
AccountID const account_
Definition Transactor.h:128
beast::Journal const j_
Definition Transactor.h:126
ApplyView & view()
Definition Transactor.h:144
XRPAmount mPriorBalance
Definition Transactor.h:129
ApplyContext & ctx_
Definition Transactor.h:124
TER doApply() override
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
constexpr value_type value() const
Returns the underlying value.
Definition XRPAmount.h:220
TER validDomain(ReadView const &view, uint256 domainID, AccountID const &subject)
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:508
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:546
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:522
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::optional< STAmount > sharesToAssetsDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:3568
@ fhZERO_IF_FROZEN
Definition View.h:59
@ fhIGNORE_FREEZE
Definition View.h:59
@ tefINTERNAL
Definition TER.h:154
TER authorizeMPToken(ApplyView &view, XRPAmount const &priorBalance, MPTID const &mptIssuanceID, AccountID const &account, beast::Journal journal, std::uint32_t flags=0, std::optional< AccountID > holderID=std::nullopt)
Definition View.cpp:1515
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:461
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.
Definition View.cpp:2777
std::optional< STAmount > assetsToSharesDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition View.cpp:3540
@ ahIGNORE_AUTH
Definition View.h:62
@ ahZERO_IF_UNAUTHORIZED
Definition View.h:62
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:3096
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:3325
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:228
@ temMALFORMED
Definition TER.h:68
@ temBAD_AMOUNT
Definition TER.h:70
bool isTesSuccess(TER x) noexcept
Definition TER.h:659
@ tecWRONG_ASSET
Definition TER.h:342
@ tecLOCKED
Definition TER.h:340
@ tecNO_ENTRY
Definition TER.h:288
@ tecPATH_DRY
Definition TER.h:276
@ tecNO_AUTH
Definition TER.h:282
@ tecINTERNAL
Definition TER.h:292
@ tecFROZEN
Definition TER.h:285
@ tecINSUFFICIENT_FUNDS
Definition TER.h:307
@ tecEXPIRED
Definition TER.h:296
@ tecPRECISION_LOSS
Definition TER.h:345
@ tecLIMIT_EXCEEDED
Definition TER.h:343
@ lsfVaultPrivate
@ lsfMPTLocked
TER enforceMPTokenAuthorization(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, XRPAmount const &priorBalance, beast::Journal j)
Enforce account has MPToken to match its authorization.
Definition View.cpp:3224
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:590
@ tesSUCCESS
Definition TER.h:226
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:61
ReadView const & view
Definition Transactor.h:64
beast::Journal const j
Definition Transactor.h:69
State information when preflighting a tx.
Definition Transactor.h:16
beast::Journal const j
Definition Transactor.h:23