mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-27 00:50:45 +00:00
Remove LoanDraw transaction (#5792)
This commit is contained in:
@@ -558,7 +558,11 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({
|
||||
{sfPreviousPaymentDate, soeREQUIRED},
|
||||
{sfNextPaymentDueDate, soeREQUIRED},
|
||||
{sfPaymentRemaining, soeREQUIRED},
|
||||
//#if LOANDRAW
|
||||
// TODO: Remove this when you remove the rest of the LOANDRAW blocks.
|
||||
// Directives don't work with macro expansion.
|
||||
{sfAssetsAvailable, soeDEFAULT},
|
||||
//#endif
|
||||
{sfPrincipalOutstanding, soeREQUIRED},
|
||||
// Save the original request amount for rounding / scaling of
|
||||
// other computations, particularly for IOUs
|
||||
|
||||
@@ -1031,7 +1031,6 @@ TRANSACTION(ttLOAN_SET, 80, LoanSet,
|
||||
{sfCloseInterestRate, soeOPTIONAL},
|
||||
{sfOverpaymentInterestRate, soeOPTIONAL},
|
||||
{sfPrincipalRequested, soeREQUIRED},
|
||||
{sfStartDate, soeREQUIRED},
|
||||
{sfPaymentTotal, soeOPTIONAL},
|
||||
{sfPaymentInterval, soeOPTIONAL},
|
||||
{sfGracePeriod, soeOPTIONAL},
|
||||
@@ -1059,6 +1058,7 @@ TRANSACTION(ttLOAN_MANAGE, 82, LoanManage,
|
||||
{sfLoanID, soeREQUIRED},
|
||||
}))
|
||||
|
||||
#if LOANDRAW
|
||||
/** The Borrower uses this transaction to draws funds from the Loan. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanDraw.h>
|
||||
@@ -1070,6 +1070,7 @@ TRANSACTION(ttLOAN_DRAW, 83, LoanDraw,
|
||||
{sfLoanID, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
}))
|
||||
#endif
|
||||
|
||||
/** The Borrower uses this transaction to make a Payment on the Loan. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
|
||||
@@ -2631,10 +2631,7 @@ class Batch_test : public beast::unit_test::suite
|
||||
batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing),
|
||||
batch::inner(
|
||||
env.json(
|
||||
set(lender,
|
||||
brokerKeylet.key,
|
||||
asset(1000).value(),
|
||||
env.now() + 3600s),
|
||||
set(lender, brokerKeylet.key, asset(1000).value()),
|
||||
// Not allowed to include the counterparty signature
|
||||
sig(sfCounterpartySignature, borrower),
|
||||
sig(none),
|
||||
@@ -2642,8 +2639,7 @@ class Batch_test : public beast::unit_test::suite
|
||||
seq(none)),
|
||||
lenderSeq + 1),
|
||||
batch::inner(
|
||||
draw(
|
||||
lender,
|
||||
pay(lender,
|
||||
loanKeylet.key,
|
||||
STAmount{asset, asset(500).value()}),
|
||||
lenderSeq + 2));
|
||||
@@ -2655,18 +2651,14 @@ class Batch_test : public beast::unit_test::suite
|
||||
batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing),
|
||||
batch::inner(
|
||||
env.json(
|
||||
set(lender,
|
||||
brokerKeylet.key,
|
||||
asset(1000).value(),
|
||||
env.now() + 3600s),
|
||||
set(lender, brokerKeylet.key, asset(1000).value()),
|
||||
// Counterparty must be set
|
||||
sig(none),
|
||||
fee(none),
|
||||
seq(none)),
|
||||
lenderSeq + 1),
|
||||
batch::inner(
|
||||
draw(
|
||||
lender,
|
||||
pay(lender,
|
||||
loanKeylet.key,
|
||||
STAmount{asset, asset(500).value()}),
|
||||
lenderSeq + 2));
|
||||
@@ -2678,10 +2670,7 @@ class Batch_test : public beast::unit_test::suite
|
||||
batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing),
|
||||
batch::inner(
|
||||
env.json(
|
||||
set(lender,
|
||||
brokerKeylet.key,
|
||||
asset(1000).value(),
|
||||
env.now() + 3600s),
|
||||
set(lender, brokerKeylet.key, asset(1000).value()),
|
||||
// Counterparty must sign the outer transaction
|
||||
counterparty(borrower.id()),
|
||||
sig(none),
|
||||
@@ -2689,8 +2678,7 @@ class Batch_test : public beast::unit_test::suite
|
||||
seq(none)),
|
||||
lenderSeq + 1),
|
||||
batch::inner(
|
||||
draw(
|
||||
lender,
|
||||
pay(lender,
|
||||
loanKeylet.key,
|
||||
STAmount{asset, asset(500).value()}),
|
||||
lenderSeq + 2));
|
||||
@@ -2706,17 +2694,14 @@ class Batch_test : public beast::unit_test::suite
|
||||
batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing),
|
||||
batch::inner(
|
||||
env.json(
|
||||
set(lender,
|
||||
brokerKeylet.key,
|
||||
asset(1000).value(),
|
||||
env.now() + 3600s),
|
||||
set(lender, brokerKeylet.key, asset(1000).value()),
|
||||
counterparty(borrower.id()),
|
||||
sig(none),
|
||||
fee(none),
|
||||
seq(none)),
|
||||
lenderSeq + 1),
|
||||
batch::inner(
|
||||
draw(
|
||||
pay(
|
||||
// However, this inner transaction will fail,
|
||||
// because the lender is not allowed to draw the
|
||||
// transaction
|
||||
@@ -2741,10 +2726,7 @@ class Batch_test : public beast::unit_test::suite
|
||||
batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing),
|
||||
batch::inner(
|
||||
env.json(
|
||||
set(lender,
|
||||
brokerKeylet.key,
|
||||
asset(1000).value(),
|
||||
env.now() + 3600s),
|
||||
set(lender, brokerKeylet.key, asset(1000).value()),
|
||||
counterparty(borrower.id()),
|
||||
sig(none),
|
||||
fee(none),
|
||||
|
||||
@@ -3503,7 +3503,7 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
|
||||
BEAST_EXPECT(mptEscrowed(env, alice, MPT) == 125);
|
||||
BEAST_EXPECT(issuerMPTEscrowed(env, MPT) == 125);
|
||||
BEAST_EXPECT(env.balance(gw, MPT) == MPT(20'000));
|
||||
BEAST_EXPECT(env.balance(gw, MPT) == MPT(-20'000));
|
||||
|
||||
// bob can finish escrow
|
||||
env(escrow::finish(bob, alice, seq1),
|
||||
@@ -3522,7 +3522,7 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
: MPT(20'000);
|
||||
BEAST_EXPECT(mptEscrowed(env, alice, MPT) == escrowedWithFix);
|
||||
BEAST_EXPECT(issuerMPTEscrowed(env, MPT) == escrowedWithFix);
|
||||
BEAST_EXPECT(env.balance(gw, MPT) == outstandingWithFix);
|
||||
BEAST_EXPECT(env.balance(gw, MPT) == -outstandingWithFix);
|
||||
}
|
||||
|
||||
// test locked rate: cancel
|
||||
@@ -3567,7 +3567,7 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
|
||||
BEAST_EXPECT(env.balance(alice, MPT) == preAlice);
|
||||
BEAST_EXPECT(env.balance(bob, MPT) == preBob);
|
||||
BEAST_EXPECT(env.balance(gw, MPT) == MPT(20'000));
|
||||
BEAST_EXPECT(env.balance(gw, MPT) == MPT(-20'000));
|
||||
BEAST_EXPECT(mptEscrowed(env, alice, MPT) == 0);
|
||||
BEAST_EXPECT(issuerMPTEscrowed(env, MPT) == 0);
|
||||
}
|
||||
@@ -3608,7 +3608,7 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
|
||||
BEAST_EXPECT(mptEscrowed(env, alice, MPT) == 125);
|
||||
BEAST_EXPECT(issuerMPTEscrowed(env, MPT) == 125);
|
||||
BEAST_EXPECT(env.balance(gw, MPT) == MPT(20'000));
|
||||
BEAST_EXPECT(env.balance(gw, MPT) == MPT(-20'000));
|
||||
|
||||
// bob can finish escrow
|
||||
env(escrow::finish(gw, alice, seq1),
|
||||
@@ -3620,7 +3620,7 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(env.balance(alice, MPT) == preAlice - delta);
|
||||
BEAST_EXPECT(mptEscrowed(env, alice, MPT) == 0);
|
||||
BEAST_EXPECT(issuerMPTEscrowed(env, MPT) == 0);
|
||||
BEAST_EXPECT(env.balance(gw, MPT) == MPT(19'875));
|
||||
BEAST_EXPECT(env.balance(gw, MPT) == MPT(-19'875));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -793,7 +793,6 @@ Json::Value
|
||||
set(AccountID const& account,
|
||||
uint256 const& loanBrokerID,
|
||||
Number principalRequested,
|
||||
NetClock::time_point const& startDate,
|
||||
std::uint32_t flags = 0);
|
||||
|
||||
auto const counterparty = JTxFieldWrapper<accountIDField>(sfCounterparty);
|
||||
@@ -835,12 +834,14 @@ manage(AccountID const& account, uint256 const& loanID, std::uint32_t flags);
|
||||
Json::Value
|
||||
del(AccountID const& account, uint256 const& loanID, std::uint32_t flags = 0);
|
||||
|
||||
#if loandraw
|
||||
Json::Value
|
||||
draw(
|
||||
AccountID const& account,
|
||||
uint256 const& loanID,
|
||||
STAmount const& amount,
|
||||
std::uint32_t flags = 0);
|
||||
#endif
|
||||
|
||||
Json::Value
|
||||
pay(AccountID const& account,
|
||||
|
||||
@@ -450,7 +450,6 @@ Json::Value
|
||||
set(AccountID const& account,
|
||||
uint256 const& loanBrokerID,
|
||||
Number principalRequested,
|
||||
NetClock::time_point const& startDate,
|
||||
std::uint32_t flags)
|
||||
{
|
||||
Json::Value jv;
|
||||
@@ -459,7 +458,6 @@ set(AccountID const& account,
|
||||
jv[sfLoanBrokerID] = to_string(loanBrokerID);
|
||||
jv[sfPrincipalRequested] = to_string(principalRequested);
|
||||
jv[sfFlags] = flags;
|
||||
jv[sfStartDate] = startDate.time_since_epoch().count();
|
||||
return jv;
|
||||
}
|
||||
|
||||
@@ -485,6 +483,7 @@ del(AccountID const& account, uint256 const& loanID, std::uint32_t flags)
|
||||
return jv;
|
||||
}
|
||||
|
||||
#if LOANDRAW
|
||||
Json::Value
|
||||
draw(
|
||||
AccountID const& account,
|
||||
@@ -500,6 +499,7 @@ draw(
|
||||
jv[sfFlags] = flags;
|
||||
return jv;
|
||||
}
|
||||
#endif
|
||||
|
||||
Json::Value
|
||||
pay(AccountID const& account,
|
||||
|
||||
@@ -121,9 +121,10 @@ LoanDelete::doApply()
|
||||
auto const vaultSle = view.peek(keylet ::vault(brokerSle->at(sfVaultID)));
|
||||
if (!vaultSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultAsset = vaultSle->at(sfAsset);
|
||||
|
||||
#if LOANDRAW
|
||||
// transfer any remaining funds to the borrower
|
||||
auto const vaultAsset = vaultSle->at(sfAsset);
|
||||
auto assetsAvailableProxy = loanSle->at(sfAssetsAvailable);
|
||||
if (assetsAvailableProxy != 0)
|
||||
{
|
||||
@@ -136,6 +137,7 @@ LoanDelete::doApply()
|
||||
WaiveTransferFee::Yes))
|
||||
return ter;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Remove LoanID from Directory of the LoanBroker pseudo-account.
|
||||
if (!view.dirRemove(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if LOANDRAW
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
@@ -121,7 +122,7 @@ LoanDraw::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
if (amount.asset() != asset)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Loan amount does not match the Vault asset.";
|
||||
JLOG(ctx.j.warn()) << "Draw amount does not match the Vault asset.";
|
||||
return tecWRONG_ASSET;
|
||||
}
|
||||
|
||||
@@ -189,3 +190,4 @@ LoanDraw::doApply()
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace ripple
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if LOANDRAW
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
@@ -51,3 +52,4 @@ public:
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -168,10 +168,15 @@ defaultLoan(
|
||||
auto brokerDebtTotalProxy = brokerSle->at(sfDebtTotal);
|
||||
auto const totalDefaultAmount = principalOutstanding + interestOutstanding;
|
||||
|
||||
#if LOANDRAW
|
||||
// The default Amount equals the outstanding principal and interest,
|
||||
// excluding any funds unclaimed by the Borrower.
|
||||
auto loanAssetsAvailableProxy = loanSle->at(sfAssetsAvailable);
|
||||
auto const defaultAmount = totalDefaultAmount - loanAssetsAvailableProxy;
|
||||
#else
|
||||
// TODO: get rid of this and just use totalDefaultAmount
|
||||
auto const defaultAmount = totalDefaultAmount;
|
||||
#endif
|
||||
// Apply the First-Loss Capital to the Default Amount
|
||||
TenthBips32 const coverRateMinimum{brokerSle->at(sfCoverRateMinimum)};
|
||||
TenthBips32 const coverRateLiquidation{
|
||||
@@ -185,7 +190,11 @@ defaultLoan(
|
||||
coverRateLiquidation),
|
||||
defaultAmount),
|
||||
originalPrincipalRequested);
|
||||
#if LOANDRAW
|
||||
auto const returnToVault = defaultCovered + loanAssetsAvailableProxy;
|
||||
#else
|
||||
auto const returnToVault = defaultCovered;
|
||||
#endif
|
||||
auto const vaultDefaultAmount = defaultAmount - defaultCovered;
|
||||
|
||||
// Update the Vault object:
|
||||
@@ -250,7 +259,9 @@ defaultLoan(
|
||||
// Update the Loan object:
|
||||
loanSle->setFlag(lsfLoanDefault);
|
||||
loanSle->at(sfPaymentRemaining) = 0;
|
||||
#if LOANDRAW
|
||||
loanAssetsAvailableProxy = 0;
|
||||
#endif
|
||||
loanSle->at(sfPrincipalOutstanding) = 0;
|
||||
view.update(loanSle);
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ LoanPay::preclaim(PreclaimContext const& ctx)
|
||||
TenthBips32 const interestRate{loanSle->at(sfInterestRate)};
|
||||
auto const paymentRemaining = loanSle->at(sfPaymentRemaining);
|
||||
TenthBips32 const lateInterestRate{loanSle->at(sfLateInterestRate)};
|
||||
auto const startDate = loanSle->at(sfStartDate);
|
||||
|
||||
if (loanSle->at(sfBorrower) != account)
|
||||
{
|
||||
@@ -90,12 +89,6 @@ LoanPay::preclaim(PreclaimContext const& ctx)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
if (!hasExpired(ctx.view, startDate))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Loan has not started yet.";
|
||||
return tecTOO_SOON;
|
||||
}
|
||||
|
||||
if (paymentRemaining == 0 || principalOutstanding == 0)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Loan is already paid off.";
|
||||
|
||||
@@ -187,12 +187,6 @@ LoanSet::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const& tx = ctx.tx;
|
||||
|
||||
if (auto const startDate(tx[sfStartDate]); hasExpired(ctx.view, startDate))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Start date is in the past.";
|
||||
return tecEXPIRED;
|
||||
}
|
||||
|
||||
auto const account = tx[sfAccount];
|
||||
auto const brokerID = tx[sfLoanBrokerID];
|
||||
|
||||
@@ -223,7 +217,6 @@ LoanSet::preclaim(PreclaimContext const& ctx)
|
||||
return terNO_ACCOUNT;
|
||||
}
|
||||
|
||||
auto const brokerPseudo = brokerSle->at(sfAccount);
|
||||
auto const vault = ctx.view.read(keylet::vault(brokerSle->at(sfVaultID)));
|
||||
if (!vault)
|
||||
// Should be impossible
|
||||
@@ -249,16 +242,6 @@ LoanSet::preclaim(PreclaimContext const& ctx)
|
||||
JLOG(ctx.j.warn()) << "Borrower account is frozen.";
|
||||
return ret;
|
||||
}
|
||||
// TODO: Remove when LoanDraw is combined with LoanSet
|
||||
// brokerPseudo is eventually going to send funds to the borrower, so it
|
||||
// can't be frozen now. It is also going to receive funds, so it can't be
|
||||
// deep frozen, but being frozen is a prerequisite for being deep frozen, so
|
||||
// checking the one is sufficient.
|
||||
if (auto const ret = checkFrozen(ctx.view, brokerPseudo, asset))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Broker pseudo-account account is frozen.";
|
||||
return ret;
|
||||
}
|
||||
// brokerOwner is going to receive funds if there's an origination fee, so
|
||||
// it can't be deep frozen
|
||||
if (auto const ret = checkDeepFrozen(ctx.view, brokerOwner, asset))
|
||||
@@ -360,16 +343,26 @@ LoanSet::doApply()
|
||||
if (mPriorBalance < view.fees().accountReserve(ownerCount))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
// Create a holding for the borrower if one does not already exist.
|
||||
|
||||
// Account for the origination fee using two payments
|
||||
//
|
||||
// 1. Transfer loanAssetsAvailable (principalRequested - originationFee)
|
||||
// from vault pseudo-account to LoanBroker pseudo-account.
|
||||
// from vault pseudo-account to the borrower.
|
||||
// Create a holding for the borrower if one does not already exist.
|
||||
if (auto const ter = addEmptyHolding(
|
||||
view,
|
||||
borrower,
|
||||
borrowerSle->at(sfBalance).value().xrp(),
|
||||
vaultAsset,
|
||||
j_);
|
||||
ter && ter != tecDUPLICATE)
|
||||
// ignore tecDUPLICATE. That means the holding already exists, and
|
||||
// is fine here
|
||||
return ter;
|
||||
|
||||
if (auto const ter = accountSend(
|
||||
view,
|
||||
vaultPseudo,
|
||||
brokerPseudo,
|
||||
borrower,
|
||||
STAmount{vaultAsset, loanAssetsAvailable},
|
||||
j_,
|
||||
WaiveTransferFee::Yes))
|
||||
@@ -414,7 +407,7 @@ LoanSet::doApply()
|
||||
paymentInterval,
|
||||
paymentTotal,
|
||||
managementFeeRate);
|
||||
auto const startDate = tx[sfStartDate];
|
||||
auto const startDate = view.info().closeTime.time_since_epoch().count();
|
||||
auto loanSequence = brokerSle->at(sfLoanSequence);
|
||||
|
||||
// Create the loan
|
||||
@@ -453,7 +446,6 @@ LoanSet::doApply()
|
||||
loan->at(sfPreviousPaymentDate) = 0;
|
||||
loan->at(sfNextPaymentDueDate) = startDate + paymentInterval;
|
||||
loan->at(sfPaymentRemaining) = paymentTotal;
|
||||
loan->at(sfAssetsAvailable) = loanAssetsAvailable;
|
||||
view.insert(loan);
|
||||
|
||||
// Update the balances in the vault
|
||||
|
||||
Reference in New Issue
Block a user