LoanBrokerSet will charge a reserve increment when creating

- Refactor: Add a Transactor base function to make it easier to get the
  owner reserve increment as a fee.
- Refactor: Add an overload jtx::fee(increment) to pay an owner reserve.
This commit is contained in:
Ed Hennis
2025-04-09 14:57:38 -04:00
parent 781136ed2e
commit cc0fe0db07
13 changed files with 76 additions and 17 deletions

View File

@@ -72,7 +72,7 @@ class LoanBroker_test : public beast::unit_test::suite
using namespace loanBroker; using namespace loanBroker;
// Can't create a loan broker regardless of whether the vault exists // Can't create a loan broker regardless of whether the vault exists
env(set(alice, keylet.key), ter(temDISABLED)); env(set(alice, keylet.key), fee(increment), ter(temDISABLED));
}; };
failAll(all - featureMPTokensV1); failAll(all - featureMPTokensV1);
failAll(all - featureSingleAssetVault - featureLendingProtocol); failAll(all - featureSingleAssetVault - featureLendingProtocol);
@@ -153,62 +153,80 @@ class LoanBroker_test : public beast::unit_test::suite
{ {
auto badKeylet = keylet::vault(alice.id(), env.seq(alice)); auto badKeylet = keylet::vault(alice.id(), env.seq(alice));
// Try some failure cases // Try some failure cases
// insufficient fee
env(set(evan, vault.vaultID), ter(telINSUF_FEE_P));
// not the vault owner // not the vault owner
env(set(evan, vault.vaultID), ter(tecNO_PERMISSION)); env(set(evan, vault.vaultID),
fee(increment),
ter(tecNO_PERMISSION));
// not a vault // not a vault
env(set(alice, badKeylet.key), ter(tecNO_ENTRY)); env(set(alice, badKeylet.key),
fee(increment),
ter(tecNO_ENTRY));
// flags are checked first // flags are checked first
env(set(evan, vault.vaultID, ~tfUniversal), env(set(evan, vault.vaultID, ~tfUniversal),
fee(increment),
ter(temINVALID_FLAG)); ter(temINVALID_FLAG));
// field length validation // field length validation
// sfData: good length, bad account // sfData: good length, bad account
env(set(evan, vault.vaultID), env(set(evan, vault.vaultID),
fee(increment),
data(std::string(maxDataPayloadLength, 'X')), data(std::string(maxDataPayloadLength, 'X')),
ter(tecNO_PERMISSION)); ter(tecNO_PERMISSION));
// sfData: too long // sfData: too long
env(set(evan, vault.vaultID), env(set(evan, vault.vaultID),
fee(increment),
data(std::string(maxDataPayloadLength + 1, 'Y')), data(std::string(maxDataPayloadLength + 1, 'Y')),
ter(temINVALID)); ter(temINVALID));
// sfManagementFeeRate: good value, bad account // sfManagementFeeRate: good value, bad account
env(set(evan, vault.vaultID), env(set(evan, vault.vaultID),
managementFeeRate(maxFeeRate), managementFeeRate(maxFeeRate),
fee(increment),
ter(tecNO_PERMISSION)); ter(tecNO_PERMISSION));
// sfManagementFeeRate: too big // sfManagementFeeRate: too big
env(set(evan, vault.vaultID), env(set(evan, vault.vaultID),
managementFeeRate(maxFeeRate + 1), managementFeeRate(maxFeeRate + 1),
fee(increment),
ter(temINVALID)); ter(temINVALID));
// sfCoverRateMinimum: good value, bad account // sfCoverRateMinimum: good value, bad account
env(set(evan, vault.vaultID), env(set(evan, vault.vaultID),
coverRateMinimum(maxCoverRate), coverRateMinimum(maxCoverRate),
fee(increment),
ter(tecNO_PERMISSION)); ter(tecNO_PERMISSION));
// sfCoverRateMinimum: too big // sfCoverRateMinimum: too big
env(set(evan, vault.vaultID), env(set(evan, vault.vaultID),
coverRateMinimum(maxCoverRate + 1), coverRateMinimum(maxCoverRate + 1),
fee(increment),
ter(temINVALID)); ter(temINVALID));
// sfCoverRateLiquidation: good value, bad account // sfCoverRateLiquidation: good value, bad account
env(set(evan, vault.vaultID), env(set(evan, vault.vaultID),
coverRateLiquidation(maxCoverRate), coverRateLiquidation(maxCoverRate),
fee(increment),
ter(tecNO_PERMISSION)); ter(tecNO_PERMISSION));
// sfCoverRateLiquidation: too big // sfCoverRateLiquidation: too big
env(set(evan, vault.vaultID), env(set(evan, vault.vaultID),
coverRateLiquidation(maxCoverRate + 1), coverRateLiquidation(maxCoverRate + 1),
fee(increment),
ter(temINVALID)); ter(temINVALID));
// sfDebtMaximum: good value, bad account // sfDebtMaximum: good value, bad account
env(set(evan, vault.vaultID), env(set(evan, vault.vaultID),
debtMaximum(Number(0)), debtMaximum(Number(0)),
fee(increment),
ter(tecNO_PERMISSION)); ter(tecNO_PERMISSION));
// sfDebtMaximum: overflow // sfDebtMaximum: overflow
env(set(evan, vault.vaultID), env(set(evan, vault.vaultID),
debtMaximum(Number(1, 100)), debtMaximum(Number(1, 100)),
fee(increment),
ter(temINVALID)); ter(temINVALID));
// sfDebtMaximum: negative // sfDebtMaximum: negative
env(set(evan, vault.vaultID), env(set(evan, vault.vaultID),
debtMaximum(Number(-1)), debtMaximum(Number(-1)),
fee(increment),
ter(temINVALID)); ter(temINVALID));
auto keylet = keylet::loanbroker(alice.id(), env.seq(alice)); auto keylet = keylet::loanbroker(alice.id(), env.seq(alice));
// Successfully create a Loan Broker with all default values. // Successfully create a Loan Broker with all default values.
env(set(alice, vault.vaultID)); env(set(alice, vault.vaultID), fee(increment));
env.close(); env.close();
if (auto broker = env.le(keylet); BEAST_EXPECT(broker)) if (auto broker = env.le(keylet); BEAST_EXPECT(broker))
{ {
@@ -339,7 +357,8 @@ class LoanBroker_test : public beast::unit_test::suite
managementFeeRate(123), managementFeeRate(123),
debtMaximum(Number(9)), debtMaximum(Number(9)),
coverRateMinimum(100), coverRateMinimum(100),
coverRateLiquidation(200)); coverRateLiquidation(200),
fee(increment));
env.close(); env.close();
if (auto broker = env.le(keylet2); BEAST_EXPECT(broker)) if (auto broker = env.le(keylet2); BEAST_EXPECT(broker))
{ {

View File

@@ -37,6 +37,7 @@ class fee
{ {
private: private:
bool manual_ = true; bool manual_ = true;
bool increment_ = false;
std::optional<STAmount> amount_; std::optional<STAmount> amount_;
public: public:
@@ -44,6 +45,10 @@ public:
{ {
} }
explicit fee(increment_t) : increment_(true)
{
}
explicit fee(none_t) explicit fee(none_t)
{ {
} }

View File

@@ -26,13 +26,15 @@ namespace test {
namespace jtx { namespace jtx {
void void
fee::operator()(Env&, JTx& jt) const fee::operator()(Env& env, JTx& jt) const
{ {
if (!manual_) if (!manual_)
return; return;
jt.fill_fee = false; jt.fill_fee = false;
if (increment_)
jt[sfFee] = STAmount(env.current()->fees().increment).getJson();
if (amount_) if (amount_)
jt[jss::Fee] = amount_->getJson(JsonOptions::none); jt[sfFee] = amount_->getJson(JsonOptions::none);
} }
} // namespace jtx } // namespace jtx

View File

@@ -49,6 +49,16 @@ struct disabled_t
}; };
static disabled_t const disabled; static disabled_t const disabled;
/** Used for fee() calls that use an owner reserve increment */
struct increment_t
{
increment_t()
{
}
};
static increment_t const increment;
} // namespace jtx } // namespace jtx
} // namespace test } // namespace test

View File

@@ -1319,21 +1319,16 @@ class Invariants_test : public beast::unit_test::suite
auto [tx, vKeylet] = auto [tx, vKeylet] =
vault.create({.owner = a, .asset = xrpAsset}); vault.create({.owner = a, .asset = xrpAsset});
env(tx); env(tx);
env.close();
BEAST_EXPECT(env.le(vKeylet)); BEAST_EXPECT(env.le(vKeylet));
vaultID = vKeylet.key; vaultID = vKeylet.key;
env(vault.deposit(
{.depositor = a, .id = vaultID, .amount = xrpAsset(50)}));
env.close();
// Create Loan Broker // Create Loan Broker
using namespace loanBroker; using namespace loanBroker;
loanBrokerKeylet = keylet::loanbroker(a.id(), env.seq(a)); loanBrokerKeylet = keylet::loanbroker(a.id(), env.seq(a));
// Create a Loan Broker with all default values. // Create a Loan Broker with all default values.
env(set(a, vaultID)); env(set(a, vaultID), fee(increment));
return BEAST_EXPECT(env.le(loanBrokerKeylet)); return BEAST_EXPECT(env.le(loanBrokerKeylet));
}; };

View File

@@ -81,7 +81,7 @@ XRPAmount
AMMCreate::calculateBaseFee(ReadView const& view, STTx const& tx) AMMCreate::calculateBaseFee(ReadView const& view, STTx const& tx)
{ {
// The fee required for AMMCreate is one owner reserve. // The fee required for AMMCreate is one owner reserve.
return view.fees().increment; return calculateOwnerReserveFee(view, tx);
} }
TER TER

View File

@@ -70,7 +70,7 @@ XRPAmount
DeleteAccount::calculateBaseFee(ReadView const& view, STTx const& tx) DeleteAccount::calculateBaseFee(ReadView const& view, STTx const& tx)
{ {
// The fee required for AccountDelete is one owner reserve. // The fee required for AccountDelete is one owner reserve.
return view.fees().increment; return calculateOwnerReserveFee(view, tx);
} }
namespace { namespace {

View File

@@ -61,7 +61,7 @@ LedgerStateFix::calculateBaseFee(ReadView const& view, STTx const& tx)
{ {
// The fee required for LedgerStateFix is one owner reserve, just like // The fee required for LedgerStateFix is one owner reserve, just like
// the fee for AccountDelete. // the fee for AccountDelete.
return view.fees().increment; return calculateOwnerReserveFee(view, tx);
} }
TER TER

View File

@@ -92,6 +92,15 @@ LoanBrokerSet::doPreflight(PreflightContext const& ctx)
return tesSUCCESS; return tesSUCCESS;
} }
XRPAmount
LoanBrokerSet::calculateBaseFee(ReadView const& view, STTx const& tx)
{
// One reserve increment is typically much greater than one base fee.
if (!tx.isFieldPresent(sfLoanBrokerID))
return calculateOwnerReserveFee(view, tx);
return Transactor::calculateBaseFee(view, tx);
}
TER TER
LoanBrokerSet::preclaim(PreclaimContext const& ctx) LoanBrokerSet::preclaim(PreclaimContext const& ctx)
{ {

View File

@@ -46,6 +46,9 @@ public:
static NotTEC static NotTEC
doPreflight(PreflightContext const& ctx); doPreflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER static TER
preclaim(PreclaimContext const& ctx); preclaim(PreclaimContext const& ctx);

View File

@@ -233,6 +233,18 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
return baseFee + (signerCount * baseFee); return baseFee + (signerCount * baseFee);
} }
// Returns the fee in fee units, not scaled for load.
XRPAmount
Transactor::calculateOwnerReserveFee(ReadView const& view, STTx const& tx)
{
// One reserve increment is typically much greater than one base fee.
XRPL_ASSERT(
view.fees().increment > view.fees().base * 100,
"ripple::Transactor::calculateOwnerReserveFee : Owner reserve is much "
"greater than base fee");
return view.fees().increment;
}
XRPAmount XRPAmount
Transactor::minimumFee( Transactor::minimumFee(
Application& app, Application& app,

View File

@@ -208,6 +208,10 @@ protected:
Fees const& fees, Fees const& fees,
ApplyFlags flags); ApplyFlags flags);
// Returns the fee in fee units, not scaled for load.
static XRPAmount
calculateOwnerReserveFee(ReadView const& view, STTx const& tx);
// Base class always returns true // Base class always returns true
static bool static bool
isEnabled(PreflightContext const& ctx); isEnabled(PreflightContext const& ctx);

View File

@@ -91,7 +91,7 @@ XRPAmount
VaultCreate::calculateBaseFee(ReadView const& view, STTx const& tx) VaultCreate::calculateBaseFee(ReadView const& view, STTx const& tx)
{ {
// One reserve increment is typically much greater than one base fee. // One reserve increment is typically much greater than one base fee.
return view.fees().increment; return calculateOwnerReserveFee(view, tx);
} }
TER TER