rippled
Loading...
Searching...
No Matches
VaultCreate.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2025 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/tx/detail/MPTokenAuthorize.h>
21#include <xrpld/app/tx/detail/MPTokenIssuanceCreate.h>
22#include <xrpld/app/tx/detail/VaultCreate.h>
23
24#include <xrpl/ledger/View.h>
25#include <xrpl/protocol/Asset.h>
26#include <xrpl/protocol/Feature.h>
27#include <xrpl/protocol/Indexes.h>
28#include <xrpl/protocol/Issue.h>
29#include <xrpl/protocol/MPTIssue.h>
30#include <xrpl/protocol/Protocol.h>
31#include <xrpl/protocol/SField.h>
32#include <xrpl/protocol/STNumber.h>
33#include <xrpl/protocol/TER.h>
34#include <xrpl/protocol/TxFlags.h>
35
36namespace ripple {
37
38bool
40{
41 if (!ctx.rules.enabled(featureMPTokensV1))
42 return false;
43
44 return !ctx.tx.isFieldPresent(sfDomainID) ||
45 ctx.rules.enabled(featurePermissionedDomains);
46}
47
53
56{
57 if (!validDataLength(ctx.tx[~sfData], maxDataPayloadLength))
58 return temMALFORMED;
59
60 if (auto const withdrawalPolicy = ctx.tx[~sfWithdrawalPolicy])
61 {
62 // Enforce valid withdrawal policy
63 if (*withdrawalPolicy != vaultStrategyFirstComeFirstServe)
64 return temMALFORMED;
65 }
66
67 if (auto const domain = ctx.tx[~sfDomainID])
68 {
69 if (*domain == beast::zero)
70 return temMALFORMED;
71 else if ((ctx.tx.getFlags() & tfVaultPrivate) == 0)
72 return temMALFORMED; // DomainID only allowed on private vaults
73 }
74
75 if (auto const assetMax = ctx.tx[~sfAssetsMaximum])
76 {
77 if (*assetMax < beast::zero)
78 return temMALFORMED;
79 }
80
81 if (auto const metadata = ctx.tx[~sfMPTokenMetadata])
82 {
83 if (metadata->length() == 0 ||
84 metadata->length() > maxMPTokenMetadataLength)
85 return temMALFORMED;
86 }
87
88 if (auto const scale = ctx.tx[~sfScale])
89 {
90 auto const vaultAsset = ctx.tx[sfAsset];
91 if (vaultAsset.holds<MPTIssue>() || vaultAsset.native())
92 return temMALFORMED;
93
94 if (scale > vaultMaximumIOUScale)
95 return temMALFORMED;
96 }
97
98 return tesSUCCESS;
99}
100
103{
104 // One reserve increment is typically much greater than one base fee.
105 return calculateOwnerReserveFee(view, tx);
106}
107
108TER
110{
111 auto const vaultAsset = ctx.tx[sfAsset];
112 auto const account = ctx.tx[sfAccount];
113
114 if (auto const ter = canAddHolding(ctx.view, vaultAsset))
115 return ter;
116
117 // Check for pseudo-account issuers - we do not want a vault to hold such
118 // assets (e.g. MPT shares to other vaults or AMM LPTokens) as they would be
119 // impossible to clawback (should the need arise)
120 if (!vaultAsset.native())
121 {
122 if (isPseudoAccount(ctx.view, vaultAsset.getIssuer()))
123 return tecWRONG_ASSET;
124 }
125
126 // Cannot create Vault for an Asset frozen for the vault owner
127 if (isFrozen(ctx.view, account, vaultAsset))
128 return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
129
130 if (auto const domain = ctx.tx[~sfDomainID])
131 {
132 auto const sleDomain =
134 if (!sleDomain)
135 return tecOBJECT_NOT_FOUND;
136 }
137
138 auto const sequence = ctx.tx.getSeqValue();
139 if (auto const accountId = pseudoAccountAddress(
140 ctx.view, keylet::vault(account, sequence).key);
141 accountId == beast::zero)
143
144 return tesSUCCESS;
145}
146
147TER
149{
150 // All return codes in `doApply` must be `tec`, `ter`, or `tes`.
151 // As we move checks into `preflight` and `preclaim`,
152 // we can consider downgrading them to `tef` or `tem`.
153
154 auto const& tx = ctx_.tx;
155 auto const sequence = tx.getSeqValue();
156 auto const owner = view().peek(keylet::account(account_));
157 if (owner == nullptr)
158 return tefINTERNAL; // LCOV_EXCL_LINE
159
160 auto vault = std::make_shared<SLE>(keylet::vault(account_, sequence));
161
162 if (auto ter = dirLink(view(), account_, vault))
163 return ter;
164 adjustOwnerCount(view(), owner, 1, j_);
165 auto ownerCount = owner->at(sfOwnerCount);
166 if (mPriorBalance < view().fees().accountReserve(ownerCount))
168
169 auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID);
170 if (!maybePseudo)
171 return maybePseudo.error(); // LCOV_EXCL_LINE
172 auto& pseudo = *maybePseudo;
173 auto pseudoId = pseudo->at(sfAccount);
174 auto asset = tx[sfAsset];
175
176 if (auto ter = addEmptyHolding(view(), pseudoId, mPriorBalance, asset, j_);
177 !isTesSuccess(ter))
178 return ter;
179
180 std::uint8_t const scale = (asset.holds<MPTIssue>() || asset.native())
181 ? 0
182 : ctx_.tx[~sfScale].value_or(vaultDefaultIOUScale);
183
184 auto txFlags = tx.getFlags();
185 std::uint32_t mptFlags = 0;
186 if ((txFlags & tfVaultShareNonTransferable) == 0)
188 if (txFlags & tfVaultPrivate)
189 mptFlags |= lsfMPTRequireAuth;
190
191 // Note, here we are **not** creating an MPToken for the assets held in
192 // the vault. That MPToken or TrustLine/RippleState is created above, in
193 // addEmptyHolding. Here we are creating MPTokenIssuance for the shares
194 // in the vault
195 auto maybeShare = MPTokenIssuanceCreate::create(
196 view(),
197 j_,
198 {
199 .priorBalance = std::nullopt,
200 .account = pseudoId->value(),
201 .sequence = 1,
202 .flags = mptFlags,
203 .assetScale = scale,
204 .metadata = tx[~sfMPTokenMetadata],
205 .domainId = tx[~sfDomainID],
206 });
207 if (!maybeShare)
208 return maybeShare.error(); // LCOV_EXCL_LINE
209 auto const& mptIssuanceID = *maybeShare;
210
211 vault->setFieldIssue(sfAsset, STIssue{sfAsset, asset});
212 vault->at(sfFlags) = txFlags & tfVaultPrivate;
213 vault->at(sfSequence) = sequence;
214 vault->at(sfOwner) = account_;
215 vault->at(sfAccount) = pseudoId;
216 vault->at(sfAssetsTotal) = Number(0);
217 vault->at(sfAssetsAvailable) = Number(0);
218 vault->at(sfLossUnrealized) = Number(0);
219 // Leave default values for AssetTotal and AssetAvailable, both zero.
220 if (auto value = tx[~sfAssetsMaximum])
221 vault->at(sfAssetsMaximum) = *value;
222 vault->at(sfShareMPTID) = mptIssuanceID;
223 if (auto value = tx[~sfData])
224 vault->at(sfData) = *value;
225 // Required field, default to vaultStrategyFirstComeFirstServe
226 if (auto value = tx[~sfWithdrawalPolicy])
227 vault->at(sfWithdrawalPolicy) = *value;
228 else
229 vault->at(sfWithdrawalPolicy) = vaultStrategyFirstComeFirstServe;
230 if (scale)
231 vault->at(sfScale) = scale;
232 view().insert(vault);
233
234 // Explicitly create MPToken for the vault owner
235 if (auto const err = authorizeMPToken(
236 view(), mPriorBalance, mptIssuanceID, account_, ctx_.journal);
237 !isTesSuccess(err))
238 return err;
239
240 // If the vault is private, set the authorized flag for the vault owner
241 if (txFlags & tfVaultPrivate)
242 {
243 if (auto const err = authorizeMPToken(
244 view(),
246 mptIssuanceID,
247 pseudoId,
249 {},
250 account_);
251 !isTesSuccess(err))
252 return err;
253 }
254
255 return tesSUCCESS;
256}
257
258} // namespace ripple
beast::Journal const journal
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state 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:33
bool native() const
Definition MPTIssue.h:64
static Expected< MPTID, TER > create(ApplyView &view, beast::Journal journal, MPTCreateArgs const &args)
A view into a ledger.
Definition ReadView.h:51
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.
Definition Rules.cpp:130
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:484
std::uint32_t getFlags() const
Definition STObject.cpp:537
std::uint32_t getSeqValue() const
Returns the first non-zero value of (Sequence, TicketSequence).
Definition STTx.cpp:231
AccountID const account_
Definition Transactor.h:147
ApplyView & view()
Definition Transactor.h:163
static XRPAmount calculateOwnerReserveFee(ReadView const &view, STTx const &tx)
beast::Journal const j_
Definition Transactor.h:145
XRPAmount mPriorBalance
Definition Transactor.h:148
static bool validDataLength(std::optional< Slice > const &slice, std::size_t maxLength)
ApplyContext & ctx_
Definition Transactor.h:143
TER doApply() override
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static bool checkExtraFeatures(PreflightContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
T is_same_v
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:570
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:564
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
@ lsfMPTCanTransfer
@ lsfMPTCanEscrow
@ lsfMPTRequireAuth
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:1029
constexpr std::uint32_t const tfVaultPrivate
Definition TxFlags.h:270
std::size_t constexpr maxDataPayloadLength
The maximum length of Data payload.
Definition Protocol.h:119
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:247
@ tefINTERNAL
Definition TER.h:173
std::size_t constexpr maxMPTokenMetadataLength
The maximum length of MPTokenMetadata.
Definition Protocol.h:113
Expected< std::shared_ptr< SLE >, TER > createPseudoAccount(ApplyView &view, uint256 const &pseudoOwnerKey, SField const &ownerField)
Create pseudo-account, storing pseudoOwnerKey into ownerField.
Definition View.cpp:1129
std::uint8_t constexpr vaultMaximumIOUScale
Maximum scale factor for a Vault.
Definition Protocol.h:129
@ tecOBJECT_NOT_FOUND
Definition TER.h:326
@ tecFROZEN
Definition TER.h:303
@ tecWRONG_ASSET
Definition TER.h:360
@ tecINSUFFICIENT_RESERVE
Definition TER.h:307
@ tecLOCKED
Definition TER.h:358
@ tesSUCCESS
Definition TER.h:244
TER addEmptyHolding(ApplyView &view, AccountID const &accountID, XRPAmount priorBalance, Issue const &issue, beast::Journal journal)
Any transactors that call addEmptyHolding() in doApply must call canAddHolding() in preflight with th...
Definition View.cpp:1213
constexpr std::uint32_t const tfVaultShareNonTransferable
Definition TxFlags.h:272
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition View.cpp:1066
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
std::uint8_t constexpr vaultStrategyFirstComeFirstServe
Vault withdrawal policies.
Definition Protocol.h:122
TER canAddHolding(ReadView const &view, Asset const &asset)
Definition View.cpp:1203
@ terADDRESS_COLLISION
Definition TER.h:228
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:1281
constexpr std::uint32_t const tfVaultCreateMask
Definition TxFlags.h:273
std::uint8_t constexpr vaultDefaultIOUScale
Default IOU scale factor for a Vault.
Definition Protocol.h:125
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition View.cpp:1115
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object)
Definition View.cpp:1055
@ temMALFORMED
Definition TER.h:87
uint256 key
Definition Keylet.h:40
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:80
ReadView const & view
Definition Transactor.h:83
State information when preflighting a tx.
Definition Transactor.h:35