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#include <xrpld/ledger/View.h>
24
25#include <xrpl/protocol/Asset.h>
26#include <xrpl/protocol/Feature.h>
27#include <xrpl/protocol/Indexes.h>
28#include <xrpl/protocol/MPTIssue.h>
29#include <xrpl/protocol/Protocol.h>
30#include <xrpl/protocol/STNumber.h>
31#include <xrpl/protocol/TER.h>
32#include <xrpl/protocol/TxFlags.h>
33
34namespace ripple {
35
38{
39 if (!ctx.rules.enabled(featureSingleAssetVault) ||
40 !ctx.rules.enabled(featureMPTokensV1))
41 return temDISABLED;
42
43 if (ctx.tx.isFieldPresent(sfDomainID) &&
44 !ctx.rules.enabled(featurePermissionedDomains))
45 return temDISABLED;
46
47 if (auto const ter = preflight1(ctx))
48 return ter;
49
50 if (ctx.tx.getFlags() & tfVaultCreateMask)
51 return temINVALID_FLAG;
52
53 if (auto const data = ctx.tx[~sfData])
54 {
55 if (data->empty() || data->length() > maxDataPayloadLength)
56 return temMALFORMED;
57 }
58
59 if (auto const withdrawalPolicy = ctx.tx[~sfWithdrawalPolicy])
60 {
61 // Enforce valid withdrawal policy
62 if (*withdrawalPolicy != vaultStrategyFirstComeFirstServe)
63 return temMALFORMED;
64 }
65
66 if (auto const domain = ctx.tx[~sfDomainID])
67 {
68 if (*domain == beast::zero)
69 return temMALFORMED;
70 else if ((ctx.tx.getFlags() & tfVaultPrivate) == 0)
71 return temMALFORMED; // DomainID only allowed on private vaults
72 }
73
74 if (auto const assetMax = ctx.tx[~sfAssetsMaximum])
75 {
76 if (*assetMax < beast::zero)
77 return temMALFORMED;
78 }
79
80 if (auto const metadata = ctx.tx[~sfMPTokenMetadata])
81 {
82 if (metadata->length() == 0 ||
83 metadata->length() > maxMPTokenMetadataLength)
84 return temMALFORMED;
85 }
86
87 return preflight2(ctx);
88}
89
92{
93 // One reserve increment is typically much greater than one base fee.
94 return view.fees().increment;
95}
96
97TER
99{
100 auto vaultAsset = ctx.tx[sfAsset];
101 auto account = ctx.tx[sfAccount];
102
103 if (vaultAsset.native())
104 ; // No special checks for XRP
105 else if (vaultAsset.holds<MPTIssue>())
106 {
107 auto mptID = vaultAsset.get<MPTIssue>().getMptID();
108 auto issuance = ctx.view.read(keylet::mptIssuance(mptID));
109 if (!issuance)
110 return tecOBJECT_NOT_FOUND;
111 if (!issuance->isFlag(lsfMPTCanTransfer))
112 {
113 // NOTE: flag lsfMPTCanTransfer is immutable, so this is debug in
114 // VaultCreate only; in other vault function it's an error.
115 JLOG(ctx.j.debug())
116 << "VaultCreate: vault assets are non-transferable.";
117 return tecNO_AUTH;
118 }
119 }
120 else if (vaultAsset.holds<Issue>())
121 {
122 auto const issuer =
123 ctx.view.read(keylet::account(vaultAsset.getIssuer()));
124 if (!issuer)
125 return terNO_ACCOUNT;
126 else if (!issuer->isFlag(lsfDefaultRipple))
127 return terNO_RIPPLE;
128 }
129
130 // Check for pseudo-account issuers - we do not want a vault to hold such
131 // assets (e.g. MPT shares to other vaults or AMM LPTokens) as they would be
132 // impossible to clawback (should the need arise)
133 if (!vaultAsset.native())
134 {
135 if (isPseudoAccount(ctx.view, vaultAsset.getIssuer()))
136 return tecWRONG_ASSET;
137 }
138
139 // Cannot create Vault for an Asset frozen for the vault owner
140 if (isFrozen(ctx.view, account, vaultAsset))
141 return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
142
143 if (auto const domain = ctx.tx[~sfDomainID])
144 {
145 auto const sleDomain =
147 if (!sleDomain)
148 return tecOBJECT_NOT_FOUND;
149 }
150
151 auto sequence = ctx.tx.getSeqValue();
152 if (auto const accountId = pseudoAccountAddress(
153 ctx.view, keylet::vault(account, sequence).key);
154 accountId == beast::zero)
156
157 return tesSUCCESS;
158}
159
160TER
162{
163 // All return codes in `doApply` must be `tec`, `ter`, or `tes`.
164 // As we move checks into `preflight` and `preclaim`,
165 // we can consider downgrading them to `tef` or `tem`.
166
167 auto const& tx = ctx_.tx;
168 auto sequence = tx.getSeqValue();
169 auto owner = view().peek(keylet::account(account_));
170 if (owner == nullptr)
171 return tefINTERNAL; // LCOV_EXCL_LINE
172
173 auto vault = std::make_shared<SLE>(keylet::vault(account_, sequence));
174
175 if (auto ter = dirLink(view(), account_, vault))
176 return ter;
177 adjustOwnerCount(view(), owner, 1, j_);
178 auto ownerCount = owner->at(sfOwnerCount);
179 if (mPriorBalance < view().fees().accountReserve(ownerCount))
181
182 auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID);
183 if (!maybePseudo)
184 return maybePseudo.error(); // LCOV_EXCL_LINE
185 auto& pseudo = *maybePseudo;
186 auto pseudoId = pseudo->at(sfAccount);
187 auto asset = tx[sfAsset];
188
189 if (auto ter = addEmptyHolding(view(), pseudoId, mPriorBalance, asset, j_);
190 !isTesSuccess(ter))
191 return ter;
192
193 auto txFlags = tx.getFlags();
194 std::uint32_t mptFlags = 0;
195 if ((txFlags & tfVaultShareNonTransferable) == 0)
197 if (txFlags & tfVaultPrivate)
198 mptFlags |= lsfMPTRequireAuth;
199
200 // Note, here we are **not** creating an MPToken for the assets held in
201 // the vault. That MPToken or TrustLine/RippleState is created above, in
202 // addEmptyHolding. Here we are creating MPTokenIssuance for the shares
203 // in the vault
204 auto maybeShare = MPTokenIssuanceCreate::create(
205 view(),
206 j_,
207 {
208 .priorBalance = std::nullopt,
209 .account = pseudoId->value(),
210 .sequence = 1,
211 .flags = mptFlags,
212 .metadata = tx[~sfMPTokenMetadata],
213 .domainId = tx[~sfDomainID],
214 });
215 if (!maybeShare)
216 return maybeShare.error(); // LCOV_EXCL_LINE
217 auto& share = *maybeShare;
218
219 vault->setFieldIssue(sfAsset, STIssue{sfAsset, asset});
220 vault->at(sfFlags) = txFlags & tfVaultPrivate;
221 vault->at(sfSequence) = sequence;
222 vault->at(sfOwner) = account_;
223 vault->at(sfAccount) = pseudoId;
224 vault->at(sfAssetsTotal) = Number(0);
225 vault->at(sfAssetsAvailable) = Number(0);
226 vault->at(sfLossUnrealized) = Number(0);
227 // Leave default values for AssetTotal and AssetAvailable, both zero.
228 if (auto value = tx[~sfAssetsMaximum])
229 vault->at(sfAssetsMaximum) = *value;
230 vault->at(sfShareMPTID) = share;
231 if (auto value = tx[~sfData])
232 vault->at(sfData) = *value;
233 // Required field, default to vaultStrategyFirstComeFirstServe
234 if (auto value = tx[~sfWithdrawalPolicy])
235 vault->at(sfWithdrawalPolicy) = *value;
236 else
237 vault->at(sfWithdrawalPolicy) = vaultStrategyFirstComeFirstServe;
238 // No `LossUnrealized`.
239 view().insert(vault);
240
241 return tesSUCCESS;
242}
243
244} // namespace ripple
Stream debug() const
Definition: Journal.h:328
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
static Expected< MPTID, TER > create(ApplyView &view, beast::Journal journal, MPTCreateArgs const &args)
A view into a ledger.
Definition: ReadView.h:52
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
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:143
ApplyView & view()
Definition: Transactor.h:159
beast::Journal const j_
Definition: Transactor.h:141
XRPAmount mPriorBalance
Definition: Transactor.h:144
ApplyContext & ctx_
Definition: Transactor.h:140
TER doApply() override
static TER preclaim(PreclaimContext const &ctx)
Definition: VaultCreate.cpp:98
static NotTEC preflight(PreflightContext const &ctx)
Definition: VaultCreate.cpp:37
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Definition: VaultCreate.cpp:91
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition: Indexes.cpp:570
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition: Indexes.cpp:526
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
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:1094
@ lsfMPTCanTransfer
@ lsfMPTCanTrade
@ lsfMPTCanEscrow
@ lsfDefaultRipple
@ lsfMPTRequireAuth
constexpr std::uint32_t const tfVaultPrivate
Definition: TxFlags.h:234
std::size_t constexpr maxDataPayloadLength
The maximum length of Data payload.
Definition: Protocol.h:120
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:91
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:248
@ tefINTERNAL
Definition: TER.h:173
std::size_t constexpr maxMPTokenMetadataLength
The maximum length of MPTokenMetadata.
Definition: Protocol.h:114
static bool adjustOwnerCount(ApplyContext &ctx, int count)
Definition: SetOracle.cpp:186
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:160
@ tecOBJECT_NOT_FOUND
Definition: TER.h:326
@ tecFROZEN
Definition: TER.h:303
@ tecWRONG_ASSET
Definition: TER.h:360
@ tecINSUFFICIENT_RESERVE
Definition: TER.h:307
@ tecNO_AUTH
Definition: TER.h:300
@ 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)
Definition: View.cpp:1153
constexpr std::uint32_t const tfVaultShareNonTransferable
Definition: TxFlags.h:236
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition: View.cpp:1067
bool isTesSuccess(TER x) noexcept
Definition: TER.h:674
std::uint8_t constexpr vaultStrategyFirstComeFirstServe
Vault withdrawal policies.
Definition: Protocol.h:123
@ terADDRESS_COLLISION
Definition: TER.h:228
@ terNO_ACCOUNT
Definition: TER.h:217
@ terNO_RIPPLE
Definition: TER.h:224
constexpr std::uint32_t const tfVaultCreateMask
Definition: TxFlags.h:237
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition: View.cpp:1139
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:605
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object)
Definition: View.cpp:1056
@ temMALFORMED
Definition: TER.h:87
@ temINVALID_FLAG
Definition: TER.h:111
@ temDISABLED
Definition: TER.h:114
XRPAmount increment
Definition: protocol/Fees.h:36
uint256 key
Definition: Keylet.h:40
State information when determining if a tx is likely to claim a fee.
Definition: Transactor.h:79
ReadView const & view
Definition: Transactor.h:82
beast::Journal const j
Definition: Transactor.h:87
State information when preflighting a tx.
Definition: Transactor.h:34