mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-04 09:16:47 +00:00
test Sponsor Reserve checks for AMM
This commit is contained in:
@@ -812,6 +812,7 @@ accountSend(
|
||||
AccountID const& to,
|
||||
STAmount const& saAmount,
|
||||
beast::Journal j,
|
||||
std::optional<AccountID> const& sponsorAcc = std::nullopt,
|
||||
WaiveTransferFee waiveFee = WaiveTransferFee::No);
|
||||
|
||||
[[nodiscard]] TER
|
||||
|
||||
@@ -1801,6 +1801,9 @@ trustDelete(
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
|
||||
removeSponsorFromLedgerEntry(sleRippleState, sfHighSponsorAccount);
|
||||
removeSponsorFromLedgerEntry(sleRippleState, sfLowSponsorAccount);
|
||||
|
||||
JLOG(j.trace()) << "trustDelete: Deleting ripple line: state";
|
||||
view.erase(sleRippleState);
|
||||
|
||||
@@ -1876,6 +1879,7 @@ rippleCreditIOU(
|
||||
AccountID const& uReceiverID,
|
||||
STAmount const& saAmount,
|
||||
bool bCheckIssuer,
|
||||
std::optional<AccountID> const& sponsorAccount,
|
||||
beast::Journal j)
|
||||
{
|
||||
AccountID const& issuer = saAmount.getIssuer();
|
||||
@@ -2029,7 +2033,7 @@ rippleCreditIOU(
|
||||
saReceiverLimit,
|
||||
0,
|
||||
0,
|
||||
std::nullopt,
|
||||
sponsorAccount,
|
||||
j);
|
||||
}
|
||||
|
||||
@@ -2044,6 +2048,7 @@ rippleSendIOU(
|
||||
STAmount const& saAmount,
|
||||
STAmount& saActual,
|
||||
beast::Journal j,
|
||||
std::optional<AccountID> const& sponsorAccount,
|
||||
WaiveTransferFee waiveFee)
|
||||
{
|
||||
auto const issuer = saAmount.getIssuer();
|
||||
@@ -2058,8 +2063,8 @@ rippleSendIOU(
|
||||
if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount())
|
||||
{
|
||||
// Direct send: redeeming IOUs and/or sending own IOUs.
|
||||
auto const ter =
|
||||
rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, j);
|
||||
auto const ter = rippleCreditIOU(
|
||||
view, uSenderID, uReceiverID, saAmount, false, sponsorAccount, j);
|
||||
if (view.rules().enabled(featureDeletableAccounts) && ter != tesSUCCESS)
|
||||
return ter;
|
||||
saActual = saAmount;
|
||||
@@ -2079,11 +2084,12 @@ rippleSendIOU(
|
||||
<< " : deliver=" << saAmount.getFullText()
|
||||
<< " cost=" << saActual.getFullText();
|
||||
|
||||
TER terResult =
|
||||
rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, j);
|
||||
TER terResult = rippleCreditIOU(
|
||||
view, issuer, uReceiverID, saAmount, true, sponsorAccount, j);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, j);
|
||||
terResult = rippleCreditIOU(
|
||||
view, uSenderID, issuer, saActual, true, sponsorAccount, j);
|
||||
|
||||
return terResult;
|
||||
}
|
||||
@@ -2095,6 +2101,7 @@ accountSendIOU(
|
||||
AccountID const& uReceiverID,
|
||||
STAmount const& saAmount,
|
||||
beast::Journal j,
|
||||
std::optional<AccountID> const& sponsorAccount,
|
||||
WaiveTransferFee waiveFee)
|
||||
{
|
||||
if (view.rules().enabled(fixAMMv1_1))
|
||||
@@ -2126,7 +2133,14 @@ accountSendIOU(
|
||||
<< saAmount.getFullText();
|
||||
|
||||
return rippleSendIOU(
|
||||
view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
|
||||
view,
|
||||
uSenderID,
|
||||
uReceiverID,
|
||||
saAmount,
|
||||
saActual,
|
||||
j,
|
||||
sponsorAccount,
|
||||
waiveFee);
|
||||
}
|
||||
|
||||
/* XRP send which does not check reserve and can do pure adjustment.
|
||||
@@ -2368,13 +2382,20 @@ accountSend(
|
||||
AccountID const& uReceiverID,
|
||||
STAmount const& saAmount,
|
||||
beast::Journal j,
|
||||
std::optional<AccountID> const& sponsorAcc,
|
||||
WaiveTransferFee waiveFee)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
return accountSendIOU(
|
||||
view, uSenderID, uReceiverID, saAmount, j, waiveFee);
|
||||
view,
|
||||
uSenderID,
|
||||
uReceiverID,
|
||||
saAmount,
|
||||
j,
|
||||
sponsorAcc,
|
||||
waiveFee);
|
||||
else
|
||||
return accountSendMPT(
|
||||
view, uSenderID, uReceiverID, saAmount, j, waiveFee);
|
||||
@@ -2423,7 +2444,7 @@ updateTrustLine(
|
||||
// Clear the reserve of the sender, possibly delete the line!
|
||||
auto const currentSponsor = getLedgerEntryReserveSponsor(
|
||||
view,
|
||||
sle,
|
||||
state,
|
||||
!bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount);
|
||||
adjustOwnerCount(view, sle, currentSponsor, -1, j);
|
||||
|
||||
@@ -3021,7 +3042,11 @@ deleteAMMTrustLine(
|
||||
if (!(sleState->getFlags() & uFlags))
|
||||
return tecINTERNAL;
|
||||
|
||||
adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, std::nullopt, -1, j);
|
||||
auto const sponsorSle = getLedgerEntryReserveSponsor(
|
||||
view, sleState, !ammLow ? sfLowSponsorAccount : sfHighSponsorAccount);
|
||||
adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, -1, j);
|
||||
removeSponsorFromLedgerEntry(
|
||||
sleState, !ammLow ? sfLowSponsorAccount : sfHighSponsorAccount);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -3040,7 +3065,13 @@ rippleCredit(
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return rippleCreditIOU(
|
||||
view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j);
|
||||
view,
|
||||
uSenderID,
|
||||
uReceiverID,
|
||||
saAmount,
|
||||
bCheckIssuer,
|
||||
std::nullopt,
|
||||
j);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -899,6 +899,338 @@ public:
|
||||
env.close();
|
||||
}
|
||||
|
||||
void
|
||||
testAMM()
|
||||
{
|
||||
testcase("AMM");
|
||||
using namespace test::jtx;
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const gw("gw");
|
||||
Account const sponsor("sponsor");
|
||||
|
||||
auto const USD = gw["USD"];
|
||||
auto const EUR = gw["EUR"];
|
||||
|
||||
auto const ammCreate = [&](Env& env,
|
||||
Account const& account,
|
||||
STAmount const& amount1,
|
||||
STAmount const& amount2) {
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::AMMCreate;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[jss::Amount] = amount1.getJson(JsonOptions::none);
|
||||
jv[jss::Amount2] = amount2.getJson(JsonOptions::none);
|
||||
jv[jss::TradingFee] = 0;
|
||||
jv[jss::Fee] =
|
||||
std::to_string(env.current()->fees().increment.drops());
|
||||
return jv;
|
||||
};
|
||||
|
||||
auto const ammDeposit = [&](Env& env,
|
||||
Account const& account,
|
||||
STAmount const& amount1,
|
||||
STAmount const& amount2) {
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::AMMDeposit;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[jss::Asset] =
|
||||
STIssue(sfAsset, amount1.issue()).getJson(JsonOptions::none);
|
||||
jv[jss::Asset2] =
|
||||
STIssue(sfAsset, amount2.issue()).getJson(JsonOptions::none);
|
||||
jv[jss::Amount] = amount1.value().getJson(JsonOptions::none);
|
||||
jv[jss::Amount2] = amount2.value().getJson(JsonOptions::none);
|
||||
jv[jss::Flags] = tfTwoAsset;
|
||||
return jv;
|
||||
};
|
||||
|
||||
{
|
||||
// AMMCreate
|
||||
// - sponsor LPToken
|
||||
// - doesn't sponsor AMM object
|
||||
Env env{*this, testable_amendments()};
|
||||
env.fund(XRP(10000), alice, gw, sponsor);
|
||||
env.close();
|
||||
|
||||
env(trust(alice, USD(10000)));
|
||||
env(trust(alice, EUR(10000)));
|
||||
env.close();
|
||||
|
||||
env(pay(gw, alice, USD(1000)));
|
||||
env(pay(gw, alice, EUR(1000)));
|
||||
env.close();
|
||||
|
||||
{
|
||||
// AMMCreate doesn't check INSUFFICIENT_RESERVE now
|
||||
// see: https://github.com/XRPLF/rippled/issues/5812
|
||||
// check INSUFFICIENT_RESERVE
|
||||
adjustAccountXRPBalance(
|
||||
env, sponsor, reserve(env, 1) - drops(1));
|
||||
|
||||
env(ammCreate(env, alice, USD(100), EUR(100)),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor),
|
||||
ter(tecINSUF_RESERVE_LINE));
|
||||
env.close();
|
||||
adjustAccountXRPBalance(env, sponsor, reserve(env, 2));
|
||||
}
|
||||
|
||||
env(ammCreate(env, alice, USD(100), EUR(100)),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
auto const amm =
|
||||
env.current()->read(keylet::amm(USD.issue(), EUR.issue()));
|
||||
auto const ammAccount =
|
||||
Account("amm", amm->getAccountID(sfAccount));
|
||||
|
||||
BEAST_EXPECT(
|
||||
ownerCount(env, alice) == 3); // RippleState (USD,EUR/LP Token)
|
||||
BEAST_EXPECT(ownerCount(env, ammAccount) == 2); // USD, EUR
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); // LPToken
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, ammAccount) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // LPToken
|
||||
BEAST_EXPECT(!env.le(keylet::amm(USD.issue(), EUR.issue()))
|
||||
->isFieldPresent(sfSponsorAccount));
|
||||
}
|
||||
{
|
||||
// AMMDeposit
|
||||
// - sponsor new LPToken
|
||||
Env env{*this, testable_amendments()};
|
||||
env.fund(XRP(10000), alice, bob, gw, sponsor);
|
||||
env.close();
|
||||
|
||||
env(trust(alice, USD(10000)));
|
||||
env(trust(alice, EUR(10000)));
|
||||
env(trust(bob, USD(10000)));
|
||||
env(trust(bob, EUR(10000)));
|
||||
env.close();
|
||||
|
||||
env(pay(gw, alice, USD(1000)));
|
||||
env(pay(gw, alice, EUR(1000)));
|
||||
env(pay(gw, bob, USD(1000)));
|
||||
env(pay(gw, bob, EUR(1000)));
|
||||
env.close();
|
||||
|
||||
env(ammCreate(env, alice, USD(100), EUR(100)));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, bob) == 2); // RippleState (USD,EUR)
|
||||
|
||||
{
|
||||
// check INSUFFICIENT_RESERVE
|
||||
adjustAccountXRPBalance(
|
||||
env, sponsor, reserve(env, 1) - drops(1));
|
||||
|
||||
env(ammDeposit(env, bob, USD(100), EUR(100)),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor),
|
||||
ter(tecINSUF_RESERVE_LINE));
|
||||
env.close();
|
||||
adjustAccountXRPBalance(env, sponsor, reserve(env, 2));
|
||||
}
|
||||
|
||||
env(ammDeposit(env, bob, USD(100), EUR(100)),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(
|
||||
ownerCount(env, bob) == 3); // RippleState (USD,EUR/LP Token)
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); // LPToken
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // LPToken
|
||||
}
|
||||
{
|
||||
// AMMWithdraw
|
||||
{
|
||||
// Single Asset Withdraw
|
||||
// - sponsor new RippleState
|
||||
Env env{*this, testable_amendments()};
|
||||
env.fund(XRP(10000), alice, bob, gw, sponsor);
|
||||
env.close();
|
||||
|
||||
env(trust(alice, USD(10000)));
|
||||
env(trust(alice, EUR(10000)));
|
||||
env.close();
|
||||
|
||||
env(pay(gw, alice, USD(1000)));
|
||||
env(pay(gw, alice, EUR(1000)));
|
||||
env.close();
|
||||
|
||||
env(ammCreate(env, alice, USD(1000), EUR(1000)),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
env(trust(alice, USD(0)));
|
||||
env(trust(alice, EUR(0)));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1); // LPToken
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); // LPToken
|
||||
BEAST_EXPECT(
|
||||
sponsoringOwnerCount(env, sponsor) == 1); // LPToken
|
||||
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::AMMWithdraw;
|
||||
jv[jss::Account] = alice.human();
|
||||
jv[jss::Asset] =
|
||||
STIssue(sfAsset, USD.issue()).getJson(JsonOptions::none);
|
||||
jv[jss::Asset2] =
|
||||
STIssue(sfAsset, EUR.issue()).getJson(JsonOptions::none);
|
||||
jv[jss::Amount] = USD(100).value().getJson(JsonOptions::none);
|
||||
jv[jss::Flags] = tfSingleAsset;
|
||||
|
||||
{
|
||||
env(ticket::create(
|
||||
sponsor, 1)); // adjust for free trustline
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, sponsor) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
// check INSUFFICIENT_RESERVE
|
||||
adjustAccountXRPBalance(
|
||||
env, sponsor, reserve(env, 2) - drops(1));
|
||||
env(jv,
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor),
|
||||
ter(tecINSUFFICIENT_RESERVE));
|
||||
env.close();
|
||||
adjustAccountXRPBalance(env, sponsor, reserve(env, 3));
|
||||
}
|
||||
|
||||
env(jv,
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2); // USD, LPToken
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2);
|
||||
}
|
||||
{
|
||||
// Double Asset Withdraw
|
||||
// - sponsor new RippleState * 2
|
||||
// - remove sponsored LPToken
|
||||
Env env{*this, testable_amendments()};
|
||||
env.fund(XRP(10000), alice, bob, gw, sponsor);
|
||||
env.close();
|
||||
|
||||
env(trust(alice, USD(10000)));
|
||||
env(trust(alice, EUR(10000)));
|
||||
env.close();
|
||||
|
||||
env(pay(gw, alice, USD(1000)));
|
||||
env(pay(gw, alice, EUR(1000)));
|
||||
env.close();
|
||||
|
||||
env(ammCreate(env, alice, USD(1000), EUR(1000)),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
env(trust(alice, USD(0)));
|
||||
env(trust(alice, EUR(0)));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1); // LPToken
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::AMMWithdraw;
|
||||
jv[jss::Account] = alice.human();
|
||||
jv[jss::Asset] =
|
||||
STIssue(sfAsset, USD.issue()).getJson(JsonOptions::none);
|
||||
jv[jss::Asset2] =
|
||||
STIssue(sfAsset, EUR.issue()).getJson(JsonOptions::none);
|
||||
jv[jss::Flags] = tfWithdrawAll;
|
||||
|
||||
{
|
||||
env(ticket::create(
|
||||
sponsor, 1)); // adjust for free trustline
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, sponsor) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
// check INSUFFICIENT_RESERVE for RippleStates * 2
|
||||
adjustAccountXRPBalance(
|
||||
env, sponsor, reserve(env, 3) - drops(1));
|
||||
env(jv,
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor),
|
||||
ter(tecINSUFFICIENT_RESERVE));
|
||||
env.close();
|
||||
adjustAccountXRPBalance(env, sponsor, reserve(env, 4));
|
||||
}
|
||||
|
||||
env(jv,
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2); // USD, EUR
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2);
|
||||
}
|
||||
}
|
||||
{
|
||||
// AMMClawback
|
||||
// - doesn't sponsor holder's new RippleState
|
||||
// - remove sponsored LPToken
|
||||
Account const gw2("gw2");
|
||||
auto const EUR2 = gw2["EUR"];
|
||||
|
||||
Env env{*this, testable_amendments()};
|
||||
env.fund(XRP(10000), alice, gw, gw2, sponsor);
|
||||
env.close();
|
||||
|
||||
env(fset(gw, asfAllowTrustLineClawback));
|
||||
env.close();
|
||||
|
||||
env(trust(alice, USD(10000)));
|
||||
env(trust(alice, EUR2(10000)));
|
||||
env.close();
|
||||
|
||||
env(pay(gw, alice, USD(100)));
|
||||
env(pay(gw2, alice, EUR2(100)));
|
||||
env.close();
|
||||
|
||||
env(ammCreate(env, alice, USD(100), EUR2(100)),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
env(trust(alice, USD(0)));
|
||||
env(trust(alice, EUR2(0)));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1); // LPToken
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
{
|
||||
// doesn't sponsor holder's new RippleState
|
||||
env(amm::ammClawback(gw, alice, USD, EUR2, USD(10)),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2); // LPToken, EUR2
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
}
|
||||
{
|
||||
env(amm::ammClawback(gw, alice, USD, EUR2, std::nullopt),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1); // EUR2
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testCheck()
|
||||
{
|
||||
@@ -3166,6 +3498,7 @@ public:
|
||||
{
|
||||
// TODO: add checks fo InsufficientReserve for Sponsoring ledger entry
|
||||
testRequireFlag();
|
||||
testAMM();
|
||||
testCheck();
|
||||
testOffer();
|
||||
testTicket();
|
||||
|
||||
@@ -122,7 +122,8 @@ public:
|
||||
static TER
|
||||
send(Args&&... args)
|
||||
{
|
||||
return accountSend(std::forward<Args>(args)..., WaiveTransferFee::Yes);
|
||||
return accountSend(
|
||||
std::forward<Args>(args)..., std::nullopt, WaiveTransferFee::Yes);
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -141,8 +141,10 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
||||
return terNO_RIPPLE;
|
||||
}
|
||||
|
||||
auto const sponsor = getTxReserveSponsorAccountID(ctx.tx);
|
||||
// Check the reserve for LPToken trustline
|
||||
STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j);
|
||||
STAmount const xrpBalance =
|
||||
xrpLiquid(ctx.view, sponsor.value_or(accountID), 1, ctx.j);
|
||||
// Insufficient reserve
|
||||
if (xrpBalance <= beast::zero)
|
||||
{
|
||||
@@ -275,7 +277,9 @@ applyCreate(
|
||||
sb.insert(ammSle);
|
||||
|
||||
// Send LPT to LP.
|
||||
auto res = accountSend(sb, accountId, account_, lpTokens, ctx_.journal);
|
||||
auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx);
|
||||
auto res =
|
||||
accountSend(sb, accountId, account_, lpTokens, ctx_.journal, sponsor);
|
||||
if (res != tesSUCCESS)
|
||||
{
|
||||
JLOG(j_.debug()) << "AMM Instance: failed to send LPT " << lpTokens;
|
||||
@@ -289,6 +293,7 @@ applyCreate(
|
||||
accountId,
|
||||
amount,
|
||||
ctx_.journal,
|
||||
std::nullopt, // don't sponsor for AMM Trustline
|
||||
WaiveTransferFee::Yes))
|
||||
return res;
|
||||
// Set AMM flag on AMM trustline
|
||||
|
||||
@@ -167,6 +167,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const accountID = ctx.tx[sfAccount];
|
||||
|
||||
auto const sponsor = getTxReserveSponsorAccountID(ctx.tx);
|
||||
|
||||
auto const ammSle =
|
||||
ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
|
||||
if (!ammSle)
|
||||
@@ -224,7 +226,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
|
||||
// Adjust the reserve if LP doesn't have LPToken trustline
|
||||
auto const sle = ctx.view.read(
|
||||
keylet::line(accountID, lpIssue.account, lpIssue.currency));
|
||||
if (xrpLiquid(ctx.view, accountID, !sle, ctx.j) >= deposit)
|
||||
if (xrpLiquid(ctx.view, sponsor.value_or(accountID), !sle, ctx.j) >=
|
||||
deposit)
|
||||
return TER(tesSUCCESS);
|
||||
if (sle)
|
||||
return tecUNFUNDED_AMM;
|
||||
@@ -352,7 +355,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
|
||||
// We checked above but need to check again if depositing IOU only.
|
||||
if (ammLPHolds(ctx.view, *ammSle, accountID, ctx.j) == beast::zero)
|
||||
{
|
||||
STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j);
|
||||
STAmount const xrpBalance =
|
||||
xrpLiquid(ctx.view, sponsor.value_or(accountID), 1, ctx.j);
|
||||
// Insufficient reserve
|
||||
if (xrpBalance <= beast::zero)
|
||||
{
|
||||
@@ -507,6 +511,8 @@ AMMDeposit::deposit(
|
||||
std::optional<STAmount> const& lpTokensDepositMin,
|
||||
std::uint16_t tfee)
|
||||
{
|
||||
auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx);
|
||||
|
||||
// Check account has sufficient funds.
|
||||
// Return true if it does, false otherwise.
|
||||
auto checkBalance = [&](auto const& depositAmount) -> TER {
|
||||
@@ -518,7 +524,8 @@ AMMDeposit::deposit(
|
||||
// Adjust the reserve if LP doesn't have LPToken trustline
|
||||
auto const sle = view.read(
|
||||
keylet::line(account_, lpIssue.account, lpIssue.currency));
|
||||
if (xrpLiquid(view, account_, !sle, j_) >= depositAmount)
|
||||
if (xrpLiquid(view, sponsor.value_or(account_), !sle, j_) >=
|
||||
depositAmount)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
else if (
|
||||
@@ -578,6 +585,7 @@ AMMDeposit::deposit(
|
||||
ammAccount,
|
||||
amountDepositActual,
|
||||
ctx_.journal,
|
||||
std::nullopt, // don't sponsor for AMM Trustline
|
||||
WaiveTransferFee::Yes);
|
||||
if (res != tesSUCCESS)
|
||||
{
|
||||
@@ -604,6 +612,7 @@ AMMDeposit::deposit(
|
||||
ammAccount,
|
||||
*amount2DepositActual,
|
||||
ctx_.journal,
|
||||
std::nullopt, // don't sponsor for AMM Trustline
|
||||
WaiveTransferFee::Yes);
|
||||
if (res != tesSUCCESS)
|
||||
{
|
||||
@@ -615,7 +624,12 @@ AMMDeposit::deposit(
|
||||
|
||||
// Deposit LP tokens
|
||||
res = accountSend(
|
||||
view, ammAccount, account_, lpTokensDepositActual, ctx_.journal);
|
||||
view,
|
||||
ammAccount,
|
||||
account_,
|
||||
lpTokensDepositActual,
|
||||
ctx_.journal,
|
||||
sponsor);
|
||||
if (res != tesSUCCESS)
|
||||
{
|
||||
JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens";
|
||||
|
||||
@@ -586,6 +586,12 @@ AMMWithdraw::withdraw(
|
||||
return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
|
||||
}
|
||||
|
||||
// this is also called from AMMClawback, but only AMMWithdraw does sponsor
|
||||
// the new trustline
|
||||
auto const sponsor = tx[sfAccount] == account
|
||||
? getTxReserveSponsorAccountID(tx)
|
||||
: std::nullopt;
|
||||
|
||||
// Check the reserve in case a trustline has to be created
|
||||
bool const enabledFixAMMv1_2 = view.rules().enabled(fixAMMv1_2);
|
||||
auto sufficientReserve = [&](Issue const& issue) -> TER {
|
||||
@@ -593,20 +599,20 @@ AMMWithdraw::withdraw(
|
||||
return tesSUCCESS;
|
||||
if (!view.exists(keylet::line(account, issue)))
|
||||
{
|
||||
auto const sleAccount = view.read(keylet::account(account));
|
||||
auto const sleAccount =
|
||||
view.read(keylet::account(sponsor.value_or(account)));
|
||||
if (!sleAccount)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
auto const balance = (*sleAccount)[sfBalance].xrp();
|
||||
std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount);
|
||||
|
||||
if (ownerCount >= 2)
|
||||
std::uint32_t const count = ownerCount(sleAccount);
|
||||
if (count >= 2)
|
||||
{
|
||||
auto const sponsor = getTxReserveSponsor(view, tx);
|
||||
if (auto const ret = checkInsufficientReserve(
|
||||
view,
|
||||
sleAccount,
|
||||
std::max(priorBalance, balance),
|
||||
sponsor,
|
||||
sponsor ? view.read(keylet::account(*sponsor))
|
||||
: std::optional<std::shared_ptr<SLE const>>{},
|
||||
1);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
@@ -625,6 +631,7 @@ AMMWithdraw::withdraw(
|
||||
account,
|
||||
amountWithdrawActual,
|
||||
journal,
|
||||
sponsor,
|
||||
WaiveTransferFee::Yes);
|
||||
if (res != tesSUCCESS)
|
||||
{
|
||||
@@ -648,6 +655,7 @@ AMMWithdraw::withdraw(
|
||||
account,
|
||||
*amount2WithdrawActual,
|
||||
journal,
|
||||
sponsor,
|
||||
WaiveTransferFee::Yes);
|
||||
if (res != tesSUCCESS)
|
||||
{
|
||||
|
||||
@@ -278,6 +278,7 @@ VaultClawback::doApply()
|
||||
vaultAccount,
|
||||
sharesDestroyed,
|
||||
j_,
|
||||
std::nullopt,
|
||||
WaiveTransferFee::Yes);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
@@ -317,6 +318,7 @@ VaultClawback::doApply()
|
||||
account_,
|
||||
assetsRecovered,
|
||||
j_,
|
||||
std::nullopt,
|
||||
WaiveTransferFee::Yes);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
@@ -302,6 +302,8 @@ VaultDeposit::doApply()
|
||||
if (maximum != 0 && *vault->at(sfAssetsTotal) > maximum)
|
||||
return tecLIMIT_EXCEEDED;
|
||||
|
||||
auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx);
|
||||
|
||||
// Transfer assets from depositor to vault.
|
||||
if (auto const ter = accountSend(
|
||||
view(),
|
||||
@@ -309,6 +311,7 @@ VaultDeposit::doApply()
|
||||
vaultAccount,
|
||||
assetsDeposited,
|
||||
j_,
|
||||
sponsor,
|
||||
WaiveTransferFee::Yes);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
@@ -335,6 +338,7 @@ VaultDeposit::doApply()
|
||||
account_,
|
||||
sharesCreated,
|
||||
j_,
|
||||
sponsor,
|
||||
WaiveTransferFee::Yes);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
@@ -277,6 +277,7 @@ VaultWithdraw::doApply()
|
||||
view().update(vault);
|
||||
|
||||
auto const& vaultAccount = vault->at(sfAccount);
|
||||
auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx);
|
||||
// Transfer shares from depositor to vault.
|
||||
if (auto const ter = accountSend(
|
||||
view(),
|
||||
@@ -284,6 +285,7 @@ VaultWithdraw::doApply()
|
||||
vaultAccount,
|
||||
sharesRedeemed,
|
||||
j_,
|
||||
sponsor,
|
||||
WaiveTransferFee::Yes);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
@@ -329,6 +331,7 @@ VaultWithdraw::doApply()
|
||||
dstAcct,
|
||||
assetsWithdrawn,
|
||||
j_,
|
||||
sponsor,
|
||||
WaiveTransferFee::Yes);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
Reference in New Issue
Block a user