Skip to content

Commit

Permalink
Create MPToken on AMMWithdraw if the token doesn't exist
Browse files Browse the repository at this point in the history
  • Loading branch information
gregtatcam committed Dec 9, 2024
1 parent 64b6d7a commit edb60ab
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 17 deletions.
22 changes: 17 additions & 5 deletions src/test/app/MPToken_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3127,14 +3127,26 @@ class MPToken_test : public beast::unit_test::suite
auto const USD = gw["USD"];
Env env(*this, features);
fund(env, gw, {alice}, XRP(1'000), {USD(1'000)});
MPTTester mpt(env, gw, {.holders = {alice}, .fund = false});
mpt.create(
{.ownerCount = 1,
.holderCount = 0,
.flags = tfMPTCanTransfer | tfMPTCanTrade});
MPTTester mpt(env, gw, {.fund = false});
mpt.create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
auto const MPT = mpt["MPT"];
AMM amm(env, gw, MPT(100), XRP(100));
amm.deposit(DepositArg{.account = alice, .asset1In = XRP(10)});
amm::ammClawback(
gw, alice, MPTIssue(mpt.issuanceID()), xrpIssue(), MPT(10));
}

{
Account const gw{"gw"};
Account const alice{"alice"};
auto const USD = gw["USD"];
Env env(*this, features);
fund(env, gw, {alice}, XRP(1'000), {USD(1'000)});
MPTTester mpt(env, gw, {.fund = false});
mpt.create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
mpt.authorize({.account = alice});
mpt.pay(gw, alice, 1'000);
auto const MPT = mpt["MPT"];
AMM amm(env, gw, MPT(100), XRP(100));
amm.deposit(DepositArg{.account = alice, .tokens = 10'000});
amm::ammClawback(
Expand Down
12 changes: 9 additions & 3 deletions src/xrpld/app/tx/detail/AMMCreate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,14 @@ applyCreate(
// Authorize MPT
if (amount.holds<MPTIssue>())
{
auto const mptokenKey =
keylet::mptoken(amount.get<MPTIssue>().getMptID(), *ammAccount);
auto const& mptIssue = amount.get<MPTIssue>();
if (auto const err = requireAuth(
ctx_.view(), mptIssue, account_, MPTAuthType::WeakAuth);
err != tesSUCCESS)
return err;

auto const& mptID = mptIssue.getMptID();
auto const mptokenKey = keylet::mptoken(mptID, *ammAccount);

auto const ownerNode = sb.dirInsert(
keylet::ownerDir(*ammAccount),
Expand All @@ -358,7 +364,7 @@ applyCreate(

auto mptoken = std::make_shared<SLE>(mptokenKey);
(*mptoken)[sfAccount] = *ammAccount;
(*mptoken)[sfMPTokenIssuanceID] = amount.get<MPTIssue>().getMptID();
(*mptoken)[sfMPTokenIssuanceID] = mptID;
(*mptoken)[sfFlags] = 0;
(*mptoken)[sfOwnerNode] = *ownerNode;
sb.insert(mptoken);
Expand Down
3 changes: 2 additions & 1 deletion src/xrpld/app/tx/detail/AMMDeposit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
// Check if either of the assets is frozen, AMMDeposit is not allowed
// if either asset is frozen
auto checkAsset = [&](Asset const& asset) -> TER {
if (auto const ter = requireAuth(ctx.view, asset, accountID))
if (auto const ter = requireAuth(
ctx.view, asset, accountID, MPTAuthType::WeakAuth))
{
JLOG(ctx.j.debug())
<< "AMM Deposit: account is not authorized, " << asset;
Expand Down
66 changes: 64 additions & 2 deletions src/xrpld/app/tx/detail/AMMWithdraw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,11 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx)
<< *amount;
return tecAMM_BALANCE;
}
if (auto const ter =
requireAuth(ctx.view, amount->asset(), accountID))
if (auto const ter = requireAuth(
ctx.view,
amount->asset(),
accountID,
MPTAuthType::WeakAuth))
{
JLOG(ctx.j.debug())
<< "AMM Withdraw: account is not authorized, "
Expand Down Expand Up @@ -639,6 +642,61 @@ AMMWithdraw::withdraw(
if (auto const err = sufficientReserve(amountWithdrawActual.issue()))
return {err, STAmount{}, STAmount{}, STAmount{}};

// Create MPToken if doesn't exist
// TODO make a library, AMMCreate, AMMAuthorize use almost identical code
auto createMPToken = [&](Asset const& asset) -> TER {
if (asset.holds<MPTIssue>())
{
auto const& mptIssue = asset.get<MPTIssue>();
auto const issuanceKey = keylet::mptIssuance(mptIssue.getMptID());
auto const mptokenKey = keylet::mptoken(issuanceKey.key, account);
if (!view.exists(mptokenKey))
{
if (auto err = requireAuth(
view, mptIssue, account, MPTAuthType::WeakAuth);
err != tesSUCCESS)
return err;
}

auto const sleAcct = view.peek(keylet::account(account));
if (!sleAcct)
return tefINTERNAL;

std::uint32_t const uOwnerCount =
sleAcct->getFieldU32(sfOwnerCount);
XRPAmount const reserveCreate(
(uOwnerCount < 2)
? XRPAmount(beast::zero)
: view.fees().accountReserve(uOwnerCount + 1));

if (priorBalance < reserveCreate)
return tecINSUFFICIENT_RESERVE;

auto const ownerNode = view.dirInsert(
keylet::ownerDir(account),
mptokenKey,
describeOwnerDir(account));

if (!ownerNode)
return tecDIR_FULL;

auto mptoken = std::make_shared<SLE>(mptokenKey);
(*mptoken)[sfAccount] = account;
(*mptoken)[sfMPTokenIssuanceID] = mptIssue.getMptID();
(*mptoken)[sfFlags] = 0;
(*mptoken)[sfOwnerNode] = *ownerNode;
view.insert(mptoken);

// Update owner count.
adjustOwnerCount(view, sleAcct, 1, journal);
}
return tesSUCCESS;
};

if (auto const res = createMPToken(amountWithdrawActual.asset());
res != tesSUCCESS)
return {res, STAmount{}, STAmount{}, STAmount{}};

// Withdraw amountWithdraw
auto res = accountSend(
view,
Expand All @@ -663,6 +721,10 @@ AMMWithdraw::withdraw(
err != tesSUCCESS)
return {err, STAmount{}, STAmount{}, STAmount{}};

if (auto const res = createMPToken(amountWithdrawActual.asset());
res != tesSUCCESS)
return {res, STAmount{}, STAmount{}, STAmount{}};

res = accountSend(
view,
ammAccount,
Expand Down
22 changes: 19 additions & 3 deletions src/xrpld/ledger/View.h
Original file line number Diff line number Diff line change
Expand Up @@ -542,25 +542,41 @@ transferXRP(
STAmount const& amount,
beast::Journal j);

/* Check if MPToken exists:
* - StrongAuth - before checking lsfMPTRequireAuth is set
* - WeakAuth - after checking if lsfMPTRequireAuth is set
*/
enum class MPTAuthType : bool { StrongAuth = true, WeakAuth = false };

/** Check if the account lacks required authorization.
* Return tecNO_AUTH or tecNO_LINE if it does
* and tesSUCCESS otherwise.
*/
[[nodiscard]] TER
requireAuth(ReadView const& view, Issue const& issue, AccountID const& account);
/* If StrongAuth then return tecNO_AUTH if MPToken doesn't exist or
* lsfMPTRequireAuth is set and MPToken is not authorized. If WeakAuth then
* return tecNO_AUTH if lsfMPTRequireAuth is set and MPToken doesn't exist or is
* not authorized.
*/
[[nodiscard]] TER
requireAuth(
ReadView const& view,
MPTIssue const& mptIssue,
AccountID const& account);
AccountID const& account,
MPTAuthType authType = MPTAuthType::StrongAuth);
[[nodiscard]] TER inline requireAuth(
ReadView const& view,
Asset const& asset,
AccountID const& account)
AccountID const& account,
MPTAuthType authType = MPTAuthType::StrongAuth)
{
return std::visit(
[&]<ValidIssueType TIss>(TIss const& issue_) {
return requireAuth(view, issue_, account);
if constexpr (std::is_same_v<TIss, Issue>)
return requireAuth(view, issue_, account);
else
return requireAuth(view, issue_, account, authType);
},
asset.value());
}
Expand Down
7 changes: 4 additions & 3 deletions src/xrpld/ledger/detail/View.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1867,7 +1867,8 @@ TER
requireAuth(
ReadView const& view,
MPTIssue const& mptIssue,
AccountID const& account)
AccountID const& account,
MPTAuthType authType)
{
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
auto const sleIssuance = view.read(mptID);
Expand All @@ -1885,12 +1886,12 @@ requireAuth(
auto const sleToken = view.read(mptokenID);

// if account has no MPToken, fail
if (!sleToken)
if (!sleToken && authType == MPTAuthType::StrongAuth)
return tecNO_AUTH;

// mptoken must be authorized if issuance enabled requireAuth
if (sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth &&
!(sleToken->getFlags() & lsfMPTAuthorized))
(!sleToken || (!(sleToken->getFlags() & lsfMPTAuthorized))))
return tecNO_AUTH;

return tesSUCCESS;
Expand Down

0 comments on commit edb60ab

Please sign in to comment.