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