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/TER.h>
15#include <xrpl/protocol/TxFlags.h>
16
17namespace xrpl {
18
19bool
21{
22 if (!ctx.rules.enabled(featureMPTokensV1))
23 return false;
24
25 if (ctx.tx.isFieldPresent(sfDomainID) &&
26 !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 ||
68 metadata->length() > maxMPTokenMetadataLength)
69 return temMALFORMED;
70 }
71
72 if (auto const scale = ctx.tx[~sfScale])
73 {
74 auto const vaultAsset = ctx.tx[sfAsset];
75 if (vaultAsset.holds<MPTIssue>() || vaultAsset.native())
76 return temMALFORMED;
77
78 if (scale > vaultMaximumIOUScale)
79 return temMALFORMED;
80 }
81
82 return tesSUCCESS;
83}
84
85TER
87{
88 auto const vaultAsset = ctx.tx[sfAsset];
89 auto const account = ctx.tx[sfAccount];
90
91 if (auto const ter = canAddHolding(ctx.view, vaultAsset))
92 return ter;
93
94 // Check for pseudo-account issuers - we do not want a vault to hold such
95 // assets (e.g. MPT shares to other vaults or AMM LPTokens) as they would be
96 // impossible to clawback (should the need arise)
97 if (!vaultAsset.native())
98 {
99 if (isPseudoAccount(ctx.view, vaultAsset.getIssuer()))
100 return tecWRONG_ASSET;
101 }
102
103 // Cannot create Vault for an Asset frozen for the vault owner
104 if (isFrozen(ctx.view, account, vaultAsset))
105 return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
106
107 if (auto const domain = ctx.tx[~sfDomainID])
108 {
109 auto const sleDomain =
111 if (!sleDomain)
112 return tecOBJECT_NOT_FOUND;
113 }
114
115 auto const sequence = ctx.tx.getSeqValue();
116 if (auto const accountId = pseudoAccountAddress(
117 ctx.view, keylet::vault(account, sequence).key);
118 accountId == beast::zero)
120
121 return tesSUCCESS;
122}
123
124TER
126{
127 // All return codes in `doApply` must be `tec`, `ter`, or `tes`.
128 // As we move checks into `preflight` and `preclaim`,
129 // we can consider downgrading them to `tef` or `tem`.
130
131 auto const& tx = ctx_.tx;
132 auto const sequence = tx.getSeqValue();
133 auto const owner = view().peek(keylet::account(account_));
134 if (owner == nullptr)
135 return tefINTERNAL; // LCOV_EXCL_LINE
136
137 auto vault = std::make_shared<SLE>(keylet::vault(account_, sequence));
138
139 if (auto ter = dirLink(view(), account_, vault))
140 return ter;
141 // We will create Vault and PseudoAccount, hence increase OwnerCount by 2
142 adjustOwnerCount(view(), owner, 2, j_);
143 auto const ownerCount = owner->at(sfOwnerCount);
144 if (mPriorBalance < view().fees().accountReserve(ownerCount))
146
147 auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID);
148 if (!maybePseudo)
149 return maybePseudo.error(); // LCOV_EXCL_LINE
150 auto& pseudo = *maybePseudo;
151 auto pseudoId = pseudo->at(sfAccount);
152 auto asset = tx[sfAsset];
153
154 if (auto ter = addEmptyHolding(view(), pseudoId, mPriorBalance, asset, j_);
155 !isTesSuccess(ter))
156 return ter;
157
158 std::uint8_t const scale = (asset.holds<MPTIssue>() || asset.native())
159 ? 0
160 : ctx_.tx[~sfScale].value_or(vaultDefaultIOUScale);
161
162 auto txFlags = tx.getFlags();
163 std::uint32_t mptFlags = 0;
164 if ((txFlags & tfVaultShareNonTransferable) == 0)
166 if (txFlags & tfVaultPrivate)
167 mptFlags |= lsfMPTRequireAuth;
168
169 // Note, here we are **not** creating an MPToken for the assets held in
170 // the vault. That MPToken or TrustLine/RippleState is created above, in
171 // addEmptyHolding. Here we are creating MPTokenIssuance for the shares
172 // in the vault
173 auto maybeShare = MPTokenIssuanceCreate::create(
174 view(),
175 j_,
176 {
177 .priorBalance = std::nullopt,
178 .account = pseudoId->value(),
179 .sequence = 1,
180 .flags = mptFlags,
181 .assetScale = scale,
182 .metadata = tx[~sfMPTokenMetadata],
183 .domainId = tx[~sfDomainID],
184 });
185 if (!maybeShare)
186 return maybeShare.error(); // LCOV_EXCL_LINE
187 auto const& mptIssuanceID = *maybeShare;
188
189 vault->setFieldIssue(sfAsset, STIssue{sfAsset, asset});
190 vault->at(sfFlags) = txFlags & tfVaultPrivate;
191 vault->at(sfSequence) = sequence;
192 vault->at(sfOwner) = account_;
193 vault->at(sfAccount) = pseudoId;
194 vault->at(sfAssetsTotal) = Number(0);
195 vault->at(sfAssetsAvailable) = Number(0);
196 vault->at(sfLossUnrealized) = Number(0);
197 // Leave default values for AssetTotal and AssetAvailable, both zero.
198 if (auto value = tx[~sfAssetsMaximum])
199 vault->at(sfAssetsMaximum) = *value;
200 vault->at(sfShareMPTID) = mptIssuanceID;
201 if (auto value = tx[~sfData])
202 vault->at(sfData) = *value;
203 // Required field, default to vaultStrategyFirstComeFirstServe
204 if (auto value = tx[~sfWithdrawalPolicy])
205 vault->at(sfWithdrawalPolicy) = *value;
206 else
207 vault->at(sfWithdrawalPolicy) = vaultStrategyFirstComeFirstServe;
208 if (scale)
209 vault->at(sfScale) = scale;
210 view().insert(vault);
211
212 // Explicitly create MPToken for the vault owner
213 if (auto const err = authorizeMPToken(
214 view(), mPriorBalance, mptIssuanceID, account_, ctx_.journal);
215 !isTesSuccess(err))
216 return err;
217
218 // If the vault is private, set the authorized flag for the vault owner
219 if (txFlags & tfVaultPrivate)
220 {
221 if (auto const err = authorizeMPToken(
222 view(),
224 mptIssuanceID,
225 pseudoId,
227 {},
228 account_);
229 !isTesSuccess(err))
230 return err;
231 }
232
233 return tesSUCCESS;
234}
235
236} // 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)
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:111
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:1439
std::uint8_t constexpr vaultStrategyFirstComeFirstServe
Vault withdrawal policies.
Definition Protocol.h:241
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition View.cpp:1175
TER canAddHolding(ReadView const &view, Asset const &asset)
Definition View.cpp:1322
@ 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: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:1515
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Definition View.cpp:1226
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:1134
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:228
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object, SF_UINT64 const &node=sfOwnerNode)
Definition View.cpp:1160
@ 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:1246
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:61
ReadView const & view
Definition Transactor.h:64
State information when preflighting a tx.
Definition Transactor.h:16