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) && !ctx.rules.enabled(featurePermissionedDomains))
27 return false;
28
29 return true;
30}
31
37
40{
41 if (!validDataLength(ctx.tx[~sfData], maxDataPayloadLength))
42 return temMALFORMED;
43
44 if (auto const withdrawalPolicy = ctx.tx[~sfWithdrawalPolicy])
45 {
46 // Enforce valid withdrawal policy
47 if (*withdrawalPolicy != vaultStrategyFirstComeFirstServe)
48 return temMALFORMED;
49 }
50
51 if (auto const domain = ctx.tx[~sfDomainID])
52 {
53 if (*domain == beast::zero)
54 return temMALFORMED;
55 else if ((ctx.tx.getFlags() & tfVaultPrivate) == 0)
56 return temMALFORMED; // DomainID only allowed on private vaults
57 }
58
59 if (auto const assetMax = ctx.tx[~sfAssetsMaximum])
60 {
61 if (*assetMax < beast::zero)
62 return temMALFORMED;
63 }
64
65 if (auto const metadata = ctx.tx[~sfMPTokenMetadata])
66 {
67 if (metadata->length() == 0 || metadata->length() > maxMPTokenMetadataLength)
68 return temMALFORMED;
69 }
70
71 if (auto const scale = ctx.tx[~sfScale])
72 {
73 auto const vaultAsset = ctx.tx[sfAsset];
74 if (vaultAsset.holds<MPTIssue>() || vaultAsset.native())
75 return temMALFORMED;
76
77 if (scale > vaultMaximumIOUScale)
78 return temMALFORMED;
79 }
80
81 return tesSUCCESS;
82}
83
84TER
86{
87 auto const vaultAsset = ctx.tx[sfAsset];
88 auto const account = ctx.tx[sfAccount];
89
90 if (auto const ter = canAddHolding(ctx.view, vaultAsset))
91 return ter;
92
93 // Check for pseudo-account issuers - we do not want a vault to hold such
94 // assets (e.g. MPT shares to other vaults or AMM LPTokens) as they would be
95 // impossible to clawback (should the need arise)
96 if (!vaultAsset.native())
97 {
98 if (isPseudoAccount(ctx.view, vaultAsset.getIssuer()))
99 return tecWRONG_ASSET;
100 }
101
102 // Cannot create Vault for an Asset frozen for the vault owner
103 if (isFrozen(ctx.view, account, vaultAsset))
104 return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
105
106 if (auto const domain = ctx.tx[~sfDomainID])
107 {
108 auto const sleDomain = ctx.view.read(keylet::permissionedDomain(*domain));
109 if (!sleDomain)
110 return tecOBJECT_NOT_FOUND;
111 }
112
113 auto const sequence = ctx.tx.getSeqValue();
114 if (auto const accountId = pseudoAccountAddress(ctx.view, keylet::vault(account, sequence).key);
115 accountId == beast::zero)
117
118 return tesSUCCESS;
119}
120
121TER
123{
124 // All return codes in `doApply` must be `tec`, `ter`, or `tes`.
125 // As we move checks into `preflight` and `preclaim`,
126 // we can consider downgrading them to `tef` or `tem`.
127
128 auto const& tx = ctx_.tx;
129 auto const sequence = tx.getSeqValue();
130 auto const owner = view().peek(keylet::account(account_));
131 if (owner == nullptr)
132 return tefINTERNAL; // LCOV_EXCL_LINE
133
134 auto vault = std::make_shared<SLE>(keylet::vault(account_, sequence));
135
136 if (auto ter = dirLink(view(), account_, vault))
137 return ter;
138 // We will create Vault and PseudoAccount, hence increase OwnerCount by 2
139 adjustOwnerCount(view(), owner, 2, j_);
140 auto const ownerCount = owner->at(sfOwnerCount);
141 if (mPriorBalance < view().fees().accountReserve(ownerCount))
143
144 auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID);
145 if (!maybePseudo)
146 return maybePseudo.error(); // LCOV_EXCL_LINE
147 auto& pseudo = *maybePseudo;
148 auto pseudoId = pseudo->at(sfAccount);
149 auto asset = tx[sfAsset];
150
151 if (auto ter = addEmptyHolding(view(), pseudoId, mPriorBalance, asset, j_); !isTesSuccess(ter))
152 return ter;
153
154 std::uint8_t const scale =
155 (asset.holds<MPTIssue>() || asset.native()) ? 0 : ctx_.tx[~sfScale].value_or(vaultDefaultIOUScale);
156
157 auto txFlags = tx.getFlags();
158 std::uint32_t mptFlags = 0;
159 if ((txFlags & tfVaultShareNonTransferable) == 0)
161 if (txFlags & tfVaultPrivate)
162 mptFlags |= lsfMPTRequireAuth;
163
164 // Note, here we are **not** creating an MPToken for the assets held in
165 // the vault. That MPToken or TrustLine/RippleState is created above, in
166 // addEmptyHolding. Here we are creating MPTokenIssuance for the shares
167 // in the vault
168 auto maybeShare = MPTokenIssuanceCreate::create(
169 view(),
170 j_,
171 {
172 .priorBalance = std::nullopt,
173 .account = pseudoId->value(),
174 .sequence = 1,
175 .flags = mptFlags,
176 .assetScale = scale,
177 .metadata = tx[~sfMPTokenMetadata],
178 .domainId = tx[~sfDomainID],
179 });
180 if (!maybeShare)
181 return maybeShare.error(); // LCOV_EXCL_LINE
182 auto const& mptIssuanceID = *maybeShare;
183
184 vault->setFieldIssue(sfAsset, STIssue{sfAsset, asset});
185 vault->at(sfFlags) = txFlags & tfVaultPrivate;
186 vault->at(sfSequence) = sequence;
187 vault->at(sfOwner) = account_;
188 vault->at(sfAccount) = pseudoId;
189 vault->at(sfAssetsTotal) = Number(0);
190 vault->at(sfAssetsAvailable) = Number(0);
191 vault->at(sfLossUnrealized) = Number(0);
192 // Leave default values for AssetTotal and AssetAvailable, both zero.
193 if (auto value = tx[~sfAssetsMaximum])
194 vault->at(sfAssetsMaximum) = *value;
195 vault->at(sfShareMPTID) = mptIssuanceID;
196 if (auto value = tx[~sfData])
197 vault->at(sfData) = *value;
198 // Required field, default to vaultStrategyFirstComeFirstServe
199 if (auto value = tx[~sfWithdrawalPolicy])
200 vault->at(sfWithdrawalPolicy) = *value;
201 else
202 vault->at(sfWithdrawalPolicy) = vaultStrategyFirstComeFirstServe;
203 if (scale)
204 vault->at(sfScale) = scale;
205 view().insert(vault);
206
207 // Explicitly create MPToken for the vault owner
208 if (auto const err = authorizeMPToken(view(), mPriorBalance, mptIssuanceID, account_, ctx_.journal);
209 !isTesSuccess(err))
210 return err;
211
212 // If the vault is private, set the authorized flag for the vault owner
213 if (txFlags & tfVaultPrivate)
214 {
215 if (auto const err =
216 authorizeMPToken(view(), mPriorBalance, mptIssuanceID, pseudoId, ctx_.journal, {}, account_);
217 !isTesSuccess(err))
218 return err;
219 }
220
221 associateAsset(*vault, asset);
222
223 return tesSUCCESS;
224}
225
226} // 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:208
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:118
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
std::uint32_t getFlags() const
Definition STObject.cpp:492
std::uint32_t getSeqValue() const
Returns the first non-zero value of (Sequence, TicketSequence).
Definition STTx.cpp:208
AccountID const account_
Definition Transactor.h:113
beast::Journal const j_
Definition Transactor.h:111
ApplyView & view()
Definition Transactor.h:129
XRPAmount mPriorBalance
Definition Transactor.h:114
static bool validDataLength(std::optional< Slice > const &slice, std::size_t maxLength)
ApplyContext & ctx_
Definition Transactor.h:109
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:492
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:510
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:1250
std::uint8_t constexpr vaultStrategyFirstComeFirstServe
Vault withdrawal policies.
Definition Protocol.h:241
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition View.cpp:971
TER canAddHolding(ReadView const &view, Asset const &asset)
Definition View.cpp:1101
@ tefINTERNAL
Definition TER.h:154
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)
Definition View.cpp:1326
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Definition View.cpp:1020
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:941
std::uint8_t constexpr vaultDefaultIOUScale
Default IOU scale factor for a Vault.
Definition Protocol.h:244
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:194
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object, SF_UINT64 const &node=sfOwnerNode)
Definition View.cpp:961
@ temMALFORMED
Definition TER.h:68
bool isTesSuccess(TER x) noexcept
Definition TER.h:650
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:1033
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
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:54
ReadView const & view
Definition Transactor.h:57
State information when preflighting a tx.
Definition Transactor.h:16