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 ripple {
18
19bool
21{
22 if (!ctx.rules.enabled(featureMPTokensV1))
23 return false;
24
25 return !ctx.tx.isFieldPresent(sfDomainID) ||
26 ctx.rules.enabled(featurePermissionedDomains);
27}
28
34
37{
38 if (!validDataLength(ctx.tx[~sfData], maxDataPayloadLength))
39 return temMALFORMED;
40
41 if (auto const withdrawalPolicy = ctx.tx[~sfWithdrawalPolicy])
42 {
43 // Enforce valid withdrawal policy
44 if (*withdrawalPolicy != vaultStrategyFirstComeFirstServe)
45 return temMALFORMED;
46 }
47
48 if (auto const domain = ctx.tx[~sfDomainID])
49 {
50 if (*domain == beast::zero)
51 return temMALFORMED;
52 else if ((ctx.tx.getFlags() & tfVaultPrivate) == 0)
53 return temMALFORMED; // DomainID only allowed on private vaults
54 }
55
56 if (auto const assetMax = ctx.tx[~sfAssetsMaximum])
57 {
58 if (*assetMax < beast::zero)
59 return temMALFORMED;
60 }
61
62 if (auto const metadata = ctx.tx[~sfMPTokenMetadata])
63 {
64 if (metadata->length() == 0 ||
65 metadata->length() > maxMPTokenMetadataLength)
66 return temMALFORMED;
67 }
68
69 if (auto const scale = ctx.tx[~sfScale])
70 {
71 auto const vaultAsset = ctx.tx[sfAsset];
72 if (vaultAsset.holds<MPTIssue>() || vaultAsset.native())
73 return temMALFORMED;
74
75 if (scale > vaultMaximumIOUScale)
76 return temMALFORMED;
77 }
78
79 return tesSUCCESS;
80}
81
84{
85 // One reserve increment is typically much greater than one base fee.
87}
88
89TER
91{
92 auto const vaultAsset = ctx.tx[sfAsset];
93 auto const account = ctx.tx[sfAccount];
94
95 if (auto const ter = canAddHolding(ctx.view, vaultAsset))
96 return ter;
97
98 // Check for pseudo-account issuers - we do not want a vault to hold such
99 // assets (e.g. MPT shares to other vaults or AMM LPTokens) as they would be
100 // impossible to clawback (should the need arise)
101 if (!vaultAsset.native())
102 {
103 if (isPseudoAccount(ctx.view, vaultAsset.getIssuer()))
104 return tecWRONG_ASSET;
105 }
106
107 // Cannot create Vault for an Asset frozen for the vault owner
108 if (isFrozen(ctx.view, account, vaultAsset))
109 return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
110
111 if (auto const domain = ctx.tx[~sfDomainID])
112 {
113 auto const sleDomain =
115 if (!sleDomain)
116 return tecOBJECT_NOT_FOUND;
117 }
118
119 auto const sequence = ctx.tx.getSeqValue();
120 if (auto const accountId = pseudoAccountAddress(
121 ctx.view, keylet::vault(account, sequence).key);
122 accountId == beast::zero)
124
125 return tesSUCCESS;
126}
127
128TER
130{
131 // All return codes in `doApply` must be `tec`, `ter`, or `tes`.
132 // As we move checks into `preflight` and `preclaim`,
133 // we can consider downgrading them to `tef` or `tem`.
134
135 auto const& tx = ctx_.tx;
136 auto const sequence = tx.getSeqValue();
137 auto const owner = view().peek(keylet::account(account_));
138 if (owner == nullptr)
139 return tefINTERNAL; // LCOV_EXCL_LINE
140
141 auto vault = std::make_shared<SLE>(keylet::vault(account_, sequence));
142
143 if (auto ter = dirLink(view(), account_, vault))
144 return ter;
145 adjustOwnerCount(view(), owner, 1, j_);
146 auto ownerCount = owner->at(sfOwnerCount);
147 if (mPriorBalance < 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, mPriorBalance, asset, j_);
158 !isTesSuccess(ter))
159 return ter;
160
161 std::uint8_t const scale = (asset.holds<MPTIssue>() || asset.native())
162 ? 0
163 : ctx_.tx[~sfScale].value_or(vaultDefaultIOUScale);
164
165 auto txFlags = tx.getFlags();
166 std::uint32_t mptFlags = 0;
167 if ((txFlags & tfVaultShareNonTransferable) == 0)
169 if (txFlags & tfVaultPrivate)
170 mptFlags |= lsfMPTRequireAuth;
171
172 // Note, here we are **not** creating an MPToken for the assets held in
173 // the vault. That MPToken or TrustLine/RippleState is created above, in
174 // addEmptyHolding. Here we are creating MPTokenIssuance for the shares
175 // in the vault
176 auto maybeShare = MPTokenIssuanceCreate::create(
177 view(),
178 j_,
179 {
180 .priorBalance = std::nullopt,
181 .account = pseudoId->value(),
182 .sequence = 1,
183 .flags = mptFlags,
184 .assetScale = scale,
185 .metadata = tx[~sfMPTokenMetadata],
186 .domainId = tx[~sfDomainID],
187 });
188 if (!maybeShare)
189 return maybeShare.error(); // LCOV_EXCL_LINE
190 auto const& mptIssuanceID = *maybeShare;
191
192 vault->setFieldIssue(sfAsset, STIssue{sfAsset, asset});
193 vault->at(sfFlags) = txFlags & tfVaultPrivate;
194 vault->at(sfSequence) = sequence;
195 vault->at(sfOwner) = account_;
196 vault->at(sfAccount) = pseudoId;
197 vault->at(sfAssetsTotal) = Number(0);
198 vault->at(sfAssetsAvailable) = Number(0);
199 vault->at(sfLossUnrealized) = Number(0);
200 // Leave default values for AssetTotal and AssetAvailable, both zero.
201 if (auto value = tx[~sfAssetsMaximum])
202 vault->at(sfAssetsMaximum) = *value;
203 vault->at(sfShareMPTID) = mptIssuanceID;
204 if (auto value = tx[~sfData])
205 vault->at(sfData) = *value;
206 // Required field, default to vaultStrategyFirstComeFirstServe
207 if (auto value = tx[~sfWithdrawalPolicy])
208 vault->at(sfWithdrawalPolicy) = *value;
209 else
210 vault->at(sfWithdrawalPolicy) = vaultStrategyFirstComeFirstServe;
211 if (scale)
212 vault->at(sfScale) = scale;
213 view().insert(vault);
214
215 // Explicitly create MPToken for the vault owner
216 if (auto const err = authorizeMPToken(
217 view(), mPriorBalance, mptIssuanceID, account_, ctx_.journal);
218 !isTesSuccess(err))
219 return err;
220
221 // If the vault is private, set the authorized flag for the vault owner
222 if (txFlags & tfVaultPrivate)
223 {
224 if (auto const err = authorizeMPToken(
225 view(),
227 mptIssuanceID,
228 pseudoId,
230 {},
231 account_);
232 !isTesSuccess(err))
233 return err;
234 }
235
236 return tesSUCCESS;
237}
238
239} // namespace ripple
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)
A view into a ledger.
Definition ReadView.h:32
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
ApplyView & view()
Definition Transactor.h:144
static XRPAmount calculateOwnerReserveFee(ReadView const &view, STTx const &tx)
beast::Journal const j_
Definition Transactor.h:126
XRPAmount mPriorBalance
Definition Transactor.h:129
static bool validDataLength(std::optional< Slice > const &slice, std::size_t maxLength)
ApplyContext & ctx_
Definition Transactor.h:124
TER doApply() override
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static bool checkExtraFeatures(PreflightContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
T is_same_v
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:551
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:545
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
@ lsfMPTCanTransfer
@ lsfMPTCanEscrow
@ lsfMPTRequireAuth
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:1013
constexpr std::uint32_t const tfVaultPrivate
Definition TxFlags.h:251
std::size_t constexpr maxDataPayloadLength
The maximum length of Data payload.
Definition Protocol.h:103
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:228
@ tefINTERNAL
Definition TER.h:154
std::size_t constexpr maxMPTokenMetadataLength
The maximum length of MPTokenMetadata.
Definition Protocol.h:97
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:1113
std::uint8_t constexpr vaultMaximumIOUScale
Maximum scale factor for a Vault.
Definition Protocol.h:113
@ tecOBJECT_NOT_FOUND
Definition TER.h:308
@ tecFROZEN
Definition TER.h:285
@ tecWRONG_ASSET
Definition TER.h:342
@ tecINSUFFICIENT_RESERVE
Definition TER.h:289
@ tecLOCKED
Definition TER.h:340
@ tesSUCCESS
Definition TER.h:226
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:1197
constexpr std::uint32_t const tfVaultShareNonTransferable
Definition TxFlags.h:253
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition View.cpp:1050
bool isTesSuccess(TER x) noexcept
Definition TER.h:659
std::uint8_t constexpr vaultStrategyFirstComeFirstServe
Vault withdrawal policies.
Definition Protocol.h:106
TER canAddHolding(ReadView const &view, Asset const &asset)
Definition View.cpp:1187
@ terADDRESS_COLLISION
Definition TER.h:209
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:1271
constexpr std::uint32_t const tfVaultCreateMask
Definition TxFlags.h:254
std::uint8_t constexpr vaultDefaultIOUScale
Default IOU scale factor for a Vault.
Definition Protocol.h:109
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition View.cpp:1099
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object)
Definition View.cpp:1039
@ temMALFORMED
Definition TER.h:68
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