test Sponsor Reserve checks for AMM

This commit is contained in:
tequ
2025-09-25 19:23:07 +09:00
parent d5a1314c47
commit e82c300de7
10 changed files with 426 additions and 24 deletions

View File

@@ -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

View File

@@ -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
{

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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";

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;