From 199fdeb09d65dde0ad953e5fb829d49f058f51df Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Thu, 26 Feb 2026 14:13:29 +0100 Subject: [PATCH] adds sfMemoData field to VaultDelete transaction (#6356) * adds sfMemoData field to VaultDelete transaction --- .../xrpl/protocol/detail/transactions.macro | 1 + .../tx/transactors/vault/VaultDelete.cpp | 7 +++ src/test/app/Vault_test.cpp | 58 +++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index dbaaf46083..42ea2f930e 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -887,6 +887,7 @@ TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, MustDeleteAcct | DestroyMptIssuance | MustModifyVault, ({ {sfVaultID, SoeRequired}, + {sfMemoData, SoeOptional}, })) /** This transaction trades assets for shares with a vault. */ diff --git a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp index 0550f08b17..13187b2fa4 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -28,6 +28,13 @@ VaultDelete::preflight(PreflightContext const& ctx) return temMALFORMED; } + if (ctx.tx.isFieldPresent(sfMemoData) && !ctx.rules.enabled(fixLendingProtocolV1_1)) + return temDISABLED; + + // The sfMemoData field is an optional field used to record the deletion reason. + if (auto const data = ctx.tx[~sfMemoData]; data && !validDataLength(data, maxDataPayloadLength)) + return temMALFORMED; + return tesSUCCESS; } diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 4643bc8555..b20d55ac18 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -7824,6 +7824,63 @@ class Vault_test : public beast::unit_test::Suite } } + void + testVaultDeleteData() + { + using namespace test::jtx; + + Env env{*this}; + + Account const owner{"owner"}; + env.fund(XRP(1'000'000), owner); + env.close(); + + Vault vault{env}; + + auto const keylet = keylet::vault(owner.id(), 1); + auto delTx = vault.del({.owner = owner, .id = keylet.key}); + + // Test VaultDelete with fixLendingProtocolV1_1 disabled + // Transaction fails if the data field is provided + { + testcase("VaultDelete data fixLendingProtocolV1_1 disabled"); + env.disableFeature(fixLendingProtocolV1_1); + delTx[sfMemoData] = strHex(std::string(maxDataPayloadLength, 'A')); + env(delTx, ter(temDISABLED), THISLINE); + env.close(); + env.enableFeature(fixLendingProtocolV1_1); + } + + // Transaction fails if the data field is too large + { + testcase("VaultDelete data fixLendingProtocolV1_1 enabled data too large"); + delTx[sfMemoData] = strHex(std::string(maxDataPayloadLength + 1, 'A')); + env(delTx, ter(temMALFORMED), THISLINE); + env.close(); + } + + // Transaction fails if the data field is set, but is empty + { + testcase("VaultDelete data fixLendingProtocolV1_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"); + PrettyAsset const xrpAsset = xrpIssue(); + auto [tx, keylet] = vault.create({.owner = owner, .asset = xrpAsset}); + env(tx, ter(tesSUCCESS), THISLINE); + env.close(); + // Recreate the transaction as the vault keylet changed + auto delTx = vault.del({.owner = owner, .id = keylet.key}); + delTx[sfMemoData] = strHex(std::string(maxDataPayloadLength, 'A')); + env(delTx, ter(tesSUCCESS), THISLINE); + env.close(); + } + } + public: void run() override @@ -7866,6 +7923,7 @@ public: testReferenceHolding(); testHoldingDeletionBlocked(); + testVaultDeleteData(); } };