mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-22 23:00:55 +00:00
Remove sponsorship from vault and loan (#7628)
This commit is contained in:
@@ -184,6 +184,10 @@ LoanBrokerDelete::doApply()
|
||||
return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
view().erase(brokerPseudoSLE);
|
||||
|
||||
view().erase(broker);
|
||||
|
||||
{
|
||||
auto owner = view().peek(keylet::account(accountID_));
|
||||
if (!owner)
|
||||
@@ -192,14 +196,9 @@ LoanBrokerDelete::doApply()
|
||||
// Decreases the owner count by two: one for the LoanBroker object, and
|
||||
// one for the pseudo-account.
|
||||
// LoanBroker object can be sponsored
|
||||
adjustOwnerCountObj(view(), owner, broker, -1, j_);
|
||||
|
||||
// pseudo-account cannot be sponsored
|
||||
adjustOwnerCount(view(), owner, {}, -1, j_);
|
||||
adjustOwnerCount(view(), owner, {}, -2, j_);
|
||||
}
|
||||
|
||||
view().erase(brokerPseudoSLE);
|
||||
view().erase(broker);
|
||||
associateAsset(*broker, vaultAsset);
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -237,29 +236,11 @@ LoanBrokerSet::doApply()
|
||||
if (auto const ter = dirLink(view, vaultPseudoID, broker, sfVaultNode))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sponsorSle = getTxReserveSponsor(view, tx);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkInsufficientReserve(
|
||||
view, tx, owner, preFeeBalance_, {}, *sponsorSle ? 1 : 2, 0, j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (*sponsorSle)
|
||||
{
|
||||
if (auto const ret = checkInsufficientReserve(
|
||||
view, tx, owner, preFeeBalance_, *sponsorSle, 1, 0, j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Increases the owner count by two: one for the LoanBroker object, and
|
||||
// one for the pseudo-account.
|
||||
// Pseudo-account cannot be sponsored
|
||||
adjustOwnerCount(view, owner, {}, 1, j_);
|
||||
// LoanBroker object can be sponsored
|
||||
adjustOwnerCount(view, owner, *sponsorSle, 1, j_);
|
||||
adjustOwnerCount(view, owner, {}, 2, j_);
|
||||
if (preFeeBalance_ < accountReserve(view, owner, j_))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID);
|
||||
if (!maybePseudo)
|
||||
@@ -289,8 +270,6 @@ LoanBrokerSet::doApply()
|
||||
if (auto const coverLiq = tx[~sfCoverRateLiquidation])
|
||||
broker->at(sfCoverRateLiquidation) = *coverLiq;
|
||||
|
||||
addSponsorToLedgerEntry(broker, *sponsorSle);
|
||||
|
||||
view.insert(broker);
|
||||
|
||||
associateAsset(*broker, vaultAsset);
|
||||
|
||||
@@ -103,6 +103,9 @@ LoanDelete::doApply()
|
||||
if (!view.dirRemove(keylet::ownerDir(borrower), loanSle->at(sfOwnerNode), loanID, false))
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
// Delete the Loan object
|
||||
view.erase(loanSle);
|
||||
|
||||
// Decrement the LoanBroker's owner count.
|
||||
// The broker's owner count is solely for the number of outstanding loans,
|
||||
// and is distinct from the broker's pseudo-account's owner count
|
||||
@@ -127,10 +130,7 @@ LoanDelete::doApply()
|
||||
}
|
||||
}
|
||||
// Decrement the borrower's owner count
|
||||
adjustOwnerCountObj(view, borrowerSle, loanSle, -1, j_);
|
||||
|
||||
// Delete the Loan object
|
||||
view.erase(loanSle);
|
||||
adjustOwnerCount(view, borrowerSle, {}, -1, j_);
|
||||
|
||||
// These associations shouldn't do anything, but do them just to be safe
|
||||
associateAsset(*loanSle, vaultAsset);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
@@ -513,18 +512,14 @@ LoanSet::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
auto const sponsorSle = getTxReserveSponsor(view, tx);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
adjustOwnerCount(view, borrowerSle, {}, 1, j_);
|
||||
|
||||
{
|
||||
auto const balance =
|
||||
accountID_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp();
|
||||
if (auto const ret =
|
||||
checkInsufficientReserve(view, tx, borrowerSle, balance, *sponsorSle, 1, 0, j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
if (balance < accountReserve(view, borrowerSle, j_))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
adjustOwnerCount(view, borrowerSle, *sponsorSle, 1, j_);
|
||||
|
||||
// Account for the origination fee using two payments
|
||||
//
|
||||
@@ -624,7 +619,6 @@ LoanSet::doApply()
|
||||
loan->at(sfPreviousPaymentDueDate) = 0;
|
||||
loan->at(sfNextPaymentDueDate) = startDate + paymentInterval;
|
||||
loan->at(sfPaymentRemaining) = paymentTotal;
|
||||
addSponsorToLedgerEntry(loan, *sponsorSle);
|
||||
view.insert(loan);
|
||||
|
||||
// Update the balances in the vault
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
@@ -157,28 +156,9 @@ VaultCreate::doApply()
|
||||
if (auto ter = dirLink(view(), accountID_, vault))
|
||||
return ter;
|
||||
// We will create Vault and PseudoAccount, hence increase OwnerCount by 2
|
||||
auto const sponsorSle = getTxReserveSponsor(view(), tx);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
if (!ctx_.view().rules().enabled(featureSponsor))
|
||||
{
|
||||
adjustOwnerCount(view(), owner, *sponsorSle, 2, j_);
|
||||
addSponsorToLedgerEntry(vault, *sponsorSle);
|
||||
if (auto const ret =
|
||||
checkInsufficientReserve(view(), tx, owner, preFeeBalance_, *sponsorSle, 0, 0, j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
// after Sponsor Amendment, check insufficient reserve first
|
||||
if (auto const ret =
|
||||
checkInsufficientReserve(view(), tx, owner, preFeeBalance_, *sponsorSle, 2, 0, j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
adjustOwnerCount(view(), owner, *sponsorSle, 2, j_);
|
||||
addSponsorToLedgerEntry(vault, *sponsorSle);
|
||||
}
|
||||
adjustOwnerCount(view(), owner, {}, 2, j_);
|
||||
if (preFeeBalance_ < accountReserve(view(), owner, j_))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID);
|
||||
if (!maybePseudo)
|
||||
|
||||
@@ -204,7 +204,7 @@ VaultDelete::doApply()
|
||||
}
|
||||
|
||||
// We are destroying Vault and PseudoAccount, hence decrease by 2
|
||||
adjustOwnerCountObj(view(), owner, vault, -2, j_);
|
||||
adjustOwnerCount(view(), owner, {}, -2, j_);
|
||||
|
||||
// Destroy the vault.
|
||||
view().erase(vault);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -343,19 +342,9 @@ VaultDeposit::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
// Transfer shares from vault to depositor.
|
||||
if (auto const ter = accountSend(
|
||||
view(),
|
||||
vaultAccount,
|
||||
accountID_,
|
||||
sharesCreated,
|
||||
j_,
|
||||
*sponsorSle,
|
||||
WaiveTransferFee::Yes);
|
||||
view(), vaultAccount, accountID_, sharesCreated, j_, {}, WaiveTransferFee::Yes);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -326,19 +325,10 @@ VaultWithdraw::doApply()
|
||||
view().update(vault);
|
||||
|
||||
auto const& vaultAccount = vault->at(sfAccount);
|
||||
auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
// Transfer shares from depositor to vault.
|
||||
if (auto const ter = accountSend(
|
||||
view(),
|
||||
accountID_,
|
||||
vaultAccount,
|
||||
sharesRedeemed,
|
||||
j_,
|
||||
*sponsorSle,
|
||||
WaiveTransferFee::Yes);
|
||||
view(), accountID_, vaultAccount, sharesRedeemed, j_, {}, WaiveTransferFee::Yes);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
|
||||
@@ -3848,584 +3848,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testVault(bool cosigning)
|
||||
{
|
||||
testcase("Vault");
|
||||
using namespace test::jtx;
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const gw("gw");
|
||||
Account const sponsor("sponsor");
|
||||
Account const sponsor2("sponsor2");
|
||||
|
||||
Asset asset = gw["IOU"].asset();
|
||||
|
||||
// VaultCreate
|
||||
{
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(1000000), alice, bob, gw, sponsor);
|
||||
env.close();
|
||||
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = alice, .asset = asset});
|
||||
|
||||
env(ticket::create(sponsor, 2));
|
||||
env.close();
|
||||
|
||||
testEachSponsorship(
|
||||
env,
|
||||
cosigning,
|
||||
sponsor,
|
||||
alice,
|
||||
3, // Vault, PseudoAccount, MPToken(Share Token)
|
||||
3,
|
||||
tecINSUFFICIENT_RESERVE,
|
||||
[&](Env& env, auto const& submit) {
|
||||
auto result = vault.create({.owner = alice, .asset = asset});
|
||||
submit(std::get<0>(result));
|
||||
keylet = std::get<1>(result);
|
||||
});
|
||||
BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor.id());
|
||||
}
|
||||
// VaultDeposit
|
||||
{
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(1000000), alice, bob, gw, sponsor);
|
||||
env.close();
|
||||
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = alice, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
env(trust(bob, asset(1000)));
|
||||
env.close();
|
||||
env(pay(gw, bob, asset(1000)));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState
|
||||
|
||||
auto const depositTx =
|
||||
vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)});
|
||||
|
||||
env(ticket::create(sponsor, 2)); // for free MPToken
|
||||
env.close();
|
||||
|
||||
testEachSponsorship(
|
||||
env,
|
||||
cosigning,
|
||||
sponsor,
|
||||
bob,
|
||||
1,
|
||||
1,
|
||||
tecINSUFFICIENT_RESERVE,
|
||||
[&](Env& env, auto const& submit) { submit(depositTx); });
|
||||
}
|
||||
// VaultWithdraw
|
||||
{
|
||||
// RippleState Vault
|
||||
{
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(1000000), alice, bob, gw, sponsor);
|
||||
env.close();
|
||||
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = alice, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
env(trust(bob, asset(100)));
|
||||
env.close();
|
||||
env(pay(gw, bob, asset(100)));
|
||||
env.close();
|
||||
|
||||
auto const depositTx =
|
||||
vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)});
|
||||
|
||||
env(ticket::create(sponsor, 2)); // for free MPToken
|
||||
env.close();
|
||||
|
||||
testEachSponsorship(
|
||||
env,
|
||||
cosigning,
|
||||
sponsor,
|
||||
bob,
|
||||
1,
|
||||
1,
|
||||
tecINSUFFICIENT_RESERVE,
|
||||
[&](Env& env, auto const& submit) { submit(depositTx); });
|
||||
|
||||
env(trust(bob, asset(0))); // remove trustline
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, bob) == 1); // MPToken(share)
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); // MPToken(share)
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // MPToken(share)
|
||||
|
||||
// create Trustline with vault withdraw
|
||||
testEachSponsorship(
|
||||
env,
|
||||
cosigning,
|
||||
sponsor,
|
||||
bob,
|
||||
1,
|
||||
1,
|
||||
tecNO_LINE_INSUF_RESERVE,
|
||||
[&](Env& env, auto const& submit) {
|
||||
submit(vault.withdraw(
|
||||
{.depositor = bob, .id = keylet.key, .amount = asset(50)}));
|
||||
});
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, bob) == 2); // RippleState, MPToken(share)
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 2); // RippleState, MPToken(share)
|
||||
BEAST_EXPECT(
|
||||
sponsoringOwnerCount(env, sponsor) == 2); // RippleState, MPToken(share)
|
||||
|
||||
// remove sponsored MPToken(share)
|
||||
env(vault.withdraw({.depositor = bob, .id = keylet.key, .amount = asset(50)}));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); // RippleState
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // RippleState
|
||||
}
|
||||
// MPToken Vault
|
||||
{
|
||||
// VaultWithdraw doesn't create MPToken for depositor
|
||||
}
|
||||
}
|
||||
// VaultClawback
|
||||
{
|
||||
// remove sponsored shares MPToken
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(1000000), alice, bob, gw, sponsor);
|
||||
env.close();
|
||||
|
||||
env(fset(gw, asfAllowTrustLineClawback));
|
||||
env.close();
|
||||
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = alice, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
env(trust(bob, asset(100)));
|
||||
env.close();
|
||||
env(pay(gw, bob, asset(100)));
|
||||
env.close();
|
||||
|
||||
auto const depositTx =
|
||||
vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)});
|
||||
|
||||
env(ticket::create(sponsor, 2)); // for free MPToken
|
||||
env.close();
|
||||
|
||||
testEachSponsorship(
|
||||
env,
|
||||
cosigning,
|
||||
sponsor,
|
||||
bob,
|
||||
1,
|
||||
1,
|
||||
tecINSUFFICIENT_RESERVE,
|
||||
[&](Env& env, auto const& submit) { submit(depositTx); });
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, bob) == 2); // RippleState, MPToken(share)
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); // MPToken(share)
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // MPToken(share)
|
||||
|
||||
env(vault.clawback({.issuer = gw, .id = keylet.key, .holder = bob, .amount = asset(0)}),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
}
|
||||
// VaultDelete
|
||||
{
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(1000000), alice, bob, gw, sponsor);
|
||||
env.close();
|
||||
|
||||
env(fset(gw, asfAllowTrustLineClawback));
|
||||
env.close();
|
||||
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = alice, .asset = asset});
|
||||
env(tx, sponsor::As(sponsor, spfSponsorReserve), Sig(sfSponsorSignature, sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 3); // Vault, PseudoAccount, MPToken(share)
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 3);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 3);
|
||||
|
||||
env(vault.del({.owner = alice, .id = keylet.key}));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
}
|
||||
// SponsorshipTransfer
|
||||
{
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(1000000), alice, bob, gw, sponsor);
|
||||
env.close();
|
||||
|
||||
Vault const vault{env};
|
||||
auto const [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
// Alice owns the vault, pseudo account and MPToken
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 3);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
|
||||
if (cosigning)
|
||||
{
|
||||
// Alice lets sponsor to sponsor her Vault
|
||||
env(sponsor::transfer(alice, tfSponsorshipCreate, vaultKeylet.key),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor));
|
||||
env.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create sponsorship with reserve count being 2 (for vault and pseudo account)
|
||||
env(sponsor::set_reserve(sponsor, 0, 2), sponsor::SponseeAcc(alice));
|
||||
env.close();
|
||||
env(sponsor::transfer(alice, tfSponsorshipCreate, vaultKeylet.key),
|
||||
sponsor::As(sponsor, spfSponsorReserve));
|
||||
env.close();
|
||||
|
||||
auto const sponsorshipSle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
if (!BEAST_EXPECT(sponsorshipSle))
|
||||
return;
|
||||
BEAST_EXPECT(sponsorshipSle->getFieldU32(sfRemainingOwnerCount) == 0);
|
||||
}
|
||||
|
||||
BEAST_EXPECT(env.le(vaultKeylet)->getAccountID(sfSponsor) == sponsor.id());
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 3);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2);
|
||||
// Vault counts for 2 reserves, vault and the pseudo account
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2);
|
||||
|
||||
// End sponsorship
|
||||
env(sponsor::transfer(alice, tfSponsorshipEnd, vaultKeylet.key));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(!env.le(vaultKeylet)->isFieldPresent(sfSponsor));
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 3);
|
||||
// Sponsorship ended and the sponsored owner count should be 0.
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testLending(bool cosigning)
|
||||
{
|
||||
testcase("Lending");
|
||||
using namespace test::jtx;
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const issuer("issuer");
|
||||
Account const sponsor("sponsor");
|
||||
Account const sponsor2("sponsor2");
|
||||
|
||||
// LoanBrokerSet / LoanBrokerDelete
|
||||
{
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(1000000), alice, bob, sponsor, sponsor2);
|
||||
env.close();
|
||||
|
||||
PrettyAsset const asset{xrpIssue(), 1'000'000};
|
||||
|
||||
Vault const vault{env};
|
||||
auto const [tx, keylet] = vault.create({.owner = alice, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(
|
||||
ownerCount(env, alice) == 3); // Vault, PseudoAccount(Vault), MPToken(Vault)
|
||||
|
||||
// LoanBrokerSet
|
||||
testEachSponsorship(
|
||||
// Both the Pseudo-account and LoanBroker objects are created, but only the
|
||||
// LoanBroker is sponsored.
|
||||
env,
|
||||
cosigning,
|
||||
sponsor,
|
||||
alice,
|
||||
2,
|
||||
1,
|
||||
tecINSUFFICIENT_RESERVE,
|
||||
[&](Env& env, auto const& submit) {
|
||||
submit(loanBroker::set(alice, keylet.key, 0));
|
||||
});
|
||||
|
||||
BEAST_EXPECT(
|
||||
ownerCount(env, alice) ==
|
||||
5); // LoanBroker, PseudoAccount(LB), (Vault, PseudoAccount(Vault), MPToken(Vault))
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
|
||||
auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice) - 1);
|
||||
|
||||
if (cosigning)
|
||||
{
|
||||
// transfer sponsor
|
||||
env(sponsor::transfer(alice, tfSponsorshipReassign, brokerKeylet.key),
|
||||
sponsor::As(sponsor2, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor2));
|
||||
env.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice));
|
||||
env.close();
|
||||
|
||||
// transfer sponsor
|
||||
env(sponsor::transfer(alice, tfSponsorshipReassign, brokerKeylet.key),
|
||||
sponsor::As(sponsor2, spfSponsorReserve));
|
||||
env.close();
|
||||
}
|
||||
|
||||
BEAST_EXPECT(
|
||||
ownerCount(env, alice) ==
|
||||
5); // LoanBroker, PseudoAccount(LB), (Vault, PseudoAccount(Vault), MPToken(Vault))
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1);
|
||||
|
||||
// LoanBrokerDelete
|
||||
env(loanBroker::del(alice, brokerKeylet.key, 0));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 3);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0);
|
||||
}
|
||||
|
||||
// LoanBrokerConverDeposit/Withdraw/Clawback
|
||||
{
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(1000), alice, bob, issuer, sponsor);
|
||||
env.close();
|
||||
|
||||
MPTTester mptt{env, issuer, kMptInitNoFund};
|
||||
mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
|
||||
env.close();
|
||||
PrettyAsset const asset = mptt["MPT"];
|
||||
mptt.authorize({.account = alice});
|
||||
env.close();
|
||||
|
||||
env(pay(issuer, alice, asset(100)));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
|
||||
Vault const vault{env};
|
||||
auto const [tx, keylet] = vault.create({.owner = alice, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
env(loanBroker::set(alice, keylet.key, 0));
|
||||
env.close();
|
||||
BEAST_EXPECT(
|
||||
ownerCount(env, alice) ==
|
||||
6); // LoanBroker, PseudoAccount(LB), (Vault, PseudoAccount(Vault),
|
||||
// MPToken(Vault), MPToken(issuer))
|
||||
|
||||
auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice) - 1);
|
||||
// LoanBrokerCoverDeposit
|
||||
// doesn't sponsor anything
|
||||
env(loanBroker::coverDeposit(alice, brokerKeylet.key, asset(100)),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 6);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
|
||||
// remove MPToken(issuer)
|
||||
mptt.authorize({.account = alice, .flags = tfMPTUnauthorize});
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 5);
|
||||
|
||||
env(ticket::create(sponsor, 2)); // for avoid free MPToken
|
||||
env.close();
|
||||
|
||||
// LoanBrokerCoverWithdraw
|
||||
testEachSponsorship(
|
||||
env,
|
||||
cosigning,
|
||||
sponsor,
|
||||
alice,
|
||||
1,
|
||||
1,
|
||||
tecINSUFFICIENT_RESERVE,
|
||||
[&](Env& env, auto const& submit) {
|
||||
submit(loanBroker::coverWithdraw(alice, brokerKeylet.key, asset(10)));
|
||||
});
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 6);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
|
||||
// LoanBrokerCoverClawback
|
||||
// doesn't sponsor anything
|
||||
env(loanBroker::coverClawback(issuer),
|
||||
loanBroker::kLoanBrokerId(brokerKeylet.key),
|
||||
kAmount(asset(1)),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 6);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
}
|
||||
// LoanSet
|
||||
{
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(1000000), alice, bob, issuer, sponsor, sponsor2);
|
||||
env.close();
|
||||
|
||||
MPTTester mptt{env, issuer, kMptInitNoFund};
|
||||
mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
|
||||
env.close();
|
||||
PrettyAsset const asset = mptt["MPT"];
|
||||
mptt.authorize({.account = alice});
|
||||
mptt.authorize({.account = bob});
|
||||
env.close();
|
||||
|
||||
env(pay(issuer, alice, asset(1000)));
|
||||
env(pay(issuer, bob, asset(1000)));
|
||||
env.close();
|
||||
|
||||
Vault const vault{env};
|
||||
auto const [tx, keylet] = vault.create({.owner = bob, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)}));
|
||||
env.close();
|
||||
|
||||
auto const brokerKeylet = keylet::loanbroker(bob.id(), env.seq(bob));
|
||||
env(loanBroker::set(bob, keylet.key, 0));
|
||||
env.close();
|
||||
env(loanBroker::coverDeposit(bob, brokerKeylet.key, asset(100)));
|
||||
env.close();
|
||||
|
||||
auto broker = env.le(brokerKeylet);
|
||||
BEAST_EXPECT(broker->getFieldU32(sfOwnerCount) == 0);
|
||||
BEAST_EXPECT(!broker->isFieldPresent(sfSponsoredOwnerCount));
|
||||
BEAST_EXPECT(!broker->isFieldPresent(sfSponsoringOwnerCount));
|
||||
|
||||
auto const loanSeq = broker->getFieldU32(sfLoanSequence);
|
||||
testEachSponsorship(
|
||||
env,
|
||||
cosigning,
|
||||
sponsor,
|
||||
alice,
|
||||
1,
|
||||
1,
|
||||
tecINSUFFICIENT_RESERVE,
|
||||
[&](Env& env, auto const& submit) {
|
||||
submit(
|
||||
loan::set(alice, brokerKeylet.key, 10),
|
||||
Sig(sfCounterpartySignature, bob),
|
||||
Fee(XRP(1)));
|
||||
});
|
||||
broker = env.le(brokerKeylet);
|
||||
// broker'object doesn't sponsored
|
||||
BEAST_EXPECT(broker->getFieldU32(sfOwnerCount) == 1);
|
||||
BEAST_EXPECT(!broker->isFieldPresent(sfSponsoredOwnerCount));
|
||||
BEAST_EXPECT(!broker->isFieldPresent(sfSponsoringOwnerCount));
|
||||
|
||||
auto const loanKeylet = keylet::loan(brokerKeylet.key, loanSeq);
|
||||
|
||||
auto sponsorSle = env.le(keylet::account(sponsor));
|
||||
BEAST_EXPECT(sponsorSle->getFieldU32(sfOwnerCount) == 0);
|
||||
BEAST_EXPECT(!sponsorSle->isFieldPresent(sfSponsoredOwnerCount));
|
||||
BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringOwnerCount) == 1);
|
||||
|
||||
// LoanManage
|
||||
env(loan::manage(bob, loanKeylet.key, lsfLoanImpaired),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor));
|
||||
env.close();
|
||||
|
||||
// doesn't sponsor anything
|
||||
sponsorSle = env.le(keylet::account(sponsor));
|
||||
BEAST_EXPECT(sponsorSle->getFieldU32(sfOwnerCount) == 0);
|
||||
BEAST_EXPECT(!sponsorSle->isFieldPresent(sfSponsoredOwnerCount));
|
||||
BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringOwnerCount) == 1);
|
||||
|
||||
// LoanPay
|
||||
env(loan::pay(alice, loanKeylet.key, asset(10)),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor));
|
||||
env.close();
|
||||
|
||||
// doesn't sponsor anything
|
||||
sponsorSle = env.le(keylet::account(sponsor));
|
||||
BEAST_EXPECT(sponsorSle->getFieldU32(sfOwnerCount) == 0);
|
||||
BEAST_EXPECT(!sponsorSle->isFieldPresent(sfSponsoredOwnerCount));
|
||||
BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringOwnerCount) == 1);
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
|
||||
// before transfer
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
|
||||
if (cosigning)
|
||||
{
|
||||
// transfer sponsor
|
||||
env(sponsor::transfer(alice, tfSponsorshipReassign, loanKeylet.key),
|
||||
sponsor::As(sponsor2, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor2));
|
||||
env.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice));
|
||||
env.close();
|
||||
|
||||
// transfer sponsor
|
||||
env(sponsor::transfer(alice, tfSponsorshipReassign, loanKeylet.key),
|
||||
sponsor::As(sponsor2, spfSponsorReserve));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// after transfer
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1);
|
||||
|
||||
// LoanDelete
|
||||
env(loan::del(alice, loanKeylet.key),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor));
|
||||
env.close();
|
||||
|
||||
// Sponsored ltLoan is deleted
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
// Sponsor for ltLoan object is deleted
|
||||
sponsorSle = env.le(keylet::account(sponsor));
|
||||
BEAST_EXPECT(sponsorSle->getFieldU32(sfOwnerCount) == 0);
|
||||
BEAST_EXPECT(!sponsorSle->isFieldPresent(sfSponsoredOwnerCount));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testAccountDelete()
|
||||
{
|
||||
@@ -4963,8 +4385,6 @@ public:
|
||||
testPayChan(cosigning);
|
||||
testSignerList(cosigning);
|
||||
testTrustSet(cosigning);
|
||||
testVault(cosigning);
|
||||
testLending(cosigning);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
Reference in New Issue
Block a user