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
82TER
84{
85 auto const vaultAsset = ctx.tx[sfAsset];
86 auto const account = ctx.tx[sfAccount];
87
88 if (auto const ter = canAddHolding(ctx.view, vaultAsset))
89 return ter;
90
91 // Check for pseudo-account issuers - we do not want a vault to hold such
92 // assets (e.g. MPT shares to other vaults or AMM LPTokens) as they would be
93 // impossible to clawback (should the need arise)
94 if (!vaultAsset.native())
95 {
96 if (isPseudoAccount(ctx.view, vaultAsset.getIssuer()))
97 return tecWRONG_ASSET;
98 }
99
100 // Cannot create Vault for an Asset frozen for the vault owner
101 if (isFrozen(ctx.view, account, vaultAsset))
102 return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
103
104 if (auto const domain = ctx.tx[~sfDomainID])
105 {
106 auto const sleDomain =
108 if (!sleDomain)
109 return tecOBJECT_NOT_FOUND;
110 }
111
112 auto const sequence = ctx.tx.getSeqValue();
113 if (auto const accountId = pseudoAccountAddress(
114 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_);
152 !isTesSuccess(ter))
153 return ter;
154
155 std::uint8_t const scale = (asset.holds<MPTIssue>() || asset.native())
156 ? 0
157 : ctx_.tx[~sfScale].value_or(vaultDefaultIOUScale);
158
159 auto txFlags = tx.getFlags();
160 std::uint32_t mptFlags = 0;
161 if ((txFlags & tfVaultShareNonTransferable) == 0)
163 if (txFlags & tfVaultPrivate)
164 mptFlags |= lsfMPTRequireAuth;
165
166 // Note, here we are **not** creating an MPToken for the assets held in
167 // the vault. That MPToken or TrustLine/RippleState is created above, in
168 // addEmptyHolding. Here we are creating MPTokenIssuance for the shares
169 // in the vault
170 auto maybeShare = MPTokenIssuanceCreate::create(
171 view(),
172 j_,
173 {
174 .priorBalance = std::nullopt,
175 .account = pseudoId->value(),
176 .sequence = 1,
177 .flags = mptFlags,
178 .assetScale = scale,
179 .metadata = tx[~sfMPTokenMetadata],
180 .domainId = tx[~sfDomainID],
181 });
182 if (!maybeShare)
183 return maybeShare.error(); // LCOV_EXCL_LINE
184 auto const& mptIssuanceID = *maybeShare;
185
186 vault->setFieldIssue(sfAsset, STIssue{sfAsset, asset});
187 vault->at(sfFlags) = txFlags & tfVaultPrivate;
188 vault->at(sfSequence) = sequence;
189 vault->at(sfOwner) = account_;
190 vault->at(sfAccount) = pseudoId;
191 vault->at(sfAssetsTotal) = Number(0);
192 vault->at(sfAssetsAvailable) = Number(0);
193 vault->at(sfLossUnrealized) = Number(0);
194 // Leave default values for AssetTotal and AssetAvailable, both zero.
195 if (auto value = tx[~sfAssetsMaximum])
196 vault->at(sfAssetsMaximum) = *value;
197 vault->at(sfShareMPTID) = mptIssuanceID;
198 if (auto value = tx[~sfData])
199 vault->at(sfData) = *value;
200 // Required field, default to vaultStrategyFirstComeFirstServe
201 if (auto value = tx[~sfWithdrawalPolicy])
202 vault->at(sfWithdrawalPolicy) = *value;
203 else
204 vault->at(sfWithdrawalPolicy) = vaultStrategyFirstComeFirstServe;
205 if (scale)
206 vault->at(sfScale) = scale;
207 view().insert(vault);
208
209 // Explicitly create MPToken for the vault owner
210 if (auto const err = authorizeMPToken(
211 view(), mPriorBalance, mptIssuanceID, account_, ctx_.journal);
212 !isTesSuccess(err))
213 return err;
214
215 // If the vault is private, set the authorized flag for the vault owner
216 if (txFlags & tfVaultPrivate)
217 {
218 if (auto const err = authorizeMPToken(
219 view(),
221 mptIssuanceID,
222 pseudoId,
224 {},
225 account_);
226 !isTesSuccess(err))
227 return err;
228 }
229
230 return tesSUCCESS;
231}
232
233} // 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)
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
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)
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