Merge remote-tracking branch 'origin/tapanito/lending-fix-amendment' into tapanito/vault-donation

# Conflicts:
#	src/libxrpl/tx/transactors/vault/VaultDeposit.cpp
This commit is contained in:
Vito
2026-03-21 15:59:41 +01:00
780 changed files with 69714 additions and 5920 deletions

View File

@@ -3,7 +3,6 @@
#include <test/jtx/Env.h>
#include <test/jtx/amount.h>
#include <test/jtx/mpt.h>
#include <test/jtx/testline.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/unit_test/suite.h>
@@ -67,15 +66,23 @@ class Vault_test : public beast::unit_test::suite
auto const vault = env.le(keylet);
BEAST_EXPECT(vault != nullptr);
if (!asset.integral())
{
BEAST_EXPECT(vault->at(sfScale) == 6);
}
else
{
BEAST_EXPECT(vault->at(sfScale) == 0);
}
auto const shares = env.le(keylet::mptIssuance(vault->at(sfShareMPTID)));
BEAST_EXPECT(shares != nullptr);
if (!asset.integral())
{
BEAST_EXPECT(shares->at(sfAssetScale) == 6);
}
else
{
BEAST_EXPECT(shares->at(sfAssetScale) == 0);
}
return {MPTIssue(vault->at(sfShareMPTID)), Account("vault", vault->at(sfAccount))};
}();
auto const shares = share.raw().get<MPTIssue>();
@@ -1332,11 +1339,17 @@ class Vault_test : public beast::unit_test::suite
BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2);
if (!asset1.native() && !asset2.native())
{
fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All);
}
else if (asset1.native())
{
fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All);
}
else if (asset2.native())
{
fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All);
}
AMM ammAlice(env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0});
@@ -2278,7 +2291,9 @@ class Vault_test : public beast::unit_test::suite
env(fset(issuer, asfDefaultRipple));
}
else
{
env.trust(asset(1000), charlie);
}
env.close();
env(rate(issuer, args.transferRate));
env.close();
@@ -2624,13 +2639,13 @@ class Vault_test : public beast::unit_test::suite
// Turn on noripple on the pseudo account's trust line.
// Charlie's is already set.
env(trust(issuer, vaultAccount(keylet)["IOU"], tfSetNoRipple), THISLINE);
env(trust(issuer, vaultAccount(keylet)["IOU"], tfSetNoRipple));
{
// Charlie cannot deposit
auto tx = vault.deposit(
{.depositor = charlie, .id = keylet.key, .amount = asset(100)});
env(tx, ter{terNO_RIPPLE}, THISLINE);
env(tx, ter{terNO_RIPPLE});
env.close();
}
@@ -2638,14 +2653,14 @@ class Vault_test : public beast::unit_test::suite
PrettyAsset shares = issuanceId(keylet);
auto tx1 =
vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)});
env(tx1, THISLINE);
env(tx1);
env.close();
// Charlie cannot receive funds
auto tx2 = vault.withdraw(
{.depositor = owner, .id = keylet.key, .amount = shares(100)});
tx2[sfDestination] = charlie.human();
env(tx2, ter{terNO_RIPPLE}, THISLINE);
env(tx2, ter{terNO_RIPPLE});
env.close();
{
@@ -2658,7 +2673,7 @@ class Vault_test : public beast::unit_test::suite
env(tx);
env.close();
}
env(pay(owner, charlie, shares(100)), THISLINE);
env(pay(owner, charlie, shares(100)));
env.close();
// Charlie cannot withdraw
@@ -2667,16 +2682,16 @@ class Vault_test : public beast::unit_test::suite
env(tx3, ter{terNO_RIPPLE});
env.close();
env(pay(charlie, owner, shares(100)), THISLINE);
env(pay(charlie, owner, shares(100)));
env.close();
}
tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)});
env(tx, THISLINE);
env(tx);
env.close();
// Delete vault with zero balance
env(vault.del({.owner = owner, .id = keylet.key}), THISLINE);
env(vault.del({.owner = owner, .id = keylet.key}));
},
{.charlieRipple = false});
@@ -3350,7 +3365,7 @@ class Vault_test : public beast::unit_test::suite
MPTIssue shares(issuanceId);
env.memoize(vaultAccount);
auto const peek = [=, &env, this](std::function<bool(SLE&, SLE&)> fn) -> bool {
auto const peek = [keylet, &env, this](std::function<bool(SLE&, SLE&)> fn) -> bool {
return env.app().openLedger().modify([&](OpenView& view, beast::Journal j) -> bool {
Sandbox sb(&view, tapNONE);
auto vault = sb.peek(keylet::vault(keylet.key));
@@ -4501,131 +4516,6 @@ class Vault_test : public beast::unit_test::suite
}
}
void
testDelegate()
{
using namespace test::jtx;
Env env(*this, testable_amendments());
Account alice{"alice"};
Account bob{"bob"};
Account carol{"carol"};
struct CaseArgs
{
PrettyAsset asset = xrpIssue();
};
auto const xrpBalance = [this](
Env const& env, Account const& account) -> std::optional<long> {
auto sle = env.le(keylet::account(account.id()));
if (BEAST_EXPECT(sle != nullptr))
return sle->getFieldAmount(sfBalance).xrp().drops();
return std::nullopt;
};
auto testCase = [&, this](auto test, CaseArgs args = {}) {
Env env{*this, testable_amendments() | featureSingleAssetVault};
Vault vault{env};
// use different initial amount to distinguish the source balance
env.fund(XRP(10000), alice);
env.fund(XRP(20000), bob);
env.fund(XRP(30000), carol);
env.close();
env(delegate::set(
carol,
alice,
{"Payment",
"VaultCreate",
"VaultSet",
"VaultDelete",
"VaultDeposit",
"VaultWithdraw",
"VaultClawback"}));
test(env, vault, args.asset);
};
testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
testcase("delegated vault creation");
auto startBalance = xrpBalance(env, carol);
if (!BEAST_EXPECT(startBalance.has_value()))
return;
auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
env(tx, delegate::as(alice));
env.close();
BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
});
testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
testcase("delegated deposit and withdrawal");
auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
env(tx);
env.close();
auto const amount = 1513;
auto const baseFee = env.current()->fees().base;
auto startBalance = xrpBalance(env, carol);
if (!BEAST_EXPECT(startBalance.has_value()))
return;
tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)});
env(tx, delegate::as(alice));
env.close();
BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
tx =
vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(amount - 1)});
env(tx, delegate::as(alice));
env.close();
BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(1)});
env(tx);
env.close();
BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
});
testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
testcase("delegated withdrawal same as base fee and deletion");
auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
env(tx);
env.close();
auto const amount = 25537;
auto const baseFee = env.current()->fees().base;
auto startBalance = xrpBalance(env, carol);
if (!BEAST_EXPECT(startBalance.has_value()))
return;
tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)});
env(tx);
env.close();
BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount - baseFee);
tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(baseFee)});
env(tx, delegate::as(alice));
env.close();
BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
tx = vault.withdraw(
{.depositor = carol, .id = keylet.key, .amount = asset(amount - baseFee)});
env(tx, delegate::as(alice));
env.close();
BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
tx = vault.del({.owner = carol, .id = keylet.key});
env(tx, delegate::as(alice));
env.close();
});
}
void
testVaultClawbackBurnShares()
{
@@ -4657,7 +4547,7 @@ class Vault_test : public beast::unit_test::suite
Vault vault{env};
auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
env(tx, ter(tesSUCCESS), THISLINE);
env(tx, ter(tesSUCCESS));
env.close();
auto const& vaultSle = env.le(vaultKeylet);
@@ -4667,8 +4557,7 @@ class Vault_test : public beast::unit_test::suite
env(vault.deposit(
{.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
ter(tesSUCCESS),
THISLINE);
ter(tesSUCCESS));
env.close();
auto const& [availablePreDefault, totalPreDefault] = vaultAssetBalance(vaultKeylet);
@@ -4681,14 +4570,13 @@ class Vault_test : public beast::unit_test::suite
.id = vaultKeylet.key,
.holder = depositor,
.amount = share(0).value()}),
ter(tecNO_PERMISSION),
THISLINE);
ter(tecNO_PERMISSION));
env.close();
auto const& sharesAvailable = vaultShareBalance(vaultKeylet);
auto const& brokerKeylet = keylet::loanbroker(owner.id(), env.seq(owner));
env(set(owner, vaultKeylet.key), THISLINE);
env(set(owner, vaultKeylet.key));
env.close();
auto const& loanKeylet = keylet::loan(brokerKeylet.key, 1);
@@ -4701,8 +4589,7 @@ class Vault_test : public beast::unit_test::suite
paymentTotal(10),
sig(sfCounterpartySignature, owner),
fee(env.current()->fees().base * 2),
ter(tesSUCCESS),
THISLINE);
ter(tesSUCCESS));
env.close();
// attempt to clawback shares while there assetsAvailable == 0 and
@@ -4712,13 +4599,12 @@ class Vault_test : public beast::unit_test::suite
.id = vaultKeylet.key,
.holder = depositor,
.amount = share(0).value()}),
ter(tecNO_PERMISSION),
THISLINE);
ter(tecNO_PERMISSION));
env.close();
env.close(std::chrono::seconds{120 + 60});
env(manage(owner, loanKeylet.key, tfLoanDefault), ter(tesSUCCESS), THISLINE);
env(manage(owner, loanKeylet.key, tfLoanDefault), ter(tesSUCCESS));
auto const& [availablePostDefault, totalPostDefault] = vaultAssetBalance(vaultKeylet);
@@ -4736,19 +4622,23 @@ class Vault_test : public beast::unit_test::suite
{
testcase("VaultClawback (share) - " + prefix + " owner asset clawback fails");
auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
// when asset is XRP or owner is not issuer clawback fail
// when owner is issuer precision loss occurs as vault is
// empty
auto const expectedTer = [&]() {
if (asset.native())
return ter(temMALFORMED);
if (asset.raw().getIssuer() != owner.id())
return ter(tecNO_PERMISSION);
return ter(tecPRECISION_LOSS);
}();
env(vault.clawback({
.issuer = owner,
.id = vaultKeylet.key,
.holder = depositor,
.amount = asset(100).value(),
}),
// when asset is XRP or owner is not issuer clawback fail
// when owner is issuer precision loss occurs as vault is
// empty
asset.native() ? ter(temMALFORMED)
: asset.raw().getIssuer() != owner.id() ? ter(tecNO_PERMISSION)
: ter(tecPRECISION_LOSS),
THISLINE);
expectedTer);
env.close();
}
@@ -4767,8 +4657,7 @@ class Vault_test : public beast::unit_test::suite
.holder = depositor,
.amount = share(1).value(),
}),
ter(tecLIMIT_EXCEEDED),
THISLINE);
ter(tecLIMIT_EXCEEDED));
env.close();
}
@@ -4784,8 +4673,7 @@ class Vault_test : public beast::unit_test::suite
}),
// when owner is issuer implicit clawback fails
asset.native() || asset.raw().getIssuer() != owner.id() ? ter(tesSUCCESS)
: ter(tecWRONG_ASSET),
THISLINE);
: ter(tecWRONG_ASSET));
env.close();
}
@@ -4805,8 +4693,7 @@ class Vault_test : public beast::unit_test::suite
.holder = depositor,
.amount = share(vaultShareBalance(vaultKeylet)).value(),
}),
ter(tesSUCCESS),
THISLINE);
ter(tesSUCCESS));
env.close();
}
{
@@ -4823,8 +4710,7 @@ class Vault_test : public beast::unit_test::suite
.holder = owner,
.amount = share(vaultShareBalance(vaultKeylet)).value(),
}),
ter(tesSUCCESS),
THISLINE);
ter(tesSUCCESS));
env.close();
}
@@ -4841,8 +4727,7 @@ class Vault_test : public beast::unit_test::suite
.holder = owner,
.amount = share(vaultShareBalance(vaultKeylet)).value(),
}),
ter(tesSUCCESS),
THISLINE);
ter(tesSUCCESS));
// Now the vault is empty, clawback again fails
env(vault.clawback({
@@ -4850,8 +4735,7 @@ class Vault_test : public beast::unit_test::suite
.id = vaultKeylet.key,
.holder = owner,
}),
ter(tecNO_PERMISSION),
THISLINE);
ter(tecNO_PERMISSION));
env.close();
}
};
@@ -4909,15 +4793,14 @@ class Vault_test : public beast::unit_test::suite
Vault vault{env};
auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
env(tx, ter(tesSUCCESS), THISLINE);
env(tx, ter(tesSUCCESS));
env.close();
auto const& vaultSle = env.le(vaultKeylet);
BEAST_EXPECT(vaultSle != nullptr);
env(vault.deposit(
{.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
ter(tesSUCCESS),
THISLINE);
ter(tesSUCCESS));
env.close();
return std::make_pair(vault, vaultKeylet);
@@ -4940,16 +4823,14 @@ class Vault_test : public beast::unit_test::suite
.holder = issuer,
.amount = asset(1).value(),
}),
ter(temMALFORMED),
THISLINE);
ter(temMALFORMED));
// When asset is implicit, clawback fails as no permission.
env(vault.clawback({
.issuer = issuer,
.id = vaultKeylet.key,
.holder = issuer,
}),
ter(tecNO_PERMISSION),
THISLINE);
ter(tecNO_PERMISSION));
return;
}
@@ -4966,8 +4847,7 @@ class Vault_test : public beast::unit_test::suite
.holder = depositor,
.amount = asset2(1).value(),
}),
ter(tecWRONG_ASSET),
THISLINE);
ter(tecWRONG_ASSET));
}
{
@@ -4980,8 +4860,7 @@ class Vault_test : public beast::unit_test::suite
.id = vaultKeylet.key,
.holder = issuer,
}),
ter(tecWRONG_ASSET),
THISLINE);
ter(tecWRONG_ASSET));
}
{
@@ -4993,8 +4872,7 @@ class Vault_test : public beast::unit_test::suite
.id = vaultKeylet.key,
.holder = depositor,
}),
ter(tecNO_PERMISSION),
THISLINE);
ter(tecNO_PERMISSION));
env(vault.clawback({
.issuer = owner,
@@ -5002,8 +4880,7 @@ class Vault_test : public beast::unit_test::suite
.holder = depositor,
.amount = asset(1).value(),
}),
ter(tecNO_PERMISSION),
THISLINE);
ter(tecNO_PERMISSION));
}
{
@@ -5014,8 +4891,7 @@ class Vault_test : public beast::unit_test::suite
.id = vaultKeylet.key,
.holder = issuer,
}),
ter(tecNO_PERMISSION),
THISLINE);
ter(tecNO_PERMISSION));
}
{
@@ -5033,8 +4909,7 @@ class Vault_test : public beast::unit_test::suite
.holder = depositor,
.amount = share(1).value(),
}),
ter(tecNO_PERMISSION),
THISLINE);
ter(tecNO_PERMISSION));
}
{
@@ -5049,8 +4924,7 @@ class Vault_test : public beast::unit_test::suite
.holder = depositor,
.amount = asset(1).value(),
}),
ter(tesSUCCESS),
THISLINE);
ter(tesSUCCESS));
}
{
@@ -5064,8 +4938,7 @@ class Vault_test : public beast::unit_test::suite
.holder = depositor,
.amount = asset(100).value(),
}),
ter(tesSUCCESS),
THISLINE);
ter(tesSUCCESS));
}
{
@@ -5079,8 +4952,7 @@ class Vault_test : public beast::unit_test::suite
.id = vaultKeylet.key,
.holder = depositor,
}),
ter(tesSUCCESS),
THISLINE);
ter(tesSUCCESS));
}
};
@@ -5155,19 +5027,19 @@ class Vault_test : public beast::unit_test::suite
tx[sfData] = "4D65746144617461";
tx[sfAssetsMaximum] = maxInt64;
env(tx, ter(tefEXCEPTION), THISLINE);
env(tx, ter(tefEXCEPTION));
env.close();
tx[sfAssetsMaximum] = initialXRPPlus1;
env(tx, ter(tefEXCEPTION), THISLINE);
env(tx, ter(tefEXCEPTION));
env.close();
tx[sfAssetsMaximum] = initialXRP;
env(tx, THISLINE);
env(tx);
env.close();
tx[sfAssetsMaximum] = maxInt64Plus1;
env(tx, ter(tefEXCEPTION), THISLINE);
env(tx, ter(tefEXCEPTION));
env.close();
// This value will be rounded
@@ -5177,7 +5049,7 @@ class Vault_test : public beast::unit_test::suite
BEAST_EXPECT(decimalTest == "9223372036854775.808");
tx[sfAssetsMaximum] = decimalTest;
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env(tx);
env.close();
auto const vaultSle = env.le(newKeylet);
@@ -5200,26 +5072,26 @@ class Vault_test : public beast::unit_test::suite
return mptAsset;
}();
env(pay(issuer, owner, mptAsset(100'000)), THISLINE);
env(pay(issuer, owner, mptAsset(100'000)));
env.close();
auto [tx, keylet] = vault.create({.owner = owner, .asset = mptAsset});
tx[sfData] = "4D65746144617461";
tx[sfAssetsMaximum] = maxInt64;
env(tx, THISLINE);
env(tx);
env.close();
tx[sfAssetsMaximum] = initialXRPPlus1;
env(tx, THISLINE);
env(tx);
env.close();
tx[sfAssetsMaximum] = initialXRP;
env(tx, THISLINE);
env(tx);
env.close();
tx[sfAssetsMaximum] = maxInt64Plus1;
env(tx, ter(tefEXCEPTION), THISLINE);
env(tx, ter(tefEXCEPTION));
env.close();
// This value will be rounded
@@ -5229,7 +5101,7 @@ class Vault_test : public beast::unit_test::suite
BEAST_EXPECT(decimalTest == "922337203685477580.8");
tx[sfAssetsMaximum] = decimalTest;
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env(tx);
env.close();
auto const vaultSle = env.le(newKeylet);
@@ -5252,19 +5124,19 @@ class Vault_test : public beast::unit_test::suite
tx[sfData] = "4D65746144617461";
tx[sfAssetsMaximum] = maxInt64;
env(tx, THISLINE);
env(tx);
env.close();
tx[sfAssetsMaximum] = initialXRPPlus1;
env(tx, THISLINE);
env(tx);
env.close();
tx[sfAssetsMaximum] = initialXRP;
env(tx, THISLINE);
env(tx);
env.close();
tx[sfAssetsMaximum] = maxInt64Plus1;
env(tx, THISLINE);
env(tx);
env.close();
tx[sfAssetsMaximum] = "1000000000000000e80";
@@ -5281,7 +5153,7 @@ class Vault_test : public beast::unit_test::suite
BEAST_EXPECT(decimalTest == "922337203685477580.8");
tx[sfAssetsMaximum] = decimalTest;
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env(tx);
env.close();
auto const vaultSle = env.le(newKeylet);
@@ -5295,7 +5167,7 @@ class Vault_test : public beast::unit_test::suite
{
tx[sfAssetsMaximum] = "9223372036854775807e40"; // max int64 * 10^40
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env(tx);
env.close();
auto const vaultSle = env.le(newKeylet);
@@ -5309,7 +5181,7 @@ class Vault_test : public beast::unit_test::suite
{
tx[sfAssetsMaximum] = "9223372036854775807e-40"; // max int64 * 10^-40
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env(tx);
env.close();
auto const vaultSle = env.le(newKeylet);
@@ -5323,7 +5195,7 @@ class Vault_test : public beast::unit_test::suite
{
tx[sfAssetsMaximum] = "9223372036854775807e-100"; // max int64 * 10^-100
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env(tx);
env.close();
// Field 'AssetsMaximum' may not be explicitly set to default.
@@ -5337,7 +5209,7 @@ class Vault_test : public beast::unit_test::suite
// What _can't_ IOUs do?
// 1. Exceed maximum exponent / offset
tx[sfAssetsMaximum] = "1000000000000000e81";
env(tx, ter(tefEXCEPTION), THISLINE);
env(tx, ter(tefEXCEPTION));
env.close();
// 2. Mantissa larger than uint64 max
@@ -5345,7 +5217,7 @@ class Vault_test : public beast::unit_test::suite
try
{
tx[sfAssetsMaximum] = "18446744073709551617e5"; // uint64 max + 1
env(tx, THISLINE);
env(tx);
BEAST_EXPECTS(false, "Expected parse_error for mantissa larger than uint64 max");
}
catch (parse_error const& e)
@@ -5374,20 +5246,20 @@ class Vault_test : public beast::unit_test::suite
auto const keylet = keylet::vault(owner.id(), 1);
auto delTx = vault.del({.owner = owner, .id = keylet.key});
// Test VaultDelete with fixLendingProtocolV1_1 disabled
// Test VaultDelete with featureLendingProtocolV1_1 disabled
// Transaction fails if the data field is provided
{
testcase("VaultDelete data fixLendingProtocolV1_1 disabled");
env.disableFeature(fixLendingProtocolV1_1);
testcase("VaultDelete data featureLendingProtocolV1_1 disabled");
env.disableFeature(featureLendingProtocolV1_1);
delTx[sfMemoData] = strHex(std::string(maxDataPayloadLength, 'A'));
env(delTx, ter(temDISABLED), THISLINE);
env.close();
env.enableFeature(fixLendingProtocolV1_1);
env.enableFeature(featureLendingProtocolV1_1);
}
// Transaction fails if the data field is too large
{
testcase("VaultDelete data fixLendingProtocolV1_1 enabled data too large");
testcase("VaultDelete data featureLendingProtocolV1_1 enabled data too large");
delTx[sfMemoData] = strHex(std::string(maxDataPayloadLength + 1, 'A'));
env(delTx, ter(temMALFORMED), THISLINE);
env.close();
@@ -5395,14 +5267,14 @@ class Vault_test : public beast::unit_test::suite
// Transaction fails if the data field is set, but is empty
{
testcase("VaultDelete data fixLendingProtocolV1_1 enabled data empty");
testcase("VaultDelete data featureLendingProtocolV1_1 enabled data empty");
delTx[sfMemoData] = strHex(std::string(0, 'A'));
env(delTx, ter(temMALFORMED), THISLINE);
env.close();
}
{
testcase("VaultDelete data fixLendingProtocolV1_1 enabled data valid");
testcase("VaultDelete data featureLendingProtocolV1_1 enabled data valid");
PrettyAsset const xrpAsset = xrpIssue();
auto [tx, keylet] = vault.create({.owner = owner, .asset = xrpAsset});
env(tx, ter(tesSUCCESS), THISLINE);
@@ -5582,7 +5454,6 @@ public:
testFailedPseudoAccount();
testScaleIOU();
testRPC();
testDelegate();
testVaultClawbackBurnShares();
testVaultClawbackAssets();
testAssetsMaximum();