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/STTakesAsset.h>
13#include <xrpl/protocol/TER.h>
14#include <xrpl/protocol/TxFlags.h>
15
16namespace xrpl {
17
20{
21 if (ctx.tx[sfVaultID] == beast::zero)
22 {
23 JLOG(ctx.j.debug()) << "VaultDeposit: zero/empty vault ID.";
24 return temMALFORMED;
25 }
26
27 if (ctx.tx[sfAmount] <= beast::zero)
28 return temBAD_AMOUNT;
29
30 return tesSUCCESS;
31}
32
33TER
35{
36 auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
37 if (!vault)
38 return tecNO_ENTRY;
39
40 auto const& account = ctx.tx[sfAccount];
41 auto const assets = ctx.tx[sfAmount];
42 auto const vaultAsset = vault->at(sfAsset);
43 if (assets.asset() != vaultAsset)
44 return tecWRONG_ASSET;
45
46 auto const& vaultAccount = vault->at(sfAccount);
47 if (auto ter = canTransfer(ctx.view, vaultAsset, account, vaultAccount); !isTesSuccess(ter))
48 {
49 JLOG(ctx.j.debug()) << "VaultDeposit: vault assets are non-transferable.";
50 return ter;
51 }
52
53 auto const mptIssuanceID = vault->at(sfShareMPTID);
54 auto const vaultShare = MPTIssue(mptIssuanceID);
55 if (vaultShare == assets.asset())
56 {
57 // LCOV_EXCL_START
58 JLOG(ctx.j.error()) << "VaultDeposit: vault shares and assets cannot be same.";
59 return tefINTERNAL;
60 // LCOV_EXCL_STOP
61 }
62
63 auto const sleIssuance = ctx.view.read(keylet::mptIssuance(mptIssuanceID));
64 if (!sleIssuance)
65 {
66 // LCOV_EXCL_START
67 JLOG(ctx.j.error()) << "VaultDeposit: missing issuance of vault shares.";
68 return tefINTERNAL;
69 // LCOV_EXCL_STOP
70 }
71
72 if (sleIssuance->isFlag(lsfMPTLocked))
73 {
74 // LCOV_EXCL_START
75 JLOG(ctx.j.error()) << "VaultDeposit: issuance of vault shares is locked.";
76 return tefINTERNAL;
77 // LCOV_EXCL_STOP
78 }
79
80 // Cannot deposit inside Vault an Asset frozen for the depositor
81 if (isFrozen(ctx.view, account, vaultAsset))
82 return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
83
84 // Cannot deposit if the shares of the vault are frozen
85 if (isFrozen(ctx.view, account, vaultShare))
86 return tecLOCKED;
87
88 if (vault->isFlag(lsfVaultPrivate) && account != vault->at(sfOwner))
89 {
90 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
91 // Since this is a private vault and the account is not its owner, we
92 // perform authorization check based on DomainID read from sleIssuance.
93 // Had the vault shares been a regular MPToken, we would allow
94 // authorization granted by the Issuer explicitly, but Vault uses Issuer
95 // pseudo-account, which cannot grant an authorization.
96 if (maybeDomainID)
97 {
98 // As per validDomain documentation, we suppress tecEXPIRED error
99 // here, so we can delete any expired credentials inside doApply.
100 if (auto const err = credentials::validDomain(ctx.view, *maybeDomainID, account);
101 !isTesSuccess(err) && err != tecEXPIRED)
102 return err;
103 }
104 else
105 return tecNO_AUTH;
106 }
107
108 // Source MPToken must exist (if asset is an MPT)
109 if (auto const ter = requireAuth(ctx.view, vaultAsset, account); !isTesSuccess(ter))
110 return ter;
111
112 if (accountHolds(
113 ctx.view,
114 account,
115 vaultAsset,
118 ctx.j,
121
122 return tesSUCCESS;
123}
124
125TER
127{
128 auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID]));
129 if (!vault)
130 return tefINTERNAL; // LCOV_EXCL_LINE
131 auto const vaultAsset = vault->at(sfAsset);
132
133 auto const amount = ctx_.tx[sfAmount];
134 // Make sure the depositor can hold shares.
135 auto const mptIssuanceID = (*vault)[sfShareMPTID];
136 auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID));
137 if (!sleIssuance)
138 {
139 // LCOV_EXCL_START
140 JLOG(j_.error()) << "VaultDeposit: missing issuance of vault shares.";
141 return tefINTERNAL;
142 // LCOV_EXCL_STOP
143 }
144
145 auto const& vaultAccount = vault->at(sfAccount);
146 // Note, vault owner is always authorized
147 if (vault->isFlag(lsfVaultPrivate) && account_ != vault->at(sfOwner))
148 {
149 if (auto const err = enforceMPTokenAuthorization(ctx_.view(), mptIssuanceID, account_, mPriorBalance, j_);
150 !isTesSuccess(err))
151 return err;
152 }
153 else // !vault->isFlag(lsfVaultPrivate) || account_ == vault->at(sfOwner)
154 {
155 // No authorization needed, but must ensure there is MPToken
156 if (!view().exists(keylet::mptoken(mptIssuanceID, account_)))
157 {
158 if (auto const err =
160 !isTesSuccess(err))
161 return err;
162 }
163
164 // If the vault is private, set the authorized flag for the vault owner
165 if (vault->isFlag(lsfVaultPrivate))
166 {
167 // This follows from the reverse of the outer enclosing if condition
168 XRPL_ASSERT(account_ == vault->at(sfOwner), "xrpl::VaultDeposit::doApply : account is owner");
169 if (auto const err = authorizeMPToken(
170 view(),
171 mPriorBalance, // priorBalance
172 mptIssuanceID->value(), // mptIssuanceID
173 sleIssuance->at(sfIssuer), // account
175 {}, // flags
176 account_ // holderID
177 );
178 !isTesSuccess(err))
179 return err;
180 }
181 }
182
183 STAmount sharesCreated = {vault->at(sfShareMPTID)}, assetsDeposited;
184 try
185 {
186 // Compute exchange before transferring any amounts.
187 {
188 auto const maybeShares = assetsToSharesDeposit(vault, sleIssuance, amount);
189 if (!maybeShares)
190 return tecINTERNAL; // LCOV_EXCL_LINE
191 sharesCreated = *maybeShares;
192 }
193 if (sharesCreated == beast::zero)
194 return tecPRECISION_LOSS;
195
196 auto const maybeAssets = sharesToAssetsDeposit(vault, sleIssuance, sharesCreated);
197 if (!maybeAssets)
198 return tecINTERNAL; // LCOV_EXCL_LINE
199 else if (*maybeAssets > amount)
200 {
201 // LCOV_EXCL_START
202 JLOG(j_.error()) << "VaultDeposit: would take more than offered.";
203 return tecINTERNAL;
204 // LCOV_EXCL_STOP
205 }
206 assetsDeposited = *maybeAssets;
207 }
208 catch (std::overflow_error const&)
209 {
210 // It's easy to hit this exception from Number with large enough Scale
211 // so we avoid spamming the log and only use debug here.
212 JLOG(j_.debug()) //
213 << "VaultDeposit: overflow error with"
214 << " scale=" << (int)vault->at(sfScale).value() //
215 << ", assetsTotal=" << vault->at(sfAssetsTotal).value()
216 << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount) << ", amount=" << amount;
217 return tecPATH_DRY;
218 }
219
220 XRPL_ASSERT(
221 sharesCreated.asset() != assetsDeposited.asset(), "xrpl::VaultDeposit::doApply : assets are not shares");
222
223 vault->at(sfAssetsTotal) += assetsDeposited;
224 vault->at(sfAssetsAvailable) += assetsDeposited;
225 view().update(vault);
226
227 // A deposit must not push the vault over its limit.
228 auto const maximum = *vault->at(sfAssetsMaximum);
229 if (maximum != 0 && *vault->at(sfAssetsTotal) > maximum)
230 return tecLIMIT_EXCEEDED;
231
232 // Transfer assets from depositor to vault.
233 if (auto const ter = accountSend(view(), account_, vaultAccount, assetsDeposited, j_, WaiveTransferFee::Yes);
234 !isTesSuccess(ter))
235 return ter;
236
237 // Sanity check
238 if (accountHolds(
239 view(),
240 account_,
241 assetsDeposited.asset(),
244 j_) < beast::zero)
245 {
246 // LCOV_EXCL_START
247 JLOG(j_.error()) << "VaultDeposit: negative balance of account assets.";
248 return tefINTERNAL;
249 // LCOV_EXCL_STOP
250 }
251
252 // Transfer shares from vault to depositor.
253 if (auto const ter = accountSend(view(), vaultAccount, account_, sharesCreated, j_, WaiveTransferFee::Yes);
254 !isTesSuccess(ter))
255 return ter;
256
257 associateAsset(*vault, vaultAsset);
258
259 return tesSUCCESS;
260}
261
262} // namespace xrpl
Stream error() const
Definition Journal.h:318
Stream debug() const
Definition Journal.h:300
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:13
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:441
AccountID const account_
Definition Transactor.h:112
beast::Journal const j_
Definition Transactor.h:110
ApplyView & view()
Definition Transactor.h:128
XRPAmount mPriorBalance
Definition Transactor.h:113
ApplyContext & ctx_
Definition Transactor.h:108
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:217
TER validDomain(ReadView const &view, uint256 domainID, AccountID const &subject)
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:462
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:492
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:474
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::optional< STAmount > sharesToAssetsDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:3128
@ fhZERO_IF_FROZEN
Definition View.h:58
@ fhIGNORE_FREEZE
Definition View.h:58
@ shFULL_BALANCE
Definition View.h:64
@ tefINTERNAL
Definition TER.h:153
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
Definition View.cpp:392
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:1326
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:2446
std::optional< STAmount > assetsToSharesDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition View.cpp:3107
@ ahIGNORE_AUTH
Definition View.h:61
@ ahZERO_IF_UNAUTHORIZED
Definition View.h:61
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:2710
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:2914
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:194
@ temMALFORMED
Definition TER.h:67
@ temBAD_AMOUNT
Definition TER.h:69
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
@ tecWRONG_ASSET
Definition TER.h:341
@ tecLOCKED
Definition TER.h:339
@ tecNO_ENTRY
Definition TER.h:287
@ tecPATH_DRY
Definition TER.h:275
@ tecNO_AUTH
Definition TER.h:281
@ tecINTERNAL
Definition TER.h:291
@ tecFROZEN
Definition TER.h:284
@ tecINSUFFICIENT_FUNDS
Definition TER.h:306
@ tecEXPIRED
Definition TER.h:295
@ tecPRECISION_LOSS
Definition TER.h:344
@ tecLIMIT_EXCEEDED
Definition TER.h:342
@ 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:2819
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
@ tesSUCCESS
Definition TER.h:225
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
ReadView const & view
Definition Transactor.h:56
beast::Journal const j
Definition Transactor.h:61
State information when preflighting a tx.
Definition Transactor.h:15
beast::Journal const j
Definition Transactor.h:22