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

View File

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

View File

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

View File

@@ -49,6 +49,16 @@ struct disabled_t
};
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 test

View File

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

View File

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

View File

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

View File

@@ -92,6 +92,15 @@ LoanBrokerSet::doPreflight(PreflightContext const& ctx)
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
LoanBrokerSet::preclaim(PreclaimContext const& ctx)
{

View File

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

View File

@@ -233,6 +233,18 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
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
Transactor::minimumFee(
Application& app,

View File

@@ -208,6 +208,10 @@ protected:
Fees const& fees,
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
static bool
isEnabled(PreflightContext const& ctx);

View File

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