From ffb03879319e7aeadc3c68b71209e6d4ce0813de Mon Sep 17 00:00:00 2001 From: "github-merge-queue[bot]" Date: Mon, 20 Oct 2025 16:35:10 -0700 Subject: [PATCH] deploy: 83ee3788e1b93c71e4365d94f028c4e950fe4ae9 --- AMMBid_8cpp_source.html | 4 +- AMMClawback_8cpp_source.html | 2 +- AMMCreate_8cpp_source.html | 4 +- AMMDeposit_8cpp_source.html | 4 +- AMMExtended__test_8cpp_source.html | 2 +- AMMOffer_8h_source.html | 2 +- AMMUtils_8cpp_source.html | 2 +- AMMWithdraw_8cpp_source.html | 6 +- ApplyContext_8cpp_source.html | 2 +- ApplyContext_8h_source.html | 2 +- ApplyStateTable_8cpp_source.html | 2 +- ApplyStateTable_8h_source.html | 2 +- ApplyViewImpl_8cpp_source.html | 2 +- ApplyViewImpl_8h_source.html | 2 +- BookTip_8cpp_source.html | 2 +- CancelOffer_8cpp_source.html | 2 +- CanonicalTXSet_8cpp_source.html | 2 +- CashCheck_8cpp_source.html | 4 +- Clawback_8cpp_source.html | 2 +- CreateOffer_8cpp_source.html | 2 +- DeleteAccount_8cpp_source.html | 2 +- DirectStep_8cpp_source.html | 2 +- Escrow_8cpp_source.html | 14 +- Flow__test_8cpp_source.html | 2 +- InvariantCheck_8cpp_source.html | 2 +- InvariantCheck_8h_source.html | 2 +- Invariants__test_8cpp_source.html | 2 +- Livecache__test_8cpp_source.html | 2 +- MPTokenAuthorize_8cpp_source.html | 2 +- NFTokenAcceptOffer_8cpp_source.html | 2 +- OfferStream_8cpp_source.html | 2 +- Offer_8h_source.html | 2 +- PayChan_8cpp_source.html | 2 +- PaymentSandbox_8cpp_source.html | 2 +- PaymentSandbox__test_8cpp_source.html | 8 +- Payment_8cpp_source.html | 6 +- RCLConsensus_8cpp_source.html | 2 +- RCLConsensus_8h_source.html | 2 +- STNumber__test_8cpp_source.html | 2 +- SetTrust_8cpp_source.html | 4 +- StrandFlow_8h_source.html | 2 +- Transactor_8cpp_source.html | 6 +- VaultClawback_8cpp_source.html | 8 +- VaultCreate_8cpp_source.html | 2 +- VaultDelete_8cpp_source.html | 2 +- VaultDeposit_8cpp_source.html | 268 +- VaultWithdraw_8cpp_source.html | 593 +- VaultWithdraw_8h_source.html | 4 +- Vault__test_8cpp_source.html | 8851 +++++++++++++------------ View_8cpp_source.html | 4097 ++++++------ View_8h_source.html | 42 +- XRPEndpointStep_8cpp_source.html | 2 +- classripple_1_1VaultWithdraw.html | 4 +- classripple_1_1Vault__test.html | 26 +- namespaceripple.html | 64 +- 55 files changed, 7151 insertions(+), 6936 deletions(-) diff --git a/AMMBid_8cpp_source.html b/AMMBid_8cpp_source.html index a07e443a13..e83d485ae5 100644 --- a/AMMBid_8cpp_source.html +++ b/AMMBid_8cpp_source.html @@ -519,11 +519,11 @@ $(document).ready(function() { init_codefold(0); });
std::uint32_t constexpr TOTAL_TIME_SLOT_SECS
Definition AMMCore.h:34
std::uint16_t constexpr AUCTION_SLOT_TIME_INTERVALS
Definition AMMCore.h:35
std::optional< std::uint8_t > ammAuctionTimeSlot(std::uint64_t current, STObject const &auctionSlot)
Get time slot of the auction slot.
Definition AMMCore.cpp:108
-
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2359
+
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2365
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
Definition AMMCore.cpp:129
@ current
This was a new validation and was added.
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
std::uint32_t constexpr AUCTION_SLOT_MIN_FEE_FRACTION
Definition AMMCore.h:39
STAmount adjustLPTokens(STAmount const &lptAMMBalance, STAmount const &lpTokens, IsDeposit isDeposit)
Adjust LP tokens to deposit/withdraw.
STAmount ammLPHolds(ReadView const &view, Currency const &cur1, Currency const &cur2, AccountID const &ammAccount, AccountID const &lpAccount, beast::Journal const j)
Get the balance of LP tokens.
Definition AMMUtils.cpp:113
diff --git a/AMMClawback_8cpp_source.html b/AMMClawback_8cpp_source.html index 51f4335b7c..72e2a45d9e 100644 --- a/AMMClawback_8cpp_source.html +++ b/AMMClawback_8cpp_source.html @@ -492,7 +492,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecNO_PERMISSION
Definition TER.h:305
@ tecAMM_INVALID_TOKENS
Definition TER.h:331
@ tecAMM_BALANCE
Definition TER.h:329
-
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2850
+
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2856
@ No
@ tesSUCCESS
Definition TER.h:244
Expected< std::tuple< STAmount, STAmount, STAmount >, TER > ammHolds(ReadView const &view, SLE const &ammSle, std::optional< Issue > const &optIssue1, std::optional< Issue > const &optIssue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool and LP token balances.
Definition AMMUtils.cpp:47
diff --git a/AMMCreate_8cpp_source.html b/AMMCreate_8cpp_source.html index 32dc8ebe06..b4a90defef 100644 --- a/AMMCreate_8cpp_source.html +++ b/AMMCreate_8cpp_source.html @@ -503,10 +503,10 @@ $(document).ready(function() { init_codefold(0); });
@ lsfAMMNode
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
Definition AMMCore.cpp:129
Issue ammLPTIssue(Currency const &cur1, Currency const &cur2, AccountID const &ammAccountID)
Calculate LPT Issue from AMM asset pair.
Definition AMMCore.cpp:57
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:247
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition STAmount.cpp:486
-
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2479
+
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2485
void initializeFeeAuctionVote(ApplyView &view, std::shared_ptr< SLE > &ammSle, AccountID const &account, Issue const &lptIssue, std::uint16_t tfee)
Initialize Auction and Voting slots and set the trading/discounted fee.
Definition AMMUtils.cpp:340
static std::pair< TER, bool > applyCreate(ApplyContext &ctx_, Sandbox &sb, AccountID const &account_, beast::Journal j_)
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:1132
diff --git a/AMMDeposit_8cpp_source.html b/AMMDeposit_8cpp_source.html index 24b4c61ec6..9a5c137402 100644 --- a/AMMDeposit_8cpp_source.html +++ b/AMMDeposit_8cpp_source.html @@ -1129,10 +1129,10 @@ $(document).ready(function() { init_codefold(0); });
STAmount ammAssetIn(STAmount const &asset1Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset deposit given LP Tokens.
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
Definition AMMCore.cpp:129
constexpr std::uint32_t tfOneAssetLPToken
Definition TxFlags.h:249
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
STAmount getRoundedLPTokens(Rules const &rules, STAmount const &balance, Number const &frac, IsDeposit isDeposit)
Round AMM deposit/withdrawal LPToken amount.
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:247
-
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2479
+
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2485
STAmount adjustLPTokens(STAmount const &lptAMMBalance, STAmount const &lpTokens, IsDeposit isDeposit)
Adjust LP tokens to deposit/withdraw.
Number feeMult(std::uint16_t tfee)
Get fee multiplier (1 - tfee) @tfee trading fee in basis points.
Definition AMMCore.h:110
void initializeFeeAuctionVote(ApplyView &view, std::shared_ptr< SLE > &ammSle, AccountID const &account, Issue const &lptIssue, std::uint16_t tfee)
Initialize Auction and Voting slots and set the trading/discounted fee.
Definition AMMUtils.cpp:340
diff --git a/AMMExtended__test_8cpp_source.html b/AMMExtended__test_8cpp_source.html index ca153909fd..782886b727 100644 --- a/AMMExtended__test_8cpp_source.html +++ b/AMMExtended__test_8cpp_source.html @@ -4239,7 +4239,7 @@ $(document).ready(function() { init_codefold(0); });
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
Definition Seed.cpp:76
constexpr std::uint32_t tfSetFreeze
Definition TxFlags.h:118
constexpr std::uint32_t tfSetNoRipple
Definition TxFlags.h:116
-
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1641
+
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:84
@ temBAD_AMOUNT
Definition TER.h:89
@ temBAD_PATH_LOOP
Definition TER.h:97
diff --git a/AMMOffer_8h_source.html b/AMMOffer_8h_source.html index 157380e812..b5310f28fb 100644 --- a/AMMOffer_8h_source.html +++ b/AMMOffer_8h_source.html @@ -259,7 +259,7 @@ $(document).ready(function() { init_codefold(0); });
T is_same_v
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
@ Yes
diff --git a/AMMUtils_8cpp_source.html b/AMMUtils_8cpp_source.html index 6233c4967a..5f1404ed2b 100644 --- a/AMMUtils_8cpp_source.html +++ b/AMMUtils_8cpp_source.html @@ -683,7 +683,7 @@ $(document).ready(function() { init_codefold(0); });
LedgerEntryType
Identifiers for on-ledger objects.
STAmount ammAccountHolds(ReadView const &view, AccountID const &ammAccountID, Issue const &issue)
Returns total amount held by AMM for the given token.
Definition AMMUtils.cpp:211
Number root(Number f, unsigned d)
Definition Number.cpp:636
-
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition View.cpp:2800
+
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition View.cpp:2806
std::uint16_t constexpr maxDeletableAMMTrustLines
The maximum number of trustlines to delete as part of AMM account deletion cleanup.
Definition Protocol.h:147
TERSubset< CanCvtToTER > TER
Definition TER.h:645
bool withinRelativeDistance(Quality const &calcQuality, Quality const &reqQuality, Number const &dist)
Check if the relative distance between the qualities is within the requested distance.
Definition AMMHelpers.h:129
diff --git a/AMMWithdraw_8cpp_source.html b/AMMWithdraw_8cpp_source.html index 86d863c462..a24a02f5d9 100644 --- a/AMMWithdraw_8cpp_source.html +++ b/AMMWithdraw_8cpp_source.html @@ -1236,16 +1236,16 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t tfWithdrawMask
Definition TxFlags.h:258
TER deleteAMMAccount(Sandbox &view, Issue const &asset, Issue const &asset2, beast::Journal j)
Delete trustlines to AMM.
Definition AMMUtils.cpp:283
std::pair< STAmount, STAmount > adjustAssetOutByTokens(Rules const &rules, STAmount const &balance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &tokens, std::uint16_t tfee)
-
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2359
+
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2365
constexpr std::uint32_t tfLimitLPToken
Definition TxFlags.h:250
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
Definition AMMCore.cpp:129
constexpr std::uint32_t tfOneAssetLPToken
Definition TxFlags.h:249
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
STAmount getRoundedLPTokens(Rules const &rules, STAmount const &balance, Number const &frac, IsDeposit isDeposit)
Round AMM deposit/withdrawal LPToken amount.
static std::optional< STAmount > tokensWithdraw(STAmount const &lpTokens, std::optional< STAmount > const &tokensIn, std::uint32_t flags)
Expected< bool, TER > verifyAndAdjustLPTokenBalance(Sandbox &sb, STAmount const &lpTokens, std::shared_ptr< SLE > &ammSle, AccountID const &account)
Due to rounding, the LPTokenBalance of the last LP might not match the LP's trustline balance.
Definition AMMUtils.cpp:469
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:247
-
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2479
+
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2485
STAmount adjustLPTokens(STAmount const &lptAMMBalance, STAmount const &lpTokens, IsDeposit isDeposit)
Adjust LP tokens to deposit/withdraw.
constexpr std::uint32_t tfTwoAsset
Definition TxFlags.h:248
STAmount ammLPHolds(ReadView const &view, Currency const &cur1, Currency const &cur2, AccountID const &ammAccount, AccountID const &lpAccount, beast::Journal const j)
Get the balance of LP tokens.
Definition AMMUtils.cpp:113
diff --git a/ApplyContext_8cpp_source.html b/ApplyContext_8cpp_source.html index 3d01b4925f..4ca6f368a9 100644 --- a/ApplyContext_8cpp_source.html +++ b/ApplyContext_8cpp_source.html @@ -294,7 +294,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecINVARIANT_FAILED
Definition TER.h:313
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
InvariantChecks getInvariantChecks()
get a tuple of all invariant checks
ApplyFlags
Definition ApplyView.h:30
@ tapDRY_RUN
Definition ApplyView.h:49
diff --git a/ApplyContext_8h_source.html b/ApplyContext_8h_source.html index cd3d078281..a3ade3373e 100644 --- a/ApplyContext_8h_source.html +++ b/ApplyContext_8h_source.html @@ -287,7 +287,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
ApplyFlags
Definition ApplyView.h:30
@ tapBATCH
Definition ApplyView.h:45
STL namespace.
diff --git a/ApplyStateTable_8cpp_source.html b/ApplyStateTable_8cpp_source.html index 3da770ad81..a7a6024746 100644 --- a/ApplyStateTable_8cpp_source.html +++ b/ApplyStateTable_8cpp_source.html @@ -896,7 +896,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
LedgerEntryType
Identifiers for on-ledger objects.
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
STL namespace.
diff --git a/ApplyStateTable_8h_source.html b/ApplyStateTable_8h_source.html index cf6c3589dd..e7e542022b 100644 --- a/ApplyStateTable_8h_source.html +++ b/ApplyStateTable_8h_source.html @@ -293,7 +293,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
diff --git a/ApplyViewImpl_8cpp_source.html b/ApplyViewImpl_8cpp_source.html index c3254db754..dca947c921 100644 --- a/ApplyViewImpl_8cpp_source.html +++ b/ApplyViewImpl_8cpp_source.html @@ -165,7 +165,7 @@ $(document).ready(function() { init_codefold(0); });
detail::ApplyStateTable items_
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
ApplyFlags
Definition ApplyView.h:30
diff --git a/ApplyViewImpl_8h_source.html b/ApplyViewImpl_8h_source.html index a6f183d318..58c0ccd8f7 100644 --- a/ApplyViewImpl_8h_source.html +++ b/ApplyViewImpl_8h_source.html @@ -183,7 +183,7 @@ $(document).ready(function() { init_codefold(0); });
ApplyFlags flags() const override
Returns the tx apply flags.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
ApplyFlags
Definition ApplyView.h:30
diff --git a/BookTip_8cpp_source.html b/BookTip_8cpp_source.html index fc60200568..b0795ad360 100644 --- a/BookTip_8cpp_source.html +++ b/BookTip_8cpp_source.html @@ -188,7 +188,7 @@ $(document).ready(function() { init_codefold(0); });
bool dirFirst(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition View.cpp:123
uint256 getQualityNext(uint256 const &uBase)
Definition Indexes.cpp:141
uint256 getBookBase(Book const &book)
Definition Indexes.cpp:115
-
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1641
+
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
diff --git a/CancelOffer_8cpp_source.html b/CancelOffer_8cpp_source.html index ab38d9cca7..8c359d5d9d 100644 --- a/CancelOffer_8cpp_source.html +++ b/CancelOffer_8cpp_source.html @@ -190,7 +190,7 @@ $(document).ready(function() { init_codefold(0); });
@ tefINTERNAL
Definition TER.h:173
@ tesSUCCESS
Definition TER.h:244
@ terNO_ACCOUNT
Definition TER.h:217
-
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1641
+
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
@ temBAD_SEQUENCE
Definition TER.h:104
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:80
diff --git a/CanonicalTXSet_8cpp_source.html b/CanonicalTXSet_8cpp_source.html index 9e852867e9..fa462dc9c3 100644 --- a/CanonicalTXSet_8cpp_source.html +++ b/CanonicalTXSet_8cpp_source.html @@ -196,7 +196,7 @@ $(document).ready(function() { init_codefold(0); });
T make_pair(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
bool operator<(Slice const &lhs, Slice const &rhs) noexcept
Definition Slice.h:223
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
diff --git a/CashCheck_8cpp_source.html b/CashCheck_8cpp_source.html index 72fd4c8c51..ea6166fd8b 100644 --- a/CashCheck_8cpp_source.html +++ b/CashCheck_8cpp_source.html @@ -670,7 +670,7 @@ $(document).ready(function() { init_codefold(0); });
@ lsfHighAuth
@ lsfLowAuth
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:1032
-
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition View.cpp:2434
+
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition View.cpp:2440
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
Definition StrandFlow.h:105
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:247
@ tefBAD_LEDGER
Definition TER.h:170
@@ -692,7 +692,7 @@ $(document).ready(function() { init_codefold(0); });
@ tesSUCCESS
Definition TER.h:244
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
-
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1392
+
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1398
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition View.cpp:618
@ temBAD_AMOUNT
Definition TER.h:89
diff --git a/Clawback_8cpp_source.html b/Clawback_8cpp_source.html index eacd36b6aa..0c5d1074b0 100644 --- a/Clawback_8cpp_source.html +++ b/Clawback_8cpp_source.html @@ -448,7 +448,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecNO_PERMISSION
Definition TER.h:305
@ tecAMM_ACCOUNT
Definition TER.h:334
@ tecNO_LINE
Definition TER.h:301
-
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2850
+
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2856
@ tesSUCCESS
Definition TER.h:244
static TER applyHelper(ApplyContext &ctx)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:387
diff --git a/CreateOffer_8cpp_source.html b/CreateOffer_8cpp_source.html index 100bfa7dc5..0b07d0224d 100644 --- a/CreateOffer_8cpp_source.html +++ b/CreateOffer_8cpp_source.html @@ -1211,7 +1211,7 @@ $(document).ready(function() { init_codefold(0); });
@ terNO_AUTH
Definition TER.h:218
@ terNO_LINE
Definition TER.h:219
bool isTecClaim(TER x) noexcept
Definition TER.h:681
-
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1641
+
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Definition View.cpp:182
@ temBAD_ISSUER
Definition TER.h:93
@ temBAD_AMOUNT
Definition TER.h:89
diff --git a/DeleteAccount_8cpp_source.html b/DeleteAccount_8cpp_source.html index f53dc29350..e1f72a0bda 100644 --- a/DeleteAccount_8cpp_source.html +++ b/DeleteAccount_8cpp_source.html @@ -618,7 +618,7 @@ $(document).ready(function() { init_codefold(0); });
bool cdirNext(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the next entry in the directory, advancing the index.
Definition View.cpp:156
@ terNO_ACCOUNT
Definition TER.h:217
TERSubset< CanCvtToTER > TER
Definition TER.h:645
-
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1641
+
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
@ temDST_IS_SRC
Definition TER.h:108
diff --git a/DirectStep_8cpp_source.html b/DirectStep_8cpp_source.html index 6c44e05934..551c82fd19 100644 --- a/DirectStep_8cpp_source.html +++ b/DirectStep_8cpp_source.html @@ -1307,7 +1307,7 @@ $(document).ready(function() { init_codefold(0); });
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition PaySteps.cpp:34
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:762
@ tecPATH_DRY
Definition TER.h:294
-
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2850
+
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2856
@ tesSUCCESS
Definition TER.h:244
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:387
diff --git a/Escrow_8cpp_source.html b/Escrow_8cpp_source.html index dc7cf8f950..c7a3ceb995 100644 --- a/Escrow_8cpp_source.html +++ b/Escrow_8cpp_source.html @@ -1682,7 +1682,7 @@ $(document).ready(function() { init_codefold(0); });
@ fhIGNORE_FREEZE
Definition View.h:77
TER escrowCreatePreclaimHelper< MPTIssue >(PreclaimContext const &ctx, AccountID const &account, AccountID const &dest, STAmount const &amount)
Definition Escrow.cpp:284
bool isXRP(AccountID const &c)
Definition AccountID.h:90
-
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2989
+
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2995
static TER escrowFinishPreclaimHelper(PreclaimContext const &ctx, AccountID const &dest, STAmount const &amount)
bool canAdd(STAmount const &amt1, STAmount const &amt2)
Safely checks if two STAmount values can be added without overflow, underflow, or precision loss.
Definition STAmount.cpp:528
TER escrowLockApplyHelper< Issue >(ApplyView &view, AccountID const &issuer, AccountID const &sender, STAmount const &amount, beast::Journal journal)
Definition Escrow.cpp:409
@@ -1701,8 +1701,8 @@ $(document).ready(function() { init_codefold(0); });
STAmount divideRound(STAmount const &amount, Rate const &rate, bool roundUp)
Definition Rate2.cpp:104
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE > const &sleDst, beast::Journal j)
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:247
-
TER rippleUnlockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, AccountID const &uGranteeID, STAmount const &netAmount, STAmount const &grossAmount, beast::Journal j)
Definition View.cpp:3086
-
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2479
+
TER rippleUnlockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, AccountID const &uGranteeID, STAmount const &netAmount, STAmount const &grossAmount, beast::Journal j)
Definition View.cpp:3092
+
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2485
constexpr HashRouterFlags SF_CF_VALID
Definition Escrow.cpp:43
@ tefBAD_LEDGER
Definition TER.h:170
@ tefINTERNAL
Definition TER.h:173
@@ -1710,7 +1710,7 @@ $(document).ready(function() { init_codefold(0); });
@ PRIVATE6
@ PRIVATE5
TER escrowCreatePreclaimHelper< Issue >(PreclaimContext const &ctx, AccountID const &account, AccountID const &dest, STAmount const &amount)
Definition Escrow.cpp:206
-
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:2698
+
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:2704
static bool checkCondition(Slice f, Slice c)
Definition Escrow.cpp:618
TER escrowUnlockApplyHelper< MPTIssue >(ApplyView &view, Rate lockedRate, std::shared_ptr< SLE > const &sleDest, STAmount const &xrpBalance, STAmount const &amount, AccountID const &issuer, AccountID const &sender, AccountID const &receiver, bool createAsset, beast::Journal journal)
Definition Escrow.cpp:957
static NotTEC escrowCreatePreflightHelper(PreflightContext const &ctx)
@@ -1734,7 +1734,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecNO_LINE
Definition TER.h:301
@ tecINSUFFICIENT_RESERVE
Definition TER.h:307
@ tecLOCKED
Definition TER.h:358
-
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2850
+
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2856
@ tesSUCCESS
Definition TER.h:244
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:387
constexpr HashRouterFlags SF_CF_INVALID
Definition Escrow.cpp:42
@@ -1742,8 +1742,8 @@ $(document).ready(function() { init_codefold(0); });
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
TER escrowFinishPreclaimHelper< MPTIssue >(PreclaimContext const &ctx, AccountID const &dest, STAmount const &amount)
Definition Escrow.cpp:741
NotTEC escrowCreatePreflightHelper< Issue >(PreflightContext const &ctx)
Definition Escrow.cpp:94
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
-
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1392
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
+
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1398
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:63
TER escrowCancelPreclaimHelper< Issue >(PreclaimContext const &ctx, AccountID const &account, STAmount const &amount)
Definition Escrow.cpp:1250
TER escrowLockApplyHelper< MPTIssue >(ApplyView &view, AccountID const &issuer, AccountID const &sender, STAmount const &amount, beast::Journal journal)
Definition Escrow.cpp:434
diff --git a/Flow__test_8cpp_source.html b/Flow__test_8cpp_source.html index 8a00a97de2..38549c1fb3 100644 --- a/Flow__test_8cpp_source.html +++ b/Flow__test_8cpp_source.html @@ -1584,7 +1584,7 @@ $(document).ready(function() { init_codefold(0); });
@ tapNONE
Definition ApplyView.h:31
TERSubset< CanCvtToTER > TER
Definition TER.h:645
constexpr std::uint32_t tfSetNoRipple
Definition TxFlags.h:116
-
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1641
+
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
@ temBAD_PATH_LOOP
Definition TER.h:97
@ temBAD_PATH
Definition TER.h:96
diff --git a/InvariantCheck_8cpp_source.html b/InvariantCheck_8cpp_source.html index 599700205f..d33be013b6 100644 --- a/InvariantCheck_8cpp_source.html +++ b/InvariantCheck_8cpp_source.html @@ -3551,7 +3551,7 @@ $(document).ready(function() { init_codefold(0); });
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
STAmount ammLPTokens(STAmount const &asset1, STAmount const &asset2, Issue const &lptIssue)
Calculate LP Tokens given AMM pool reserves.
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
@ transactionID
transaction plus signature to give transaction ID
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
Definition Indexes.cpp:170
std::vector< SField const * > const & getPseudoAccountFields()
Definition View.cpp:1092
diff --git a/InvariantCheck_8h_source.html b/InvariantCheck_8h_source.html index 90d2cef3cc..a83085b89c 100644 --- a/InvariantCheck_8h_source.html +++ b/InvariantCheck_8h_source.html @@ -953,7 +953,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
InvariantChecks getInvariantChecks()
get a tuple of all invariant checks
diff --git a/Invariants__test_8cpp_source.html b/Invariants__test_8cpp_source.html index eaa65b1e9e..21f5e9ddc2 100644 --- a/Invariants__test_8cpp_source.html +++ b/Invariants__test_8cpp_source.html @@ -3684,7 +3684,7 @@ $(document).ready(function() { init_codefold(0); });
std::array< keyletDesc< AccountID const & >, 6 > const directAccountKeylets
Definition Indexes.h:384
@ tefINVARIANT_FAILED
Definition TER.h:183
base_uint< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:56
-
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition View.cpp:1601
+
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition View.cpp:1607
@ tecINVARIANT_FAILED
Definition TER.h:313
@ tesSUCCESS
Definition TER.h:244
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition View.cpp:1069
diff --git a/Livecache__test_8cpp_source.html b/Livecache__test_8cpp_source.html index 12a549674f..cd08168473 100644 --- a/Livecache__test_8cpp_source.html +++ b/Livecache__test_8cpp_source.html @@ -387,7 +387,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
static auto sum(TCollection const &col)
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
T sort(T... args)
Describes a connectible peer address along with some metadata.
beast::IP::Endpoint address
diff --git a/MPTokenAuthorize_8cpp_source.html b/MPTokenAuthorize_8cpp_source.html index 91fabc1007..2a7e4b8004 100644 --- a/MPTokenAuthorize_8cpp_source.html +++ b/MPTokenAuthorize_8cpp_source.html @@ -323,7 +323,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecHAS_OBLIGATIONS
Definition TER.h:317
@ tecNO_AUTH
Definition TER.h:300
@ tesSUCCESS
Definition TER.h:244
-
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:1284
+
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:1290
@ temMALFORMED
Definition TER.h:87
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:80
diff --git a/NFTokenAcceptOffer_8cpp_source.html b/NFTokenAcceptOffer_8cpp_source.html index f51252b780..474f07ca3d 100644 --- a/NFTokenAcceptOffer_8cpp_source.html +++ b/NFTokenAcceptOffer_8cpp_source.html @@ -751,7 +751,7 @@ $(document).ready(function() { init_codefold(0); });
@ fhZERO_IF_FROZEN
Definition View.h:77
@ lsfSellNFToken
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:53
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:173
@ tecOBJECT_NOT_FOUND
Definition TER.h:326
@ tecNFTOKEN_OFFER_TYPE_MISMATCH
Definition TER.h:323
diff --git a/OfferStream_8cpp_source.html b/OfferStream_8cpp_source.html index 41245fcea8..9e6a0bafaa 100644 --- a/OfferStream_8cpp_source.html +++ b/OfferStream_8cpp_source.html @@ -586,7 +586,7 @@ $(document).ready(function() { init_codefold(0); });
void erase(STObject &st, TypedField< U > const &f)
Remove a field in an STObject.
Definition STExchange.h:172
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:387
XRPAmount toAmount< XRPAmount >(STAmount const &amt)
-
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1641
+
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
diff --git a/Offer_8h_source.html b/Offer_8h_source.html index 0bcebc7603..e317bd9af9 100644 --- a/Offer_8h_source.html +++ b/Offer_8h_source.html @@ -525,7 +525,7 @@ $(document).ready(function() { init_codefold(0); });
bool isFeatureEnabled(uint256 const &feature)
Definition Rules.cpp:166
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
std::ostream & operator<<(std::ostream &out, base_uint< Bits, Tag > const &u)
Definition base_uint.h:647
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
std::optional< Rules > const & getCurrentTransactionRules()
Definition Rules.cpp:47
diff --git a/PayChan_8cpp_source.html b/PayChan_8cpp_source.html index 686e670196..d85a664c50 100644 --- a/PayChan_8cpp_source.html +++ b/PayChan_8cpp_source.html @@ -810,7 +810,7 @@ $(document).ready(function() { init_codefold(0); });
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
constexpr std::uint32_t tfClose
Definition TxFlags.h:135
constexpr std::uint32_t tfPayChanClaimMask
Definition TxFlags.h:136
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:63
@ terNO_ACCOUNT
Definition TER.h:217
TERSubset< CanCvtToTER > TER
Definition TER.h:645
diff --git a/PaymentSandbox_8cpp_source.html b/PaymentSandbox_8cpp_source.html index eaf93703bf..53e4f4ae58 100644 --- a/PaymentSandbox_8cpp_source.html +++ b/PaymentSandbox_8cpp_source.html @@ -551,7 +551,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
bool isXRP(AccountID const &c)
Definition AccountID.h:90
AccountID const & xrpAccount()
Compute AccountID from public key.
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
diff --git a/PaymentSandbox__test_8cpp_source.html b/PaymentSandbox__test_8cpp_source.html index e07439a249..87e7907c35 100644 --- a/PaymentSandbox__test_8cpp_source.html +++ b/PaymentSandbox__test_8cpp_source.html @@ -579,14 +579,14 @@ $(document).ready(function() { init_codefold(0); });
@ fhZERO_IF_FROZEN
Definition View.h:77
@ fhIGNORE_FREEZE
Definition View.h:77
AccountID const & xrpAccount()
Compute AccountID from public key.
-
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2359
+
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2365
constexpr std::uint32_t tfPassive
Definition TxFlags.h:98
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
constexpr std::uint32_t tfPartialPayment
Definition TxFlags.h:108
Currency const & xrpCurrency()
XRP currency.
-
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2259
+
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2265
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:123
-
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2850
+
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2856
constexpr std::uint32_t tfNoRippleDirect
Definition TxFlags.h:107
@ tesSUCCESS
Definition TER.h:244
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:387
diff --git a/Payment_8cpp_source.html b/Payment_8cpp_source.html index f92de59f0b..8eca3c8abe 100644 --- a/Payment_8cpp_source.html +++ b/Payment_8cpp_source.html @@ -908,14 +908,14 @@ $(document).ready(function() { init_codefold(0); });
@ lsfPasswordSpent
@ lsfDepositAuth
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:53
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE > const &sleDst, beast::Journal j)
-
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2479
+
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2485
@ tefEXCEPTION
Definition TER.h:172
@ tefINTERNAL
Definition TER.h:173
constexpr std::uint32_t tfPartialPayment
Definition TxFlags.h:108
void loadGranularPermission(std::shared_ptr< SLE const > const &delegate, TxType const &type, std::unordered_set< GranularPermissionType > &granularPermissions)
Load the granular permissions granted to the delegate account for the specified transaction type.
-
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:2698
+
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:2704
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:762
TER checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
@ tecNO_DELEGATE_PERMISSION
Definition TER.h:364
diff --git a/RCLConsensus_8cpp_source.html b/RCLConsensus_8cpp_source.html index b0e9b02c9b..7e38165566 100644 --- a/RCLConsensus_8cpp_source.html +++ b/RCLConsensus_8cpp_source.html @@ -1438,7 +1438,7 @@ $(document).ready(function() { init_codefold(0); });
boost::intrusive_ptr< SHAMapItem > make_shamapitem(uint256 const &tag, Slice data)
Definition SHAMapItem.h:161
Rules makeRulesGivenLedger(DigestAwareReadView const &ledger, Rules const &current)
Definition ReadView.cpp:69
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
@ tapNONE
Definition ApplyView.h:31
@ ledgerMaster
ledger master data for signing
@ proposal
proposal for signing
diff --git a/RCLConsensus_8h_source.html b/RCLConsensus_8h_source.html index 13155a77e5..8c6f8eb510 100644 --- a/RCLConsensus_8h_source.html +++ b/RCLConsensus_8h_source.html @@ -613,7 +613,7 @@ $(document).ready(function() { init_codefold(0); });
base_uint< 160, detail::NodeIDTag > NodeID
NodeID is a 160-bit hash representing one node.
Definition UintTypes.h:59
boost::outcome_v2::result< T, std::error_code > Result
Definition b58_utils.h:37
ConsensusPhase
Phases of consensus for a single ledger round.
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
@ ledgerMaster
ledger master data for signing
@ proposal
proposal for signing
diff --git a/STNumber__test_8cpp_source.html b/STNumber__test_8cpp_source.html index c6722beb95..42258734d4 100644 --- a/STNumber__test_8cpp_source.html +++ b/STNumber__test_8cpp_source.html @@ -405,7 +405,7 @@ $(document).ready(function() { init_codefold(0); });
@ out
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:123
STNumber numberFromJson(SField const &field, Json::Value const &value)
Definition STNumber.cpp:179
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
diff --git a/SetTrust_8cpp_source.html b/SetTrust_8cpp_source.html index f0755f0085..daafa8c240 100644 --- a/SetTrust_8cpp_source.html +++ b/SetTrust_8cpp_source.html @@ -911,7 +911,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t tfSetfAuth
Definition TxFlags.h:115
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:119
TER checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
-
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition View.cpp:1601
+
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition View.cpp:1607
@ tecNO_LINE_REDUNDANT
Definition TER.h:293
@ tecNO_DELEGATE_PERMISSION
Definition TER.h:364
@ tecPSEUDO_ACCOUNT
Definition TER.h:362
@@ -925,7 +925,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t tfClearDeepFreeze
Definition TxFlags.h:121
constexpr std::uint32_t tfTrustSetMask
Definition TxFlags.h:122
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
-
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1392
+
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1398
@ terNO_ACCOUNT
Definition TER.h:217
constexpr std::uint32_t tfSetFreeze
Definition TxFlags.h:118
constexpr std::uint32_t tfSetNoRipple
Definition TxFlags.h:116
diff --git a/StrandFlow_8h_source.html b/StrandFlow_8h_source.html index 4a2b112253..24f581efc7 100644 --- a/StrandFlow_8h_source.html +++ b/StrandFlow_8h_source.html @@ -1003,7 +1003,7 @@ $(document).ready(function() { init_codefold(0); });
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
bool withinRelativeDistance(Quality const &calcQuality, Quality const &reqQuality, Number const &dist)
Check if the relative distance between the qualities is within the requested distance.
Definition AMMHelpers.h:129
-
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1641
+
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
STL namespace.
T next(T... args)
diff --git a/Transactor_8cpp_source.html b/Transactor_8cpp_source.html index 31ad58b35c..2b185ce21b 100644 --- a/Transactor_8cpp_source.html +++ b/Transactor_8cpp_source.html @@ -1694,10 +1694,10 @@ $(document).ready(function() { init_codefold(0); });
@ SigBad
Signature is bad. Didn't do local checks.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
static void removeDeletedTrustLines(ApplyView &view, std::vector< uint256 > const &trustLines, beast::Journal viewJ)
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:63
XRPAmount scaleFeeLoad(XRPAmount fee, LoadFeeTrack const &feeTrack, Fees const &fees, bool bUnlimited)
-
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition View.cpp:2800
+
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition View.cpp:2806
ApplyFlags
Definition ApplyView.h:30
@ tapFAIL_HARD
Definition ApplyView.h:35
@ tapUNLIMITED
Definition ApplyView.h:42
@@ -1711,7 +1711,7 @@ $(document).ready(function() { init_codefold(0); });
@ terPRE_TICKET
Definition TER.h:226
bool isTecClaim(TER x) noexcept
Definition TER.h:681
NotTEC preflight0(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the txid.
-
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1641
+
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
constexpr std::uint32_t tfInnerBatchTxn
Definition TxFlags.h:61
@ temBAD_SRC_ACCOUNT
Definition TER.h:106
diff --git a/VaultClawback_8cpp_source.html b/VaultClawback_8cpp_source.html index 6b1c6b4eb1..fcd4c63266 100644 --- a/VaultClawback_8cpp_source.html +++ b/VaultClawback_8cpp_source.html @@ -457,10 +457,10 @@ $(document).ready(function() { init_codefold(0); });
@ lsfMPTCanClawback
@ lsfNoFreeze
@ ahIGNORE_AUTH
Definition View.h:80
-
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:2935
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:2941
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
@ tefINTERNAL
Definition TER.h:173
-
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2964
+
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2970
std::string transToken(TER code)
Definition TER.cpp:264
@ Yes
@ tecNO_ENTRY
Definition TER.h:306
@@ -476,7 +476,7 @@ $(document).ready(function() { init_codefold(0); });
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
@ yes
-
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition View.cpp:1511
+
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition View.cpp:1517
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
@ temBAD_AMOUNT
Definition TER.h:89
@ temMALFORMED
Definition TER.h:87
diff --git a/VaultCreate_8cpp_source.html b/VaultCreate_8cpp_source.html index 5f2107a098..468f4ba11b 100644 --- a/VaultCreate_8cpp_source.html +++ b/VaultCreate_8cpp_source.html @@ -414,7 +414,7 @@ $(document).ready(function() { init_codefold(0); });
std::uint8_t constexpr vaultStrategyFirstComeFirstServe
Vault withdrawal policies.
Definition Protocol.h:122
TER canAddHolding(ReadView const &view, Asset const &asset)
Definition View.cpp:1206
@ terADDRESS_COLLISION
Definition TER.h:228
-
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:1284
+
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:1290
constexpr std::uint32_t const tfVaultCreateMask
Definition TxFlags.h:273
std::uint8_t constexpr vaultDefaultIOUScale
Default IOU scale factor for a Vault.
Definition Protocol.h:125
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition View.cpp:1118
diff --git a/VaultDelete_8cpp_source.html b/VaultDelete_8cpp_source.html index 8f074ab1c3..750cdc0a3a 100644 --- a/VaultDelete_8cpp_source.html +++ b/VaultDelete_8cpp_source.html @@ -320,7 +320,7 @@ $(document).ready(function() { init_codefold(0); });
@ tesSUCCESS
Definition TER.h:244
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
-
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition View.cpp:1511
+
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition View.cpp:1517
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
@ temMALFORMED
Definition TER.h:87
uint256 key
Definition Keylet.h:40
diff --git a/VaultDeposit_8cpp_source.html b/VaultDeposit_8cpp_source.html index dddc6b517a..173a1f7a5d 100644 --- a/VaultDeposit_8cpp_source.html +++ b/VaultDeposit_8cpp_source.html @@ -290,135 +290,134 @@ $(document).ready(function() { init_codefold(0); });
202 else // !vault->isFlag(lsfVaultPrivate) || account_ == vault->at(sfOwner)
203 {
204 // No authorization needed, but must ensure there is MPToken
-
205 auto sleMpt = view().read(keylet::mptoken(mptIssuanceID, account_));
-
206 if (!sleMpt)
-
207 {
-
208 if (auto const err = authorizeMPToken(
-
209 view(),
-
210 mPriorBalance,
-
211 mptIssuanceID->value(),
-
212 account_,
-
213 ctx_.journal);
-
214 !isTesSuccess(err))
-
215 return err;
-
216 }
-
217
-
218 // If the vault is private, set the authorized flag for the vault owner
-
219 if (vault->isFlag(lsfVaultPrivate))
-
220 {
-
221 // This follows from the reverse of the outer enclosing if condition
-
222 XRPL_ASSERT(
-
223 account_ == vault->at(sfOwner),
-
224 "ripple::VaultDeposit::doApply : account is owner");
-
225 if (auto const err = authorizeMPToken(
-
226 view(),
-
227 mPriorBalance, // priorBalance
-
228 mptIssuanceID->value(), // mptIssuanceID
-
229 sleIssuance->at(sfIssuer), // account
-
230 ctx_.journal,
-
231 {}, // flags
-
232 account_ // holderID
-
233 );
-
234 !isTesSuccess(err))
-
235 return err;
-
236 }
-
237 }
-
238
-
239 STAmount sharesCreated = {vault->at(sfShareMPTID)}, assetsDeposited;
-
240 try
-
241 {
-
242 // Compute exchange before transferring any amounts.
-
243 {
-
244 auto const maybeShares =
-
245 assetsToSharesDeposit(vault, sleIssuance, amount);
-
246 if (!maybeShares)
-
247 return tecINTERNAL; // LCOV_EXCL_LINE
-
248 sharesCreated = *maybeShares;
-
249 }
-
250 if (sharesCreated == beast::zero)
-
251 return tecPRECISION_LOSS;
-
252
-
253 auto const maybeAssets =
-
254 sharesToAssetsDeposit(vault, sleIssuance, sharesCreated);
-
255 if (!maybeAssets)
-
256 return tecINTERNAL; // LCOV_EXCL_LINE
-
257 else if (*maybeAssets > amount)
-
258 {
-
259 // LCOV_EXCL_START
-
260 JLOG(j_.error()) << "VaultDeposit: would take more than offered.";
-
261 return tecINTERNAL;
-
262 // LCOV_EXCL_STOP
-
263 }
-
264 assetsDeposited = *maybeAssets;
-
265 }
-
266 catch (std::overflow_error const&)
-
267 {
-
268 // It's easy to hit this exception from Number with large enough Scale
-
269 // so we avoid spamming the log and only use debug here.
-
270 JLOG(j_.debug()) //
-
271 << "VaultDeposit: overflow error with"
-
272 << " scale=" << (int)vault->at(sfScale).value() //
-
273 << ", assetsTotal=" << vault->at(sfAssetsTotal).value()
-
274 << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount)
-
275 << ", amount=" << amount;
-
276 return tecPATH_DRY;
-
277 }
-
278
-
279 XRPL_ASSERT(
-
280 sharesCreated.asset() != assetsDeposited.asset(),
-
281 "ripple::VaultDeposit::doApply : assets are not shares");
-
282
-
283 vault->at(sfAssetsTotal) += assetsDeposited;
-
284 vault->at(sfAssetsAvailable) += assetsDeposited;
-
285 view().update(vault);
-
286
-
287 // A deposit must not push the vault over its limit.
-
288 auto const maximum = *vault->at(sfAssetsMaximum);
-
289 if (maximum != 0 && *vault->at(sfAssetsTotal) > maximum)
-
290 return tecLIMIT_EXCEEDED;
-
291
-
292 // Transfer assets from depositor to vault.
-
293 if (auto const ter = accountSend(
-
294 view(),
-
295 account_,
-
296 vaultAccount,
-
297 assetsDeposited,
-
298 j_,
-
299 WaiveTransferFee::Yes);
-
300 !isTesSuccess(ter))
-
301 return ter;
-
302
-
303 // Sanity check
-
304 if (accountHolds(
-
305 view(),
-
306 account_,
-
307 assetsDeposited.asset(),
-
308 FreezeHandling::fhIGNORE_FREEZE,
-
309 AuthHandling::ahIGNORE_AUTH,
-
310 j_) < beast::zero)
-
311 {
-
312 // LCOV_EXCL_START
-
313 JLOG(j_.error()) << "VaultDeposit: negative balance of account assets.";
-
314 return tefINTERNAL;
-
315 // LCOV_EXCL_STOP
-
316 }
-
317
-
318 // Transfer shares from vault to depositor.
-
319 if (auto const ter = accountSend(
-
320 view(),
-
321 vaultAccount,
-
322 account_,
-
323 sharesCreated,
-
324 j_,
-
325 WaiveTransferFee::Yes);
-
326 !isTesSuccess(ter))
-
327 return ter;
-
328
-
329 return tesSUCCESS;
-
330}
+
205 if (!view().exists(keylet::mptoken(mptIssuanceID, account_)))
+
206 {
+
207 if (auto const err = authorizeMPToken(
+
208 view(),
+
209 mPriorBalance,
+
210 mptIssuanceID->value(),
+
211 account_,
+
212 ctx_.journal);
+
213 !isTesSuccess(err))
+
214 return err;
+
215 }
+
216
+
217 // If the vault is private, set the authorized flag for the vault owner
+
218 if (vault->isFlag(lsfVaultPrivate))
+
219 {
+
220 // This follows from the reverse of the outer enclosing if condition
+
221 XRPL_ASSERT(
+
222 account_ == vault->at(sfOwner),
+
223 "ripple::VaultDeposit::doApply : account is owner");
+
224 if (auto const err = authorizeMPToken(
+
225 view(),
+
226 mPriorBalance, // priorBalance
+
227 mptIssuanceID->value(), // mptIssuanceID
+
228 sleIssuance->at(sfIssuer), // account
+
229 ctx_.journal,
+
230 {}, // flags
+
231 account_ // holderID
+
232 );
+
233 !isTesSuccess(err))
+
234 return err;
+
235 }
+
236 }
+
237
+
238 STAmount sharesCreated = {vault->at(sfShareMPTID)}, assetsDeposited;
+
239 try
+
240 {
+
241 // Compute exchange before transferring any amounts.
+
242 {
+
243 auto const maybeShares =
+
244 assetsToSharesDeposit(vault, sleIssuance, amount);
+
245 if (!maybeShares)
+
246 return tecINTERNAL; // LCOV_EXCL_LINE
+
247 sharesCreated = *maybeShares;
+
248 }
+
249 if (sharesCreated == beast::zero)
+
250 return tecPRECISION_LOSS;
+
251
+
252 auto const maybeAssets =
+
253 sharesToAssetsDeposit(vault, sleIssuance, sharesCreated);
+
254 if (!maybeAssets)
+
255 return tecINTERNAL; // LCOV_EXCL_LINE
+
256 else if (*maybeAssets > amount)
+
257 {
+
258 // LCOV_EXCL_START
+
259 JLOG(j_.error()) << "VaultDeposit: would take more than offered.";
+
260 return tecINTERNAL;
+
261 // LCOV_EXCL_STOP
+
262 }
+
263 assetsDeposited = *maybeAssets;
+
264 }
+
265 catch (std::overflow_error const&)
+
266 {
+
267 // It's easy to hit this exception from Number with large enough Scale
+
268 // so we avoid spamming the log and only use debug here.
+
269 JLOG(j_.debug()) //
+
270 << "VaultDeposit: overflow error with"
+
271 << " scale=" << (int)vault->at(sfScale).value() //
+
272 << ", assetsTotal=" << vault->at(sfAssetsTotal).value()
+
273 << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount)
+
274 << ", amount=" << amount;
+
275 return tecPATH_DRY;
+
276 }
+
277
+
278 XRPL_ASSERT(
+
279 sharesCreated.asset() != assetsDeposited.asset(),
+
280 "ripple::VaultDeposit::doApply : assets are not shares");
+
281
+
282 vault->at(sfAssetsTotal) += assetsDeposited;
+
283 vault->at(sfAssetsAvailable) += assetsDeposited;
+
284 view().update(vault);
+
285
+
286 // A deposit must not push the vault over its limit.
+
287 auto const maximum = *vault->at(sfAssetsMaximum);
+
288 if (maximum != 0 && *vault->at(sfAssetsTotal) > maximum)
+
289 return tecLIMIT_EXCEEDED;
+
290
+
291 // Transfer assets from depositor to vault.
+
292 if (auto const ter = accountSend(
+
293 view(),
+
294 account_,
+
295 vaultAccount,
+
296 assetsDeposited,
+
297 j_,
+
298 WaiveTransferFee::Yes);
+
299 !isTesSuccess(ter))
+
300 return ter;
+
301
+
302 // Sanity check
+
303 if (accountHolds(
+
304 view(),
+
305 account_,
+
306 assetsDeposited.asset(),
+
307 FreezeHandling::fhIGNORE_FREEZE,
+
308 AuthHandling::ahIGNORE_AUTH,
+
309 j_) < beast::zero)
+
310 {
+
311 // LCOV_EXCL_START
+
312 JLOG(j_.error()) << "VaultDeposit: negative balance of account assets.";
+
313 return tefINTERNAL;
+
314 // LCOV_EXCL_STOP
+
315 }
+
316
+
317 // Transfer shares from vault to depositor.
+
318 if (auto const ter = accountSend(
+
319 view(),
+
320 vaultAccount,
+
321 account_,
+
322 sharesCreated,
+
323 j_,
+
324 WaiveTransferFee::Yes);
+
325 !isTesSuccess(ter))
+
326 return ter;
+
327
+
328 return tesSUCCESS;
+
329}
-
331
-
332} // namespace ripple
+
330
+
331} // namespace ripple
Stream error() const
Definition Journal.h:346
Stream debug() const
Definition Journal.h:328
ApplyView & view()
@@ -429,6 +428,7 @@ $(document).ready(function() { init_codefold(0); });
A currency issued by an account.
Definition Issue.h:33
Definition MPTIssue.h:33
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
+
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
Definition STAmount.h:50
Asset const & asset() const
Definition STAmount.h:483
@@ -449,16 +449,16 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
@ fhZERO_IF_FROZEN
Definition View.h:77
@ fhIGNORE_FREEZE
Definition View.h:77
-
std::optional< STAmount > sharesToAssetsDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2906
+
std::optional< STAmount > sharesToAssetsDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2912
@ lsfMPTCanTransfer
@ lsfVaultPrivate
@ lsfMPTLocked
-
std::optional< STAmount > assetsToSharesDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition View.cpp:2878
+
std::optional< STAmount > assetsToSharesDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition View.cpp:2884
@ ahZERO_IF_UNAUTHORIZED
Definition View.h:80
@ ahIGNORE_AUTH
Definition View.h:80
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:247
-
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2479
+
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2485
@ tefINTERNAL
Definition TER.h:173
@ Yes
@ tecNO_ENTRY
Definition TER.h:306
@@ -476,8 +476,8 @@ $(document).ready(function() { init_codefold(0); });
@ tesSUCCESS
Definition TER.h:244
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:387
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
-
TER enforceMPTokenAuthorization(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, XRPAmount const &priorBalance, beast::Journal j)
Enforce account has MPToken to match its authorization.
Definition View.cpp:2597
-
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:1284
+
TER enforceMPTokenAuthorization(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, XRPAmount const &priorBalance, beast::Journal j)
Enforce account has MPToken to match its authorization.
Definition View.cpp:2603
+
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:1290
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
@ temBAD_AMOUNT
Definition TER.h:89
@ temMALFORMED
Definition TER.h:87
diff --git a/VaultWithdraw_8cpp_source.html b/VaultWithdraw_8cpp_source.html index 7ac6774ee5..6336c635a0 100644 --- a/VaultWithdraw_8cpp_source.html +++ b/VaultWithdraw_8cpp_source.html @@ -136,325 +136,316 @@ $(document).ready(function() { init_codefold(0); });
52 return temMALFORMED;
53 }
54 }
-
55 else if (ctx.tx.isFieldPresent(sfDestinationTag))
-
56 {
-
57 JLOG(ctx.j.debug()) << "VaultWithdraw: sfDestinationTag is set but "
-
58 "sfDestination is not";
-
59 return temMALFORMED;
-
60 }
-
61
-
62 return tesSUCCESS;
-
63}
+
55
+
56 return tesSUCCESS;
+
57}
-
64
-
65TER
-
- -
67{
-
68 auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
-
69 if (!vault)
-
70 return tecNO_ENTRY;
+
58
+
59TER
+
+ +
61{
+
62 auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
+
63 if (!vault)
+
64 return tecNO_ENTRY;
+
65
+
66 auto const assets = ctx.tx[sfAmount];
+
67 auto const vaultAsset = vault->at(sfAsset);
+
68 auto const vaultShare = vault->at(sfShareMPTID);
+
69 if (assets.asset() != vaultAsset && assets.asset() != vaultShare)
+
70 return tecWRONG_ASSET;
71
-
72 auto const assets = ctx.tx[sfAmount];
-
73 auto const vaultAsset = vault->at(sfAsset);
-
74 auto const vaultShare = vault->at(sfShareMPTID);
-
75 if (assets.asset() != vaultAsset && assets.asset() != vaultShare)
-
76 return tecWRONG_ASSET;
-
77
-
78 if (vaultAsset.native())
-
79 ; // No special checks for XRP
-
80 else if (vaultAsset.holds<MPTIssue>())
-
81 {
-
82 auto mptID = vaultAsset.get<MPTIssue>().getMptID();
-
83 auto issuance = ctx.view.read(keylet::mptIssuance(mptID));
-
84 if (!issuance)
- -
86 if (!issuance->isFlag(lsfMPTCanTransfer))
-
87 {
-
88 // LCOV_EXCL_START
-
89 JLOG(ctx.j.error())
-
90 << "VaultWithdraw: vault assets are non-transferable.";
-
91 return tecNO_AUTH;
-
92 // LCOV_EXCL_STOP
-
93 }
-
94 }
-
95 else if (vaultAsset.holds<Issue>())
-
96 {
-
97 auto const issuer =
-
98 ctx.view.read(keylet::account(vaultAsset.getIssuer()));
-
99 if (!issuer)
-
100 {
-
101 // LCOV_EXCL_START
-
102 JLOG(ctx.j.error())
-
103 << "VaultWithdraw: missing issuer of vault assets.";
-
104 return tefINTERNAL;
-
105 // LCOV_EXCL_STOP
-
106 }
-
107 }
-
108
-
109 // Enforce valid withdrawal policy
-
110 if (vault->at(sfWithdrawalPolicy) != vaultStrategyFirstComeFirstServe)
-
111 {
-
112 // LCOV_EXCL_START
-
113 JLOG(ctx.j.error()) << "VaultWithdraw: invalid withdrawal policy.";
-
114 return tefINTERNAL;
-
115 // LCOV_EXCL_STOP
-
116 }
+
72 if (vaultAsset.native())
+
73 ; // No special checks for XRP
+
74 else if (vaultAsset.holds<MPTIssue>())
+
75 {
+
76 auto mptID = vaultAsset.get<MPTIssue>().getMptID();
+
77 auto issuance = ctx.view.read(keylet::mptIssuance(mptID));
+
78 if (!issuance)
+ +
80 if (!issuance->isFlag(lsfMPTCanTransfer))
+
81 {
+
82 // LCOV_EXCL_START
+
83 JLOG(ctx.j.error())
+
84 << "VaultWithdraw: vault assets are non-transferable.";
+
85 return tecNO_AUTH;
+
86 // LCOV_EXCL_STOP
+
87 }
+
88 }
+
89 else if (vaultAsset.holds<Issue>())
+
90 {
+
91 auto const issuer =
+
92 ctx.view.read(keylet::account(vaultAsset.getIssuer()));
+
93 if (!issuer)
+
94 {
+
95 // LCOV_EXCL_START
+
96 JLOG(ctx.j.error())
+
97 << "VaultWithdraw: missing issuer of vault assets.";
+
98 return tefINTERNAL;
+
99 // LCOV_EXCL_STOP
+
100 }
+
101 }
+
102
+
103 // Enforce valid withdrawal policy
+
104 if (vault->at(sfWithdrawalPolicy) != vaultStrategyFirstComeFirstServe)
+
105 {
+
106 // LCOV_EXCL_START
+
107 JLOG(ctx.j.error()) << "VaultWithdraw: invalid withdrawal policy.";
+
108 return tefINTERNAL;
+
109 // LCOV_EXCL_STOP
+
110 }
+
111
+
112 auto const account = ctx.tx[sfAccount];
+
113 auto const dstAcct = ctx.tx[~sfDestination].value_or(account);
+
114 auto const sleDst = ctx.view.read(keylet::account(dstAcct));
+
115 if (sleDst == nullptr)
+
116 return account == dstAcct ? tecINTERNAL : tecNO_DST;
117
-
118 auto const account = ctx.tx[sfAccount];
-
119 auto const dstAcct = [&]() -> AccountID {
-
120 if (ctx.tx.isFieldPresent(sfDestination))
-
121 return ctx.tx.getAccountID(sfDestination);
-
122 return account;
-
123 }();
-
124
-
125 // Withdrawal to a 3rd party destination account is essentially a transfer,
-
126 // via shares in the vault. Enforce all the usual asset transfer checks.
-
127 AuthType authType = AuthType::Legacy;
-
128 if (account != dstAcct)
-
129 {
-
130 auto const sleDst = ctx.view.read(keylet::account(dstAcct));
-
131 if (sleDst == nullptr)
-
132 return tecNO_DST;
-
133
-
134 if (sleDst->isFlag(lsfRequireDestTag) &&
-
135 !ctx.tx.isFieldPresent(sfDestinationTag))
-
136 return tecDST_TAG_NEEDED; // Cannot send without a tag
-
137
-
138 if (sleDst->isFlag(lsfDepositAuth))
-
139 {
-
140 if (!ctx.view.exists(keylet::depositPreauth(dstAcct, account)))
-
141 return tecNO_PERMISSION;
-
142 }
-
143 // The destination account must have consented to receive the asset by
-
144 // creating a RippleState or MPToken
-
145 authType = AuthType::StrongAuth;
-
146 }
-
147
-
148 // Destination MPToken (for an MPT) or trust line (for an IOU) must exist
-
149 // if not sending to Account.
-
150 if (auto const ter = requireAuth(ctx.view, vaultAsset, dstAcct, authType);
-
151 !isTesSuccess(ter))
-
152 return ter;
-
153
-
154 // Cannot withdraw from a Vault an Asset frozen for the destination account
-
155 if (auto const ret = checkFrozen(ctx.view, dstAcct, vaultAsset))
-
156 return ret;
-
157
-
158 if (auto const ret = checkFrozen(ctx.view, account, vaultShare))
-
159 return ret;
-
160
-
161 return tesSUCCESS;
-
162}
+
118 if (sleDst->isFlag(lsfRequireDestTag) &&
+
119 !ctx.tx.isFieldPresent(sfDestinationTag))
+
120 return tecDST_TAG_NEEDED; // Cannot send without a tag
+
121
+
122 // Withdrawal to a 3rd party destination account is essentially a transfer,
+
123 // via shares in the vault. Enforce all the usual asset transfer checks.
+
124 if (account != dstAcct && sleDst->isFlag(lsfDepositAuth))
+
125 {
+
126 if (!ctx.view.exists(keylet::depositPreauth(dstAcct, account)))
+
127 return tecNO_PERMISSION;
+
128 }
+
129
+
130 // If sending to Account (i.e. not a transfer), we will also create (only
+
131 // if authorized) a trust line or MPToken as needed, in doApply().
+
132 // Destination MPToken or trust line must exist if _not_ sending to Account.
+
133 AuthType const authType =
+
134 account == dstAcct ? AuthType::WeakAuth : AuthType::StrongAuth;
+
135 if (auto const ter = requireAuth(ctx.view, vaultAsset, dstAcct, authType);
+
136 !isTesSuccess(ter))
+
137 return ter;
+
138
+
139 // Cannot withdraw from a Vault an Asset frozen for the destination account
+
140 if (auto const ret = checkFrozen(ctx.view, dstAcct, vaultAsset))
+
141 return ret;
+
142
+
143 if (auto const ret = checkFrozen(ctx.view, account, vaultShare))
+
144 return ret;
+
145
+
146 return tesSUCCESS;
+
147}
-
163
-
164TER
-
- -
166{
-
167 auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID]));
-
168 if (!vault)
-
169 return tefINTERNAL; // LCOV_EXCL_LINE
+
148
+
149TER
+
+ +
151{
+
152 auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID]));
+
153 if (!vault)
+
154 return tefINTERNAL; // LCOV_EXCL_LINE
+
155
+
156 auto const mptIssuanceID = *((*vault)[sfShareMPTID]);
+
157 auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID));
+
158 if (!sleIssuance)
+
159 {
+
160 // LCOV_EXCL_START
+
161 JLOG(j_.error()) << "VaultWithdraw: missing issuance of vault shares.";
+
162 return tefINTERNAL;
+
163 // LCOV_EXCL_STOP
+
164 }
+
165
+
166 // Note, we intentionally do not check lsfVaultPrivate flag on the Vault. If
+
167 // you have a share in the vault, it means you were at some point authorized
+
168 // to deposit into it, and this means you are also indefinitely authorized
+
169 // to withdraw from it.
170
-
171 auto const mptIssuanceID = *((*vault)[sfShareMPTID]);
-
172 auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID));
-
173 if (!sleIssuance)
-
174 {
-
175 // LCOV_EXCL_START
-
176 JLOG(j_.error()) << "VaultWithdraw: missing issuance of vault shares.";
-
177 return tefINTERNAL;
-
178 // LCOV_EXCL_STOP
-
179 }
-
180
-
181 // Note, we intentionally do not check lsfVaultPrivate flag on the Vault. If
-
182 // you have a share in the vault, it means you were at some point authorized
-
183 // to deposit into it, and this means you are also indefinitely authorized
-
184 // to withdraw from it.
-
185
-
186 auto const amount = ctx_.tx[sfAmount];
-
187 Asset const vaultAsset = vault->at(sfAsset);
-
188 MPTIssue const share{mptIssuanceID};
-
189 STAmount sharesRedeemed = {share};
-
190 STAmount assetsWithdrawn;
-
191 try
-
192 {
-
193 if (amount.asset() == vaultAsset)
-
194 {
-
195 // Fixed assets, variable shares.
-
196 {
-
197 auto const maybeShares =
-
198 assetsToSharesWithdraw(vault, sleIssuance, amount);
-
199 if (!maybeShares)
-
200 return tecINTERNAL; // LCOV_EXCL_LINE
-
201 sharesRedeemed = *maybeShares;
-
202 }
-
203
-
204 if (sharesRedeemed == beast::zero)
-
205 return tecPRECISION_LOSS;
-
206 auto const maybeAssets =
-
207 sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed);
-
208 if (!maybeAssets)
-
209 return tecINTERNAL; // LCOV_EXCL_LINE
-
210 assetsWithdrawn = *maybeAssets;
-
211 }
-
212 else if (amount.asset() == share)
-
213 {
-
214 // Fixed shares, variable assets.
-
215 sharesRedeemed = amount;
-
216 auto const maybeAssets =
-
217 sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed);
-
218 if (!maybeAssets)
-
219 return tecINTERNAL; // LCOV_EXCL_LINE
-
220 assetsWithdrawn = *maybeAssets;
-
221 }
-
222 else
-
223 return tefINTERNAL; // LCOV_EXCL_LINE
-
224 }
-
225 catch (std::overflow_error const&)
-
226 {
-
227 // It's easy to hit this exception from Number with large enough Scale
-
228 // so we avoid spamming the log and only use debug here.
-
229 JLOG(j_.debug()) //
-
230 << "VaultWithdraw: overflow error with"
-
231 << " scale=" << (int)vault->at(sfScale).value() //
-
232 << ", assetsTotal=" << vault->at(sfAssetsTotal).value()
-
233 << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount)
-
234 << ", amount=" << amount.value();
-
235 return tecPATH_DRY;
-
236 }
-
237
-
238 if (accountHolds(
-
239 view(),
-
240 account_,
-
241 share,
- - -
244 j_) < sharesRedeemed)
-
245 {
-
246 JLOG(j_.debug()) << "VaultWithdraw: account doesn't hold enough shares";
- -
248 }
-
249
-
250 auto assetsAvailable = vault->at(sfAssetsAvailable);
-
251 auto assetsTotal = vault->at(sfAssetsTotal);
-
252 [[maybe_unused]] auto const lossUnrealized = vault->at(sfLossUnrealized);
-
253 XRPL_ASSERT(
-
254 lossUnrealized <= (assetsTotal - assetsAvailable),
-
255 "ripple::VaultWithdraw::doApply : loss and assets do balance");
-
256
-
257 // The vault must have enough assets on hand. The vault may hold assets
-
258 // that it has already pledged. That is why we look at AssetAvailable
-
259 // instead of the pseudo-account balance.
-
260 if (*assetsAvailable < assetsWithdrawn)
-
261 {
-
262 JLOG(j_.debug()) << "VaultWithdraw: vault doesn't hold enough assets";
- -
264 }
-
265
-
266 assetsTotal -= assetsWithdrawn;
-
267 assetsAvailable -= assetsWithdrawn;
-
268 view().update(vault);
-
269
-
270 auto const& vaultAccount = vault->at(sfAccount);
-
271 // Transfer shares from depositor to vault.
-
272 if (auto const ter = accountSend(
-
273 view(),
-
274 account_,
-
275 vaultAccount,
-
276 sharesRedeemed,
-
277 j_,
- -
279 !isTesSuccess(ter))
-
280 return ter;
-
281
-
282 // Try to remove MPToken for shares, if the account balance is zero. Vault
-
283 // pseudo-account will never set lsfMPTAuthorized, so we ignore flags.
-
284 // Keep MPToken if holder is the vault owner.
-
285 if (account_ != vault->at(sfOwner))
-
286 {
-
287 if (auto const ter = removeEmptyHolding(
-
288 view(), account_, sharesRedeemed.asset(), j_);
-
289 isTesSuccess(ter))
-
290 {
-
291 JLOG(j_.debug()) //
-
292 << "VaultWithdraw: removed empty MPToken for vault shares"
-
293 << " MPTID=" << to_string(mptIssuanceID) //
-
294 << " account=" << toBase58(account_);
-
295 }
-
296 else if (ter != tecHAS_OBLIGATIONS)
-
297 {
-
298 // LCOV_EXCL_START
-
299 JLOG(j_.error()) //
-
300 << "VaultWithdraw: failed to remove MPToken for vault shares"
-
301 << " MPTID=" << to_string(mptIssuanceID) //
-
302 << " account=" << toBase58(account_) //
-
303 << " with result: " << transToken(ter);
-
304 return ter;
-
305 // LCOV_EXCL_STOP
-
306 }
-
307 // else quietly ignore, account balance is not zero
-
308 }
-
309
-
310 auto const dstAcct = [&]() -> AccountID {
-
311 if (ctx_.tx.isFieldPresent(sfDestination))
-
312 return ctx_.tx.getAccountID(sfDestination);
-
313 return account_;
-
314 }();
-
315
-
316 // Transfer assets from vault to depositor or destination account.
-
317 if (auto const ter = accountSend(
-
318 view(),
-
319 vaultAccount,
-
320 dstAcct,
-
321 assetsWithdrawn,
-
322 j_,
- -
324 !isTesSuccess(ter))
-
325 return ter;
-
326
-
327 // Sanity check
-
328 if (accountHolds(
-
329 view(),
-
330 vaultAccount,
-
331 assetsWithdrawn.asset(),
- - -
334 j_) < beast::zero)
-
335 {
-
336 // LCOV_EXCL_START
-
337 JLOG(j_.error()) << "VaultWithdraw: negative balance of vault assets.";
-
338 return tefINTERNAL;
-
339 // LCOV_EXCL_STOP
-
340 }
-
341
-
342 return tesSUCCESS;
-
343}
+
171 auto const amount = ctx_.tx[sfAmount];
+
172 Asset const vaultAsset = vault->at(sfAsset);
+
173 MPTIssue const share{mptIssuanceID};
+
174 STAmount sharesRedeemed = {share};
+
175 STAmount assetsWithdrawn;
+
176 try
+
177 {
+
178 if (amount.asset() == vaultAsset)
+
179 {
+
180 // Fixed assets, variable shares.
+
181 {
+
182 auto const maybeShares =
+
183 assetsToSharesWithdraw(vault, sleIssuance, amount);
+
184 if (!maybeShares)
+
185 return tecINTERNAL; // LCOV_EXCL_LINE
+
186 sharesRedeemed = *maybeShares;
+
187 }
+
188
+
189 if (sharesRedeemed == beast::zero)
+
190 return tecPRECISION_LOSS;
+
191 auto const maybeAssets =
+
192 sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed);
+
193 if (!maybeAssets)
+
194 return tecINTERNAL; // LCOV_EXCL_LINE
+
195 assetsWithdrawn = *maybeAssets;
+
196 }
+
197 else if (amount.asset() == share)
+
198 {
+
199 // Fixed shares, variable assets.
+
200 sharesRedeemed = amount;
+
201 auto const maybeAssets =
+
202 sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed);
+
203 if (!maybeAssets)
+
204 return tecINTERNAL; // LCOV_EXCL_LINE
+
205 assetsWithdrawn = *maybeAssets;
+
206 }
+
207 else
+
208 return tefINTERNAL; // LCOV_EXCL_LINE
+
209 }
+
210 catch (std::overflow_error const&)
+
211 {
+
212 // It's easy to hit this exception from Number with large enough Scale
+
213 // so we avoid spamming the log and only use debug here.
+
214 JLOG(j_.debug()) //
+
215 << "VaultWithdraw: overflow error with"
+
216 << " scale=" << (int)vault->at(sfScale).value() //
+
217 << ", assetsTotal=" << vault->at(sfAssetsTotal).value()
+
218 << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount)
+
219 << ", amount=" << amount.value();
+
220 return tecPATH_DRY;
+
221 }
+
222
+
223 if (accountHolds(
+
224 view(),
+
225 account_,
+
226 share,
+ + +
229 j_) < sharesRedeemed)
+
230 {
+
231 JLOG(j_.debug()) << "VaultWithdraw: account doesn't hold enough shares";
+ +
233 }
+
234
+
235 auto assetsAvailable = vault->at(sfAssetsAvailable);
+
236 auto assetsTotal = vault->at(sfAssetsTotal);
+
237 [[maybe_unused]] auto const lossUnrealized = vault->at(sfLossUnrealized);
+
238 XRPL_ASSERT(
+
239 lossUnrealized <= (assetsTotal - assetsAvailable),
+
240 "ripple::VaultWithdraw::doApply : loss and assets do balance");
+
241
+
242 // The vault must have enough assets on hand. The vault may hold assets
+
243 // that it has already pledged. That is why we look at AssetAvailable
+
244 // instead of the pseudo-account balance.
+
245 if (*assetsAvailable < assetsWithdrawn)
+
246 {
+
247 JLOG(j_.debug()) << "VaultWithdraw: vault doesn't hold enough assets";
+ +
249 }
+
250
+
251 assetsTotal -= assetsWithdrawn;
+
252 assetsAvailable -= assetsWithdrawn;
+
253 view().update(vault);
+
254
+
255 auto const& vaultAccount = vault->at(sfAccount);
+
256 // Transfer shares from depositor to vault.
+
257 if (auto const ter = accountSend(
+
258 view(),
+
259 account_,
+
260 vaultAccount,
+
261 sharesRedeemed,
+
262 j_,
+ +
264 !isTesSuccess(ter))
+
265 return ter;
+
266
+
267 // Try to remove MPToken for shares, if the account balance is zero. Vault
+
268 // pseudo-account will never set lsfMPTAuthorized, so we ignore flags.
+
269 // Keep MPToken if holder is the vault owner.
+
270 if (account_ != vault->at(sfOwner))
+
271 {
+
272 if (auto const ter = removeEmptyHolding(
+
273 view(), account_, sharesRedeemed.asset(), j_);
+
274 isTesSuccess(ter))
+
275 {
+
276 JLOG(j_.debug()) //
+
277 << "VaultWithdraw: removed empty MPToken for vault shares"
+
278 << " MPTID=" << to_string(mptIssuanceID) //
+
279 << " account=" << toBase58(account_);
+
280 }
+
281 else if (ter != tecHAS_OBLIGATIONS)
+
282 {
+
283 // LCOV_EXCL_START
+
284 JLOG(j_.error()) //
+
285 << "VaultWithdraw: failed to remove MPToken for vault shares"
+
286 << " MPTID=" << to_string(mptIssuanceID) //
+
287 << " account=" << toBase58(account_) //
+
288 << " with result: " << transToken(ter);
+
289 return ter;
+
290 // LCOV_EXCL_STOP
+
291 }
+
292 // else quietly ignore, account balance is not zero
+
293 }
+
294
+
295 auto const dstAcct = ctx_.tx[~sfDestination].value_or(account_);
+
296 if (!vaultAsset.native() && //
+
297 dstAcct != vaultAsset.getIssuer() && //
+
298 dstAcct == account_)
+
299 {
+
300 if (auto const ter = addEmptyHolding(
+
301 view(), account_, mPriorBalance, vaultAsset, j_);
+
302 !isTesSuccess(ter) && ter != tecDUPLICATE)
+
303 return ter;
+
304 }
+
305
+
306 // Transfer assets from vault to depositor or destination account.
+
307 if (auto const ter = accountSend(
+
308 view(),
+
309 vaultAccount,
+
310 dstAcct,
+
311 assetsWithdrawn,
+
312 j_,
+ +
314 !isTesSuccess(ter))
+
315 return ter;
+
316
+
317 // Sanity check
+
318 if (accountHolds(
+
319 view(),
+
320 vaultAccount,
+
321 assetsWithdrawn.asset(),
+ + +
324 j_) < beast::zero)
+
325 {
+
326 // LCOV_EXCL_START
+
327 JLOG(j_.error()) << "VaultWithdraw: negative balance of vault assets.";
+
328 return tefINTERNAL;
+
329 // LCOV_EXCL_STOP
+
330 }
+
331
+
332 return tesSUCCESS;
+
333}
-
344
-
345} // namespace ripple
+
334
+
335} // namespace ripple
Stream error() const
Definition Journal.h:346
Stream debug() const
Definition Journal.h:328
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
+
bool native() const
Definition Asset.h:101
+
AccountID const & getIssuer() const
Definition Asset.cpp:36
A currency issued by an account.
Definition Issue.h:33
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
Asset const & asset() const
Definition STAmount.h:483
-
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:657
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:484
AccountID const account_
Definition Transactor.h:147
ApplyView & view()
Definition Transactor.h:163
beast::Journal const j_
Definition Transactor.h:145
+
XRPAmount mPriorBalance
Definition Transactor.h:148
ApplyContext & ctx_
Definition Transactor.h:143
-
static TER preclaim(PreclaimContext const &ctx)
+
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
- - +
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:526
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:564
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
@@ -468,19 +459,20 @@ $(document).ready(function() { init_codefold(0); });
@ lsfRequireDestTag
AuthType
Definition View.h:786
- +
@ ahIGNORE_AUTH
Definition View.h:80
-
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:2935
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
-
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2479
+
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:2941
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
+
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2485
@ tefINTERNAL
Definition TER.h:173
-
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2964
+
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2970
std::string transToken(TER code)
Definition TER.cpp:264
@ tecNO_ENTRY
Definition TER.h:306
@ tecNO_DST
Definition TER.h:290
@ tecOBJECT_NOT_FOUND
Definition TER.h:326
+
@ tecDUPLICATE
Definition TER.h:315
@ tecINSUFFICIENT_FUNDS
Definition TER.h:325
@ tecINTERNAL
Definition TER.h:310
@ tecNO_PERMISSION
Definition TER.h:305
@@ -491,11 +483,12 @@ $(document).ready(function() { init_codefold(0); });
@ tecPATH_DRY
Definition TER.h:294
@ tecNO_AUTH
Definition TER.h:300
@ tesSUCCESS
Definition TER.h:244
+
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:1216
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:387
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
std::uint8_t constexpr vaultStrategyFirstComeFirstServe
Vault withdrawal policies.
Definition Protocol.h:122
-
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition View.cpp:1511
+
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition View.cpp:1517
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
@ temBAD_AMOUNT
Definition TER.h:89
@ temMALFORMED
Definition TER.h:87
diff --git a/VaultWithdraw_8h_source.html b/VaultWithdraw_8h_source.html index 4d7dca1270..f47476fad3 100644 --- a/VaultWithdraw_8h_source.html +++ b/VaultWithdraw_8h_source.html @@ -139,9 +139,9 @@ $(document).ready(function() { init_codefold(0); }); -
static TER preclaim(PreclaimContext const &ctx)
+
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
- +
static constexpr ConsequencesFactoryType ConsequencesFactory
VaultWithdraw(ApplyContext &ctx)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
diff --git a/Vault__test_8cpp_source.html b/Vault__test_8cpp_source.html index 80839edb9b..f87b1a7628 100644 --- a/Vault__test_8cpp_source.html +++ b/Vault__test_8cpp_source.html @@ -146,1631 +146,1631 @@ $(document).ready(function() { init_codefold(0); });
60 {
61 using namespace test::jtx;
-
62
-
63 auto const testSequence = [this](
-
64 std::string const& prefix,
-
65 Env& env,
-
66 Account const& issuer,
-
67 Account const& owner,
-
68 Account const& depositor,
-
69 Account const& charlie,
-
70 Vault& vault,
-
71 PrettyAsset const& asset) {
-
72 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
73 tx[sfData] = "AFEED00E";
-
74 tx[sfAssetsMaximum] = asset(100).number();
-
75 env(tx);
-
76 env.close();
-
77 BEAST_EXPECT(env.le(keylet));
-
78 std::uint64_t const scale = asset.raw().holds<MPTIssue>() ? 1 : 1e6;
-
79
-
80 auto const [share, vaultAccount] =
-
81 [&env,
-
82 keylet = keylet,
-
83 asset,
- -
85 auto const vault = env.le(keylet);
-
86 BEAST_EXPECT(vault != nullptr);
-
87 if (asset.raw().holds<Issue>() && !asset.raw().native())
-
88 BEAST_EXPECT(vault->at(sfScale) == 6);
-
89 else
-
90 BEAST_EXPECT(vault->at(sfScale) == 0);
-
91 auto const shares =
-
92 env.le(keylet::mptIssuance(vault->at(sfShareMPTID)));
-
93 BEAST_EXPECT(shares != nullptr);
-
94 if (asset.raw().holds<Issue>() && !asset.raw().native())
-
95 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
-
96 else
-
97 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
-
98 return {
-
99 MPTIssue(vault->at(sfShareMPTID)),
-
100 Account("vault", vault->at(sfAccount))};
-
101 }();
-
102 auto const shares = share.raw().get<MPTIssue>();
-
103 env.memoize(vaultAccount);
-
104
-
105 // Several 3rd party accounts which cannot receive funds
-
106 Account alice{"alice"};
-
107 Account dave{"dave"};
+
62 Account issuer{"issuer"};
+
63 Account owner{"owner"};
+
64 Account depositor{"depositor"};
+
65 Account charlie{"charlie"}; // authorized 3rd party
+
66 Account dave{"dave"};
+
67
+
68 auto const testSequence = [&, this](
+
69 std::string const& prefix,
+
70 Env& env,
+
71 Vault& vault,
+
72 PrettyAsset const& asset) {
+
73 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
74 tx[sfData] = "AFEED00E";
+
75 tx[sfAssetsMaximum] = asset(100).number();
+
76 env(tx);
+
77 env.close();
+
78 BEAST_EXPECT(env.le(keylet));
+
79 std::uint64_t const scale = asset.raw().holds<MPTIssue>() ? 1 : 1e6;
+
80
+
81 auto const [share, vaultAccount] =
+
82 [&env,
+
83 keylet = keylet,
+
84 asset,
+ +
86 auto const vault = env.le(keylet);
+
87 BEAST_EXPECT(vault != nullptr);
+
88 if (asset.raw().holds<Issue>() && !asset.raw().native())
+
89 BEAST_EXPECT(vault->at(sfScale) == 6);
+
90 else
+
91 BEAST_EXPECT(vault->at(sfScale) == 0);
+
92 auto const shares =
+
93 env.le(keylet::mptIssuance(vault->at(sfShareMPTID)));
+
94 BEAST_EXPECT(shares != nullptr);
+
95 if (asset.raw().holds<Issue>() && !asset.raw().native())
+
96 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
+
97 else
+
98 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
+
99 return {
+
100 MPTIssue(vault->at(sfShareMPTID)),
+
101 Account("vault", vault->at(sfAccount))};
+
102 }();
+
103 auto const shares = share.raw().get<MPTIssue>();
+
104 env.memoize(vaultAccount);
+
105
+
106 // Several 3rd party accounts which cannot receive funds
+
107 Account alice{"alice"};
108 Account erin{"erin"}; // not authorized by issuer
-
109 env.fund(XRP(1000), alice, dave, erin);
+
109 env.fund(XRP(1000), alice, erin);
110 env(fset(alice, asfDepositAuth));
-
111 env(fset(dave, asfRequireDest));
-
112 env.close();
-
113
-
114 {
-
115 testcase(prefix + " fail to deposit more than assets held");
-
116 auto tx = vault.deposit(
-
117 {.depositor = depositor,
-
118 .id = keylet.key,
-
119 .amount = asset(10000)});
-
120 env(tx, ter(tecINSUFFICIENT_FUNDS));
-
121 env.close();
-
122 }
-
123
-
124 {
-
125 testcase(prefix + " deposit non-zero amount");
-
126 auto tx = vault.deposit(
-
127 {.depositor = depositor,
-
128 .id = keylet.key,
-
129 .amount = asset(50)});
-
130 env(tx);
-
131 env.close();
-
132 BEAST_EXPECT(
-
133 env.balance(depositor, shares) == share(50 * scale));
-
134 }
-
135
-
136 {
-
137 testcase(prefix + " deposit non-zero amount again");
-
138 auto tx = vault.deposit(
-
139 {.depositor = depositor,
-
140 .id = keylet.key,
-
141 .amount = asset(50)});
-
142 env(tx);
-
143 env.close();
-
144 BEAST_EXPECT(
-
145 env.balance(depositor, shares) == share(100 * scale));
-
146 }
-
147
-
148 {
-
149 testcase(prefix + " fail to delete non-empty vault");
-
150 auto tx = vault.del({.owner = owner, .id = keylet.key});
-
151 env(tx, ter(tecHAS_OBLIGATIONS));
-
152 env.close();
-
153 }
-
154
-
155 {
-
156 testcase(prefix + " fail to update because wrong owner");
-
157 auto tx = vault.set({.owner = issuer, .id = keylet.key});
-
158 tx[sfAssetsMaximum] = asset(50).number();
-
159 env(tx, ter(tecNO_PERMISSION));
-
160 env.close();
-
161 }
-
162
-
163 {
-
164 testcase(
-
165 prefix + " fail to set maximum lower than current amount");
-
166 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
167 tx[sfAssetsMaximum] = asset(50).number();
-
168 env(tx, ter(tecLIMIT_EXCEEDED));
-
169 env.close();
-
170 }
-
171
-
172 {
-
173 testcase(prefix + " set maximum higher than current amount");
-
174 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
175 tx[sfAssetsMaximum] = asset(150).number();
-
176 env(tx);
-
177 env.close();
-
178 }
-
179
-
180 {
-
181 testcase(prefix + " set maximum is idempotent, set it again");
-
182 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
183 tx[sfAssetsMaximum] = asset(150).number();
-
184 env(tx);
-
185 env.close();
-
186 }
-
187
-
188 {
-
189 testcase(prefix + " set data");
-
190 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
191 tx[sfData] = "0";
-
192 env(tx);
-
193 env.close();
-
194 }
-
195
-
196 {
-
197 testcase(prefix + " fail to set domain on public vault");
-
198 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
199 tx[sfDomainID] = to_string(base_uint<256>(42ul));
-
200 env(tx, ter{tecNO_PERMISSION});
-
201 env.close();
-
202 }
-
203
-
204 {
-
205 testcase(prefix + " fail to deposit more than maximum");
-
206 auto tx = vault.deposit(
-
207 {.depositor = depositor,
-
208 .id = keylet.key,
-
209 .amount = asset(100)});
-
210 env(tx, ter(tecLIMIT_EXCEEDED));
-
211 env.close();
-
212 }
-
213
-
214 {
-
215 testcase(prefix + " reset maximum to zero i.e. not enforced");
-
216 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
217 tx[sfAssetsMaximum] = asset(0).number();
-
218 env(tx);
-
219 env.close();
-
220 }
-
221
-
222 {
-
223 testcase(prefix + " fail to withdraw more than assets held");
-
224 auto tx = vault.withdraw(
-
225 {.depositor = depositor,
-
226 .id = keylet.key,
-
227 .amount = asset(1000)});
-
228 env(tx, ter(tecINSUFFICIENT_FUNDS));
-
229 env.close();
-
230 }
-
231
-
232 {
-
233 testcase(prefix + " deposit some more");
-
234 auto tx = vault.deposit(
-
235 {.depositor = depositor,
-
236 .id = keylet.key,
-
237 .amount = asset(100)});
-
238 env(tx);
-
239 env.close();
-
240 BEAST_EXPECT(
-
241 env.balance(depositor, shares) == share(200 * scale));
-
242 }
-
243
-
244 {
-
245 testcase(prefix + " clawback some");
-
246 auto code =
-
247 asset.raw().native() ? ter(temMALFORMED) : ter(tesSUCCESS);
-
248 auto tx = vault.clawback(
-
249 {.issuer = issuer,
-
250 .id = keylet.key,
-
251 .holder = depositor,
-
252 .amount = asset(10)});
-
253 env(tx, code);
-
254 env.close();
-
255 if (!asset.raw().native())
-
256 {
-
257 BEAST_EXPECT(
-
258 env.balance(depositor, shares) == share(190 * scale));
-
259 }
-
260 }
-
261
-
262 {
-
263 testcase(prefix + " clawback all");
-
264 auto code = asset.raw().native() ? ter(tecNO_PERMISSION)
-
265 : ter(tesSUCCESS);
-
266 auto tx = vault.clawback(
-
267 {.issuer = issuer, .id = keylet.key, .holder = depositor});
-
268 env(tx, code);
-
269 env.close();
-
270 if (!asset.raw().native())
-
271 {
-
272 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
-
273
-
274 {
-
275 auto tx = vault.clawback(
-
276 {.issuer = issuer,
-
277 .id = keylet.key,
-
278 .holder = depositor,
-
279 .amount = asset(10)});
-
280 env(tx, ter{tecPRECISION_LOSS});
-
281 env.close();
-
282 }
-
283
-
284 {
-
285 auto tx = vault.withdraw(
-
286 {.depositor = depositor,
-
287 .id = keylet.key,
-
288 .amount = asset(10)});
-
289 env(tx, ter{tecPRECISION_LOSS});
-
290 env.close();
-
291 }
-
292 }
-
293 }
-
294
-
295 if (!asset.raw().native())
-
296 {
-
297 testcase(prefix + " deposit again");
-
298 auto tx = vault.deposit(
-
299 {.depositor = depositor,
-
300 .id = keylet.key,
-
301 .amount = asset(200)});
-
302 env(tx);
-
303 env.close();
-
304 BEAST_EXPECT(
-
305 env.balance(depositor, shares) == share(200 * scale));
-
306 }
-
307
-
308 {
-
309 testcase(
-
310 prefix + " fail to withdraw to 3rd party lsfDepositAuth");
-
311 auto tx = vault.withdraw(
-
312 {.depositor = depositor,
-
313 .id = keylet.key,
-
314 .amount = asset(100)});
-
315 tx[sfDestination] = alice.human();
-
316 env(tx, ter{tecNO_PERMISSION});
-
317 env.close();
-
318 }
-
319
-
320 {
-
321 testcase(prefix + " fail to withdraw to zero destination");
-
322 auto tx = vault.withdraw(
-
323 {.depositor = depositor,
-
324 .id = keylet.key,
-
325 .amount = asset(1000)});
-
326 tx[sfDestination] = "0";
-
327 env(tx, ter(temMALFORMED));
-
328 env.close();
-
329 }
-
330
+
111 env.close();
+
112
+
113 {
+
114 testcase(prefix + " fail to deposit more than assets held");
+
115 auto tx = vault.deposit(
+
116 {.depositor = depositor,
+
117 .id = keylet.key,
+
118 .amount = asset(10000)});
+
119 env(tx, ter(tecINSUFFICIENT_FUNDS));
+
120 env.close();
+
121 }
+
122
+
123 {
+
124 testcase(prefix + " deposit non-zero amount");
+
125 auto tx = vault.deposit(
+
126 {.depositor = depositor,
+
127 .id = keylet.key,
+
128 .amount = asset(50)});
+
129 env(tx);
+
130 env.close();
+
131 BEAST_EXPECT(
+
132 env.balance(depositor, shares) == share(50 * scale));
+
133 }
+
134
+
135 {
+
136 testcase(prefix + " deposit non-zero amount again");
+
137 auto tx = vault.deposit(
+
138 {.depositor = depositor,
+
139 .id = keylet.key,
+
140 .amount = asset(50)});
+
141 env(tx);
+
142 env.close();
+
143 BEAST_EXPECT(
+
144 env.balance(depositor, shares) == share(100 * scale));
+
145 }
+
146
+
147 {
+
148 testcase(prefix + " fail to delete non-empty vault");
+
149 auto tx = vault.del({.owner = owner, .id = keylet.key});
+
150 env(tx, ter(tecHAS_OBLIGATIONS));
+
151 env.close();
+
152 }
+
153
+
154 {
+
155 testcase(prefix + " fail to update because wrong owner");
+
156 auto tx = vault.set({.owner = issuer, .id = keylet.key});
+
157 tx[sfAssetsMaximum] = asset(50).number();
+
158 env(tx, ter(tecNO_PERMISSION));
+
159 env.close();
+
160 }
+
161
+
162 {
+
163 testcase(
+
164 prefix + " fail to set maximum lower than current amount");
+
165 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
166 tx[sfAssetsMaximum] = asset(50).number();
+
167 env(tx, ter(tecLIMIT_EXCEEDED));
+
168 env.close();
+
169 }
+
170
+
171 {
+
172 testcase(prefix + " set maximum higher than current amount");
+
173 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
174 tx[sfAssetsMaximum] = asset(150).number();
+
175 env(tx);
+
176 env.close();
+
177 }
+
178
+
179 {
+
180 testcase(prefix + " set maximum is idempotent, set it again");
+
181 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
182 tx[sfAssetsMaximum] = asset(150).number();
+
183 env(tx);
+
184 env.close();
+
185 }
+
186
+
187 {
+
188 testcase(prefix + " set data");
+
189 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
190 tx[sfData] = "0";
+
191 env(tx);
+
192 env.close();
+
193 }
+
194
+
195 {
+
196 testcase(prefix + " fail to set domain on public vault");
+
197 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
198 tx[sfDomainID] = to_string(base_uint<256>(42ul));
+
199 env(tx, ter{tecNO_PERMISSION});
+
200 env.close();
+
201 }
+
202
+
203 {
+
204 testcase(prefix + " fail to deposit more than maximum");
+
205 auto tx = vault.deposit(
+
206 {.depositor = depositor,
+
207 .id = keylet.key,
+
208 .amount = asset(100)});
+
209 env(tx, ter(tecLIMIT_EXCEEDED));
+
210 env.close();
+
211 }
+
212
+
213 {
+
214 testcase(prefix + " reset maximum to zero i.e. not enforced");
+
215 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
216 tx[sfAssetsMaximum] = asset(0).number();
+
217 env(tx);
+
218 env.close();
+
219 }
+
220
+
221 {
+
222 testcase(prefix + " fail to withdraw more than assets held");
+
223 auto tx = vault.withdraw(
+
224 {.depositor = depositor,
+
225 .id = keylet.key,
+
226 .amount = asset(1000)});
+
227 env(tx, ter(tecINSUFFICIENT_FUNDS));
+
228 env.close();
+
229 }
+
230
+
231 {
+
232 testcase(prefix + " deposit some more");
+
233 auto tx = vault.deposit(
+
234 {.depositor = depositor,
+
235 .id = keylet.key,
+
236 .amount = asset(100)});
+
237 env(tx);
+
238 env.close();
+
239 BEAST_EXPECT(
+
240 env.balance(depositor, shares) == share(200 * scale));
+
241 }
+
242
+
243 {
+
244 testcase(prefix + " clawback some");
+
245 auto code =
+
246 asset.raw().native() ? ter(temMALFORMED) : ter(tesSUCCESS);
+
247 auto tx = vault.clawback(
+
248 {.issuer = issuer,
+
249 .id = keylet.key,
+
250 .holder = depositor,
+
251 .amount = asset(10)});
+
252 env(tx, code);
+
253 env.close();
+
254 if (!asset.raw().native())
+
255 {
+
256 BEAST_EXPECT(
+
257 env.balance(depositor, shares) == share(190 * scale));
+
258 }
+
259 }
+
260
+
261 {
+
262 testcase(prefix + " clawback all");
+
263 auto code = asset.raw().native() ? ter(tecNO_PERMISSION)
+
264 : ter(tesSUCCESS);
+
265 auto tx = vault.clawback(
+
266 {.issuer = issuer, .id = keylet.key, .holder = depositor});
+
267 env(tx, code);
+
268 env.close();
+
269 if (!asset.raw().native())
+
270 {
+
271 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
+
272
+
273 {
+
274 auto tx = vault.clawback(
+
275 {.issuer = issuer,
+
276 .id = keylet.key,
+
277 .holder = depositor,
+
278 .amount = asset(10)});
+
279 env(tx, ter{tecPRECISION_LOSS});
+
280 env.close();
+
281 }
+
282
+
283 {
+
284 auto tx = vault.withdraw(
+
285 {.depositor = depositor,
+
286 .id = keylet.key,
+
287 .amount = asset(10)});
+
288 env(tx, ter{tecPRECISION_LOSS});
+
289 env.close();
+
290 }
+
291 }
+
292 }
+
293
+
294 if (!asset.raw().native())
+
295 {
+
296 testcase(prefix + " deposit again");
+
297 auto tx = vault.deposit(
+
298 {.depositor = depositor,
+
299 .id = keylet.key,
+
300 .amount = asset(200)});
+
301 env(tx);
+
302 env.close();
+
303 BEAST_EXPECT(
+
304 env.balance(depositor, shares) == share(200 * scale));
+
305 }
+
306
+
307 {
+
308 testcase(
+
309 prefix + " fail to withdraw to 3rd party lsfDepositAuth");
+
310 auto tx = vault.withdraw(
+
311 {.depositor = depositor,
+
312 .id = keylet.key,
+
313 .amount = asset(100)});
+
314 tx[sfDestination] = alice.human();
+
315 env(tx, ter{tecNO_PERMISSION});
+
316 env.close();
+
317 }
+
318
+
319 {
+
320 testcase(prefix + " fail to withdraw to zero destination");
+
321 auto tx = vault.withdraw(
+
322 {.depositor = depositor,
+
323 .id = keylet.key,
+
324 .amount = asset(1000)});
+
325 tx[sfDestination] = "0";
+
326 env(tx, ter(temMALFORMED));
+
327 env.close();
+
328 }
+
329
+
330 if (!asset.raw().native())
331 {
332 testcase(
-
333 prefix +
-
334 " fail to withdraw with tag but without destination");
-
335 auto tx = vault.withdraw(
-
336 {.depositor = depositor,
-
337 .id = keylet.key,
-
338 .amount = asset(1000)});
-
339 tx[sfDestinationTag] = "0";
-
340 env(tx, ter(temMALFORMED));
+
333 prefix + " fail to withdraw to 3rd party no authorization");
+
334 auto tx = vault.withdraw(
+
335 {.depositor = depositor,
+
336 .id = keylet.key,
+
337 .amount = asset(100)});
+
338 tx[sfDestination] = erin.human();
+
339 env(tx,
+
340 ter{asset.raw().holds<Issue>() ? tecNO_LINE : tecNO_AUTH});
341 env.close();
342 }
343
-
344 if (!asset.raw().native())
-
345 {
-
346 testcase(
-
347 prefix + " fail to withdraw to 3rd party no authorization");
+
344 {
+
345 testcase(
+
346 prefix +
+
347 " fail to withdraw to 3rd party lsfRequireDestTag");
348 auto tx = vault.withdraw(
349 {.depositor = depositor,
350 .id = keylet.key,
351 .amount = asset(100)});
-
352 tx[sfDestination] = erin.human();
-
353 env(tx,
-
354 ter{asset.raw().holds<Issue>() ? tecNO_LINE : tecNO_AUTH});
-
355 env.close();
-
356 }
-
357
-
358 {
-
359 testcase(
-
360 prefix +
-
361 " fail to withdraw to 3rd party lsfRequireDestTag");
-
362 auto tx = vault.withdraw(
-
363 {.depositor = depositor,
-
364 .id = keylet.key,
-
365 .amount = asset(100)});
-
366 tx[sfDestination] = dave.human();
-
367 env(tx, ter{tecDST_TAG_NEEDED});
-
368 env.close();
-
369 }
-
370
-
371 {
-
372 testcase(prefix + " withdraw to authorized 3rd party");
-
373 auto tx = vault.withdraw(
-
374 {.depositor = depositor,
-
375 .id = keylet.key,
-
376 .amount = asset(100)});
-
377 tx[sfDestination] = charlie.human();
-
378 env(tx);
-
379 env.close();
-
380 BEAST_EXPECT(
-
381 env.balance(depositor, shares) == share(100 * scale));
-
382 }
-
383
-
384 {
-
385 testcase(prefix + " withdraw to issuer");
-
386 auto tx = vault.withdraw(
-
387 {.depositor = depositor,
-
388 .id = keylet.key,
-
389 .amount = asset(50)});
-
390 tx[sfDestination] = issuer.human();
-
391 env(tx);
-
392 env.close();
-
393 BEAST_EXPECT(
-
394 env.balance(depositor, shares) == share(50 * scale));
-
395 }
-
396
-
397 if (!asset.raw().native())
-
398 {
-
399 testcase(prefix + " issuer deposits");
-
400 auto tx = vault.deposit(
-
401 {.depositor = issuer,
-
402 .id = keylet.key,
-
403 .amount = asset(10)});
-
404 env(tx);
-
405 env.close();
-
406 BEAST_EXPECT(env.balance(issuer, shares) == share(10 * scale));
-
407
-
408 testcase(prefix + " issuer withdraws");
-
409 tx = vault.withdraw(
-
410 {.depositor = issuer,
+
352 tx[sfDestination] = dave.human();
+
353 env(tx, ter{tecDST_TAG_NEEDED});
+
354 env.close();
+
355 }
+
356
+
357 {
+
358 testcase(prefix + " withdraw to 3rd party lsfRequireDestTag");
+
359 auto tx = vault.withdraw(
+
360 {.depositor = depositor,
+
361 .id = keylet.key,
+
362 .amount = asset(50)});
+
363 tx[sfDestination] = dave.human();
+
364 tx[sfDestinationTag] = "0";
+
365 env(tx);
+
366 env.close();
+
367 }
+
368
+
369 {
+
370 testcase(prefix + " deposit again");
+
371 auto tx = vault.deposit(
+
372 {.depositor = dave, .id = keylet.key, .amount = asset(50)});
+
373 env(tx);
+
374 env.close();
+
375 }
+
376
+
377 {
+
378 testcase(prefix + " fail to withdraw lsfRequireDestTag");
+
379 auto tx = vault.withdraw(
+
380 {.depositor = dave, .id = keylet.key, .amount = asset(50)});
+
381 env(tx, ter{tecDST_TAG_NEEDED});
+
382 env.close();
+
383 }
+
384
+
385 {
+
386 testcase(prefix + " withdraw with tag");
+
387 auto tx = vault.withdraw(
+
388 {.depositor = dave, .id = keylet.key, .amount = asset(50)});
+
389 tx[sfDestinationTag] = "0";
+
390 env(tx);
+
391 env.close();
+
392 }
+
393
+
394 {
+
395 testcase(prefix + " withdraw to authorized 3rd party");
+
396 auto tx = vault.withdraw(
+
397 {.depositor = depositor,
+
398 .id = keylet.key,
+
399 .amount = asset(50)});
+
400 tx[sfDestination] = charlie.human();
+
401 env(tx);
+
402 env.close();
+
403 BEAST_EXPECT(
+
404 env.balance(depositor, shares) == share(100 * scale));
+
405 }
+
406
+
407 {
+
408 testcase(prefix + " withdraw to issuer");
+
409 auto tx = vault.withdraw(
+
410 {.depositor = depositor,
411 .id = keylet.key,
-
412 .amount = share(10 * scale)});
-
413 env(tx);
-
414 env.close();
-
415 BEAST_EXPECT(env.balance(issuer, shares) == share(0 * scale));
-
416 }
-
417
-
418 {
-
419 testcase(prefix + " withdraw remaining assets");
-
420 auto tx = vault.withdraw(
-
421 {.depositor = depositor,
-
422 .id = keylet.key,
-
423 .amount = asset(50)});
-
424 env(tx);
-
425 env.close();
-
426 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
-
427
-
428 if (!asset.raw().native())
-
429 {
-
430 auto tx = vault.clawback(
-
431 {.issuer = issuer,
-
432 .id = keylet.key,
-
433 .holder = depositor,
-
434 .amount = asset(0)});
-
435 env(tx, ter{tecPRECISION_LOSS});
-
436 env.close();
-
437 }
-
438
-
439 {
-
440 auto tx = vault.withdraw(
-
441 {.depositor = depositor,
-
442 .id = keylet.key,
-
443 .amount = share(10)});
-
444 env(tx, ter{tecINSUFFICIENT_FUNDS});
-
445 env.close();
-
446 }
-
447 }
-
448
-
449 if (!asset.raw().native() && asset.raw().holds<Issue>())
-
450 {
-
451 testcase(prefix + " temporary authorization for 3rd party");
-
452 env(trust(erin, asset(1000)));
-
453 env(trust(issuer, asset(0), erin, tfSetfAuth));
-
454 env(pay(issuer, erin, asset(10)));
-
455
-
456 // Erin deposits all in vault, then sends shares to depositor
-
457 auto tx = vault.deposit(
-
458 {.depositor = erin, .id = keylet.key, .amount = asset(10)});
-
459 env(tx);
-
460 env.close();
-
461 {
-
462 auto tx = pay(erin, depositor, share(10 * scale));
-
463
-
464 // depositor no longer has MPToken for shares
-
465 env(tx, ter{tecNO_AUTH});
-
466 env.close();
-
467
-
468 // depositor will gain MPToken for shares again
-
469 env(vault.deposit(
-
470 {.depositor = depositor,
-
471 .id = keylet.key,
-
472 .amount = asset(1)}));
-
473 env.close();
-
474
-
475 env(tx);
-
476 env.close();
-
477 }
+
412 .amount = asset(50)});
+
413 tx[sfDestination] = issuer.human();
+
414 env(tx);
+
415 env.close();
+
416 BEAST_EXPECT(
+
417 env.balance(depositor, shares) == share(50 * scale));
+
418 }
+
419
+
420 if (!asset.raw().native())
+
421 {
+
422 testcase(prefix + " issuer deposits");
+
423 auto tx = vault.deposit(
+
424 {.depositor = issuer,
+
425 .id = keylet.key,
+
426 .amount = asset(10)});
+
427 env(tx);
+
428 env.close();
+
429 BEAST_EXPECT(env.balance(issuer, shares) == share(10 * scale));
+
430
+
431 testcase(prefix + " issuer withdraws");
+
432 tx = vault.withdraw(
+
433 {.depositor = issuer,
+
434 .id = keylet.key,
+
435 .amount = share(10 * scale)});
+
436 env(tx);
+
437 env.close();
+
438 BEAST_EXPECT(env.balance(issuer, shares) == share(0 * scale));
+
439 }
+
440
+
441 {
+
442 testcase(prefix + " withdraw remaining assets");
+
443 auto tx = vault.withdraw(
+
444 {.depositor = depositor,
+
445 .id = keylet.key,
+
446 .amount = asset(50)});
+
447 env(tx);
+
448 env.close();
+
449 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
+
450
+
451 if (!asset.raw().native())
+
452 {
+
453 auto tx = vault.clawback(
+
454 {.issuer = issuer,
+
455 .id = keylet.key,
+
456 .holder = depositor,
+
457 .amount = asset(0)});
+
458 env(tx, ter{tecPRECISION_LOSS});
+
459 env.close();
+
460 }
+
461
+
462 {
+
463 auto tx = vault.withdraw(
+
464 {.depositor = depositor,
+
465 .id = keylet.key,
+
466 .amount = share(10)});
+
467 env(tx, ter{tecINSUFFICIENT_FUNDS});
+
468 env.close();
+
469 }
+
470 }
+
471
+
472 if (!asset.raw().native() && asset.raw().holds<Issue>())
+
473 {
+
474 testcase(prefix + " temporary authorization for 3rd party");
+
475 env(trust(erin, asset(1000)));
+
476 env(trust(issuer, asset(0), erin, tfSetfAuth));
+
477 env(pay(issuer, erin, asset(10)));
478
-
479 testcase(prefix + " withdraw to authorized 3rd party");
-
480 // Depositor withdraws assets, destined to Erin
-
481 tx = vault.withdraw(
-
482 {.depositor = depositor,
-
483 .id = keylet.key,
-
484 .amount = asset(10)});
-
485 tx[sfDestination] = erin.human();
-
486 env(tx);
-
487 env.close();
-
488
-
489 // Erin returns assets to issuer
-
490 env(pay(erin, issuer, asset(10)));
-
491 env.close();
-
492
-
493 testcase(prefix + " fail to pay to unauthorized 3rd party");
-
494 env(trust(erin, asset(0)));
-
495 env.close();
-
496
-
497 // Erin has MPToken but is no longer authorized to hold assets
-
498 env(pay(depositor, erin, share(1)), ter{tecNO_LINE});
-
499 env.close();
-
500
-
501 // Depositor withdraws remaining single asset
-
502 tx = vault.withdraw(
-
503 {.depositor = depositor,
-
504 .id = keylet.key,
-
505 .amount = asset(1)});
-
506 env(tx);
-
507 env.close();
-
508 }
-
509
-
510 {
-
511 testcase(prefix + " fail to delete because wrong owner");
-
512 auto tx = vault.del({.owner = issuer, .id = keylet.key});
-
513 env(tx, ter(tecNO_PERMISSION));
+
479 // Erin deposits all in vault, then sends shares to depositor
+
480 auto tx = vault.deposit(
+
481 {.depositor = erin, .id = keylet.key, .amount = asset(10)});
+
482 env(tx);
+
483 env.close();
+
484 {
+
485 auto tx = pay(erin, depositor, share(10 * scale));
+
486
+
487 // depositor no longer has MPToken for shares
+
488 env(tx, ter{tecNO_AUTH});
+
489 env.close();
+
490
+
491 // depositor will gain MPToken for shares again
+
492 env(vault.deposit(
+
493 {.depositor = depositor,
+
494 .id = keylet.key,
+
495 .amount = asset(1)}));
+
496 env.close();
+
497
+
498 env(tx);
+
499 env.close();
+
500 }
+
501
+
502 testcase(prefix + " withdraw to authorized 3rd party");
+
503 // Depositor withdraws assets, destined to Erin
+
504 tx = vault.withdraw(
+
505 {.depositor = depositor,
+
506 .id = keylet.key,
+
507 .amount = asset(10)});
+
508 tx[sfDestination] = erin.human();
+
509 env(tx);
+
510 env.close();
+
511
+
512 // Erin returns assets to issuer
+
513 env(pay(erin, issuer, asset(10)));
514 env.close();
-
515 }
-
516
-
517 {
-
518 testcase(prefix + " delete empty vault");
-
519 auto tx = vault.del({.owner = owner, .id = keylet.key});
-
520 env(tx);
-
521 env.close();
-
522 BEAST_EXPECT(!env.le(keylet));
-
523 }
-
524 };
-
525
-
526 auto testCases = [this, &testSequence](
-
527 std::string prefix,
- -
529 Env & env,
-
530 Account const& issuer,
-
531 Account const& owner,
-
532 Account const& depositor,
-
533 Account const& charlie)> setup) {
-
534 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
535 Account issuer{"issuer"};
-
536 Account owner{"owner"};
-
537 Account depositor{"depositor"};
-
538 Account charlie{"charlie"}; // authorized 3rd party
-
539 Vault vault{env};
-
540 env.fund(XRP(1000), issuer, owner, depositor, charlie);
-
541 env.close();
-
542 env(fset(issuer, asfAllowTrustLineClawback));
-
543 env(fset(issuer, asfRequireAuth));
-
544 env.close();
-
545 env.require(flags(issuer, asfAllowTrustLineClawback));
-
546 env.require(flags(issuer, asfRequireAuth));
-
547
-
548 PrettyAsset asset = setup(env, issuer, owner, depositor, charlie);
-
549 testSequence(
-
550 prefix, env, issuer, owner, depositor, charlie, vault, asset);
-
551 };
-
552
-
553 testCases(
-
554 "XRP",
-
555 [](Env& env,
-
556 Account const& issuer,
-
557 Account const& owner,
-
558 Account const& depositor,
-
559 Account const& charlie) -> PrettyAsset {
-
560 return {xrpIssue(), 1'000'000};
-
561 });
-
562
-
563 testCases(
-
564 "IOU",
-
565 [](Env& env,
-
566 Account const& issuer,
-
567 Account const& owner,
-
568 Account const& depositor,
-
569 Account const& charlie) -> Asset {
-
570 PrettyAsset asset = issuer["IOU"];
-
571 env(trust(owner, asset(1000)));
-
572 env(trust(depositor, asset(1000)));
-
573 env(trust(charlie, asset(1000)));
-
574 env(trust(issuer, asset(0), owner, tfSetfAuth));
-
575 env(trust(issuer, asset(0), depositor, tfSetfAuth));
-
576 env(trust(issuer, asset(0), charlie, tfSetfAuth));
-
577 env(pay(issuer, depositor, asset(1000)));
-
578 env.close();
-
579 return asset;
-
580 });
-
581
-
582 testCases(
-
583 "MPT",
-
584 [](Env& env,
-
585 Account const& issuer,
-
586 Account const& owner,
-
587 Account const& depositor,
-
588 Account const& charlie) -> Asset {
-
589 MPTTester mptt{env, issuer, mptInitNoFund};
-
590 mptt.create(
-
591 {.flags =
- -
593 PrettyAsset asset = mptt.issuanceID();
-
594 mptt.authorize({.account = depositor});
-
595 mptt.authorize({.account = charlie});
-
596 env(pay(issuer, depositor, asset(1000)));
-
597 env.close();
-
598 return asset;
-
599 });
-
600 }
+
515
+
516 testcase(prefix + " fail to pay to unauthorized 3rd party");
+
517 env(trust(erin, asset(0)));
+
518 env.close();
+
519
+
520 // Erin has MPToken but is no longer authorized to hold assets
+
521 env(pay(depositor, erin, share(1)), ter{tecNO_LINE});
+
522 env.close();
+
523
+
524 // Depositor withdraws remaining single asset
+
525 tx = vault.withdraw(
+
526 {.depositor = depositor,
+
527 .id = keylet.key,
+
528 .amount = asset(1)});
+
529 env(tx);
+
530 env.close();
+
531 }
+
532
+
533 {
+
534 testcase(prefix + " fail to delete because wrong owner");
+
535 auto tx = vault.del({.owner = issuer, .id = keylet.key});
+
536 env(tx, ter(tecNO_PERMISSION));
+
537 env.close();
+
538 }
+
539
+
540 {
+
541 testcase(prefix + " delete empty vault");
+
542 auto tx = vault.del({.owner = owner, .id = keylet.key});
+
543 env(tx);
+
544 env.close();
+
545 BEAST_EXPECT(!env.le(keylet));
+
546 }
+
547 };
+
548
+
549 auto testCases = [&, this](
+
550 std::string prefix,
+
551 std::function<PrettyAsset(Env & env)> setup) {
+
552 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
553
+
554 Vault vault{env};
+
555 env.fund(XRP(1000), issuer, owner, depositor, charlie, dave);
+
556 env.close();
+
557 env(fset(issuer, asfAllowTrustLineClawback));
+
558 env(fset(issuer, asfRequireAuth));
+
559 env(fset(dave, asfRequireDest));
+
560 env.close();
+
561 env.require(flags(issuer, asfAllowTrustLineClawback));
+
562 env.require(flags(issuer, asfRequireAuth));
+
563
+
564 PrettyAsset asset = setup(env);
+
565 testSequence(prefix, env, vault, asset);
+
566 };
+
567
+
568 testCases("XRP", [&](Env& env) -> PrettyAsset {
+
569 return {xrpIssue(), 1'000'000};
+
570 });
+
571
+
572 testCases("IOU", [&](Env& env) -> Asset {
+
573 PrettyAsset asset = issuer["IOU"];
+
574 env(trust(owner, asset(1000)));
+
575 env(trust(depositor, asset(1000)));
+
576 env(trust(charlie, asset(1000)));
+
577 env(trust(dave, asset(1000)));
+
578 env(trust(issuer, asset(0), owner, tfSetfAuth));
+
579 env(trust(issuer, asset(0), depositor, tfSetfAuth));
+
580 env(trust(issuer, asset(0), charlie, tfSetfAuth));
+
581 env(trust(issuer, asset(0), dave, tfSetfAuth));
+
582 env(pay(issuer, depositor, asset(1000)));
+
583 env.close();
+
584 return asset;
+
585 });
+
586
+
587 testCases("MPT", [&](Env& env) -> Asset {
+
588 MPTTester mptt{env, issuer, mptInitNoFund};
+
589 mptt.create(
+ +
591 PrettyAsset asset = mptt.issuanceID();
+
592 mptt.authorize({.account = depositor});
+
593 mptt.authorize({.account = charlie});
+
594 mptt.authorize({.account = dave});
+
595 env(pay(issuer, depositor, asset(1000)));
+
596 env.close();
+
597 return asset;
+
598 });
+
599 }
-
601
-
602 void
-
- -
604 {
-
605 using namespace test::jtx;
-
606
-
607 struct CaseArgs
-
608 {
-
609 FeatureBitset features =
-
610 testable_amendments() | featureSingleAssetVault;
-
611 };
-
612
-
613 auto testCase = [&, this](
-
614 std::function<void(
-
615 Env & env,
-
616 Account const& issuer,
-
617 Account const& owner,
-
618 Asset const& asset,
-
619 Vault& vault)> test,
-
620 CaseArgs args = {}) {
-
621 Env env{*this, args.features};
-
622 Account issuer{"issuer"};
-
623 Account owner{"owner"};
-
624 Vault vault{env};
-
625 env.fund(XRP(1000), issuer, owner);
-
626 env.close();
-
627
-
628 env(fset(issuer, asfAllowTrustLineClawback));
-
629 env(fset(issuer, asfRequireAuth));
-
630 env.close();
-
631
-
632 PrettyAsset asset = issuer["IOU"];
-
633 env(trust(owner, asset(1000)));
-
634 env(trust(issuer, asset(0), owner, tfSetfAuth));
-
635 env(pay(issuer, owner, asset(1000)));
-
636 env.close();
-
637
-
638 test(env, issuer, owner, asset, vault);
-
639 };
-
640
-
641 testCase(
-
642 [&](Env& env,
-
643 Account const& issuer,
-
644 Account const& owner,
-
645 Asset const& asset,
-
646 Vault& vault) {
-
647 testcase("disabled single asset vault");
-
648
-
649 auto [tx, keylet] =
-
650 vault.create({.owner = owner, .asset = asset});
-
651 env(tx, ter{temDISABLED});
-
652
-
653 {
-
654 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
655 env(tx, ter{temDISABLED});
-
656 }
-
657
-
658 {
-
659 auto tx = vault.deposit(
-
660 {.depositor = owner,
-
661 .id = keylet.key,
-
662 .amount = asset(10)});
-
663 env(tx, ter{temDISABLED});
-
664 }
-
665
-
666 {
-
667 auto tx = vault.withdraw(
-
668 {.depositor = owner,
-
669 .id = keylet.key,
-
670 .amount = asset(10)});
-
671 env(tx, ter{temDISABLED});
-
672 }
-
673
-
674 {
-
675 auto tx = vault.clawback(
-
676 {.issuer = issuer,
-
677 .id = keylet.key,
-
678 .holder = owner,
-
679 .amount = asset(10)});
-
680 env(tx, ter{temDISABLED});
-
681 }
-
682
-
683 {
-
684 auto tx = vault.del({.owner = owner, .id = keylet.key});
-
685 env(tx, ter{temDISABLED});
-
686 }
-
687 },
-
688 {.features = testable_amendments() - featureSingleAssetVault});
-
689
-
690 testCase([&](Env& env,
-
691 Account const& issuer,
-
692 Account const& owner,
-
693 Asset const& asset,
-
694 Vault& vault) {
-
695 testcase("invalid flags");
-
696
-
697 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
698 tx[sfFlags] = tfClearDeepFreeze;
-
699 env(tx, ter{temINVALID_FLAG});
-
700
-
701 {
-
702 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
703 tx[sfFlags] = tfClearDeepFreeze;
-
704 env(tx, ter{temINVALID_FLAG});
-
705 }
-
706
-
707 {
-
708 auto tx = vault.deposit(
-
709 {.depositor = owner,
-
710 .id = keylet.key,
-
711 .amount = asset(10)});
-
712 tx[sfFlags] = tfClearDeepFreeze;
-
713 env(tx, ter{temINVALID_FLAG});
-
714 }
-
715
-
716 {
-
717 auto tx = vault.withdraw(
-
718 {.depositor = owner,
-
719 .id = keylet.key,
-
720 .amount = asset(10)});
-
721 tx[sfFlags] = tfClearDeepFreeze;
-
722 env(tx, ter{temINVALID_FLAG});
-
723 }
-
724
-
725 {
-
726 auto tx = vault.clawback(
-
727 {.issuer = issuer,
-
728 .id = keylet.key,
-
729 .holder = owner,
-
730 .amount = asset(10)});
-
731 tx[sfFlags] = tfClearDeepFreeze;
-
732 env(tx, ter{temINVALID_FLAG});
-
733 }
-
734
-
735 {
-
736 auto tx = vault.del({.owner = owner, .id = keylet.key});
-
737 tx[sfFlags] = tfClearDeepFreeze;
-
738 env(tx, ter{temINVALID_FLAG});
-
739 }
-
740 });
-
741
-
742 testCase([&](Env& env,
-
743 Account const& issuer,
-
744 Account const& owner,
-
745 Asset const& asset,
-
746 Vault& vault) {
-
747 testcase("invalid fee");
-
748
-
749 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
750 tx[jss::Fee] = "-1";
-
751 env(tx, ter{temBAD_FEE});
-
752
-
753 {
-
754 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
755 tx[jss::Fee] = "-1";
-
756 env(tx, ter{temBAD_FEE});
-
757 }
-
758
-
759 {
-
760 auto tx = vault.deposit(
-
761 {.depositor = owner,
-
762 .id = keylet.key,
-
763 .amount = asset(10)});
-
764 tx[jss::Fee] = "-1";
-
765 env(tx, ter{temBAD_FEE});
-
766 }
-
767
-
768 {
-
769 auto tx = vault.withdraw(
-
770 {.depositor = owner,
-
771 .id = keylet.key,
-
772 .amount = asset(10)});
-
773 tx[jss::Fee] = "-1";
-
774 env(tx, ter{temBAD_FEE});
-
775 }
-
776
-
777 {
-
778 auto tx = vault.clawback(
-
779 {.issuer = issuer,
-
780 .id = keylet.key,
-
781 .holder = owner,
-
782 .amount = asset(10)});
-
783 tx[jss::Fee] = "-1";
-
784 env(tx, ter{temBAD_FEE});
-
785 }
-
786
-
787 {
-
788 auto tx = vault.del({.owner = owner, .id = keylet.key});
-
789 tx[jss::Fee] = "-1";
-
790 env(tx, ter{temBAD_FEE});
-
791 }
-
792 });
-
793
-
794 testCase(
-
795 [&](Env& env,
-
796 Account const&,
-
797 Account const& owner,
-
798 Asset const&,
-
799 Vault& vault) {
-
800 testcase("disabled permissioned domain");
-
801
-
802 auto [tx, keylet] =
-
803 vault.create({.owner = owner, .asset = xrpIssue()});
-
804 tx[sfDomainID] = to_string(base_uint<256>(42ul));
-
805 env(tx, ter{temDISABLED});
-
806
-
807 {
-
808 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
809 tx[sfDomainID] = to_string(base_uint<256>(42ul));
-
810 env(tx, ter{temDISABLED});
-
811 }
-
812
-
813 {
-
814 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
815 tx[sfDomainID] = "0";
-
816 env(tx, ter{temDISABLED});
-
817 }
-
818 },
-
819 {.features = (testable_amendments() | featureSingleAssetVault) -
-
820 featurePermissionedDomains});
-
821
-
822 testCase([&](Env& env,
-
823 Account const& issuer,
-
824 Account const& owner,
-
825 Asset const& asset,
-
826 Vault& vault) {
-
827 testcase("use zero vault");
-
828
-
829 auto [tx, keylet] =
-
830 vault.create({.owner = owner, .asset = xrpIssue()});
-
831
-
832 {
-
833 auto tx = vault.set({
-
834 .owner = owner,
-
835 .id = beast::zero,
-
836 });
-
837 env(tx, ter{temMALFORMED});
-
838 }
-
839
-
840 {
-
841 auto tx = vault.deposit(
-
842 {.depositor = owner,
-
843 .id = beast::zero,
-
844 .amount = asset(10)});
-
845 env(tx, ter(temMALFORMED));
-
846 }
-
847
-
848 {
-
849 auto tx = vault.withdraw(
-
850 {.depositor = owner,
-
851 .id = beast::zero,
-
852 .amount = asset(10)});
-
853 env(tx, ter{temMALFORMED});
-
854 }
-
855
-
856 {
-
857 auto tx = vault.clawback(
-
858 {.issuer = issuer,
-
859 .id = beast::zero,
-
860 .holder = owner,
-
861 .amount = asset(10)});
-
862 env(tx, ter{temMALFORMED});
-
863 }
-
864
-
865 {
-
866 auto tx = vault.del({
-
867 .owner = owner,
-
868 .id = beast::zero,
-
869 });
-
870 env(tx, ter{temMALFORMED});
-
871 }
-
872 });
-
873
-
874 testCase([&](Env& env,
-
875 Account const& issuer,
-
876 Account const& owner,
-
877 Asset const& asset,
-
878 Vault& vault) {
-
879 testcase("clawback from self");
-
880
-
881 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
882
-
883 {
-
884 auto tx = vault.clawback(
-
885 {.issuer = issuer,
-
886 .id = keylet.key,
-
887 .holder = issuer,
-
888 .amount = asset(10)});
-
889 env(tx, ter{temMALFORMED});
-
890 }
-
891 });
-
892
-
893 testCase([&](Env& env,
-
894 Account const&,
-
895 Account const& owner,
-
896 Asset const& asset,
-
897 Vault& vault) {
-
898 testcase("withdraw to bad destination");
-
899
-
900 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
901
-
902 {
-
903 auto tx = vault.withdraw(
-
904 {.depositor = owner,
-
905 .id = keylet.key,
-
906 .amount = asset(10)});
-
907 tx[jss::Destination] = "0";
-
908 env(tx, ter{temMALFORMED});
-
909 }
-
910 });
-
911
-
912 testCase([&](Env& env,
-
913 Account const&,
-
914 Account const& owner,
-
915 Asset const& asset,
-
916 Vault& vault) {
-
917 testcase("create with Scale");
-
918
-
919 {
-
920 auto [tx, keylet] =
-
921 vault.create({.owner = owner, .asset = asset});
-
922 tx[sfScale] = 255;
-
923 env(tx, ter(temMALFORMED));
-
924 }
-
925
-
926 {
-
927 auto [tx, keylet] =
-
928 vault.create({.owner = owner, .asset = asset});
-
929 tx[sfScale] = 19;
-
930 env(tx, ter(temMALFORMED));
-
931 }
-
932
-
933 // accepted range from 0 to 18
-
934 {
-
935 auto [tx, keylet] =
-
936 vault.create({.owner = owner, .asset = asset});
-
937 tx[sfScale] = 18;
-
938 env(tx);
-
939 env.close();
-
940 auto const sleVault = env.le(keylet);
-
941 BEAST_EXPECT(sleVault);
-
942 BEAST_EXPECT((*sleVault)[sfScale] == 18);
-
943 }
-
944
-
945 {
-
946 auto [tx, keylet] =
-
947 vault.create({.owner = owner, .asset = asset});
-
948 tx[sfScale] = 0;
-
949 env(tx);
-
950 env.close();
-
951 auto const sleVault = env.le(keylet);
-
952 BEAST_EXPECT(sleVault);
-
953 BEAST_EXPECT((*sleVault)[sfScale] == 0);
-
954 }
-
955
-
956 {
-
957 auto [tx, keylet] =
-
958 vault.create({.owner = owner, .asset = asset});
-
959 env(tx);
-
960 env.close();
-
961 auto const sleVault = env.le(keylet);
-
962 BEAST_EXPECT(sleVault);
-
963 BEAST_EXPECT((*sleVault)[sfScale] == 6);
-
964 }
-
965 });
-
966
-
967 testCase([&](Env& env,
-
968 Account const&,
-
969 Account const& owner,
-
970 Asset const& asset,
-
971 Vault& vault) {
-
972 testcase("create or set invalid data");
-
973
-
974 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
-
975
-
976 {
-
977 auto tx = tx1;
-
978 tx[sfData] = "";
-
979 env(tx, ter(temMALFORMED));
-
980 }
-
981
-
982 {
-
983 auto tx = tx1;
-
984 // A hexadecimal string of 257 bytes.
-
985 tx[sfData] = std::string(514, 'A');
-
986 env(tx, ter(temMALFORMED));
-
987 }
-
988
-
989 {
-
990 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
991 tx[sfData] = "";
-
992 env(tx, ter{temMALFORMED});
-
993 }
-
994
-
995 {
-
996 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
997 // A hexadecimal string of 257 bytes.
-
998 tx[sfData] = std::string(514, 'A');
-
999 env(tx, ter{temMALFORMED});
-
1000 }
-
1001 });
-
1002
-
1003 testCase([&](Env& env,
-
1004 Account const&,
-
1005 Account const& owner,
-
1006 Asset const& asset,
-
1007 Vault& vault) {
-
1008 testcase("set nothing updated");
-
1009
-
1010 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1011
-
1012 {
-
1013 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
1014 env(tx, ter{temMALFORMED});
-
1015 }
-
1016 });
-
1017
-
1018 testCase([&](Env& env,
-
1019 Account const&,
-
1020 Account const& owner,
-
1021 Asset const& asset,
-
1022 Vault& vault) {
-
1023 testcase("create with invalid metadata");
-
1024
-
1025 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
-
1026
-
1027 {
-
1028 auto tx = tx1;
-
1029 tx[sfMPTokenMetadata] = "";
-
1030 env(tx, ter(temMALFORMED));
-
1031 }
-
1032
-
1033 {
-
1034 auto tx = tx1;
-
1035 // This metadata is for the share token.
-
1036 // A hexadecimal string of 1025 bytes.
-
1037 tx[sfMPTokenMetadata] = std::string(2050, 'B');
-
1038 env(tx, ter(temMALFORMED));
-
1039 }
-
1040 });
-
1041
-
1042 testCase([&](Env& env,
-
1043 Account const&,
-
1044 Account const& owner,
-
1045 Asset const& asset,
-
1046 Vault& vault) {
-
1047 testcase("set negative maximum");
-
1048
-
1049 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1050
-
1051 {
-
1052 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
1053 tx[sfAssetsMaximum] = negativeAmount(asset).number();
-
1054 env(tx, ter{temMALFORMED});
-
1055 }
-
1056 });
-
1057
-
1058 testCase([&](Env& env,
-
1059 Account const&,
-
1060 Account const& owner,
-
1061 Asset const& asset,
-
1062 Vault& vault) {
-
1063 testcase("invalid deposit amount");
-
1064
-
1065 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1066
-
1067 {
-
1068 auto tx = vault.deposit(
-
1069 {.depositor = owner,
-
1070 .id = keylet.key,
-
1071 .amount = negativeAmount(asset)});
-
1072 env(tx, ter(temBAD_AMOUNT));
-
1073 }
-
1074
-
1075 {
-
1076 auto tx = vault.deposit(
-
1077 {.depositor = owner, .id = keylet.key, .amount = asset(0)});
-
1078 env(tx, ter(temBAD_AMOUNT));
-
1079 }
-
1080 });
-
1081
-
1082 testCase([&](Env& env,
-
1083 Account const&,
-
1084 Account const& owner,
-
1085 Asset const& asset,
-
1086 Vault& vault) {
-
1087 testcase("invalid set immutable flag");
-
1088
-
1089 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1090
-
1091 {
-
1092 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
1093 tx[sfFlags] = tfVaultPrivate;
-
1094 env(tx, ter(temINVALID_FLAG));
-
1095 }
-
1096 });
-
1097
-
1098 testCase([&](Env& env,
-
1099 Account const&,
-
1100 Account const& owner,
-
1101 Asset const& asset,
-
1102 Vault& vault) {
-
1103 testcase("invalid withdraw amount");
-
1104
-
1105 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1106
-
1107 {
-
1108 auto tx = vault.withdraw(
-
1109 {.depositor = owner,
-
1110 .id = keylet.key,
-
1111 .amount = negativeAmount(asset)});
-
1112 env(tx, ter(temBAD_AMOUNT));
-
1113 }
-
1114
-
1115 {
-
1116 auto tx = vault.withdraw(
-
1117 {.depositor = owner, .id = keylet.key, .amount = asset(0)});
-
1118 env(tx, ter(temBAD_AMOUNT));
-
1119 }
-
1120 });
-
1121
-
1122 testCase([&](Env& env,
-
1123 Account const& issuer,
-
1124 Account const& owner,
-
1125 Asset const& asset,
-
1126 Vault& vault) {
-
1127 testcase("invalid clawback");
-
1128
-
1129 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1130
-
1131 {
-
1132 auto tx = vault.clawback(
-
1133 {.issuer = owner,
-
1134 .id = keylet.key,
-
1135 .holder = issuer,
-
1136 .amount = asset(50)});
-
1137 env(tx, ter(temMALFORMED));
-
1138 }
-
1139
-
1140 {
-
1141 auto tx = vault.clawback(
-
1142 {.issuer = issuer,
-
1143 .id = keylet.key,
-
1144 .holder = owner,
-
1145 .amount = negativeAmount(asset)});
-
1146 env(tx, ter(temBAD_AMOUNT));
-
1147 }
-
1148 });
-
1149
-
1150 testCase([&](Env& env,
-
1151 Account const&,
-
1152 Account const& owner,
-
1153 Asset const& asset,
-
1154 Vault& vault) {
-
1155 testcase("invalid create");
-
1156
-
1157 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
-
1158
-
1159 {
-
1160 auto tx = tx1;
-
1161 tx[sfWithdrawalPolicy] = 0;
-
1162 env(tx, ter(temMALFORMED));
-
1163 }
-
1164
-
1165 {
-
1166 auto tx = tx1;
-
1167 tx[sfDomainID] = to_string(base_uint<256>(42ul));
-
1168 env(tx, ter{temMALFORMED});
-
1169 }
-
1170
-
1171 {
-
1172 auto tx = tx1;
-
1173 tx[sfAssetsMaximum] = negativeAmount(asset).number();
-
1174 env(tx, ter{temMALFORMED});
-
1175 }
-
1176
-
1177 {
-
1178 auto tx = tx1;
-
1179 tx[sfFlags] = tfVaultPrivate;
-
1180 tx[sfDomainID] = "0";
-
1181 env(tx, ter{temMALFORMED});
-
1182 }
-
1183 });
-
1184 }
+
600
+
601 void
+
+ +
603 {
+
604 using namespace test::jtx;
+
605
+
606 struct CaseArgs
+
607 {
+
608 FeatureBitset features =
+
609 testable_amendments() | featureSingleAssetVault;
+
610 };
+
611
+
612 auto testCase = [&, this](
+
613 std::function<void(
+
614 Env & env,
+
615 Account const& issuer,
+
616 Account const& owner,
+
617 Asset const& asset,
+
618 Vault& vault)> test,
+
619 CaseArgs args = {}) {
+
620 Env env{*this, args.features};
+
621 Account issuer{"issuer"};
+
622 Account owner{"owner"};
+
623 Vault vault{env};
+
624 env.fund(XRP(1000), issuer, owner);
+
625 env.close();
+
626
+
627 env(fset(issuer, asfAllowTrustLineClawback));
+
628 env(fset(issuer, asfRequireAuth));
+
629 env.close();
+
630
+
631 PrettyAsset asset = issuer["IOU"];
+
632 env(trust(owner, asset(1000)));
+
633 env(trust(issuer, asset(0), owner, tfSetfAuth));
+
634 env(pay(issuer, owner, asset(1000)));
+
635 env.close();
+
636
+
637 test(env, issuer, owner, asset, vault);
+
638 };
+
639
+
640 testCase(
+
641 [&](Env& env,
+
642 Account const& issuer,
+
643 Account const& owner,
+
644 Asset const& asset,
+
645 Vault& vault) {
+
646 testcase("disabled single asset vault");
+
647
+
648 auto [tx, keylet] =
+
649 vault.create({.owner = owner, .asset = asset});
+
650 env(tx, ter{temDISABLED});
+
651
+
652 {
+
653 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
654 env(tx, ter{temDISABLED});
+
655 }
+
656
+
657 {
+
658 auto tx = vault.deposit(
+
659 {.depositor = owner,
+
660 .id = keylet.key,
+
661 .amount = asset(10)});
+
662 env(tx, ter{temDISABLED});
+
663 }
+
664
+
665 {
+
666 auto tx = vault.withdraw(
+
667 {.depositor = owner,
+
668 .id = keylet.key,
+
669 .amount = asset(10)});
+
670 env(tx, ter{temDISABLED});
+
671 }
+
672
+
673 {
+
674 auto tx = vault.clawback(
+
675 {.issuer = issuer,
+
676 .id = keylet.key,
+
677 .holder = owner,
+
678 .amount = asset(10)});
+
679 env(tx, ter{temDISABLED});
+
680 }
+
681
+
682 {
+
683 auto tx = vault.del({.owner = owner, .id = keylet.key});
+
684 env(tx, ter{temDISABLED});
+
685 }
+
686 },
+
687 {.features = testable_amendments() - featureSingleAssetVault});
+
688
+
689 testCase([&](Env& env,
+
690 Account const& issuer,
+
691 Account const& owner,
+
692 Asset const& asset,
+
693 Vault& vault) {
+
694 testcase("invalid flags");
+
695
+
696 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
697 tx[sfFlags] = tfClearDeepFreeze;
+
698 env(tx, ter{temINVALID_FLAG});
+
699
+
700 {
+
701 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
702 tx[sfFlags] = tfClearDeepFreeze;
+
703 env(tx, ter{temINVALID_FLAG});
+
704 }
+
705
+
706 {
+
707 auto tx = vault.deposit(
+
708 {.depositor = owner,
+
709 .id = keylet.key,
+
710 .amount = asset(10)});
+
711 tx[sfFlags] = tfClearDeepFreeze;
+
712 env(tx, ter{temINVALID_FLAG});
+
713 }
+
714
+
715 {
+
716 auto tx = vault.withdraw(
+
717 {.depositor = owner,
+
718 .id = keylet.key,
+
719 .amount = asset(10)});
+
720 tx[sfFlags] = tfClearDeepFreeze;
+
721 env(tx, ter{temINVALID_FLAG});
+
722 }
+
723
+
724 {
+
725 auto tx = vault.clawback(
+
726 {.issuer = issuer,
+
727 .id = keylet.key,
+
728 .holder = owner,
+
729 .amount = asset(10)});
+
730 tx[sfFlags] = tfClearDeepFreeze;
+
731 env(tx, ter{temINVALID_FLAG});
+
732 }
+
733
+
734 {
+
735 auto tx = vault.del({.owner = owner, .id = keylet.key});
+
736 tx[sfFlags] = tfClearDeepFreeze;
+
737 env(tx, ter{temINVALID_FLAG});
+
738 }
+
739 });
+
740
+
741 testCase([&](Env& env,
+
742 Account const& issuer,
+
743 Account const& owner,
+
744 Asset const& asset,
+
745 Vault& vault) {
+
746 testcase("invalid fee");
+
747
+
748 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
749 tx[jss::Fee] = "-1";
+
750 env(tx, ter{temBAD_FEE});
+
751
+
752 {
+
753 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
754 tx[jss::Fee] = "-1";
+
755 env(tx, ter{temBAD_FEE});
+
756 }
+
757
+
758 {
+
759 auto tx = vault.deposit(
+
760 {.depositor = owner,
+
761 .id = keylet.key,
+
762 .amount = asset(10)});
+
763 tx[jss::Fee] = "-1";
+
764 env(tx, ter{temBAD_FEE});
+
765 }
+
766
+
767 {
+
768 auto tx = vault.withdraw(
+
769 {.depositor = owner,
+
770 .id = keylet.key,
+
771 .amount = asset(10)});
+
772 tx[jss::Fee] = "-1";
+
773 env(tx, ter{temBAD_FEE});
+
774 }
+
775
+
776 {
+
777 auto tx = vault.clawback(
+
778 {.issuer = issuer,
+
779 .id = keylet.key,
+
780 .holder = owner,
+
781 .amount = asset(10)});
+
782 tx[jss::Fee] = "-1";
+
783 env(tx, ter{temBAD_FEE});
+
784 }
+
785
+
786 {
+
787 auto tx = vault.del({.owner = owner, .id = keylet.key});
+
788 tx[jss::Fee] = "-1";
+
789 env(tx, ter{temBAD_FEE});
+
790 }
+
791 });
+
792
+
793 testCase(
+
794 [&](Env& env,
+
795 Account const&,
+
796 Account const& owner,
+
797 Asset const&,
+
798 Vault& vault) {
+
799 testcase("disabled permissioned domain");
+
800
+
801 auto [tx, keylet] =
+
802 vault.create({.owner = owner, .asset = xrpIssue()});
+
803 tx[sfDomainID] = to_string(base_uint<256>(42ul));
+
804 env(tx, ter{temDISABLED});
+
805
+
806 {
+
807 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
808 tx[sfDomainID] = to_string(base_uint<256>(42ul));
+
809 env(tx, ter{temDISABLED});
+
810 }
+
811
+
812 {
+
813 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
814 tx[sfDomainID] = "0";
+
815 env(tx, ter{temDISABLED});
+
816 }
+
817 },
+
818 {.features = (testable_amendments() | featureSingleAssetVault) -
+
819 featurePermissionedDomains});
+
820
+
821 testCase([&](Env& env,
+
822 Account const& issuer,
+
823 Account const& owner,
+
824 Asset const& asset,
+
825 Vault& vault) {
+
826 testcase("use zero vault");
+
827
+
828 auto [tx, keylet] =
+
829 vault.create({.owner = owner, .asset = xrpIssue()});
+
830
+
831 {
+
832 auto tx = vault.set({
+
833 .owner = owner,
+
834 .id = beast::zero,
+
835 });
+
836 env(tx, ter{temMALFORMED});
+
837 }
+
838
+
839 {
+
840 auto tx = vault.deposit(
+
841 {.depositor = owner,
+
842 .id = beast::zero,
+
843 .amount = asset(10)});
+
844 env(tx, ter(temMALFORMED));
+
845 }
+
846
+
847 {
+
848 auto tx = vault.withdraw(
+
849 {.depositor = owner,
+
850 .id = beast::zero,
+
851 .amount = asset(10)});
+
852 env(tx, ter{temMALFORMED});
+
853 }
+
854
+
855 {
+
856 auto tx = vault.clawback(
+
857 {.issuer = issuer,
+
858 .id = beast::zero,
+
859 .holder = owner,
+
860 .amount = asset(10)});
+
861 env(tx, ter{temMALFORMED});
+
862 }
+
863
+
864 {
+
865 auto tx = vault.del({
+
866 .owner = owner,
+
867 .id = beast::zero,
+
868 });
+
869 env(tx, ter{temMALFORMED});
+
870 }
+
871 });
+
872
+
873 testCase([&](Env& env,
+
874 Account const& issuer,
+
875 Account const& owner,
+
876 Asset const& asset,
+
877 Vault& vault) {
+
878 testcase("clawback from self");
+
879
+
880 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
881
+
882 {
+
883 auto tx = vault.clawback(
+
884 {.issuer = issuer,
+
885 .id = keylet.key,
+
886 .holder = issuer,
+
887 .amount = asset(10)});
+
888 env(tx, ter{temMALFORMED});
+
889 }
+
890 });
+
891
+
892 testCase([&](Env& env,
+
893 Account const&,
+
894 Account const& owner,
+
895 Asset const& asset,
+
896 Vault& vault) {
+
897 testcase("withdraw to bad destination");
+
898
+
899 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
900
+
901 {
+
902 auto tx = vault.withdraw(
+
903 {.depositor = owner,
+
904 .id = keylet.key,
+
905 .amount = asset(10)});
+
906 tx[jss::Destination] = "0";
+
907 env(tx, ter{temMALFORMED});
+
908 }
+
909 });
+
910
+
911 testCase([&](Env& env,
+
912 Account const&,
+
913 Account const& owner,
+
914 Asset const& asset,
+
915 Vault& vault) {
+
916 testcase("create with Scale");
+
917
+
918 {
+
919 auto [tx, keylet] =
+
920 vault.create({.owner = owner, .asset = asset});
+
921 tx[sfScale] = 255;
+
922 env(tx, ter(temMALFORMED));
+
923 }
+
924
+
925 {
+
926 auto [tx, keylet] =
+
927 vault.create({.owner = owner, .asset = asset});
+
928 tx[sfScale] = 19;
+
929 env(tx, ter(temMALFORMED));
+
930 }
+
931
+
932 // accepted range from 0 to 18
+
933 {
+
934 auto [tx, keylet] =
+
935 vault.create({.owner = owner, .asset = asset});
+
936 tx[sfScale] = 18;
+
937 env(tx);
+
938 env.close();
+
939 auto const sleVault = env.le(keylet);
+
940 BEAST_EXPECT(sleVault);
+
941 BEAST_EXPECT((*sleVault)[sfScale] == 18);
+
942 }
+
943
+
944 {
+
945 auto [tx, keylet] =
+
946 vault.create({.owner = owner, .asset = asset});
+
947 tx[sfScale] = 0;
+
948 env(tx);
+
949 env.close();
+
950 auto const sleVault = env.le(keylet);
+
951 BEAST_EXPECT(sleVault);
+
952 BEAST_EXPECT((*sleVault)[sfScale] == 0);
+
953 }
+
954
+
955 {
+
956 auto [tx, keylet] =
+
957 vault.create({.owner = owner, .asset = asset});
+
958 env(tx);
+
959 env.close();
+
960 auto const sleVault = env.le(keylet);
+
961 BEAST_EXPECT(sleVault);
+
962 BEAST_EXPECT((*sleVault)[sfScale] == 6);
+
963 }
+
964 });
+
965
+
966 testCase([&](Env& env,
+
967 Account const&,
+
968 Account const& owner,
+
969 Asset const& asset,
+
970 Vault& vault) {
+
971 testcase("create or set invalid data");
+
972
+
973 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
+
974
+
975 {
+
976 auto tx = tx1;
+
977 tx[sfData] = "";
+
978 env(tx, ter(temMALFORMED));
+
979 }
+
980
+
981 {
+
982 auto tx = tx1;
+
983 // A hexadecimal string of 257 bytes.
+
984 tx[sfData] = std::string(514, 'A');
+
985 env(tx, ter(temMALFORMED));
+
986 }
+
987
+
988 {
+
989 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
990 tx[sfData] = "";
+
991 env(tx, ter{temMALFORMED});
+
992 }
+
993
+
994 {
+
995 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
996 // A hexadecimal string of 257 bytes.
+
997 tx[sfData] = std::string(514, 'A');
+
998 env(tx, ter{temMALFORMED});
+
999 }
+
1000 });
+
1001
+
1002 testCase([&](Env& env,
+
1003 Account const&,
+
1004 Account const& owner,
+
1005 Asset const& asset,
+
1006 Vault& vault) {
+
1007 testcase("set nothing updated");
+
1008
+
1009 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1010
+
1011 {
+
1012 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
1013 env(tx, ter{temMALFORMED});
+
1014 }
+
1015 });
+
1016
+
1017 testCase([&](Env& env,
+
1018 Account const&,
+
1019 Account const& owner,
+
1020 Asset const& asset,
+
1021 Vault& vault) {
+
1022 testcase("create with invalid metadata");
+
1023
+
1024 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
+
1025
+
1026 {
+
1027 auto tx = tx1;
+
1028 tx[sfMPTokenMetadata] = "";
+
1029 env(tx, ter(temMALFORMED));
+
1030 }
+
1031
+
1032 {
+
1033 auto tx = tx1;
+
1034 // This metadata is for the share token.
+
1035 // A hexadecimal string of 1025 bytes.
+
1036 tx[sfMPTokenMetadata] = std::string(2050, 'B');
+
1037 env(tx, ter(temMALFORMED));
+
1038 }
+
1039 });
+
1040
+
1041 testCase([&](Env& env,
+
1042 Account const&,
+
1043 Account const& owner,
+
1044 Asset const& asset,
+
1045 Vault& vault) {
+
1046 testcase("set negative maximum");
+
1047
+
1048 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1049
+
1050 {
+
1051 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
1052 tx[sfAssetsMaximum] = negativeAmount(asset).number();
+
1053 env(tx, ter{temMALFORMED});
+
1054 }
+
1055 });
+
1056
+
1057 testCase([&](Env& env,
+
1058 Account const&,
+
1059 Account const& owner,
+
1060 Asset const& asset,
+
1061 Vault& vault) {
+
1062 testcase("invalid deposit amount");
+
1063
+
1064 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1065
+
1066 {
+
1067 auto tx = vault.deposit(
+
1068 {.depositor = owner,
+
1069 .id = keylet.key,
+
1070 .amount = negativeAmount(asset)});
+
1071 env(tx, ter(temBAD_AMOUNT));
+
1072 }
+
1073
+
1074 {
+
1075 auto tx = vault.deposit(
+
1076 {.depositor = owner, .id = keylet.key, .amount = asset(0)});
+
1077 env(tx, ter(temBAD_AMOUNT));
+
1078 }
+
1079 });
+
1080
+
1081 testCase([&](Env& env,
+
1082 Account const&,
+
1083 Account const& owner,
+
1084 Asset const& asset,
+
1085 Vault& vault) {
+
1086 testcase("invalid set immutable flag");
+
1087
+
1088 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1089
+
1090 {
+
1091 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
1092 tx[sfFlags] = tfVaultPrivate;
+
1093 env(tx, ter(temINVALID_FLAG));
+
1094 }
+
1095 });
+
1096
+
1097 testCase([&](Env& env,
+
1098 Account const&,
+
1099 Account const& owner,
+
1100 Asset const& asset,
+
1101 Vault& vault) {
+
1102 testcase("invalid withdraw amount");
+
1103
+
1104 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1105
+
1106 {
+
1107 auto tx = vault.withdraw(
+
1108 {.depositor = owner,
+
1109 .id = keylet.key,
+
1110 .amount = negativeAmount(asset)});
+
1111 env(tx, ter(temBAD_AMOUNT));
+
1112 }
+
1113
+
1114 {
+
1115 auto tx = vault.withdraw(
+
1116 {.depositor = owner, .id = keylet.key, .amount = asset(0)});
+
1117 env(tx, ter(temBAD_AMOUNT));
+
1118 }
+
1119 });
+
1120
+
1121 testCase([&](Env& env,
+
1122 Account const& issuer,
+
1123 Account const& owner,
+
1124 Asset const& asset,
+
1125 Vault& vault) {
+
1126 testcase("invalid clawback");
+
1127
+
1128 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1129
+
1130 {
+
1131 auto tx = vault.clawback(
+
1132 {.issuer = owner,
+
1133 .id = keylet.key,
+
1134 .holder = issuer,
+
1135 .amount = asset(50)});
+
1136 env(tx, ter(temMALFORMED));
+
1137 }
+
1138
+
1139 {
+
1140 auto tx = vault.clawback(
+
1141 {.issuer = issuer,
+
1142 .id = keylet.key,
+
1143 .holder = owner,
+
1144 .amount = negativeAmount(asset)});
+
1145 env(tx, ter(temBAD_AMOUNT));
+
1146 }
+
1147 });
+
1148
+
1149 testCase([&](Env& env,
+
1150 Account const&,
+
1151 Account const& owner,
+
1152 Asset const& asset,
+
1153 Vault& vault) {
+
1154 testcase("invalid create");
+
1155
+
1156 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
+
1157
+
1158 {
+
1159 auto tx = tx1;
+
1160 tx[sfWithdrawalPolicy] = 0;
+
1161 env(tx, ter(temMALFORMED));
+
1162 }
+
1163
+
1164 {
+
1165 auto tx = tx1;
+
1166 tx[sfDomainID] = to_string(base_uint<256>(42ul));
+
1167 env(tx, ter{temMALFORMED});
+
1168 }
+
1169
+
1170 {
+
1171 auto tx = tx1;
+
1172 tx[sfAssetsMaximum] = negativeAmount(asset).number();
+
1173 env(tx, ter{temMALFORMED});
+
1174 }
+
1175
+
1176 {
+
1177 auto tx = tx1;
+
1178 tx[sfFlags] = tfVaultPrivate;
+
1179 tx[sfDomainID] = "0";
+
1180 env(tx, ter{temMALFORMED});
+
1181 }
+
1182 });
+
1183 }
-
1185
-
1186 // Test for non-asset specific behaviors.
-
1187 void
-
- -
1189 {
-
1190 using namespace test::jtx;
-
1191
-
1192 auto testCase = [this](std::function<void(
-
1193 Env & env,
-
1194 Account const& issuer,
-
1195 Account const& owner,
-
1196 Account const& depositor,
-
1197 Asset const& asset,
-
1198 Vault& vault)> test) {
-
1199 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1200 Account issuer{"issuer"};
-
1201 Account owner{"owner"};
-
1202 Account depositor{"depositor"};
-
1203 env.fund(XRP(1000), issuer, owner, depositor);
-
1204 env.close();
-
1205 Vault vault{env};
-
1206 Asset asset = xrpIssue();
-
1207
-
1208 test(env, issuer, owner, depositor, asset, vault);
-
1209 };
-
1210
-
1211 testCase([this](
-
1212 Env& env,
-
1213 Account const& issuer,
-
1214 Account const& owner,
-
1215 Account const& depositor,
-
1216 PrettyAsset const& asset,
-
1217 Vault& vault) {
-
1218 testcase("nothing to set");
-
1219 auto tx = vault.set({.owner = owner, .id = keylet::skip().key});
-
1220 tx[sfAssetsMaximum] = asset(0).number();
-
1221 env(tx, ter(tecNO_ENTRY));
-
1222 });
-
1223
-
1224 testCase([this](
-
1225 Env& env,
-
1226 Account const& issuer,
-
1227 Account const& owner,
-
1228 Account const& depositor,
-
1229 PrettyAsset const& asset,
-
1230 Vault& vault) {
-
1231 testcase("nothing to deposit to");
-
1232 auto tx = vault.deposit(
-
1233 {.depositor = depositor,
-
1234 .id = keylet::skip().key,
-
1235 .amount = asset(10)});
-
1236 env(tx, ter(tecNO_ENTRY));
-
1237 });
-
1238
-
1239 testCase([this](
-
1240 Env& env,
-
1241 Account const& issuer,
-
1242 Account const& owner,
-
1243 Account const& depositor,
-
1244 PrettyAsset const& asset,
-
1245 Vault& vault) {
-
1246 testcase("nothing to withdraw from");
-
1247 auto tx = vault.withdraw(
-
1248 {.depositor = depositor,
-
1249 .id = keylet::skip().key,
-
1250 .amount = asset(10)});
-
1251 env(tx, ter(tecNO_ENTRY));
-
1252 });
-
1253
-
1254 testCase([this](
-
1255 Env& env,
-
1256 Account const& issuer,
-
1257 Account const& owner,
-
1258 Account const& depositor,
-
1259 Asset const& asset,
-
1260 Vault& vault) {
-
1261 testcase("nothing to delete");
-
1262 auto tx = vault.del({.owner = owner, .id = keylet::skip().key});
-
1263 env(tx, ter(tecNO_ENTRY));
-
1264 });
-
1265
-
1266 testCase([this](
-
1267 Env& env,
-
1268 Account const& issuer,
-
1269 Account const& owner,
-
1270 Account const& depositor,
-
1271 Asset const& asset,
-
1272 Vault& vault) {
-
1273 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1274 testcase("transaction is good");
-
1275 env(tx);
-
1276 });
-
1277
-
1278 testCase([this](
-
1279 Env& env,
-
1280 Account const& issuer,
-
1281 Account const& owner,
-
1282 Account const& depositor,
-
1283 Asset const& asset,
-
1284 Vault& vault) {
-
1285 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1286 tx[sfWithdrawalPolicy] = 1;
-
1287 testcase("explicitly select withdrawal policy");
-
1288 env(tx);
-
1289 });
-
1290
-
1291 testCase([this](
-
1292 Env& env,
-
1293 Account const& issuer,
-
1294 Account const& owner,
-
1295 Account const& depositor,
-
1296 Asset const& asset,
-
1297 Vault& vault) {
-
1298 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1299 testcase("insufficient fee");
-
1300 env(tx, fee(env.current()->fees().base), ter(telINSUF_FEE_P));
-
1301 });
-
1302
-
1303 testCase([this](
-
1304 Env& env,
-
1305 Account const& issuer,
-
1306 Account const& owner,
-
1307 Account const& depositor,
-
1308 Asset const& asset,
-
1309 Vault& vault) {
-
1310 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1311 testcase("insufficient reserve");
-
1312 // It is possible to construct a complicated mathematical
-
1313 // expression for this amount, but it is sadly not easy.
-
1314 env(pay(owner, issuer, XRP(775)));
-
1315 env.close();
-
1316 env(tx, ter(tecINSUFFICIENT_RESERVE));
-
1317 });
-
1318
-
1319 testCase([this](
-
1320 Env& env,
-
1321 Account const& issuer,
-
1322 Account const& owner,
-
1323 Account const& depositor,
-
1324 Asset const& asset,
-
1325 Vault& vault) {
-
1326 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1327 tx[sfFlags] = tfVaultPrivate;
-
1328 tx[sfDomainID] = to_string(base_uint<256>(42ul));
-
1329 testcase("non-existing domain");
-
1330 env(tx, ter{tecOBJECT_NOT_FOUND});
-
1331 });
-
1332
-
1333 testCase([this](
-
1334 Env& env,
-
1335 Account const& issuer,
-
1336 Account const& owner,
-
1337 Account const& depositor,
-
1338 Asset const& asset,
-
1339 Vault& vault) {
-
1340 testcase("cannot set Scale=0");
-
1341 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1342 tx[sfScale] = 0;
-
1343 env(tx, ter{temMALFORMED});
-
1344 });
-
1345
-
1346 testCase([this](
-
1347 Env& env,
-
1348 Account const& issuer,
-
1349 Account const& owner,
-
1350 Account const& depositor,
-
1351 Asset const& asset,
-
1352 Vault& vault) {
-
1353 testcase("cannot set Scale=1");
-
1354 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1355 tx[sfScale] = 1;
-
1356 env(tx, ter{temMALFORMED});
-
1357 });
-
1358 }
+
1184
+
1185 // Test for non-asset specific behaviors.
+
1186 void
+
+ +
1188 {
+
1189 using namespace test::jtx;
+
1190
+
1191 auto testCase = [this](std::function<void(
+
1192 Env & env,
+
1193 Account const& issuer,
+
1194 Account const& owner,
+
1195 Account const& depositor,
+
1196 Asset const& asset,
+
1197 Vault& vault)> test) {
+
1198 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1199 Account issuer{"issuer"};
+
1200 Account owner{"owner"};
+
1201 Account depositor{"depositor"};
+
1202 env.fund(XRP(1000), issuer, owner, depositor);
+
1203 env.close();
+
1204 Vault vault{env};
+
1205 Asset asset = xrpIssue();
+
1206
+
1207 test(env, issuer, owner, depositor, asset, vault);
+
1208 };
+
1209
+
1210 testCase([this](
+
1211 Env& env,
+
1212 Account const& issuer,
+
1213 Account const& owner,
+
1214 Account const& depositor,
+
1215 PrettyAsset const& asset,
+
1216 Vault& vault) {
+
1217 testcase("nothing to set");
+
1218 auto tx = vault.set({.owner = owner, .id = keylet::skip().key});
+
1219 tx[sfAssetsMaximum] = asset(0).number();
+
1220 env(tx, ter(tecNO_ENTRY));
+
1221 });
+
1222
+
1223 testCase([this](
+
1224 Env& env,
+
1225 Account const& issuer,
+
1226 Account const& owner,
+
1227 Account const& depositor,
+
1228 PrettyAsset const& asset,
+
1229 Vault& vault) {
+
1230 testcase("nothing to deposit to");
+
1231 auto tx = vault.deposit(
+
1232 {.depositor = depositor,
+
1233 .id = keylet::skip().key,
+
1234 .amount = asset(10)});
+
1235 env(tx, ter(tecNO_ENTRY));
+
1236 });
+
1237
+
1238 testCase([this](
+
1239 Env& env,
+
1240 Account const& issuer,
+
1241 Account const& owner,
+
1242 Account const& depositor,
+
1243 PrettyAsset const& asset,
+
1244 Vault& vault) {
+
1245 testcase("nothing to withdraw from");
+
1246 auto tx = vault.withdraw(
+
1247 {.depositor = depositor,
+
1248 .id = keylet::skip().key,
+
1249 .amount = asset(10)});
+
1250 env(tx, ter(tecNO_ENTRY));
+
1251 });
+
1252
+
1253 testCase([this](
+
1254 Env& env,
+
1255 Account const& issuer,
+
1256 Account const& owner,
+
1257 Account const& depositor,
+
1258 Asset const& asset,
+
1259 Vault& vault) {
+
1260 testcase("nothing to delete");
+
1261 auto tx = vault.del({.owner = owner, .id = keylet::skip().key});
+
1262 env(tx, ter(tecNO_ENTRY));
+
1263 });
+
1264
+
1265 testCase([this](
+
1266 Env& env,
+
1267 Account const& issuer,
+
1268 Account const& owner,
+
1269 Account const& depositor,
+
1270 Asset const& asset,
+
1271 Vault& vault) {
+
1272 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1273 testcase("transaction is good");
+
1274 env(tx);
+
1275 });
+
1276
+
1277 testCase([this](
+
1278 Env& env,
+
1279 Account const& issuer,
+
1280 Account const& owner,
+
1281 Account const& depositor,
+
1282 Asset const& asset,
+
1283 Vault& vault) {
+
1284 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1285 tx[sfWithdrawalPolicy] = 1;
+
1286 testcase("explicitly select withdrawal policy");
+
1287 env(tx);
+
1288 });
+
1289
+
1290 testCase([this](
+
1291 Env& env,
+
1292 Account const& issuer,
+
1293 Account const& owner,
+
1294 Account const& depositor,
+
1295 Asset const& asset,
+
1296 Vault& vault) {
+
1297 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1298 testcase("insufficient fee");
+
1299 env(tx, fee(env.current()->fees().base), ter(telINSUF_FEE_P));
+
1300 });
+
1301
+
1302 testCase([this](
+
1303 Env& env,
+
1304 Account const& issuer,
+
1305 Account const& owner,
+
1306 Account const& depositor,
+
1307 Asset const& asset,
+
1308 Vault& vault) {
+
1309 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1310 testcase("insufficient reserve");
+
1311 // It is possible to construct a complicated mathematical
+
1312 // expression for this amount, but it is sadly not easy.
+
1313 env(pay(owner, issuer, XRP(775)));
+
1314 env.close();
+
1315 env(tx, ter(tecINSUFFICIENT_RESERVE));
+
1316 });
+
1317
+
1318 testCase([this](
+
1319 Env& env,
+
1320 Account const& issuer,
+
1321 Account const& owner,
+
1322 Account const& depositor,
+
1323 Asset const& asset,
+
1324 Vault& vault) {
+
1325 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1326 tx[sfFlags] = tfVaultPrivate;
+
1327 tx[sfDomainID] = to_string(base_uint<256>(42ul));
+
1328 testcase("non-existing domain");
+
1329 env(tx, ter{tecOBJECT_NOT_FOUND});
+
1330 });
+
1331
+
1332 testCase([this](
+
1333 Env& env,
+
1334 Account const& issuer,
+
1335 Account const& owner,
+
1336 Account const& depositor,
+
1337 Asset const& asset,
+
1338 Vault& vault) {
+
1339 testcase("cannot set Scale=0");
+
1340 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1341 tx[sfScale] = 0;
+
1342 env(tx, ter{temMALFORMED});
+
1343 });
+
1344
+
1345 testCase([this](
+
1346 Env& env,
+
1347 Account const& issuer,
+
1348 Account const& owner,
+
1349 Account const& depositor,
+
1350 Asset const& asset,
+
1351 Vault& vault) {
+
1352 testcase("cannot set Scale=1");
+
1353 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1354 tx[sfScale] = 1;
+
1355 env(tx, ter{temMALFORMED});
+
1356 });
+
1357 }
-
1359
-
1360 void
-
- -
1362 {
-
1363 using namespace test::jtx;
-
1364 {
-
1365 {
-
1366 testcase("IOU fail because MPT is disabled");
-
1367 Env env{
-
1368 *this,
-
1369 (testable_amendments() - featureMPTokensV1) |
-
1370 featureSingleAssetVault};
-
1371 Account issuer{"issuer"};
-
1372 Account owner{"owner"};
-
1373 env.fund(XRP(1000), issuer, owner);
-
1374 env.close();
-
1375
-
1376 Vault vault{env};
-
1377 Asset asset = issuer["IOU"].asset();
-
1378 auto [tx, keylet] =
-
1379 vault.create({.owner = owner, .asset = asset});
-
1380
-
1381 env(tx, ter(temDISABLED));
-
1382 env.close();
-
1383 }
-
1384
-
1385 {
-
1386 testcase("IOU fail create frozen");
-
1387 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1388 Account issuer{"issuer"};
-
1389 Account owner{"owner"};
-
1390 env.fund(XRP(1000), issuer, owner);
-
1391 env.close();
-
1392 env(fset(issuer, asfGlobalFreeze));
-
1393 env.close();
-
1394
-
1395 Vault vault{env};
-
1396 Asset asset = issuer["IOU"].asset();
-
1397 auto [tx, keylet] =
-
1398 vault.create({.owner = owner, .asset = asset});
-
1399
-
1400 env(tx, ter(tecFROZEN));
-
1401 env.close();
-
1402 }
-
1403
-
1404 {
-
1405 testcase("IOU fail create no ripling");
-
1406 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1407 Account issuer{"issuer"};
-
1408 Account owner{"owner"};
-
1409 env.fund(XRP(1000), issuer, owner);
-
1410 env.close();
-
1411 env(fclear(issuer, asfDefaultRipple));
-
1412 env.close();
-
1413
-
1414 Vault vault{env};
-
1415 Asset asset = issuer["IOU"].asset();
-
1416 auto [tx, keylet] =
-
1417 vault.create({.owner = owner, .asset = asset});
-
1418 env(tx, ter(terNO_RIPPLE));
-
1419 env.close();
-
1420 }
-
1421
-
1422 {
-
1423 testcase("IOU no issuer");
-
1424 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1425 Account issuer{"issuer"};
-
1426 Account owner{"owner"};
-
1427 env.fund(XRP(1000), owner);
-
1428 env.close();
-
1429
-
1430 Vault vault{env};
-
1431 Asset asset = issuer["IOU"].asset();
-
1432 {
-
1433 auto [tx, keylet] =
-
1434 vault.create({.owner = owner, .asset = asset});
-
1435 env(tx, ter(terNO_ACCOUNT));
-
1436 env.close();
-
1437 }
-
1438 }
-
1439 }
-
1440
-
1441 {
-
1442 testcase("IOU fail create vault for AMM LPToken");
-
1443 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1444 Account const gw("gateway");
-
1445 Account const alice("alice");
-
1446 Account const carol("carol");
-
1447 IOU const USD = gw["USD"];
-
1448
-
1449 auto const [asset1, asset2] =
-
1450 std::pair<STAmount, STAmount>(XRP(10000), USD(10000));
-
1451 auto tofund = [&](STAmount const& a) -> STAmount {
-
1452 if (a.native())
-
1453 {
-
1454 auto const defXRP = XRP(30000);
-
1455 if (a <= defXRP)
-
1456 return defXRP;
-
1457 return a + XRP(1000);
-
1458 }
-
1459 auto const defIOU = STAmount{a.issue(), 30000};
-
1460 if (a <= defIOU)
-
1461 return defIOU;
-
1462 return a + STAmount{a.issue(), 1000};
-
1463 };
-
1464 auto const toFund1 = tofund(asset1);
-
1465 auto const toFund2 = tofund(asset2);
-
1466 BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2);
-
1467
-
1468 if (!asset1.native() && !asset2.native())
-
1469 fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All);
-
1470 else if (asset1.native())
-
1471 fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All);
-
1472 else if (asset2.native())
-
1473 fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All);
-
1474
-
1475 AMM ammAlice(
-
1476 env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0});
-
1477
-
1478 Account const owner{"owner"};
-
1479 env.fund(XRP(1000000), owner);
-
1480
-
1481 Vault vault{env};
-
1482 auto [tx, k] =
-
1483 vault.create({.owner = owner, .asset = ammAlice.lptIssue()});
-
1484 env(tx, ter{tecWRONG_ASSET});
-
1485 env.close();
-
1486 }
-
1487 }
+
1358
+
1359 void
+
+ +
1361 {
+
1362 using namespace test::jtx;
+
1363 {
+
1364 {
+
1365 testcase("IOU fail because MPT is disabled");
+
1366 Env env{
+
1367 *this,
+
1368 (testable_amendments() - featureMPTokensV1) |
+
1369 featureSingleAssetVault};
+
1370 Account issuer{"issuer"};
+
1371 Account owner{"owner"};
+
1372 env.fund(XRP(1000), issuer, owner);
+
1373 env.close();
+
1374
+
1375 Vault vault{env};
+
1376 Asset asset = issuer["IOU"].asset();
+
1377 auto [tx, keylet] =
+
1378 vault.create({.owner = owner, .asset = asset});
+
1379
+
1380 env(tx, ter(temDISABLED));
+
1381 env.close();
+
1382 }
+
1383
+
1384 {
+
1385 testcase("IOU fail create frozen");
+
1386 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1387 Account issuer{"issuer"};
+
1388 Account owner{"owner"};
+
1389 env.fund(XRP(1000), issuer, owner);
+
1390 env.close();
+
1391 env(fset(issuer, asfGlobalFreeze));
+
1392 env.close();
+
1393
+
1394 Vault vault{env};
+
1395 Asset asset = issuer["IOU"].asset();
+
1396 auto [tx, keylet] =
+
1397 vault.create({.owner = owner, .asset = asset});
+
1398
+
1399 env(tx, ter(tecFROZEN));
+
1400 env.close();
+
1401 }
+
1402
+
1403 {
+
1404 testcase("IOU fail create no ripling");
+
1405 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1406 Account issuer{"issuer"};
+
1407 Account owner{"owner"};
+
1408 env.fund(XRP(1000), issuer, owner);
+
1409 env.close();
+
1410 env(fclear(issuer, asfDefaultRipple));
+
1411 env.close();
+
1412
+
1413 Vault vault{env};
+
1414 Asset asset = issuer["IOU"].asset();
+
1415 auto [tx, keylet] =
+
1416 vault.create({.owner = owner, .asset = asset});
+
1417 env(tx, ter(terNO_RIPPLE));
+
1418 env.close();
+
1419 }
+
1420
+
1421 {
+
1422 testcase("IOU no issuer");
+
1423 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1424 Account issuer{"issuer"};
+
1425 Account owner{"owner"};
+
1426 env.fund(XRP(1000), owner);
+
1427 env.close();
+
1428
+
1429 Vault vault{env};
+
1430 Asset asset = issuer["IOU"].asset();
+
1431 {
+
1432 auto [tx, keylet] =
+
1433 vault.create({.owner = owner, .asset = asset});
+
1434 env(tx, ter(terNO_ACCOUNT));
+
1435 env.close();
+
1436 }
+
1437 }
+
1438 }
+
1439
+
1440 {
+
1441 testcase("IOU fail create vault for AMM LPToken");
+
1442 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1443 Account const gw("gateway");
+
1444 Account const alice("alice");
+
1445 Account const carol("carol");
+
1446 IOU const USD = gw["USD"];
+
1447
+
1448 auto const [asset1, asset2] =
+
1449 std::pair<STAmount, STAmount>(XRP(10000), USD(10000));
+
1450 auto tofund = [&](STAmount const& a) -> STAmount {
+
1451 if (a.native())
+
1452 {
+
1453 auto const defXRP = XRP(30000);
+
1454 if (a <= defXRP)
+
1455 return defXRP;
+
1456 return a + XRP(1000);
+
1457 }
+
1458 auto const defIOU = STAmount{a.issue(), 30000};
+
1459 if (a <= defIOU)
+
1460 return defIOU;
+
1461 return a + STAmount{a.issue(), 1000};
+
1462 };
+
1463 auto const toFund1 = tofund(asset1);
+
1464 auto const toFund2 = tofund(asset2);
+
1465 BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2);
+
1466
+
1467 if (!asset1.native() && !asset2.native())
+
1468 fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All);
+
1469 else if (asset1.native())
+
1470 fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All);
+
1471 else if (asset2.native())
+
1472 fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All);
+
1473
+
1474 AMM ammAlice(
+
1475 env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0});
+
1476
+
1477 Account const owner{"owner"};
+
1478 env.fund(XRP(1000000), owner);
+
1479
+
1480 Vault vault{env};
+
1481 auto [tx, k] =
+
1482 vault.create({.owner = owner, .asset = ammAlice.lptIssue()});
+
1483 env(tx, ter{tecWRONG_ASSET});
+
1484 env.close();
+
1485 }
+
1486 }
-
1488
-
1489 void
-
- -
1491 {
-
1492 using namespace test::jtx;
-
1493
-
1494 auto testCase = [this](std::function<void(
-
1495 Env & env,
-
1496 Account const& issuer,
-
1497 Account const& owner,
-
1498 Account const& depositor,
-
1499 Asset const& asset,
-
1500 Vault& vault)> test) {
-
1501 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1502 Account issuer{"issuer"};
-
1503 Account owner{"owner"};
-
1504 Account depositor{"depositor"};
-
1505 env.fund(XRP(1000), issuer, owner, depositor);
-
1506 env.close();
-
1507 Vault vault{env};
-
1508 MPTTester mptt{env, issuer, mptInitNoFund};
-
1509 // Locked because that is the default flag.
-
1510 mptt.create();
-
1511 Asset asset = mptt.issuanceID();
-
1512
-
1513 test(env, issuer, owner, depositor, asset, vault);
-
1514 };
-
1515
-
1516 testCase([this](
-
1517 Env& env,
-
1518 Account const& issuer,
-
1519 Account const& owner,
-
1520 Account const& depositor,
-
1521 Asset const& asset,
-
1522 Vault& vault) {
-
1523 testcase("MPT no authorization");
-
1524 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1525 env(tx, ter(tecNO_AUTH));
-
1526 });
-
1527
-
1528 testCase([this](
-
1529 Env& env,
-
1530 Account const& issuer,
-
1531 Account const& owner,
-
1532 Account const& depositor,
-
1533 Asset const& asset,
-
1534 Vault& vault) {
-
1535 testcase("MPT cannot set Scale=0");
-
1536 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1537 tx[sfScale] = 0;
-
1538 env(tx, ter{temMALFORMED});
-
1539 });
-
1540
-
1541 testCase([this](
-
1542 Env& env,
-
1543 Account const& issuer,
-
1544 Account const& owner,
-
1545 Account const& depositor,
-
1546 Asset const& asset,
-
1547 Vault& vault) {
-
1548 testcase("MPT cannot set Scale=1");
-
1549 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1550 tx[sfScale] = 1;
-
1551 env(tx, ter{temMALFORMED});
-
1552 });
-
1553 }
+
1487
+
1488 void
+
+ +
1490 {
+
1491 using namespace test::jtx;
+
1492
+
1493 auto testCase = [this](std::function<void(
+
1494 Env & env,
+
1495 Account const& issuer,
+
1496 Account const& owner,
+
1497 Account const& depositor,
+
1498 Asset const& asset,
+
1499 Vault& vault)> test) {
+
1500 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1501 Account issuer{"issuer"};
+
1502 Account owner{"owner"};
+
1503 Account depositor{"depositor"};
+
1504 env.fund(XRP(1000), issuer, owner, depositor);
+
1505 env.close();
+
1506 Vault vault{env};
+
1507 MPTTester mptt{env, issuer, mptInitNoFund};
+
1508 // Locked because that is the default flag.
+
1509 mptt.create();
+
1510 Asset asset = mptt.issuanceID();
+
1511
+
1512 test(env, issuer, owner, depositor, asset, vault);
+
1513 };
+
1514
+
1515 testCase([this](
+
1516 Env& env,
+
1517 Account const& issuer,
+
1518 Account const& owner,
+
1519 Account const& depositor,
+
1520 Asset const& asset,
+
1521 Vault& vault) {
+
1522 testcase("MPT no authorization");
+
1523 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1524 env(tx, ter(tecNO_AUTH));
+
1525 });
+
1526
+
1527 testCase([this](
+
1528 Env& env,
+
1529 Account const& issuer,
+
1530 Account const& owner,
+
1531 Account const& depositor,
+
1532 Asset const& asset,
+
1533 Vault& vault) {
+
1534 testcase("MPT cannot set Scale=0");
+
1535 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1536 tx[sfScale] = 0;
+
1537 env(tx, ter{temMALFORMED});
+
1538 });
+
1539
+
1540 testCase([this](
+
1541 Env& env,
+
1542 Account const& issuer,
+
1543 Account const& owner,
+
1544 Account const& depositor,
+
1545 Asset const& asset,
+
1546 Vault& vault) {
+
1547 testcase("MPT cannot set Scale=1");
+
1548 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1549 tx[sfScale] = 1;
+
1550 env(tx, ter{temMALFORMED});
+
1551 });
+
1552 }
-
1554
-
1555 void
-
- -
1557 {
-
1558 using namespace test::jtx;
-
1559
-
1560 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1561 Account issuer{"issuer"};
-
1562 Account owner{"owner"};
-
1563 Account depositor{"depositor"};
-
1564 env.fund(XRP(1000), issuer, owner, depositor);
-
1565 env.close();
-
1566
-
1567 Vault vault{env};
-
1568 PrettyAsset asset = issuer["IOU"];
-
1569 env.trust(asset(1000), owner);
-
1570 env(pay(issuer, owner, asset(100)));
-
1571 env.trust(asset(1000), depositor);
-
1572 env(pay(issuer, depositor, asset(100)));
-
1573 env.close();
-
1574
-
1575 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1576 tx[sfFlags] = tfVaultShareNonTransferable;
-
1577 env(tx);
-
1578 env.close();
-
1579
-
1580 {
-
1581 testcase("nontransferable deposits");
-
1582 auto tx1 = vault.deposit(
-
1583 {.depositor = depositor,
-
1584 .id = keylet.key,
-
1585 .amount = asset(40)});
-
1586 env(tx1);
-
1587
-
1588 auto tx2 = vault.deposit(
-
1589 {.depositor = owner, .id = keylet.key, .amount = asset(60)});
-
1590 env(tx2);
-
1591 env.close();
-
1592 }
-
1593
-
1594 auto const vaultAccount = //
-
1595 [&env, key = keylet.key, this]() -> AccountID {
-
1596 auto jvVault = env.rpc("vault_info", strHex(key));
-
1597
-
1598 BEAST_EXPECT(
-
1599 jvVault[jss::result][jss::vault][sfAssetsTotal] == "100");
-
1600 BEAST_EXPECT(
-
1601 jvVault[jss::result][jss::vault][jss::shares]
-
1602 [sfOutstandingAmount] == "100000000");
-
1603
-
1604 // Vault pseudo-account
-
1605 return parseBase58<AccountID>(
-
1606 jvVault[jss::result][jss::vault][jss::Account]
-
1607 .asString())
-
1608 .value();
-
1609 }();
-
1610
-
1611 auto const MptID = makeMptID(1, vaultAccount);
-
1612 Asset shares = MptID;
-
1613
-
1614 {
-
1615 testcase("nontransferable shares cannot be moved");
-
1616 env(pay(owner, depositor, shares(10)), ter{tecNO_AUTH});
-
1617 env(pay(depositor, owner, shares(10)), ter{tecNO_AUTH});
-
1618 }
-
1619
-
1620 {
-
1621 testcase("nontransferable shares can be used to withdraw");
-
1622 auto tx1 = vault.withdraw(
-
1623 {.depositor = depositor,
-
1624 .id = keylet.key,
-
1625 .amount = asset(20)});
-
1626 env(tx1);
-
1627
-
1628 auto tx2 = vault.withdraw(
-
1629 {.depositor = owner, .id = keylet.key, .amount = asset(30)});
-
1630 env(tx2);
-
1631 env.close();
-
1632 }
-
1633
-
1634 {
-
1635 testcase("nontransferable shares balance check");
-
1636 auto jvVault = env.rpc("vault_info", strHex(keylet.key));
-
1637 BEAST_EXPECT(
-
1638 jvVault[jss::result][jss::vault][sfAssetsTotal] == "50");
-
1639 BEAST_EXPECT(
-
1640 jvVault[jss::result][jss::vault][jss::shares]
-
1641 [sfOutstandingAmount] == "50000000");
-
1642 }
-
1643
-
1644 {
-
1645 testcase("nontransferable shares withdraw rest");
-
1646 auto tx1 = vault.withdraw(
-
1647 {.depositor = depositor,
-
1648 .id = keylet.key,
-
1649 .amount = asset(20)});
-
1650 env(tx1);
-
1651
-
1652 auto tx2 = vault.withdraw(
-
1653 {.depositor = owner, .id = keylet.key, .amount = asset(30)});
-
1654 env(tx2);
-
1655 env.close();
-
1656 }
-
1657
-
1658 {
-
1659 testcase("nontransferable shares delete empty vault");
-
1660 auto tx = vault.del({.owner = owner, .id = keylet.key});
-
1661 env(tx);
-
1662 BEAST_EXPECT(!env.le(keylet));
-
1663 }
-
1664 }
+
1553
+
1554 void
+
+ +
1556 {
+
1557 using namespace test::jtx;
+
1558
+
1559 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1560 Account issuer{"issuer"};
+
1561 Account owner{"owner"};
+
1562 Account depositor{"depositor"};
+
1563 env.fund(XRP(1000), issuer, owner, depositor);
+
1564 env.close();
+
1565
+
1566 Vault vault{env};
+
1567 PrettyAsset asset = issuer["IOU"];
+
1568 env.trust(asset(1000), owner);
+
1569 env(pay(issuer, owner, asset(100)));
+
1570 env.trust(asset(1000), depositor);
+
1571 env(pay(issuer, depositor, asset(100)));
+
1572 env.close();
+
1573
+
1574 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1575 tx[sfFlags] = tfVaultShareNonTransferable;
+
1576 env(tx);
+
1577 env.close();
+
1578
+
1579 {
+
1580 testcase("nontransferable deposits");
+
1581 auto tx1 = vault.deposit(
+
1582 {.depositor = depositor,
+
1583 .id = keylet.key,
+
1584 .amount = asset(40)});
+
1585 env(tx1);
+
1586
+
1587 auto tx2 = vault.deposit(
+
1588 {.depositor = owner, .id = keylet.key, .amount = asset(60)});
+
1589 env(tx2);
+
1590 env.close();
+
1591 }
+
1592
+
1593 auto const vaultAccount = //
+
1594 [&env, key = keylet.key, this]() -> AccountID {
+
1595 auto jvVault = env.rpc("vault_info", strHex(key));
+
1596
+
1597 BEAST_EXPECT(
+
1598 jvVault[jss::result][jss::vault][sfAssetsTotal] == "100");
+
1599 BEAST_EXPECT(
+
1600 jvVault[jss::result][jss::vault][jss::shares]
+
1601 [sfOutstandingAmount] == "100000000");
+
1602
+
1603 // Vault pseudo-account
+
1604 return parseBase58<AccountID>(
+
1605 jvVault[jss::result][jss::vault][jss::Account]
+
1606 .asString())
+
1607 .value();
+
1608 }();
+
1609
+
1610 auto const MptID = makeMptID(1, vaultAccount);
+
1611 Asset shares = MptID;
+
1612
+
1613 {
+
1614 testcase("nontransferable shares cannot be moved");
+
1615 env(pay(owner, depositor, shares(10)), ter{tecNO_AUTH});
+
1616 env(pay(depositor, owner, shares(10)), ter{tecNO_AUTH});
+
1617 }
+
1618
+
1619 {
+
1620 testcase("nontransferable shares can be used to withdraw");
+
1621 auto tx1 = vault.withdraw(
+
1622 {.depositor = depositor,
+
1623 .id = keylet.key,
+
1624 .amount = asset(20)});
+
1625 env(tx1);
+
1626
+
1627 auto tx2 = vault.withdraw(
+
1628 {.depositor = owner, .id = keylet.key, .amount = asset(30)});
+
1629 env(tx2);
+
1630 env.close();
+
1631 }
+
1632
+
1633 {
+
1634 testcase("nontransferable shares balance check");
+
1635 auto jvVault = env.rpc("vault_info", strHex(keylet.key));
+
1636 BEAST_EXPECT(
+
1637 jvVault[jss::result][jss::vault][sfAssetsTotal] == "50");
+
1638 BEAST_EXPECT(
+
1639 jvVault[jss::result][jss::vault][jss::shares]
+
1640 [sfOutstandingAmount] == "50000000");
+
1641 }
+
1642
+
1643 {
+
1644 testcase("nontransferable shares withdraw rest");
+
1645 auto tx1 = vault.withdraw(
+
1646 {.depositor = depositor,
+
1647 .id = keylet.key,
+
1648 .amount = asset(20)});
+
1649 env(tx1);
+
1650
+
1651 auto tx2 = vault.withdraw(
+
1652 {.depositor = owner, .id = keylet.key, .amount = asset(30)});
+
1653 env(tx2);
+
1654 env.close();
+
1655 }
+
1656
+
1657 {
+
1658 testcase("nontransferable shares delete empty vault");
+
1659 auto tx = vault.del({.owner = owner, .id = keylet.key});
+
1660 env(tx);
+
1661 BEAST_EXPECT(!env.le(keylet));
+
1662 }
+
1663 }
-
1665
-
1666 void
-
- -
1668 {
-
1669 using namespace test::jtx;
-
1670
-
1671 struct CaseArgs
-
1672 {
-
1673 bool enableClawback = true;
-
1674 bool requireAuth = true;
+
1664
+
1665 void
+
+ +
1667 {
+
1668 using namespace test::jtx;
+
1669
+
1670 struct CaseArgs
+
1671 {
+
1672 bool enableClawback = true;
+
1673 bool requireAuth = true;
+
1674 int initialXRP = 1000;
1675 };
1676
1677 auto testCase = [this](
@@ -1787,7 +1787,7 @@ $(document).ready(function() { init_codefold(0); });
1688 Account issuer{"issuer"};
1689 Account owner{"owner"};
1690 Account depositor{"depositor"};
-
1691 env.fund(XRP(1000), issuer, owner, depositor);
+
1691 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
1692 env.close();
1693 Vault vault{env};
1694
@@ -1967,2762 +1967,2972 @@ $(document).ready(function() { init_codefold(0); });
1868 PrettyAsset const& asset,
1869 Vault& vault,
1870 MPTTester& mptt) {
-
1871 testcase(
-
1872 "MPT 3rd party without MPToken cannot be withdrawal "
-
1873 "destination");
-
1874
-
1875 auto [tx, keylet] =
-
1876 vault.create({.owner = owner, .asset = asset});
-
1877 env(tx);
-
1878 env.close();
-
1879
-
1880 tx = vault.deposit(
-
1881 {.depositor = depositor,
-
1882 .id = keylet.key,
-
1883 .amount = asset(100)});
-
1884 env(tx);
-
1885 env.close();
-
1886
-
1887 {
-
1888 // Set destination to 3rd party without MPToken
-
1889 Account charlie{"charlie"};
-
1890 env.fund(XRP(1000), charlie);
-
1891 env.close();
-
1892
-
1893 tx = vault.withdraw(
-
1894 {.depositor = depositor,
-
1895 .id = keylet.key,
-
1896 .amount = asset(100)});
-
1897 tx[sfDestination] = charlie.human();
-
1898 env(tx, ter(tecNO_AUTH));
-
1899 }
-
1900 },
-
1901 {.requireAuth = false});
+
1871 testcase("MPT depositor without MPToken, auth required");
+
1872
+
1873 auto [tx, keylet] =
+
1874 vault.create({.owner = owner, .asset = asset});
+
1875 env(tx);
+
1876 env.close();
+
1877
+
1878 tx = vault.deposit(
+
1879 {.depositor = depositor,
+
1880 .id = keylet.key,
+
1881 .amount = asset(1000)});
+
1882 env(tx);
+
1883 env.close();
+
1884
+
1885 {
+
1886 // Remove depositor MPToken and it will not be re-created
+
1887 mptt.authorize(
+
1888 {.account = depositor, .flags = tfMPTUnauthorize});
+
1889 env.close();
+
1890
+
1891 auto const mptoken =
+
1892 keylet::mptoken(mptt.issuanceID(), depositor);
+
1893 auto const sleMPT1 = env.le(mptoken);
+
1894 BEAST_EXPECT(sleMPT1 == nullptr);
+
1895
+
1896 tx = vault.withdraw(
+
1897 {.depositor = depositor,
+
1898 .id = keylet.key,
+
1899 .amount = asset(100)});
+
1900 env(tx, ter{tecNO_AUTH});
+
1901 env.close();
1902
-
1903 testCase(
-
1904 [this](
-
1905 Env& env,
-
1906 Account const& issuer,
-
1907 Account const& owner,
-
1908 Account const& depositor,
-
1909 PrettyAsset const& asset,
-
1910 Vault& vault,
-
1911 MPTTester& mptt) {
-
1912 testcase("MPT depositor without MPToken cannot withdraw");
-
1913
-
1914 auto [tx, keylet] =
-
1915 vault.create({.owner = owner, .asset = asset});
-
1916 env(tx);
-
1917 env.close();
-
1918 auto v = env.le(keylet);
-
1919 BEAST_EXPECT(v);
-
1920 MPTID share = (*v)[sfShareMPTID];
-
1921
-
1922 tx = vault.deposit(
-
1923 {.depositor = depositor,
-
1924 .id = keylet.key,
-
1925 .amount = asset(1000)}); // all assets held by depositor
-
1926 env(tx);
-
1927 env.close();
-
1928
-
1929 {
-
1930 // Remove depositor's MPToken and withdraw will fail
-
1931 mptt.authorize(
-
1932 {.account = depositor, .flags = tfMPTUnauthorize});
-
1933 env.close();
-
1934 auto const mptoken =
-
1935 env.le(keylet::mptoken(mptt.issuanceID(), depositor));
-
1936 BEAST_EXPECT(mptoken == nullptr);
-
1937
-
1938 tx = vault.withdraw(
-
1939 {.depositor = depositor,
-
1940 .id = keylet.key,
-
1941 .amount = asset(100)});
-
1942 env(tx, ter(tecNO_AUTH));
-
1943 }
-
1944
-
1945 {
-
1946 // Restore depositor's MPToken and withdraw will succeed
-
1947 mptt.authorize({.account = depositor});
-
1948 env.close();
-
1949
-
1950 tx = vault.withdraw(
-
1951 {.depositor = depositor,
-
1952 .id = keylet.key,
-
1953 .amount = asset(1000)});
-
1954 env(tx);
-
1955 env.close();
-
1956
-
1957 // Withdraw removed shares MPToken
-
1958 auto const mptSle =
-
1959 env.le(keylet::mptoken(share, depositor.id()));
-
1960 BEAST_EXPECT(mptSle == nullptr);
-
1961 }
-
1962 },
-
1963 {.requireAuth = false});
-
1964
-
1965 testCase([this](
-
1966 Env& env,
-
1967 Account const& issuer,
-
1968 Account const& owner,
-
1969 Account const& depositor,
-
1970 PrettyAsset const& asset,
-
1971 Vault& vault,
-
1972 MPTTester& mptt) {
-
1973 testcase("MPT issuance deleted");
-
1974
-
1975 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1976 env(tx);
-
1977 env.close();
-
1978
-
1979 tx = vault.deposit(
-
1980 {.depositor = depositor,
-
1981 .id = keylet.key,
-
1982 .amount = asset(1000)});
-
1983 env(tx);
-
1984 env.close();
-
1985
-
1986 {
-
1987 auto tx = vault.clawback(
-
1988 {.issuer = issuer,
-
1989 .id = keylet.key,
-
1990 .holder = depositor,
-
1991 .amount = asset(0)});
-
1992 env(tx);
-
1993 }
-
1994
-
1995 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
-
1996 env.close();
-
1997
-
1998 {
-
1999 auto [tx, keylet] =
-
2000 vault.create({.owner = depositor, .asset = asset});
-
2001 env(tx, ter{tecOBJECT_NOT_FOUND});
-
2002 }
-
2003
-
2004 {
-
2005 auto tx = vault.deposit(
-
2006 {.depositor = depositor,
-
2007 .id = keylet.key,
-
2008 .amount = asset(10)});
-
2009 env(tx, ter{tecOBJECT_NOT_FOUND});
-
2010 }
-
2011
-
2012 {
-
2013 auto tx = vault.withdraw(
-
2014 {.depositor = depositor,
-
2015 .id = keylet.key,
-
2016 .amount = asset(10)});
-
2017 env(tx, ter{tecOBJECT_NOT_FOUND});
-
2018 }
-
2019
-
2020 {
-
2021 auto tx = vault.clawback(
-
2022 {.issuer = issuer,
-
2023 .id = keylet.key,
-
2024 .holder = depositor,
-
2025 .amount = asset(0)});
-
2026 env(tx, ter{tecOBJECT_NOT_FOUND});
-
2027 }
-
2028
-
2029 env(vault.del({.owner = owner, .id = keylet.key}));
-
2030 });
-
2031
-
2032 testCase([this](
-
2033 Env& env,
-
2034 Account const& issuer,
-
2035 Account const& owner,
-
2036 Account const& depositor,
-
2037 PrettyAsset const& asset,
-
2038 Vault& vault,
-
2039 MPTTester& mptt) {
-
2040 testcase("MPT vault owner can receive shares unless unauthorized");
-
2041
-
2042 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2043 env(tx);
-
2044 env.close();
-
2045
-
2046 tx = vault.deposit(
-
2047 {.depositor = depositor,
-
2048 .id = keylet.key,
-
2049 .amount = asset(1000)});
-
2050 env(tx);
-
2051 env.close();
-
2052
-
2053 auto const issuanceId = [&env](ripple::Keylet keylet) -> MPTID {
-
2054 auto const vault = env.le(keylet);
-
2055 return vault->at(sfShareMPTID);
-
2056 }(keylet);
-
2057 PrettyAsset shares = MPTIssue(issuanceId);
-
2058
-
2059 {
-
2060 // owner has MPToken for shares they did not explicitly create
-
2061 env(pay(depositor, owner, shares(1)));
-
2062 env.close();
-
2063
-
2064 tx = vault.withdraw(
-
2065 {.depositor = owner,
-
2066 .id = keylet.key,
-
2067 .amount = shares(1)});
-
2068 env(tx);
-
2069 env.close();
-
2070
-
2071 // owner's MPToken for vault shares not destroyed by withdraw
-
2072 env(pay(depositor, owner, shares(1)));
-
2073 env.close();
-
2074
-
2075 tx = vault.clawback(
-
2076 {.issuer = issuer,
-
2077 .id = keylet.key,
-
2078 .holder = owner,
-
2079 .amount = asset(0)});
-
2080 env(tx);
-
2081 env.close();
-
2082
-
2083 // owner's MPToken for vault shares not destroyed by clawback
-
2084 env(pay(depositor, owner, shares(1)));
-
2085 env.close();
-
2086
-
2087 // pay back, so we can destroy owner's MPToken now
-
2088 env(pay(owner, depositor, shares(1)));
-
2089 env.close();
-
2090
-
2091 {
-
2092 // explicitly destroy vault owners MPToken with zero balance
-
2093 Json::Value jv;
-
2094 jv[sfAccount] = owner.human();
-
2095 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
-
2096 jv[sfFlags] = tfMPTUnauthorize;
-
2097 jv[sfTransactionType] = jss::MPTokenAuthorize;
-
2098 env(jv);
-
2099 env.close();
-
2100 }
+
1903 auto const sleMPT2 = env.le(mptoken);
+
1904 BEAST_EXPECT(sleMPT2 == nullptr);
+
1905 }
+
1906
+
1907 {
+
1908 // Set destination to 3rd party without MPToken
+
1909 Account charlie{"charlie"};
+
1910 env.fund(XRP(1000), charlie);
+
1911 env.close();
+
1912
+
1913 tx = vault.withdraw(
+
1914 {.depositor = depositor,
+
1915 .id = keylet.key,
+
1916 .amount = asset(100)});
+
1917 tx[sfDestination] = charlie.human();
+
1918 env(tx, ter(tecNO_AUTH));
+
1919 }
+
1920 },
+
1921 {.requireAuth = true});
+
1922
+
1923 testCase(
+
1924 [this](
+
1925 Env& env,
+
1926 Account const& issuer,
+
1927 Account const& owner,
+
1928 Account const& depositor,
+
1929 PrettyAsset const& asset,
+
1930 Vault& vault,
+
1931 MPTTester& mptt) {
+
1932 testcase("MPT depositor without MPToken, no auth required");
+
1933
+
1934 auto [tx, keylet] =
+
1935 vault.create({.owner = owner, .asset = asset});
+
1936 env(tx);
+
1937 env.close();
+
1938 auto v = env.le(keylet);
+
1939 BEAST_EXPECT(v);
+
1940
+
1941 tx = vault.deposit(
+
1942 {.depositor = depositor,
+
1943 .id = keylet.key,
+
1944 .amount = asset(1000)}); // all assets held by depositor
+
1945 env(tx);
+
1946 env.close();
+
1947
+
1948 {
+
1949 // Remove depositor's MPToken and it will be re-created
+
1950 mptt.authorize(
+
1951 {.account = depositor, .flags = tfMPTUnauthorize});
+
1952 env.close();
+
1953
+
1954 auto const mptoken =
+
1955 keylet::mptoken(mptt.issuanceID(), depositor);
+
1956 auto const sleMPT1 = env.le(mptoken);
+
1957 BEAST_EXPECT(sleMPT1 == nullptr);
+
1958
+
1959 tx = vault.withdraw(
+
1960 {.depositor = depositor,
+
1961 .id = keylet.key,
+
1962 .amount = asset(100)});
+
1963 env(tx);
+
1964 env.close();
+
1965
+
1966 auto const sleMPT2 = env.le(mptoken);
+
1967 BEAST_EXPECT(sleMPT2 != nullptr);
+
1968 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
+
1969 }
+
1970
+
1971 {
+
1972 // Remove 3rd party MPToken and it will not be re-created
+
1973 mptt.authorize(
+
1974 {.account = owner, .flags = tfMPTUnauthorize});
+
1975 env.close();
+
1976
+
1977 auto const mptoken =
+
1978 keylet::mptoken(mptt.issuanceID(), owner);
+
1979 auto const sleMPT1 = env.le(mptoken);
+
1980 BEAST_EXPECT(sleMPT1 == nullptr);
+
1981
+
1982 tx = vault.withdraw(
+
1983 {.depositor = depositor,
+
1984 .id = keylet.key,
+
1985 .amount = asset(100)});
+
1986 tx[sfDestination] = owner.human();
+
1987 env(tx, ter(tecNO_AUTH));
+
1988 env.close();
+
1989
+
1990 auto const sleMPT2 = env.le(mptoken);
+
1991 BEAST_EXPECT(sleMPT2 == nullptr);
+
1992 }
+
1993 },
+
1994 {.requireAuth = false});
+
1995
+
1996 auto const [acctReserve, incReserve] = [this]() -> std::pair<int, int> {
+
1997 Env env{*this, testable_amendments()};
+
1998 return {
+
1999 env.current()->fees().accountReserve(0).drops() /
+ +
2001 env.current()->fees().increment.drops() /
+ +
2003 }();
+
2004
+
2005 testCase(
+
2006 [&, this](
+
2007 Env& env,
+
2008 Account const& issuer,
+
2009 Account const& owner,
+
2010 Account const& depositor,
+
2011 PrettyAsset const& asset,
+
2012 Vault& vault,
+
2013 MPTTester& mptt) {
+
2014 testcase("MPT failed reserve to re-create MPToken");
+
2015
+
2016 auto [tx, keylet] =
+
2017 vault.create({.owner = owner, .asset = asset});
+
2018 env(tx);
+
2019 env.close();
+
2020 auto v = env.le(keylet);
+
2021 BEAST_EXPECT(v);
+
2022
+
2023 env(pay(depositor, owner, asset(1000)));
+
2024 env.close();
+
2025
+
2026 tx = vault.deposit(
+
2027 {.depositor = owner,
+
2028 .id = keylet.key,
+
2029 .amount = asset(1000)}); // all assets held by owner
+
2030 env(tx);
+
2031 env.close();
+
2032
+
2033 {
+
2034 // Remove owners's MPToken and it will not be re-created
+
2035 mptt.authorize(
+
2036 {.account = owner, .flags = tfMPTUnauthorize});
+
2037 env.close();
+
2038
+
2039 auto const mptoken =
+
2040 keylet::mptoken(mptt.issuanceID(), owner);
+
2041 auto const sleMPT = env.le(mptoken);
+
2042 BEAST_EXPECT(sleMPT == nullptr);
+
2043
+
2044 // No reserve to create MPToken for asset in VaultWithdraw
+
2045 tx = vault.withdraw(
+
2046 {.depositor = owner,
+
2047 .id = keylet.key,
+
2048 .amount = asset(100)});
+
2049 env(tx, ter{tecINSUFFICIENT_RESERVE});
+
2050 env.close();
+
2051
+
2052 env(pay(depositor, owner, XRP(incReserve)));
+
2053 env.close();
+
2054
+
2055 // Withdraw can now create asset MPToken, tx will succeed
+
2056 env(tx);
+
2057 env.close();
+
2058 }
+
2059 },
+
2060 {.requireAuth = false,
+
2061 .initialXRP = acctReserve + incReserve * 4 - 1});
+
2062
+
2063 testCase([this](
+
2064 Env& env,
+
2065 Account const& issuer,
+
2066 Account const& owner,
+
2067 Account const& depositor,
+
2068 PrettyAsset const& asset,
+
2069 Vault& vault,
+
2070 MPTTester& mptt) {
+
2071 testcase("MPT issuance deleted");
+
2072
+
2073 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2074 env(tx);
+
2075 env.close();
+
2076
+
2077 tx = vault.deposit(
+
2078 {.depositor = depositor,
+
2079 .id = keylet.key,
+
2080 .amount = asset(1000)});
+
2081 env(tx);
+
2082 env.close();
+
2083
+
2084 {
+
2085 auto tx = vault.clawback(
+
2086 {.issuer = issuer,
+
2087 .id = keylet.key,
+
2088 .holder = depositor,
+
2089 .amount = asset(0)});
+
2090 env(tx);
+
2091 }
+
2092
+
2093 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
+
2094 env.close();
+
2095
+
2096 {
+
2097 auto [tx, keylet] =
+
2098 vault.create({.owner = depositor, .asset = asset});
+
2099 env(tx, ter{tecOBJECT_NOT_FOUND});
+
2100 }
2101
-
2102 // owner no longer has MPToken for vault shares
-
2103 tx = pay(depositor, owner, shares(1));
-
2104 env(tx, ter{tecNO_AUTH});
-
2105 env.close();
-
2106
-
2107 // destroy all remaining shares, so we can delete vault
-
2108 tx = vault.clawback(
-
2109 {.issuer = issuer,
-
2110 .id = keylet.key,
-
2111 .holder = depositor,
-
2112 .amount = asset(0)});
-
2113 env(tx);
-
2114 env.close();
-
2115
-
2116 // will soft fail destroying MPToken for vault owner
-
2117 env(vault.del({.owner = owner, .id = keylet.key}));
-
2118 env.close();
-
2119 }
-
2120 });
-
2121
-
2122 testCase(
-
2123 [this](
-
2124 Env& env,
-
2125 Account const& issuer,
-
2126 Account const& owner,
-
2127 Account const& depositor,
-
2128 PrettyAsset const& asset,
-
2129 Vault& vault,
-
2130 MPTTester& mptt) {
-
2131 testcase("MPT clawback disabled");
-
2132
-
2133 auto [tx, keylet] =
-
2134 vault.create({.owner = owner, .asset = asset});
-
2135 env(tx);
-
2136 env.close();
-
2137
-
2138 tx = vault.deposit(
-
2139 {.depositor = depositor,
-
2140 .id = keylet.key,
-
2141 .amount = asset(1000)});
-
2142 env(tx);
-
2143 env.close();
-
2144
-
2145 {
-
2146 auto tx = vault.clawback(
-
2147 {.issuer = issuer,
-
2148 .id = keylet.key,
-
2149 .holder = depositor,
-
2150 .amount = asset(0)});
-
2151 env(tx, ter{tecNO_PERMISSION});
-
2152 }
-
2153 },
-
2154 {.enableClawback = false});
-
2155
-
2156 testCase([this](
-
2157 Env& env,
-
2158 Account const& issuer,
-
2159 Account const& owner,
-
2160 Account const& depositor,
-
2161 Asset const& asset,
-
2162 Vault& vault,
-
2163 MPTTester& mptt) {
-
2164 testcase("MPT un-authorization");
-
2165 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2166 env(tx);
-
2167 env.close();
-
2168 tx = vault.deposit(
-
2169 {.depositor = depositor,
-
2170 .id = keylet.key,
-
2171 .amount = asset(1000)});
-
2172 env(tx);
-
2173 env.close();
-
2174
-
2175 mptt.authorize(
-
2176 {.account = issuer,
-
2177 .holder = depositor,
-
2178 .flags = tfMPTUnauthorize});
-
2179 env.close();
+
2102 {
+
2103 auto tx = vault.deposit(
+
2104 {.depositor = depositor,
+
2105 .id = keylet.key,
+
2106 .amount = asset(10)});
+
2107 env(tx, ter{tecOBJECT_NOT_FOUND});
+
2108 }
+
2109
+
2110 {
+
2111 auto tx = vault.withdraw(
+
2112 {.depositor = depositor,
+
2113 .id = keylet.key,
+
2114 .amount = asset(10)});
+
2115 env(tx, ter{tecOBJECT_NOT_FOUND});
+
2116 }
+
2117
+
2118 {
+
2119 auto tx = vault.clawback(
+
2120 {.issuer = issuer,
+
2121 .id = keylet.key,
+
2122 .holder = depositor,
+
2123 .amount = asset(0)});
+
2124 env(tx, ter{tecOBJECT_NOT_FOUND});
+
2125 }
+
2126
+
2127 env(vault.del({.owner = owner, .id = keylet.key}));
+
2128 });
+
2129
+
2130 testCase([this](
+
2131 Env& env,
+
2132 Account const& issuer,
+
2133 Account const& owner,
+
2134 Account const& depositor,
+
2135 PrettyAsset const& asset,
+
2136 Vault& vault,
+
2137 MPTTester& mptt) {
+
2138 testcase("MPT vault owner can receive shares unless unauthorized");
+
2139
+
2140 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2141 env(tx);
+
2142 env.close();
+
2143
+
2144 tx = vault.deposit(
+
2145 {.depositor = depositor,
+
2146 .id = keylet.key,
+
2147 .amount = asset(1000)});
+
2148 env(tx);
+
2149 env.close();
+
2150
+
2151 auto const issuanceId = [&env](ripple::Keylet keylet) -> MPTID {
+
2152 auto const vault = env.le(keylet);
+
2153 return vault->at(sfShareMPTID);
+
2154 }(keylet);
+
2155 PrettyAsset shares = MPTIssue(issuanceId);
+
2156
+
2157 {
+
2158 // owner has MPToken for shares they did not explicitly create
+
2159 env(pay(depositor, owner, shares(1)));
+
2160 env.close();
+
2161
+
2162 tx = vault.withdraw(
+
2163 {.depositor = owner,
+
2164 .id = keylet.key,
+
2165 .amount = shares(1)});
+
2166 env(tx);
+
2167 env.close();
+
2168
+
2169 // owner's MPToken for vault shares not destroyed by withdraw
+
2170 env(pay(depositor, owner, shares(1)));
+
2171 env.close();
+
2172
+
2173 tx = vault.clawback(
+
2174 {.issuer = issuer,
+
2175 .id = keylet.key,
+
2176 .holder = owner,
+
2177 .amount = asset(0)});
+
2178 env(tx);
+
2179 env.close();
2180
-
2181 {
-
2182 auto tx = vault.withdraw(
-
2183 {.depositor = depositor,
-
2184 .id = keylet.key,
-
2185 .amount = asset(100)});
-
2186 env(tx, ter(tecNO_AUTH));
-
2187
-
2188 // Withdrawal to other (authorized) accounts works
-
2189 tx[sfDestination] = issuer.human();
-
2190 env(tx);
-
2191 env.close();
-
2192
-
2193 tx[sfDestination] = owner.human();
-
2194 env(tx);
-
2195 env.close();
-
2196 }
-
2197
-
2198 {
-
2199 // Cannot deposit some more
-
2200 auto tx = vault.deposit(
-
2201 {.depositor = depositor,
-
2202 .id = keylet.key,
-
2203 .amount = asset(100)});
-
2204 env(tx, ter(tecNO_AUTH));
-
2205 }
-
2206
-
2207 // Clawback works
-
2208 tx = vault.clawback(
-
2209 {.issuer = issuer,
-
2210 .id = keylet.key,
-
2211 .holder = depositor,
-
2212 .amount = asset(800)});
-
2213 env(tx);
-
2214 env.close();
-
2215
-
2216 env(vault.del({.owner = owner, .id = keylet.key}));
-
2217 });
-
2218
-
2219 testCase([this](
-
2220 Env& env,
-
2221 Account const& issuer,
-
2222 Account const& owner,
-
2223 Account const& depositor,
-
2224 Asset const& asset,
-
2225 Vault& vault,
-
2226 MPTTester& mptt) {
-
2227 testcase("MPT lock of vault pseudo-account");
-
2228 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2229 env(tx);
-
2230 env.close();
-
2231
-
2232 auto const vaultAccount =
-
2233 [&env, keylet = keylet, this]() -> AccountID {
-
2234 auto const vault = env.le(keylet);
-
2235 BEAST_EXPECT(vault != nullptr);
-
2236 return vault->at(sfAccount);
-
2237 }();
-
2238
-
2239 tx = vault.deposit(
-
2240 {.depositor = depositor,
-
2241 .id = keylet.key,
-
2242 .amount = asset(100)});
-
2243 env(tx);
-
2244 env.close();
-
2245
-
2246 tx = [&]() {
-
2247 Json::Value jv;
-
2248 jv[jss::Account] = issuer.human();
-
2249 jv[sfMPTokenIssuanceID] =
-
2250 to_string(asset.get<MPTIssue>().getMptID());
-
2251 jv[jss::Holder] = toBase58(vaultAccount);
-
2252 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
-
2253 jv[jss::Flags] = tfMPTLock;
-
2254 return jv;
-
2255 }();
-
2256 env(tx);
-
2257 env.close();
-
2258
-
2259 tx = vault.deposit(
-
2260 {.depositor = depositor,
-
2261 .id = keylet.key,
-
2262 .amount = asset(100)});
-
2263 env(tx, ter(tecLOCKED));
-
2264
-
2265 tx = vault.withdraw(
-
2266 {.depositor = depositor,
-
2267 .id = keylet.key,
-
2268 .amount = asset(100)});
-
2269 env(tx, ter(tecLOCKED));
-
2270
-
2271 // Clawback works, even when locked
-
2272 tx = vault.clawback(
-
2273 {.issuer = issuer,
-
2274 .id = keylet.key,
+
2181 // owner's MPToken for vault shares not destroyed by clawback
+
2182 env(pay(depositor, owner, shares(1)));
+
2183 env.close();
+
2184
+
2185 // pay back, so we can destroy owner's MPToken now
+
2186 env(pay(owner, depositor, shares(1)));
+
2187 env.close();
+
2188
+
2189 {
+
2190 // explicitly destroy vault owners MPToken with zero balance
+
2191 Json::Value jv;
+
2192 jv[sfAccount] = owner.human();
+
2193 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
+
2194 jv[sfFlags] = tfMPTUnauthorize;
+
2195 jv[sfTransactionType] = jss::MPTokenAuthorize;
+
2196 env(jv);
+
2197 env.close();
+
2198 }
+
2199
+
2200 // owner no longer has MPToken for vault shares
+
2201 tx = pay(depositor, owner, shares(1));
+
2202 env(tx, ter{tecNO_AUTH});
+
2203 env.close();
+
2204
+
2205 // destroy all remaining shares, so we can delete vault
+
2206 tx = vault.clawback(
+
2207 {.issuer = issuer,
+
2208 .id = keylet.key,
+
2209 .holder = depositor,
+
2210 .amount = asset(0)});
+
2211 env(tx);
+
2212 env.close();
+
2213
+
2214 // will soft fail destroying MPToken for vault owner
+
2215 env(vault.del({.owner = owner, .id = keylet.key}));
+
2216 env.close();
+
2217 }
+
2218 });
+
2219
+
2220 testCase(
+
2221 [this](
+
2222 Env& env,
+
2223 Account const& issuer,
+
2224 Account const& owner,
+
2225 Account const& depositor,
+
2226 PrettyAsset const& asset,
+
2227 Vault& vault,
+
2228 MPTTester& mptt) {
+
2229 testcase("MPT clawback disabled");
+
2230
+
2231 auto [tx, keylet] =
+
2232 vault.create({.owner = owner, .asset = asset});
+
2233 env(tx);
+
2234 env.close();
+
2235
+
2236 tx = vault.deposit(
+
2237 {.depositor = depositor,
+
2238 .id = keylet.key,
+
2239 .amount = asset(1000)});
+
2240 env(tx);
+
2241 env.close();
+
2242
+
2243 {
+
2244 auto tx = vault.clawback(
+
2245 {.issuer = issuer,
+
2246 .id = keylet.key,
+
2247 .holder = depositor,
+
2248 .amount = asset(0)});
+
2249 env(tx, ter{tecNO_PERMISSION});
+
2250 }
+
2251 },
+
2252 {.enableClawback = false});
+
2253
+
2254 testCase([this](
+
2255 Env& env,
+
2256 Account const& issuer,
+
2257 Account const& owner,
+
2258 Account const& depositor,
+
2259 Asset const& asset,
+
2260 Vault& vault,
+
2261 MPTTester& mptt) {
+
2262 testcase("MPT un-authorization");
+
2263 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2264 env(tx);
+
2265 env.close();
+
2266 tx = vault.deposit(
+
2267 {.depositor = depositor,
+
2268 .id = keylet.key,
+
2269 .amount = asset(1000)});
+
2270 env(tx);
+
2271 env.close();
+
2272
+
2273 mptt.authorize(
+
2274 {.account = issuer,
2275 .holder = depositor,
-
2276 .amount = asset(100)});
-
2277 env(tx);
+
2276 .flags = tfMPTUnauthorize});
+
2277 env.close();
2278
-
2279 // Can delete an empty vault even when asset is locked.
-
2280 tx = vault.del({.owner = owner, .id = keylet.key});
-
2281 env(tx);
-
2282 });
-
2283
-
2284 {
-
2285 testcase("MPT shares to a vault");
-
2286
-
2287 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
2288 Account owner{"owner"};
-
2289 Account issuer{"issuer"};
-
2290 env.fund(XRP(1000000), owner, issuer);
-
2291 env.close();
-
2292 Vault vault{env};
-
2293
-
2294 MPTTester mptt{env, issuer, mptInitNoFund};
-
2295 mptt.create(
- - -
2298 mptt.authorize({.account = owner});
-
2299 mptt.authorize({.account = issuer, .holder = owner});
-
2300 PrettyAsset asset = mptt.issuanceID();
-
2301 env(pay(issuer, owner, asset(100)));
-
2302 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
-
2303 env(tx1);
-
2304 env.close();
-
2305
-
2306 auto const shares = [&env, keylet = k1, this]() -> Asset {
-
2307 auto const vault = env.le(keylet);
-
2308 BEAST_EXPECT(vault != nullptr);
-
2309 return MPTIssue(vault->at(sfShareMPTID));
-
2310 }();
-
2311
-
2312 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
-
2313 env(tx2, ter{tecWRONG_ASSET});
-
2314 env.close();
-
2315 }
-
2316 }
-
-
2317
-
2318 void
-
- -
2320 {
-
2321 using namespace test::jtx;
-
2322
-
2323 auto testCase =
-
2324 [&,
-
2325 this](std::function<void(
-
2326 Env & env,
-
2327 Account const& owner,
-
2328 Account const& issuer,
-
2329 Account const& charlie,
-
2330 std::function<Account(ripple::Keylet)> vaultAccount,
-
2331 Vault& vault,
-
2332 PrettyAsset const& asset,
-
2333 std::function<MPTID(ripple::Keylet)> issuanceId)> test) {
-
2334 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
2335 Account const owner{"owner"};
-
2336 Account const issuer{"issuer"};
-
2337 Account const charlie{"charlie"};
-
2338 Vault vault{env};
-
2339 env.fund(XRP(1000), issuer, owner, charlie);
-
2340 env(fset(issuer, asfAllowTrustLineClawback));
-
2341 env.close();
-
2342
-
2343 PrettyAsset const asset = issuer["IOU"];
-
2344 env.trust(asset(1000), owner);
-
2345 env.trust(asset(1000), charlie);
-
2346 env(pay(issuer, owner, asset(200)));
-
2347 env(rate(issuer, 1.25));
-
2348 env.close();
-
2349
-
2350 auto const vaultAccount =
-
2351 [&env](ripple::Keylet keylet) -> Account {
-
2352 return Account("vault", env.le(keylet)->at(sfAccount));
-
2353 };
-
2354 auto const issuanceId = [&env](ripple::Keylet keylet) -> MPTID {
-
2355 return env.le(keylet)->at(sfShareMPTID);
-
2356 };
-
2357
-
2358 test(
-
2359 env,
-
2360 owner,
-
2361 issuer,
-
2362 charlie,
-
2363 vaultAccount,
-
2364 vault,
-
2365 asset,
-
2366 issuanceId);
-
2367 };
+
2279 {
+
2280 auto tx = vault.withdraw(
+
2281 {.depositor = depositor,
+
2282 .id = keylet.key,
+
2283 .amount = asset(100)});
+
2284 env(tx, ter(tecNO_AUTH));
+
2285
+
2286 // Withdrawal to other (authorized) accounts works
+
2287 tx[sfDestination] = issuer.human();
+
2288 env(tx);
+
2289 env.close();
+
2290
+
2291 tx[sfDestination] = owner.human();
+
2292 env(tx);
+
2293 env.close();
+
2294 }
+
2295
+
2296 {
+
2297 // Cannot deposit some more
+
2298 auto tx = vault.deposit(
+
2299 {.depositor = depositor,
+
2300 .id = keylet.key,
+
2301 .amount = asset(100)});
+
2302 env(tx, ter(tecNO_AUTH));
+
2303 }
+
2304
+
2305 // Clawback works
+
2306 tx = vault.clawback(
+
2307 {.issuer = issuer,
+
2308 .id = keylet.key,
+
2309 .holder = depositor,
+
2310 .amount = asset(800)});
+
2311 env(tx);
+
2312 env.close();
+
2313
+
2314 env(vault.del({.owner = owner, .id = keylet.key}));
+
2315 });
+
2316
+
2317 testCase([this](
+
2318 Env& env,
+
2319 Account const& issuer,
+
2320 Account const& owner,
+
2321 Account const& depositor,
+
2322 Asset const& asset,
+
2323 Vault& vault,
+
2324 MPTTester& mptt) {
+
2325 testcase("MPT lock of vault pseudo-account");
+
2326 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2327 env(tx);
+
2328 env.close();
+
2329
+
2330 auto const vaultAccount =
+
2331 [&env, keylet = keylet, this]() -> AccountID {
+
2332 auto const vault = env.le(keylet);
+
2333 BEAST_EXPECT(vault != nullptr);
+
2334 return vault->at(sfAccount);
+
2335 }();
+
2336
+
2337 tx = vault.deposit(
+
2338 {.depositor = depositor,
+
2339 .id = keylet.key,
+
2340 .amount = asset(100)});
+
2341 env(tx);
+
2342 env.close();
+
2343
+
2344 tx = [&]() {
+
2345 Json::Value jv;
+
2346 jv[jss::Account] = issuer.human();
+
2347 jv[sfMPTokenIssuanceID] =
+
2348 to_string(asset.get<MPTIssue>().getMptID());
+
2349 jv[jss::Holder] = toBase58(vaultAccount);
+
2350 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
+
2351 jv[jss::Flags] = tfMPTLock;
+
2352 return jv;
+
2353 }();
+
2354 env(tx);
+
2355 env.close();
+
2356
+
2357 tx = vault.deposit(
+
2358 {.depositor = depositor,
+
2359 .id = keylet.key,
+
2360 .amount = asset(100)});
+
2361 env(tx, ter(tecLOCKED));
+
2362
+
2363 tx = vault.withdraw(
+
2364 {.depositor = depositor,
+
2365 .id = keylet.key,
+
2366 .amount = asset(100)});
+
2367 env(tx, ter(tecLOCKED));
2368
-
2369 testCase([&, this](
-
2370 Env& env,
-
2371 Account const& owner,
-
2372 Account const& issuer,
-
2373 Account const&,
-
2374 auto vaultAccount,
-
2375 Vault& vault,
-
2376 PrettyAsset const& asset,
-
2377 auto&&...) {
-
2378 testcase("IOU cannot use different asset");
-
2379 PrettyAsset const foo = issuer["FOO"];
-
2380
-
2381 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2382 env(tx);
-
2383 env.close();
+
2369 // Clawback works, even when locked
+
2370 tx = vault.clawback(
+
2371 {.issuer = issuer,
+
2372 .id = keylet.key,
+
2373 .holder = depositor,
+
2374 .amount = asset(100)});
+
2375 env(tx);
+
2376
+
2377 // Can delete an empty vault even when asset is locked.
+
2378 tx = vault.del({.owner = owner, .id = keylet.key});
+
2379 env(tx);
+
2380 });
+
2381
+
2382 {
+
2383 testcase("MPT shares to a vault");
2384
-
2385 {
-
2386 // Cannot create new trustline to a vault
-
2387 auto tx = [&, account = vaultAccount(keylet)]() {
-
2388 Json::Value jv;
-
2389 jv[jss::Account] = issuer.human();
-
2390 {
-
2391 auto& ja = jv[jss::LimitAmount] =
-
2392 foo(0).value().getJson(JsonOptions::none);
-
2393 ja[jss::issuer] = toBase58(account);
-
2394 }
-
2395 jv[jss::TransactionType] = jss::TrustSet;
-
2396 jv[jss::Flags] = tfSetFreeze;
-
2397 return jv;
-
2398 }();
-
2399 env(tx, ter{tecNO_PERMISSION});
-
2400 env.close();
-
2401 }
-
2402
-
2403 {
-
2404 auto tx = vault.deposit(
-
2405 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
-
2406 env(tx, ter{tecWRONG_ASSET});
-
2407 env.close();
-
2408 }
+
2385 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
2386 Account owner{"owner"};
+
2387 Account issuer{"issuer"};
+
2388 env.fund(XRP(1000000), owner, issuer);
+
2389 env.close();
+
2390 Vault vault{env};
+
2391
+
2392 MPTTester mptt{env, issuer, mptInitNoFund};
+
2393 mptt.create(
+ + +
2396 mptt.authorize({.account = owner});
+
2397 mptt.authorize({.account = issuer, .holder = owner});
+
2398 PrettyAsset asset = mptt.issuanceID();
+
2399 env(pay(issuer, owner, asset(100)));
+
2400 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
+
2401 env(tx1);
+
2402 env.close();
+
2403
+
2404 auto const shares = [&env, keylet = k1, this]() -> Asset {
+
2405 auto const vault = env.le(keylet);
+
2406 BEAST_EXPECT(vault != nullptr);
+
2407 return MPTIssue(vault->at(sfShareMPTID));
+
2408 }();
2409
-
2410 {
-
2411 auto tx = vault.withdraw(
-
2412 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
-
2413 env(tx, ter{tecWRONG_ASSET});
-
2414 env.close();
-
2415 }
-
2416
-
2417 env(vault.del({.owner = owner, .id = keylet.key}));
-
2418 env.close();
-
2419 });
+
2410 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
+
2411 env(tx2, ter{tecWRONG_ASSET});
+
2412 env.close();
+
2413 }
+
2414 }
+
+
2415
+
2416 void
+
+ +
2418 {
+
2419 using namespace test::jtx;
2420
-
2421 testCase([&, this](
-
2422 Env& env,
-
2423 Account const& owner,
-
2424 Account const& issuer,
-
2425 Account const& charlie,
-
2426 auto vaultAccount,
-
2427 Vault& vault,
-
2428 PrettyAsset const& asset,
-
2429 auto issuanceId) {
-
2430 testcase("IOU frozen trust line to vault account");
-
2431
-
2432 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2433 env(tx);
-
2434 env.close();
-
2435
-
2436 env(vault.deposit(
-
2437 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2438 env.close();
-
2439
-
2440 Asset const share = Asset(issuanceId(keylet));
-
2441
-
2442 // Freeze the trustline to the vault
-
2443 auto trustSet = [&, account = vaultAccount(keylet)]() {
-
2444 Json::Value jv;
-
2445 jv[jss::Account] = issuer.human();
-
2446 {
-
2447 auto& ja = jv[jss::LimitAmount] =
-
2448 asset(0).value().getJson(JsonOptions::none);
-
2449 ja[jss::issuer] = toBase58(account);
-
2450 }
-
2451 jv[jss::TransactionType] = jss::TrustSet;
-
2452 jv[jss::Flags] = tfSetFreeze;
-
2453 return jv;
-
2454 }();
-
2455 env(trustSet);
-
2456 env.close();
-
2457
-
2458 {
-
2459 // Note, the "frozen" state of the trust line to vault account
-
2460 // is reported as "locked" state of the vault shares, because
-
2461 // this state is attached to shares by means of the transitive
-
2462 // isFrozen.
-
2463 auto tx = vault.deposit(
-
2464 {.depositor = owner,
-
2465 .id = keylet.key,
-
2466 .amount = asset(80)});
-
2467 env(tx, ter{tecLOCKED});
-
2468 }
-
2469
-
2470 {
-
2471 auto tx = vault.withdraw(
-
2472 {.depositor = owner,
-
2473 .id = keylet.key,
-
2474 .amount = asset(100)});
-
2475 env(tx, ter{tecLOCKED});
-
2476
-
2477 // also when trying to withdraw to a 3rd party
-
2478 tx[sfDestination] = charlie.human();
-
2479 env(tx, ter{tecLOCKED});
-
2480 env.close();
-
2481 }
-
2482
-
2483 {
-
2484 // Clawback works, even when locked
-
2485 auto tx = vault.clawback(
-
2486 {.issuer = issuer,
-
2487 .id = keylet.key,
-
2488 .holder = owner,
-
2489 .amount = asset(50)});
-
2490 env(tx);
-
2491 env.close();
-
2492 }
-
2493
-
2494 // Clear the frozen state
-
2495 trustSet[jss::Flags] = tfClearFreeze;
-
2496 env(trustSet);
-
2497 env.close();
-
2498
-
2499 env(vault.withdraw(
-
2500 {.depositor = owner,
-
2501 .id = keylet.key,
-
2502 .amount = share(50'000'000)}));
-
2503
-
2504 env(vault.del({.owner = owner, .id = keylet.key}));
-
2505 env.close();
-
2506 });
+
2421 struct CaseArgs
+
2422 {
+
2423 int initialXRP = 1000;
+
2424 double transferRate = 1.0;
+
2425 };
+
2426
+
2427 auto testCase =
+
2428 [&, this](
+
2429 std::function<void(
+
2430 Env & env,
+
2431 Account const& owner,
+
2432 Account const& issuer,
+
2433 Account const& charlie,
+
2434 std::function<Account(ripple::Keylet)> vaultAccount,
+
2435 Vault& vault,
+
2436 PrettyAsset const& asset,
+
2437 std::function<MPTID(ripple::Keylet)> issuanceId)> test,
+
2438 CaseArgs args = {}) {
+
2439 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
2440 Account const owner{"owner"};
+
2441 Account const issuer{"issuer"};
+
2442 Account const charlie{"charlie"};
+
2443 Vault vault{env};
+
2444 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
+
2445 env(fset(issuer, asfAllowTrustLineClawback));
+
2446 env.close();
+
2447
+
2448 PrettyAsset const asset = issuer["IOU"];
+
2449 env.trust(asset(1000), owner);
+
2450 env.trust(asset(1000), charlie);
+
2451 env(pay(issuer, owner, asset(200)));
+
2452 env(rate(issuer, args.transferRate));
+
2453 env.close();
+
2454
+
2455 auto const vaultAccount =
+
2456 [&env](ripple::Keylet keylet) -> Account {
+
2457 return Account("vault", env.le(keylet)->at(sfAccount));
+
2458 };
+
2459 auto const issuanceId = [&env](ripple::Keylet keylet) -> MPTID {
+
2460 return env.le(keylet)->at(sfShareMPTID);
+
2461 };
+
2462
+
2463 test(
+
2464 env,
+
2465 owner,
+
2466 issuer,
+
2467 charlie,
+
2468 vaultAccount,
+
2469 vault,
+
2470 asset,
+
2471 issuanceId);
+
2472 };
+
2473
+
2474 testCase([&, this](
+
2475 Env& env,
+
2476 Account const& owner,
+
2477 Account const& issuer,
+
2478 Account const&,
+
2479 auto vaultAccount,
+
2480 Vault& vault,
+
2481 PrettyAsset const& asset,
+
2482 auto&&...) {
+
2483 testcase("IOU cannot use different asset");
+
2484 PrettyAsset const foo = issuer["FOO"];
+
2485
+
2486 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2487 env(tx);
+
2488 env.close();
+
2489
+
2490 {
+
2491 // Cannot create new trustline to a vault
+
2492 auto tx = [&, account = vaultAccount(keylet)]() {
+
2493 Json::Value jv;
+
2494 jv[jss::Account] = issuer.human();
+
2495 {
+
2496 auto& ja = jv[jss::LimitAmount] =
+
2497 foo(0).value().getJson(JsonOptions::none);
+
2498 ja[jss::issuer] = toBase58(account);
+
2499 }
+
2500 jv[jss::TransactionType] = jss::TrustSet;
+
2501 jv[jss::Flags] = tfSetFreeze;
+
2502 return jv;
+
2503 }();
+
2504 env(tx, ter{tecNO_PERMISSION});
+
2505 env.close();
+
2506 }
2507
-
2508 testCase([&, this](
-
2509 Env& env,
-
2510 Account const& owner,
-
2511 Account const& issuer,
-
2512 Account const& charlie,
-
2513 auto vaultAccount,
-
2514 Vault& vault,
-
2515 PrettyAsset const& asset,
-
2516 auto issuanceId) {
-
2517 testcase("IOU transfer fees not applied");
-
2518
-
2519 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2520 env(tx);
-
2521 env.close();
-
2522
-
2523 env(vault.deposit(
-
2524 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2525 env.close();
-
2526
-
2527 auto const issue = asset.raw().get<Issue>();
-
2528 Asset const share = Asset(issuanceId(keylet));
-
2529
-
2530 // transfer fees ignored on deposit
-
2531 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
-
2532 BEAST_EXPECT(
-
2533 env.balance(vaultAccount(keylet), issue) == asset(100));
-
2534
-
2535 {
-
2536 auto tx = vault.clawback(
-
2537 {.issuer = issuer,
-
2538 .id = keylet.key,
-
2539 .holder = owner,
-
2540 .amount = asset(50)});
-
2541 env(tx);
-
2542 env.close();
-
2543 }
+
2508 {
+
2509 auto tx = vault.deposit(
+
2510 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
+
2511 env(tx, ter{tecWRONG_ASSET});
+
2512 env.close();
+
2513 }
+
2514
+
2515 {
+
2516 auto tx = vault.withdraw(
+
2517 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
+
2518 env(tx, ter{tecWRONG_ASSET});
+
2519 env.close();
+
2520 }
+
2521
+
2522 env(vault.del({.owner = owner, .id = keylet.key}));
+
2523 env.close();
+
2524 });
+
2525
+
2526 testCase([&, this](
+
2527 Env& env,
+
2528 Account const& owner,
+
2529 Account const& issuer,
+
2530 Account const& charlie,
+
2531 auto vaultAccount,
+
2532 Vault& vault,
+
2533 PrettyAsset const& asset,
+
2534 auto issuanceId) {
+
2535 testcase("IOU frozen trust line to vault account");
+
2536
+
2537 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2538 env(tx);
+
2539 env.close();
+
2540
+
2541 env(vault.deposit(
+
2542 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2543 env.close();
2544
-
2545 // transfer fees ignored on clawback
-
2546 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
-
2547 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(50));
-
2548
-
2549 env(vault.withdraw(
-
2550 {.depositor = owner,
-
2551 .id = keylet.key,
-
2552 .amount = share(20'000'000)}));
-
2553
-
2554 // transfer fees ignored on withdraw
-
2555 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
-
2556 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(30));
-
2557
-
2558 {
-
2559 auto tx = vault.withdraw(
-
2560 {.depositor = owner,
-
2561 .id = keylet.key,
-
2562 .amount = share(30'000'000)});
-
2563 tx[sfDestination] = charlie.human();
-
2564 env(tx);
-
2565 }
-
2566
-
2567 // transfer fees ignored on withdraw to 3rd party
-
2568 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
-
2569 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
-
2570 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(0));
-
2571
-
2572 env(vault.del({.owner = owner, .id = keylet.key}));
-
2573 env.close();
-
2574 });
-
2575
-
2576 testCase([&, this](
-
2577 Env& env,
-
2578 Account const& owner,
-
2579 Account const& issuer,
-
2580 Account const& charlie,
-
2581 auto,
-
2582 Vault& vault,
-
2583 PrettyAsset const& asset,
-
2584 auto&&...) {
-
2585 testcase("IOU frozen trust line to depositor");
-
2586
-
2587 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2588 env(tx);
-
2589 env.close();
-
2590
-
2591 env(vault.deposit(
-
2592 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2593 env.close();
-
2594
-
2595 // Withdraw to 3rd party works
-
2596 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
-
2597 auto tx = vault.withdraw(
-
2598 {.depositor = owner,
-
2599 .id = keylet.key,
-
2600 .amount = asset(10)});
-
2601 tx[sfDestination] = charlie.human();
-
2602 return tx;
-
2603 }(keylet);
-
2604 env(withdrawToCharlie);
-
2605
-
2606 // Freeze the owner
-
2607 env(trust(issuer, asset(0), owner, tfSetFreeze));
-
2608 env.close();
-
2609
-
2610 // Cannot withdraw
-
2611 auto const withdraw = vault.withdraw(
-
2612 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2613 env(withdraw, ter{tecFROZEN});
-
2614
-
2615 // Cannot withdraw to 3rd party
-
2616 env(withdrawToCharlie, ter{tecLOCKED});
-
2617 env.close();
-
2618
-
2619 {
-
2620 // Cannot deposit some more
-
2621 auto tx = vault.deposit(
-
2622 {.depositor = owner,
-
2623 .id = keylet.key,
-
2624 .amount = asset(10)});
-
2625 env(tx, ter{tecFROZEN});
-
2626 }
-
2627
-
2628 {
-
2629 // Clawback still works
-
2630 auto tx = vault.clawback(
-
2631 {.issuer = issuer,
-
2632 .id = keylet.key,
-
2633 .holder = owner,
-
2634 .amount = asset(0)});
-
2635 env(tx);
-
2636 env.close();
-
2637 }
+
2545 Asset const share = Asset(issuanceId(keylet));
+
2546
+
2547 // Freeze the trustline to the vault
+
2548 auto trustSet = [&, account = vaultAccount(keylet)]() {
+
2549 Json::Value jv;
+
2550 jv[jss::Account] = issuer.human();
+
2551 {
+
2552 auto& ja = jv[jss::LimitAmount] =
+
2553 asset(0).value().getJson(JsonOptions::none);
+
2554 ja[jss::issuer] = toBase58(account);
+
2555 }
+
2556 jv[jss::TransactionType] = jss::TrustSet;
+
2557 jv[jss::Flags] = tfSetFreeze;
+
2558 return jv;
+
2559 }();
+
2560 env(trustSet);
+
2561 env.close();
+
2562
+
2563 {
+
2564 // Note, the "frozen" state of the trust line to vault account
+
2565 // is reported as "locked" state of the vault shares, because
+
2566 // this state is attached to shares by means of the transitive
+
2567 // isFrozen.
+
2568 auto tx = vault.deposit(
+
2569 {.depositor = owner,
+
2570 .id = keylet.key,
+
2571 .amount = asset(80)});
+
2572 env(tx, ter{tecLOCKED});
+
2573 }
+
2574
+
2575 {
+
2576 auto tx = vault.withdraw(
+
2577 {.depositor = owner,
+
2578 .id = keylet.key,
+
2579 .amount = asset(100)});
+
2580 env(tx, ter{tecLOCKED});
+
2581
+
2582 // also when trying to withdraw to a 3rd party
+
2583 tx[sfDestination] = charlie.human();
+
2584 env(tx, ter{tecLOCKED});
+
2585 env.close();
+
2586 }
+
2587
+
2588 {
+
2589 // Clawback works, even when locked
+
2590 auto tx = vault.clawback(
+
2591 {.issuer = issuer,
+
2592 .id = keylet.key,
+
2593 .holder = owner,
+
2594 .amount = asset(50)});
+
2595 env(tx);
+
2596 env.close();
+
2597 }
+
2598
+
2599 // Clear the frozen state
+
2600 trustSet[jss::Flags] = tfClearFreeze;
+
2601 env(trustSet);
+
2602 env.close();
+
2603
+
2604 env(vault.withdraw(
+
2605 {.depositor = owner,
+
2606 .id = keylet.key,
+
2607 .amount = share(50'000'000)}));
+
2608
+
2609 env(vault.del({.owner = owner, .id = keylet.key}));
+
2610 env.close();
+
2611 });
+
2612
+
2613 testCase(
+
2614 [&, this](
+
2615 Env& env,
+
2616 Account const& owner,
+
2617 Account const& issuer,
+
2618 Account const& charlie,
+
2619 auto vaultAccount,
+
2620 Vault& vault,
+
2621 PrettyAsset const& asset,
+
2622 auto issuanceId) {
+
2623 testcase("IOU transfer fees not applied");
+
2624
+
2625 auto [tx, keylet] =
+
2626 vault.create({.owner = owner, .asset = asset});
+
2627 env(tx);
+
2628 env.close();
+
2629
+
2630 env(vault.deposit(
+
2631 {.depositor = owner,
+
2632 .id = keylet.key,
+
2633 .amount = asset(100)}));
+
2634 env.close();
+
2635
+
2636 auto const issue = asset.raw().get<Issue>();
+
2637 Asset const share = Asset(issuanceId(keylet));
2638
-
2639 env(vault.del({.owner = owner, .id = keylet.key}));
-
2640 env.close();
-
2641 });
-
2642
-
2643 testCase([&, this](
-
2644 Env& env,
-
2645 Account const& owner,
-
2646 Account const& issuer,
-
2647 Account const& charlie,
-
2648 auto,
-
2649 Vault& vault,
-
2650 PrettyAsset const& asset,
-
2651 auto&&...) {
-
2652 testcase("IOU no trust line to 3rd party");
+
2639 // transfer fees ignored on deposit
+
2640 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
+
2641 BEAST_EXPECT(
+
2642 env.balance(vaultAccount(keylet), issue) == asset(100));
+
2643
+
2644 {
+
2645 auto tx = vault.clawback(
+
2646 {.issuer = issuer,
+
2647 .id = keylet.key,
+
2648 .holder = owner,
+
2649 .amount = asset(50)});
+
2650 env(tx);
+
2651 env.close();
+
2652 }
2653
-
2654 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2655 env(tx);
-
2656 env.close();
-
2657
-
2658 env(vault.deposit(
-
2659 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2660 env.close();
-
2661
-
2662 Account const erin{"erin"};
-
2663 env.fund(XRP(1000), erin);
-
2664 env.close();
-
2665
-
2666 // Withdraw to 3rd party without trust line
-
2667 auto const tx1 = [&](ripple::Keylet keylet) {
-
2668 auto tx = vault.withdraw(
-
2669 {.depositor = owner,
-
2670 .id = keylet.key,
-
2671 .amount = asset(10)});
-
2672 tx[sfDestination] = erin.human();
-
2673 return tx;
-
2674 }(keylet);
-
2675 env(tx1, ter{tecNO_LINE});
-
2676 });
+
2654 // transfer fees ignored on clawback
+
2655 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
+
2656 BEAST_EXPECT(
+
2657 env.balance(vaultAccount(keylet), issue) == asset(50));
+
2658
+
2659 env(vault.withdraw(
+
2660 {.depositor = owner,
+
2661 .id = keylet.key,
+
2662 .amount = share(20'000'000)}));
+
2663
+
2664 // transfer fees ignored on withdraw
+
2665 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
+
2666 BEAST_EXPECT(
+
2667 env.balance(vaultAccount(keylet), issue) == asset(30));
+
2668
+
2669 {
+
2670 auto tx = vault.withdraw(
+
2671 {.depositor = owner,
+
2672 .id = keylet.key,
+
2673 .amount = share(30'000'000)});
+
2674 tx[sfDestination] = charlie.human();
+
2675 env(tx);
+
2676 }
2677
-
2678 testCase([&, this](
-
2679 Env& env,
-
2680 Account const& owner,
-
2681 Account const& issuer,
-
2682 Account const& charlie,
-
2683 auto,
-
2684 Vault& vault,
-
2685 PrettyAsset const& asset,
-
2686 auto&&...) {
-
2687 testcase("IOU no trust line to depositor");
+
2678 // transfer fees ignored on withdraw to 3rd party
+
2679 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
+
2680 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
+
2681 BEAST_EXPECT(
+
2682 env.balance(vaultAccount(keylet), issue) == asset(0));
+
2683
+
2684 env(vault.del({.owner = owner, .id = keylet.key}));
+
2685 env.close();
+
2686 },
+
2687 CaseArgs{.transferRate = 1.25});
2688
-
2689 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2690 env(tx);
-
2691 env.close();
-
2692
-
2693 // reset limit, so deposit of all funds will delete the trust line
-
2694 env.trust(asset(0), owner);
-
2695 env.close();
-
2696
-
2697 env(vault.deposit(
-
2698 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
-
2699 env.close();
-
2700
-
2701 auto trustline =
-
2702 env.le(keylet::line(owner, asset.raw().get<Issue>()));
-
2703 BEAST_EXPECT(trustline == nullptr);
-
2704
-
2705 // Withdraw without trust line, will succeed
-
2706 auto const tx1 = [&](ripple::Keylet keylet) {
-
2707 auto tx = vault.withdraw(
-
2708 {.depositor = owner,
-
2709 .id = keylet.key,
-
2710 .amount = asset(10)});
-
2711 return tx;
-
2712 }(keylet);
-
2713 env(tx1);
-
2714 });
-
2715
-
2716 testCase([&, this](
-
2717 Env& env,
-
2718 Account const& owner,
-
2719 Account const& issuer,
-
2720 Account const& charlie,
-
2721 auto,
-
2722 Vault& vault,
-
2723 PrettyAsset const& asset,
-
2724 auto&&...) {
-
2725 testcase("IOU frozen trust line to 3rd party");
-
2726
-
2727 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2728 env(tx);
-
2729 env.close();
-
2730
-
2731 env(vault.deposit(
-
2732 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2733 env.close();
-
2734
-
2735 // Withdraw to 3rd party works
-
2736 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
-
2737 auto tx = vault.withdraw(
-
2738 {.depositor = owner,
-
2739 .id = keylet.key,
-
2740 .amount = asset(10)});
-
2741 tx[sfDestination] = charlie.human();
-
2742 return tx;
-
2743 }(keylet);
-
2744 env(withdrawToCharlie);
-
2745
-
2746 // Freeze the 3rd party
-
2747 env(trust(issuer, asset(0), charlie, tfSetFreeze));
-
2748 env.close();
-
2749
-
2750 // Can withdraw
-
2751 auto const withdraw = vault.withdraw(
-
2752 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2753 env(withdraw);
-
2754 env.close();
+
2689 testCase([&, this](
+
2690 Env& env,
+
2691 Account const& owner,
+
2692 Account const& issuer,
+
2693 Account const& charlie,
+
2694 auto,
+
2695 Vault& vault,
+
2696 PrettyAsset const& asset,
+
2697 auto&&...) {
+
2698 testcase("IOU frozen trust line to depositor");
+
2699
+
2700 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2701 env(tx);
+
2702 env.close();
+
2703
+
2704 env(vault.deposit(
+
2705 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2706 env.close();
+
2707
+
2708 // Withdraw to 3rd party works
+
2709 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
+
2710 auto tx = vault.withdraw(
+
2711 {.depositor = owner,
+
2712 .id = keylet.key,
+
2713 .amount = asset(10)});
+
2714 tx[sfDestination] = charlie.human();
+
2715 return tx;
+
2716 }(keylet);
+
2717 env(withdrawToCharlie);
+
2718
+
2719 // Freeze the owner
+
2720 env(trust(issuer, asset(0), owner, tfSetFreeze));
+
2721 env.close();
+
2722
+
2723 // Cannot withdraw
+
2724 auto const withdraw = vault.withdraw(
+
2725 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2726 env(withdraw, ter{tecFROZEN});
+
2727
+
2728 // Cannot withdraw to 3rd party
+
2729 env(withdrawToCharlie, ter{tecLOCKED});
+
2730 env.close();
+
2731
+
2732 {
+
2733 // Cannot deposit some more
+
2734 auto tx = vault.deposit(
+
2735 {.depositor = owner,
+
2736 .id = keylet.key,
+
2737 .amount = asset(10)});
+
2738 env(tx, ter{tecFROZEN});
+
2739 }
+
2740
+
2741 {
+
2742 // Clawback still works
+
2743 auto tx = vault.clawback(
+
2744 {.issuer = issuer,
+
2745 .id = keylet.key,
+
2746 .holder = owner,
+
2747 .amount = asset(0)});
+
2748 env(tx);
+
2749 env.close();
+
2750 }
+
2751
+
2752 env(vault.del({.owner = owner, .id = keylet.key}));
+
2753 env.close();
+
2754 });
2755
-
2756 // Cannot withdraw to 3rd party
-
2757 env(withdrawToCharlie, ter{tecFROZEN});
-
2758 env.close();
-
2759
-
2760 env(vault.clawback(
-
2761 {.issuer = issuer,
-
2762 .id = keylet.key,
-
2763 .holder = owner,
-
2764 .amount = asset(0)}));
-
2765 env.close();
+
2756 testCase([&, this](
+
2757 Env& env,
+
2758 Account const& owner,
+
2759 Account const& issuer,
+
2760 Account const& charlie,
+
2761 auto,
+
2762 Vault& vault,
+
2763 PrettyAsset const& asset,
+
2764 auto&&...) {
+
2765 testcase("IOU no trust line to 3rd party");
2766
-
2767 env(vault.del({.owner = owner, .id = keylet.key}));
-
2768 env.close();
-
2769 });
+
2767 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2768 env(tx);
+
2769 env.close();
2770
-
2771 testCase([&, this](
-
2772 Env& env,
-
2773 Account const& owner,
-
2774 Account const& issuer,
-
2775 Account const& charlie,
-
2776 auto,
-
2777 Vault& vault,
-
2778 PrettyAsset const& asset,
-
2779 auto&&...) {
-
2780 testcase("IOU global freeze");
-
2781
-
2782 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2783 env(tx);
-
2784 env.close();
-
2785
-
2786 env(vault.deposit(
-
2787 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2788 env.close();
-
2789
-
2790 env(fset(issuer, asfGlobalFreeze));
-
2791 env.close();
-
2792
-
2793 {
-
2794 // Cannot withdraw
-
2795 auto tx = vault.withdraw(
-
2796 {.depositor = owner,
-
2797 .id = keylet.key,
-
2798 .amount = asset(10)});
-
2799 env(tx, ter{tecFROZEN});
-
2800
-
2801 // Cannot withdraw to 3rd party
-
2802 tx[sfDestination] = charlie.human();
-
2803 env(tx, ter{tecFROZEN});
-
2804 env.close();
+
2771 env(vault.deposit(
+
2772 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2773 env.close();
+
2774
+
2775 Account const erin{"erin"};
+
2776 env.fund(XRP(1000), erin);
+
2777 env.close();
+
2778
+
2779 // Withdraw to 3rd party without trust line
+
2780 auto const tx1 = [&](ripple::Keylet keylet) {
+
2781 auto tx = vault.withdraw(
+
2782 {.depositor = owner,
+
2783 .id = keylet.key,
+
2784 .amount = asset(10)});
+
2785 tx[sfDestination] = erin.human();
+
2786 return tx;
+
2787 }(keylet);
+
2788 env(tx1, ter{tecNO_LINE});
+
2789 });
+
2790
+
2791 testCase([&, this](
+
2792 Env& env,
+
2793 Account const& owner,
+
2794 Account const& issuer,
+
2795 Account const& charlie,
+
2796 auto,
+
2797 Vault& vault,
+
2798 PrettyAsset const& asset,
+
2799 auto&&...) {
+
2800 testcase("IOU no trust line to depositor");
+
2801
+
2802 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2803 env(tx);
+
2804 env.close();
2805
-
2806 // Cannot deposit some more
-
2807 tx = vault.deposit(
-
2808 {.depositor = owner,
-
2809 .id = keylet.key,
-
2810 .amount = asset(10)});
-
2811
-
2812 env(tx, ter{tecFROZEN});
-
2813 }
-
2814
-
2815 // Clawback is permitted
-
2816 env(vault.clawback(
-
2817 {.issuer = issuer,
-
2818 .id = keylet.key,
-
2819 .holder = owner,
-
2820 .amount = asset(0)}));
-
2821 env.close();
-
2822
-
2823 env(vault.del({.owner = owner, .id = keylet.key}));
-
2824 env.close();
-
2825 });
-
2826 }
-
-
2827
-
2828 void
-
- -
2830 {
-
2831 using namespace test::jtx;
-
2832
-
2833 testcase("private vault");
-
2834
-
2835 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
2836 Account issuer{"issuer"};
-
2837 Account owner{"owner"};
-
2838 Account depositor{"depositor"};
-
2839 Account charlie{"charlie"};
-
2840 Account pdOwner{"pdOwner"};
-
2841 Account credIssuer1{"credIssuer1"};
-
2842 Account credIssuer2{"credIssuer2"};
-
2843 std::string const credType = "credential";
-
2844 Vault vault{env};
-
2845 env.fund(
-
2846 XRP(1000),
-
2847 issuer,
-
2848 owner,
-
2849 depositor,
-
2850 charlie,
-
2851 pdOwner,
-
2852 credIssuer1,
-
2853 credIssuer2);
-
2854 env.close();
-
2855 env(fset(issuer, asfAllowTrustLineClawback));
-
2856 env.close();
-
2857 env.require(flags(issuer, asfAllowTrustLineClawback));
+
2806 // reset limit, so deposit of all funds will delete the trust line
+
2807 env.trust(asset(0), owner);
+
2808 env.close();
+
2809
+
2810 env(vault.deposit(
+
2811 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
+
2812 env.close();
+
2813
+
2814 auto trustline =
+
2815 env.le(keylet::line(owner, asset.raw().get<Issue>()));
+
2816 BEAST_EXPECT(trustline == nullptr);
+
2817
+
2818 // Withdraw without trust line, will succeed
+
2819 auto const tx1 = [&](ripple::Keylet keylet) {
+
2820 auto tx = vault.withdraw(
+
2821 {.depositor = owner,
+
2822 .id = keylet.key,
+
2823 .amount = asset(10)});
+
2824 return tx;
+
2825 }(keylet);
+
2826 env(tx1);
+
2827 });
+
2828
+
2829 auto const [acctReserve, incReserve] = [this]() -> std::pair<int, int> {
+
2830 Env env{*this, testable_amendments()};
+
2831 return {
+
2832 env.current()->fees().accountReserve(0).drops() /
+ +
2834 env.current()->fees().increment.drops() /
+ +
2836 }();
+
2837
+
2838 testCase(
+
2839 [&, this](
+
2840 Env& env,
+
2841 Account const& owner,
+
2842 Account const& issuer,
+
2843 Account const& charlie,
+
2844 auto,
+
2845 Vault& vault,
+
2846 PrettyAsset const& asset,
+
2847 auto&&...) {
+
2848 testcase("IOU no trust line to depositor no reserve");
+
2849 auto [tx, keylet] =
+
2850 vault.create({.owner = owner, .asset = asset});
+
2851 env(tx);
+
2852 env.close();
+
2853
+
2854 // reset limit, so deposit of all funds will delete the trust
+
2855 // line
+
2856 env.trust(asset(0), owner);
+
2857 env.close();
2858
-
2859 PrettyAsset asset = issuer["IOU"];
-
2860 env.trust(asset(1000), owner);
-
2861 env(pay(issuer, owner, asset(500)));
-
2862 env.trust(asset(1000), depositor);
-
2863 env(pay(issuer, depositor, asset(500)));
-
2864 env.trust(asset(1000), charlie);
-
2865 env(pay(issuer, charlie, asset(5)));
-
2866 env.close();
-
2867
-
2868 auto [tx, keylet] = vault.create(
-
2869 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
-
2870 env(tx);
-
2871 env.close();
-
2872 BEAST_EXPECT(env.le(keylet));
-
2873
-
2874 {
-
2875 testcase("private vault owner can deposit");
-
2876 auto tx = vault.deposit(
-
2877 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
-
2878 env(tx);
-
2879 }
-
2880
-
2881 {
-
2882 testcase("private vault depositor not authorized yet");
-
2883 auto tx = vault.deposit(
-
2884 {.depositor = depositor,
-
2885 .id = keylet.key,
-
2886 .amount = asset(50)});
-
2887 env(tx, ter{tecNO_AUTH});
-
2888 }
-
2889
-
2890 {
-
2891 testcase("private vault cannot set non-existing domain");
-
2892 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
2893 tx[sfDomainID] = to_string(base_uint<256>(42ul));
-
2894 env(tx, ter{tecOBJECT_NOT_FOUND});
-
2895 }
-
2896
-
2897 {
-
2898 testcase("private vault set domainId");
-
2899
-
2900 {
-
2901 pdomain::Credentials const credentials1{
-
2902 {.issuer = credIssuer1, .credType = credType}};
-
2903
-
2904 env(pdomain::setTx(pdOwner, credentials1));
-
2905 auto const domainId1 = [&]() {
-
2906 auto tx = env.tx()->getJson(JsonOptions::none);
-
2907 return pdomain::getNewDomain(env.meta());
-
2908 }();
-
2909
-
2910 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
2911 tx[sfDomainID] = to_string(domainId1);
-
2912 env(tx);
-
2913 env.close();
-
2914
-
2915 // Update domain second time, should be harmless
-
2916 env(tx);
-
2917 env.close();
-
2918 }
+
2859 env(vault.deposit(
+
2860 {.depositor = owner,
+
2861 .id = keylet.key,
+
2862 .amount = asset(200)}));
+
2863 env.close();
+
2864
+
2865 auto trustline =
+
2866 env.le(keylet::line(owner, asset.raw().get<Issue>()));
+
2867 BEAST_EXPECT(trustline == nullptr);
+
2868
+
2869 // Fail because not enough reserve to create trust line
+
2870 tx = vault.withdraw(
+
2871 {.depositor = owner,
+
2872 .id = keylet.key,
+
2873 .amount = asset(10)});
+
2874 env(tx, ter{tecNO_LINE_INSUF_RESERVE});
+
2875 env.close();
+
2876
+
2877 env(pay(charlie, owner, XRP(incReserve)));
+
2878 env.close();
+
2879
+
2880 // Withdraw can now create trust line, will succeed
+
2881 env(tx);
+
2882 env.close();
+
2883 },
+
2884 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
+
2885
+
2886 testCase(
+
2887 [&, this](
+
2888 Env& env,
+
2889 Account const& owner,
+
2890 Account const& issuer,
+
2891 Account const& charlie,
+
2892 auto,
+
2893 Vault& vault,
+
2894 PrettyAsset const& asset,
+
2895 auto&&...) {
+
2896 testcase("IOU no reserve for share MPToken");
+
2897 auto [tx, keylet] =
+
2898 vault.create({.owner = owner, .asset = asset});
+
2899 env(tx);
+
2900 env.close();
+
2901
+
2902 env(pay(owner, charlie, asset(100)));
+
2903 env.close();
+
2904
+
2905 // Use up some reserve on tickets
+
2906 env(ticket::create(charlie, 2));
+
2907 env.close();
+
2908
+
2909 // Fail because not enough reserve to create MPToken for shares
+
2910 tx = vault.deposit(
+
2911 {.depositor = charlie,
+
2912 .id = keylet.key,
+
2913 .amount = asset(100)});
+
2914 env(tx, ter{tecINSUFFICIENT_RESERVE});
+
2915 env.close();
+
2916
+
2917 env(pay(issuer, charlie, XRP(incReserve)));
+
2918 env.close();
2919
-
2920 {
-
2921 pdomain::Credentials const credentials{
-
2922 {.issuer = credIssuer1, .credType = credType},
-
2923 {.issuer = credIssuer2, .credType = credType}};
-
2924
-
2925 env(pdomain::setTx(pdOwner, credentials));
-
2926 auto const domainId = [&]() {
-
2927 auto tx = env.tx()->getJson(JsonOptions::none);
-
2928 return pdomain::getNewDomain(env.meta());
-
2929 }();
-
2930
-
2931 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
2932 tx[sfDomainID] = to_string(domainId);
-
2933 env(tx);
-
2934 env.close();
-
2935
-
2936 // Should be idempotent
-
2937 tx = vault.set({.owner = owner, .id = keylet.key});
-
2938 tx[sfDomainID] = to_string(domainId);
-
2939 env(tx);
-
2940 env.close();
-
2941 }
-
2942 }
-
2943
-
2944 {
-
2945 testcase("private vault depositor still not authorized");
-
2946 auto tx = vault.deposit(
-
2947 {.depositor = depositor,
-
2948 .id = keylet.key,
-
2949 .amount = asset(50)});
-
2950 env(tx, ter{tecNO_AUTH});
-
2951 env.close();
-
2952 }
-
2953
-
2954 auto const credKeylet =
-
2955 credentials::keylet(depositor, credIssuer1, credType);
-
2956 {
-
2957 testcase("private vault depositor now authorized");
-
2958 env(credentials::create(depositor, credIssuer1, credType));
-
2959 env(credentials::accept(depositor, credIssuer1, credType));
-
2960 env(credentials::create(charlie, credIssuer1, credType));
-
2961 // charlie's credential not accepted
-
2962 env.close();
-
2963 auto credSle = env.le(credKeylet);
-
2964 BEAST_EXPECT(credSle != nullptr);
+
2920 // Deposit can now create MPToken, will succeed
+
2921 env(tx);
+
2922 env.close();
+
2923 },
+
2924 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
+
2925
+
2926 testCase([&, this](
+
2927 Env& env,
+
2928 Account const& owner,
+
2929 Account const& issuer,
+
2930 Account const& charlie,
+
2931 auto,
+
2932 Vault& vault,
+
2933 PrettyAsset const& asset,
+
2934 auto&&...) {
+
2935 testcase("IOU frozen trust line to 3rd party");
+
2936
+
2937 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2938 env(tx);
+
2939 env.close();
+
2940
+
2941 env(vault.deposit(
+
2942 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2943 env.close();
+
2944
+
2945 // Withdraw to 3rd party works
+
2946 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
+
2947 auto tx = vault.withdraw(
+
2948 {.depositor = owner,
+
2949 .id = keylet.key,
+
2950 .amount = asset(10)});
+
2951 tx[sfDestination] = charlie.human();
+
2952 return tx;
+
2953 }(keylet);
+
2954 env(withdrawToCharlie);
+
2955
+
2956 // Freeze the 3rd party
+
2957 env(trust(issuer, asset(0), charlie, tfSetFreeze));
+
2958 env.close();
+
2959
+
2960 // Can withdraw
+
2961 auto const withdraw = vault.withdraw(
+
2962 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2963 env(withdraw);
+
2964 env.close();
2965
-
2966 auto tx = vault.deposit(
-
2967 {.depositor = depositor,
-
2968 .id = keylet.key,
-
2969 .amount = asset(50)});
-
2970 env(tx);
-
2971 env.close();
-
2972
-
2973 tx = vault.deposit(
-
2974 {.depositor = charlie, .id = keylet.key, .amount = asset(50)});
-
2975 env(tx, ter{tecNO_AUTH});
-
2976 env.close();
-
2977 }
-
2978
-
2979 {
-
2980 testcase("private vault depositor lost authorization");
-
2981 env(credentials::deleteCred(
-
2982 credIssuer1, depositor, credIssuer1, credType));
-
2983 env(credentials::deleteCred(
-
2984 credIssuer1, charlie, credIssuer1, credType));
-
2985 env.close();
-
2986 auto credSle = env.le(credKeylet);
-
2987 BEAST_EXPECT(credSle == nullptr);
-
2988
-
2989 auto tx = vault.deposit(
-
2990 {.depositor = depositor,
-
2991 .id = keylet.key,
-
2992 .amount = asset(50)});
-
2993 env(tx, ter{tecNO_AUTH});
+
2966 // Cannot withdraw to 3rd party
+
2967 env(withdrawToCharlie, ter{tecFROZEN});
+
2968 env.close();
+
2969
+
2970 env(vault.clawback(
+
2971 {.issuer = issuer,
+
2972 .id = keylet.key,
+
2973 .holder = owner,
+
2974 .amount = asset(0)}));
+
2975 env.close();
+
2976
+
2977 env(vault.del({.owner = owner, .id = keylet.key}));
+
2978 env.close();
+
2979 });
+
2980
+
2981 testCase([&, this](
+
2982 Env& env,
+
2983 Account const& owner,
+
2984 Account const& issuer,
+
2985 Account const& charlie,
+
2986 auto,
+
2987 Vault& vault,
+
2988 PrettyAsset const& asset,
+
2989 auto&&...) {
+
2990 testcase("IOU global freeze");
+
2991
+
2992 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2993 env(tx);
2994 env.close();
-
2995 }
-
2996
-
2997 auto const shares = [&env, keylet = keylet, this]() -> Asset {
-
2998 auto const vault = env.le(keylet);
-
2999 BEAST_EXPECT(vault != nullptr);
-
3000 return MPTIssue(vault->at(sfShareMPTID));
-
3001 }();
+
2995
+
2996 env(vault.deposit(
+
2997 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2998 env.close();
+
2999
+
3000 env(fset(issuer, asfGlobalFreeze));
+
3001 env.close();
3002
-
3003 {
-
3004 testcase("private vault expired authorization");
-
3005 uint32_t const closeTime = env.current()
-
3006 ->info()
-
3007 .parentCloseTime.time_since_epoch()
-
3008 .count();
-
3009 {
-
3010 auto tx0 =
-
3011 credentials::create(depositor, credIssuer2, credType);
-
3012 tx0[sfExpiration] = closeTime + 20;
-
3013 env(tx0);
-
3014 tx0 = credentials::create(charlie, credIssuer2, credType);
-
3015 tx0[sfExpiration] = closeTime + 20;
-
3016 env(tx0);
-
3017 env.close();
-
3018
-
3019 env(credentials::accept(depositor, credIssuer2, credType));
-
3020 env(credentials::accept(charlie, credIssuer2, credType));
-
3021 env.close();
-
3022 }
-
3023
-
3024 {
-
3025 auto tx1 = vault.deposit(
-
3026 {.depositor = depositor,
-
3027 .id = keylet.key,
-
3028 .amount = asset(50)});
-
3029 env(tx1);
-
3030 env.close();
-
3031
-
3032 auto const tokenKeylet = keylet::mptoken(
-
3033 shares.get<MPTIssue>().getMptID(), depositor.id());
-
3034 BEAST_EXPECT(env.le(tokenKeylet) != nullptr);
-
3035 }
-
3036
-
3037 {
-
3038 // time advance
-
3039 env.close();
-
3040 env.close();
-
3041 env.close();
-
3042
-
3043 auto const credsKeylet =
-
3044 credentials::keylet(depositor, credIssuer2, credType);
-
3045 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
-
3046
-
3047 auto tx2 = vault.deposit(
-
3048 {.depositor = depositor,
-
3049 .id = keylet.key,
-
3050 .amount = asset(1)});
-
3051 env(tx2, ter{tecEXPIRED});
-
3052 env.close();
-
3053
-
3054 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
-
3055 }
-
3056
-
3057 {
-
3058 auto const credsKeylet =
-
3059 credentials::keylet(charlie, credIssuer2, credType);
-
3060 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
-
3061 auto const tokenKeylet = keylet::mptoken(
-
3062 shares.get<MPTIssue>().getMptID(), charlie.id());
-
3063 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
-
3064
-
3065 auto tx3 = vault.deposit(
-
3066 {.depositor = charlie,
-
3067 .id = keylet.key,
-
3068 .amount = asset(2)});
-
3069 env(tx3, ter{tecEXPIRED});
-
3070
-
3071 env.close();
-
3072 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
-
3073 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
-
3074 }
-
3075 }
-
3076
-
3077 {
-
3078 testcase("private vault reset domainId");
-
3079 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
3080 tx[sfDomainID] = "0";
-
3081 env(tx);
-
3082 env.close();
-
3083
-
3084 tx = vault.deposit(
-
3085 {.depositor = depositor,
-
3086 .id = keylet.key,
-
3087 .amount = asset(50)});
-
3088 env(tx, ter{tecNO_AUTH});
-
3089 env.close();
-
3090
-
3091 tx = vault.withdraw(
-
3092 {.depositor = depositor,
-
3093 .id = keylet.key,
-
3094 .amount = asset(50)});
-
3095 env(tx);
-
3096 env.close();
-
3097
-
3098 tx = vault.clawback(
-
3099 {.issuer = issuer,
-
3100 .id = keylet.key,
-
3101 .holder = depositor,
-
3102 .amount = asset(0)});
-
3103 env(tx);
-
3104
-
3105 tx = vault.clawback(
-
3106 {.issuer = issuer,
-
3107 .id = keylet.key,
-
3108 .holder = owner,
-
3109 .amount = asset(0)});
-
3110 env(tx);
-
3111 env.close();
-
3112
-
3113 tx = vault.del({
-
3114 .owner = owner,
-
3115 .id = keylet.key,
-
3116 });
-
3117 env(tx);
-
3118 }
-
3119 }
+
3003 {
+
3004 // Cannot withdraw
+
3005 auto tx = vault.withdraw(
+
3006 {.depositor = owner,
+
3007 .id = keylet.key,
+
3008 .amount = asset(10)});
+
3009 env(tx, ter{tecFROZEN});
+
3010
+
3011 // Cannot withdraw to 3rd party
+
3012 tx[sfDestination] = charlie.human();
+
3013 env(tx, ter{tecFROZEN});
+
3014 env.close();
+
3015
+
3016 // Cannot deposit some more
+
3017 tx = vault.deposit(
+
3018 {.depositor = owner,
+
3019 .id = keylet.key,
+
3020 .amount = asset(10)});
+
3021
+
3022 env(tx, ter{tecFROZEN});
+
3023 }
+
3024
+
3025 // Clawback is permitted
+
3026 env(vault.clawback(
+
3027 {.issuer = issuer,
+
3028 .id = keylet.key,
+
3029 .holder = owner,
+
3030 .amount = asset(0)}));
+
3031 env.close();
+
3032
+
3033 env(vault.del({.owner = owner, .id = keylet.key}));
+
3034 env.close();
+
3035 });
+
3036 }
-
3120
-
3121 void
-
- -
3123 {
-
3124 using namespace test::jtx;
-
3125
-
3126 testcase("private XRP vault");
-
3127
-
3128 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
3129 Account owner{"owner"};
-
3130 Account depositor{"depositor"};
-
3131 Account alice{"charlie"};
-
3132 std::string const credType = "credential";
-
3133 Vault vault{env};
-
3134 env.fund(XRP(100000), owner, depositor, alice);
-
3135 env.close();
-
3136
-
3137 PrettyAsset asset = xrpIssue();
-
3138 auto [tx, keylet] = vault.create(
-
3139 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
-
3140 env(tx);
-
3141 env.close();
-
3142
-
3143 auto const [vaultAccount, issuanceId] =
-
3144 [&env, keylet = keylet, this]() -> std::tuple<AccountID, uint192> {
-
3145 auto const vault = env.le(keylet);
-
3146 BEAST_EXPECT(vault != nullptr);
-
3147 return {vault->at(sfAccount), vault->at(sfShareMPTID)};
-
3148 }();
-
3149 BEAST_EXPECT(env.le(keylet::account(vaultAccount)));
-
3150 BEAST_EXPECT(env.le(keylet::mptIssuance(issuanceId)));
-
3151 PrettyAsset shares{issuanceId};
-
3152
-
3153 {
-
3154 testcase("private XRP vault owner can deposit");
-
3155 auto tx = vault.deposit(
-
3156 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
-
3157 env(tx);
-
3158 env.close();
-
3159 }
-
3160
-
3161 {
-
3162 testcase("private XRP vault cannot pay shares to depositor yet");
-
3163 env(pay(owner, depositor, shares(1)), ter{tecNO_AUTH});
-
3164 }
-
3165
+
3037
+
3038 void
+
+ +
3040 {
+
3041 using namespace test::jtx;
+
3042
+
3043 testcase("private vault");
+
3044
+
3045 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3046 Account issuer{"issuer"};
+
3047 Account owner{"owner"};
+
3048 Account depositor{"depositor"};
+
3049 Account charlie{"charlie"};
+
3050 Account pdOwner{"pdOwner"};
+
3051 Account credIssuer1{"credIssuer1"};
+
3052 Account credIssuer2{"credIssuer2"};
+
3053 std::string const credType = "credential";
+
3054 Vault vault{env};
+
3055 env.fund(
+
3056 XRP(1000),
+
3057 issuer,
+
3058 owner,
+
3059 depositor,
+
3060 charlie,
+
3061 pdOwner,
+
3062 credIssuer1,
+
3063 credIssuer2);
+
3064 env.close();
+
3065 env(fset(issuer, asfAllowTrustLineClawback));
+
3066 env.close();
+
3067 env.require(flags(issuer, asfAllowTrustLineClawback));
+
3068
+
3069 PrettyAsset asset = issuer["IOU"];
+
3070 env.trust(asset(1000), owner);
+
3071 env(pay(issuer, owner, asset(500)));
+
3072 env.trust(asset(1000), depositor);
+
3073 env(pay(issuer, depositor, asset(500)));
+
3074 env.trust(asset(1000), charlie);
+
3075 env(pay(issuer, charlie, asset(5)));
+
3076 env.close();
+
3077
+
3078 auto [tx, keylet] = vault.create(
+
3079 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
+
3080 env(tx);
+
3081 env.close();
+
3082 BEAST_EXPECT(env.le(keylet));
+
3083
+
3084 {
+
3085 testcase("private vault owner can deposit");
+
3086 auto tx = vault.deposit(
+
3087 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
+
3088 env(tx);
+
3089 }
+
3090
+
3091 {
+
3092 testcase("private vault depositor not authorized yet");
+
3093 auto tx = vault.deposit(
+
3094 {.depositor = depositor,
+
3095 .id = keylet.key,
+
3096 .amount = asset(50)});
+
3097 env(tx, ter{tecNO_AUTH});
+
3098 }
+
3099
+
3100 {
+
3101 testcase("private vault cannot set non-existing domain");
+
3102 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
3103 tx[sfDomainID] = to_string(base_uint<256>(42ul));
+
3104 env(tx, ter{tecOBJECT_NOT_FOUND});
+
3105 }
+
3106
+
3107 {
+
3108 testcase("private vault set domainId");
+
3109
+
3110 {
+
3111 pdomain::Credentials const credentials1{
+
3112 {.issuer = credIssuer1, .credType = credType}};
+
3113
+
3114 env(pdomain::setTx(pdOwner, credentials1));
+
3115 auto const domainId1 = [&]() {
+
3116 auto tx = env.tx()->getJson(JsonOptions::none);
+
3117 return pdomain::getNewDomain(env.meta());
+
3118 }();
+
3119
+
3120 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
3121 tx[sfDomainID] = to_string(domainId1);
+
3122 env(tx);
+
3123 env.close();
+
3124
+
3125 // Update domain second time, should be harmless
+
3126 env(tx);
+
3127 env.close();
+
3128 }
+
3129
+
3130 {
+
3131 pdomain::Credentials const credentials{
+
3132 {.issuer = credIssuer1, .credType = credType},
+
3133 {.issuer = credIssuer2, .credType = credType}};
+
3134
+
3135 env(pdomain::setTx(pdOwner, credentials));
+
3136 auto const domainId = [&]() {
+
3137 auto tx = env.tx()->getJson(JsonOptions::none);
+
3138 return pdomain::getNewDomain(env.meta());
+
3139 }();
+
3140
+
3141 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
3142 tx[sfDomainID] = to_string(domainId);
+
3143 env(tx);
+
3144 env.close();
+
3145
+
3146 // Should be idempotent
+
3147 tx = vault.set({.owner = owner, .id = keylet.key});
+
3148 tx[sfDomainID] = to_string(domainId);
+
3149 env(tx);
+
3150 env.close();
+
3151 }
+
3152 }
+
3153
+
3154 {
+
3155 testcase("private vault depositor still not authorized");
+
3156 auto tx = vault.deposit(
+
3157 {.depositor = depositor,
+
3158 .id = keylet.key,
+
3159 .amount = asset(50)});
+
3160 env(tx, ter{tecNO_AUTH});
+
3161 env.close();
+
3162 }
+
3163
+
3164 auto const credKeylet =
+
3165 credentials::keylet(depositor, credIssuer1, credType);
3166 {
-
3167 testcase("private XRP vault depositor not authorized yet");
-
3168 auto tx = vault.deposit(
-
3169 {.depositor = depositor,
-
3170 .id = keylet.key,
-
3171 .amount = asset(50)});
-
3172 env(tx, ter{tecNO_AUTH});
-
3173 }
-
3174
-
3175 {
-
3176 testcase("private XRP vault set DomainID");
-
3177 pdomain::Credentials const credentials{
-
3178 {.issuer = owner, .credType = credType}};
-
3179
-
3180 env(pdomain::setTx(owner, credentials));
-
3181 auto const domainId = [&]() {
-
3182 auto tx = env.tx()->getJson(JsonOptions::none);
-
3183 return pdomain::getNewDomain(env.meta());
-
3184 }();
-
3185
-
3186 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
3187 tx[sfDomainID] = to_string(domainId);
-
3188 env(tx);
-
3189 env.close();
-
3190 }
-
3191
-
3192 auto const credKeylet = credentials::keylet(depositor, owner, credType);
-
3193 {
-
3194 testcase("private XRP vault depositor now authorized");
-
3195 env(credentials::create(depositor, owner, credType));
-
3196 env(credentials::accept(depositor, owner, credType));
-
3197 env.close();
+
3167 testcase("private vault depositor now authorized");
+
3168 env(credentials::create(depositor, credIssuer1, credType));
+
3169 env(credentials::accept(depositor, credIssuer1, credType));
+
3170 env(credentials::create(charlie, credIssuer1, credType));
+
3171 // charlie's credential not accepted
+
3172 env.close();
+
3173 auto credSle = env.le(credKeylet);
+
3174 BEAST_EXPECT(credSle != nullptr);
+
3175
+
3176 auto tx = vault.deposit(
+
3177 {.depositor = depositor,
+
3178 .id = keylet.key,
+
3179 .amount = asset(50)});
+
3180 env(tx);
+
3181 env.close();
+
3182
+
3183 tx = vault.deposit(
+
3184 {.depositor = charlie, .id = keylet.key, .amount = asset(50)});
+
3185 env(tx, ter{tecNO_AUTH});
+
3186 env.close();
+
3187 }
+
3188
+
3189 {
+
3190 testcase("private vault depositor lost authorization");
+
3191 env(credentials::deleteCred(
+
3192 credIssuer1, depositor, credIssuer1, credType));
+
3193 env(credentials::deleteCred(
+
3194 credIssuer1, charlie, credIssuer1, credType));
+
3195 env.close();
+
3196 auto credSle = env.le(credKeylet);
+
3197 BEAST_EXPECT(credSle == nullptr);
3198
-
3199 BEAST_EXPECT(env.le(credKeylet));
-
3200 auto tx = vault.deposit(
-
3201 {.depositor = depositor,
-
3202 .id = keylet.key,
-
3203 .amount = asset(50)});
-
3204 env(tx);
-
3205 env.close();
-
3206 }
-
3207
-
3208 {
-
3209 testcase("private XRP vault can pay shares to depositor");
-
3210 env(pay(owner, depositor, shares(1)));
-
3211 }
+
3199 auto tx = vault.deposit(
+
3200 {.depositor = depositor,
+
3201 .id = keylet.key,
+
3202 .amount = asset(50)});
+
3203 env(tx, ter{tecNO_AUTH});
+
3204 env.close();
+
3205 }
+
3206
+
3207 auto const shares = [&env, keylet = keylet, this]() -> Asset {
+
3208 auto const vault = env.le(keylet);
+
3209 BEAST_EXPECT(vault != nullptr);
+
3210 return MPTIssue(vault->at(sfShareMPTID));
+
3211 }();
3212
3213 {
-
3214 testcase("private XRP vault cannot pay shares to 3rd party");
-
3215 Json::Value jv;
-
3216 jv[sfAccount] = alice.human();
-
3217 jv[sfTransactionType] = jss::MPTokenAuthorize;
-
3218 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
-
3219 env(jv);
-
3220 env.close();
-
3221
-
3222 env(pay(owner, alice, shares(1)), ter{tecNO_AUTH});
-
3223 }
-
3224 }
-
-
3225
-
3226 void
-
- -
3228 {
-
3229 using namespace test::jtx;
-
3230
-
3231 testcase("fail pseudo-account allocation");
-
3232 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
3233 Account const owner{"owner"};
-
3234 Vault vault{env};
-
3235 env.fund(XRP(1000), owner);
-
3236
-
3237 auto const keylet = keylet::vault(owner.id(), env.seq(owner));
-
3238 for (int i = 0; i < 256; ++i)
-
3239 {
-
3240 AccountID const accountId =
-
3241 ripple::pseudoAccountAddress(*env.current(), keylet.key);
-
3242
-
3243 env(pay(env.master.id(), accountId, XRP(1000)),
-
3244 seq(autofill),
-
3245 fee(autofill),
-
3246 sig(autofill));
-
3247 }
-
3248
-
3249 auto [tx, keylet1] =
-
3250 vault.create({.owner = owner, .asset = xrpIssue()});
-
3251 BEAST_EXPECT(keylet.key == keylet1.key);
-
3252 env(tx, ter{terADDRESS_COLLISION});
-
3253 }
-
-
3254
-
3255 void
-
- -
3257 {
-
3258 using namespace test::jtx;
-
3259
-
3260 struct Data
-
3261 {
-
3262 Account const& owner;
-
3263 Account const& issuer;
-
3264 Account const& depositor;
-
3265 Account const& vaultAccount;
-
3266 MPTIssue shares;
-
3267 PrettyAsset const& share;
-
3268 Vault& vault;
-
3269 ripple::Keylet keylet;
-
3270 Issue assets;
-
3271 PrettyAsset const& asset;
-
3272 std::function<bool(std::function<bool(SLE&, SLE&)>)> peek;
-
3273 };
+
3214 testcase("private vault expired authorization");
+
3215 uint32_t const closeTime = env.current()
+
3216 ->info()
+
3217 .parentCloseTime.time_since_epoch()
+
3218 .count();
+
3219 {
+
3220 auto tx0 =
+
3221 credentials::create(depositor, credIssuer2, credType);
+
3222 tx0[sfExpiration] = closeTime + 20;
+
3223 env(tx0);
+
3224 tx0 = credentials::create(charlie, credIssuer2, credType);
+
3225 tx0[sfExpiration] = closeTime + 20;
+
3226 env(tx0);
+
3227 env.close();
+
3228
+
3229 env(credentials::accept(depositor, credIssuer2, credType));
+
3230 env(credentials::accept(charlie, credIssuer2, credType));
+
3231 env.close();
+
3232 }
+
3233
+
3234 {
+
3235 auto tx1 = vault.deposit(
+
3236 {.depositor = depositor,
+
3237 .id = keylet.key,
+
3238 .amount = asset(50)});
+
3239 env(tx1);
+
3240 env.close();
+
3241
+
3242 auto const tokenKeylet = keylet::mptoken(
+
3243 shares.get<MPTIssue>().getMptID(), depositor.id());
+
3244 BEAST_EXPECT(env.le(tokenKeylet) != nullptr);
+
3245 }
+
3246
+
3247 {
+
3248 // time advance
+
3249 env.close();
+
3250 env.close();
+
3251 env.close();
+
3252
+
3253 auto const credsKeylet =
+
3254 credentials::keylet(depositor, credIssuer2, credType);
+
3255 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
+
3256
+
3257 auto tx2 = vault.deposit(
+
3258 {.depositor = depositor,
+
3259 .id = keylet.key,
+
3260 .amount = asset(1)});
+
3261 env(tx2, ter{tecEXPIRED});
+
3262 env.close();
+
3263
+
3264 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
+
3265 }
+
3266
+
3267 {
+
3268 auto const credsKeylet =
+
3269 credentials::keylet(charlie, credIssuer2, credType);
+
3270 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
+
3271 auto const tokenKeylet = keylet::mptoken(
+
3272 shares.get<MPTIssue>().getMptID(), charlie.id());
+
3273 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
3274
-
3275 auto testCase = [&, this](
-
3276 std::uint8_t scale,
-
3277 std::function<void(Env & env, Data data)> test) {
-
3278 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
3279 Account const owner{"owner"};
-
3280 Account const issuer{"issuer"};
-
3281 Account const depositor{"depositor"};
-
3282 Vault vault{env};
-
3283 env.fund(XRP(1000), issuer, owner, depositor);
-
3284 env(fset(issuer, asfAllowTrustLineClawback));
-
3285 env.close();
+
3275 auto tx3 = vault.deposit(
+
3276 {.depositor = charlie,
+
3277 .id = keylet.key,
+
3278 .amount = asset(2)});
+
3279 env(tx3, ter{tecEXPIRED});
+
3280
+
3281 env.close();
+
3282 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
+
3283 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
+
3284 }
+
3285 }
3286
-
3287 PrettyAsset const asset = issuer["IOU"];
-
3288 env.trust(asset(1000), owner);
-
3289 env.trust(asset(1000), depositor);
-
3290 env(pay(issuer, owner, asset(200)));
-
3291 env(pay(issuer, depositor, asset(200)));
+
3287 {
+
3288 testcase("private vault reset domainId");
+
3289 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
3290 tx[sfDomainID] = "0";
+
3291 env(tx);
3292 env.close();
3293
-
3294 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
3295 tx[sfScale] = scale;
-
3296 env(tx);
-
3297
-
3298 auto const [vaultAccount, issuanceId] =
-
3299 [&env](ripple::Keylet keylet) -> std::tuple<Account, MPTID> {
-
3300 auto const vault = env.le(keylet);
-
3301 return {
-
3302 Account("vault", vault->at(sfAccount)),
-
3303 vault->at(sfShareMPTID)};
-
3304 }(keylet);
-
3305 MPTIssue shares(issuanceId);
-
3306 env.memoize(vaultAccount);
+
3294 tx = vault.deposit(
+
3295 {.depositor = depositor,
+
3296 .id = keylet.key,
+
3297 .amount = asset(50)});
+
3298 env(tx, ter{tecNO_AUTH});
+
3299 env.close();
+
3300
+
3301 tx = vault.withdraw(
+
3302 {.depositor = depositor,
+
3303 .id = keylet.key,
+
3304 .amount = asset(50)});
+
3305 env(tx);
+
3306 env.close();
3307
-
3308 auto const peek =
-
3309 [=, &env, this](std::function<bool(SLE&, SLE&)> fn) -> bool {
-
3310 return env.app().openLedger().modify(
-
3311 [&](OpenView& view, beast::Journal j) -> bool {
-
3312 Sandbox sb(&view, tapNONE);
-
3313 auto vault = sb.peek(keylet::vault(keylet.key));
-
3314 if (!BEAST_EXPECT(vault != nullptr))
-
3315 return false;
-
3316 auto shares = sb.peek(
-
3317 keylet::mptIssuance(vault->at(sfShareMPTID)));
-
3318 if (!BEAST_EXPECT(shares != nullptr))
-
3319 return false;
-
3320 if (fn(*vault, *shares))
-
3321 {
-
3322 sb.update(vault);
-
3323 sb.update(shares);
-
3324 sb.apply(view);
-
3325 return true;
-
3326 }
-
3327 return false;
-
3328 });
-
3329 };
+
3308 tx = vault.clawback(
+
3309 {.issuer = issuer,
+
3310 .id = keylet.key,
+
3311 .holder = depositor,
+
3312 .amount = asset(0)});
+
3313 env(tx);
+
3314
+
3315 tx = vault.clawback(
+
3316 {.issuer = issuer,
+
3317 .id = keylet.key,
+
3318 .holder = owner,
+
3319 .amount = asset(0)});
+
3320 env(tx);
+
3321 env.close();
+
3322
+
3323 tx = vault.del({
+
3324 .owner = owner,
+
3325 .id = keylet.key,
+
3326 });
+
3327 env(tx);
+
3328 }
+
3329 }
+
3330
-
3331 test(
-
3332 env,
-
3333 {.owner = owner,
-
3334 .issuer = issuer,
-
3335 .depositor = depositor,
-
3336 .vaultAccount = vaultAccount,
-
3337 .shares = shares,
-
3338 .share = PrettyAsset(shares),
-
3339 .vault = vault,
-
3340 .keylet = keylet,
-
3341 .assets = asset.raw().get<Issue>(),
-
3342 .asset = asset,
-
3343 .peek = peek});
-
3344 };
-
3345
-
3346 testCase(18, [&, this](Env& env, Data d) {
-
3347 testcase("Scale deposit overflow on first deposit");
-
3348 auto tx = d.vault.deposit(
-
3349 {.depositor = d.depositor,
-
3350 .id = d.keylet.key,
-
3351 .amount = d.asset(10)});
-
3352 env(tx, ter{tecPATH_DRY});
-
3353 env.close();
-
3354 });
-
3355
-
3356 testCase(18, [&, this](Env& env, Data d) {
-
3357 testcase("Scale deposit overflow on second deposit");
-
3358
-
3359 {
-
3360 auto tx = d.vault.deposit(
-
3361 {.depositor = d.depositor,
-
3362 .id = d.keylet.key,
-
3363 .amount = d.asset(5)});
-
3364 env(tx);
-
3365 env.close();
-
3366 }
-
3367
-
3368 {
-
3369 auto tx = d.vault.deposit(
-
3370 {.depositor = d.depositor,
-
3371 .id = d.keylet.key,
-
3372 .amount = d.asset(10)});
-
3373 env(tx, ter{tecPATH_DRY});
-
3374 env.close();
-
3375 }
-
3376 });
-
3377
-
3378 testCase(18, [&, this](Env& env, Data d) {
-
3379 testcase("Scale deposit overflow on total shares");
-
3380
-
3381 {
-
3382 auto tx = d.vault.deposit(
-
3383 {.depositor = d.depositor,
-
3384 .id = d.keylet.key,
-
3385 .amount = d.asset(5)});
-
3386 env(tx);
-
3387 env.close();
-
3388 }
+
3331 void
+
+ +
3333 {
+
3334 using namespace test::jtx;
+
3335
+
3336 testcase("private XRP vault");
+
3337
+
3338 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3339 Account owner{"owner"};
+
3340 Account depositor{"depositor"};
+
3341 Account alice{"charlie"};
+
3342 std::string const credType = "credential";
+
3343 Vault vault{env};
+
3344 env.fund(XRP(100000), owner, depositor, alice);
+
3345 env.close();
+
3346
+
3347 PrettyAsset asset = xrpIssue();
+
3348 auto [tx, keylet] = vault.create(
+
3349 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
+
3350 env(tx);
+
3351 env.close();
+
3352
+
3353 auto const [vaultAccount, issuanceId] =
+
3354 [&env, keylet = keylet, this]() -> std::tuple<AccountID, uint192> {
+
3355 auto const vault = env.le(keylet);
+
3356 BEAST_EXPECT(vault != nullptr);
+
3357 return {vault->at(sfAccount), vault->at(sfShareMPTID)};
+
3358 }();
+
3359 BEAST_EXPECT(env.le(keylet::account(vaultAccount)));
+
3360 BEAST_EXPECT(env.le(keylet::mptIssuance(issuanceId)));
+
3361 PrettyAsset shares{issuanceId};
+
3362
+
3363 {
+
3364 testcase("private XRP vault owner can deposit");
+
3365 auto tx = vault.deposit(
+
3366 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
+
3367 env(tx);
+
3368 env.close();
+
3369 }
+
3370
+
3371 {
+
3372 testcase("private XRP vault cannot pay shares to depositor yet");
+
3373 env(pay(owner, depositor, shares(1)), ter{tecNO_AUTH});
+
3374 }
+
3375
+
3376 {
+
3377 testcase("private XRP vault depositor not authorized yet");
+
3378 auto tx = vault.deposit(
+
3379 {.depositor = depositor,
+
3380 .id = keylet.key,
+
3381 .amount = asset(50)});
+
3382 env(tx, ter{tecNO_AUTH});
+
3383 }
+
3384
+
3385 {
+
3386 testcase("private XRP vault set DomainID");
+
3387 pdomain::Credentials const credentials{
+
3388 {.issuer = owner, .credType = credType}};
3389
-
3390 {
-
3391 auto tx = d.vault.deposit(
-
3392 {.depositor = d.depositor,
-
3393 .id = d.keylet.key,
-
3394 .amount = d.asset(5)});
-
3395 env(tx, ter{tecPATH_DRY});
-
3396 env.close();
-
3397 }
-
3398 });
-
3399
-
3400 testCase(1, [&, this](Env& env, Data d) {
-
3401 testcase("Scale deposit exact");
-
3402
-
3403 auto const start = env.balance(d.depositor, d.assets).number();
-
3404 auto tx = d.vault.deposit(
-
3405 {.depositor = d.depositor,
-
3406 .id = d.keylet.key,
-
3407 .amount = d.asset(1)});
-
3408 env(tx);
-
3409 env.close();
-
3410 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
-
3411 BEAST_EXPECT(
-
3412 env.balance(d.depositor, d.assets) ==
-
3413 STAmount(d.asset, start - 1));
-
3414 });
-
3415
-
3416 testCase(1, [&, this](Env& env, Data d) {
-
3417 testcase("Scale deposit insignificant amount");
-
3418
-
3419 auto tx = d.vault.deposit(
-
3420 {.depositor = d.depositor,
-
3421 .id = d.keylet.key,
-
3422 .amount = STAmount(d.asset, Number(9, -2))});
-
3423 env(tx, ter{tecPRECISION_LOSS});
-
3424 });
-
3425
-
3426 testCase(1, [&, this](Env& env, Data d) {
-
3427 testcase("Scale deposit exact, using full precision");
-
3428
-
3429 auto const start = env.balance(d.depositor, d.assets).number();
-
3430 auto tx = d.vault.deposit(
-
3431 {.depositor = d.depositor,
-
3432 .id = d.keylet.key,
-
3433 .amount = STAmount(d.asset, Number(15, -1))});
-
3434 env(tx);
-
3435 env.close();
-
3436 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
-
3437 BEAST_EXPECT(
-
3438 env.balance(d.depositor, d.assets) ==
-
3439 STAmount(d.asset, start - Number(15, -1)));
-
3440 });
-
3441
-
3442 testCase(1, [&, this](Env& env, Data d) {
-
3443 testcase("Scale deposit exact, truncating from .5");
-
3444
-
3445 auto const start = env.balance(d.depositor, d.assets).number();
-
3446 // Each of the cases below will transfer exactly 1.2 IOU to the
-
3447 // vault and receive 12 shares in exchange
-
3448 {
-
3449 auto tx = d.vault.deposit(
-
3450 {.depositor = d.depositor,
-
3451 .id = d.keylet.key,
-
3452 .amount = STAmount(d.asset, Number(125, -2))});
-
3453 env(tx);
-
3454 env.close();
-
3455 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
-
3456 BEAST_EXPECT(
-
3457 env.balance(d.depositor, d.assets) ==
-
3458 STAmount(d.asset, start - Number(12, -1)));
-
3459 }
-
3460
-
3461 {
-
3462 auto tx = d.vault.deposit(
-
3463 {.depositor = d.depositor,
-
3464 .id = d.keylet.key,
-
3465 .amount = STAmount(d.asset, Number(1201, -3))});
-
3466 env(tx);
-
3467 env.close();
-
3468 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
-
3469 BEAST_EXPECT(
-
3470 env.balance(d.depositor, d.assets) ==
-
3471 STAmount(d.asset, start - Number(24, -1)));
-
3472 }
-
3473
-
3474 {
-
3475 auto tx = d.vault.deposit(
-
3476 {.depositor = d.depositor,
-
3477 .id = d.keylet.key,
-
3478 .amount = STAmount(d.asset, Number(1299, -3))});
-
3479 env(tx);
-
3480 env.close();
-
3481 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
-
3482 BEAST_EXPECT(
-
3483 env.balance(d.depositor, d.assets) ==
-
3484 STAmount(d.asset, start - Number(36, -1)));
-
3485 }
-
3486 });
-
3487
-
3488 testCase(1, [&, this](Env& env, Data d) {
-
3489 testcase("Scale deposit exact, truncating from .01");
-
3490
-
3491 auto const start = env.balance(d.depositor, d.assets).number();
-
3492 // round to 12
-
3493 auto tx = d.vault.deposit(
-
3494 {.depositor = d.depositor,
-
3495 .id = d.keylet.key,
-
3496 .amount = STAmount(d.asset, Number(1201, -3))});
-
3497 env(tx);
-
3498 env.close();
-
3499 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
-
3500 BEAST_EXPECT(
-
3501 env.balance(d.depositor, d.assets) ==
-
3502 STAmount(d.asset, start - Number(12, -1)));
+
3390 env(pdomain::setTx(owner, credentials));
+
3391 auto const domainId = [&]() {
+
3392 auto tx = env.tx()->getJson(JsonOptions::none);
+
3393 return pdomain::getNewDomain(env.meta());
+
3394 }();
+
3395
+
3396 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
3397 tx[sfDomainID] = to_string(domainId);
+
3398 env(tx);
+
3399 env.close();
+
3400 }
+
3401
+
3402 auto const credKeylet = credentials::keylet(depositor, owner, credType);
+
3403 {
+
3404 testcase("private XRP vault depositor now authorized");
+
3405 env(credentials::create(depositor, owner, credType));
+
3406 env(credentials::accept(depositor, owner, credType));
+
3407 env.close();
+
3408
+
3409 BEAST_EXPECT(env.le(credKeylet));
+
3410 auto tx = vault.deposit(
+
3411 {.depositor = depositor,
+
3412 .id = keylet.key,
+
3413 .amount = asset(50)});
+
3414 env(tx);
+
3415 env.close();
+
3416 }
+
3417
+
3418 {
+
3419 testcase("private XRP vault can pay shares to depositor");
+
3420 env(pay(owner, depositor, shares(1)));
+
3421 }
+
3422
+
3423 {
+
3424 testcase("private XRP vault cannot pay shares to 3rd party");
+
3425 Json::Value jv;
+
3426 jv[sfAccount] = alice.human();
+
3427 jv[sfTransactionType] = jss::MPTokenAuthorize;
+
3428 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
+
3429 env(jv);
+
3430 env.close();
+
3431
+
3432 env(pay(owner, alice, shares(1)), ter{tecNO_AUTH});
+
3433 }
+
3434 }
+
+
3435
+
3436 void
+
+ +
3438 {
+
3439 using namespace test::jtx;
+
3440
+
3441 testcase("fail pseudo-account allocation");
+
3442 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3443 Account const owner{"owner"};
+
3444 Vault vault{env};
+
3445 env.fund(XRP(1000), owner);
+
3446
+
3447 auto const keylet = keylet::vault(owner.id(), env.seq(owner));
+
3448 for (int i = 0; i < 256; ++i)
+
3449 {
+
3450 AccountID const accountId =
+
3451 ripple::pseudoAccountAddress(*env.current(), keylet.key);
+
3452
+
3453 env(pay(env.master.id(), accountId, XRP(1000)),
+
3454 seq(autofill),
+
3455 fee(autofill),
+
3456 sig(autofill));
+
3457 }
+
3458
+
3459 auto [tx, keylet1] =
+
3460 vault.create({.owner = owner, .asset = xrpIssue()});
+
3461 BEAST_EXPECT(keylet.key == keylet1.key);
+
3462 env(tx, ter{terADDRESS_COLLISION});
+
3463 }
+
+
3464
+
3465 void
+
+ +
3467 {
+
3468 using namespace test::jtx;
+
3469
+
3470 struct Data
+
3471 {
+
3472 Account const& owner;
+
3473 Account const& issuer;
+
3474 Account const& depositor;
+
3475 Account const& vaultAccount;
+
3476 MPTIssue shares;
+
3477 PrettyAsset const& share;
+
3478 Vault& vault;
+
3479 ripple::Keylet keylet;
+
3480 Issue assets;
+
3481 PrettyAsset const& asset;
+
3482 std::function<bool(std::function<bool(SLE&, SLE&)>)> peek;
+
3483 };
+
3484
+
3485 auto testCase = [&, this](
+
3486 std::uint8_t scale,
+
3487 std::function<void(Env & env, Data data)> test) {
+
3488 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3489 Account const owner{"owner"};
+
3490 Account const issuer{"issuer"};
+
3491 Account const depositor{"depositor"};
+
3492 Vault vault{env};
+
3493 env.fund(XRP(1000), issuer, owner, depositor);
+
3494 env(fset(issuer, asfAllowTrustLineClawback));
+
3495 env.close();
+
3496
+
3497 PrettyAsset const asset = issuer["IOU"];
+
3498 env.trust(asset(1000), owner);
+
3499 env.trust(asset(1000), depositor);
+
3500 env(pay(issuer, owner, asset(200)));
+
3501 env(pay(issuer, depositor, asset(200)));
+
3502 env.close();
3503
-
3504 {
-
3505 // round to 6
-
3506 auto tx = d.vault.deposit(
-
3507 {.depositor = d.depositor,
-
3508 .id = d.keylet.key,
-
3509 .amount = STAmount(d.asset, Number(69, -2))});
-
3510 env(tx);
-
3511 env.close();
-
3512 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
-
3513 BEAST_EXPECT(
-
3514 env.balance(d.depositor, d.assets) ==
-
3515 STAmount(d.asset, start - Number(18, -1)));
-
3516 }
-
3517 });
-
3518
-
3519 testCase(1, [&, this](Env& env, Data d) {
-
3520 testcase("Scale deposit exact, truncating from .99");
-
3521
-
3522 auto const start = env.balance(d.depositor, d.assets).number();
-
3523 // round to 12
-
3524 auto tx = d.vault.deposit(
-
3525 {.depositor = d.depositor,
-
3526 .id = d.keylet.key,
-
3527 .amount = STAmount(d.asset, Number(1299, -3))});
-
3528 env(tx);
-
3529 env.close();
-
3530 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
-
3531 BEAST_EXPECT(
-
3532 env.balance(d.depositor, d.assets) ==
-
3533 STAmount(d.asset, start - Number(12, -1)));
-
3534
-
3535 {
-
3536 // round to 6
-
3537 auto tx = d.vault.deposit(
-
3538 {.depositor = d.depositor,
-
3539 .id = d.keylet.key,
-
3540 .amount = STAmount(d.asset, Number(62, -2))});
-
3541 env(tx);
-
3542 env.close();
-
3543 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
-
3544 BEAST_EXPECT(
-
3545 env.balance(d.depositor, d.assets) ==
-
3546 STAmount(d.asset, start - Number(18, -1)));
-
3547 }
-
3548 });
-
3549
-
3550 testCase(1, [&, this](Env& env, Data d) {
-
3551 // initial setup: deposit 100 IOU, receive 1000 shares
-
3552 auto const start = env.balance(d.depositor, d.assets).number();
-
3553 auto tx = d.vault.deposit(
-
3554 {.depositor = d.depositor,
-
3555 .id = d.keylet.key,
-
3556 .amount = STAmount(d.asset, Number(100, 0))});
-
3557 env(tx);
-
3558 env.close();
-
3559 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
-
3560 BEAST_EXPECT(
-
3561 env.balance(d.depositor, d.assets) ==
-
3562 STAmount(d.asset, start - Number(100, 0)));
-
3563 BEAST_EXPECT(
-
3564 env.balance(d.vaultAccount, d.assets) ==
-
3565 STAmount(d.asset, Number(100, 0)));
-
3566 BEAST_EXPECT(
-
3567 env.balance(d.vaultAccount, d.shares) ==
-
3568 STAmount(d.share, Number(-1000, 0)));
-
3569
-
3570 {
-
3571 testcase("Scale redeem exact");
-
3572 // sharesToAssetsWithdraw:
-
3573 // assets = assetsTotal * (shares / sharesTotal)
-
3574 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
-
3575
-
3576 auto const start = env.balance(d.depositor, d.assets).number();
-
3577 auto tx = d.vault.withdraw(
-
3578 {.depositor = d.depositor,
-
3579 .id = d.keylet.key,
-
3580 .amount = STAmount(d.share, Number(100, 0))});
-
3581 env(tx);
-
3582 env.close();
-
3583 BEAST_EXPECT(
-
3584 env.balance(d.depositor, d.shares) == d.share(900));
-
3585 BEAST_EXPECT(
-
3586 env.balance(d.depositor, d.assets) ==
-
3587 STAmount(d.asset, start + Number(10, 0)));
-
3588 BEAST_EXPECT(
-
3589 env.balance(d.vaultAccount, d.assets) ==
-
3590 STAmount(d.asset, Number(90, 0)));
-
3591 BEAST_EXPECT(
-
3592 env.balance(d.vaultAccount, d.shares) ==
-
3593 STAmount(d.share, Number(-900, 0)));
-
3594 }
-
3595
-
3596 {
-
3597 testcase("Scale redeem with rounding");
-
3598 // sharesToAssetsWithdraw:
-
3599 // assets = assetsTotal * (shares / sharesTotal)
-
3600 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
-
3601
-
3602 auto const start = env.balance(d.depositor, d.assets).number();
-
3603 d.peek([](SLE& vault, auto&) -> bool {
-
3604 vault[sfAssetsAvailable] = Number(1);
-
3605 return true;
-
3606 });
-
3607
-
3608 // Note, this transaction fails first (because of above change
-
3609 // in the open ledger) but then succeeds when the ledger is
-
3610 // closed (because a modification like above is not persistent),
-
3611 // which is why the checks below are expected to pass.
-
3612 auto tx = d.vault.withdraw(
-
3613 {.depositor = d.depositor,
-
3614 .id = d.keylet.key,
-
3615 .amount = STAmount(d.share, Number(25, 0))});
-
3616 env(tx, ter{tecINSUFFICIENT_FUNDS});
-
3617 env.close();
-
3618 BEAST_EXPECT(
-
3619 env.balance(d.depositor, d.shares) == d.share(900 - 25));
-
3620 BEAST_EXPECT(
-
3621 env.balance(d.depositor, d.assets) ==
-
3622 STAmount(d.asset, start + Number(25, -1)));
-
3623 BEAST_EXPECT(
-
3624 env.balance(d.vaultAccount, d.assets) ==
-
3625 STAmount(d.asset, Number(900 - 25, -1)));
-
3626 BEAST_EXPECT(
-
3627 env.balance(d.vaultAccount, d.shares) ==
-
3628 STAmount(d.share, -Number(900 - 25, 0)));
-
3629 }
-
3630
-
3631 {
-
3632 testcase("Scale redeem exact");
-
3633 // sharesToAssetsWithdraw:
-
3634 // assets = assetsTotal * (shares / sharesTotal)
-
3635 // assets = 87.5 * 21 / 875 = 87.5 * 0.024 = 2.1
-
3636
-
3637 auto const start = env.balance(d.depositor, d.assets).number();
+
3504 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
3505 tx[sfScale] = scale;
+
3506 env(tx);
+
3507
+
3508 auto const [vaultAccount, issuanceId] =
+
3509 [&env](ripple::Keylet keylet) -> std::tuple<Account, MPTID> {
+
3510 auto const vault = env.le(keylet);
+
3511 return {
+
3512 Account("vault", vault->at(sfAccount)),
+
3513 vault->at(sfShareMPTID)};
+
3514 }(keylet);
+
3515 MPTIssue shares(issuanceId);
+
3516 env.memoize(vaultAccount);
+
3517
+
3518 auto const peek =
+
3519 [=, &env, this](std::function<bool(SLE&, SLE&)> fn) -> bool {
+
3520 return env.app().openLedger().modify(
+
3521 [&](OpenView& view, beast::Journal j) -> bool {
+
3522 Sandbox sb(&view, tapNONE);
+
3523 auto vault = sb.peek(keylet::vault(keylet.key));
+
3524 if (!BEAST_EXPECT(vault != nullptr))
+
3525 return false;
+
3526 auto shares = sb.peek(
+
3527 keylet::mptIssuance(vault->at(sfShareMPTID)));
+
3528 if (!BEAST_EXPECT(shares != nullptr))
+
3529 return false;
+
3530 if (fn(*vault, *shares))
+
3531 {
+
3532 sb.update(vault);
+
3533 sb.update(shares);
+
3534 sb.apply(view);
+
3535 return true;
+
3536 }
+
3537 return false;
+
3538 });
+
3539 };
+
3540
+
3541 test(
+
3542 env,
+
3543 {.owner = owner,
+
3544 .issuer = issuer,
+
3545 .depositor = depositor,
+
3546 .vaultAccount = vaultAccount,
+
3547 .shares = shares,
+
3548 .share = PrettyAsset(shares),
+
3549 .vault = vault,
+
3550 .keylet = keylet,
+
3551 .assets = asset.raw().get<Issue>(),
+
3552 .asset = asset,
+
3553 .peek = peek});
+
3554 };
+
3555
+
3556 testCase(18, [&, this](Env& env, Data d) {
+
3557 testcase("Scale deposit overflow on first deposit");
+
3558 auto tx = d.vault.deposit(
+
3559 {.depositor = d.depositor,
+
3560 .id = d.keylet.key,
+
3561 .amount = d.asset(10)});
+
3562 env(tx, ter{tecPATH_DRY});
+
3563 env.close();
+
3564 });
+
3565
+
3566 testCase(18, [&, this](Env& env, Data d) {
+
3567 testcase("Scale deposit overflow on second deposit");
+
3568
+
3569 {
+
3570 auto tx = d.vault.deposit(
+
3571 {.depositor = d.depositor,
+
3572 .id = d.keylet.key,
+
3573 .amount = d.asset(5)});
+
3574 env(tx);
+
3575 env.close();
+
3576 }
+
3577
+
3578 {
+
3579 auto tx = d.vault.deposit(
+
3580 {.depositor = d.depositor,
+
3581 .id = d.keylet.key,
+
3582 .amount = d.asset(10)});
+
3583 env(tx, ter{tecPATH_DRY});
+
3584 env.close();
+
3585 }
+
3586 });
+
3587
+
3588 testCase(18, [&, this](Env& env, Data d) {
+
3589 testcase("Scale deposit overflow on total shares");
+
3590
+
3591 {
+
3592 auto tx = d.vault.deposit(
+
3593 {.depositor = d.depositor,
+
3594 .id = d.keylet.key,
+
3595 .amount = d.asset(5)});
+
3596 env(tx);
+
3597 env.close();
+
3598 }
+
3599
+
3600 {
+
3601 auto tx = d.vault.deposit(
+
3602 {.depositor = d.depositor,
+
3603 .id = d.keylet.key,
+
3604 .amount = d.asset(5)});
+
3605 env(tx, ter{tecPATH_DRY});
+
3606 env.close();
+
3607 }
+
3608 });
+
3609
+
3610 testCase(1, [&, this](Env& env, Data d) {
+
3611 testcase("Scale deposit exact");
+
3612
+
3613 auto const start = env.balance(d.depositor, d.assets).number();
+
3614 auto tx = d.vault.deposit(
+
3615 {.depositor = d.depositor,
+
3616 .id = d.keylet.key,
+
3617 .amount = d.asset(1)});
+
3618 env(tx);
+
3619 env.close();
+
3620 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
+
3621 BEAST_EXPECT(
+
3622 env.balance(d.depositor, d.assets) ==
+
3623 STAmount(d.asset, start - 1));
+
3624 });
+
3625
+
3626 testCase(1, [&, this](Env& env, Data d) {
+
3627 testcase("Scale deposit insignificant amount");
+
3628
+
3629 auto tx = d.vault.deposit(
+
3630 {.depositor = d.depositor,
+
3631 .id = d.keylet.key,
+
3632 .amount = STAmount(d.asset, Number(9, -2))});
+
3633 env(tx, ter{tecPRECISION_LOSS});
+
3634 });
+
3635
+
3636 testCase(1, [&, this](Env& env, Data d) {
+
3637 testcase("Scale deposit exact, using full precision");
3638
-
3639 tx = d.vault.withdraw(
-
3640 {.depositor = d.depositor,
-
3641 .id = d.keylet.key,
-
3642 .amount = STAmount(d.share, Number(21, 0))});
-
3643 env(tx);
-
3644 env.close();
-
3645 BEAST_EXPECT(
-
3646 env.balance(d.depositor, d.shares) == d.share(875 - 21));
-
3647 BEAST_EXPECT(
-
3648 env.balance(d.depositor, d.assets) ==
-
3649 STAmount(d.asset, start + Number(21, -1)));
-
3650 BEAST_EXPECT(
-
3651 env.balance(d.vaultAccount, d.assets) ==
-
3652 STAmount(d.asset, Number(875 - 21, -1)));
-
3653 BEAST_EXPECT(
-
3654 env.balance(d.vaultAccount, d.shares) ==
-
3655 STAmount(d.share, -Number(875 - 21, 0)));
-
3656 }
-
3657
+
3639 auto const start = env.balance(d.depositor, d.assets).number();
+
3640 auto tx = d.vault.deposit(
+
3641 {.depositor = d.depositor,
+
3642 .id = d.keylet.key,
+
3643 .amount = STAmount(d.asset, Number(15, -1))});
+
3644 env(tx);
+
3645 env.close();
+
3646 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
+
3647 BEAST_EXPECT(
+
3648 env.balance(d.depositor, d.assets) ==
+
3649 STAmount(d.asset, start - Number(15, -1)));
+
3650 });
+
3651
+
3652 testCase(1, [&, this](Env& env, Data d) {
+
3653 testcase("Scale deposit exact, truncating from .5");
+
3654
+
3655 auto const start = env.balance(d.depositor, d.assets).number();
+
3656 // Each of the cases below will transfer exactly 1.2 IOU to the
+
3657 // vault and receive 12 shares in exchange
3658 {
-
3659 testcase("Scale redeem rest");
-
3660 auto const rest = env.balance(d.depositor, d.shares).number();
-
3661
-
3662 tx = d.vault.withdraw(
-
3663 {.depositor = d.depositor,
-
3664 .id = d.keylet.key,
-
3665 .amount = STAmount(d.share, rest)});
-
3666 env(tx);
-
3667 env.close();
-
3668 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
-
3669 BEAST_EXPECT(
-
3670 env.balance(d.vaultAccount, d.assets).number() == 0);
-
3671 BEAST_EXPECT(
-
3672 env.balance(d.vaultAccount, d.shares).number() == 0);
-
3673 }
-
3674 });
-
3675
-
3676 testCase(18, [&, this](Env& env, Data d) {
-
3677 testcase("Scale withdraw overflow");
-
3678
-
3679 {
-
3680 auto tx = d.vault.deposit(
-
3681 {.depositor = d.depositor,
-
3682 .id = d.keylet.key,
-
3683 .amount = d.asset(5)});
-
3684 env(tx);
-
3685 env.close();
-
3686 }
-
3687
-
3688 {
-
3689 auto tx = d.vault.withdraw(
-
3690 {.depositor = d.depositor,
-
3691 .id = d.keylet.key,
-
3692 .amount = STAmount(d.asset, Number(10, 0))});
-
3693 env(tx, ter{tecPATH_DRY});
-
3694 env.close();
+
3659 auto tx = d.vault.deposit(
+
3660 {.depositor = d.depositor,
+
3661 .id = d.keylet.key,
+
3662 .amount = STAmount(d.asset, Number(125, -2))});
+
3663 env(tx);
+
3664 env.close();
+
3665 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
+
3666 BEAST_EXPECT(
+
3667 env.balance(d.depositor, d.assets) ==
+
3668 STAmount(d.asset, start - Number(12, -1)));
+
3669 }
+
3670
+
3671 {
+
3672 auto tx = d.vault.deposit(
+
3673 {.depositor = d.depositor,
+
3674 .id = d.keylet.key,
+
3675 .amount = STAmount(d.asset, Number(1201, -3))});
+
3676 env(tx);
+
3677 env.close();
+
3678 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
+
3679 BEAST_EXPECT(
+
3680 env.balance(d.depositor, d.assets) ==
+
3681 STAmount(d.asset, start - Number(24, -1)));
+
3682 }
+
3683
+
3684 {
+
3685 auto tx = d.vault.deposit(
+
3686 {.depositor = d.depositor,
+
3687 .id = d.keylet.key,
+
3688 .amount = STAmount(d.asset, Number(1299, -3))});
+
3689 env(tx);
+
3690 env.close();
+
3691 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
+
3692 BEAST_EXPECT(
+
3693 env.balance(d.depositor, d.assets) ==
+
3694 STAmount(d.asset, start - Number(36, -1)));
3695 }
3696 });
3697
3698 testCase(1, [&, this](Env& env, Data d) {
-
3699 // initial setup: deposit 100 IOU, receive 1000 shares
-
3700 auto const start = env.balance(d.depositor, d.assets).number();
-
3701 auto tx = d.vault.deposit(
-
3702 {.depositor = d.depositor,
-
3703 .id = d.keylet.key,
-
3704 .amount = STAmount(d.asset, Number(100, 0))});
-
3705 env(tx);
-
3706 env.close();
-
3707 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
-
3708 BEAST_EXPECT(
-
3709 env.balance(d.depositor, d.assets) ==
-
3710 STAmount(d.asset, start - Number(100, 0)));
-
3711 BEAST_EXPECT(
-
3712 env.balance(d.vaultAccount, d.assets) ==
-
3713 STAmount(d.asset, Number(100, 0)));
-
3714 BEAST_EXPECT(
-
3715 env.balance(d.vaultAccount, d.shares) ==
-
3716 STAmount(d.share, Number(-1000, 0)));
-
3717
-
3718 {
-
3719 testcase("Scale withdraw exact");
-
3720 // assetsToSharesWithdraw:
-
3721 // shares = sharesTotal * (assets / assetsTotal)
-
3722 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
-
3723 // sharesToAssetsWithdraw:
-
3724 // assets = assetsTotal * (shares / sharesTotal)
-
3725 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
-
3726
-
3727 auto const start = env.balance(d.depositor, d.assets).number();
-
3728 auto tx = d.vault.withdraw(
-
3729 {.depositor = d.depositor,
-
3730 .id = d.keylet.key,
-
3731 .amount = STAmount(d.asset, Number(10, 0))});
-
3732 env(tx);
-
3733 env.close();
-
3734 BEAST_EXPECT(
-
3735 env.balance(d.depositor, d.shares) == d.share(900));
-
3736 BEAST_EXPECT(
-
3737 env.balance(d.depositor, d.assets) ==
-
3738 STAmount(d.asset, start + Number(10, 0)));
-
3739 BEAST_EXPECT(
-
3740 env.balance(d.vaultAccount, d.assets) ==
-
3741 STAmount(d.asset, Number(90, 0)));
-
3742 BEAST_EXPECT(
-
3743 env.balance(d.vaultAccount, d.shares) ==
-
3744 STAmount(d.share, Number(-900, 0)));
-
3745 }
-
3746
-
3747 {
-
3748 testcase("Scale withdraw insignificant amount");
-
3749 auto tx = d.vault.withdraw(
-
3750 {.depositor = d.depositor,
-
3751 .id = d.keylet.key,
-
3752 .amount = STAmount(d.asset, Number(4, -2))});
-
3753 env(tx, ter{tecPRECISION_LOSS});
-
3754 }
-
3755
-
3756 {
-
3757 testcase("Scale withdraw with rounding assets");
-
3758 // assetsToSharesWithdraw:
-
3759 // shares = sharesTotal * (assets / assetsTotal)
-
3760 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
-
3761 // sharesToAssetsWithdraw:
-
3762 // assets = assetsTotal * (shares / sharesTotal)
-
3763 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
-
3764
-
3765 auto const start = env.balance(d.depositor, d.assets).number();
-
3766 d.peek([](SLE& vault, auto&) -> bool {
-
3767 vault[sfAssetsAvailable] = Number(1);
-
3768 return true;
-
3769 });
-
3770
-
3771 // Note, this transaction fails first (because of above change
-
3772 // in the open ledger) but then succeeds when the ledger is
-
3773 // closed (because a modification like above is not persistent),
-
3774 // which is why the checks below are expected to pass.
-
3775 auto tx = d.vault.withdraw(
-
3776 {.depositor = d.depositor,
-
3777 .id = d.keylet.key,
-
3778 .amount = STAmount(d.asset, Number(25, -1))});
-
3779 env(tx, ter{tecINSUFFICIENT_FUNDS});
-
3780 env.close();
-
3781 BEAST_EXPECT(
-
3782 env.balance(d.depositor, d.shares) == d.share(900 - 25));
-
3783 BEAST_EXPECT(
-
3784 env.balance(d.depositor, d.assets) ==
-
3785 STAmount(d.asset, start + Number(25, -1)));
-
3786 BEAST_EXPECT(
-
3787 env.balance(d.vaultAccount, d.assets) ==
-
3788 STAmount(d.asset, Number(900 - 25, -1)));
-
3789 BEAST_EXPECT(
-
3790 env.balance(d.vaultAccount, d.shares) ==
-
3791 STAmount(d.share, -Number(900 - 25, 0)));
-
3792 }
-
3793
-
3794 {
-
3795 testcase("Scale withdraw with rounding shares up");
-
3796 // assetsToSharesWithdraw:
-
3797 // shares = sharesTotal * (assets / assetsTotal)
-
3798 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
-
3799 // sharesToAssetsWithdraw:
-
3800 // assets = assetsTotal * (shares / sharesTotal)
-
3801 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
-
3802
-
3803 auto const start = env.balance(d.depositor, d.assets).number();
-
3804 auto tx = d.vault.withdraw(
-
3805 {.depositor = d.depositor,
-
3806 .id = d.keylet.key,
-
3807 .amount = STAmount(d.asset, Number(375, -2))});
-
3808 env(tx);
-
3809 env.close();
-
3810 BEAST_EXPECT(
-
3811 env.balance(d.depositor, d.shares) == d.share(875 - 38));
-
3812 BEAST_EXPECT(
-
3813 env.balance(d.depositor, d.assets) ==
-
3814 STAmount(d.asset, start + Number(38, -1)));
-
3815 BEAST_EXPECT(
-
3816 env.balance(d.vaultAccount, d.assets) ==
-
3817 STAmount(d.asset, Number(875 - 38, -1)));
-
3818 BEAST_EXPECT(
-
3819 env.balance(d.vaultAccount, d.shares) ==
-
3820 STAmount(d.share, -Number(875 - 38, 0)));
-
3821 }
-
3822
-
3823 {
-
3824 testcase("Scale withdraw with rounding shares down");
-
3825 // assetsToSharesWithdraw:
-
3826 // shares = sharesTotal * (assets / assetsTotal)
-
3827 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
-
3828 // sharesToAssetsWithdraw:
-
3829 // assets = assetsTotal * (shares / sharesTotal)
-
3830 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
-
3831
-
3832 auto const start = env.balance(d.depositor, d.assets).number();
-
3833 auto tx = d.vault.withdraw(
-
3834 {.depositor = d.depositor,
-
3835 .id = d.keylet.key,
-
3836 .amount = STAmount(d.asset, Number(372, -2))});
-
3837 env(tx);
-
3838 env.close();
-
3839 BEAST_EXPECT(
-
3840 env.balance(d.depositor, d.shares) == d.share(837 - 37));
-
3841 BEAST_EXPECT(
-
3842 env.balance(d.depositor, d.assets) ==
-
3843 STAmount(d.asset, start + Number(37, -1)));
-
3844 BEAST_EXPECT(
-
3845 env.balance(d.vaultAccount, d.assets) ==
-
3846 STAmount(d.asset, Number(837 - 37, -1)));
-
3847 BEAST_EXPECT(
-
3848 env.balance(d.vaultAccount, d.shares) ==
-
3849 STAmount(d.share, -Number(837 - 37, 0)));
-
3850 }
-
3851
-
3852 {
-
3853 testcase("Scale withdraw tiny amount");
-
3854
-
3855 auto const start = env.balance(d.depositor, d.assets).number();
-
3856 auto tx = d.vault.withdraw(
-
3857 {.depositor = d.depositor,
-
3858 .id = d.keylet.key,
-
3859 .amount = STAmount(d.asset, Number(9, -2))});
-
3860 env(tx);
-
3861 env.close();
-
3862 BEAST_EXPECT(
-
3863 env.balance(d.depositor, d.shares) == d.share(800 - 1));
-
3864 BEAST_EXPECT(
-
3865 env.balance(d.depositor, d.assets) ==
-
3866 STAmount(d.asset, start + Number(1, -1)));
-
3867 BEAST_EXPECT(
-
3868 env.balance(d.vaultAccount, d.assets) ==
-
3869 STAmount(d.asset, Number(800 - 1, -1)));
-
3870 BEAST_EXPECT(
-
3871 env.balance(d.vaultAccount, d.shares) ==
-
3872 STAmount(d.share, -Number(800 - 1, 0)));
-
3873 }
-
3874
-
3875 {
-
3876 testcase("Scale withdraw rest");
-
3877 auto const rest =
-
3878 env.balance(d.vaultAccount, d.assets).number();
-
3879
-
3880 tx = d.vault.withdraw(
-
3881 {.depositor = d.depositor,
-
3882 .id = d.keylet.key,
-
3883 .amount = STAmount(d.asset, rest)});
-
3884 env(tx);
-
3885 env.close();
-
3886 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
-
3887 BEAST_EXPECT(
-
3888 env.balance(d.vaultAccount, d.assets).number() == 0);
-
3889 BEAST_EXPECT(
-
3890 env.balance(d.vaultAccount, d.shares).number() == 0);
-
3891 }
-
3892 });
-
3893
-
3894 testCase(18, [&, this](Env& env, Data d) {
-
3895 testcase("Scale clawback overflow");
-
3896
-
3897 {
-
3898 auto tx = d.vault.deposit(
-
3899 {.depositor = d.depositor,
-
3900 .id = d.keylet.key,
-
3901 .amount = d.asset(5)});
-
3902 env(tx);
-
3903 env.close();
-
3904 }
-
3905
-
3906 {
-
3907 auto tx = d.vault.clawback(
-
3908 {.issuer = d.issuer,
-
3909 .id = d.keylet.key,
-
3910 .holder = d.depositor,
-
3911 .amount = STAmount(d.asset, Number(10, 0))});
-
3912 env(tx, ter{tecPATH_DRY});
-
3913 env.close();
-
3914 }
-
3915 });
-
3916
-
3917 testCase(1, [&, this](Env& env, Data d) {
-
3918 // initial setup: deposit 100 IOU, receive 1000 shares
-
3919 auto const start = env.balance(d.depositor, d.assets).number();
-
3920 auto tx = d.vault.deposit(
-
3921 {.depositor = d.depositor,
-
3922 .id = d.keylet.key,
-
3923 .amount = STAmount(d.asset, Number(100, 0))});
-
3924 env(tx);
-
3925 env.close();
-
3926 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
-
3927 BEAST_EXPECT(
-
3928 env.balance(d.depositor, d.assets) ==
-
3929 STAmount(d.asset, start - Number(100, 0)));
-
3930 BEAST_EXPECT(
-
3931 env.balance(d.vaultAccount, d.assets) ==
-
3932 STAmount(d.asset, Number(100, 0)));
-
3933 BEAST_EXPECT(
-
3934 env.balance(d.vaultAccount, d.shares) ==
-
3935 STAmount(d.share, -Number(1000, 0)));
-
3936 {
-
3937 testcase("Scale clawback exact");
-
3938 // assetsToSharesWithdraw:
-
3939 // shares = sharesTotal * (assets / assetsTotal)
-
3940 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
-
3941 // sharesToAssetsWithdraw:
-
3942 // assets = assetsTotal * (shares / sharesTotal)
-
3943 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
-
3944
-
3945 auto const start = env.balance(d.depositor, d.assets).number();
-
3946 auto tx = d.vault.clawback(
-
3947 {.issuer = d.issuer,
-
3948 .id = d.keylet.key,
-
3949 .holder = d.depositor,
-
3950 .amount = STAmount(d.asset, Number(10, 0))});
-
3951 env(tx);
-
3952 env.close();
-
3953 BEAST_EXPECT(
-
3954 env.balance(d.depositor, d.shares) == d.share(900));
-
3955 BEAST_EXPECT(
-
3956 env.balance(d.depositor, d.assets) ==
-
3957 STAmount(d.asset, start));
-
3958 BEAST_EXPECT(
-
3959 env.balance(d.vaultAccount, d.assets) ==
-
3960 STAmount(d.asset, Number(90, 0)));
-
3961 BEAST_EXPECT(
-
3962 env.balance(d.vaultAccount, d.shares) ==
-
3963 STAmount(d.share, -Number(900, 0)));
+
3699 testcase("Scale deposit exact, truncating from .01");
+
3700
+
3701 auto const start = env.balance(d.depositor, d.assets).number();
+
3702 // round to 12
+
3703 auto tx = d.vault.deposit(
+
3704 {.depositor = d.depositor,
+
3705 .id = d.keylet.key,
+
3706 .amount = STAmount(d.asset, Number(1201, -3))});
+
3707 env(tx);
+
3708 env.close();
+
3709 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
+
3710 BEAST_EXPECT(
+
3711 env.balance(d.depositor, d.assets) ==
+
3712 STAmount(d.asset, start - Number(12, -1)));
+
3713
+
3714 {
+
3715 // round to 6
+
3716 auto tx = d.vault.deposit(
+
3717 {.depositor = d.depositor,
+
3718 .id = d.keylet.key,
+
3719 .amount = STAmount(d.asset, Number(69, -2))});
+
3720 env(tx);
+
3721 env.close();
+
3722 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
+
3723 BEAST_EXPECT(
+
3724 env.balance(d.depositor, d.assets) ==
+
3725 STAmount(d.asset, start - Number(18, -1)));
+
3726 }
+
3727 });
+
3728
+
3729 testCase(1, [&, this](Env& env, Data d) {
+
3730 testcase("Scale deposit exact, truncating from .99");
+
3731
+
3732 auto const start = env.balance(d.depositor, d.assets).number();
+
3733 // round to 12
+
3734 auto tx = d.vault.deposit(
+
3735 {.depositor = d.depositor,
+
3736 .id = d.keylet.key,
+
3737 .amount = STAmount(d.asset, Number(1299, -3))});
+
3738 env(tx);
+
3739 env.close();
+
3740 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
+
3741 BEAST_EXPECT(
+
3742 env.balance(d.depositor, d.assets) ==
+
3743 STAmount(d.asset, start - Number(12, -1)));
+
3744
+
3745 {
+
3746 // round to 6
+
3747 auto tx = d.vault.deposit(
+
3748 {.depositor = d.depositor,
+
3749 .id = d.keylet.key,
+
3750 .amount = STAmount(d.asset, Number(62, -2))});
+
3751 env(tx);
+
3752 env.close();
+
3753 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
+
3754 BEAST_EXPECT(
+
3755 env.balance(d.depositor, d.assets) ==
+
3756 STAmount(d.asset, start - Number(18, -1)));
+
3757 }
+
3758 });
+
3759
+
3760 testCase(1, [&, this](Env& env, Data d) {
+
3761 // initial setup: deposit 100 IOU, receive 1000 shares
+
3762 auto const start = env.balance(d.depositor, d.assets).number();
+
3763 auto tx = d.vault.deposit(
+
3764 {.depositor = d.depositor,
+
3765 .id = d.keylet.key,
+
3766 .amount = STAmount(d.asset, Number(100, 0))});
+
3767 env(tx);
+
3768 env.close();
+
3769 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
+
3770 BEAST_EXPECT(
+
3771 env.balance(d.depositor, d.assets) ==
+
3772 STAmount(d.asset, start - Number(100, 0)));
+
3773 BEAST_EXPECT(
+
3774 env.balance(d.vaultAccount, d.assets) ==
+
3775 STAmount(d.asset, Number(100, 0)));
+
3776 BEAST_EXPECT(
+
3777 env.balance(d.vaultAccount, d.shares) ==
+
3778 STAmount(d.share, Number(-1000, 0)));
+
3779
+
3780 {
+
3781 testcase("Scale redeem exact");
+
3782 // sharesToAssetsWithdraw:
+
3783 // assets = assetsTotal * (shares / sharesTotal)
+
3784 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
+
3785
+
3786 auto const start = env.balance(d.depositor, d.assets).number();
+
3787 auto tx = d.vault.withdraw(
+
3788 {.depositor = d.depositor,
+
3789 .id = d.keylet.key,
+
3790 .amount = STAmount(d.share, Number(100, 0))});
+
3791 env(tx);
+
3792 env.close();
+
3793 BEAST_EXPECT(
+
3794 env.balance(d.depositor, d.shares) == d.share(900));
+
3795 BEAST_EXPECT(
+
3796 env.balance(d.depositor, d.assets) ==
+
3797 STAmount(d.asset, start + Number(10, 0)));
+
3798 BEAST_EXPECT(
+
3799 env.balance(d.vaultAccount, d.assets) ==
+
3800 STAmount(d.asset, Number(90, 0)));
+
3801 BEAST_EXPECT(
+
3802 env.balance(d.vaultAccount, d.shares) ==
+
3803 STAmount(d.share, Number(-900, 0)));
+
3804 }
+
3805
+
3806 {
+
3807 testcase("Scale redeem with rounding");
+
3808 // sharesToAssetsWithdraw:
+
3809 // assets = assetsTotal * (shares / sharesTotal)
+
3810 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
+
3811
+
3812 auto const start = env.balance(d.depositor, d.assets).number();
+
3813 d.peek([](SLE& vault, auto&) -> bool {
+
3814 vault[sfAssetsAvailable] = Number(1);
+
3815 return true;
+
3816 });
+
3817
+
3818 // Note, this transaction fails first (because of above change
+
3819 // in the open ledger) but then succeeds when the ledger is
+
3820 // closed (because a modification like above is not persistent),
+
3821 // which is why the checks below are expected to pass.
+
3822 auto tx = d.vault.withdraw(
+
3823 {.depositor = d.depositor,
+
3824 .id = d.keylet.key,
+
3825 .amount = STAmount(d.share, Number(25, 0))});
+
3826 env(tx, ter{tecINSUFFICIENT_FUNDS});
+
3827 env.close();
+
3828 BEAST_EXPECT(
+
3829 env.balance(d.depositor, d.shares) == d.share(900 - 25));
+
3830 BEAST_EXPECT(
+
3831 env.balance(d.depositor, d.assets) ==
+
3832 STAmount(d.asset, start + Number(25, -1)));
+
3833 BEAST_EXPECT(
+
3834 env.balance(d.vaultAccount, d.assets) ==
+
3835 STAmount(d.asset, Number(900 - 25, -1)));
+
3836 BEAST_EXPECT(
+
3837 env.balance(d.vaultAccount, d.shares) ==
+
3838 STAmount(d.share, -Number(900 - 25, 0)));
+
3839 }
+
3840
+
3841 {
+
3842 testcase("Scale redeem exact");
+
3843 // sharesToAssetsWithdraw:
+
3844 // assets = assetsTotal * (shares / sharesTotal)
+
3845 // assets = 87.5 * 21 / 875 = 87.5 * 0.024 = 2.1
+
3846
+
3847 auto const start = env.balance(d.depositor, d.assets).number();
+
3848
+
3849 tx = d.vault.withdraw(
+
3850 {.depositor = d.depositor,
+
3851 .id = d.keylet.key,
+
3852 .amount = STAmount(d.share, Number(21, 0))});
+
3853 env(tx);
+
3854 env.close();
+
3855 BEAST_EXPECT(
+
3856 env.balance(d.depositor, d.shares) == d.share(875 - 21));
+
3857 BEAST_EXPECT(
+
3858 env.balance(d.depositor, d.assets) ==
+
3859 STAmount(d.asset, start + Number(21, -1)));
+
3860 BEAST_EXPECT(
+
3861 env.balance(d.vaultAccount, d.assets) ==
+
3862 STAmount(d.asset, Number(875 - 21, -1)));
+
3863 BEAST_EXPECT(
+
3864 env.balance(d.vaultAccount, d.shares) ==
+
3865 STAmount(d.share, -Number(875 - 21, 0)));
+
3866 }
+
3867
+
3868 {
+
3869 testcase("Scale redeem rest");
+
3870 auto const rest = env.balance(d.depositor, d.shares).number();
+
3871
+
3872 tx = d.vault.withdraw(
+
3873 {.depositor = d.depositor,
+
3874 .id = d.keylet.key,
+
3875 .amount = STAmount(d.share, rest)});
+
3876 env(tx);
+
3877 env.close();
+
3878 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
+
3879 BEAST_EXPECT(
+
3880 env.balance(d.vaultAccount, d.assets).number() == 0);
+
3881 BEAST_EXPECT(
+
3882 env.balance(d.vaultAccount, d.shares).number() == 0);
+
3883 }
+
3884 });
+
3885
+
3886 testCase(18, [&, this](Env& env, Data d) {
+
3887 testcase("Scale withdraw overflow");
+
3888
+
3889 {
+
3890 auto tx = d.vault.deposit(
+
3891 {.depositor = d.depositor,
+
3892 .id = d.keylet.key,
+
3893 .amount = d.asset(5)});
+
3894 env(tx);
+
3895 env.close();
+
3896 }
+
3897
+
3898 {
+
3899 auto tx = d.vault.withdraw(
+
3900 {.depositor = d.depositor,
+
3901 .id = d.keylet.key,
+
3902 .amount = STAmount(d.asset, Number(10, 0))});
+
3903 env(tx, ter{tecPATH_DRY});
+
3904 env.close();
+
3905 }
+
3906 });
+
3907
+
3908 testCase(1, [&, this](Env& env, Data d) {
+
3909 // initial setup: deposit 100 IOU, receive 1000 shares
+
3910 auto const start = env.balance(d.depositor, d.assets).number();
+
3911 auto tx = d.vault.deposit(
+
3912 {.depositor = d.depositor,
+
3913 .id = d.keylet.key,
+
3914 .amount = STAmount(d.asset, Number(100, 0))});
+
3915 env(tx);
+
3916 env.close();
+
3917 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
+
3918 BEAST_EXPECT(
+
3919 env.balance(d.depositor, d.assets) ==
+
3920 STAmount(d.asset, start - Number(100, 0)));
+
3921 BEAST_EXPECT(
+
3922 env.balance(d.vaultAccount, d.assets) ==
+
3923 STAmount(d.asset, Number(100, 0)));
+
3924 BEAST_EXPECT(
+
3925 env.balance(d.vaultAccount, d.shares) ==
+
3926 STAmount(d.share, Number(-1000, 0)));
+
3927
+
3928 {
+
3929 testcase("Scale withdraw exact");
+
3930 // assetsToSharesWithdraw:
+
3931 // shares = sharesTotal * (assets / assetsTotal)
+
3932 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
+
3933 // sharesToAssetsWithdraw:
+
3934 // assets = assetsTotal * (shares / sharesTotal)
+
3935 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
+
3936
+
3937 auto const start = env.balance(d.depositor, d.assets).number();
+
3938 auto tx = d.vault.withdraw(
+
3939 {.depositor = d.depositor,
+
3940 .id = d.keylet.key,
+
3941 .amount = STAmount(d.asset, Number(10, 0))});
+
3942 env(tx);
+
3943 env.close();
+
3944 BEAST_EXPECT(
+
3945 env.balance(d.depositor, d.shares) == d.share(900));
+
3946 BEAST_EXPECT(
+
3947 env.balance(d.depositor, d.assets) ==
+
3948 STAmount(d.asset, start + Number(10, 0)));
+
3949 BEAST_EXPECT(
+
3950 env.balance(d.vaultAccount, d.assets) ==
+
3951 STAmount(d.asset, Number(90, 0)));
+
3952 BEAST_EXPECT(
+
3953 env.balance(d.vaultAccount, d.shares) ==
+
3954 STAmount(d.share, Number(-900, 0)));
+
3955 }
+
3956
+
3957 {
+
3958 testcase("Scale withdraw insignificant amount");
+
3959 auto tx = d.vault.withdraw(
+
3960 {.depositor = d.depositor,
+
3961 .id = d.keylet.key,
+
3962 .amount = STAmount(d.asset, Number(4, -2))});
+
3963 env(tx, ter{tecPRECISION_LOSS});
3964 }
3965
3966 {
-
3967 testcase("Scale clawback insignificant amount");
-
3968 auto tx = d.vault.clawback(
-
3969 {.issuer = d.issuer,
-
3970 .id = d.keylet.key,
-
3971 .holder = d.depositor,
-
3972 .amount = STAmount(d.asset, Number(4, -2))});
-
3973 env(tx, ter{tecPRECISION_LOSS});
-
3974 }
-
3975
-
3976 {
-
3977 testcase("Scale clawback with rounding assets");
-
3978 // assetsToSharesWithdraw:
-
3979 // shares = sharesTotal * (assets / assetsTotal)
-
3980 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
-
3981 // sharesToAssetsWithdraw:
-
3982 // assets = assetsTotal * (shares / sharesTotal)
-
3983 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
-
3984
-
3985 auto const start = env.balance(d.depositor, d.assets).number();
-
3986 auto tx = d.vault.clawback(
-
3987 {.issuer = d.issuer,
-
3988 .id = d.keylet.key,
-
3989 .holder = d.depositor,
-
3990 .amount = STAmount(d.asset, Number(25, -1))});
-
3991 env(tx);
-
3992 env.close();
+
3967 testcase("Scale withdraw with rounding assets");
+
3968 // assetsToSharesWithdraw:
+
3969 // shares = sharesTotal * (assets / assetsTotal)
+
3970 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
+
3971 // sharesToAssetsWithdraw:
+
3972 // assets = assetsTotal * (shares / sharesTotal)
+
3973 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
+
3974
+
3975 auto const start = env.balance(d.depositor, d.assets).number();
+
3976 d.peek([](SLE& vault, auto&) -> bool {
+
3977 vault[sfAssetsAvailable] = Number(1);
+
3978 return true;
+
3979 });
+
3980
+
3981 // Note, this transaction fails first (because of above change
+
3982 // in the open ledger) but then succeeds when the ledger is
+
3983 // closed (because a modification like above is not persistent),
+
3984 // which is why the checks below are expected to pass.
+
3985 auto tx = d.vault.withdraw(
+
3986 {.depositor = d.depositor,
+
3987 .id = d.keylet.key,
+
3988 .amount = STAmount(d.asset, Number(25, -1))});
+
3989 env(tx, ter{tecINSUFFICIENT_FUNDS});
+
3990 env.close();
+
3991 BEAST_EXPECT(
+
3992 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3993 BEAST_EXPECT(
-
3994 env.balance(d.depositor, d.shares) == d.share(900 - 25));
-
3995 BEAST_EXPECT(
-
3996 env.balance(d.depositor, d.assets) ==
-
3997 STAmount(d.asset, start));
-
3998 BEAST_EXPECT(
-
3999 env.balance(d.vaultAccount, d.assets) ==
-
4000 STAmount(d.asset, Number(900 - 25, -1)));
-
4001 BEAST_EXPECT(
-
4002 env.balance(d.vaultAccount, d.shares) ==
-
4003 STAmount(d.share, -Number(900 - 25, 0)));
-
4004 }
-
4005
-
4006 {
-
4007 testcase("Scale clawback with rounding shares up");
-
4008 // assetsToSharesWithdraw:
-
4009 // shares = sharesTotal * (assets / assetsTotal)
-
4010 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
-
4011 // sharesToAssetsWithdraw:
-
4012 // assets = assetsTotal * (shares / sharesTotal)
-
4013 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
-
4014
-
4015 auto const start = env.balance(d.depositor, d.assets).number();
-
4016 auto tx = d.vault.clawback(
-
4017 {.issuer = d.issuer,
-
4018 .id = d.keylet.key,
-
4019 .holder = d.depositor,
-
4020 .amount = STAmount(d.asset, Number(375, -2))});
-
4021 env(tx);
-
4022 env.close();
-
4023 BEAST_EXPECT(
-
4024 env.balance(d.depositor, d.shares) == d.share(875 - 38));
+
3994 env.balance(d.depositor, d.assets) ==
+
3995 STAmount(d.asset, start + Number(25, -1)));
+
3996 BEAST_EXPECT(
+
3997 env.balance(d.vaultAccount, d.assets) ==
+
3998 STAmount(d.asset, Number(900 - 25, -1)));
+
3999 BEAST_EXPECT(
+
4000 env.balance(d.vaultAccount, d.shares) ==
+
4001 STAmount(d.share, -Number(900 - 25, 0)));
+
4002 }
+
4003
+
4004 {
+
4005 testcase("Scale withdraw with rounding shares up");
+
4006 // assetsToSharesWithdraw:
+
4007 // shares = sharesTotal * (assets / assetsTotal)
+
4008 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
+
4009 // sharesToAssetsWithdraw:
+
4010 // assets = assetsTotal * (shares / sharesTotal)
+
4011 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
+
4012
+
4013 auto const start = env.balance(d.depositor, d.assets).number();
+
4014 auto tx = d.vault.withdraw(
+
4015 {.depositor = d.depositor,
+
4016 .id = d.keylet.key,
+
4017 .amount = STAmount(d.asset, Number(375, -2))});
+
4018 env(tx);
+
4019 env.close();
+
4020 BEAST_EXPECT(
+
4021 env.balance(d.depositor, d.shares) == d.share(875 - 38));
+
4022 BEAST_EXPECT(
+
4023 env.balance(d.depositor, d.assets) ==
+
4024 STAmount(d.asset, start + Number(38, -1)));
4025 BEAST_EXPECT(
-
4026 env.balance(d.depositor, d.assets) ==
-
4027 STAmount(d.asset, start));
+
4026 env.balance(d.vaultAccount, d.assets) ==
+
4027 STAmount(d.asset, Number(875 - 38, -1)));
4028 BEAST_EXPECT(
-
4029 env.balance(d.vaultAccount, d.assets) ==
-
4030 STAmount(d.asset, Number(875 - 38, -1)));
-
4031 BEAST_EXPECT(
-
4032 env.balance(d.vaultAccount, d.shares) ==
-
4033 STAmount(d.share, -Number(875 - 38, 0)));
-
4034 }
-
4035
-
4036 {
-
4037 testcase("Scale clawback with rounding shares down");
-
4038 // assetsToSharesWithdraw:
-
4039 // shares = sharesTotal * (assets / assetsTotal)
-
4040 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
-
4041 // sharesToAssetsWithdraw:
-
4042 // assets = assetsTotal * (shares / sharesTotal)
-
4043 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
-
4044
-
4045 auto const start = env.balance(d.depositor, d.assets).number();
-
4046 auto tx = d.vault.clawback(
-
4047 {.issuer = d.issuer,
-
4048 .id = d.keylet.key,
-
4049 .holder = d.depositor,
-
4050 .amount = STAmount(d.asset, Number(372, -2))});
-
4051 env(tx);
-
4052 env.close();
-
4053 BEAST_EXPECT(
-
4054 env.balance(d.depositor, d.shares) == d.share(837 - 37));
-
4055 BEAST_EXPECT(
-
4056 env.balance(d.depositor, d.assets) ==
-
4057 STAmount(d.asset, start));
-
4058 BEAST_EXPECT(
-
4059 env.balance(d.vaultAccount, d.assets) ==
-
4060 STAmount(d.asset, Number(837 - 37, -1)));
-
4061 BEAST_EXPECT(
-
4062 env.balance(d.vaultAccount, d.shares) ==
-
4063 STAmount(d.share, -Number(837 - 37, 0)));
-
4064 }
-
4065
-
4066 {
-
4067 testcase("Scale clawback tiny amount");
-
4068
-
4069 auto const start = env.balance(d.depositor, d.assets).number();
-
4070 auto tx = d.vault.clawback(
-
4071 {.issuer = d.issuer,
-
4072 .id = d.keylet.key,
-
4073 .holder = d.depositor,
-
4074 .amount = STAmount(d.asset, Number(9, -2))});
-
4075 env(tx);
-
4076 env.close();
+
4029 env.balance(d.vaultAccount, d.shares) ==
+
4030 STAmount(d.share, -Number(875 - 38, 0)));
+
4031 }
+
4032
+
4033 {
+
4034 testcase("Scale withdraw with rounding shares down");
+
4035 // assetsToSharesWithdraw:
+
4036 // shares = sharesTotal * (assets / assetsTotal)
+
4037 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
+
4038 // sharesToAssetsWithdraw:
+
4039 // assets = assetsTotal * (shares / sharesTotal)
+
4040 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
+
4041
+
4042 auto const start = env.balance(d.depositor, d.assets).number();
+
4043 auto tx = d.vault.withdraw(
+
4044 {.depositor = d.depositor,
+
4045 .id = d.keylet.key,
+
4046 .amount = STAmount(d.asset, Number(372, -2))});
+
4047 env(tx);
+
4048 env.close();
+
4049 BEAST_EXPECT(
+
4050 env.balance(d.depositor, d.shares) == d.share(837 - 37));
+
4051 BEAST_EXPECT(
+
4052 env.balance(d.depositor, d.assets) ==
+
4053 STAmount(d.asset, start + Number(37, -1)));
+
4054 BEAST_EXPECT(
+
4055 env.balance(d.vaultAccount, d.assets) ==
+
4056 STAmount(d.asset, Number(837 - 37, -1)));
+
4057 BEAST_EXPECT(
+
4058 env.balance(d.vaultAccount, d.shares) ==
+
4059 STAmount(d.share, -Number(837 - 37, 0)));
+
4060 }
+
4061
+
4062 {
+
4063 testcase("Scale withdraw tiny amount");
+
4064
+
4065 auto const start = env.balance(d.depositor, d.assets).number();
+
4066 auto tx = d.vault.withdraw(
+
4067 {.depositor = d.depositor,
+
4068 .id = d.keylet.key,
+
4069 .amount = STAmount(d.asset, Number(9, -2))});
+
4070 env(tx);
+
4071 env.close();
+
4072 BEAST_EXPECT(
+
4073 env.balance(d.depositor, d.shares) == d.share(800 - 1));
+
4074 BEAST_EXPECT(
+
4075 env.balance(d.depositor, d.assets) ==
+
4076 STAmount(d.asset, start + Number(1, -1)));
4077 BEAST_EXPECT(
-
4078 env.balance(d.depositor, d.shares) == d.share(800 - 1));
-
4079 BEAST_EXPECT(
-
4080 env.balance(d.depositor, d.assets) ==
-
4081 STAmount(d.asset, start));
-
4082 BEAST_EXPECT(
-
4083 env.balance(d.vaultAccount, d.assets) ==
-
4084 STAmount(d.asset, Number(800 - 1, -1)));
-
4085 BEAST_EXPECT(
-
4086 env.balance(d.vaultAccount, d.shares) ==
-
4087 STAmount(d.share, -Number(800 - 1, 0)));
-
4088 }
+
4078 env.balance(d.vaultAccount, d.assets) ==
+
4079 STAmount(d.asset, Number(800 - 1, -1)));
+
4080 BEAST_EXPECT(
+
4081 env.balance(d.vaultAccount, d.shares) ==
+
4082 STAmount(d.share, -Number(800 - 1, 0)));
+
4083 }
+
4084
+
4085 {
+
4086 testcase("Scale withdraw rest");
+
4087 auto const rest =
+
4088 env.balance(d.vaultAccount, d.assets).number();
4089
-
4090 {
-
4091 testcase("Scale clawback rest");
-
4092 auto const rest =
-
4093 env.balance(d.vaultAccount, d.assets).number();
-
4094 d.peek([](SLE& vault, auto&) -> bool {
-
4095 vault[sfAssetsAvailable] = Number(5);
-
4096 return true;
-
4097 });
-
4098
-
4099 // Note, this transaction yields two different results:
-
4100 // * in the open ledger, with AssetsAvailable = 5
-
4101 // * when the ledger is closed with unmodified AssetsAvailable
-
4102 // because a modification like above is not persistent.
-
4103 tx = d.vault.clawback(
-
4104 {.issuer = d.issuer,
-
4105 .id = d.keylet.key,
-
4106 .holder = d.depositor,
-
4107 .amount = STAmount(d.asset, rest)});
-
4108 env(tx);
-
4109 env.close();
-
4110 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
-
4111 BEAST_EXPECT(
-
4112 env.balance(d.vaultAccount, d.assets).number() == 0);
-
4113 BEAST_EXPECT(
-
4114 env.balance(d.vaultAccount, d.shares).number() == 0);
-
4115 }
-
4116 });
-
4117 }
-
-
4118
-
4119 void
-
- -
4121 {
-
4122 using namespace test::jtx;
-
4123
-
4124 testcase("RPC");
-
4125 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
4126 Account const owner{"owner"};
-
4127 Account const issuer{"issuer"};
-
4128 Vault vault{env};
-
4129 env.fund(XRP(1000), issuer, owner);
-
4130 env.close();
-
4131
-
4132 PrettyAsset asset = issuer["IOU"];
-
4133 env.trust(asset(1000), owner);
-
4134 env(pay(issuer, owner, asset(200)));
-
4135 env.close();
-
4136
-
4137 auto const sequence = env.seq(owner);
-
4138 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
4139 env(tx);
-
4140 env.close();
-
4141
-
4142 // Set some fields
-
4143 {
-
4144 auto tx1 = vault.deposit(
-
4145 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
-
4146 env(tx1);
-
4147
-
4148 auto tx2 = vault.set({.owner = owner, .id = keylet.key});
-
4149 tx2[sfAssetsMaximum] = asset(1000).number();
-
4150 env(tx2);
-
4151 env.close();
-
4152 }
-
4153
-
4154 auto const sleVault = [&env, keylet = keylet, this]() {
-
4155 auto const vault = env.le(keylet);
-
4156 BEAST_EXPECT(vault != nullptr);
-
4157 return vault;
-
4158 }();
-
4159
-
4160 auto const check = [&, keylet = keylet, sle = sleVault, this](
-
4161 Json::Value const& vault,
-
4162 Json::Value const& issuance = Json::nullValue) {
-
4163 BEAST_EXPECT(vault.isObject());
-
4164
-
4165 constexpr auto checkString =
-
4166 [](auto& node, SField const& field, std::string v) -> bool {
-
4167 return node.isMember(field.fieldName) &&
-
4168 node[field.fieldName].isString() &&
-
4169 node[field.fieldName] == v;
-
4170 };
-
4171 constexpr auto checkObject =
-
4172 [](auto& node, SField const& field, Json::Value v) -> bool {
-
4173 return node.isMember(field.fieldName) &&
-
4174 node[field.fieldName].isObject() &&
-
4175 node[field.fieldName] == v;
-
4176 };
-
4177 constexpr auto checkInt =
-
4178 [](auto& node, SField const& field, int v) -> bool {
-
4179 return node.isMember(field.fieldName) &&
-
4180 ((node[field.fieldName].isInt() &&
-
4181 node[field.fieldName] == Json::Int(v)) ||
-
4182 (node[field.fieldName].isUInt() &&
-
4183 node[field.fieldName] == Json::UInt(v)));
-
4184 };
+
4090 tx = d.vault.withdraw(
+
4091 {.depositor = d.depositor,
+
4092 .id = d.keylet.key,
+
4093 .amount = STAmount(d.asset, rest)});
+
4094 env(tx);
+
4095 env.close();
+
4096 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
+
4097 BEAST_EXPECT(
+
4098 env.balance(d.vaultAccount, d.assets).number() == 0);
+
4099 BEAST_EXPECT(
+
4100 env.balance(d.vaultAccount, d.shares).number() == 0);
+
4101 }
+
4102 });
+
4103
+
4104 testCase(18, [&, this](Env& env, Data d) {
+
4105 testcase("Scale clawback overflow");
+
4106
+
4107 {
+
4108 auto tx = d.vault.deposit(
+
4109 {.depositor = d.depositor,
+
4110 .id = d.keylet.key,
+
4111 .amount = d.asset(5)});
+
4112 env(tx);
+
4113 env.close();
+
4114 }
+
4115
+
4116 {
+
4117 auto tx = d.vault.clawback(
+
4118 {.issuer = d.issuer,
+
4119 .id = d.keylet.key,
+
4120 .holder = d.depositor,
+
4121 .amount = STAmount(d.asset, Number(10, 0))});
+
4122 env(tx, ter{tecPATH_DRY});
+
4123 env.close();
+
4124 }
+
4125 });
+
4126
+
4127 testCase(1, [&, this](Env& env, Data d) {
+
4128 // initial setup: deposit 100 IOU, receive 1000 shares
+
4129 auto const start = env.balance(d.depositor, d.assets).number();
+
4130 auto tx = d.vault.deposit(
+
4131 {.depositor = d.depositor,
+
4132 .id = d.keylet.key,
+
4133 .amount = STAmount(d.asset, Number(100, 0))});
+
4134 env(tx);
+
4135 env.close();
+
4136 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
+
4137 BEAST_EXPECT(
+
4138 env.balance(d.depositor, d.assets) ==
+
4139 STAmount(d.asset, start - Number(100, 0)));
+
4140 BEAST_EXPECT(
+
4141 env.balance(d.vaultAccount, d.assets) ==
+
4142 STAmount(d.asset, Number(100, 0)));
+
4143 BEAST_EXPECT(
+
4144 env.balance(d.vaultAccount, d.shares) ==
+
4145 STAmount(d.share, -Number(1000, 0)));
+
4146 {
+
4147 testcase("Scale clawback exact");
+
4148 // assetsToSharesWithdraw:
+
4149 // shares = sharesTotal * (assets / assetsTotal)
+
4150 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
+
4151 // sharesToAssetsWithdraw:
+
4152 // assets = assetsTotal * (shares / sharesTotal)
+
4153 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
+
4154
+
4155 auto const start = env.balance(d.depositor, d.assets).number();
+
4156 auto tx = d.vault.clawback(
+
4157 {.issuer = d.issuer,
+
4158 .id = d.keylet.key,
+
4159 .holder = d.depositor,
+
4160 .amount = STAmount(d.asset, Number(10, 0))});
+
4161 env(tx);
+
4162 env.close();
+
4163 BEAST_EXPECT(
+
4164 env.balance(d.depositor, d.shares) == d.share(900));
+
4165 BEAST_EXPECT(
+
4166 env.balance(d.depositor, d.assets) ==
+
4167 STAmount(d.asset, start));
+
4168 BEAST_EXPECT(
+
4169 env.balance(d.vaultAccount, d.assets) ==
+
4170 STAmount(d.asset, Number(90, 0)));
+
4171 BEAST_EXPECT(
+
4172 env.balance(d.vaultAccount, d.shares) ==
+
4173 STAmount(d.share, -Number(900, 0)));
+
4174 }
+
4175
+
4176 {
+
4177 testcase("Scale clawback insignificant amount");
+
4178 auto tx = d.vault.clawback(
+
4179 {.issuer = d.issuer,
+
4180 .id = d.keylet.key,
+
4181 .holder = d.depositor,
+
4182 .amount = STAmount(d.asset, Number(4, -2))});
+
4183 env(tx, ter{tecPRECISION_LOSS});
+
4184 }
4185
-
4186 BEAST_EXPECT(vault["LedgerEntryType"].asString() == "Vault");
-
4187 BEAST_EXPECT(vault[jss::index].asString() == strHex(keylet.key));
-
4188 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
-
4189 // Ignore all other standard fields, this test doesn't care
-
4190
-
4191 BEAST_EXPECT(
-
4192 checkString(vault, sfAccount, toBase58(sle->at(sfAccount))));
-
4193 BEAST_EXPECT(
-
4194 checkObject(vault, sfAsset, to_json(sle->at(sfAsset))));
-
4195 BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
-
4196 BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
-
4197 BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
-
4198 BEAST_EXPECT(checkString(vault, sfLossUnrealized, "0"));
-
4199
-
4200 auto const strShareID = strHex(sle->at(sfShareMPTID));
-
4201 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
-
4202 BEAST_EXPECT(checkString(vault, sfOwner, toBase58(owner.id())));
-
4203 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
-
4204 BEAST_EXPECT(checkInt(
-
4205 vault, sfWithdrawalPolicy, vaultStrategyFirstComeFirstServe));
-
4206
-
4207 if (issuance.isObject())
-
4208 {
-
4209 BEAST_EXPECT(
-
4210 issuance["LedgerEntryType"].asString() ==
-
4211 "MPTokenIssuance");
-
4212 BEAST_EXPECT(
-
4213 issuance[jss::mpt_issuance_id].asString() == strShareID);
-
4214 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
-
4215 BEAST_EXPECT(checkInt(
-
4216 issuance,
-
4217 sfFlags,
- -
4219 BEAST_EXPECT(
-
4220 checkString(issuance, sfOutstandingAmount, "50000000"));
-
4221 }
-
4222 };
-
4223
-
4224 {
-
4225 testcase("RPC ledger_entry selected by key");
-
4226 Json::Value jvParams;
-
4227 jvParams[jss::ledger_index] = jss::validated;
-
4228 jvParams[jss::vault] = strHex(keylet.key);
-
4229 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4230
-
4231 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
-
4232 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
-
4233 check(jvVault[jss::result][jss::node]);
-
4234 }
-
4235
-
4236 {
-
4237 testcase("RPC ledger_entry selected by owner and seq");
-
4238 Json::Value jvParams;
-
4239 jvParams[jss::ledger_index] = jss::validated;
-
4240 jvParams[jss::vault][jss::owner] = owner.human();
-
4241 jvParams[jss::vault][jss::seq] = sequence;
-
4242 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4243
-
4244 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
-
4245 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
-
4246 check(jvVault[jss::result][jss::node]);
-
4247 }
-
4248
-
4249 {
-
4250 testcase("RPC ledger_entry cannot find vault by key");
-
4251 Json::Value jvParams;
-
4252 jvParams[jss::ledger_index] = jss::validated;
-
4253 jvParams[jss::vault] = to_string(uint256(42));
-
4254 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4255 BEAST_EXPECT(
-
4256 jvVault[jss::result][jss::error].asString() == "entryNotFound");
-
4257 }
-
4258
-
4259 {
-
4260 testcase("RPC ledger_entry cannot find vault by owner and seq");
-
4261 Json::Value jvParams;
-
4262 jvParams[jss::ledger_index] = jss::validated;
-
4263 jvParams[jss::vault][jss::owner] = issuer.human();
-
4264 jvParams[jss::vault][jss::seq] = 1'000'000;
-
4265 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4266 BEAST_EXPECT(
-
4267 jvVault[jss::result][jss::error].asString() == "entryNotFound");
-
4268 }
-
4269
-
4270 {
-
4271 testcase("RPC ledger_entry malformed key");
-
4272 Json::Value jvParams;
-
4273 jvParams[jss::ledger_index] = jss::validated;
-
4274 jvParams[jss::vault] = 42;
-
4275 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4276 BEAST_EXPECT(
-
4277 jvVault[jss::result][jss::error].asString() ==
-
4278 "malformedRequest");
-
4279 }
-
4280
-
4281 {
-
4282 testcase("RPC ledger_entry malformed owner");
-
4283 Json::Value jvParams;
-
4284 jvParams[jss::ledger_index] = jss::validated;
-
4285 jvParams[jss::vault][jss::owner] = 42;
-
4286 jvParams[jss::vault][jss::seq] = sequence;
-
4287 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4288 BEAST_EXPECT(
-
4289 jvVault[jss::result][jss::error].asString() ==
-
4290 "malformedOwner");
-
4291 }
-
4292
-
4293 {
-
4294 testcase("RPC ledger_entry malformed seq");
-
4295 Json::Value jvParams;
-
4296 jvParams[jss::ledger_index] = jss::validated;
-
4297 jvParams[jss::vault][jss::owner] = issuer.human();
-
4298 jvParams[jss::vault][jss::seq] = "foo";
-
4299 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4300 BEAST_EXPECT(
-
4301 jvVault[jss::result][jss::error].asString() ==
-
4302 "malformedRequest");
-
4303 }
-
4304
-
4305 {
-
4306 testcase("RPC ledger_entry negative seq");
-
4307 Json::Value jvParams;
-
4308 jvParams[jss::ledger_index] = jss::validated;
-
4309 jvParams[jss::vault][jss::owner] = issuer.human();
-
4310 jvParams[jss::vault][jss::seq] = -1;
-
4311 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4312 BEAST_EXPECT(
-
4313 jvVault[jss::result][jss::error].asString() ==
-
4314 "malformedRequest");
-
4315 }
-
4316
-
4317 {
-
4318 testcase("RPC ledger_entry oversized seq");
-
4319 Json::Value jvParams;
-
4320 jvParams[jss::ledger_index] = jss::validated;
-
4321 jvParams[jss::vault][jss::owner] = issuer.human();
-
4322 jvParams[jss::vault][jss::seq] = 1e20;
-
4323 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4324 BEAST_EXPECT(
-
4325 jvVault[jss::result][jss::error].asString() ==
-
4326 "malformedRequest");
-
4327 }
+
4186 {
+
4187 testcase("Scale clawback with rounding assets");
+
4188 // assetsToSharesWithdraw:
+
4189 // shares = sharesTotal * (assets / assetsTotal)
+
4190 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
+
4191 // sharesToAssetsWithdraw:
+
4192 // assets = assetsTotal * (shares / sharesTotal)
+
4193 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
+
4194
+
4195 auto const start = env.balance(d.depositor, d.assets).number();
+
4196 auto tx = d.vault.clawback(
+
4197 {.issuer = d.issuer,
+
4198 .id = d.keylet.key,
+
4199 .holder = d.depositor,
+
4200 .amount = STAmount(d.asset, Number(25, -1))});
+
4201 env(tx);
+
4202 env.close();
+
4203 BEAST_EXPECT(
+
4204 env.balance(d.depositor, d.shares) == d.share(900 - 25));
+
4205 BEAST_EXPECT(
+
4206 env.balance(d.depositor, d.assets) ==
+
4207 STAmount(d.asset, start));
+
4208 BEAST_EXPECT(
+
4209 env.balance(d.vaultAccount, d.assets) ==
+
4210 STAmount(d.asset, Number(900 - 25, -1)));
+
4211 BEAST_EXPECT(
+
4212 env.balance(d.vaultAccount, d.shares) ==
+
4213 STAmount(d.share, -Number(900 - 25, 0)));
+
4214 }
+
4215
+
4216 {
+
4217 testcase("Scale clawback with rounding shares up");
+
4218 // assetsToSharesWithdraw:
+
4219 // shares = sharesTotal * (assets / assetsTotal)
+
4220 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
+
4221 // sharesToAssetsWithdraw:
+
4222 // assets = assetsTotal * (shares / sharesTotal)
+
4223 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
+
4224
+
4225 auto const start = env.balance(d.depositor, d.assets).number();
+
4226 auto tx = d.vault.clawback(
+
4227 {.issuer = d.issuer,
+
4228 .id = d.keylet.key,
+
4229 .holder = d.depositor,
+
4230 .amount = STAmount(d.asset, Number(375, -2))});
+
4231 env(tx);
+
4232 env.close();
+
4233 BEAST_EXPECT(
+
4234 env.balance(d.depositor, d.shares) == d.share(875 - 38));
+
4235 BEAST_EXPECT(
+
4236 env.balance(d.depositor, d.assets) ==
+
4237 STAmount(d.asset, start));
+
4238 BEAST_EXPECT(
+
4239 env.balance(d.vaultAccount, d.assets) ==
+
4240 STAmount(d.asset, Number(875 - 38, -1)));
+
4241 BEAST_EXPECT(
+
4242 env.balance(d.vaultAccount, d.shares) ==
+
4243 STAmount(d.share, -Number(875 - 38, 0)));
+
4244 }
+
4245
+
4246 {
+
4247 testcase("Scale clawback with rounding shares down");
+
4248 // assetsToSharesWithdraw:
+
4249 // shares = sharesTotal * (assets / assetsTotal)
+
4250 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
+
4251 // sharesToAssetsWithdraw:
+
4252 // assets = assetsTotal * (shares / sharesTotal)
+
4253 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
+
4254
+
4255 auto const start = env.balance(d.depositor, d.assets).number();
+
4256 auto tx = d.vault.clawback(
+
4257 {.issuer = d.issuer,
+
4258 .id = d.keylet.key,
+
4259 .holder = d.depositor,
+
4260 .amount = STAmount(d.asset, Number(372, -2))});
+
4261 env(tx);
+
4262 env.close();
+
4263 BEAST_EXPECT(
+
4264 env.balance(d.depositor, d.shares) == d.share(837 - 37));
+
4265 BEAST_EXPECT(
+
4266 env.balance(d.depositor, d.assets) ==
+
4267 STAmount(d.asset, start));
+
4268 BEAST_EXPECT(
+
4269 env.balance(d.vaultAccount, d.assets) ==
+
4270 STAmount(d.asset, Number(837 - 37, -1)));
+
4271 BEAST_EXPECT(
+
4272 env.balance(d.vaultAccount, d.shares) ==
+
4273 STAmount(d.share, -Number(837 - 37, 0)));
+
4274 }
+
4275
+
4276 {
+
4277 testcase("Scale clawback tiny amount");
+
4278
+
4279 auto const start = env.balance(d.depositor, d.assets).number();
+
4280 auto tx = d.vault.clawback(
+
4281 {.issuer = d.issuer,
+
4282 .id = d.keylet.key,
+
4283 .holder = d.depositor,
+
4284 .amount = STAmount(d.asset, Number(9, -2))});
+
4285 env(tx);
+
4286 env.close();
+
4287 BEAST_EXPECT(
+
4288 env.balance(d.depositor, d.shares) == d.share(800 - 1));
+
4289 BEAST_EXPECT(
+
4290 env.balance(d.depositor, d.assets) ==
+
4291 STAmount(d.asset, start));
+
4292 BEAST_EXPECT(
+
4293 env.balance(d.vaultAccount, d.assets) ==
+
4294 STAmount(d.asset, Number(800 - 1, -1)));
+
4295 BEAST_EXPECT(
+
4296 env.balance(d.vaultAccount, d.shares) ==
+
4297 STAmount(d.share, -Number(800 - 1, 0)));
+
4298 }
+
4299
+
4300 {
+
4301 testcase("Scale clawback rest");
+
4302 auto const rest =
+
4303 env.balance(d.vaultAccount, d.assets).number();
+
4304 d.peek([](SLE& vault, auto&) -> bool {
+
4305 vault[sfAssetsAvailable] = Number(5);
+
4306 return true;
+
4307 });
+
4308
+
4309 // Note, this transaction yields two different results:
+
4310 // * in the open ledger, with AssetsAvailable = 5
+
4311 // * when the ledger is closed with unmodified AssetsAvailable
+
4312 // because a modification like above is not persistent.
+
4313 tx = d.vault.clawback(
+
4314 {.issuer = d.issuer,
+
4315 .id = d.keylet.key,
+
4316 .holder = d.depositor,
+
4317 .amount = STAmount(d.asset, rest)});
+
4318 env(tx);
+
4319 env.close();
+
4320 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
+
4321 BEAST_EXPECT(
+
4322 env.balance(d.vaultAccount, d.assets).number() == 0);
+
4323 BEAST_EXPECT(
+
4324 env.balance(d.vaultAccount, d.shares).number() == 0);
+
4325 }
+
4326 });
+
4327 }
+
4328
-
4329 {
-
4330 testcase("RPC ledger_entry bool seq");
-
4331 Json::Value jvParams;
-
4332 jvParams[jss::ledger_index] = jss::validated;
-
4333 jvParams[jss::vault][jss::owner] = issuer.human();
-
4334 jvParams[jss::vault][jss::seq] = true;
-
4335 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4336 BEAST_EXPECT(
-
4337 jvVault[jss::result][jss::error].asString() ==
-
4338 "malformedRequest");
-
4339 }
-
4340
-
4341 {
-
4342 testcase("RPC account_objects");
-
4343
-
4344 Json::Value jvParams;
-
4345 jvParams[jss::account] = owner.human();
-
4346 jvParams[jss::type] = jss::vault;
-
4347 auto jv = env.rpc(
-
4348 "json", "account_objects", to_string(jvParams))[jss::result];
-
4349
-
4350 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
-
4351 check(jv[jss::account_objects][0u]);
-
4352 }
-
4353
-
4354 {
-
4355 testcase("RPC ledger_data");
-
4356
-
4357 Json::Value jvParams;
-
4358 jvParams[jss::ledger_index] = jss::validated;
-
4359 jvParams[jss::binary] = false;
-
4360 jvParams[jss::type] = jss::vault;
-
4361 Json::Value jv =
-
4362 env.rpc("json", "ledger_data", to_string(jvParams));
-
4363 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
-
4364 check(jv[jss::result][jss::state][0u]);
-
4365 }
-
4366
-
4367 {
-
4368 testcase("RPC vault_info command line");
-
4369 Json::Value jv =
-
4370 env.rpc("vault_info", strHex(keylet.key), "validated");
-
4371
-
4372 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
-
4373 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
-
4374 check(
-
4375 jv[jss::result][jss::vault],
-
4376 jv[jss::result][jss::vault][jss::shares]);
-
4377 }
-
4378
-
4379 {
-
4380 testcase("RPC vault_info json");
-
4381 Json::Value jvParams;
-
4382 jvParams[jss::ledger_index] = jss::validated;
-
4383 jvParams[jss::vault_id] = strHex(keylet.key);
-
4384 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4385
-
4386 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
-
4387 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
-
4388 check(
-
4389 jv[jss::result][jss::vault],
-
4390 jv[jss::result][jss::vault][jss::shares]);
-
4391 }
-
4392
-
4393 {
-
4394 testcase("RPC vault_info invalid vault_id");
-
4395 Json::Value jvParams;
-
4396 jvParams[jss::ledger_index] = jss::validated;
-
4397 jvParams[jss::vault_id] = "foobar";
-
4398 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4399 BEAST_EXPECT(
-
4400 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4401 }
-
4402
-
4403 {
-
4404 testcase("RPC vault_info json invalid index");
-
4405 Json::Value jvParams;
-
4406 jvParams[jss::ledger_index] = jss::validated;
-
4407 jvParams[jss::vault_id] = 0;
-
4408 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4409 BEAST_EXPECT(
-
4410 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4411 }
-
4412
-
4413 {
-
4414 testcase("RPC vault_info json by owner and sequence");
-
4415 Json::Value jvParams;
-
4416 jvParams[jss::ledger_index] = jss::validated;
-
4417 jvParams[jss::owner] = owner.human();
-
4418 jvParams[jss::seq] = sequence;
-
4419 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4420
-
4421 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
-
4422 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
-
4423 check(
-
4424 jv[jss::result][jss::vault],
-
4425 jv[jss::result][jss::vault][jss::shares]);
-
4426 }
-
4427
-
4428 {
-
4429 testcase("RPC vault_info json malformed sequence");
-
4430 Json::Value jvParams;
-
4431 jvParams[jss::ledger_index] = jss::validated;
-
4432 jvParams[jss::owner] = owner.human();
-
4433 jvParams[jss::seq] = "foobar";
-
4434 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4435 BEAST_EXPECT(
-
4436 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4437 }
-
4438
-
4439 {
-
4440 testcase("RPC vault_info json invalid sequence");
-
4441 Json::Value jvParams;
-
4442 jvParams[jss::ledger_index] = jss::validated;
-
4443 jvParams[jss::owner] = owner.human();
-
4444 jvParams[jss::seq] = 0;
-
4445 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4446 BEAST_EXPECT(
-
4447 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4448 }
-
4449
-
4450 {
-
4451 testcase("RPC vault_info json negative sequence");
-
4452 Json::Value jvParams;
-
4453 jvParams[jss::ledger_index] = jss::validated;
-
4454 jvParams[jss::owner] = owner.human();
-
4455 jvParams[jss::seq] = -1;
-
4456 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4457 BEAST_EXPECT(
-
4458 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4459 }
-
4460
-
4461 {
-
4462 testcase("RPC vault_info json oversized sequence");
-
4463 Json::Value jvParams;
-
4464 jvParams[jss::ledger_index] = jss::validated;
-
4465 jvParams[jss::owner] = owner.human();
-
4466 jvParams[jss::seq] = 1e20;
-
4467 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4468 BEAST_EXPECT(
-
4469 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4470 }
-
4471
-
4472 {
-
4473 testcase("RPC vault_info json bool sequence");
-
4474 Json::Value jvParams;
-
4475 jvParams[jss::ledger_index] = jss::validated;
-
4476 jvParams[jss::owner] = owner.human();
-
4477 jvParams[jss::seq] = true;
-
4478 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4479 BEAST_EXPECT(
-
4480 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4481 }
-
4482
-
4483 {
-
4484 testcase("RPC vault_info json malformed owner");
-
4485 Json::Value jvParams;
-
4486 jvParams[jss::ledger_index] = jss::validated;
-
4487 jvParams[jss::owner] = "foobar";
-
4488 jvParams[jss::seq] = sequence;
-
4489 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4490 BEAST_EXPECT(
-
4491 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4492 }
-
4493
-
4494 {
-
4495 testcase("RPC vault_info json invalid combination only owner");
-
4496 Json::Value jvParams;
-
4497 jvParams[jss::ledger_index] = jss::validated;
-
4498 jvParams[jss::owner] = owner.human();
-
4499 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4500 BEAST_EXPECT(
-
4501 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4502 }
-
4503
-
4504 {
-
4505 testcase("RPC vault_info json invalid combination only seq");
-
4506 Json::Value jvParams;
-
4507 jvParams[jss::ledger_index] = jss::validated;
-
4508 jvParams[jss::seq] = sequence;
-
4509 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4329 void
+
+ +
4331 {
+
4332 using namespace test::jtx;
+
4333
+
4334 testcase("RPC");
+
4335 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
4336 Account const owner{"owner"};
+
4337 Account const issuer{"issuer"};
+
4338 Vault vault{env};
+
4339 env.fund(XRP(1000), issuer, owner);
+
4340 env.close();
+
4341
+
4342 PrettyAsset asset = issuer["IOU"];
+
4343 env.trust(asset(1000), owner);
+
4344 env(pay(issuer, owner, asset(200)));
+
4345 env.close();
+
4346
+
4347 auto const sequence = env.seq(owner);
+
4348 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
4349 env(tx);
+
4350 env.close();
+
4351
+
4352 // Set some fields
+
4353 {
+
4354 auto tx1 = vault.deposit(
+
4355 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
+
4356 env(tx1);
+
4357
+
4358 auto tx2 = vault.set({.owner = owner, .id = keylet.key});
+
4359 tx2[sfAssetsMaximum] = asset(1000).number();
+
4360 env(tx2);
+
4361 env.close();
+
4362 }
+
4363
+
4364 auto const sleVault = [&env, keylet = keylet, this]() {
+
4365 auto const vault = env.le(keylet);
+
4366 BEAST_EXPECT(vault != nullptr);
+
4367 return vault;
+
4368 }();
+
4369
+
4370 auto const check = [&, keylet = keylet, sle = sleVault, this](
+
4371 Json::Value const& vault,
+
4372 Json::Value const& issuance = Json::nullValue) {
+
4373 BEAST_EXPECT(vault.isObject());
+
4374
+
4375 constexpr auto checkString =
+
4376 [](auto& node, SField const& field, std::string v) -> bool {
+
4377 return node.isMember(field.fieldName) &&
+
4378 node[field.fieldName].isString() &&
+
4379 node[field.fieldName] == v;
+
4380 };
+
4381 constexpr auto checkObject =
+
4382 [](auto& node, SField const& field, Json::Value v) -> bool {
+
4383 return node.isMember(field.fieldName) &&
+
4384 node[field.fieldName].isObject() &&
+
4385 node[field.fieldName] == v;
+
4386 };
+
4387 constexpr auto checkInt =
+
4388 [](auto& node, SField const& field, int v) -> bool {
+
4389 return node.isMember(field.fieldName) &&
+
4390 ((node[field.fieldName].isInt() &&
+
4391 node[field.fieldName] == Json::Int(v)) ||
+
4392 (node[field.fieldName].isUInt() &&
+
4393 node[field.fieldName] == Json::UInt(v)));
+
4394 };
+
4395
+
4396 BEAST_EXPECT(vault["LedgerEntryType"].asString() == "Vault");
+
4397 BEAST_EXPECT(vault[jss::index].asString() == strHex(keylet.key));
+
4398 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
+
4399 // Ignore all other standard fields, this test doesn't care
+
4400
+
4401 BEAST_EXPECT(
+
4402 checkString(vault, sfAccount, toBase58(sle->at(sfAccount))));
+
4403 BEAST_EXPECT(
+
4404 checkObject(vault, sfAsset, to_json(sle->at(sfAsset))));
+
4405 BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
+
4406 BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
+
4407 BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
+
4408 BEAST_EXPECT(checkString(vault, sfLossUnrealized, "0"));
+
4409
+
4410 auto const strShareID = strHex(sle->at(sfShareMPTID));
+
4411 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
+
4412 BEAST_EXPECT(checkString(vault, sfOwner, toBase58(owner.id())));
+
4413 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
+
4414 BEAST_EXPECT(checkInt(
+
4415 vault, sfWithdrawalPolicy, vaultStrategyFirstComeFirstServe));
+
4416
+
4417 if (issuance.isObject())
+
4418 {
+
4419 BEAST_EXPECT(
+
4420 issuance["LedgerEntryType"].asString() ==
+
4421 "MPTokenIssuance");
+
4422 BEAST_EXPECT(
+
4423 issuance[jss::mpt_issuance_id].asString() == strShareID);
+
4424 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
+
4425 BEAST_EXPECT(checkInt(
+
4426 issuance,
+
4427 sfFlags,
+ +
4429 BEAST_EXPECT(
+
4430 checkString(issuance, sfOutstandingAmount, "50000000"));
+
4431 }
+
4432 };
+
4433
+
4434 {
+
4435 testcase("RPC ledger_entry selected by key");
+
4436 Json::Value jvParams;
+
4437 jvParams[jss::ledger_index] = jss::validated;
+
4438 jvParams[jss::vault] = strHex(keylet.key);
+
4439 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4440
+
4441 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
+
4442 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
+
4443 check(jvVault[jss::result][jss::node]);
+
4444 }
+
4445
+
4446 {
+
4447 testcase("RPC ledger_entry selected by owner and seq");
+
4448 Json::Value jvParams;
+
4449 jvParams[jss::ledger_index] = jss::validated;
+
4450 jvParams[jss::vault][jss::owner] = owner.human();
+
4451 jvParams[jss::vault][jss::seq] = sequence;
+
4452 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4453
+
4454 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
+
4455 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
+
4456 check(jvVault[jss::result][jss::node]);
+
4457 }
+
4458
+
4459 {
+
4460 testcase("RPC ledger_entry cannot find vault by key");
+
4461 Json::Value jvParams;
+
4462 jvParams[jss::ledger_index] = jss::validated;
+
4463 jvParams[jss::vault] = to_string(uint256(42));
+
4464 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4465 BEAST_EXPECT(
+
4466 jvVault[jss::result][jss::error].asString() == "entryNotFound");
+
4467 }
+
4468
+
4469 {
+
4470 testcase("RPC ledger_entry cannot find vault by owner and seq");
+
4471 Json::Value jvParams;
+
4472 jvParams[jss::ledger_index] = jss::validated;
+
4473 jvParams[jss::vault][jss::owner] = issuer.human();
+
4474 jvParams[jss::vault][jss::seq] = 1'000'000;
+
4475 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4476 BEAST_EXPECT(
+
4477 jvVault[jss::result][jss::error].asString() == "entryNotFound");
+
4478 }
+
4479
+
4480 {
+
4481 testcase("RPC ledger_entry malformed key");
+
4482 Json::Value jvParams;
+
4483 jvParams[jss::ledger_index] = jss::validated;
+
4484 jvParams[jss::vault] = 42;
+
4485 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4486 BEAST_EXPECT(
+
4487 jvVault[jss::result][jss::error].asString() ==
+
4488 "malformedRequest");
+
4489 }
+
4490
+
4491 {
+
4492 testcase("RPC ledger_entry malformed owner");
+
4493 Json::Value jvParams;
+
4494 jvParams[jss::ledger_index] = jss::validated;
+
4495 jvParams[jss::vault][jss::owner] = 42;
+
4496 jvParams[jss::vault][jss::seq] = sequence;
+
4497 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4498 BEAST_EXPECT(
+
4499 jvVault[jss::result][jss::error].asString() ==
+
4500 "malformedOwner");
+
4501 }
+
4502
+
4503 {
+
4504 testcase("RPC ledger_entry malformed seq");
+
4505 Json::Value jvParams;
+
4506 jvParams[jss::ledger_index] = jss::validated;
+
4507 jvParams[jss::vault][jss::owner] = issuer.human();
+
4508 jvParams[jss::vault][jss::seq] = "foo";
+
4509 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
4510 BEAST_EXPECT(
-
4511 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4512 }
-
4513
-
4514 {
-
4515 testcase("RPC vault_info json invalid combination seq vault_id");
-
4516 Json::Value jvParams;
-
4517 jvParams[jss::ledger_index] = jss::validated;
-
4518 jvParams[jss::vault_id] = strHex(keylet.key);
-
4519 jvParams[jss::seq] = sequence;
-
4520 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4521 BEAST_EXPECT(
-
4522 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4523 }
-
4524
-
4525 {
-
4526 testcase("RPC vault_info json invalid combination owner vault_id");
-
4527 Json::Value jvParams;
-
4528 jvParams[jss::ledger_index] = jss::validated;
-
4529 jvParams[jss::vault_id] = strHex(keylet.key);
-
4530 jvParams[jss::owner] = owner.human();
-
4531 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4532 BEAST_EXPECT(
-
4533 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4534 }
-
4535
-
4536 {
-
4537 testcase(
-
4538 "RPC vault_info json invalid combination owner seq "
-
4539 "vault_id");
-
4540 Json::Value jvParams;
-
4541 jvParams[jss::ledger_index] = jss::validated;
-
4542 jvParams[jss::vault_id] = strHex(keylet.key);
-
4543 jvParams[jss::seq] = sequence;
-
4544 jvParams[jss::owner] = owner.human();
-
4545 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4511 jvVault[jss::result][jss::error].asString() ==
+
4512 "malformedRequest");
+
4513 }
+
4514
+
4515 {
+
4516 testcase("RPC ledger_entry negative seq");
+
4517 Json::Value jvParams;
+
4518 jvParams[jss::ledger_index] = jss::validated;
+
4519 jvParams[jss::vault][jss::owner] = issuer.human();
+
4520 jvParams[jss::vault][jss::seq] = -1;
+
4521 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4522 BEAST_EXPECT(
+
4523 jvVault[jss::result][jss::error].asString() ==
+
4524 "malformedRequest");
+
4525 }
+
4526
+
4527 {
+
4528 testcase("RPC ledger_entry oversized seq");
+
4529 Json::Value jvParams;
+
4530 jvParams[jss::ledger_index] = jss::validated;
+
4531 jvParams[jss::vault][jss::owner] = issuer.human();
+
4532 jvParams[jss::vault][jss::seq] = 1e20;
+
4533 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4534 BEAST_EXPECT(
+
4535 jvVault[jss::result][jss::error].asString() ==
+
4536 "malformedRequest");
+
4537 }
+
4538
+
4539 {
+
4540 testcase("RPC ledger_entry bool seq");
+
4541 Json::Value jvParams;
+
4542 jvParams[jss::ledger_index] = jss::validated;
+
4543 jvParams[jss::vault][jss::owner] = issuer.human();
+
4544 jvParams[jss::vault][jss::seq] = true;
+
4545 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
4546 BEAST_EXPECT(
-
4547 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4548 }
-
4549
-
4550 {
-
4551 testcase("RPC vault_info json no input");
-
4552 Json::Value jvParams;
-
4553 jvParams[jss::ledger_index] = jss::validated;
-
4554 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4555 BEAST_EXPECT(
-
4556 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4557 }
-
4558
-
4559 {
-
4560 testcase("RPC vault_info command line invalid index");
-
4561 Json::Value jv = env.rpc("vault_info", "foobar", "validated");
-
4562 BEAST_EXPECT(jv[jss::error].asString() == "invalidParams");
-
4563 }
-
4564
-
4565 {
-
4566 testcase("RPC vault_info command line invalid index");
-
4567 Json::Value jv = env.rpc("vault_info", "0", "validated");
-
4568 BEAST_EXPECT(
-
4569 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4570 }
-
4571
-
4572 {
-
4573 testcase("RPC vault_info command line invalid index");
-
4574 Json::Value jv =
-
4575 env.rpc("vault_info", strHex(uint256(42)), "validated");
-
4576 BEAST_EXPECT(
-
4577 jv[jss::result][jss::error].asString() == "entryNotFound");
-
4578 }
-
4579
-
4580 {
-
4581 testcase("RPC vault_info command line invalid ledger");
-
4582 Json::Value jv = env.rpc("vault_info", strHex(keylet.key), "0");
-
4583 BEAST_EXPECT(
-
4584 jv[jss::result][jss::error].asString() == "lgrNotFound");
-
4585 }
-
4586 }
+
4547 jvVault[jss::result][jss::error].asString() ==
+
4548 "malformedRequest");
+
4549 }
+
4550
+
4551 {
+
4552 testcase("RPC account_objects");
+
4553
+
4554 Json::Value jvParams;
+
4555 jvParams[jss::account] = owner.human();
+
4556 jvParams[jss::type] = jss::vault;
+
4557 auto jv = env.rpc(
+
4558 "json", "account_objects", to_string(jvParams))[jss::result];
+
4559
+
4560 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
+
4561 check(jv[jss::account_objects][0u]);
+
4562 }
+
4563
+
4564 {
+
4565 testcase("RPC ledger_data");
+
4566
+
4567 Json::Value jvParams;
+
4568 jvParams[jss::ledger_index] = jss::validated;
+
4569 jvParams[jss::binary] = false;
+
4570 jvParams[jss::type] = jss::vault;
+
4571 Json::Value jv =
+
4572 env.rpc("json", "ledger_data", to_string(jvParams));
+
4573 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
+
4574 check(jv[jss::result][jss::state][0u]);
+
4575 }
+
4576
+
4577 {
+
4578 testcase("RPC vault_info command line");
+
4579 Json::Value jv =
+
4580 env.rpc("vault_info", strHex(keylet.key), "validated");
+
4581
+
4582 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
+
4583 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
+
4584 check(
+
4585 jv[jss::result][jss::vault],
+
4586 jv[jss::result][jss::vault][jss::shares]);
+
4587 }
+
4588
+
4589 {
+
4590 testcase("RPC vault_info json");
+
4591 Json::Value jvParams;
+
4592 jvParams[jss::ledger_index] = jss::validated;
+
4593 jvParams[jss::vault_id] = strHex(keylet.key);
+
4594 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4595
+
4596 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
+
4597 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
+
4598 check(
+
4599 jv[jss::result][jss::vault],
+
4600 jv[jss::result][jss::vault][jss::shares]);
+
4601 }
+
4602
+
4603 {
+
4604 testcase("RPC vault_info invalid vault_id");
+
4605 Json::Value jvParams;
+
4606 jvParams[jss::ledger_index] = jss::validated;
+
4607 jvParams[jss::vault_id] = "foobar";
+
4608 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4609 BEAST_EXPECT(
+
4610 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4611 }
+
4612
+
4613 {
+
4614 testcase("RPC vault_info json invalid index");
+
4615 Json::Value jvParams;
+
4616 jvParams[jss::ledger_index] = jss::validated;
+
4617 jvParams[jss::vault_id] = 0;
+
4618 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4619 BEAST_EXPECT(
+
4620 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4621 }
+
4622
+
4623 {
+
4624 testcase("RPC vault_info json by owner and sequence");
+
4625 Json::Value jvParams;
+
4626 jvParams[jss::ledger_index] = jss::validated;
+
4627 jvParams[jss::owner] = owner.human();
+
4628 jvParams[jss::seq] = sequence;
+
4629 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4630
+
4631 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
+
4632 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
+
4633 check(
+
4634 jv[jss::result][jss::vault],
+
4635 jv[jss::result][jss::vault][jss::shares]);
+
4636 }
+
4637
+
4638 {
+
4639 testcase("RPC vault_info json malformed sequence");
+
4640 Json::Value jvParams;
+
4641 jvParams[jss::ledger_index] = jss::validated;
+
4642 jvParams[jss::owner] = owner.human();
+
4643 jvParams[jss::seq] = "foobar";
+
4644 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4645 BEAST_EXPECT(
+
4646 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4647 }
+
4648
+
4649 {
+
4650 testcase("RPC vault_info json invalid sequence");
+
4651 Json::Value jvParams;
+
4652 jvParams[jss::ledger_index] = jss::validated;
+
4653 jvParams[jss::owner] = owner.human();
+
4654 jvParams[jss::seq] = 0;
+
4655 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4656 BEAST_EXPECT(
+
4657 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4658 }
+
4659
+
4660 {
+
4661 testcase("RPC vault_info json negative sequence");
+
4662 Json::Value jvParams;
+
4663 jvParams[jss::ledger_index] = jss::validated;
+
4664 jvParams[jss::owner] = owner.human();
+
4665 jvParams[jss::seq] = -1;
+
4666 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4667 BEAST_EXPECT(
+
4668 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4669 }
+
4670
+
4671 {
+
4672 testcase("RPC vault_info json oversized sequence");
+
4673 Json::Value jvParams;
+
4674 jvParams[jss::ledger_index] = jss::validated;
+
4675 jvParams[jss::owner] = owner.human();
+
4676 jvParams[jss::seq] = 1e20;
+
4677 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4678 BEAST_EXPECT(
+
4679 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4680 }
+
4681
+
4682 {
+
4683 testcase("RPC vault_info json bool sequence");
+
4684 Json::Value jvParams;
+
4685 jvParams[jss::ledger_index] = jss::validated;
+
4686 jvParams[jss::owner] = owner.human();
+
4687 jvParams[jss::seq] = true;
+
4688 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4689 BEAST_EXPECT(
+
4690 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4691 }
+
4692
+
4693 {
+
4694 testcase("RPC vault_info json malformed owner");
+
4695 Json::Value jvParams;
+
4696 jvParams[jss::ledger_index] = jss::validated;
+
4697 jvParams[jss::owner] = "foobar";
+
4698 jvParams[jss::seq] = sequence;
+
4699 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4700 BEAST_EXPECT(
+
4701 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4702 }
+
4703
+
4704 {
+
4705 testcase("RPC vault_info json invalid combination only owner");
+
4706 Json::Value jvParams;
+
4707 jvParams[jss::ledger_index] = jss::validated;
+
4708 jvParams[jss::owner] = owner.human();
+
4709 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4710 BEAST_EXPECT(
+
4711 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4712 }
+
4713
+
4714 {
+
4715 testcase("RPC vault_info json invalid combination only seq");
+
4716 Json::Value jvParams;
+
4717 jvParams[jss::ledger_index] = jss::validated;
+
4718 jvParams[jss::seq] = sequence;
+
4719 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4720 BEAST_EXPECT(
+
4721 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4722 }
+
4723
+
4724 {
+
4725 testcase("RPC vault_info json invalid combination seq vault_id");
+
4726 Json::Value jvParams;
+
4727 jvParams[jss::ledger_index] = jss::validated;
+
4728 jvParams[jss::vault_id] = strHex(keylet.key);
+
4729 jvParams[jss::seq] = sequence;
+
4730 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4731 BEAST_EXPECT(
+
4732 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4733 }
+
4734
+
4735 {
+
4736 testcase("RPC vault_info json invalid combination owner vault_id");
+
4737 Json::Value jvParams;
+
4738 jvParams[jss::ledger_index] = jss::validated;
+
4739 jvParams[jss::vault_id] = strHex(keylet.key);
+
4740 jvParams[jss::owner] = owner.human();
+
4741 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4742 BEAST_EXPECT(
+
4743 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4744 }
+
4745
+
4746 {
+
4747 testcase(
+
4748 "RPC vault_info json invalid combination owner seq "
+
4749 "vault_id");
+
4750 Json::Value jvParams;
+
4751 jvParams[jss::ledger_index] = jss::validated;
+
4752 jvParams[jss::vault_id] = strHex(keylet.key);
+
4753 jvParams[jss::seq] = sequence;
+
4754 jvParams[jss::owner] = owner.human();
+
4755 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4756 BEAST_EXPECT(
+
4757 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4758 }
+
4759
+
4760 {
+
4761 testcase("RPC vault_info json no input");
+
4762 Json::Value jvParams;
+
4763 jvParams[jss::ledger_index] = jss::validated;
+
4764 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4765 BEAST_EXPECT(
+
4766 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4767 }
+
4768
+
4769 {
+
4770 testcase("RPC vault_info command line invalid index");
+
4771 Json::Value jv = env.rpc("vault_info", "foobar", "validated");
+
4772 BEAST_EXPECT(jv[jss::error].asString() == "invalidParams");
+
4773 }
+
4774
+
4775 {
+
4776 testcase("RPC vault_info command line invalid index");
+
4777 Json::Value jv = env.rpc("vault_info", "0", "validated");
+
4778 BEAST_EXPECT(
+
4779 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4780 }
+
4781
+
4782 {
+
4783 testcase("RPC vault_info command line invalid index");
+
4784 Json::Value jv =
+
4785 env.rpc("vault_info", strHex(uint256(42)), "validated");
+
4786 BEAST_EXPECT(
+
4787 jv[jss::result][jss::error].asString() == "entryNotFound");
+
4788 }
+
4789
+
4790 {
+
4791 testcase("RPC vault_info command line invalid ledger");
+
4792 Json::Value jv = env.rpc("vault_info", strHex(keylet.key), "0");
+
4793 BEAST_EXPECT(
+
4794 jv[jss::result][jss::error].asString() == "lgrNotFound");
+
4795 }
+
4796 }
-
4587
-
4588public:
-
4589 void
-
-
4590 run() override
-
4591 {
-
4592 testSequences();
-
4593 testPreflight();
- - - -
4597 testWithMPT();
-
4598 testWithIOU();
- - - - -
4603 testScaleIOU();
-
4604 testRPC();
-
4605 }
+
4797
+
4798public:
+
4799 void
+
+
4800 run() override
+
4801 {
+
4802 testSequences();
+
4803 testPreflight();
+ + + +
4807 testWithMPT();
+
4808 testWithIOU();
+ + + + +
4813 testScaleIOU();
+
4814 testRPC();
+
4815 }
-
4606};
+
4816};
-
4607
-
4608BEAST_DEFINE_TESTSUITE_PRIO(Vault, app, ripple, 1);
-
4609
-
4610} // namespace ripple
+
4817
+
4818BEAST_DEFINE_TESTSUITE_PRIO(Vault, app, ripple, 1);
+
4819
+
4820} // namespace ripple
Represents a JSON value.
Definition json_value.h:149
A generic endpoint for log messages.
Definition Journal.h:60
@@ -4744,22 +4954,23 @@ $(document).ready(function() { init_codefold(0); });
Discardable, editable view to a ledger.
Definition Sandbox.h:35
void apply(RawView &to)
Definition Sandbox.h:55
- - - + + +
ripple::test::jtx::PrettyAsset PrettyAsset
- - - -
void testNonTransferableShares()
- - + + + +
void testNonTransferableShares()
+ +
static auto constexpr negativeAmount
-
void run() override
Runs the suite.
- +
void run() override
Runs the suite.
+ - - + + +
constexpr value_type drops() const
Returns the number of drops.
Definition XRPAmount.h:177
Integers of any length that is a multiple of 32-bits.
Definition base_uint.h:86
void update(std::shared_ptr< SLE > const &sle) override
Indicate changes to a peeked SLE.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
@@ -4792,14 +5003,16 @@ $(document).ready(function() { init_codefold(0); });
@ lsfMPTCanEscrow
@ lsfMPTCanClawback
constexpr std::uint32_t const tfVaultPrivate
Definition TxFlags.h:270
-
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2479
+
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2485
Json::Value to_json(Asset const &asset)
Definition Asset.h:123
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:30
constexpr std::uint32_t const tfMPTUnauthorize
Definition TxFlags.h:172
constexpr std::uint32_t tfSetfAuth
Definition TxFlags.h:115
constexpr std::uint32_t asfDefaultRipple
Definition TxFlags.h:84
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:119
+
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:762
@ tecNO_ENTRY
Definition TER.h:306
+
@ tecNO_LINE_INSUF_RESERVE
Definition TER.h:292
@ tecLIMIT_EXCEEDED
Definition TER.h:361
@ tecOBJECT_NOT_FOUND
Definition TER.h:326
@ tecFROZEN
Definition TER.h:303
@@ -4833,11 +5046,13 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t tfSetFreeze
Definition TxFlags.h:118
constexpr std::uint32_t const tfMPTCanLock
Definition TxFlags.h:148
constexpr std::uint32_t const tfMPTCanClawback
Definition TxFlags.h:153
+
constexpr XRPAmount DROPS_PER_XRP
Number of drops per 1 XRP.
Definition XRPAmount.h:259
@ temBAD_AMOUNT
Definition TER.h:89
@ temBAD_FEE
Definition TER.h:92
@ temMALFORMED
Definition TER.h:87
@ temINVALID_FLAG
Definition TER.h:111
@ temDISABLED
Definition TER.h:114
+
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:39
uint256 key
Definition Keylet.h:40
diff --git a/View_8cpp_source.html b/View_8cpp_source.html index bab0278568..260b60da4a 100644 --- a/View_8cpp_source.html +++ b/View_8cpp_source.html @@ -1418,2092 +1418,2098 @@ $(document).ready(function() { init_codefold(0); });
1242 // If the line already exists, don't create it again.
1243 if (view.read(index))
1244 return tecDUPLICATE;
-
1245 return trustCreate(
-
1246 view,
-
1247 high,
-
1248 srcId,
-
1249 dstId,
-
1250 index.key,
-
1251 sleDst,
-
1252 /*auth=*/false,
-
1253 /*noRipple=*/true,
-
1254 /*freeze=*/false,
-
1255 /*deepFreeze*/ false,
-
1256 /*balance=*/STAmount{Issue{currency, noAccount()}},
-
1257 /*limit=*/STAmount{Issue{currency, dstId}},
-
1258 /*qualityIn=*/0,
-
1259 /*qualityOut=*/0,
-
1260 journal);
-
1261}
+
1245
+
1246 // Can the account cover the trust line reserve ?
+
1247 std::uint32_t const ownerCount = sleDst->at(sfOwnerCount);
+
1248 if (priorBalance < view.fees().accountReserve(ownerCount + 1))
+ +
1250
+
1251 return trustCreate(
+
1252 view,
+
1253 high,
+
1254 srcId,
+
1255 dstId,
+
1256 index.key,
+
1257 sleDst,
+
1258 /*auth=*/false,
+
1259 /*noRipple=*/true,
+
1260 /*freeze=*/false,
+
1261 /*deepFreeze*/ false,
+
1262 /*balance=*/STAmount{Issue{currency, noAccount()}},
+
1263 /*limit=*/STAmount{Issue{currency, dstId}},
+
1264 /*qualityIn=*/0,
+
1265 /*qualityOut=*/0,
+
1266 journal);
+
1267}
-
1262
-
1263[[nodiscard]] TER
-
- -
1265 ApplyView& view,
-
1266 AccountID const& accountID,
-
1267 XRPAmount priorBalance,
-
1268 MPTIssue const& mptIssue,
-
1269 beast::Journal journal)
-
1270{
-
1271 auto const& mptID = mptIssue.getMptID();
-
1272 auto const mpt = view.peek(keylet::mptIssuance(mptID));
-
1273 if (!mpt)
-
1274 return tefINTERNAL; // LCOV_EXCL_LINE
-
1275 if (mpt->isFlag(lsfMPTLocked))
-
1276 return tefINTERNAL; // LCOV_EXCL_LINE
-
1277 if (view.peek(keylet::mptoken(mptID, accountID)))
-
1278 return tecDUPLICATE;
-
1279
-
1280 return authorizeMPToken(view, priorBalance, mptID, accountID, journal);
-
1281}
+
1268
+
1269[[nodiscard]] TER
+
+ +
1271 ApplyView& view,
+
1272 AccountID const& accountID,
+
1273 XRPAmount priorBalance,
+
1274 MPTIssue const& mptIssue,
+
1275 beast::Journal journal)
+
1276{
+
1277 auto const& mptID = mptIssue.getMptID();
+
1278 auto const mpt = view.peek(keylet::mptIssuance(mptID));
+
1279 if (!mpt)
+
1280 return tefINTERNAL; // LCOV_EXCL_LINE
+
1281 if (mpt->isFlag(lsfMPTLocked))
+
1282 return tefINTERNAL; // LCOV_EXCL_LINE
+
1283 if (view.peek(keylet::mptoken(mptID, accountID)))
+
1284 return tecDUPLICATE;
+
1285
+
1286 return authorizeMPToken(view, priorBalance, mptID, accountID, journal);
+
1287}
-
1282
-
1283[[nodiscard]] TER
-
- -
1285 ApplyView& view,
-
1286 XRPAmount const& priorBalance,
-
1287 MPTID const& mptIssuanceID,
-
1288 AccountID const& account,
-
1289 beast::Journal journal,
-
1290 std::uint32_t flags,
-
1291 std::optional<AccountID> holderID)
-
1292{
-
1293 auto const sleAcct = view.peek(keylet::account(account));
-
1294 if (!sleAcct)
-
1295 return tecINTERNAL; // LCOV_EXCL_LINE
-
1296
-
1297 // If the account that submitted the tx is a holder
-
1298 // Note: `account_` is holder's account
-
1299 // `holderID` is NOT used
-
1300 if (!holderID)
-
1301 {
-
1302 // When a holder wants to unauthorize/delete a MPT, the ledger must
-
1303 // - delete mptokenKey from owner directory
-
1304 // - delete the MPToken
-
1305 if (flags & tfMPTUnauthorize)
-
1306 {
-
1307 auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
-
1308 auto const sleMpt = view.peek(mptokenKey);
-
1309 if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0)
-
1310 return tecINTERNAL; // LCOV_EXCL_LINE
-
1311
-
1312 if (!view.dirRemove(
-
1313 keylet::ownerDir(account),
-
1314 (*sleMpt)[sfOwnerNode],
-
1315 sleMpt->key(),
-
1316 false))
-
1317 return tecINTERNAL; // LCOV_EXCL_LINE
-
1318
-
1319 adjustOwnerCount(view, sleAcct, -1, journal);
-
1320
-
1321 view.erase(sleMpt);
-
1322 return tesSUCCESS;
-
1323 }
+
1288
+
1289[[nodiscard]] TER
+
+ +
1291 ApplyView& view,
+
1292 XRPAmount const& priorBalance,
+
1293 MPTID const& mptIssuanceID,
+
1294 AccountID const& account,
+
1295 beast::Journal journal,
+
1296 std::uint32_t flags,
+
1297 std::optional<AccountID> holderID)
+
1298{
+
1299 auto const sleAcct = view.peek(keylet::account(account));
+
1300 if (!sleAcct)
+
1301 return tecINTERNAL; // LCOV_EXCL_LINE
+
1302
+
1303 // If the account that submitted the tx is a holder
+
1304 // Note: `account_` is holder's account
+
1305 // `holderID` is NOT used
+
1306 if (!holderID)
+
1307 {
+
1308 // When a holder wants to unauthorize/delete a MPT, the ledger must
+
1309 // - delete mptokenKey from owner directory
+
1310 // - delete the MPToken
+
1311 if (flags & tfMPTUnauthorize)
+
1312 {
+
1313 auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
+
1314 auto const sleMpt = view.peek(mptokenKey);
+
1315 if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0)
+
1316 return tecINTERNAL; // LCOV_EXCL_LINE
+
1317
+
1318 if (!view.dirRemove(
+
1319 keylet::ownerDir(account),
+
1320 (*sleMpt)[sfOwnerNode],
+
1321 sleMpt->key(),
+
1322 false))
+
1323 return tecINTERNAL; // LCOV_EXCL_LINE
1324
-
1325 // A potential holder wants to authorize/hold a mpt, the ledger must:
-
1326 // - add the new mptokenKey to the owner directory
-
1327 // - create the MPToken object for the holder
-
1328
-
1329 // The reserve that is required to create the MPToken. Note
-
1330 // that although the reserve increases with every item
-
1331 // an account owns, in the case of MPTokens we only
-
1332 // *enforce* a reserve if the user owns more than two
-
1333 // items. This is similar to the reserve requirements of trust lines.
-
1334 std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount);
-
1335 XRPAmount const reserveCreate(
-
1336 (uOwnerCount < 2) ? XRPAmount(beast::zero)
-
1337 : view.fees().accountReserve(uOwnerCount + 1));
-
1338
-
1339 if (priorBalance < reserveCreate)
- -
1341
-
1342 auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
-
1343 auto mptoken = std::make_shared<SLE>(mptokenKey);
-
1344 if (auto ter = dirLink(view, account, mptoken))
-
1345 return ter; // LCOV_EXCL_LINE
-
1346
-
1347 (*mptoken)[sfAccount] = account;
-
1348 (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
-
1349 (*mptoken)[sfFlags] = 0;
-
1350 view.insert(mptoken);
-
1351
-
1352 // Update owner count.
-
1353 adjustOwnerCount(view, sleAcct, 1, journal);
-
1354
-
1355 return tesSUCCESS;
-
1356 }
+
1325 adjustOwnerCount(view, sleAcct, -1, journal);
+
1326
+
1327 view.erase(sleMpt);
+
1328 return tesSUCCESS;
+
1329 }
+
1330
+
1331 // A potential holder wants to authorize/hold a mpt, the ledger must:
+
1332 // - add the new mptokenKey to the owner directory
+
1333 // - create the MPToken object for the holder
+
1334
+
1335 // The reserve that is required to create the MPToken. Note
+
1336 // that although the reserve increases with every item
+
1337 // an account owns, in the case of MPTokens we only
+
1338 // *enforce* a reserve if the user owns more than two
+
1339 // items. This is similar to the reserve requirements of trust lines.
+
1340 std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount);
+
1341 XRPAmount const reserveCreate(
+
1342 (uOwnerCount < 2) ? XRPAmount(beast::zero)
+
1343 : view.fees().accountReserve(uOwnerCount + 1));
+
1344
+
1345 if (priorBalance < reserveCreate)
+ +
1347
+
1348 auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
+
1349 auto mptoken = std::make_shared<SLE>(mptokenKey);
+
1350 if (auto ter = dirLink(view, account, mptoken))
+
1351 return ter; // LCOV_EXCL_LINE
+
1352
+
1353 (*mptoken)[sfAccount] = account;
+
1354 (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
+
1355 (*mptoken)[sfFlags] = 0;
+
1356 view.insert(mptoken);
1357
-
1358 auto const sleMptIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
-
1359 if (!sleMptIssuance)
-
1360 return tecINTERNAL; // LCOV_EXCL_LINE
-
1361
-
1362 // If the account that submitted this tx is the issuer of the MPT
-
1363 // Note: `account_` is issuer's account
-
1364 // `holderID` is holder's account
-
1365 if (account != (*sleMptIssuance)[sfIssuer])
+
1358 // Update owner count.
+
1359 adjustOwnerCount(view, sleAcct, 1, journal);
+
1360
+
1361 return tesSUCCESS;
+
1362 }
+
1363
+
1364 auto const sleMptIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
+
1365 if (!sleMptIssuance)
1366 return tecINTERNAL; // LCOV_EXCL_LINE
1367
-
1368 auto const sleMpt = view.peek(keylet::mptoken(mptIssuanceID, *holderID));
-
1369 if (!sleMpt)
-
1370 return tecINTERNAL; // LCOV_EXCL_LINE
-
1371
-
1372 std::uint32_t const flagsIn = sleMpt->getFieldU32(sfFlags);
-
1373 std::uint32_t flagsOut = flagsIn;
-
1374
-
1375 // Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on
-
1376 // their MPToken
-
1377 if (flags & tfMPTUnauthorize)
-
1378 flagsOut &= ~lsfMPTAuthorized;
-
1379 // Issuer wants to authorize a holder, set lsfMPTAuthorized on their
-
1380 // MPToken
-
1381 else
-
1382 flagsOut |= lsfMPTAuthorized;
-
1383
-
1384 if (flagsIn != flagsOut)
-
1385 sleMpt->setFieldU32(sfFlags, flagsOut);
-
1386
-
1387 view.update(sleMpt);
-
1388 return tesSUCCESS;
-
1389}
+
1368 // If the account that submitted this tx is the issuer of the MPT
+
1369 // Note: `account_` is issuer's account
+
1370 // `holderID` is holder's account
+
1371 if (account != (*sleMptIssuance)[sfIssuer])
+
1372 return tecINTERNAL; // LCOV_EXCL_LINE
+
1373
+
1374 auto const sleMpt = view.peek(keylet::mptoken(mptIssuanceID, *holderID));
+
1375 if (!sleMpt)
+
1376 return tecINTERNAL; // LCOV_EXCL_LINE
+
1377
+
1378 std::uint32_t const flagsIn = sleMpt->getFieldU32(sfFlags);
+
1379 std::uint32_t flagsOut = flagsIn;
+
1380
+
1381 // Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on
+
1382 // their MPToken
+
1383 if (flags & tfMPTUnauthorize)
+
1384 flagsOut &= ~lsfMPTAuthorized;
+
1385 // Issuer wants to authorize a holder, set lsfMPTAuthorized on their
+
1386 // MPToken
+
1387 else
+
1388 flagsOut |= lsfMPTAuthorized;
+
1389
+
1390 if (flagsIn != flagsOut)
+
1391 sleMpt->setFieldU32(sfFlags, flagsOut);
+
1392
+
1393 view.update(sleMpt);
+
1394 return tesSUCCESS;
+
1395}
-
1390
-
1391TER
-
- -
1393 ApplyView& view,
-
1394 bool const bSrcHigh,
-
1395 AccountID const& uSrcAccountID,
-
1396 AccountID const& uDstAccountID,
-
1397 uint256 const& uIndex, // --> ripple state entry
-
1398 SLE::ref sleAccount, // --> the account being set.
-
1399 bool const bAuth, // --> authorize account.
-
1400 bool const bNoRipple, // --> others cannot ripple through
-
1401 bool const bFreeze, // --> funds cannot leave
-
1402 bool bDeepFreeze, // --> can neither receive nor send funds
-
1403 STAmount const& saBalance, // --> balance of account being set.
-
1404 // Issuer should be noAccount()
-
1405 STAmount const& saLimit, // --> limit for account being set.
-
1406 // Issuer should be the account being set.
-
1407 std::uint32_t uQualityIn,
-
1408 std::uint32_t uQualityOut,
- -
1410{
-
1411 JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", "
-
1412 << to_string(uDstAccountID) << ", "
-
1413 << saBalance.getFullText();
-
1414
-
1415 auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
-
1416 auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
-
1417
-
1418 auto const sleRippleState = std::make_shared<SLE>(ltRIPPLE_STATE, uIndex);
-
1419 view.insert(sleRippleState);
+
1396
+
1397TER
+
+ +
1399 ApplyView& view,
+
1400 bool const bSrcHigh,
+
1401 AccountID const& uSrcAccountID,
+
1402 AccountID const& uDstAccountID,
+
1403 uint256 const& uIndex, // --> ripple state entry
+
1404 SLE::ref sleAccount, // --> the account being set.
+
1405 bool const bAuth, // --> authorize account.
+
1406 bool const bNoRipple, // --> others cannot ripple through
+
1407 bool const bFreeze, // --> funds cannot leave
+
1408 bool bDeepFreeze, // --> can neither receive nor send funds
+
1409 STAmount const& saBalance, // --> balance of account being set.
+
1410 // Issuer should be noAccount()
+
1411 STAmount const& saLimit, // --> limit for account being set.
+
1412 // Issuer should be the account being set.
+
1413 std::uint32_t uQualityIn,
+
1414 std::uint32_t uQualityOut,
+ +
1416{
+
1417 JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", "
+
1418 << to_string(uDstAccountID) << ", "
+
1419 << saBalance.getFullText();
1420
-
1421 auto lowNode = view.dirInsert(
-
1422 keylet::ownerDir(uLowAccountID),
-
1423 sleRippleState->key(),
-
1424 describeOwnerDir(uLowAccountID));
-
1425
-
1426 if (!lowNode)
-
1427 return tecDIR_FULL; // LCOV_EXCL_LINE
-
1428
-
1429 auto highNode = view.dirInsert(
-
1430 keylet::ownerDir(uHighAccountID),
-
1431 sleRippleState->key(),
-
1432 describeOwnerDir(uHighAccountID));
-
1433
-
1434 if (!highNode)
-
1435 return tecDIR_FULL; // LCOV_EXCL_LINE
-
1436
-
1437 bool const bSetDst = saLimit.getIssuer() == uDstAccountID;
-
1438 bool const bSetHigh = bSrcHigh ^ bSetDst;
+
1421 auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
+
1422 auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
+
1423
+
1424 auto const sleRippleState = std::make_shared<SLE>(ltRIPPLE_STATE, uIndex);
+
1425 view.insert(sleRippleState);
+
1426
+
1427 auto lowNode = view.dirInsert(
+
1428 keylet::ownerDir(uLowAccountID),
+
1429 sleRippleState->key(),
+
1430 describeOwnerDir(uLowAccountID));
+
1431
+
1432 if (!lowNode)
+
1433 return tecDIR_FULL; // LCOV_EXCL_LINE
+
1434
+
1435 auto highNode = view.dirInsert(
+
1436 keylet::ownerDir(uHighAccountID),
+
1437 sleRippleState->key(),
+
1438 describeOwnerDir(uHighAccountID));
1439
-
1440 XRPL_ASSERT(sleAccount, "ripple::trustCreate : non-null SLE");
-
1441 if (!sleAccount)
-
1442 return tefINTERNAL; // LCOV_EXCL_LINE
-
1443
-
1444 XRPL_ASSERT(
-
1445 sleAccount->getAccountID(sfAccount) ==
-
1446 (bSetHigh ? uHighAccountID : uLowAccountID),
-
1447 "ripple::trustCreate : matching account ID");
-
1448 auto const slePeer =
-
1449 view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID));
-
1450 if (!slePeer)
-
1451 return tecNO_TARGET;
-
1452
-
1453 // Remember deletion hints.
-
1454 sleRippleState->setFieldU64(sfLowNode, *lowNode);
-
1455 sleRippleState->setFieldU64(sfHighNode, *highNode);
-
1456
-
1457 sleRippleState->setFieldAmount(
-
1458 bSetHigh ? sfHighLimit : sfLowLimit, saLimit);
-
1459 sleRippleState->setFieldAmount(
-
1460 bSetHigh ? sfLowLimit : sfHighLimit,
- -
1462 saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID}));
-
1463
-
1464 if (uQualityIn)
-
1465 sleRippleState->setFieldU32(
-
1466 bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn);
-
1467
-
1468 if (uQualityOut)
-
1469 sleRippleState->setFieldU32(
-
1470 bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut);
-
1471
-
1472 std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve;
+
1440 if (!highNode)
+
1441 return tecDIR_FULL; // LCOV_EXCL_LINE
+
1442
+
1443 bool const bSetDst = saLimit.getIssuer() == uDstAccountID;
+
1444 bool const bSetHigh = bSrcHigh ^ bSetDst;
+
1445
+
1446 XRPL_ASSERT(sleAccount, "ripple::trustCreate : non-null SLE");
+
1447 if (!sleAccount)
+
1448 return tefINTERNAL; // LCOV_EXCL_LINE
+
1449
+
1450 XRPL_ASSERT(
+
1451 sleAccount->getAccountID(sfAccount) ==
+
1452 (bSetHigh ? uHighAccountID : uLowAccountID),
+
1453 "ripple::trustCreate : matching account ID");
+
1454 auto const slePeer =
+
1455 view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID));
+
1456 if (!slePeer)
+
1457 return tecNO_TARGET;
+
1458
+
1459 // Remember deletion hints.
+
1460 sleRippleState->setFieldU64(sfLowNode, *lowNode);
+
1461 sleRippleState->setFieldU64(sfHighNode, *highNode);
+
1462
+
1463 sleRippleState->setFieldAmount(
+
1464 bSetHigh ? sfHighLimit : sfLowLimit, saLimit);
+
1465 sleRippleState->setFieldAmount(
+
1466 bSetHigh ? sfLowLimit : sfHighLimit,
+ +
1468 saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID}));
+
1469
+
1470 if (uQualityIn)
+
1471 sleRippleState->setFieldU32(
+
1472 bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn);
1473
-
1474 if (bAuth)
-
1475 {
-
1476 uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth);
-
1477 }
-
1478 if (bNoRipple)
-
1479 {
-
1480 uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple);
-
1481 }
-
1482 if (bFreeze)
-
1483 {
-
1484 uFlags |= (bSetHigh ? lsfHighFreeze : lsfLowFreeze);
-
1485 }
-
1486 if (bDeepFreeze)
-
1487 {
-
1488 uFlags |= (bSetHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze);
-
1489 }
-
1490
-
1491 if ((slePeer->getFlags() & lsfDefaultRipple) == 0)
-
1492 {
-
1493 // The other side's default is no rippling
-
1494 uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple);
+
1474 if (uQualityOut)
+
1475 sleRippleState->setFieldU32(
+
1476 bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut);
+
1477
+
1478 std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve;
+
1479
+
1480 if (bAuth)
+
1481 {
+
1482 uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth);
+
1483 }
+
1484 if (bNoRipple)
+
1485 {
+
1486 uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple);
+
1487 }
+
1488 if (bFreeze)
+
1489 {
+
1490 uFlags |= (bSetHigh ? lsfHighFreeze : lsfLowFreeze);
+
1491 }
+
1492 if (bDeepFreeze)
+
1493 {
+
1494 uFlags |= (bSetHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze);
1495 }
1496
-
1497 sleRippleState->setFieldU32(sfFlags, uFlags);
-
1498 adjustOwnerCount(view, sleAccount, 1, j);
-
1499
-
1500 // ONLY: Create ripple balance.
-
1501 sleRippleState->setFieldAmount(
-
1502 sfBalance, bSetHigh ? -saBalance : saBalance);
-
1503
-
1504 view.creditHook(
-
1505 uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed());
-
1506
-
1507 return tesSUCCESS;
-
1508}
-
+
1497 if ((slePeer->getFlags() & lsfDefaultRipple) == 0)
+
1498 {
+
1499 // The other side's default is no rippling
+
1500 uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple);
+
1501 }
+
1502
+
1503 sleRippleState->setFieldU32(sfFlags, uFlags);
+
1504 adjustOwnerCount(view, sleAccount, 1, j);
+
1505
+
1506 // ONLY: Create ripple balance.
+
1507 sleRippleState->setFieldAmount(
+
1508 sfBalance, bSetHigh ? -saBalance : saBalance);
1509
-
1510[[nodiscard]] TER
-
- -
1512 ApplyView& view,
-
1513 AccountID const& accountID,
-
1514 Issue const& issue,
-
1515 beast::Journal journal)
-
1516{
-
1517 if (issue.native())
-
1518 {
-
1519 auto const sle = view.read(keylet::account(accountID));
-
1520 if (!sle)
-
1521 return tecINTERNAL; // LCOV_EXCL_LINE
-
1522
-
1523 auto const balance = sle->getFieldAmount(sfBalance);
-
1524 if (balance.xrp() != 0)
-
1525 return tecHAS_OBLIGATIONS;
-
1526
-
1527 return tesSUCCESS;
-
1528 }
-
1529
-
1530 // `asset` is an IOU.
-
1531 auto const line = view.peek(keylet::line(accountID, issue));
-
1532 if (!line)
-
1533 return tecOBJECT_NOT_FOUND;
-
1534 if (line->at(sfBalance)->iou() != beast::zero)
-
1535 return tecHAS_OBLIGATIONS;
-
1536
-
1537 // Adjust the owner count(s)
-
1538 if (line->isFlag(lsfLowReserve))
-
1539 {
-
1540 // Clear reserve for low account.
-
1541 auto sleLowAccount =
-
1542 view.peek(keylet::account(line->at(sfLowLimit)->getIssuer()));
-
1543 if (!sleLowAccount)
-
1544 return tecINTERNAL; // LCOV_EXCL_LINE
-
1545
-
1546 adjustOwnerCount(view, sleLowAccount, -1, journal);
-
1547 // It's not really necessary to clear the reserve flag, since the line
-
1548 // is about to be deleted, but this will make the metadata reflect an
-
1549 // accurate state at the time of deletion.
-
1550 line->clearFlag(lsfLowReserve);
-
1551 }
-
1552
-
1553 if (line->isFlag(lsfHighReserve))
-
1554 {
-
1555 // Clear reserve for high account.
-
1556 auto sleHighAccount =
-
1557 view.peek(keylet::account(line->at(sfHighLimit)->getIssuer()));
-
1558 if (!sleHighAccount)
-
1559 return tecINTERNAL; // LCOV_EXCL_LINE
-
1560
-
1561 adjustOwnerCount(view, sleHighAccount, -1, journal);
-
1562 // It's not really necessary to clear the reserve flag, since the line
-
1563 // is about to be deleted, but this will make the metadata reflect an
-
1564 // accurate state at the time of deletion.
-
1565 line->clearFlag(lsfHighReserve);
-
1566 }
-
1567
-
1568 return trustDelete(
-
1569 view,
-
1570 line,
-
1571 line->at(sfLowLimit)->getIssuer(),
-
1572 line->at(sfHighLimit)->getIssuer(),
-
1573 journal);
-
1574}
+
1510 view.creditHook(
+
1511 uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed());
+
1512
+
1513 return tesSUCCESS;
+
1514}
-
1575
-
1576[[nodiscard]] TER
-
- -
1578 ApplyView& view,
-
1579 AccountID const& accountID,
-
1580 MPTIssue const& mptIssue,
-
1581 beast::Journal journal)
-
1582{
-
1583 auto const& mptID = mptIssue.getMptID();
-
1584 auto const mptoken = view.peek(keylet::mptoken(mptID, accountID));
-
1585 if (!mptoken)
-
1586 return tecOBJECT_NOT_FOUND;
-
1587 if (mptoken->at(sfMPTAmount) != 0)
-
1588 return tecHAS_OBLIGATIONS;
-
1589
-
1590 return authorizeMPToken(
-
1591 view,
-
1592 {}, // priorBalance
-
1593 mptID,
-
1594 accountID,
-
1595 journal,
-
1596 tfMPTUnauthorize // flags
-
1597 );
-
1598}
+
1515
+
1516[[nodiscard]] TER
+
+ +
1518 ApplyView& view,
+
1519 AccountID const& accountID,
+
1520 Issue const& issue,
+
1521 beast::Journal journal)
+
1522{
+
1523 if (issue.native())
+
1524 {
+
1525 auto const sle = view.read(keylet::account(accountID));
+
1526 if (!sle)
+
1527 return tecINTERNAL; // LCOV_EXCL_LINE
+
1528
+
1529 auto const balance = sle->getFieldAmount(sfBalance);
+
1530 if (balance.xrp() != 0)
+
1531 return tecHAS_OBLIGATIONS;
+
1532
+
1533 return tesSUCCESS;
+
1534 }
+
1535
+
1536 // `asset` is an IOU.
+
1537 auto const line = view.peek(keylet::line(accountID, issue));
+
1538 if (!line)
+
1539 return tecOBJECT_NOT_FOUND;
+
1540 if (line->at(sfBalance)->iou() != beast::zero)
+
1541 return tecHAS_OBLIGATIONS;
+
1542
+
1543 // Adjust the owner count(s)
+
1544 if (line->isFlag(lsfLowReserve))
+
1545 {
+
1546 // Clear reserve for low account.
+
1547 auto sleLowAccount =
+
1548 view.peek(keylet::account(line->at(sfLowLimit)->getIssuer()));
+
1549 if (!sleLowAccount)
+
1550 return tecINTERNAL; // LCOV_EXCL_LINE
+
1551
+
1552 adjustOwnerCount(view, sleLowAccount, -1, journal);
+
1553 // It's not really necessary to clear the reserve flag, since the line
+
1554 // is about to be deleted, but this will make the metadata reflect an
+
1555 // accurate state at the time of deletion.
+
1556 line->clearFlag(lsfLowReserve);
+
1557 }
+
1558
+
1559 if (line->isFlag(lsfHighReserve))
+
1560 {
+
1561 // Clear reserve for high account.
+
1562 auto sleHighAccount =
+
1563 view.peek(keylet::account(line->at(sfHighLimit)->getIssuer()));
+
1564 if (!sleHighAccount)
+
1565 return tecINTERNAL; // LCOV_EXCL_LINE
+
1566
+
1567 adjustOwnerCount(view, sleHighAccount, -1, journal);
+
1568 // It's not really necessary to clear the reserve flag, since the line
+
1569 // is about to be deleted, but this will make the metadata reflect an
+
1570 // accurate state at the time of deletion.
+
1571 line->clearFlag(lsfHighReserve);
+
1572 }
+
1573
+
1574 return trustDelete(
+
1575 view,
+
1576 line,
+
1577 line->at(sfLowLimit)->getIssuer(),
+
1578 line->at(sfHighLimit)->getIssuer(),
+
1579 journal);
+
1580}
-
1599
-
1600TER
-
- -
1602 ApplyView& view,
-
1603 std::shared_ptr<SLE> const& sleRippleState,
-
1604 AccountID const& uLowAccountID,
-
1605 AccountID const& uHighAccountID,
- -
1607{
-
1608 // Detect legacy dirs.
-
1609 std::uint64_t uLowNode = sleRippleState->getFieldU64(sfLowNode);
-
1610 std::uint64_t uHighNode = sleRippleState->getFieldU64(sfHighNode);
-
1611
-
1612 JLOG(j.trace()) << "trustDelete: Deleting ripple line: low";
-
1613
-
1614 if (!view.dirRemove(
-
1615 keylet::ownerDir(uLowAccountID),
-
1616 uLowNode,
-
1617 sleRippleState->key(),
-
1618 false))
-
1619 {
-
1620 return tefBAD_LEDGER; // LCOV_EXCL_LINE
-
1621 }
-
1622
-
1623 JLOG(j.trace()) << "trustDelete: Deleting ripple line: high";
-
1624
-
1625 if (!view.dirRemove(
-
1626 keylet::ownerDir(uHighAccountID),
-
1627 uHighNode,
-
1628 sleRippleState->key(),
-
1629 false))
-
1630 {
-
1631 return tefBAD_LEDGER; // LCOV_EXCL_LINE
-
1632 }
-
1633
-
1634 JLOG(j.trace()) << "trustDelete: Deleting ripple line: state";
-
1635 view.erase(sleRippleState);
-
1636
-
1637 return tesSUCCESS;
-
1638}
+
1581
+
1582[[nodiscard]] TER
+
+ +
1584 ApplyView& view,
+
1585 AccountID const& accountID,
+
1586 MPTIssue const& mptIssue,
+
1587 beast::Journal journal)
+
1588{
+
1589 auto const& mptID = mptIssue.getMptID();
+
1590 auto const mptoken = view.peek(keylet::mptoken(mptID, accountID));
+
1591 if (!mptoken)
+
1592 return tecOBJECT_NOT_FOUND;
+
1593 if (mptoken->at(sfMPTAmount) != 0)
+
1594 return tecHAS_OBLIGATIONS;
+
1595
+
1596 return authorizeMPToken(
+
1597 view,
+
1598 {}, // priorBalance
+
1599 mptID,
+
1600 accountID,
+
1601 journal,
+
1602 tfMPTUnauthorize // flags
+
1603 );
+
1604}
+
1605
+
1606TER
+
+ +
1608 ApplyView& view,
+
1609 std::shared_ptr<SLE> const& sleRippleState,
+
1610 AccountID const& uLowAccountID,
+
1611 AccountID const& uHighAccountID,
+ +
1613{
+
1614 // Detect legacy dirs.
+
1615 std::uint64_t uLowNode = sleRippleState->getFieldU64(sfLowNode);
+
1616 std::uint64_t uHighNode = sleRippleState->getFieldU64(sfHighNode);
+
1617
+
1618 JLOG(j.trace()) << "trustDelete: Deleting ripple line: low";
+
1619
+
1620 if (!view.dirRemove(
+
1621 keylet::ownerDir(uLowAccountID),
+
1622 uLowNode,
+
1623 sleRippleState->key(),
+
1624 false))
+
1625 {
+
1626 return tefBAD_LEDGER; // LCOV_EXCL_LINE
+
1627 }
+
1628
+
1629 JLOG(j.trace()) << "trustDelete: Deleting ripple line: high";
+
1630
+
1631 if (!view.dirRemove(
+
1632 keylet::ownerDir(uHighAccountID),
+
1633 uHighNode,
+
1634 sleRippleState->key(),
+
1635 false))
+
1636 {
+
1637 return tefBAD_LEDGER; // LCOV_EXCL_LINE
+
1638 }
1639
-
1640TER
-
- -
1642{
-
1643 if (!sle)
-
1644 return tesSUCCESS;
-
1645 auto offerIndex = sle->key();
-
1646 auto owner = sle->getAccountID(sfAccount);
-
1647
-
1648 // Detect legacy directories.
-
1649 uint256 uDirectory = sle->getFieldH256(sfBookDirectory);
-
1650
-
1651 if (!view.dirRemove(
-
1652 keylet::ownerDir(owner),
-
1653 sle->getFieldU64(sfOwnerNode),
-
1654 offerIndex,
-
1655 false))
-
1656 {
-
1657 return tefBAD_LEDGER; // LCOV_EXCL_LINE
-
1658 }
-
1659
-
1660 if (!view.dirRemove(
-
1661 keylet::page(uDirectory),
-
1662 sle->getFieldU64(sfBookNode),
-
1663 offerIndex,
-
1664 false))
-
1665 {
-
1666 return tefBAD_LEDGER; // LCOV_EXCL_LINE
-
1667 }
-
1668
-
1669 if (sle->isFieldPresent(sfAdditionalBooks))
-
1670 {
-
1671 XRPL_ASSERT(
-
1672 sle->isFlag(lsfHybrid) && sle->isFieldPresent(sfDomainID),
-
1673 "ripple::offerDelete : should be a hybrid domain offer");
+
1640 JLOG(j.trace()) << "trustDelete: Deleting ripple line: state";
+
1641 view.erase(sleRippleState);
+
1642
+
1643 return tesSUCCESS;
+
1644}
+
+
1645
+
1646TER
+
+ +
1648{
+
1649 if (!sle)
+
1650 return tesSUCCESS;
+
1651 auto offerIndex = sle->key();
+
1652 auto owner = sle->getAccountID(sfAccount);
+
1653
+
1654 // Detect legacy directories.
+
1655 uint256 uDirectory = sle->getFieldH256(sfBookDirectory);
+
1656
+
1657 if (!view.dirRemove(
+
1658 keylet::ownerDir(owner),
+
1659 sle->getFieldU64(sfOwnerNode),
+
1660 offerIndex,
+
1661 false))
+
1662 {
+
1663 return tefBAD_LEDGER; // LCOV_EXCL_LINE
+
1664 }
+
1665
+
1666 if (!view.dirRemove(
+
1667 keylet::page(uDirectory),
+
1668 sle->getFieldU64(sfBookNode),
+
1669 offerIndex,
+
1670 false))
+
1671 {
+
1672 return tefBAD_LEDGER; // LCOV_EXCL_LINE
+
1673 }
1674
-
1675 auto const& additionalBookDirs = sle->getFieldArray(sfAdditionalBooks);
-
1676
-
1677 for (auto const& bookDir : additionalBookDirs)
-
1678 {
-
1679 auto const& dirIndex = bookDir.getFieldH256(sfBookDirectory);
-
1680 auto const& dirNode = bookDir.getFieldU64(sfBookNode);
-
1681
-
1682 if (!view.dirRemove(
-
1683 keylet::page(dirIndex), dirNode, offerIndex, false))
-
1684 {
-
1685 return tefBAD_LEDGER; // LCOV_EXCL_LINE
-
1686 }
-
1687 }
-
1688 }
-
1689
-
1690 adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j);
-
1691
-
1692 view.erase(sle);
-
1693
-
1694 return tesSUCCESS;
-
1695}
+
1675 if (sle->isFieldPresent(sfAdditionalBooks))
+
1676 {
+
1677 XRPL_ASSERT(
+
1678 sle->isFlag(lsfHybrid) && sle->isFieldPresent(sfDomainID),
+
1679 "ripple::offerDelete : should be a hybrid domain offer");
+
1680
+
1681 auto const& additionalBookDirs = sle->getFieldArray(sfAdditionalBooks);
+
1682
+
1683 for (auto const& bookDir : additionalBookDirs)
+
1684 {
+
1685 auto const& dirIndex = bookDir.getFieldH256(sfBookDirectory);
+
1686 auto const& dirNode = bookDir.getFieldU64(sfBookNode);
+
1687
+
1688 if (!view.dirRemove(
+
1689 keylet::page(dirIndex), dirNode, offerIndex, false))
+
1690 {
+
1691 return tefBAD_LEDGER; // LCOV_EXCL_LINE
+
1692 }
+
1693 }
+
1694 }
+
1695
+
1696 adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j);
+
1697
+
1698 view.erase(sle);
+
1699
+
1700 return tesSUCCESS;
+
1701}
-
1696
-
1697// Direct send w/o fees:
-
1698// - Redeeming IOUs and/or sending sender's own IOUs.
-
1699// - Create trust line if needed.
-
1700// --> bCheckIssuer : normally require issuer to be involved.
-
1701static TER
-
- -
1703 ApplyView& view,
-
1704 AccountID const& uSenderID,
-
1705 AccountID const& uReceiverID,
-
1706 STAmount const& saAmount,
-
1707 bool bCheckIssuer,
- -
1709{
-
1710 AccountID const& issuer = saAmount.getIssuer();
-
1711 Currency const& currency = saAmount.getCurrency();
-
1712
-
1713 // Make sure issuer is involved.
-
1714 XRPL_ASSERT(
-
1715 !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer,
-
1716 "ripple::rippleCreditIOU : matching issuer or don't care");
-
1717 (void)issuer;
+
1702
+
1703// Direct send w/o fees:
+
1704// - Redeeming IOUs and/or sending sender's own IOUs.
+
1705// - Create trust line if needed.
+
1706// --> bCheckIssuer : normally require issuer to be involved.
+
1707static TER
+
+ +
1709 ApplyView& view,
+
1710 AccountID const& uSenderID,
+
1711 AccountID const& uReceiverID,
+
1712 STAmount const& saAmount,
+
1713 bool bCheckIssuer,
+ +
1715{
+
1716 AccountID const& issuer = saAmount.getIssuer();
+
1717 Currency const& currency = saAmount.getCurrency();
1718
-
1719 // Disallow sending to self.
+
1719 // Make sure issuer is involved.
1720 XRPL_ASSERT(
-
1721 uSenderID != uReceiverID,
-
1722 "ripple::rippleCreditIOU : sender is not receiver");
-
1723
-
1724 bool const bSenderHigh = uSenderID > uReceiverID;
-
1725 auto const index = keylet::line(uSenderID, uReceiverID, currency);
-
1726
-
1727 XRPL_ASSERT(
-
1728 !isXRP(uSenderID) && uSenderID != noAccount(),
-
1729 "ripple::rippleCreditIOU : sender is not XRP");
-
1730 XRPL_ASSERT(
-
1731 !isXRP(uReceiverID) && uReceiverID != noAccount(),
-
1732 "ripple::rippleCreditIOU : receiver is not XRP");
-
1733
-
1734 // If the line exists, modify it accordingly.
-
1735 if (auto const sleRippleState = view.peek(index))
-
1736 {
-
1737 STAmount saBalance = sleRippleState->getFieldAmount(sfBalance);
-
1738
-
1739 if (bSenderHigh)
-
1740 saBalance.negate(); // Put balance in sender terms.
-
1741
-
1742 view.creditHook(uSenderID, uReceiverID, saAmount, saBalance);
-
1743
-
1744 STAmount const saBefore = saBalance;
-
1745
-
1746 saBalance -= saAmount;
+
1721 !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer,
+
1722 "ripple::rippleCreditIOU : matching issuer or don't care");
+
1723 (void)issuer;
+
1724
+
1725 // Disallow sending to self.
+
1726 XRPL_ASSERT(
+
1727 uSenderID != uReceiverID,
+
1728 "ripple::rippleCreditIOU : sender is not receiver");
+
1729
+
1730 bool const bSenderHigh = uSenderID > uReceiverID;
+
1731 auto const index = keylet::line(uSenderID, uReceiverID, currency);
+
1732
+
1733 XRPL_ASSERT(
+
1734 !isXRP(uSenderID) && uSenderID != noAccount(),
+
1735 "ripple::rippleCreditIOU : sender is not XRP");
+
1736 XRPL_ASSERT(
+
1737 !isXRP(uReceiverID) && uReceiverID != noAccount(),
+
1738 "ripple::rippleCreditIOU : receiver is not XRP");
+
1739
+
1740 // If the line exists, modify it accordingly.
+
1741 if (auto const sleRippleState = view.peek(index))
+
1742 {
+
1743 STAmount saBalance = sleRippleState->getFieldAmount(sfBalance);
+
1744
+
1745 if (bSenderHigh)
+
1746 saBalance.negate(); // Put balance in sender terms.
1747
-
1748 JLOG(j.trace()) << "rippleCreditIOU: " << to_string(uSenderID) << " -> "
-
1749 << to_string(uReceiverID)
-
1750 << " : before=" << saBefore.getFullText()
-
1751 << " amount=" << saAmount.getFullText()
-
1752 << " after=" << saBalance.getFullText();
+
1748 view.creditHook(uSenderID, uReceiverID, saAmount, saBalance);
+
1749
+
1750 STAmount const saBefore = saBalance;
+
1751
+
1752 saBalance -= saAmount;
1753
-
1754 std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags));
-
1755 bool bDelete = false;
-
1756
-
1757 // FIXME This NEEDS to be cleaned up and simplified. It's impossible
-
1758 // for anyone to understand.
-
1759 if (saBefore > beast::zero
-
1760 // Sender balance was positive.
-
1761 && saBalance <= beast::zero
-
1762 // Sender is zero or negative.
-
1763 && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
-
1764 // Sender reserve is set.
-
1765 &&
-
1766 static_cast<bool>(
-
1767 uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
-
1768 static_cast<bool>(
-
1769 view.read(keylet::account(uSenderID))->getFlags() &
- -
1771 !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
-
1772 !sleRippleState->getFieldAmount(
-
1773 !bSenderHigh ? sfLowLimit : sfHighLimit)
-
1774 // Sender trust limit is 0.
-
1775 && !sleRippleState->getFieldU32(
-
1776 !bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
-
1777 // Sender quality in is 0.
-
1778 && !sleRippleState->getFieldU32(
-
1779 !bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
-
1780 // Sender quality out is 0.
-
1781 {
-
1782 // Clear the reserve of the sender, possibly delete the line!
- -
1784 view, view.peek(keylet::account(uSenderID)), -1, j);
-
1785
-
1786 // Clear reserve flag.
-
1787 sleRippleState->setFieldU32(
-
1788 sfFlags,
-
1789 uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
-
1790
-
1791 // Balance is zero, receiver reserve is clear.
-
1792 bDelete = !saBalance // Balance is zero.
-
1793 && !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve));
-
1794 // Receiver reserve is clear.
-
1795 }
+
1754 JLOG(j.trace()) << "rippleCreditIOU: " << to_string(uSenderID) << " -> "
+
1755 << to_string(uReceiverID)
+
1756 << " : before=" << saBefore.getFullText()
+
1757 << " amount=" << saAmount.getFullText()
+
1758 << " after=" << saBalance.getFullText();
+
1759
+
1760 std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags));
+
1761 bool bDelete = false;
+
1762
+
1763 // FIXME This NEEDS to be cleaned up and simplified. It's impossible
+
1764 // for anyone to understand.
+
1765 if (saBefore > beast::zero
+
1766 // Sender balance was positive.
+
1767 && saBalance <= beast::zero
+
1768 // Sender is zero or negative.
+
1769 && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
+
1770 // Sender reserve is set.
+
1771 &&
+
1772 static_cast<bool>(
+
1773 uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
+
1774 static_cast<bool>(
+
1775 view.read(keylet::account(uSenderID))->getFlags() &
+ +
1777 !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
+
1778 !sleRippleState->getFieldAmount(
+
1779 !bSenderHigh ? sfLowLimit : sfHighLimit)
+
1780 // Sender trust limit is 0.
+
1781 && !sleRippleState->getFieldU32(
+
1782 !bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
+
1783 // Sender quality in is 0.
+
1784 && !sleRippleState->getFieldU32(
+
1785 !bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
+
1786 // Sender quality out is 0.
+
1787 {
+
1788 // Clear the reserve of the sender, possibly delete the line!
+ +
1790 view, view.peek(keylet::account(uSenderID)), -1, j);
+
1791
+
1792 // Clear reserve flag.
+
1793 sleRippleState->setFieldU32(
+
1794 sfFlags,
+
1795 uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
1796
-
1797 if (bSenderHigh)
-
1798 saBalance.negate();
-
1799
-
1800 // Want to reflect balance to zero even if we are deleting line.
-
1801 sleRippleState->setFieldAmount(sfBalance, saBalance);
-
1802 // ONLY: Adjust ripple balance.
-
1803
-
1804 if (bDelete)
-
1805 {
-
1806 return trustDelete(
-
1807 view,
-
1808 sleRippleState,
-
1809 bSenderHigh ? uReceiverID : uSenderID,
-
1810 !bSenderHigh ? uReceiverID : uSenderID,
-
1811 j);
-
1812 }
-
1813
-
1814 view.update(sleRippleState);
-
1815 return tesSUCCESS;
-
1816 }
-
1817
-
1818 STAmount const saReceiverLimit(Issue{currency, uReceiverID});
-
1819 STAmount saBalance{saAmount};
-
1820
-
1821 saBalance.setIssuer(noAccount());
-
1822
-
1823 JLOG(j.debug()) << "rippleCreditIOU: "
-
1824 "create line: "
-
1825 << to_string(uSenderID) << " -> " << to_string(uReceiverID)
-
1826 << " : " << saAmount.getFullText();
-
1827
-
1828 auto const sleAccount = view.peek(keylet::account(uReceiverID));
-
1829 if (!sleAccount)
-
1830 return tefINTERNAL; // LCOV_EXCL_LINE
-
1831
-
1832 bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0;
+
1797 // Balance is zero, receiver reserve is clear.
+
1798 bDelete = !saBalance // Balance is zero.
+
1799 && !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve));
+
1800 // Receiver reserve is clear.
+
1801 }
+
1802
+
1803 if (bSenderHigh)
+
1804 saBalance.negate();
+
1805
+
1806 // Want to reflect balance to zero even if we are deleting line.
+
1807 sleRippleState->setFieldAmount(sfBalance, saBalance);
+
1808 // ONLY: Adjust ripple balance.
+
1809
+
1810 if (bDelete)
+
1811 {
+
1812 return trustDelete(
+
1813 view,
+
1814 sleRippleState,
+
1815 bSenderHigh ? uReceiverID : uSenderID,
+
1816 !bSenderHigh ? uReceiverID : uSenderID,
+
1817 j);
+
1818 }
+
1819
+
1820 view.update(sleRippleState);
+
1821 return tesSUCCESS;
+
1822 }
+
1823
+
1824 STAmount const saReceiverLimit(Issue{currency, uReceiverID});
+
1825 STAmount saBalance{saAmount};
+
1826
+
1827 saBalance.setIssuer(noAccount());
+
1828
+
1829 JLOG(j.debug()) << "rippleCreditIOU: "
+
1830 "create line: "
+
1831 << to_string(uSenderID) << " -> " << to_string(uReceiverID)
+
1832 << " : " << saAmount.getFullText();
1833
-
1834 return trustCreate(
-
1835 view,
-
1836 bSenderHigh,
-
1837 uSenderID,
-
1838 uReceiverID,
-
1839 index.key,
-
1840 sleAccount,
-
1841 false,
-
1842 noRipple,
-
1843 false,
-
1844 false,
-
1845 saBalance,
-
1846 saReceiverLimit,
-
1847 0,
-
1848 0,
-
1849 j);
-
1850}
+
1834 auto const sleAccount = view.peek(keylet::account(uReceiverID));
+
1835 if (!sleAccount)
+
1836 return tefINTERNAL; // LCOV_EXCL_LINE
+
1837
+
1838 bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0;
+
1839
+
1840 return trustCreate(
+
1841 view,
+
1842 bSenderHigh,
+
1843 uSenderID,
+
1844 uReceiverID,
+
1845 index.key,
+
1846 sleAccount,
+
1847 false,
+
1848 noRipple,
+
1849 false,
+
1850 false,
+
1851 saBalance,
+
1852 saReceiverLimit,
+
1853 0,
+
1854 0,
+
1855 j);
+
1856}
-
1851
-
1852// Send regardless of limits.
-
1853// --> saAmount: Amount/currency/issuer to deliver to receiver.
-
1854// <-- saActual: Amount actually cost. Sender pays fees.
-
1855static TER
-
- -
1857 ApplyView& view,
-
1858 AccountID const& uSenderID,
-
1859 AccountID const& uReceiverID,
-
1860 STAmount const& saAmount,
-
1861 STAmount& saActual,
- -
1863 WaiveTransferFee waiveFee)
-
1864{
-
1865 auto const issuer = saAmount.getIssuer();
-
1866
-
1867 XRPL_ASSERT(
-
1868 !isXRP(uSenderID) && !isXRP(uReceiverID),
-
1869 "ripple::rippleSendIOU : neither sender nor receiver is XRP");
-
1870 XRPL_ASSERT(
-
1871 uSenderID != uReceiverID,
-
1872 "ripple::rippleSendIOU : sender is not receiver");
-
1873
-
1874 if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount())
-
1875 {
-
1876 // Direct send: redeeming IOUs and/or sending own IOUs.
-
1877 auto const ter =
-
1878 rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, j);
-
1879 if (view.rules().enabled(featureDeletableAccounts) && ter != tesSUCCESS)
-
1880 return ter;
-
1881 saActual = saAmount;
-
1882 return tesSUCCESS;
-
1883 }
-
1884
-
1885 // Sending 3rd party IOUs: transit.
-
1886
-
1887 // Calculate the amount to transfer accounting
-
1888 // for any transfer fees if the fee is not waived:
-
1889 saActual = (waiveFee == WaiveTransferFee::Yes)
-
1890 ? saAmount
-
1891 : multiply(saAmount, transferRate(view, issuer));
+
1857
+
1858// Send regardless of limits.
+
1859// --> saAmount: Amount/currency/issuer to deliver to receiver.
+
1860// <-- saActual: Amount actually cost. Sender pays fees.
+
1861static TER
+
+ +
1863 ApplyView& view,
+
1864 AccountID const& uSenderID,
+
1865 AccountID const& uReceiverID,
+
1866 STAmount const& saAmount,
+
1867 STAmount& saActual,
+ +
1869 WaiveTransferFee waiveFee)
+
1870{
+
1871 auto const issuer = saAmount.getIssuer();
+
1872
+
1873 XRPL_ASSERT(
+
1874 !isXRP(uSenderID) && !isXRP(uReceiverID),
+
1875 "ripple::rippleSendIOU : neither sender nor receiver is XRP");
+
1876 XRPL_ASSERT(
+
1877 uSenderID != uReceiverID,
+
1878 "ripple::rippleSendIOU : sender is not receiver");
+
1879
+
1880 if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount())
+
1881 {
+
1882 // Direct send: redeeming IOUs and/or sending own IOUs.
+
1883 auto const ter =
+
1884 rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, j);
+
1885 if (view.rules().enabled(featureDeletableAccounts) && ter != tesSUCCESS)
+
1886 return ter;
+
1887 saActual = saAmount;
+
1888 return tesSUCCESS;
+
1889 }
+
1890
+
1891 // Sending 3rd party IOUs: transit.
1892
-
1893 JLOG(j.debug()) << "rippleSendIOU> " << to_string(uSenderID) << " - > "
-
1894 << to_string(uReceiverID)
-
1895 << " : deliver=" << saAmount.getFullText()
-
1896 << " cost=" << saActual.getFullText();
-
1897
-
1898 TER terResult =
-
1899 rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, j);
-
1900
-
1901 if (tesSUCCESS == terResult)
-
1902 terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, j);
+
1893 // Calculate the amount to transfer accounting
+
1894 // for any transfer fees if the fee is not waived:
+
1895 saActual = (waiveFee == WaiveTransferFee::Yes)
+
1896 ? saAmount
+
1897 : multiply(saAmount, transferRate(view, issuer));
+
1898
+
1899 JLOG(j.debug()) << "rippleSendIOU> " << to_string(uSenderID) << " - > "
+
1900 << to_string(uReceiverID)
+
1901 << " : deliver=" << saAmount.getFullText()
+
1902 << " cost=" << saActual.getFullText();
1903
-
1904 return terResult;
-
1905}
-
+
1904 TER terResult =
+
1905 rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, j);
1906
-
1907static TER
-
- -
1909 ApplyView& view,
-
1910 AccountID const& uSenderID,
-
1911 AccountID const& uReceiverID,
-
1912 STAmount const& saAmount,
- -
1914 WaiveTransferFee waiveFee)
-
1915{
-
1916 if (view.rules().enabled(fixAMMv1_1))
-
1917 {
-
1918 if (saAmount < beast::zero || saAmount.holds<MPTIssue>())
-
1919 {
-
1920 return tecINTERNAL; // LCOV_EXCL_LINE
-
1921 }
-
1922 }
-
1923 else
-
1924 {
-
1925 // LCOV_EXCL_START
-
1926 XRPL_ASSERT(
-
1927 saAmount >= beast::zero && !saAmount.holds<MPTIssue>(),
-
1928 "ripple::accountSendIOU : minimum amount and not MPT");
-
1929 // LCOV_EXCL_STOP
-
1930 }
-
1931
-
1932 /* If we aren't sending anything or if the sender is the same as the
-
1933 * receiver then we don't need to do anything.
-
1934 */
-
1935 if (!saAmount || (uSenderID == uReceiverID))
-
1936 return tesSUCCESS;
+
1907 if (tesSUCCESS == terResult)
+
1908 terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, j);
+
1909
+
1910 return terResult;
+
1911}
+
+
1912
+
1913static TER
+
+ +
1915 ApplyView& view,
+
1916 AccountID const& uSenderID,
+
1917 AccountID const& uReceiverID,
+
1918 STAmount const& saAmount,
+ +
1920 WaiveTransferFee waiveFee)
+
1921{
+
1922 if (view.rules().enabled(fixAMMv1_1))
+
1923 {
+
1924 if (saAmount < beast::zero || saAmount.holds<MPTIssue>())
+
1925 {
+
1926 return tecINTERNAL; // LCOV_EXCL_LINE
+
1927 }
+
1928 }
+
1929 else
+
1930 {
+
1931 // LCOV_EXCL_START
+
1932 XRPL_ASSERT(
+
1933 saAmount >= beast::zero && !saAmount.holds<MPTIssue>(),
+
1934 "ripple::accountSendIOU : minimum amount and not MPT");
+
1935 // LCOV_EXCL_STOP
+
1936 }
1937
-
1938 if (!saAmount.native())
-
1939 {
-
1940 STAmount saActual;
-
1941
-
1942 JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> "
-
1943 << to_string(uReceiverID) << " : "
-
1944 << saAmount.getFullText();
-
1945
-
1946 return rippleSendIOU(
-
1947 view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
-
1948 }
-
1949
-
1950 /* XRP send which does not check reserve and can do pure adjustment.
-
1951 * Note that sender or receiver may be null and this not a mistake; this
-
1952 * setup is used during pathfinding and it is carefully controlled to
-
1953 * ensure that transfers are balanced.
-
1954 */
-
1955 TER terResult(tesSUCCESS);
-
1956
-
1957 SLE::pointer sender = uSenderID != beast::zero
-
1958 ? view.peek(keylet::account(uSenderID))
-
1959 : SLE::pointer();
-
1960 SLE::pointer receiver = uReceiverID != beast::zero
-
1961 ? view.peek(keylet::account(uReceiverID))
-
1962 : SLE::pointer();
-
1963
-
1964 if (auto stream = j.trace())
-
1965 {
-
1966 std::string sender_bal("-");
-
1967 std::string receiver_bal("-");
-
1968
-
1969 if (sender)
-
1970 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
-
1971
-
1972 if (receiver)
-
1973 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
+
1938 /* If we aren't sending anything or if the sender is the same as the
+
1939 * receiver then we don't need to do anything.
+
1940 */
+
1941 if (!saAmount || (uSenderID == uReceiverID))
+
1942 return tesSUCCESS;
+
1943
+
1944 if (!saAmount.native())
+
1945 {
+
1946 STAmount saActual;
+
1947
+
1948 JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> "
+
1949 << to_string(uReceiverID) << " : "
+
1950 << saAmount.getFullText();
+
1951
+
1952 return rippleSendIOU(
+
1953 view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
+
1954 }
+
1955
+
1956 /* XRP send which does not check reserve and can do pure adjustment.
+
1957 * Note that sender or receiver may be null and this not a mistake; this
+
1958 * setup is used during pathfinding and it is carefully controlled to
+
1959 * ensure that transfers are balanced.
+
1960 */
+
1961 TER terResult(tesSUCCESS);
+
1962
+
1963 SLE::pointer sender = uSenderID != beast::zero
+
1964 ? view.peek(keylet::account(uSenderID))
+
1965 : SLE::pointer();
+
1966 SLE::pointer receiver = uReceiverID != beast::zero
+
1967 ? view.peek(keylet::account(uReceiverID))
+
1968 : SLE::pointer();
+
1969
+
1970 if (auto stream = j.trace())
+
1971 {
+
1972 std::string sender_bal("-");
+
1973 std::string receiver_bal("-");
1974
-
1975 stream << "accountSendIOU> " << to_string(uSenderID) << " ("
-
1976 << sender_bal << ") -> " << to_string(uReceiverID) << " ("
-
1977 << receiver_bal << ") : " << saAmount.getFullText();
-
1978 }
-
1979
-
1980 if (sender)
-
1981 {
-
1982 if (sender->getFieldAmount(sfBalance) < saAmount)
-
1983 {
-
1984 // VFALCO Its laborious to have to mutate the
-
1985 // TER based on params everywhere
-
1986 // LCOV_EXCL_START
-
1987 terResult = view.open() ? TER{telFAILED_PROCESSING}
- -
1989 // LCOV_EXCL_STOP
-
1990 }
-
1991 else
-
1992 {
-
1993 auto const sndBal = sender->getFieldAmount(sfBalance);
-
1994 view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal);
-
1995
-
1996 // Decrement XRP balance.
-
1997 sender->setFieldAmount(sfBalance, sndBal - saAmount);
-
1998 view.update(sender);
-
1999 }
-
2000 }
+
1975 if (sender)
+
1976 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
+
1977
+
1978 if (receiver)
+
1979 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
+
1980
+
1981 stream << "accountSendIOU> " << to_string(uSenderID) << " ("
+
1982 << sender_bal << ") -> " << to_string(uReceiverID) << " ("
+
1983 << receiver_bal << ") : " << saAmount.getFullText();
+
1984 }
+
1985
+
1986 if (sender)
+
1987 {
+
1988 if (sender->getFieldAmount(sfBalance) < saAmount)
+
1989 {
+
1990 // VFALCO Its laborious to have to mutate the
+
1991 // TER based on params everywhere
+
1992 // LCOV_EXCL_START
+
1993 terResult = view.open() ? TER{telFAILED_PROCESSING}
+ +
1995 // LCOV_EXCL_STOP
+
1996 }
+
1997 else
+
1998 {
+
1999 auto const sndBal = sender->getFieldAmount(sfBalance);
+
2000 view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal);
2001
-
2002 if (tesSUCCESS == terResult && receiver)
-
2003 {
-
2004 // Increment XRP balance.
-
2005 auto const rcvBal = receiver->getFieldAmount(sfBalance);
-
2006 receiver->setFieldAmount(sfBalance, rcvBal + saAmount);
-
2007 view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal);
-
2008
-
2009 view.update(receiver);
-
2010 }
-
2011
-
2012 if (auto stream = j.trace())
-
2013 {
-
2014 std::string sender_bal("-");
-
2015 std::string receiver_bal("-");
-
2016
-
2017 if (sender)
-
2018 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
-
2019
-
2020 if (receiver)
-
2021 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
+
2002 // Decrement XRP balance.
+
2003 sender->setFieldAmount(sfBalance, sndBal - saAmount);
+
2004 view.update(sender);
+
2005 }
+
2006 }
+
2007
+
2008 if (tesSUCCESS == terResult && receiver)
+
2009 {
+
2010 // Increment XRP balance.
+
2011 auto const rcvBal = receiver->getFieldAmount(sfBalance);
+
2012 receiver->setFieldAmount(sfBalance, rcvBal + saAmount);
+
2013 view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal);
+
2014
+
2015 view.update(receiver);
+
2016 }
+
2017
+
2018 if (auto stream = j.trace())
+
2019 {
+
2020 std::string sender_bal("-");
+
2021 std::string receiver_bal("-");
2022
-
2023 stream << "accountSendIOU< " << to_string(uSenderID) << " ("
-
2024 << sender_bal << ") -> " << to_string(uReceiverID) << " ("
-
2025 << receiver_bal << ") : " << saAmount.getFullText();
-
2026 }
-
2027
-
2028 return terResult;
-
2029}
+
2023 if (sender)
+
2024 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
+
2025
+
2026 if (receiver)
+
2027 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
+
2028
+
2029 stream << "accountSendIOU< " << to_string(uSenderID) << " ("
+
2030 << sender_bal << ") -> " << to_string(uReceiverID) << " ("
+
2031 << receiver_bal << ") : " << saAmount.getFullText();
+
2032 }
+
2033
+
2034 return terResult;
+
2035}
-
2030
-
2031static TER
-
- -
2033 ApplyView& view,
-
2034 AccountID const& uSenderID,
-
2035 AccountID const& uReceiverID,
-
2036 STAmount const& saAmount,
- -
2038{
-
2039 // Do not check MPT authorization here - it must have been checked earlier
-
2040 auto const mptID = keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID());
-
2041 auto const issuer = saAmount.getIssuer();
-
2042 auto sleIssuance = view.peek(mptID);
-
2043 if (!sleIssuance)
-
2044 return tecOBJECT_NOT_FOUND;
-
2045 if (uSenderID == issuer)
-
2046 {
-
2047 (*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value();
-
2048 view.update(sleIssuance);
-
2049 }
-
2050 else
-
2051 {
-
2052 auto const mptokenID = keylet::mptoken(mptID.key, uSenderID);
-
2053 if (auto sle = view.peek(mptokenID))
-
2054 {
-
2055 auto const amt = sle->getFieldU64(sfMPTAmount);
-
2056 auto const pay = saAmount.mpt().value();
-
2057 if (amt < pay)
-
2058 return tecINSUFFICIENT_FUNDS;
-
2059 (*sle)[sfMPTAmount] = amt - pay;
-
2060 view.update(sle);
-
2061 }
-
2062 else
-
2063 return tecNO_AUTH;
-
2064 }
-
2065
-
2066 if (uReceiverID == issuer)
-
2067 {
-
2068 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
-
2069 auto const redeem = saAmount.mpt().value();
-
2070 if (outstanding >= redeem)
-
2071 {
-
2072 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
-
2073 view.update(sleIssuance);
-
2074 }
-
2075 else
-
2076 return tecINTERNAL; // LCOV_EXCL_LINE
-
2077 }
-
2078 else
-
2079 {
-
2080 auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID);
-
2081 if (auto sle = view.peek(mptokenID))
-
2082 {
-
2083 (*sle)[sfMPTAmount] += saAmount.mpt().value();
-
2084 view.update(sle);
-
2085 }
-
2086 else
-
2087 return tecNO_AUTH;
-
2088 }
-
2089
-
2090 return tesSUCCESS;
-
2091}
+
2036
+
2037static TER
+
+ +
2039 ApplyView& view,
+
2040 AccountID const& uSenderID,
+
2041 AccountID const& uReceiverID,
+
2042 STAmount const& saAmount,
+ +
2044{
+
2045 // Do not check MPT authorization here - it must have been checked earlier
+
2046 auto const mptID = keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID());
+
2047 auto const issuer = saAmount.getIssuer();
+
2048 auto sleIssuance = view.peek(mptID);
+
2049 if (!sleIssuance)
+
2050 return tecOBJECT_NOT_FOUND;
+
2051 if (uSenderID == issuer)
+
2052 {
+
2053 (*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value();
+
2054 view.update(sleIssuance);
+
2055 }
+
2056 else
+
2057 {
+
2058 auto const mptokenID = keylet::mptoken(mptID.key, uSenderID);
+
2059 if (auto sle = view.peek(mptokenID))
+
2060 {
+
2061 auto const amt = sle->getFieldU64(sfMPTAmount);
+
2062 auto const pay = saAmount.mpt().value();
+
2063 if (amt < pay)
+
2064 return tecINSUFFICIENT_FUNDS;
+
2065 (*sle)[sfMPTAmount] = amt - pay;
+
2066 view.update(sle);
+
2067 }
+
2068 else
+
2069 return tecNO_AUTH;
+
2070 }
+
2071
+
2072 if (uReceiverID == issuer)
+
2073 {
+
2074 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
+
2075 auto const redeem = saAmount.mpt().value();
+
2076 if (outstanding >= redeem)
+
2077 {
+
2078 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
+
2079 view.update(sleIssuance);
+
2080 }
+
2081 else
+
2082 return tecINTERNAL; // LCOV_EXCL_LINE
+
2083 }
+
2084 else
+
2085 {
+
2086 auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID);
+
2087 if (auto sle = view.peek(mptokenID))
+
2088 {
+
2089 (*sle)[sfMPTAmount] += saAmount.mpt().value();
+
2090 view.update(sle);
+
2091 }
+
2092 else
+
2093 return tecNO_AUTH;
+
2094 }
+
2095
+
2096 return tesSUCCESS;
+
2097}
-
2092
-
2093static TER
-
- -
2095 ApplyView& view,
-
2096 AccountID const& uSenderID,
-
2097 AccountID const& uReceiverID,
-
2098 STAmount const& saAmount,
-
2099 STAmount& saActual,
- -
2101 WaiveTransferFee waiveFee)
-
2102{
-
2103 XRPL_ASSERT(
-
2104 uSenderID != uReceiverID,
-
2105 "ripple::rippleSendMPT : sender is not receiver");
-
2106
-
2107 // Safe to get MPT since rippleSendMPT is only called by accountSendMPT
-
2108 auto const issuer = saAmount.getIssuer();
-
2109
-
2110 auto const sle =
-
2111 view.read(keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID()));
-
2112 if (!sle)
-
2113 return tecOBJECT_NOT_FOUND;
-
2114
-
2115 if (uSenderID == issuer || uReceiverID == issuer)
-
2116 {
-
2117 // if sender is issuer, check that the new OutstandingAmount will not
-
2118 // exceed MaximumAmount
-
2119 if (uSenderID == issuer)
-
2120 {
-
2121 auto const sendAmount = saAmount.mpt().value();
-
2122 auto const maximumAmount =
-
2123 sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
-
2124 if (sendAmount > maximumAmount ||
-
2125 sle->getFieldU64(sfOutstandingAmount) >
-
2126 maximumAmount - sendAmount)
-
2127 return tecPATH_DRY;
-
2128 }
-
2129
-
2130 // Direct send: redeeming MPTs and/or sending own MPTs.
-
2131 auto const ter =
-
2132 rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j);
-
2133 if (ter != tesSUCCESS)
-
2134 return ter;
-
2135 saActual = saAmount;
-
2136 return tesSUCCESS;
-
2137 }
-
2138
-
2139 // Sending 3rd party MPTs: transit.
-
2140 saActual = (waiveFee == WaiveTransferFee::Yes)
-
2141 ? saAmount
-
2142 : multiply(
-
2143 saAmount,
-
2144 transferRate(view, saAmount.get<MPTIssue>().getMptID()));
-
2145
-
2146 JLOG(j.debug()) << "rippleSendMPT> " << to_string(uSenderID) << " - > "
-
2147 << to_string(uReceiverID)
-
2148 << " : deliver=" << saAmount.getFullText()
-
2149 << " cost=" << saActual.getFullText();
-
2150
-
2151 if (auto const terResult =
-
2152 rippleCreditMPT(view, issuer, uReceiverID, saAmount, j);
-
2153 terResult != tesSUCCESS)
-
2154 return terResult;
-
2155
-
2156 return rippleCreditMPT(view, uSenderID, issuer, saActual, j);
-
2157}
+
2098
+
2099static TER
+
+ +
2101 ApplyView& view,
+
2102 AccountID const& uSenderID,
+
2103 AccountID const& uReceiverID,
+
2104 STAmount const& saAmount,
+
2105 STAmount& saActual,
+ +
2107 WaiveTransferFee waiveFee)
+
2108{
+
2109 XRPL_ASSERT(
+
2110 uSenderID != uReceiverID,
+
2111 "ripple::rippleSendMPT : sender is not receiver");
+
2112
+
2113 // Safe to get MPT since rippleSendMPT is only called by accountSendMPT
+
2114 auto const issuer = saAmount.getIssuer();
+
2115
+
2116 auto const sle =
+
2117 view.read(keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID()));
+
2118 if (!sle)
+
2119 return tecOBJECT_NOT_FOUND;
+
2120
+
2121 if (uSenderID == issuer || uReceiverID == issuer)
+
2122 {
+
2123 // if sender is issuer, check that the new OutstandingAmount will not
+
2124 // exceed MaximumAmount
+
2125 if (uSenderID == issuer)
+
2126 {
+
2127 auto const sendAmount = saAmount.mpt().value();
+
2128 auto const maximumAmount =
+
2129 sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
+
2130 if (sendAmount > maximumAmount ||
+
2131 sle->getFieldU64(sfOutstandingAmount) >
+
2132 maximumAmount - sendAmount)
+
2133 return tecPATH_DRY;
+
2134 }
+
2135
+
2136 // Direct send: redeeming MPTs and/or sending own MPTs.
+
2137 auto const ter =
+
2138 rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j);
+
2139 if (ter != tesSUCCESS)
+
2140 return ter;
+
2141 saActual = saAmount;
+
2142 return tesSUCCESS;
+
2143 }
+
2144
+
2145 // Sending 3rd party MPTs: transit.
+
2146 saActual = (waiveFee == WaiveTransferFee::Yes)
+
2147 ? saAmount
+
2148 : multiply(
+
2149 saAmount,
+
2150 transferRate(view, saAmount.get<MPTIssue>().getMptID()));
+
2151
+
2152 JLOG(j.debug()) << "rippleSendMPT> " << to_string(uSenderID) << " - > "
+
2153 << to_string(uReceiverID)
+
2154 << " : deliver=" << saAmount.getFullText()
+
2155 << " cost=" << saActual.getFullText();
+
2156
+
2157 if (auto const terResult =
+
2158 rippleCreditMPT(view, issuer, uReceiverID, saAmount, j);
+
2159 terResult != tesSUCCESS)
+
2160 return terResult;
+
2161
+
2162 return rippleCreditMPT(view, uSenderID, issuer, saActual, j);
+
2163}
-
2158
-
2159static TER
-
- -
2161 ApplyView& view,
-
2162 AccountID const& uSenderID,
-
2163 AccountID const& uReceiverID,
-
2164 STAmount const& saAmount,
- -
2166 WaiveTransferFee waiveFee)
-
2167{
-
2168 XRPL_ASSERT(
-
2169 saAmount >= beast::zero && saAmount.holds<MPTIssue>(),
-
2170 "ripple::accountSendMPT : minimum amount and MPT");
-
2171
-
2172 /* If we aren't sending anything or if the sender is the same as the
-
2173 * receiver then we don't need to do anything.
-
2174 */
-
2175 if (!saAmount || (uSenderID == uReceiverID))
-
2176 return tesSUCCESS;
+
2164
+
2165static TER
+
+ +
2167 ApplyView& view,
+
2168 AccountID const& uSenderID,
+
2169 AccountID const& uReceiverID,
+
2170 STAmount const& saAmount,
+ +
2172 WaiveTransferFee waiveFee)
+
2173{
+
2174 XRPL_ASSERT(
+
2175 saAmount >= beast::zero && saAmount.holds<MPTIssue>(),
+
2176 "ripple::accountSendMPT : minimum amount and MPT");
2177
-
2178 STAmount saActual{saAmount.asset()};
-
2179
-
2180 return rippleSendMPT(
-
2181 view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
-
2182}
-
+
2178 /* If we aren't sending anything or if the sender is the same as the
+
2179 * receiver then we don't need to do anything.
+
2180 */
+
2181 if (!saAmount || (uSenderID == uReceiverID))
+
2182 return tesSUCCESS;
2183
-
2184TER
-
- -
2186 ApplyView& view,
-
2187 AccountID const& uSenderID,
-
2188 AccountID const& uReceiverID,
-
2189 STAmount const& saAmount,
- -
2191 WaiveTransferFee waiveFee)
-
2192{
-
2193 return std::visit(
-
2194 [&]<ValidIssueType TIss>(TIss const& issue) {
-
2195 if constexpr (std::is_same_v<TIss, Issue>)
-
2196 return accountSendIOU(
-
2197 view, uSenderID, uReceiverID, saAmount, j, waiveFee);
-
2198 else
-
2199 return accountSendMPT(
-
2200 view, uSenderID, uReceiverID, saAmount, j, waiveFee);
-
2201 },
-
2202 saAmount.asset().value());
-
2203}
+
2184 STAmount saActual{saAmount.asset()};
+
2185
+
2186 return rippleSendMPT(
+
2187 view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
+
2188}
-
2204
-
2205static bool
-
- -
2207 ApplyView& view,
-
2208 SLE::pointer state,
-
2209 bool bSenderHigh,
-
2210 AccountID const& sender,
-
2211 STAmount const& before,
-
2212 STAmount const& after,
- -
2214{
-
2215 if (!state)
-
2216 return false;
-
2217 std::uint32_t const flags(state->getFieldU32(sfFlags));
-
2218
-
2219 auto sle = view.peek(keylet::account(sender));
-
2220 if (!sle)
-
2221 return false;
-
2222
-
2223 // YYY Could skip this if rippling in reverse.
-
2224 if (before > beast::zero
-
2225 // Sender balance was positive.
-
2226 && after <= beast::zero
-
2227 // Sender is zero or negative.
-
2228 && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
-
2229 // Sender reserve is set.
-
2230 && static_cast<bool>(
-
2231 flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
-
2232 static_cast<bool>(sle->getFlags() & lsfDefaultRipple) &&
-
2233 !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
-
2234 !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
-
2235 // Sender trust limit is 0.
-
2236 && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
-
2237 // Sender quality in is 0.
-
2238 &&
-
2239 !state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
-
2240 // Sender quality out is 0.
-
2241 {
-
2242 // VFALCO Where is the line being deleted?
-
2243 // Clear the reserve of the sender, possibly delete the line!
-
2244 adjustOwnerCount(view, sle, -1, j);
-
2245
-
2246 // Clear reserve flag.
-
2247 state->setFieldU32(
-
2248 sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
-
2249
-
2250 // Balance is zero, receiver reserve is clear.
-
2251 if (!after // Balance is zero.
-
2252 && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)))
-
2253 return true;
-
2254 }
-
2255 return false;
-
2256}
+
2189
+
2190TER
+
+ +
2192 ApplyView& view,
+
2193 AccountID const& uSenderID,
+
2194 AccountID const& uReceiverID,
+
2195 STAmount const& saAmount,
+ +
2197 WaiveTransferFee waiveFee)
+
2198{
+
2199 return std::visit(
+
2200 [&]<ValidIssueType TIss>(TIss const& issue) {
+
2201 if constexpr (std::is_same_v<TIss, Issue>)
+
2202 return accountSendIOU(
+
2203 view, uSenderID, uReceiverID, saAmount, j, waiveFee);
+
2204 else
+
2205 return accountSendMPT(
+
2206 view, uSenderID, uReceiverID, saAmount, j, waiveFee);
+
2207 },
+
2208 saAmount.asset().value());
+
2209}
-
2257
-
2258TER
-
- -
2260 ApplyView& view,
-
2261 AccountID const& account,
-
2262 STAmount const& amount,
-
2263 Issue const& issue,
- -
2265{
-
2266 XRPL_ASSERT(
-
2267 !isXRP(account) && !isXRP(issue.account),
-
2268 "ripple::issueIOU : neither account nor issuer is XRP");
-
2269
-
2270 // Consistency check
-
2271 XRPL_ASSERT(issue == amount.issue(), "ripple::issueIOU : matching issue");
-
2272
-
2273 // Can't send to self!
-
2274 XRPL_ASSERT(
-
2275 issue.account != account, "ripple::issueIOU : not issuer account");
-
2276
-
2277 JLOG(j.trace()) << "issueIOU: " << to_string(account) << ": "
-
2278 << amount.getFullText();
-
2279
-
2280 bool bSenderHigh = issue.account > account;
-
2281
-
2282 auto const index = keylet::line(issue.account, account, issue.currency);
-
2283
-
2284 if (auto state = view.peek(index))
-
2285 {
-
2286 STAmount final_balance = state->getFieldAmount(sfBalance);
+
2210
+
2211static bool
+
+ +
2213 ApplyView& view,
+
2214 SLE::pointer state,
+
2215 bool bSenderHigh,
+
2216 AccountID const& sender,
+
2217 STAmount const& before,
+
2218 STAmount const& after,
+ +
2220{
+
2221 if (!state)
+
2222 return false;
+
2223 std::uint32_t const flags(state->getFieldU32(sfFlags));
+
2224
+
2225 auto sle = view.peek(keylet::account(sender));
+
2226 if (!sle)
+
2227 return false;
+
2228
+
2229 // YYY Could skip this if rippling in reverse.
+
2230 if (before > beast::zero
+
2231 // Sender balance was positive.
+
2232 && after <= beast::zero
+
2233 // Sender is zero or negative.
+
2234 && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
+
2235 // Sender reserve is set.
+
2236 && static_cast<bool>(
+
2237 flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
+
2238 static_cast<bool>(sle->getFlags() & lsfDefaultRipple) &&
+
2239 !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
+
2240 !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
+
2241 // Sender trust limit is 0.
+
2242 && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
+
2243 // Sender quality in is 0.
+
2244 &&
+
2245 !state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
+
2246 // Sender quality out is 0.
+
2247 {
+
2248 // VFALCO Where is the line being deleted?
+
2249 // Clear the reserve of the sender, possibly delete the line!
+
2250 adjustOwnerCount(view, sle, -1, j);
+
2251
+
2252 // Clear reserve flag.
+
2253 state->setFieldU32(
+
2254 sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
+
2255
+
2256 // Balance is zero, receiver reserve is clear.
+
2257 if (!after // Balance is zero.
+
2258 && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)))
+
2259 return true;
+
2260 }
+
2261 return false;
+
2262}
+
+
2263
+
2264TER
+
+ +
2266 ApplyView& view,
+
2267 AccountID const& account,
+
2268 STAmount const& amount,
+
2269 Issue const& issue,
+ +
2271{
+
2272 XRPL_ASSERT(
+
2273 !isXRP(account) && !isXRP(issue.account),
+
2274 "ripple::issueIOU : neither account nor issuer is XRP");
+
2275
+
2276 // Consistency check
+
2277 XRPL_ASSERT(issue == amount.issue(), "ripple::issueIOU : matching issue");
+
2278
+
2279 // Can't send to self!
+
2280 XRPL_ASSERT(
+
2281 issue.account != account, "ripple::issueIOU : not issuer account");
+
2282
+
2283 JLOG(j.trace()) << "issueIOU: " << to_string(account) << ": "
+
2284 << amount.getFullText();
+
2285
+
2286 bool bSenderHigh = issue.account > account;
2287
-
2288 if (bSenderHigh)
-
2289 final_balance.negate(); // Put balance in sender terms.
-
2290
-
2291 STAmount const start_balance = final_balance;
-
2292
-
2293 final_balance -= amount;
-
2294
-
2295 auto const must_delete = updateTrustLine(
-
2296 view,
-
2297 state,
-
2298 bSenderHigh,
-
2299 issue.account,
-
2300 start_balance,
-
2301 final_balance,
-
2302 j);
-
2303
-
2304 view.creditHook(issue.account, account, amount, start_balance);
-
2305
-
2306 if (bSenderHigh)
-
2307 final_balance.negate();
-
2308
-
2309 // Adjust the balance on the trust line if necessary. We do this even if
-
2310 // we are going to delete the line to reflect the correct balance at the
-
2311 // time of deletion.
-
2312 state->setFieldAmount(sfBalance, final_balance);
-
2313 if (must_delete)
-
2314 return trustDelete(
-
2315 view,
-
2316 state,
-
2317 bSenderHigh ? account : issue.account,
-
2318 bSenderHigh ? issue.account : account,
-
2319 j);
-
2320
-
2321 view.update(state);
-
2322
-
2323 return tesSUCCESS;
-
2324 }
-
2325
-
2326 // NIKB TODO: The limit uses the receiver's account as the issuer and
-
2327 // this is unnecessarily inefficient as copying which could be avoided
-
2328 // is now required. Consider available options.
-
2329 STAmount const limit(Issue{issue.currency, account});
-
2330 STAmount final_balance = amount;
+
2288 auto const index = keylet::line(issue.account, account, issue.currency);
+
2289
+
2290 if (auto state = view.peek(index))
+
2291 {
+
2292 STAmount final_balance = state->getFieldAmount(sfBalance);
+
2293
+
2294 if (bSenderHigh)
+
2295 final_balance.negate(); // Put balance in sender terms.
+
2296
+
2297 STAmount const start_balance = final_balance;
+
2298
+
2299 final_balance -= amount;
+
2300
+
2301 auto const must_delete = updateTrustLine(
+
2302 view,
+
2303 state,
+
2304 bSenderHigh,
+
2305 issue.account,
+
2306 start_balance,
+
2307 final_balance,
+
2308 j);
+
2309
+
2310 view.creditHook(issue.account, account, amount, start_balance);
+
2311
+
2312 if (bSenderHigh)
+
2313 final_balance.negate();
+
2314
+
2315 // Adjust the balance on the trust line if necessary. We do this even if
+
2316 // we are going to delete the line to reflect the correct balance at the
+
2317 // time of deletion.
+
2318 state->setFieldAmount(sfBalance, final_balance);
+
2319 if (must_delete)
+
2320 return trustDelete(
+
2321 view,
+
2322 state,
+
2323 bSenderHigh ? account : issue.account,
+
2324 bSenderHigh ? issue.account : account,
+
2325 j);
+
2326
+
2327 view.update(state);
+
2328
+
2329 return tesSUCCESS;
+
2330 }
2331
-
2332 final_balance.setIssuer(noAccount());
-
2333
-
2334 auto const receiverAccount = view.peek(keylet::account(account));
-
2335 if (!receiverAccount)
-
2336 return tefINTERNAL; // LCOV_EXCL_LINE
+
2332 // NIKB TODO: The limit uses the receiver's account as the issuer and
+
2333 // this is unnecessarily inefficient as copying which could be avoided
+
2334 // is now required. Consider available options.
+
2335 STAmount const limit(Issue{issue.currency, account});
+
2336 STAmount final_balance = amount;
2337
-
2338 bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0;
+
2338 final_balance.setIssuer(noAccount());
2339
-
2340 return trustCreate(
-
2341 view,
-
2342 bSenderHigh,
-
2343 issue.account,
-
2344 account,
-
2345 index.key,
-
2346 receiverAccount,
-
2347 false,
-
2348 noRipple,
-
2349 false,
-
2350 false,
-
2351 final_balance,
-
2352 limit,
-
2353 0,
-
2354 0,
-
2355 j);
-
2356}
+
2340 auto const receiverAccount = view.peek(keylet::account(account));
+
2341 if (!receiverAccount)
+
2342 return tefINTERNAL; // LCOV_EXCL_LINE
+
2343
+
2344 bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0;
+
2345
+
2346 return trustCreate(
+
2347 view,
+
2348 bSenderHigh,
+
2349 issue.account,
+
2350 account,
+
2351 index.key,
+
2352 receiverAccount,
+
2353 false,
+
2354 noRipple,
+
2355 false,
+
2356 false,
+
2357 final_balance,
+
2358 limit,
+
2359 0,
+
2360 0,
+
2361 j);
+
2362}
-
2357
-
2358TER
-
- -
2360 ApplyView& view,
-
2361 AccountID const& account,
-
2362 STAmount const& amount,
-
2363 Issue const& issue,
- -
2365{
-
2366 XRPL_ASSERT(
-
2367 !isXRP(account) && !isXRP(issue.account),
-
2368 "ripple::redeemIOU : neither account nor issuer is XRP");
-
2369
-
2370 // Consistency check
-
2371 XRPL_ASSERT(issue == amount.issue(), "ripple::redeemIOU : matching issue");
-
2372
-
2373 // Can't send to self!
-
2374 XRPL_ASSERT(
-
2375 issue.account != account, "ripple::redeemIOU : not issuer account");
-
2376
-
2377 JLOG(j.trace()) << "redeemIOU: " << to_string(account) << ": "
-
2378 << amount.getFullText();
-
2379
-
2380 bool bSenderHigh = account > issue.account;
-
2381
-
2382 if (auto state =
-
2383 view.peek(keylet::line(account, issue.account, issue.currency)))
-
2384 {
-
2385 STAmount final_balance = state->getFieldAmount(sfBalance);
-
2386
-
2387 if (bSenderHigh)
-
2388 final_balance.negate(); // Put balance in sender terms.
-
2389
-
2390 STAmount const start_balance = final_balance;
-
2391
-
2392 final_balance -= amount;
-
2393
-
2394 auto const must_delete = updateTrustLine(
-
2395 view, state, bSenderHigh, account, start_balance, final_balance, j);
-
2396
-
2397 view.creditHook(account, issue.account, amount, start_balance);
-
2398
-
2399 if (bSenderHigh)
-
2400 final_balance.negate();
-
2401
-
2402 // Adjust the balance on the trust line if necessary. We do this even if
-
2403 // we are going to delete the line to reflect the correct balance at the
-
2404 // time of deletion.
-
2405 state->setFieldAmount(sfBalance, final_balance);
-
2406
-
2407 if (must_delete)
-
2408 {
-
2409 return trustDelete(
-
2410 view,
-
2411 state,
-
2412 bSenderHigh ? issue.account : account,
-
2413 bSenderHigh ? account : issue.account,
-
2414 j);
-
2415 }
-
2416
-
2417 view.update(state);
-
2418 return tesSUCCESS;
-
2419 }
-
2420
-
2421 // In order to hold an IOU, a trust line *MUST* exist to track the
-
2422 // balance. If it doesn't, then something is very wrong. Don't try
-
2423 // to continue.
-
2424 // LCOV_EXCL_START
-
2425 JLOG(j.fatal()) << "redeemIOU: " << to_string(account)
-
2426 << " attempts to redeem " << amount.getFullText()
-
2427 << " but no trust line exists!";
-
2428
-
2429 return tefINTERNAL;
-
2430 // LCOV_EXCL_STOP
-
2431}
+
2363
+
2364TER
+
+ +
2366 ApplyView& view,
+
2367 AccountID const& account,
+
2368 STAmount const& amount,
+
2369 Issue const& issue,
+ +
2371{
+
2372 XRPL_ASSERT(
+
2373 !isXRP(account) && !isXRP(issue.account),
+
2374 "ripple::redeemIOU : neither account nor issuer is XRP");
+
2375
+
2376 // Consistency check
+
2377 XRPL_ASSERT(issue == amount.issue(), "ripple::redeemIOU : matching issue");
+
2378
+
2379 // Can't send to self!
+
2380 XRPL_ASSERT(
+
2381 issue.account != account, "ripple::redeemIOU : not issuer account");
+
2382
+
2383 JLOG(j.trace()) << "redeemIOU: " << to_string(account) << ": "
+
2384 << amount.getFullText();
+
2385
+
2386 bool bSenderHigh = account > issue.account;
+
2387
+
2388 if (auto state =
+
2389 view.peek(keylet::line(account, issue.account, issue.currency)))
+
2390 {
+
2391 STAmount final_balance = state->getFieldAmount(sfBalance);
+
2392
+
2393 if (bSenderHigh)
+
2394 final_balance.negate(); // Put balance in sender terms.
+
2395
+
2396 STAmount const start_balance = final_balance;
+
2397
+
2398 final_balance -= amount;
+
2399
+
2400 auto const must_delete = updateTrustLine(
+
2401 view, state, bSenderHigh, account, start_balance, final_balance, j);
+
2402
+
2403 view.creditHook(account, issue.account, amount, start_balance);
+
2404
+
2405 if (bSenderHigh)
+
2406 final_balance.negate();
+
2407
+
2408 // Adjust the balance on the trust line if necessary. We do this even if
+
2409 // we are going to delete the line to reflect the correct balance at the
+
2410 // time of deletion.
+
2411 state->setFieldAmount(sfBalance, final_balance);
+
2412
+
2413 if (must_delete)
+
2414 {
+
2415 return trustDelete(
+
2416 view,
+
2417 state,
+
2418 bSenderHigh ? issue.account : account,
+
2419 bSenderHigh ? account : issue.account,
+
2420 j);
+
2421 }
+
2422
+
2423 view.update(state);
+
2424 return tesSUCCESS;
+
2425 }
+
2426
+
2427 // In order to hold an IOU, a trust line *MUST* exist to track the
+
2428 // balance. If it doesn't, then something is very wrong. Don't try
+
2429 // to continue.
+
2430 // LCOV_EXCL_START
+
2431 JLOG(j.fatal()) << "redeemIOU: " << to_string(account)
+
2432 << " attempts to redeem " << amount.getFullText()
+
2433 << " but no trust line exists!";
+
2434
+
2435 return tefINTERNAL;
+
2436 // LCOV_EXCL_STOP
+
2437}
-
2432
-
2433TER
-
- -
2435 ApplyView& view,
-
2436 AccountID const& from,
-
2437 AccountID const& to,
-
2438 STAmount const& amount,
- -
2440{
-
2441 XRPL_ASSERT(
-
2442 from != beast::zero, "ripple::transferXRP : nonzero from account");
-
2443 XRPL_ASSERT(to != beast::zero, "ripple::transferXRP : nonzero to account");
-
2444 XRPL_ASSERT(from != to, "ripple::transferXRP : sender is not receiver");
-
2445 XRPL_ASSERT(amount.native(), "ripple::transferXRP : amount is XRP");
-
2446
-
2447 SLE::pointer const sender = view.peek(keylet::account(from));
-
2448 SLE::pointer const receiver = view.peek(keylet::account(to));
-
2449 if (!sender || !receiver)
-
2450 return tefINTERNAL; // LCOV_EXCL_LINE
-
2451
-
2452 JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> "
-
2453 << to_string(to) << ") : " << amount.getFullText();
-
2454
-
2455 if (sender->getFieldAmount(sfBalance) < amount)
-
2456 {
-
2457 // VFALCO Its unfortunate we have to keep
-
2458 // mutating these TER everywhere
-
2459 // FIXME: this logic should be moved to callers maybe?
-
2460 // LCOV_EXCL_START
-
2461 return view.open() ? TER{telFAILED_PROCESSING}
- -
2463 // LCOV_EXCL_STOP
-
2464 }
-
2465
-
2466 // Decrement XRP balance.
-
2467 sender->setFieldAmount(
-
2468 sfBalance, sender->getFieldAmount(sfBalance) - amount);
-
2469 view.update(sender);
-
2470
-
2471 receiver->setFieldAmount(
-
2472 sfBalance, receiver->getFieldAmount(sfBalance) + amount);
-
2473 view.update(receiver);
-
2474
-
2475 return tesSUCCESS;
-
2476}
+
2438
+
2439TER
+
+ +
2441 ApplyView& view,
+
2442 AccountID const& from,
+
2443 AccountID const& to,
+
2444 STAmount const& amount,
+ +
2446{
+
2447 XRPL_ASSERT(
+
2448 from != beast::zero, "ripple::transferXRP : nonzero from account");
+
2449 XRPL_ASSERT(to != beast::zero, "ripple::transferXRP : nonzero to account");
+
2450 XRPL_ASSERT(from != to, "ripple::transferXRP : sender is not receiver");
+
2451 XRPL_ASSERT(amount.native(), "ripple::transferXRP : amount is XRP");
+
2452
+
2453 SLE::pointer const sender = view.peek(keylet::account(from));
+
2454 SLE::pointer const receiver = view.peek(keylet::account(to));
+
2455 if (!sender || !receiver)
+
2456 return tefINTERNAL; // LCOV_EXCL_LINE
+
2457
+
2458 JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> "
+
2459 << to_string(to) << ") : " << amount.getFullText();
+
2460
+
2461 if (sender->getFieldAmount(sfBalance) < amount)
+
2462 {
+
2463 // VFALCO Its unfortunate we have to keep
+
2464 // mutating these TER everywhere
+
2465 // FIXME: this logic should be moved to callers maybe?
+
2466 // LCOV_EXCL_START
+
2467 return view.open() ? TER{telFAILED_PROCESSING}
+ +
2469 // LCOV_EXCL_STOP
+
2470 }
+
2471
+
2472 // Decrement XRP balance.
+
2473 sender->setFieldAmount(
+
2474 sfBalance, sender->getFieldAmount(sfBalance) - amount);
+
2475 view.update(sender);
+
2476
+
2477 receiver->setFieldAmount(
+
2478 sfBalance, receiver->getFieldAmount(sfBalance) + amount);
+
2479 view.update(receiver);
+
2480
+
2481 return tesSUCCESS;
+
2482}
-
2477
-
2478TER
-
- -
2480 ReadView const& view,
-
2481 Issue const& issue,
-
2482 AccountID const& account,
-
2483 AuthType authType)
-
2484{
-
2485 if (isXRP(issue) || issue.account == account)
-
2486 return tesSUCCESS;
-
2487
-
2488 auto const trustLine =
-
2489 view.read(keylet::line(account, issue.account, issue.currency));
-
2490 // If account has no line, and this is a strong check, fail
-
2491 if (!trustLine && authType == AuthType::StrongAuth)
-
2492 return tecNO_LINE;
+
2483
+
2484TER
+
+ +
2486 ReadView const& view,
+
2487 Issue const& issue,
+
2488 AccountID const& account,
+
2489 AuthType authType)
+
2490{
+
2491 if (isXRP(issue) || issue.account == account)
+
2492 return tesSUCCESS;
2493
-
2494 // If this is a weak or legacy check, or if the account has a line, fail if
-
2495 // auth is required and not set on the line
-
2496 if (auto const issuerAccount = view.read(keylet::account(issue.account));
-
2497 issuerAccount && (*issuerAccount)[sfFlags] & lsfRequireAuth)
-
2498 {
-
2499 if (trustLine)
-
2500 return ((*trustLine)[sfFlags] &
-
2501 ((account > issue.account) ? lsfLowAuth : lsfHighAuth))
-
2502 ? tesSUCCESS
-
2503 : TER{tecNO_AUTH};
-
2504 return TER{tecNO_LINE};
-
2505 }
-
2506
-
2507 return tesSUCCESS;
-
2508}
+
2494 auto const trustLine =
+
2495 view.read(keylet::line(account, issue.account, issue.currency));
+
2496 // If account has no line, and this is a strong check, fail
+
2497 if (!trustLine && authType == AuthType::StrongAuth)
+
2498 return tecNO_LINE;
+
2499
+
2500 // If this is a weak or legacy check, or if the account has a line, fail if
+
2501 // auth is required and not set on the line
+
2502 if (auto const issuerAccount = view.read(keylet::account(issue.account));
+
2503 issuerAccount && (*issuerAccount)[sfFlags] & lsfRequireAuth)
+
2504 {
+
2505 if (trustLine)
+
2506 return ((*trustLine)[sfFlags] &
+
2507 ((account > issue.account) ? lsfLowAuth : lsfHighAuth))
+
2508 ? tesSUCCESS
+
2509 : TER{tecNO_AUTH};
+
2510 return TER{tecNO_LINE};
+
2511 }
+
2512
+
2513 return tesSUCCESS;
+
2514}
-
2509
-
2510TER
-
- -
2512 ReadView const& view,
-
2513 MPTIssue const& mptIssue,
-
2514 AccountID const& account,
-
2515 AuthType authType,
-
2516 int depth)
-
2517{
-
2518 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
-
2519 auto const sleIssuance = view.read(mptID);
-
2520 if (!sleIssuance)
-
2521 return tecOBJECT_NOT_FOUND;
-
2522
-
2523 auto const mptIssuer = sleIssuance->getAccountID(sfIssuer);
-
2524
-
2525 // issuer is always "authorized"
-
2526 if (mptIssuer == account) // Issuer won't have MPToken
-
2527 return tesSUCCESS;
+
2515
+
2516TER
+
+ +
2518 ReadView const& view,
+
2519 MPTIssue const& mptIssue,
+
2520 AccountID const& account,
+
2521 AuthType authType,
+
2522 int depth)
+
2523{
+
2524 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
+
2525 auto const sleIssuance = view.read(mptID);
+
2526 if (!sleIssuance)
+
2527 return tecOBJECT_NOT_FOUND;
2528
-
2529 if (view.rules().enabled(featureSingleAssetVault))
-
2530 {
-
2531 if (depth >= maxAssetCheckDepth)
-
2532 return tecINTERNAL; // LCOV_EXCL_LINE
-
2533
-
2534 // requireAuth is recursive if the issuer is a vault pseudo-account
-
2535 auto const sleIssuer = view.read(keylet::account(mptIssuer));
-
2536 if (!sleIssuer)
-
2537 return tefINTERNAL; // LCOV_EXCL_LINE
-
2538
-
2539 if (sleIssuer->isFieldPresent(sfVaultID))
-
2540 {
-
2541 auto const sleVault =
-
2542 view.read(keylet::vault(sleIssuer->getFieldH256(sfVaultID)));
-
2543 if (!sleVault)
-
2544 return tefINTERNAL; // LCOV_EXCL_LINE
-
2545
-
2546 auto const asset = sleVault->at(sfAsset);
-
2547 if (auto const err = std::visit(
-
2548 [&]<ValidIssueType TIss>(TIss const& issue) {
-
2549 if constexpr (std::is_same_v<TIss, Issue>)
-
2550 return requireAuth(view, issue, account, authType);
-
2551 else
-
2552 return requireAuth(
-
2553 view, issue, account, authType, depth + 1);
-
2554 },
-
2555 asset.value());
-
2556 !isTesSuccess(err))
-
2557 return err;
-
2558 }
-
2559 }
-
2560
-
2561 auto const mptokenID = keylet::mptoken(mptID.key, account);
-
2562 auto const sleToken = view.read(mptokenID);
-
2563
-
2564 // if account has no MPToken, fail
-
2565 if (!sleToken &&
-
2566 (authType == AuthType::StrongAuth || authType == AuthType::Legacy))
-
2567 return tecNO_AUTH;
-
2568
-
2569 // Note, this check is not amendment-gated because DomainID will be always
-
2570 // empty **unless** writing to it has been enabled by an amendment
-
2571 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
-
2572 if (maybeDomainID)
-
2573 {
-
2574 XRPL_ASSERT(
-
2575 sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth,
-
2576 "ripple::requireAuth : issuance requires authorization");
-
2577 // ter = tefINTERNAL | tecOBJECT_NOT_FOUND | tecNO_AUTH | tecEXPIRED
-
2578 if (auto const ter =
-
2579 credentials::validDomain(view, *maybeDomainID, account);
-
2580 isTesSuccess(ter))
-
2581 return ter; // Note: sleToken might be null
-
2582 else if (!sleToken)
-
2583 return ter;
-
2584 // We ignore error from validDomain if we found sleToken, as it could
-
2585 // belong to someone who is explicitly authorized e.g. a vault owner.
-
2586 }
-
2587
-
2588 // mptoken must be authorized if issuance enabled requireAuth
-
2589 if (sleIssuance->isFlag(lsfMPTRequireAuth) &&
-
2590 (!sleToken || !sleToken->isFlag(lsfMPTAuthorized)))
-
2591 return tecNO_AUTH;
-
2592
-
2593 return tesSUCCESS; // Note: sleToken might be null
-
2594}
+
2529 auto const mptIssuer = sleIssuance->getAccountID(sfIssuer);
+
2530
+
2531 // issuer is always "authorized"
+
2532 if (mptIssuer == account) // Issuer won't have MPToken
+
2533 return tesSUCCESS;
+
2534
+
2535 if (view.rules().enabled(featureSingleAssetVault))
+
2536 {
+
2537 if (depth >= maxAssetCheckDepth)
+
2538 return tecINTERNAL; // LCOV_EXCL_LINE
+
2539
+
2540 // requireAuth is recursive if the issuer is a vault pseudo-account
+
2541 auto const sleIssuer = view.read(keylet::account(mptIssuer));
+
2542 if (!sleIssuer)
+
2543 return tefINTERNAL; // LCOV_EXCL_LINE
+
2544
+
2545 if (sleIssuer->isFieldPresent(sfVaultID))
+
2546 {
+
2547 auto const sleVault =
+
2548 view.read(keylet::vault(sleIssuer->getFieldH256(sfVaultID)));
+
2549 if (!sleVault)
+
2550 return tefINTERNAL; // LCOV_EXCL_LINE
+
2551
+
2552 auto const asset = sleVault->at(sfAsset);
+
2553 if (auto const err = std::visit(
+
2554 [&]<ValidIssueType TIss>(TIss const& issue) {
+
2555 if constexpr (std::is_same_v<TIss, Issue>)
+
2556 return requireAuth(view, issue, account, authType);
+
2557 else
+
2558 return requireAuth(
+
2559 view, issue, account, authType, depth + 1);
+
2560 },
+
2561 asset.value());
+
2562 !isTesSuccess(err))
+
2563 return err;
+
2564 }
+
2565 }
+
2566
+
2567 auto const mptokenID = keylet::mptoken(mptID.key, account);
+
2568 auto const sleToken = view.read(mptokenID);
+
2569
+
2570 // if account has no MPToken, fail
+
2571 if (!sleToken &&
+
2572 (authType == AuthType::StrongAuth || authType == AuthType::Legacy))
+
2573 return tecNO_AUTH;
+
2574
+
2575 // Note, this check is not amendment-gated because DomainID will be always
+
2576 // empty **unless** writing to it has been enabled by an amendment
+
2577 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
+
2578 if (maybeDomainID)
+
2579 {
+
2580 XRPL_ASSERT(
+
2581 sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth,
+
2582 "ripple::requireAuth : issuance requires authorization");
+
2583 // ter = tefINTERNAL | tecOBJECT_NOT_FOUND | tecNO_AUTH | tecEXPIRED
+
2584 if (auto const ter =
+
2585 credentials::validDomain(view, *maybeDomainID, account);
+
2586 isTesSuccess(ter))
+
2587 return ter; // Note: sleToken might be null
+
2588 else if (!sleToken)
+
2589 return ter;
+
2590 // We ignore error from validDomain if we found sleToken, as it could
+
2591 // belong to someone who is explicitly authorized e.g. a vault owner.
+
2592 }
+
2593
+
2594 // mptoken must be authorized if issuance enabled requireAuth
+
2595 if (sleIssuance->isFlag(lsfMPTRequireAuth) &&
+
2596 (!sleToken || !sleToken->isFlag(lsfMPTAuthorized)))
+
2597 return tecNO_AUTH;
+
2598
+
2599 return tesSUCCESS; // Note: sleToken might be null
+
2600}
-
2595
-
2596[[nodiscard]] TER
-
- -
2598 ApplyView& view,
-
2599 MPTID const& mptIssuanceID,
-
2600 AccountID const& account,
-
2601 XRPAmount const& priorBalance, // for MPToken authorization
- -
2603{
-
2604 auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
-
2605 if (!sleIssuance)
-
2606 return tefINTERNAL; // LCOV_EXCL_LINE
-
2607
-
2608 XRPL_ASSERT(
-
2609 sleIssuance->isFlag(lsfMPTRequireAuth),
-
2610 "ripple::enforceMPTokenAuthorization : authorization required");
-
2611
-
2612 if (account == sleIssuance->at(sfIssuer))
-
2613 return tefINTERNAL; // LCOV_EXCL_LINE
-
2614
-
2615 auto const keylet = keylet::mptoken(mptIssuanceID, account);
-
2616 auto const sleToken = view.read(keylet); // NOTE: might be null
-
2617 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
-
2618 bool expired = false;
-
2619 bool const authorizedByDomain = [&]() -> bool {
-
2620 // NOTE: defensive here, shuld be checked in preclaim
-
2621 if (!maybeDomainID.has_value())
-
2622 return false; // LCOV_EXCL_LINE
-
2623
-
2624 auto const ter = verifyValidDomain(view, account, *maybeDomainID, j);
-
2625 if (isTesSuccess(ter))
-
2626 return true;
-
2627 if (ter == tecEXPIRED)
-
2628 expired = true;
-
2629 return false;
-
2630 }();
-
2631
-
2632 if (!authorizedByDomain && sleToken == nullptr)
-
2633 {
-
2634 // Could not find MPToken and won't create one, could be either of:
-
2635 //
-
2636 // 1. Field sfDomainID not set in MPTokenIssuance or
-
2637 // 2. Account has no matching and accepted credentials or
-
2638 // 3. Account has all expired credentials (deleted in verifyValidDomain)
-
2639 //
-
2640 // Either way, return tecNO_AUTH and there is nothing else to do
-
2641 return expired ? tecEXPIRED : tecNO_AUTH;
-
2642 }
-
2643 else if (!authorizedByDomain && maybeDomainID.has_value())
-
2644 {
-
2645 // Found an MPToken but the account is not authorized and we expect
-
2646 // it to have been authorized by the domain. This could be because the
-
2647 // credentials used to create the MPToken have expired or been deleted.
-
2648 return expired ? tecEXPIRED : tecNO_AUTH;
-
2649 }
-
2650 else if (!authorizedByDomain)
-
2651 {
-
2652 // We found an MPToken, but sfDomainID is not set, so this is a classic
-
2653 // MPToken which requires authorization by the token issuer.
-
2654 XRPL_ASSERT(
-
2655 sleToken != nullptr && !maybeDomainID.has_value(),
-
2656 "ripple::enforceMPTokenAuthorization : found MPToken");
-
2657 if (sleToken->isFlag(lsfMPTAuthorized))
-
2658 return tesSUCCESS;
-
2659
-
2660 return tecNO_AUTH;
-
2661 }
-
2662 else if (authorizedByDomain && sleToken != nullptr)
-
2663 {
-
2664 // Found an MPToken, authorized by the domain. Ignore authorization flag
-
2665 // lsfMPTAuthorized because it is meaningless. Return tesSUCCESS
-
2666 XRPL_ASSERT(
-
2667 maybeDomainID.has_value(),
-
2668 "ripple::enforceMPTokenAuthorization : found MPToken for domain");
-
2669 return tesSUCCESS;
-
2670 }
-
2671 else if (authorizedByDomain)
-
2672 {
-
2673 // Could not find MPToken but there should be one because we are
-
2674 // authorized by domain. Proceed to create it, then return tesSUCCESS
-
2675 XRPL_ASSERT(
-
2676 maybeDomainID.has_value() && sleToken == nullptr,
-
2677 "ripple::enforceMPTokenAuthorization : new MPToken for domain");
-
2678 if (auto const err = authorizeMPToken(
-
2679 view,
-
2680 priorBalance, // priorBalance
-
2681 mptIssuanceID, // mptIssuanceID
-
2682 account, // account
-
2683 j);
-
2684 !isTesSuccess(err))
-
2685 return err;
-
2686
-
2687 return tesSUCCESS;
-
2688 }
-
2689
-
2690 // LCOV_EXCL_START
-
2691 UNREACHABLE(
-
2692 "ripple::enforceMPTokenAuthorization : condition list is incomplete");
-
2693 return tefINTERNAL;
-
2694 // LCOV_EXCL_STOP
-
2695}
+
2601
+
2602[[nodiscard]] TER
+
+ +
2604 ApplyView& view,
+
2605 MPTID const& mptIssuanceID,
+
2606 AccountID const& account,
+
2607 XRPAmount const& priorBalance, // for MPToken authorization
+ +
2609{
+
2610 auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
+
2611 if (!sleIssuance)
+
2612 return tefINTERNAL; // LCOV_EXCL_LINE
+
2613
+
2614 XRPL_ASSERT(
+
2615 sleIssuance->isFlag(lsfMPTRequireAuth),
+
2616 "ripple::enforceMPTokenAuthorization : authorization required");
+
2617
+
2618 if (account == sleIssuance->at(sfIssuer))
+
2619 return tefINTERNAL; // LCOV_EXCL_LINE
+
2620
+
2621 auto const keylet = keylet::mptoken(mptIssuanceID, account);
+
2622 auto const sleToken = view.read(keylet); // NOTE: might be null
+
2623 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
+
2624 bool expired = false;
+
2625 bool const authorizedByDomain = [&]() -> bool {
+
2626 // NOTE: defensive here, shuld be checked in preclaim
+
2627 if (!maybeDomainID.has_value())
+
2628 return false; // LCOV_EXCL_LINE
+
2629
+
2630 auto const ter = verifyValidDomain(view, account, *maybeDomainID, j);
+
2631 if (isTesSuccess(ter))
+
2632 return true;
+
2633 if (ter == tecEXPIRED)
+
2634 expired = true;
+
2635 return false;
+
2636 }();
+
2637
+
2638 if (!authorizedByDomain && sleToken == nullptr)
+
2639 {
+
2640 // Could not find MPToken and won't create one, could be either of:
+
2641 //
+
2642 // 1. Field sfDomainID not set in MPTokenIssuance or
+
2643 // 2. Account has no matching and accepted credentials or
+
2644 // 3. Account has all expired credentials (deleted in verifyValidDomain)
+
2645 //
+
2646 // Either way, return tecNO_AUTH and there is nothing else to do
+
2647 return expired ? tecEXPIRED : tecNO_AUTH;
+
2648 }
+
2649 else if (!authorizedByDomain && maybeDomainID.has_value())
+
2650 {
+
2651 // Found an MPToken but the account is not authorized and we expect
+
2652 // it to have been authorized by the domain. This could be because the
+
2653 // credentials used to create the MPToken have expired or been deleted.
+
2654 return expired ? tecEXPIRED : tecNO_AUTH;
+
2655 }
+
2656 else if (!authorizedByDomain)
+
2657 {
+
2658 // We found an MPToken, but sfDomainID is not set, so this is a classic
+
2659 // MPToken which requires authorization by the token issuer.
+
2660 XRPL_ASSERT(
+
2661 sleToken != nullptr && !maybeDomainID.has_value(),
+
2662 "ripple::enforceMPTokenAuthorization : found MPToken");
+
2663 if (sleToken->isFlag(lsfMPTAuthorized))
+
2664 return tesSUCCESS;
+
2665
+
2666 return tecNO_AUTH;
+
2667 }
+
2668 else if (authorizedByDomain && sleToken != nullptr)
+
2669 {
+
2670 // Found an MPToken, authorized by the domain. Ignore authorization flag
+
2671 // lsfMPTAuthorized because it is meaningless. Return tesSUCCESS
+
2672 XRPL_ASSERT(
+
2673 maybeDomainID.has_value(),
+
2674 "ripple::enforceMPTokenAuthorization : found MPToken for domain");
+
2675 return tesSUCCESS;
+
2676 }
+
2677 else if (authorizedByDomain)
+
2678 {
+
2679 // Could not find MPToken but there should be one because we are
+
2680 // authorized by domain. Proceed to create it, then return tesSUCCESS
+
2681 XRPL_ASSERT(
+
2682 maybeDomainID.has_value() && sleToken == nullptr,
+
2683 "ripple::enforceMPTokenAuthorization : new MPToken for domain");
+
2684 if (auto const err = authorizeMPToken(
+
2685 view,
+
2686 priorBalance, // priorBalance
+
2687 mptIssuanceID, // mptIssuanceID
+
2688 account, // account
+
2689 j);
+
2690 !isTesSuccess(err))
+
2691 return err;
+
2692
+
2693 return tesSUCCESS;
+
2694 }
+
2695
+
2696 // LCOV_EXCL_START
+
2697 UNREACHABLE(
+
2698 "ripple::enforceMPTokenAuthorization : condition list is incomplete");
+
2699 return tefINTERNAL;
+
2700 // LCOV_EXCL_STOP
+
2701}
-
2696
-
2697TER
-
- -
2699 ReadView const& view,
-
2700 MPTIssue const& mptIssue,
-
2701 AccountID const& from,
-
2702 AccountID const& to)
-
2703{
-
2704 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
-
2705 auto const sleIssuance = view.read(mptID);
-
2706 if (!sleIssuance)
-
2707 return tecOBJECT_NOT_FOUND;
-
2708
-
2709 if (!(sleIssuance->getFieldU32(sfFlags) & lsfMPTCanTransfer))
-
2710 {
-
2711 if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer])
-
2712 return TER{tecNO_AUTH};
-
2713 }
-
2714 return tesSUCCESS;
-
2715}
+
2702
+
2703TER
+
+ +
2705 ReadView const& view,
+
2706 MPTIssue const& mptIssue,
+
2707 AccountID const& from,
+
2708 AccountID const& to)
+
2709{
+
2710 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
+
2711 auto const sleIssuance = view.read(mptID);
+
2712 if (!sleIssuance)
+
2713 return tecOBJECT_NOT_FOUND;
+
2714
+
2715 if (!(sleIssuance->getFieldU32(sfFlags) & lsfMPTCanTransfer))
+
2716 {
+
2717 if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer])
+
2718 return TER{tecNO_AUTH};
+
2719 }
+
2720 return tesSUCCESS;
+
2721}
-
2716
-
2717TER
-
- -
2719 ApplyView& view,
-
2720 Keylet const& ownerDirKeylet,
-
2721 EntryDeleter const& deleter,
- -
2723 std::optional<uint16_t> maxNodesToDelete)
-
2724{
-
2725 // Delete all the entries in the account directory.
-
2726 std::shared_ptr<SLE> sleDirNode{};
-
2727 unsigned int uDirEntry{0};
-
2728 uint256 dirEntry{beast::zero};
-
2729 std::uint32_t deleted = 0;
-
2730
-
2731 if (view.exists(ownerDirKeylet) &&
-
2732 dirFirst(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
-
2733 {
-
2734 do
-
2735 {
-
2736 if (maxNodesToDelete && ++deleted > *maxNodesToDelete)
-
2737 return tecINCOMPLETE;
-
2738
-
2739 // Choose the right way to delete each directory node.
-
2740 auto sleItem = view.peek(keylet::child(dirEntry));
-
2741 if (!sleItem)
-
2742 {
-
2743 // Directory node has an invalid index. Bail out.
-
2744 // LCOV_EXCL_START
-
2745 JLOG(j.fatal())
-
2746 << "DeleteAccount: Directory node in ledger " << view.seq()
-
2747 << " has index to object that is missing: "
-
2748 << to_string(dirEntry);
-
2749 return tefBAD_LEDGER;
-
2750 // LCOV_EXCL_STOP
-
2751 }
-
2752
-
2753 LedgerEntryType const nodeType{safe_cast<LedgerEntryType>(
-
2754 sleItem->getFieldU16(sfLedgerEntryType))};
-
2755
-
2756 // Deleter handles the details of specific account-owned object
-
2757 // deletion
-
2758 auto const [ter, skipEntry] = deleter(nodeType, dirEntry, sleItem);
-
2759 if (ter != tesSUCCESS)
-
2760 return ter;
+
2722
+
2723TER
+
+ +
2725 ApplyView& view,
+
2726 Keylet const& ownerDirKeylet,
+
2727 EntryDeleter const& deleter,
+ +
2729 std::optional<uint16_t> maxNodesToDelete)
+
2730{
+
2731 // Delete all the entries in the account directory.
+
2732 std::shared_ptr<SLE> sleDirNode{};
+
2733 unsigned int uDirEntry{0};
+
2734 uint256 dirEntry{beast::zero};
+
2735 std::uint32_t deleted = 0;
+
2736
+
2737 if (view.exists(ownerDirKeylet) &&
+
2738 dirFirst(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
+
2739 {
+
2740 do
+
2741 {
+
2742 if (maxNodesToDelete && ++deleted > *maxNodesToDelete)
+
2743 return tecINCOMPLETE;
+
2744
+
2745 // Choose the right way to delete each directory node.
+
2746 auto sleItem = view.peek(keylet::child(dirEntry));
+
2747 if (!sleItem)
+
2748 {
+
2749 // Directory node has an invalid index. Bail out.
+
2750 // LCOV_EXCL_START
+
2751 JLOG(j.fatal())
+
2752 << "DeleteAccount: Directory node in ledger " << view.seq()
+
2753 << " has index to object that is missing: "
+
2754 << to_string(dirEntry);
+
2755 return tefBAD_LEDGER;
+
2756 // LCOV_EXCL_STOP
+
2757 }
+
2758
+
2759 LedgerEntryType const nodeType{safe_cast<LedgerEntryType>(
+
2760 sleItem->getFieldU16(sfLedgerEntryType))};
2761
-
2762 // dirFirst() and dirNext() are like iterators with exposed
-
2763 // internal state. We'll take advantage of that exposed state
-
2764 // to solve a common C++ problem: iterator invalidation while
-
2765 // deleting elements from a container.
-
2766 //
-
2767 // We have just deleted one directory entry, which means our
-
2768 // "iterator state" is invalid.
-
2769 //
-
2770 // 1. During the process of getting an entry from the
-
2771 // directory uDirEntry was incremented from 'it' to 'it'+1.
+
2762 // Deleter handles the details of specific account-owned object
+
2763 // deletion
+
2764 auto const [ter, skipEntry] = deleter(nodeType, dirEntry, sleItem);
+
2765 if (ter != tesSUCCESS)
+
2766 return ter;
+
2767
+
2768 // dirFirst() and dirNext() are like iterators with exposed
+
2769 // internal state. We'll take advantage of that exposed state
+
2770 // to solve a common C++ problem: iterator invalidation while
+
2771 // deleting elements from a container.
2772 //
-
2773 // 2. We then deleted the entry at index 'it', which means the
-
2774 // entry that was at 'it'+1 has now moved to 'it'.
+
2773 // We have just deleted one directory entry, which means our
+
2774 // "iterator state" is invalid.
2775 //
-
2776 // 3. So we verify that uDirEntry is indeed 'it'+1. Then we jam it
-
2777 // back to 'it' to "un-invalidate" the iterator.
-
2778 XRPL_ASSERT(
-
2779 uDirEntry >= 1,
-
2780 "ripple::cleanupOnAccountDelete : minimum dir entries");
-
2781 if (uDirEntry == 0)
-
2782 {
-
2783 // LCOV_EXCL_START
-
2784 JLOG(j.error())
-
2785 << "DeleteAccount iterator re-validation failed.";
-
2786 return tefBAD_LEDGER;
-
2787 // LCOV_EXCL_STOP
-
2788 }
-
2789 if (skipEntry == SkipEntry::No)
-
2790 uDirEntry--;
-
2791
-
2792 } while (
-
2793 dirNext(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
-
2794 }
-
2795
-
2796 return tesSUCCESS;
-
2797}
+
2776 // 1. During the process of getting an entry from the
+
2777 // directory uDirEntry was incremented from 'it' to 'it'+1.
+
2778 //
+
2779 // 2. We then deleted the entry at index 'it', which means the
+
2780 // entry that was at 'it'+1 has now moved to 'it'.
+
2781 //
+
2782 // 3. So we verify that uDirEntry is indeed 'it'+1. Then we jam it
+
2783 // back to 'it' to "un-invalidate" the iterator.
+
2784 XRPL_ASSERT(
+
2785 uDirEntry >= 1,
+
2786 "ripple::cleanupOnAccountDelete : minimum dir entries");
+
2787 if (uDirEntry == 0)
+
2788 {
+
2789 // LCOV_EXCL_START
+
2790 JLOG(j.error())
+
2791 << "DeleteAccount iterator re-validation failed.";
+
2792 return tefBAD_LEDGER;
+
2793 // LCOV_EXCL_STOP
+
2794 }
+
2795 if (skipEntry == SkipEntry::No)
+
2796 uDirEntry--;
+
2797
+
2798 } while (
+
2799 dirNext(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
+
2800 }
+
2801
+
2802 return tesSUCCESS;
+
2803}
-
2798
-
2799TER
-
- -
2801 ApplyView& view,
-
2802 std::shared_ptr<SLE> sleState,
-
2803 std::optional<AccountID> const& ammAccountID,
- -
2805{
-
2806 if (!sleState || sleState->getType() != ltRIPPLE_STATE)
-
2807 return tecINTERNAL; // LCOV_EXCL_LINE
-
2808
-
2809 auto const& [low, high] = std::minmax(
-
2810 sleState->getFieldAmount(sfLowLimit).getIssuer(),
-
2811 sleState->getFieldAmount(sfHighLimit).getIssuer());
-
2812 auto sleLow = view.peek(keylet::account(low));
-
2813 auto sleHigh = view.peek(keylet::account(high));
-
2814 if (!sleLow || !sleHigh)
-
2815 return tecINTERNAL; // LCOV_EXCL_LINE
-
2816
-
2817 bool const ammLow = sleLow->isFieldPresent(sfAMMID);
-
2818 bool const ammHigh = sleHigh->isFieldPresent(sfAMMID);
-
2819
-
2820 // can't both be AMM
-
2821 if (ammLow && ammHigh)
-
2822 return tecINTERNAL; // LCOV_EXCL_LINE
-
2823
-
2824 // at least one must be
-
2825 if (!ammLow && !ammHigh)
-
2826 return terNO_AMM;
-
2827
-
2828 // one must be the target amm
-
2829 if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
-
2830 return terNO_AMM;
-
2831
-
2832 if (auto const ter = trustDelete(view, sleState, low, high, j);
-
2833 ter != tesSUCCESS)
-
2834 {
-
2835 JLOG(j.error())
-
2836 << "deleteAMMTrustLine: failed to delete the trustline.";
-
2837 return ter;
-
2838 }
-
2839
-
2840 auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve;
-
2841 if (!(sleState->getFlags() & uFlags))
-
2842 return tecINTERNAL; // LCOV_EXCL_LINE
-
2843
-
2844 adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j);
+
2804
+
2805TER
+
+ +
2807 ApplyView& view,
+
2808 std::shared_ptr<SLE> sleState,
+
2809 std::optional<AccountID> const& ammAccountID,
+ +
2811{
+
2812 if (!sleState || sleState->getType() != ltRIPPLE_STATE)
+
2813 return tecINTERNAL; // LCOV_EXCL_LINE
+
2814
+
2815 auto const& [low, high] = std::minmax(
+
2816 sleState->getFieldAmount(sfLowLimit).getIssuer(),
+
2817 sleState->getFieldAmount(sfHighLimit).getIssuer());
+
2818 auto sleLow = view.peek(keylet::account(low));
+
2819 auto sleHigh = view.peek(keylet::account(high));
+
2820 if (!sleLow || !sleHigh)
+
2821 return tecINTERNAL; // LCOV_EXCL_LINE
+
2822
+
2823 bool const ammLow = sleLow->isFieldPresent(sfAMMID);
+
2824 bool const ammHigh = sleHigh->isFieldPresent(sfAMMID);
+
2825
+
2826 // can't both be AMM
+
2827 if (ammLow && ammHigh)
+
2828 return tecINTERNAL; // LCOV_EXCL_LINE
+
2829
+
2830 // at least one must be
+
2831 if (!ammLow && !ammHigh)
+
2832 return terNO_AMM;
+
2833
+
2834 // one must be the target amm
+
2835 if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
+
2836 return terNO_AMM;
+
2837
+
2838 if (auto const ter = trustDelete(view, sleState, low, high, j);
+
2839 ter != tesSUCCESS)
+
2840 {
+
2841 JLOG(j.error())
+
2842 << "deleteAMMTrustLine: failed to delete the trustline.";
+
2843 return ter;
+
2844 }
2845
-
2846 return tesSUCCESS;
-
2847}
+
2846 auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve;
+
2847 if (!(sleState->getFlags() & uFlags))
+
2848 return tecINTERNAL; // LCOV_EXCL_LINE
+
2849
+
2850 adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j);
+
2851
+
2852 return tesSUCCESS;
+
2853}
-
2848
-
2849TER
-
- -
2851 ApplyView& view,
-
2852 AccountID const& uSenderID,
-
2853 AccountID const& uReceiverID,
-
2854 STAmount const& saAmount,
-
2855 bool bCheckIssuer,
- -
2857{
-
2858 return std::visit(
-
2859 [&]<ValidIssueType TIss>(TIss const& issue) {
-
2860 if constexpr (std::is_same_v<TIss, Issue>)
-
2861 {
-
2862 return rippleCreditIOU(
-
2863 view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j);
-
2864 }
-
2865 else
-
2866 {
-
2867 XRPL_ASSERT(
-
2868 !bCheckIssuer,
-
2869 "ripple::rippleCredit : not checking issuer");
-
2870 return rippleCreditMPT(
-
2871 view, uSenderID, uReceiverID, saAmount, j);
-
2872 }
-
2873 },
-
2874 saAmount.asset().value());
-
2875}
+
2854
+
2855TER
+
+ +
2857 ApplyView& view,
+
2858 AccountID const& uSenderID,
+
2859 AccountID const& uReceiverID,
+
2860 STAmount const& saAmount,
+
2861 bool bCheckIssuer,
+ +
2863{
+
2864 return std::visit(
+
2865 [&]<ValidIssueType TIss>(TIss const& issue) {
+
2866 if constexpr (std::is_same_v<TIss, Issue>)
+
2867 {
+
2868 return rippleCreditIOU(
+
2869 view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j);
+
2870 }
+
2871 else
+
2872 {
+
2873 XRPL_ASSERT(
+
2874 !bCheckIssuer,
+
2875 "ripple::rippleCredit : not checking issuer");
+
2876 return rippleCreditMPT(
+
2877 view, uSenderID, uReceiverID, saAmount, j);
+
2878 }
+
2879 },
+
2880 saAmount.asset().value());
+
2881}
-
2876
-
2877[[nodiscard]] std::optional<STAmount>
-
- -
2879 std::shared_ptr<SLE const> const& vault,
-
2880 std::shared_ptr<SLE const> const& issuance,
-
2881 STAmount const& assets)
-
2882{
-
2883 XRPL_ASSERT(
-
2884 !assets.negative(),
-
2885 "ripple::assetsToSharesDeposit : non-negative assets");
-
2886 XRPL_ASSERT(
-
2887 assets.asset() == vault->at(sfAsset),
-
2888 "ripple::assetsToSharesDeposit : assets and vault match");
-
2889 if (assets.negative() || assets.asset() != vault->at(sfAsset))
-
2890 return std::nullopt; // LCOV_EXCL_LINE
-
2891
-
2892 Number const assetTotal = vault->at(sfAssetsTotal);
-
2893 STAmount shares{vault->at(sfShareMPTID)};
-
2894 if (assetTotal == 0)
-
2895 return STAmount{
-
2896 shares.asset(),
-
2897 Number(assets.mantissa(), assets.exponent() + vault->at(sfScale))
-
2898 .truncate()};
-
2899
-
2900 Number const shareTotal = issuance->at(sfOutstandingAmount);
-
2901 shares = (shareTotal * (assets / assetTotal)).truncate();
-
2902 return shares;
-
2903}
+
2882
+
2883[[nodiscard]] std::optional<STAmount>
+
+ +
2885 std::shared_ptr<SLE const> const& vault,
+
2886 std::shared_ptr<SLE const> const& issuance,
+
2887 STAmount const& assets)
+
2888{
+
2889 XRPL_ASSERT(
+
2890 !assets.negative(),
+
2891 "ripple::assetsToSharesDeposit : non-negative assets");
+
2892 XRPL_ASSERT(
+
2893 assets.asset() == vault->at(sfAsset),
+
2894 "ripple::assetsToSharesDeposit : assets and vault match");
+
2895 if (assets.negative() || assets.asset() != vault->at(sfAsset))
+
2896 return std::nullopt; // LCOV_EXCL_LINE
+
2897
+
2898 Number const assetTotal = vault->at(sfAssetsTotal);
+
2899 STAmount shares{vault->at(sfShareMPTID)};
+
2900 if (assetTotal == 0)
+
2901 return STAmount{
+
2902 shares.asset(),
+
2903 Number(assets.mantissa(), assets.exponent() + vault->at(sfScale))
+
2904 .truncate()};
+
2905
+
2906 Number const shareTotal = issuance->at(sfOutstandingAmount);
+
2907 shares = (shareTotal * (assets / assetTotal)).truncate();
+
2908 return shares;
+
2909}
-
2904
-
2905[[nodiscard]] std::optional<STAmount>
-
- -
2907 std::shared_ptr<SLE const> const& vault,
-
2908 std::shared_ptr<SLE const> const& issuance,
-
2909 STAmount const& shares)
-
2910{
-
2911 XRPL_ASSERT(
-
2912 !shares.negative(),
-
2913 "ripple::sharesToAssetsDeposit : non-negative shares");
-
2914 XRPL_ASSERT(
-
2915 shares.asset() == vault->at(sfShareMPTID),
-
2916 "ripple::sharesToAssetsDeposit : shares and vault match");
-
2917 if (shares.negative() || shares.asset() != vault->at(sfShareMPTID))
-
2918 return std::nullopt; // LCOV_EXCL_LINE
-
2919
-
2920 Number const assetTotal = vault->at(sfAssetsTotal);
-
2921 STAmount assets{vault->at(sfAsset)};
-
2922 if (assetTotal == 0)
-
2923 return STAmount{
-
2924 assets.asset(),
-
2925 shares.mantissa(),
-
2926 shares.exponent() - vault->at(sfScale),
-
2927 false};
-
2928
-
2929 Number const shareTotal = issuance->at(sfOutstandingAmount);
-
2930 assets = assetTotal * (shares / shareTotal);
-
2931 return assets;
-
2932}
+
2910
+
2911[[nodiscard]] std::optional<STAmount>
+
+ +
2913 std::shared_ptr<SLE const> const& vault,
+
2914 std::shared_ptr<SLE const> const& issuance,
+
2915 STAmount const& shares)
+
2916{
+
2917 XRPL_ASSERT(
+
2918 !shares.negative(),
+
2919 "ripple::sharesToAssetsDeposit : non-negative shares");
+
2920 XRPL_ASSERT(
+
2921 shares.asset() == vault->at(sfShareMPTID),
+
2922 "ripple::sharesToAssetsDeposit : shares and vault match");
+
2923 if (shares.negative() || shares.asset() != vault->at(sfShareMPTID))
+
2924 return std::nullopt; // LCOV_EXCL_LINE
+
2925
+
2926 Number const assetTotal = vault->at(sfAssetsTotal);
+
2927 STAmount assets{vault->at(sfAsset)};
+
2928 if (assetTotal == 0)
+
2929 return STAmount{
+
2930 assets.asset(),
+
2931 shares.mantissa(),
+
2932 shares.exponent() - vault->at(sfScale),
+
2933 false};
+
2934
+
2935 Number const shareTotal = issuance->at(sfOutstandingAmount);
+
2936 assets = assetTotal * (shares / shareTotal);
+
2937 return assets;
+
2938}
-
2933
-
2934[[nodiscard]] std::optional<STAmount>
-
- -
2936 std::shared_ptr<SLE const> const& vault,
-
2937 std::shared_ptr<SLE const> const& issuance,
-
2938 STAmount const& assets,
-
2939 TruncateShares truncate)
-
2940{
-
2941 XRPL_ASSERT(
-
2942 !assets.negative(),
-
2943 "ripple::assetsToSharesDeposit : non-negative assets");
-
2944 XRPL_ASSERT(
-
2945 assets.asset() == vault->at(sfAsset),
-
2946 "ripple::assetsToSharesWithdraw : assets and vault match");
-
2947 if (assets.negative() || assets.asset() != vault->at(sfAsset))
-
2948 return std::nullopt; // LCOV_EXCL_LINE
-
2949
-
2950 Number assetTotal = vault->at(sfAssetsTotal);
-
2951 assetTotal -= vault->at(sfLossUnrealized);
-
2952 STAmount shares{vault->at(sfShareMPTID)};
-
2953 if (assetTotal == 0)
-
2954 return shares;
-
2955 Number const shareTotal = issuance->at(sfOutstandingAmount);
-
2956 Number result = shareTotal * (assets / assetTotal);
-
2957 if (truncate == TruncateShares::yes)
-
2958 result = result.truncate();
-
2959 shares = result;
-
2960 return shares;
-
2961}
+
2939
+
2940[[nodiscard]] std::optional<STAmount>
+
+ +
2942 std::shared_ptr<SLE const> const& vault,
+
2943 std::shared_ptr<SLE const> const& issuance,
+
2944 STAmount const& assets,
+
2945 TruncateShares truncate)
+
2946{
+
2947 XRPL_ASSERT(
+
2948 !assets.negative(),
+
2949 "ripple::assetsToSharesDeposit : non-negative assets");
+
2950 XRPL_ASSERT(
+
2951 assets.asset() == vault->at(sfAsset),
+
2952 "ripple::assetsToSharesWithdraw : assets and vault match");
+
2953 if (assets.negative() || assets.asset() != vault->at(sfAsset))
+
2954 return std::nullopt; // LCOV_EXCL_LINE
+
2955
+
2956 Number assetTotal = vault->at(sfAssetsTotal);
+
2957 assetTotal -= vault->at(sfLossUnrealized);
+
2958 STAmount shares{vault->at(sfShareMPTID)};
+
2959 if (assetTotal == 0)
+
2960 return shares;
+
2961 Number const shareTotal = issuance->at(sfOutstandingAmount);
+
2962 Number result = shareTotal * (assets / assetTotal);
+
2963 if (truncate == TruncateShares::yes)
+
2964 result = result.truncate();
+
2965 shares = result;
+
2966 return shares;
+
2967}
-
2962
-
2963[[nodiscard]] std::optional<STAmount>
-
- -
2965 std::shared_ptr<SLE const> const& vault,
-
2966 std::shared_ptr<SLE const> const& issuance,
-
2967 STAmount const& shares)
-
2968{
-
2969 XRPL_ASSERT(
-
2970 !shares.negative(),
-
2971 "ripple::sharesToAssetsDeposit : non-negative shares");
-
2972 XRPL_ASSERT(
-
2973 shares.asset() == vault->at(sfShareMPTID),
-
2974 "ripple::sharesToAssetsWithdraw : shares and vault match");
-
2975 if (shares.negative() || shares.asset() != vault->at(sfShareMPTID))
-
2976 return std::nullopt; // LCOV_EXCL_LINE
-
2977
-
2978 Number assetTotal = vault->at(sfAssetsTotal);
-
2979 assetTotal -= vault->at(sfLossUnrealized);
-
2980 STAmount assets{vault->at(sfAsset)};
-
2981 if (assetTotal == 0)
-
2982 return assets;
-
2983 Number const shareTotal = issuance->at(sfOutstandingAmount);
-
2984 assets = assetTotal * (shares / shareTotal);
-
2985 return assets;
-
2986}
+
2968
+
2969[[nodiscard]] std::optional<STAmount>
+
+ +
2971 std::shared_ptr<SLE const> const& vault,
+
2972 std::shared_ptr<SLE const> const& issuance,
+
2973 STAmount const& shares)
+
2974{
+
2975 XRPL_ASSERT(
+
2976 !shares.negative(),
+
2977 "ripple::sharesToAssetsDeposit : non-negative shares");
+
2978 XRPL_ASSERT(
+
2979 shares.asset() == vault->at(sfShareMPTID),
+
2980 "ripple::sharesToAssetsWithdraw : shares and vault match");
+
2981 if (shares.negative() || shares.asset() != vault->at(sfShareMPTID))
+
2982 return std::nullopt; // LCOV_EXCL_LINE
+
2983
+
2984 Number assetTotal = vault->at(sfAssetsTotal);
+
2985 assetTotal -= vault->at(sfLossUnrealized);
+
2986 STAmount assets{vault->at(sfAsset)};
+
2987 if (assetTotal == 0)
+
2988 return assets;
+
2989 Number const shareTotal = issuance->at(sfOutstandingAmount);
+
2990 assets = assetTotal * (shares / shareTotal);
+
2991 return assets;
+
2992}
-
2987
-
2988TER
-
- -
2990 ApplyView& view,
-
2991 AccountID const& sender,
-
2992 STAmount const& amount,
- -
2994{
-
2995 auto const mptIssue = amount.get<MPTIssue>();
-
2996 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
-
2997 auto sleIssuance = view.peek(mptID);
-
2998 if (!sleIssuance)
-
2999 { // LCOV_EXCL_START
-
3000 JLOG(j.error()) << "rippleLockEscrowMPT: MPT issuance not found for "
-
3001 << mptIssue.getMptID();
-
3002 return tecOBJECT_NOT_FOUND;
-
3003 } // LCOV_EXCL_STOP
-
3004
-
3005 if (amount.getIssuer() == sender)
-
3006 { // LCOV_EXCL_START
-
3007 JLOG(j.error())
-
3008 << "rippleLockEscrowMPT: sender is the issuer, cannot lock MPTs.";
-
3009 return tecINTERNAL;
-
3010 } // LCOV_EXCL_STOP
-
3011
-
3012 // 1. Decrease the MPT Holder MPTAmount
-
3013 // 2. Increase the MPT Holder EscrowedAmount
-
3014 {
-
3015 auto const mptokenID = keylet::mptoken(mptID.key, sender);
-
3016 auto sle = view.peek(mptokenID);
-
3017 if (!sle)
-
3018 { // LCOV_EXCL_START
-
3019 JLOG(j.error())
-
3020 << "rippleLockEscrowMPT: MPToken not found for " << sender;
-
3021 return tecOBJECT_NOT_FOUND;
-
3022 } // LCOV_EXCL_STOP
-
3023
-
3024 auto const amt = sle->getFieldU64(sfMPTAmount);
-
3025 auto const pay = amount.mpt().value();
-
3026
-
3027 // Underflow check for subtraction
-
3028 if (!canSubtract(STAmount(mptIssue, amt), STAmount(mptIssue, pay)))
-
3029 { // LCOV_EXCL_START
-
3030 JLOG(j.error())
-
3031 << "rippleLockEscrowMPT: insufficient MPTAmount for "
-
3032 << to_string(sender) << ": " << amt << " < " << pay;
-
3033 return tecINTERNAL;
-
3034 } // LCOV_EXCL_STOP
-
3035
-
3036 (*sle)[sfMPTAmount] = amt - pay;
-
3037
-
3038 // Overflow check for addition
-
3039 uint64_t const locked = (*sle)[~sfLockedAmount].value_or(0);
-
3040
-
3041 if (!canAdd(STAmount(mptIssue, locked), STAmount(mptIssue, pay)))
-
3042 { // LCOV_EXCL_START
-
3043 JLOG(j.error())
-
3044 << "rippleLockEscrowMPT: overflow on locked amount for "
-
3045 << to_string(sender) << ": " << locked << " + " << pay;
-
3046 return tecINTERNAL;
-
3047 } // LCOV_EXCL_STOP
-
3048
-
3049 if (sle->isFieldPresent(sfLockedAmount))
-
3050 (*sle)[sfLockedAmount] += pay;
-
3051 else
-
3052 sle->setFieldU64(sfLockedAmount, pay);
-
3053
-
3054 view.update(sle);
-
3055 }
-
3056
-
3057 // 1. Increase the Issuance EscrowedAmount
-
3058 // 2. DO NOT change the Issuance OutstandingAmount
-
3059 {
-
3060 uint64_t const issuanceEscrowed =
-
3061 (*sleIssuance)[~sfLockedAmount].value_or(0);
-
3062 auto const pay = amount.mpt().value();
-
3063
-
3064 // Overflow check for addition
-
3065 if (!canAdd(
-
3066 STAmount(mptIssue, issuanceEscrowed), STAmount(mptIssue, pay)))
-
3067 { // LCOV_EXCL_START
-
3068 JLOG(j.error()) << "rippleLockEscrowMPT: overflow on issuance "
-
3069 "locked amount for "
-
3070 << mptIssue.getMptID() << ": " << issuanceEscrowed
-
3071 << " + " << pay;
-
3072 return tecINTERNAL;
-
3073 } // LCOV_EXCL_STOP
-
3074
-
3075 if (sleIssuance->isFieldPresent(sfLockedAmount))
-
3076 (*sleIssuance)[sfLockedAmount] += pay;
-
3077 else
-
3078 sleIssuance->setFieldU64(sfLockedAmount, pay);
-
3079
-
3080 view.update(sleIssuance);
-
3081 }
-
3082 return tesSUCCESS;
-
3083}
-
-
3084
-
3085TER
-
- -
3087 ApplyView& view,
-
3088 AccountID const& sender,
-
3089 AccountID const& receiver,
-
3090 STAmount const& netAmount,
-
3091 STAmount const& grossAmount,
- -
3093{
-
3094 if (!view.rules().enabled(fixTokenEscrowV1))
-
3095 XRPL_ASSERT(
-
3096 netAmount == grossAmount,
-
3097 "ripple::rippleUnlockEscrowMPT : netAmount == grossAmount");
-
3098
-
3099 auto const& issuer = netAmount.getIssuer();
-
3100 auto const& mptIssue = netAmount.get<MPTIssue>();
-
3101 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
-
3102 auto sleIssuance = view.peek(mptID);
-
3103 if (!sleIssuance)
-
3104 { // LCOV_EXCL_START
-
3105 JLOG(j.error()) << "rippleUnlockEscrowMPT: MPT issuance not found for "
-
3106 << mptIssue.getMptID();
-
3107 return tecOBJECT_NOT_FOUND;
-
3108 } // LCOV_EXCL_STOP
-
3109
-
3110 // Decrease the Issuance EscrowedAmount
-
3111 {
-
3112 if (!sleIssuance->isFieldPresent(sfLockedAmount))
-
3113 { // LCOV_EXCL_START
-
3114 JLOG(j.error())
-
3115 << "rippleUnlockEscrowMPT: no locked amount in issuance for "
-
3116 << mptIssue.getMptID();
-
3117 return tecINTERNAL;
-
3118 } // LCOV_EXCL_STOP
-
3119
-
3120 auto const locked = sleIssuance->getFieldU64(sfLockedAmount);
-
3121 auto const redeem = grossAmount.mpt().value();
-
3122
-
3123 // Underflow check for subtraction
-
3124 if (!canSubtract(
-
3125 STAmount(mptIssue, locked), STAmount(mptIssue, redeem)))
-
3126 { // LCOV_EXCL_START
-
3127 JLOG(j.error())
-
3128 << "rippleUnlockEscrowMPT: insufficient locked amount for "
-
3129 << mptIssue.getMptID() << ": " << locked << " < " << redeem;
-
3130 return tecINTERNAL;
-
3131 } // LCOV_EXCL_STOP
-
3132
-
3133 auto const newLocked = locked - redeem;
-
3134 if (newLocked == 0)
-
3135 sleIssuance->makeFieldAbsent(sfLockedAmount);
-
3136 else
-
3137 sleIssuance->setFieldU64(sfLockedAmount, newLocked);
-
3138 view.update(sleIssuance);
-
3139 }
-
3140
-
3141 if (issuer != receiver)
-
3142 {
-
3143 // Increase the MPT Holder MPTAmount
-
3144 auto const mptokenID = keylet::mptoken(mptID.key, receiver);
-
3145 auto sle = view.peek(mptokenID);
-
3146 if (!sle)
-
3147 { // LCOV_EXCL_START
-
3148 JLOG(j.error())
-
3149 << "rippleUnlockEscrowMPT: MPToken not found for " << receiver;
-
3150 return tecOBJECT_NOT_FOUND;
-
3151 } // LCOV_EXCL_STOP
-
3152
-
3153 auto current = sle->getFieldU64(sfMPTAmount);
-
3154 auto delta = netAmount.mpt().value();
-
3155
-
3156 // Overflow check for addition
-
3157 if (!canAdd(STAmount(mptIssue, current), STAmount(mptIssue, delta)))
-
3158 { // LCOV_EXCL_START
-
3159 JLOG(j.error())
-
3160 << "rippleUnlockEscrowMPT: overflow on MPTAmount for "
-
3161 << to_string(receiver) << ": " << current << " + " << delta;
-
3162 return tecINTERNAL;
-
3163 } // LCOV_EXCL_STOP
-
3164
-
3165 (*sle)[sfMPTAmount] += delta;
-
3166 view.update(sle);
-
3167 }
-
3168 else
-
3169 {
-
3170 // Decrease the Issuance OutstandingAmount
-
3171 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
-
3172 auto const redeem = netAmount.mpt().value();
-
3173
-
3174 // Underflow check for subtraction
-
3175 if (!canSubtract(
-
3176 STAmount(mptIssue, outstanding), STAmount(mptIssue, redeem)))
-
3177 { // LCOV_EXCL_START
-
3178 JLOG(j.error())
-
3179 << "rippleUnlockEscrowMPT: insufficient outstanding amount for "
-
3180 << mptIssue.getMptID() << ": " << outstanding << " < "
-
3181 << redeem;
-
3182 return tecINTERNAL;
-
3183 } // LCOV_EXCL_STOP
-
3184
-
3185 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
-
3186 view.update(sleIssuance);
-
3187 }
-
3188
-
3189 if (issuer == sender)
-
3190 { // LCOV_EXCL_START
-
3191 JLOG(j.error()) << "rippleUnlockEscrowMPT: sender is the issuer, "
-
3192 "cannot unlock MPTs.";
-
3193 return tecINTERNAL;
-
3194 } // LCOV_EXCL_STOP
-
3195 else
-
3196 {
-
3197 // Decrease the MPT Holder EscrowedAmount
-
3198 auto const mptokenID = keylet::mptoken(mptID.key, sender);
-
3199 auto sle = view.peek(mptokenID);
-
3200 if (!sle)
-
3201 { // LCOV_EXCL_START
-
3202 JLOG(j.error())
-
3203 << "rippleUnlockEscrowMPT: MPToken not found for " << sender;
-
3204 return tecOBJECT_NOT_FOUND;
-
3205 } // LCOV_EXCL_STOP
-
3206
-
3207 if (!sle->isFieldPresent(sfLockedAmount))
-
3208 { // LCOV_EXCL_START
-
3209 JLOG(j.error())
-
3210 << "rippleUnlockEscrowMPT: no locked amount in MPToken for "
-
3211 << to_string(sender);
-
3212 return tecINTERNAL;
-
3213 } // LCOV_EXCL_STOP
-
3214
-
3215 auto const locked = sle->getFieldU64(sfLockedAmount);
-
3216 auto const delta = grossAmount.mpt().value();
-
3217
-
3218 // Underflow check for subtraction
-
3219 if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta)))
-
3220 { // LCOV_EXCL_START
-
3221 JLOG(j.error())
-
3222 << "rippleUnlockEscrowMPT: insufficient locked amount for "
-
3223 << to_string(sender) << ": " << locked << " < " << delta;
-
3224 return tecINTERNAL;
-
3225 } // LCOV_EXCL_STOP
-
3226
-
3227 auto const newLocked = locked - delta;
-
3228 if (newLocked == 0)
-
3229 sle->makeFieldAbsent(sfLockedAmount);
-
3230 else
-
3231 sle->setFieldU64(sfLockedAmount, newLocked);
-
3232 view.update(sle);
-
3233 }
-
3234
-
3235 // Note: The gross amount is the amount that was locked, the net
-
3236 // amount is the amount that is being unlocked. The difference is the fee
-
3237 // that was charged for the transfer. If this difference is greater than
-
3238 // zero, we need to update the outstanding amount.
-
3239 auto const diff = grossAmount.mpt().value() - netAmount.mpt().value();
-
3240 if (diff != 0)
-
3241 {
-
3242 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
-
3243 // Underflow check for subtraction
-
3244 if (!canSubtract(
-
3245 STAmount(mptIssue, outstanding), STAmount(mptIssue, diff)))
-
3246 { // LCOV_EXCL_START
-
3247 JLOG(j.error())
-
3248 << "rippleUnlockEscrowMPT: insufficient outstanding amount for "
-
3249 << mptIssue.getMptID() << ": " << outstanding << " < " << diff;
-
3250 return tecINTERNAL;
-
3251 } // LCOV_EXCL_STOP
-
3252
-
3253 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff);
-
3254 view.update(sleIssuance);
-
3255 }
-
3256 return tesSUCCESS;
-
3257}
+
2993
+
2994TER
+
+ +
2996 ApplyView& view,
+
2997 AccountID const& sender,
+
2998 STAmount const& amount,
+ +
3000{
+
3001 auto const mptIssue = amount.get<MPTIssue>();
+
3002 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
+
3003 auto sleIssuance = view.peek(mptID);
+
3004 if (!sleIssuance)
+
3005 { // LCOV_EXCL_START
+
3006 JLOG(j.error()) << "rippleLockEscrowMPT: MPT issuance not found for "
+
3007 << mptIssue.getMptID();
+
3008 return tecOBJECT_NOT_FOUND;
+
3009 } // LCOV_EXCL_STOP
+
3010
+
3011 if (amount.getIssuer() == sender)
+
3012 { // LCOV_EXCL_START
+
3013 JLOG(j.error())
+
3014 << "rippleLockEscrowMPT: sender is the issuer, cannot lock MPTs.";
+
3015 return tecINTERNAL;
+
3016 } // LCOV_EXCL_STOP
+
3017
+
3018 // 1. Decrease the MPT Holder MPTAmount
+
3019 // 2. Increase the MPT Holder EscrowedAmount
+
3020 {
+
3021 auto const mptokenID = keylet::mptoken(mptID.key, sender);
+
3022 auto sle = view.peek(mptokenID);
+
3023 if (!sle)
+
3024 { // LCOV_EXCL_START
+
3025 JLOG(j.error())
+
3026 << "rippleLockEscrowMPT: MPToken not found for " << sender;
+
3027 return tecOBJECT_NOT_FOUND;
+
3028 } // LCOV_EXCL_STOP
+
3029
+
3030 auto const amt = sle->getFieldU64(sfMPTAmount);
+
3031 auto const pay = amount.mpt().value();
+
3032
+
3033 // Underflow check for subtraction
+
3034 if (!canSubtract(STAmount(mptIssue, amt), STAmount(mptIssue, pay)))
+
3035 { // LCOV_EXCL_START
+
3036 JLOG(j.error())
+
3037 << "rippleLockEscrowMPT: insufficient MPTAmount for "
+
3038 << to_string(sender) << ": " << amt << " < " << pay;
+
3039 return tecINTERNAL;
+
3040 } // LCOV_EXCL_STOP
+
3041
+
3042 (*sle)[sfMPTAmount] = amt - pay;
+
3043
+
3044 // Overflow check for addition
+
3045 uint64_t const locked = (*sle)[~sfLockedAmount].value_or(0);
+
3046
+
3047 if (!canAdd(STAmount(mptIssue, locked), STAmount(mptIssue, pay)))
+
3048 { // LCOV_EXCL_START
+
3049 JLOG(j.error())
+
3050 << "rippleLockEscrowMPT: overflow on locked amount for "
+
3051 << to_string(sender) << ": " << locked << " + " << pay;
+
3052 return tecINTERNAL;
+
3053 } // LCOV_EXCL_STOP
+
3054
+
3055 if (sle->isFieldPresent(sfLockedAmount))
+
3056 (*sle)[sfLockedAmount] += pay;
+
3057 else
+
3058 sle->setFieldU64(sfLockedAmount, pay);
+
3059
+
3060 view.update(sle);
+
3061 }
+
3062
+
3063 // 1. Increase the Issuance EscrowedAmount
+
3064 // 2. DO NOT change the Issuance OutstandingAmount
+
3065 {
+
3066 uint64_t const issuanceEscrowed =
+
3067 (*sleIssuance)[~sfLockedAmount].value_or(0);
+
3068 auto const pay = amount.mpt().value();
+
3069
+
3070 // Overflow check for addition
+
3071 if (!canAdd(
+
3072 STAmount(mptIssue, issuanceEscrowed), STAmount(mptIssue, pay)))
+
3073 { // LCOV_EXCL_START
+
3074 JLOG(j.error()) << "rippleLockEscrowMPT: overflow on issuance "
+
3075 "locked amount for "
+
3076 << mptIssue.getMptID() << ": " << issuanceEscrowed
+
3077 << " + " << pay;
+
3078 return tecINTERNAL;
+
3079 } // LCOV_EXCL_STOP
+
3080
+
3081 if (sleIssuance->isFieldPresent(sfLockedAmount))
+
3082 (*sleIssuance)[sfLockedAmount] += pay;
+
3083 else
+
3084 sleIssuance->setFieldU64(sfLockedAmount, pay);
+
3085
+
3086 view.update(sleIssuance);
+
3087 }
+
3088 return tesSUCCESS;
+
3089}
+
3090
+
3091TER
+
+ +
3093 ApplyView& view,
+
3094 AccountID const& sender,
+
3095 AccountID const& receiver,
+
3096 STAmount const& netAmount,
+
3097 STAmount const& grossAmount,
+ +
3099{
+
3100 if (!view.rules().enabled(fixTokenEscrowV1))
+
3101 XRPL_ASSERT(
+
3102 netAmount == grossAmount,
+
3103 "ripple::rippleUnlockEscrowMPT : netAmount == grossAmount");
+
3104
+
3105 auto const& issuer = netAmount.getIssuer();
+
3106 auto const& mptIssue = netAmount.get<MPTIssue>();
+
3107 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
+
3108 auto sleIssuance = view.peek(mptID);
+
3109 if (!sleIssuance)
+
3110 { // LCOV_EXCL_START
+
3111 JLOG(j.error()) << "rippleUnlockEscrowMPT: MPT issuance not found for "
+
3112 << mptIssue.getMptID();
+
3113 return tecOBJECT_NOT_FOUND;
+
3114 } // LCOV_EXCL_STOP
+
3115
+
3116 // Decrease the Issuance EscrowedAmount
+
3117 {
+
3118 if (!sleIssuance->isFieldPresent(sfLockedAmount))
+
3119 { // LCOV_EXCL_START
+
3120 JLOG(j.error())
+
3121 << "rippleUnlockEscrowMPT: no locked amount in issuance for "
+
3122 << mptIssue.getMptID();
+
3123 return tecINTERNAL;
+
3124 } // LCOV_EXCL_STOP
+
3125
+
3126 auto const locked = sleIssuance->getFieldU64(sfLockedAmount);
+
3127 auto const redeem = grossAmount.mpt().value();
+
3128
+
3129 // Underflow check for subtraction
+
3130 if (!canSubtract(
+
3131 STAmount(mptIssue, locked), STAmount(mptIssue, redeem)))
+
3132 { // LCOV_EXCL_START
+
3133 JLOG(j.error())
+
3134 << "rippleUnlockEscrowMPT: insufficient locked amount for "
+
3135 << mptIssue.getMptID() << ": " << locked << " < " << redeem;
+
3136 return tecINTERNAL;
+
3137 } // LCOV_EXCL_STOP
+
3138
+
3139 auto const newLocked = locked - redeem;
+
3140 if (newLocked == 0)
+
3141 sleIssuance->makeFieldAbsent(sfLockedAmount);
+
3142 else
+
3143 sleIssuance->setFieldU64(sfLockedAmount, newLocked);
+
3144 view.update(sleIssuance);
+
3145 }
+
3146
+
3147 if (issuer != receiver)
+
3148 {
+
3149 // Increase the MPT Holder MPTAmount
+
3150 auto const mptokenID = keylet::mptoken(mptID.key, receiver);
+
3151 auto sle = view.peek(mptokenID);
+
3152 if (!sle)
+
3153 { // LCOV_EXCL_START
+
3154 JLOG(j.error())
+
3155 << "rippleUnlockEscrowMPT: MPToken not found for " << receiver;
+
3156 return tecOBJECT_NOT_FOUND;
+
3157 } // LCOV_EXCL_STOP
+
3158
+
3159 auto current = sle->getFieldU64(sfMPTAmount);
+
3160 auto delta = netAmount.mpt().value();
+
3161
+
3162 // Overflow check for addition
+
3163 if (!canAdd(STAmount(mptIssue, current), STAmount(mptIssue, delta)))
+
3164 { // LCOV_EXCL_START
+
3165 JLOG(j.error())
+
3166 << "rippleUnlockEscrowMPT: overflow on MPTAmount for "
+
3167 << to_string(receiver) << ": " << current << " + " << delta;
+
3168 return tecINTERNAL;
+
3169 } // LCOV_EXCL_STOP
+
3170
+
3171 (*sle)[sfMPTAmount] += delta;
+
3172 view.update(sle);
+
3173 }
+
3174 else
+
3175 {
+
3176 // Decrease the Issuance OutstandingAmount
+
3177 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
+
3178 auto const redeem = netAmount.mpt().value();
+
3179
+
3180 // Underflow check for subtraction
+
3181 if (!canSubtract(
+
3182 STAmount(mptIssue, outstanding), STAmount(mptIssue, redeem)))
+
3183 { // LCOV_EXCL_START
+
3184 JLOG(j.error())
+
3185 << "rippleUnlockEscrowMPT: insufficient outstanding amount for "
+
3186 << mptIssue.getMptID() << ": " << outstanding << " < "
+
3187 << redeem;
+
3188 return tecINTERNAL;
+
3189 } // LCOV_EXCL_STOP
+
3190
+
3191 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
+
3192 view.update(sleIssuance);
+
3193 }
+
3194
+
3195 if (issuer == sender)
+
3196 { // LCOV_EXCL_START
+
3197 JLOG(j.error()) << "rippleUnlockEscrowMPT: sender is the issuer, "
+
3198 "cannot unlock MPTs.";
+
3199 return tecINTERNAL;
+
3200 } // LCOV_EXCL_STOP
+
3201 else
+
3202 {
+
3203 // Decrease the MPT Holder EscrowedAmount
+
3204 auto const mptokenID = keylet::mptoken(mptID.key, sender);
+
3205 auto sle = view.peek(mptokenID);
+
3206 if (!sle)
+
3207 { // LCOV_EXCL_START
+
3208 JLOG(j.error())
+
3209 << "rippleUnlockEscrowMPT: MPToken not found for " << sender;
+
3210 return tecOBJECT_NOT_FOUND;
+
3211 } // LCOV_EXCL_STOP
+
3212
+
3213 if (!sle->isFieldPresent(sfLockedAmount))
+
3214 { // LCOV_EXCL_START
+
3215 JLOG(j.error())
+
3216 << "rippleUnlockEscrowMPT: no locked amount in MPToken for "
+
3217 << to_string(sender);
+
3218 return tecINTERNAL;
+
3219 } // LCOV_EXCL_STOP
+
3220
+
3221 auto const locked = sle->getFieldU64(sfLockedAmount);
+
3222 auto const delta = grossAmount.mpt().value();
+
3223
+
3224 // Underflow check for subtraction
+
3225 if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta)))
+
3226 { // LCOV_EXCL_START
+
3227 JLOG(j.error())
+
3228 << "rippleUnlockEscrowMPT: insufficient locked amount for "
+
3229 << to_string(sender) << ": " << locked << " < " << delta;
+
3230 return tecINTERNAL;
+
3231 } // LCOV_EXCL_STOP
+
3232
+
3233 auto const newLocked = locked - delta;
+
3234 if (newLocked == 0)
+
3235 sle->makeFieldAbsent(sfLockedAmount);
+
3236 else
+
3237 sle->setFieldU64(sfLockedAmount, newLocked);
+
3238 view.update(sle);
+
3239 }
+
3240
+
3241 // Note: The gross amount is the amount that was locked, the net
+
3242 // amount is the amount that is being unlocked. The difference is the fee
+
3243 // that was charged for the transfer. If this difference is greater than
+
3244 // zero, we need to update the outstanding amount.
+
3245 auto const diff = grossAmount.mpt().value() - netAmount.mpt().value();
+
3246 if (diff != 0)
+
3247 {
+
3248 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
+
3249 // Underflow check for subtraction
+
3250 if (!canSubtract(
+
3251 STAmount(mptIssue, outstanding), STAmount(mptIssue, diff)))
+
3252 { // LCOV_EXCL_START
+
3253 JLOG(j.error())
+
3254 << "rippleUnlockEscrowMPT: insufficient outstanding amount for "
+
3255 << mptIssue.getMptID() << ": " << outstanding << " < " << diff;
+
3256 return tecINTERNAL;
+
3257 } // LCOV_EXCL_STOP
3258
-
3259bool
-
- -
3261{
-
3262 return now.time_since_epoch().count() > mark;
+
3259 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff);
+
3260 view.update(sleIssuance);
+
3261 }
+
3262 return tesSUCCESS;
3263}
3264
-
3265} // namespace ripple
+
3265bool
+
+ +
3267{
+
3268 return now.time_since_epoch().count() > mark;
+
3269}
+
+
3270
+
3271} // namespace ripple
Provide a light-weight way to check active() before string formatting.
Definition Journal.h:205
@@ -3606,7 +3612,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:48
AccountID const & noAccount()
A placeholder for empty accounts.
-
static bool updateTrustLine(ApplyView &view, SLE::pointer state, bool bSenderHigh, AccountID const &sender, STAmount const &before, STAmount const &after, beast::Journal j)
Definition View.cpp:2206
+
static bool updateTrustLine(ApplyView &view, SLE::pointer state, bool bSenderHigh, AccountID const &sender, STAmount const &before, STAmount const &after, beast::Journal j)
Definition View.cpp:2212
std::uint8_t constexpr maxAssetCheckDepth
Maximum recursion depth for vault shares being put as an asset inside another vault; counted from 0.
Definition Protocol.h:133
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition View.cpp:554
FreezeHandling
Controls the treatment of frozen account balances.
Definition View.h:77
@@ -3614,19 +3620,19 @@ $(document).ready(function() { init_codefold(0); });
bool areCompatible(ReadView const &validLedger, ReadView const &testLedger, beast::Journal::Stream &s, char const *reason)
Return false if the test ledger is provably incompatible with the valid ledger, that is,...
Definition View.cpp:799
bool isXRP(AccountID const &c)
Definition AccountID.h:90
AccountID const & xrpAccount()
Compute AccountID from public key.
-
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2989
+
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2995
@ telFAILED_PROCESSING
Definition TER.h:56
-
std::optional< STAmount > sharesToAssetsDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2906
+
std::optional< STAmount > sharesToAssetsDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2912
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:213
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
Definition STAmount.cpp:608
-
static TER rippleSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:2094
+
static TER rippleSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:2100
bool dirFirst(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition View.cpp:123
bool dirNext(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition View.cpp:134
bool canAdd(STAmount const &amt1, STAmount const &amt2)
Safely checks if two STAmount values can be added without overflow, underflow, or precision loss.
Definition STAmount.cpp:528
bool isDeepFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:350
std::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition View.cpp:961
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
Definition Protocol.h:116
-
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2359
+
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2365
@ lsfHighDeepFreeze
@ lsfMPTCanTransfer
@ lsfDefaultRipple
@@ -3650,40 +3656,41 @@ $(document).ready(function() { init_codefold(0); });
AuthType
Definition View.h:786
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:1032
-
std::optional< STAmount > assetsToSharesDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition View.cpp:2878
+
std::optional< STAmount > assetsToSharesDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition View.cpp:2884
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:53
AuthHandling
Controls the treatment of unauthorized MPT balances.
Definition View.h:80
@ ahZERO_IF_UNAUTHORIZED
Definition View.h:80
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:1050
-
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:2935
-
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition View.cpp:2434
+
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:2941
+
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition View.cpp:2440
@ current
This was a new validation and was added.
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
Definition View.cpp:907
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< std::uint16_t > maxNodesToDelete=std::nullopt)
Cleanup owner directory entries on account delete.
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:247
@ expired
List is expired, but has the largest non-pending sequence seen so far.
std::set< uint256 > getEnabledAmendments(ReadView const &view)
Definition View.cpp:921
-
TER rippleUnlockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, AccountID const &uGranteeID, STAmount const &netAmount, STAmount const &grossAmount, beast::Journal j)
Definition View.cpp:3086
-
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2479
+
TER rippleUnlockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, AccountID const &uGranteeID, STAmount const &netAmount, STAmount const &grossAmount, beast::Journal j)
Definition View.cpp:3092
+
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2485
@ tefBAD_LEDGER
Definition TER.h:170
@ tefINTERNAL
Definition TER.h:173
-
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2964
+
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2970
void forEachItem(ReadView const &view, Keylet const &root, std::function< void(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items in the given directory.
Definition View.cpp:656
static std::uint32_t confineOwnerCount(std::uint32_t current, std::int32_t adjustment, std::optional< AccountID > const &id=std::nullopt, beast::Journal j=beast::Journal{beast::Journal::getNullSink()})
Definition View.cpp:580
-
static TER rippleCreditIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Definition View.cpp:1702
+
static TER rippleCreditIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Definition View.cpp:1708
constexpr std::uint32_t const tfMPTUnauthorize
Definition TxFlags.h:172
-
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:2698
+
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:2704
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:1132
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:173
bool isVaultPseudoAccountFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptShare, int depth)
Definition View.cpp:307
-
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2259
-
static TER accountSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:1908
+
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2265
+
static TER accountSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:1914
std::map< uint256, NetClock::time_point > majorityAmendments_t
Definition View.h:400
bool forEachItemAfter(ReadView const &view, Keylet const &root, uint256 const &after, std::uint64_t const hint, unsigned int limit, std::function< bool(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items after an item in the given directory.
Definition View.cpp:684
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:762
WaiveTransferFee
Definition View.h:43
-
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition View.cpp:1601
+
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition View.cpp:1607
+
@ tecNO_LINE_INSUF_RESERVE
Definition TER.h:292
@ tecOBJECT_NOT_FOUND
Definition TER.h:326
@ tecNO_TARGET
Definition TER.h:304
@ tecDIR_FULL
Definition TER.h:287
@@ -3699,7 +3706,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecFAILED_PROCESSING
Definition TER.h:286
@ tecEXPIRED
Definition TER.h:314
@ tecNO_AUTH
Definition TER.h:300
-
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2850
+
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2856
@ tesSUCCESS
Definition TER.h:244
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:1216
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition View.cpp:1069
@@ -3711,30 +3718,30 @@ $(document).ready(function() { init_codefold(0); });
bool cdirFirst(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the first entry in the directory, advancing the index.
Definition View.cpp:145
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
LedgerEntryType
Identifiers for on-ledger objects.
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
-
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1392
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
+
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1398
TruncateShares
Definition View.h:949
Number root(Number f, unsigned d)
Definition Number.cpp:636
TER verifyValidDomain(ApplyView &view, AccountID const &account, uint256 domainID, beast::Journal j)
-
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition View.cpp:2800
-
TER enforceMPTokenAuthorization(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, XRPAmount const &priorBalance, beast::Journal j)
Enforce account has MPToken to match its authorization.
Definition View.cpp:2597
+
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition View.cpp:2806
+
TER enforceMPTokenAuthorization(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, XRPAmount const &priorBalance, beast::Journal j)
Enforce account has MPToken to match its authorization.
Definition View.cpp:2603
TER canAddHolding(ReadView const &view, Asset const &asset)
Definition View.cpp:1206
bool cdirNext(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the next entry in the directory, advancing the index.
Definition View.cpp:156
-
static TER rippleSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:1856
+
static TER rippleSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:1862
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
Definition View.cpp:282
std::vector< SField const * > const & getPseudoAccountFields()
Definition View.cpp:1092
@ terNO_ACCOUNT
Definition TER.h:217
@ terNO_RIPPLE
Definition TER.h:224
@ terNO_AMM
Definition TER.h:227
-
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:1284
+
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:1290
TERSubset< CanCvtToTER > TER
Definition TER.h:645
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:224
-
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition View.cpp:1511
-
static TER rippleCreditMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2032
+
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition View.cpp:1517
+
static TER rippleCreditMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2038
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
-
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1641
+
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition View.cpp:1118
-
static TER accountSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:2160
+
static TER accountSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:2166
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object)
Definition View.cpp:1058
Rate const parityRate
A transfer rate signifying a 1:1 exchange.
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition View.cpp:618
diff --git a/View_8h_source.html b/View_8h_source.html index 06a3bd37fa..29fdc786d7 100644 --- a/View_8h_source.html +++ b/View_8h_source.html @@ -897,56 +897,56 @@ $(document).ready(function() { init_codefold(0); });
@ fhZERO_IF_FROZEN
Definition View.h:77
@ fhIGNORE_FREEZE
Definition View.h:77
bool areCompatible(ReadView const &validLedger, ReadView const &testLedger, beast::Journal::Stream &s, char const *reason)
Return false if the test ledger is provably incompatible with the valid ledger, that is,...
Definition View.cpp:799
-
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2989
+
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2995
LedgerIndex getCandidateLedger(LedgerIndex requested)
Find a ledger index from which we could easily get the requested ledger.
Definition View.h:429
-
std::optional< STAmount > sharesToAssetsDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2906
+
std::optional< STAmount > sharesToAssetsDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2912
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:213
bool dirFirst(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition View.cpp:123
base_uint< 256 > uint256
Definition base_uint.h:558
bool dirNext(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition View.cpp:134
bool isDeepFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:350
std::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition View.cpp:961
-
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2359
+
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2365
base_uint< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:64
AuthType
Definition View.h:786
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:1032
-
std::optional< STAmount > assetsToSharesDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition View.cpp:2878
+
std::optional< STAmount > assetsToSharesDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition View.cpp:2884
AuthHandling
Controls the treatment of unauthorized MPT balances.
Definition View.h:80
@ ahZERO_IF_UNAUTHORIZED
Definition View.h:80
@ ahIGNORE_AUTH
Definition View.h:80
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:1050
SkipEntry
Definition View.h:44
-
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:2935
-
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition View.cpp:2434
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:2941
+
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition View.cpp:2440
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
Definition View.cpp:907
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< std::uint16_t > maxNodesToDelete=std::nullopt)
Cleanup owner directory entries on account delete.
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:247
std::set< uint256 > getEnabledAmendments(ReadView const &view)
Definition View.cpp:921
-
TER rippleUnlockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, AccountID const &uGranteeID, STAmount const &netAmount, STAmount const &grossAmount, beast::Journal j)
Definition View.cpp:3086
-
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2479
+
TER rippleUnlockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, AccountID const &uGranteeID, STAmount const &netAmount, STAmount const &grossAmount, beast::Journal j)
Definition View.cpp:3092
+
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2485
@ no
Definition Steps.h:45
@ yes
Definition Steps.h:45
-
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2964
+
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2970
void forEachItem(ReadView const &view, Keylet const &root, std::function< void(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items in the given directory.
Definition View.cpp:656
base_uint< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:56
-
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:2698
+
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:2704
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:1132
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:173
bool isVaultPseudoAccountFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptShare, int depth)
Definition View.cpp:307
-
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2259
+
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2265
bool forEachItemAfter(ReadView const &view, Keylet const &root, uint256 const &after, std::uint64_t const hint, unsigned int limit, std::function< bool(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items after an item in the given directory.
Definition View.cpp:684
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:762
WaiveTransferFee
Definition View.h:43
-
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition View.cpp:1601
+
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition View.cpp:1607
@ tecFROZEN
Definition TER.h:303
@ tecLOCKED
Definition TER.h:358
-
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2850
+
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2856
@ tesSUCCESS
Definition TER.h:244
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:1216
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition View.cpp:1069
@@ -955,21 +955,21 @@ $(document).ready(function() { init_codefold(0); });
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition View.cpp:938
bool cdirFirst(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the first entry in the directory, advancing the index.
Definition View.cpp:145
LedgerEntryType
Identifiers for on-ledger objects.
-
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
-
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1392
+
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
+
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1398
TruncateShares
Definition View.h:949
Number root(Number f, unsigned d)
Definition Number.cpp:636
-
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition View.cpp:2800
-
TER enforceMPTokenAuthorization(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, XRPAmount const &priorBalance, beast::Journal j)
Enforce account has MPToken to match its authorization.
Definition View.cpp:2597
+
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition View.cpp:2806
+
TER enforceMPTokenAuthorization(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, XRPAmount const &priorBalance, beast::Journal j)
Enforce account has MPToken to match its authorization.
Definition View.cpp:2603
TER canAddHolding(ReadView const &view, Asset const &asset)
Definition View.cpp:1206
bool cdirNext(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the next entry in the directory, advancing the index.
Definition View.cpp:156
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
Definition View.cpp:282
std::vector< SField const * > const & getPseudoAccountFields()
Definition View.cpp:1092
-
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:1284
+
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:1290
TERSubset< CanCvtToTER > TER
Definition TER.h:645
-
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition View.cpp:1511
-
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1641
+
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition View.cpp:1517
+
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1647
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition View.cpp:1118
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object)
Definition View.cpp:1058
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition View.cpp:618
diff --git a/XRPEndpointStep_8cpp_source.html b/XRPEndpointStep_8cpp_source.html index 972c82b026..1dec98a6d0 100644 --- a/XRPEndpointStep_8cpp_source.html +++ b/XRPEndpointStep_8cpp_source.html @@ -632,7 +632,7 @@ $(document).ready(function() { init_codefold(0); });
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition StepChecks.h:33
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
-
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
+
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2191
@ tefINTERNAL
Definition TER.h:173
diff --git a/classripple_1_1VaultWithdraw.html b/classripple_1_1VaultWithdraw.html index 6bb1501c81..1e51def6b6 100644 --- a/classripple_1_1VaultWithdraw.html +++ b/classripple_1_1VaultWithdraw.html @@ -470,7 +470,7 @@ Static Private Member Functions
-

Definition at line 66 of file VaultWithdraw.cpp.

+

Definition at line 60 of file VaultWithdraw.cpp.

@@ -499,7 +499,7 @@ Static Private Member Functions

Implements ripple::Transactor.

-

Definition at line 165 of file VaultWithdraw.cpp.

+

Definition at line 150 of file VaultWithdraw.cpp.

diff --git a/classripple_1_1Vault__test.html b/classripple_1_1Vault__test.html index 0382555a0e..250f833566 100644 --- a/classripple_1_1Vault__test.html +++ b/classripple_1_1Vault__test.html @@ -381,7 +381,7 @@ Static Private Attributes
-

Definition at line 603 of file Vault_test.cpp.

+

Definition at line 602 of file Vault_test.cpp.

@@ -408,7 +408,7 @@ Static Private Attributes
-

Definition at line 1188 of file Vault_test.cpp.

+

Definition at line 1187 of file Vault_test.cpp.

@@ -435,7 +435,7 @@ Static Private Attributes
-

Definition at line 1361 of file Vault_test.cpp.

+

Definition at line 1360 of file Vault_test.cpp.

@@ -462,7 +462,7 @@ Static Private Attributes
-

Definition at line 1490 of file Vault_test.cpp.

+

Definition at line 1489 of file Vault_test.cpp.

@@ -489,7 +489,7 @@ Static Private Attributes
-

Definition at line 1556 of file Vault_test.cpp.

+

Definition at line 1555 of file Vault_test.cpp.

@@ -516,7 +516,7 @@ Static Private Attributes
-

Definition at line 1667 of file Vault_test.cpp.

+

Definition at line 1666 of file Vault_test.cpp.

@@ -543,7 +543,7 @@ Static Private Attributes
-

Definition at line 2319 of file Vault_test.cpp.

+

Definition at line 2417 of file Vault_test.cpp.

@@ -570,7 +570,7 @@ Static Private Attributes
-

Definition at line 2829 of file Vault_test.cpp.

+

Definition at line 3039 of file Vault_test.cpp.

@@ -597,7 +597,7 @@ Static Private Attributes
-

Definition at line 3122 of file Vault_test.cpp.

+

Definition at line 3332 of file Vault_test.cpp.

@@ -624,7 +624,7 @@ Static Private Attributes
-

Definition at line 3227 of file Vault_test.cpp.

+

Definition at line 3437 of file Vault_test.cpp.

@@ -651,7 +651,7 @@ Static Private Attributes
-

Definition at line 3256 of file Vault_test.cpp.

+

Definition at line 3466 of file Vault_test.cpp.

@@ -678,7 +678,7 @@ Static Private Attributes
-

Definition at line 4120 of file Vault_test.cpp.

+

Definition at line 4330 of file Vault_test.cpp.

@@ -709,7 +709,7 @@ Static Private Attributes

Implements beast::unit_test::suite.

-

Definition at line 4590 of file Vault_test.cpp.

+

Definition at line 4800 of file Vault_test.cpp.

diff --git a/namespaceripple.html b/namespaceripple.html index 5b87b48b4b..5655b715fb 100644 --- a/namespaceripple.html +++ b/namespaceripple.html @@ -16909,7 +16909,7 @@ template<class T >
-

Definition at line 1264 of file View.cpp.

+

Definition at line 1270 of file View.cpp.

@@ -17017,7 +17017,7 @@ template<class T >
-

Definition at line 1284 of file View.cpp.

+

Definition at line 1290 of file View.cpp.

@@ -17128,7 +17128,7 @@ template<class T >

Create a trust line.

This can set an initial balance.

-

Definition at line 1392 of file View.cpp.

+

Definition at line 1398 of file View.cpp.

@@ -17170,7 +17170,7 @@ template<class T >
-

Definition at line 1511 of file View.cpp.

+

Definition at line 1517 of file View.cpp.

@@ -17212,7 +17212,7 @@ template<class T >
-

Definition at line 1577 of file View.cpp.

+

Definition at line 1583 of file View.cpp.

@@ -17302,7 +17302,7 @@ template<class T >
-

Definition at line 1601 of file View.cpp.

+

Definition at line 1607 of file View.cpp.

@@ -17341,7 +17341,7 @@ template<class T >

Delete an offer.

Requirements: The passed sle be obtained from a prior call to view.peek()

-

Definition at line 1641 of file View.cpp.

+

Definition at line 1647 of file View.cpp.

@@ -17398,7 +17398,7 @@ template<class T >

Calls static rippleCreditIOU if saAmount represents Issue.

Calls static rippleCreditMPT if saAmount represents MPTIssue.

-

Definition at line 2850 of file View.cpp.

+

Definition at line 2856 of file View.cpp.

@@ -17440,7 +17440,7 @@ template<class T >
-

Definition at line 2989 of file View.cpp.

+

Definition at line 2995 of file View.cpp.

@@ -17494,7 +17494,7 @@ template<class T >
-

Definition at line 3086 of file View.cpp.

+

Definition at line 3092 of file View.cpp.

@@ -17551,7 +17551,7 @@ template<class T >

Calls static accountSendIOU if saAmount represents Issue.

Calls static accountSendMPT if saAmount represents MPTIssue.

-

Definition at line 2185 of file View.cpp.

+

Definition at line 2191 of file View.cpp.

@@ -17599,7 +17599,7 @@ template<class T >
-

Definition at line 2259 of file View.cpp.

+

Definition at line 2265 of file View.cpp.

@@ -17647,7 +17647,7 @@ template<class T >
-

Definition at line 2359 of file View.cpp.

+

Definition at line 2365 of file View.cpp.

@@ -17695,7 +17695,7 @@ template<class T >
-

Definition at line 2434 of file View.cpp.

+

Definition at line 2440 of file View.cpp.

@@ -17743,7 +17743,7 @@ template<class T >

If WeakAuth then return tecNO_AUTH if lsfRequireAuth is set, and the RippleState exists, and is not authorized. Return tecNO_LINE if lsfRequireAuth is set and the RippleState doesn't exist. Consequently, if WeakAuth and lsfRequireAuth is not set, this function will return tesSUCCESS even if RippleState does not exist.

The default "Legacy" auth type is equivalent to WeakAuth.

-

Definition at line 2479 of file View.cpp.

+

Definition at line 2485 of file View.cpp.

@@ -17798,7 +17798,7 @@ template<class T >

If WeakAuth then return tecNO_AUTH if lsfMPTRequireAuth is set and MPToken doesn't exist or is not authorized (explicitly or via credentials, if DomainID is set in MPTokenIssuance). Consequently, if WeakAuth and lsfMPTRequireAuth is not set, this function will return true even if MPToken does not exist.

The default "Legacy" auth type is equivalent to StrongAuth.

-

Definition at line 2511 of file View.cpp.

+

Definition at line 2517 of file View.cpp.

@@ -17895,7 +17895,7 @@ template<class T >

The caller does NOT need to look for existing MPToken to match mptIssue/account - this function checks lsfMPTAuthorized of an existing MPToken iff DomainID is not set.

Do not use for accounts which hold implied permission e.g. object owners or if MPTokenIssuance does not require authorization. In both cases use MPTokenAuthorize::authorize if MPToken does not yet exist.

-

Definition at line 2597 of file View.cpp.

+

Definition at line 2603 of file View.cpp.

@@ -17940,7 +17940,7 @@ template<class T >

Check if the destination account is allowed to receive MPT.

Return tecNO_AUTH if it doesn't and tesSUCCESS otherwise.

-

Definition at line 2698 of file View.cpp.

+

Definition at line 2704 of file View.cpp.

@@ -18034,7 +18034,7 @@ template<class T >

Delete trustline to AMM.

The passed sle must be obtained from a prior call to view.peek(). Fail if neither side of the trustline is AMM or if ammAccountID is seated and is not one of the trustline's side.

-

Definition at line 2800 of file View.cpp.

+

Definition at line 2806 of file View.cpp.

@@ -18070,7 +18070,7 @@ template<class T >
-

Definition at line 2878 of file View.cpp.

+

Definition at line 2884 of file View.cpp.

@@ -18106,7 +18106,7 @@ template<class T >
-

Definition at line 2906 of file View.cpp.

+

Definition at line 2912 of file View.cpp.

@@ -18148,7 +18148,7 @@ template<class T >
-

Definition at line 2935 of file View.cpp.

+

Definition at line 2941 of file View.cpp.

@@ -18184,7 +18184,7 @@ template<class T >
-

Definition at line 2964 of file View.cpp.

+

Definition at line 2970 of file View.cpp.

@@ -18224,7 +18224,7 @@ template<class T >
Returns
true if now refers to a time strictly after mark, else false.
-

Definition at line 3260 of file View.cpp.

+

Definition at line 3266 of file View.cpp.

@@ -26818,7 +26818,7 @@ template<class Handler >
-

Definition at line 1702 of file View.cpp.

+

Definition at line 1708 of file View.cpp.

@@ -26886,7 +26886,7 @@ template<class Handler >
-

Definition at line 1856 of file View.cpp.

+

Definition at line 1862 of file View.cpp.

@@ -26948,7 +26948,7 @@ template<class Handler >
-

Definition at line 1908 of file View.cpp.

+

Definition at line 1914 of file View.cpp.

@@ -27004,7 +27004,7 @@ template<class Handler >
-

Definition at line 2032 of file View.cpp.

+

Definition at line 2038 of file View.cpp.

@@ -27072,7 +27072,7 @@ template<class Handler >
-

Definition at line 2094 of file View.cpp.

+

Definition at line 2100 of file View.cpp.

@@ -27134,7 +27134,7 @@ template<class Handler >
-

Definition at line 2160 of file View.cpp.

+

Definition at line 2166 of file View.cpp.

@@ -27202,7 +27202,7 @@ template<class Handler >
-

Definition at line 2206 of file View.cpp.

+

Definition at line 2212 of file View.cpp.

@@ -27250,7 +27250,7 @@ template<class Handler >
-

Definition at line 2718 of file View.cpp.

+

Definition at line 2724 of file View.cpp.