rippled
Loading...
Searching...
No Matches
VaultCreate.cpp
1#include <xrpl/ledger/View.h>
2#include <xrpl/ledger/helpers/AccountRootHelpers.h>
3#include <xrpl/ledger/helpers/MPTokenHelpers.h>
4#include <xrpl/ledger/helpers/TokenHelpers.h>
5#include <xrpl/protocol/Asset.h>
6#include <xrpl/protocol/Feature.h>
7#include <xrpl/protocol/Indexes.h>
8#include <xrpl/protocol/Issue.h>
9#include <xrpl/protocol/MPTIssue.h>
10#include <xrpl/protocol/Protocol.h>
11#include <xrpl/protocol/SField.h>
12#include <xrpl/protocol/STNumber.h>
13#include <xrpl/protocol/STTakesAsset.h>
14#include <xrpl/protocol/TER.h>
15#include <xrpl/protocol/TxFlags.h>
16#include <xrpl/tx/transactors/token/MPTokenAuthorize.h>
17#include <xrpl/tx/transactors/token/MPTokenIssuanceCreate.h>
18#include <xrpl/tx/transactors/vault/VaultCreate.h>
19
20namespace xrpl {
21
22bool
24{
25 if (!ctx.rules.enabled(featureMPTokensV1))
26 return false;
27
28 if (ctx.tx.isFieldPresent(sfDomainID) && !ctx.rules.enabled(featurePermissionedDomains))
29 return false;
30
31 return true;
32}
33
36{
37 return tfVaultCreateMask;
38}
39
42{
43 if (!validDataLength(ctx.tx[~sfData], maxDataPayloadLength))
44 return temMALFORMED;
45
46 if (auto const withdrawalPolicy = ctx.tx[~sfWithdrawalPolicy])
47 {
48 // Enforce valid withdrawal policy
49 if (*withdrawalPolicy != vaultStrategyFirstComeFirstServe)
50 return temMALFORMED;
51 }
52
53 if (auto const domain = ctx.tx[~sfDomainID])
54 {
55 if (*domain == beast::zero)
56 {
57 return temMALFORMED;
58 }
59 if ((ctx.tx.getFlags() & tfVaultPrivate) == 0)
60 {
61 return temMALFORMED; // DomainID only allowed on private vaults
62 }
63 }
64
65 if (auto const assetMax = ctx.tx[~sfAssetsMaximum])
66 {
67 if (*assetMax < beast::zero)
68 return temMALFORMED;
69 }
70
71 if (auto const metadata = ctx.tx[~sfMPTokenMetadata])
72 {
73 if (metadata->empty() || metadata->length() > maxMPTokenMetadataLength)
74 return temMALFORMED;
75 }
76
77 if (auto const scale = ctx.tx[~sfScale])
78 {
79 auto const vaultAsset = ctx.tx[sfAsset];
80 if (vaultAsset.holds<MPTIssue>() || vaultAsset.native())
81 return temMALFORMED;
82
83 if (scale > vaultMaximumIOUScale)
84 return temMALFORMED;
85 }
86
87 return tesSUCCESS;
88}
89
90TER
92{
93 auto const vaultAsset = ctx.tx[sfAsset];
94 auto const account = ctx.tx[sfAccount];
95
96 if (auto const ter = canAddHolding(ctx.view, vaultAsset))
97 return ter;
98
99 // Check for pseudo-account issuers - we do not want a vault to hold such
100 // assets (e.g. MPT shares to other vaults or AMM LPTokens) as they would be
101 // impossible to clawback (should the need arise)
102 if (!vaultAsset.native())
103 {
104 if (isPseudoAccount(ctx.view, vaultAsset.getIssuer()))
105 return tecWRONG_ASSET;
106 }
107
108 // Cannot create Vault for an Asset frozen for the vault owner
109 if (isFrozen(ctx.view, account, vaultAsset))
110 return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
111
112 if (auto const domain = ctx.tx[~sfDomainID])
113 {
114 auto const sleDomain = ctx.view.read(keylet::permissionedDomain(*domain));
115 if (!sleDomain)
116 return tecOBJECT_NOT_FOUND;
117 }
118
119 auto const sequence = ctx.tx.getSeqValue();
120 if (auto const accountId = pseudoAccountAddress(ctx.view, keylet::vault(account, sequence).key);
121 accountId == beast::zero)
123
124 return tesSUCCESS;
125}
126
127TER
129{
130 // All return codes in `doApply` must be `tec`, `ter`, or `tes`.
131 // As we move checks into `preflight` and `preclaim`,
132 // we can consider downgrading them to `tef` or `tem`.
133
134 auto const& tx = ctx_.tx;
135 auto const sequence = tx.getSeqValue();
136 auto const owner = view().peek(keylet::account(account_));
137 if (owner == nullptr)
138 return tefINTERNAL; // LCOV_EXCL_LINE
139
140 auto vault = std::make_shared<SLE>(keylet::vault(account_, sequence));
141
142 if (auto ter = dirLink(view(), account_, vault))
143 return ter;
144 // We will create Vault and PseudoAccount, hence increase OwnerCount by 2
145 adjustOwnerCount(view(), owner, 2, j_);
146 auto const ownerCount = owner->at(sfOwnerCount);
147 if (preFeeBalance_ < view().fees().accountReserve(ownerCount))
149
150 auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID);
151 if (!maybePseudo)
152 return maybePseudo.error(); // LCOV_EXCL_LINE
153 auto& pseudo = *maybePseudo;
154 auto pseudoId = pseudo->at(sfAccount);
155 auto asset = tx[sfAsset];
156
157 if (auto ter = addEmptyHolding(view(), pseudoId, preFeeBalance_, asset, j_); !isTesSuccess(ter))
158 return ter;
159
160 std::uint8_t const scale = (asset.holds<MPTIssue>() || asset.native())
161 ? 0
162 : ctx_.tx[~sfScale].value_or(vaultDefaultIOUScale);
163
164 auto txFlags = tx.getFlags();
165 std::uint32_t mptFlags = 0;
166 if ((txFlags & tfVaultShareNonTransferable) == 0)
167 mptFlags |= (lsfMPTCanEscrow | lsfMPTCanTrade | lsfMPTCanTransfer);
168 if ((txFlags & tfVaultPrivate) != 0u)
169 mptFlags |= lsfMPTRequireAuth;
170
171 // Note, here we are **not** creating an MPToken for the assets held in
172 // the vault. That MPToken or TrustLine/RippleState is created above, in
173 // addEmptyHolding. Here we are creating MPTokenIssuance for the shares
174 // in the vault
175 auto maybeShare = MPTokenIssuanceCreate::create(
176 view(),
177 j_,
178 {
179 .priorBalance = std::nullopt,
180 .account = pseudoId->value(),
181 .sequence = 1,
182 .flags = mptFlags,
183 .assetScale = scale,
184 .metadata = tx[~sfMPTokenMetadata],
185 .domainId = tx[~sfDomainID],
186 });
187 if (!maybeShare)
188 return maybeShare.error(); // LCOV_EXCL_LINE
189 auto const& mptIssuanceID = *maybeShare;
190
191 vault->setFieldIssue(sfAsset, STIssue{sfAsset, asset});
192 vault->at(sfFlags) = txFlags & tfVaultPrivate;
193 vault->at(sfSequence) = sequence;
194 vault->at(sfOwner) = account_;
195 vault->at(sfAccount) = pseudoId;
196 vault->at(sfAssetsTotal) = Number(0);
197 vault->at(sfAssetsAvailable) = Number(0);
198 vault->at(sfLossUnrealized) = Number(0);
199 // Leave default values for AssetTotal and AssetAvailable, both zero.
200 if (auto value = tx[~sfAssetsMaximum])
201 vault->at(sfAssetsMaximum) = *value;
202 vault->at(sfShareMPTID) = mptIssuanceID;
203 if (auto value = tx[~sfData])
204 vault->at(sfData) = *value;
205 // Required field, default to vaultStrategyFirstComeFirstServe
206 if (auto value = tx[~sfWithdrawalPolicy])
207 {
208 vault->at(sfWithdrawalPolicy) = *value;
209 }
210 else
211 {
212 vault->at(sfWithdrawalPolicy) = vaultStrategyFirstComeFirstServe;
213 }
214 if (scale != 0u)
215 vault->at(sfScale) = scale;
216 view().insert(vault);
217
218 // Explicitly create MPToken for the vault owner
219 if (auto const err =
221 !isTesSuccess(err))
222 return err;
223
224 // If the vault is private, set the authorized flag for the vault owner
225 if ((txFlags & tfVaultPrivate) != 0u)
226 {
227 if (auto const err = authorizeMPToken(
228 view(), preFeeBalance_, mptIssuanceID, pseudoId, ctx_.journal, {}, account_);
229 !isTesSuccess(err))
230 return err;
231 }
232
233 associateAsset(*vault, asset);
234
235 return tesSUCCESS;
236}
237
238} // namespace xrpl
STTx const & tx
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:13
bool native() const
Definition MPTIssue.h:44
static Expected< MPTID, TER > create(ApplyView &view, beast::Journal journal, MPTCreateArgs const &args)
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
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:120
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:456
std::uint32_t getFlags() const
Definition STObject.cpp:509
std::uint32_t getSeqValue() const
Returns the first non-zero value of (Sequence, TicketSequence).
Definition STTx.cpp:211
AccountID const account_
Definition Transactor.h:116
beast::Journal const j_
Definition Transactor.h:114
ApplyView & view()
Definition Transactor.h:132
XRPAmount preFeeBalance_
Definition Transactor.h:117
static bool validDataLength(std::optional< Slice > const &slice, std::size_t maxLength)
ApplyContext & ctx_
Definition Transactor.h:112
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
TER doApply() override
static bool checkExtraFeatures(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
T is_same_v
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:504
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:522
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terADDRESS_COLLISION
Definition TER.h:208
std::uint8_t constexpr vaultStrategyFirstComeFirstServe
Vault withdrawal policies.
Definition Protocol.h:241
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Generate a pseudo-account address from a pseudo owner key.
TER addEmptyHolding(ApplyView &view, AccountID const &accountID, XRPAmount priorBalance, MPTIssue const &mptIssue, beast::Journal journal)
@ tefINTERNAL
Definition TER.h:153
std::size_t constexpr maxMPTokenMetadataLength
The maximum length of MPTokenMetadata.
Definition Protocol.h:231
std::size_t constexpr maxDataPayloadLength
The maximum length of Data payload.
Definition Protocol.h:238
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)
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Returns true if and only if sleAcct is a pseudo-account or specific pseudo-accounts in pseudoFieldFil...
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, int depth=0)
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
TER canAddHolding(ReadView const &view, MPTIssue const &mptIssue)
std::uint8_t constexpr vaultDefaultIOUScale
Default IOU scale factor for a Vault.
Definition Protocol.h:244
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object, SF_UINT64 const &node=sfOwnerNode)
Definition View.cpp:296
@ temMALFORMED
Definition TER.h:67
bool isTesSuccess(TER x) noexcept
Definition TER.h:651
@ tecWRONG_ASSET
Definition TER.h:341
@ tecLOCKED
Definition TER.h:339
@ tecOBJECT_NOT_FOUND
Definition TER.h:307
@ tecFROZEN
Definition TER.h:284
@ tecINSUFFICIENT_RESERVE
Definition TER.h:288
Expected< std::shared_ptr< SLE >, TER > createPseudoAccount(ApplyView &view, uint256 const &pseudoOwnerKey, SField const &ownerField)
Create pseudo-account, storing pseudoOwnerKey into ownerField.
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
std::uint8_t constexpr vaultMaximumIOUScale
Maximum scale factor for a Vault.
Definition Protocol.h:248
@ tesSUCCESS
Definition TER.h:225
uint256 key
Definition Keylet.h:20
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:57
ReadView const & view
Definition Transactor.h:60
State information when preflighting a tx.
Definition Transactor.h:14