Change tecFROZEN to tecLOCKED for locked MPT.

Replace checkMPTTxAllowed() with canMPTTradeAndTransfer().
This commit is contained in:
Gregory Tsipenyuk
2026-04-07 14:00:04 -04:00
parent d26d93130e
commit 12eb050bf8
13 changed files with 164 additions and 177 deletions

View File

@@ -495,6 +495,22 @@ canTrade(ReadView const& view, Asset const& asset)
});
}
TER
canMPTTradeAndTransfer(
ReadView const& view,
Asset const& asset,
AccountID const& from,
AccountID const& to)
{
if (!asset.holds<MPTIssue>())
return tesSUCCESS;
if (auto const ter = canTrade(view, asset); !isTesSuccess(ter))
return ter;
return canTransfer(view, asset, from, to);
}
TER
lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount, beast::Journal j)
{
@@ -862,65 +878,4 @@ issuerSelfDebitHookMPT(ApplyView& view, MPTIssue const& issue, std::uint64_t amo
view.issuerSelfDebitHookMPT(issue, amount, available);
}
static TER
checkMPTAllowed(ReadView const& view, TxType txType, Asset const& asset, AccountID const& accountID)
{
if (!asset.holds<MPTIssue>())
return tesSUCCESS;
auto const& issuanceID = asset.get<MPTIssue>().getMptID();
auto const validTx = txType == ttAMM_CREATE || txType == ttAMM_DEPOSIT ||
txType == ttAMM_WITHDRAW || txType == ttOFFER_CREATE || txType == ttCHECK_CREATE ||
txType == ttCHECK_CASH || txType == ttPAYMENT;
XRPL_ASSERT(validTx, "xrpl::checkMPTAllowed : all MPT tx or DEX");
if (!validTx)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const& issuer = asset.getIssuer();
if (!view.exists(keylet::account(issuer)))
return tecNO_ISSUER; // LCOV_EXCL_LINE
auto const issuanceKey = keylet::mptIssuance(issuanceID);
auto const issuanceSle = view.read(issuanceKey);
if (!issuanceSle)
return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE
auto const flags = issuanceSle->getFlags();
if ((flags & lsfMPTLocked) != 0u)
return tecLOCKED; // LCOV_EXCL_LINE
// Offer crossing and Payment
if ((flags & lsfMPTCanTrade) == 0)
return tecNO_PERMISSION;
if (accountID != issuer)
{
if ((flags & lsfMPTCanTransfer) == 0)
return tecNO_PERMISSION;
auto const mptSle = view.read(keylet::mptoken(issuanceKey.key, accountID));
// Allow to succeed since some tx create MPToken if it doesn't exist.
// Tx's have their own check for missing MPToken.
if (!mptSle)
return tesSUCCESS;
if (mptSle->isFlag(lsfMPTLocked))
return tecLOCKED;
}
return tesSUCCESS;
}
TER
checkMPTTxAllowed(
ReadView const& view,
TxType txType,
Asset const& asset,
AccountID const& accountID)
{
// use isDEXAllowed for payment/offer crossing
XRPL_ASSERT(txType != ttPAYMENT, "xrpl::checkMPTTxAllowed : not payment");
return checkMPTAllowed(view, txType, asset, accountID);
}
} // namespace xrpl

View File

@@ -40,6 +40,14 @@ isGlobalFrozen(ReadView const& view, Asset const& asset)
[&](MPTIssue const& issue) { return isGlobalFrozen(view, issue); });
}
TER
checkGlobalFrozen(ReadView const& view, Asset const& asset)
{
if (isGlobalFrozen(view, asset))
return asset.holds<MPTIssue>() ? tecLOCKED : tecFROZEN;
return tesSUCCESS;
}
bool
isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
{
@@ -47,6 +55,14 @@ isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const&
[&](auto const& issue) { return isIndividualFrozen(view, account, issue); }, asset.value());
}
TER
checkIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
{
if (isIndividualFrozen(view, account, asset))
return asset.holds<MPTIssue>() ? tecLOCKED : tecFROZEN;
return tesSUCCESS;
}
bool
isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth)
{

View File

@@ -94,10 +94,10 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
{
// The currency may not be globally frozen
AccountID const& issuerId{sendMax.getIssuer()};
if (isGlobalFrozen(ctx.view, sendMax.asset()))
if (auto const ter = checkGlobalFrozen(ctx.view, sendMax.asset()); !isTesSuccess(ter))
{
JLOG(ctx.j.warn()) << "Creating a check for frozen asset";
return sendMax.asset().holds<MPTIssue>() ? tecLOCKED : tecFROZEN;
JLOG(ctx.j.warn()) << "Creating a check for frozen or locked asset";
return ter;
}
auto const err = sendMax.asset().visit(
[&](Issue const& issue) -> std::optional<TER> {

View File

@@ -94,11 +94,16 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
}
// Globally or individually frozen
if (isFrozen(ctx.view, accountID, amount.asset()) ||
isFrozen(ctx.view, accountID, amount2.asset()))
if (auto const ter = checkFrozen(ctx.view, accountID, amount.asset()); !isTesSuccess(ter))
{
JLOG(ctx.j.debug()) << "AMM Instance: involves frozen asset.";
return tecFROZEN;
JLOG(ctx.j.debug()) << "AMM Instance: involves frozen or locked asset.";
return ter;
}
if (auto const ter = checkFrozen(ctx.view, accountID, amount2.asset()); !isTesSuccess(ter))
{
JLOG(ctx.j.debug()) << "AMM Instance: involves frozen or locked asset.";
return ter;
}
auto noDefaultRipple = [](ReadView const& view, Asset const& asset) {
@@ -165,10 +170,10 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
return terADDRESS_COLLISION;
}
if (auto const ter = checkMPTTxAllowed(ctx.view, ttAMM_CREATE, amount.asset(), accountID);
if (auto const ter = canMPTTradeAndTransfer(ctx.view, amount.asset(), accountID, accountID);
!isTesSuccess(ter))
return ter;
if (auto const ter = checkMPTTxAllowed(ctx.view, ttAMM_CREATE, amount2.asset(), accountID);
if (auto const ter = canMPTTradeAndTransfer(ctx.view, amount2.asset(), accountID, accountID);
!isTesSuccess(ter))
return ter;

View File

@@ -243,12 +243,12 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
return ter;
}
if (isFrozen(ctx.view, accountID, asset))
if (auto const ter = checkFrozen(ctx.view, accountID, asset); !isTesSuccess(ter))
{
JLOG(ctx.j.debug()) << "AMM Deposit: account or currency is frozen, "
JLOG(ctx.j.debug()) << "AMM Deposit: account or currency is frozen or locked, "
<< to_string(accountID) << " " << to_string(asset);
return tecFROZEN;
return ter;
}
return tesSUCCESS;
@@ -280,18 +280,20 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
// LCOV_EXCL_STOP
}
// AMM account or currency frozen
if (isFrozen(ctx.view, ammAccountID, amount->asset()))
if (auto const ter = checkFrozen(ctx.view, ammAccountID, amount->asset());
!isTesSuccess(ter))
{
JLOG(ctx.j.debug())
<< "AMM Deposit: AMM account or currency is frozen, " << to_string(accountID);
return tecFROZEN;
JLOG(ctx.j.debug()) << "AMM Deposit: AMM account or currency is frozen or locked, "
<< to_string(accountID);
return ter;
}
// Account frozen
if (isIndividualFrozen(ctx.view, accountID, amount->asset()))
if (auto const ter = checkIndividualFrozen(ctx.view, accountID, amount->asset());
!isTesSuccess(ter))
{
JLOG(ctx.j.debug()) << "AMM Deposit: account is frozen, " << to_string(accountID)
<< " " << to_string(amount->asset());
return tecFROZEN;
JLOG(ctx.j.debug()) << "AMM Deposit: account is frozen or locked, "
<< to_string(accountID) << " " << to_string(amount->asset());
return ter;
}
if (checkBalance)
{
@@ -344,10 +346,10 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
}
}
if (auto const ter = checkMPTTxAllowed(ctx.view, ttAMM_DEPOSIT, ctx.tx[sfAsset], accountID);
if (auto const ter = canMPTTradeAndTransfer(ctx.view, ctx.tx[sfAsset], accountID, accountID);
!isTesSuccess(ter))
return ter;
if (auto const ter = checkMPTTxAllowed(ctx.view, ttAMM_DEPOSIT, ctx.tx[sfAsset2], accountID);
if (auto const ter = canMPTTradeAndTransfer(ctx.view, ctx.tx[sfAsset2], accountID, accountID);
!isTesSuccess(ter))
return ter;

View File

@@ -211,22 +211,24 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx)
return ter;
}
// AMM account or currency frozen
if (isFrozen(ctx.view, ammAccountID, amount->asset()))
if (auto const ter = checkFrozen(ctx.view, ammAccountID, amount->asset());
!isTesSuccess(ter))
{
JLOG(ctx.j.debug())
<< "AMM Withdraw: AMM account or currency is frozen, " << to_string(accountID);
return tecFROZEN;
JLOG(ctx.j.debug()) << "AMM Withdraw: AMM account or currency is frozen or locked, "
<< to_string(accountID);
return ter;
}
// Account frozen
if (isIndividualFrozen(ctx.view, accountID, amount->asset()))
if (auto const ter = checkIndividualFrozen(ctx.view, accountID, amount->asset());
!isTesSuccess(ter))
{
JLOG(ctx.j.debug()) << "AMM Withdraw: account is frozen, " << to_string(accountID)
<< " " << to_string(amount->asset());
return tecFROZEN;
JLOG(ctx.j.debug()) << "AMM Withdraw: account is frozen or locked, "
<< to_string(accountID) << " " << to_string(amount->asset());
return ter;
}
if (auto const ter =
checkMPTTxAllowed(ctx.view, ttAMM_WITHDRAW, amount->asset(), accountID);
canMPTTradeAndTransfer(ctx.view, amount->asset(), accountID, accountID);
!isTesSuccess(ter))
return ter;
}

View File

@@ -146,11 +146,15 @@ OfferCreate::preclaim(PreclaimContext const& ctx)
auto viewJ = ctx.registry.get().getJournal("View");
if (isGlobalFrozen(ctx.view, saTakerPays.asset()) ||
isGlobalFrozen(ctx.view, saTakerGets.asset()))
if (auto const ter = checkGlobalFrozen(ctx.view, saTakerPays.asset()); !isTesSuccess(ter))
{
JLOG(ctx.j.debug()) << "Offer involves frozen asset";
return tecFROZEN;
return ter;
}
if (auto const ter = checkGlobalFrozen(ctx.view, saTakerGets.asset()); !isTesSuccess(ter))
{
JLOG(ctx.j.debug()) << "Offer involves frozen asset";
return ter;
}
// Allow unfunded MPT for issuer (OutstandingAmount >= MaximumAmount)
@@ -281,7 +285,13 @@ OfferCreate::checkAcceptAsset(
[&](MPTIssue const& issue) -> TER {
// WeakAuth - don't check if MPToken exists since it's created
// if needed.
return requireAuth(view, issue, id, AuthType::WeakAuth);
if (auto const ter = requireAuth(view, issue, id, AuthType::WeakAuth);
!isTesSuccess(ter))
{
return ter;
}
return checkFrozen(view, id, issue);
});
}

View File

@@ -3063,10 +3063,8 @@ private:
BTC.set({.holder = bob, .flags = tfMPTLock});
{
// different from IOU. The offer is created but not crossed.
env(offer(bob, BTC(5), XRP(25)));
env(offer(bob, BTC(5), XRP(25)), ter(tecLOCKED));
env.close();
BEAST_EXPECT(expectOffers(env, bob, 1, {{{BTC(5), XRP(25)}}}));
BEAST_EXPECT(ammAlice.expectBalances(XRP(500), BTC(105), ammAlice.tokens()));
}
@@ -3169,7 +3167,7 @@ private:
BTC.set({.flags = tfMPTLock});
// assets can't be bought on the market
AMM const ammA3(env, A3, BTC(1), XRP(1), ter(tecFROZEN));
AMM const ammA3(env, A3, BTC(1), XRP(1), ter(tecLOCKED));
// direct issues can be sent
env(pay(G1, A2, BTC(1)));

View File

@@ -99,7 +99,7 @@ private:
.pay = 30'000,
.flags = tfMPTCanLock | MPTDEXFlags});
USD.set({.flags = tfMPTLock});
AMM const ammAliceFail(env, alice, XRP(10'000), USD(10'000), ter(tecFROZEN));
AMM const ammAliceFail(env, alice, XRP(10'000), USD(10'000), ter(tecLOCKED));
USD.set({.flags = tfMPTUnlock});
AMM const ammAlice(env, alice, XRP(10'000), USD(10'000));
}
@@ -303,7 +303,7 @@ private:
.pay = 30'000,
.flags = tfMPTCanLock | MPTDEXFlags});
BTC.set({.flags = tfMPTLock});
AMM const ammAlice(env, alice, USD(10'000), BTC(10'000), ter(tecFROZEN));
AMM const ammAlice(env, alice, USD(10'000), BTC(10'000), ter(tecLOCKED));
BEAST_EXPECT(!ammAlice.ammExists());
}
@@ -320,7 +320,7 @@ private:
BTC.set({.holder = alice, .flags = tfMPTLock});
// alice's token is locked
AMM const ammAlice(env, alice, USD(10'000), BTC(10'000), ter(tecFROZEN));
AMM const ammAlice(env, alice, USD(10'000), BTC(10'000), ter(tecLOCKED));
BEAST_EXPECT(!ammAlice.ammExists());
// bob can create
@@ -645,14 +645,14 @@ private:
BTC.set({.flags = tfMPTLock});
ammAlice.deposit(
carol, BTC(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN));
carol, BTC(100), std::nullopt, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.deposit(
carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN));
carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.deposit(carol, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.deposit(carol, 1'000, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.deposit(carol, USD(100), BTC(100), std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.deposit(carol, USD(100), BTC(100), std::nullopt, std::nullopt, ter(tecLOCKED));
}
// Individually lock MPT or freeze IOU (AMM) with IOU/MPT AMM
@@ -673,20 +673,19 @@ private:
// Carol can not deposit locked mpt
ammAlice.deposit(
carol, BTC(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN));
carol, BTC(100), std::nullopt, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.deposit(carol, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.deposit(carol, 1'000, std::nullopt, std::nullopt, ter(tecLOCKED));
if (!features[featureAMMClawback])
{
ammAlice.deposit(
carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.deposit(carol, USD(100), std::nullopt, std::nullopt, std::nullopt);
}
else
{
// Carol can not deposit non-forzen token either
// Carol can not deposit non-frozen token either
ammAlice.deposit(
carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN));
carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecLOCKED));
}
// Alice can deposit because she's not individually locked
@@ -727,9 +726,9 @@ private:
ammAlice.deposit(carol, USD(100), std::nullopt, std::nullopt, std::nullopt);
// Can not deposit locked token
ammAlice.deposit(carol, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.deposit(carol, 1'000, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.deposit(
carol, BTC(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN));
carol, BTC(100), std::nullopt, std::nullopt, std::nullopt, ter(tecLOCKED));
// Unlock AMM MPT
BTC.set({.holder = ammAlice.ammAccount(), .flags = tfMPTUnlock});
@@ -761,10 +760,10 @@ private:
// Carol's BTC is locked
BTC.set({.holder = carol, .flags = tfMPTLock});
ammAlice.deposit(
carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN));
carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.deposit(
carol, BTC(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN));
carol, BTC(100), std::nullopt, std::nullopt, std::nullopt, ter(tecLOCKED));
// Unlock carol's BTC
BTC.set({.holder = carol, .flags = tfMPTUnlock});
@@ -780,9 +779,9 @@ private:
ammAlice.deposit(carol, USD(100), std::nullopt, std::nullopt, std::nullopt);
// Can not deposit locked token BTC
ammAlice.deposit(carol, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.deposit(carol, 1'000, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.deposit(
carol, BTC(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN));
carol, BTC(100), std::nullopt, std::nullopt, std::nullopt, ter(tecLOCKED));
// Unlock AMM MPT BTC
BTC.set({.holder = ammAlice.ammAccount(), .flags = tfMPTUnlock});
@@ -797,9 +796,9 @@ private:
ammAlice.deposit(carol, BTC(100), std::nullopt, std::nullopt, std::nullopt);
// Can not deposit locked token USD
ammAlice.deposit(carol, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.deposit(carol, 1'000, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.deposit(
carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN));
carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecLOCKED));
// Unlock AMM MPT USD
USD.set({.holder = ammAlice.ammAccount(), .flags = tfMPTUnlock});
@@ -853,7 +852,7 @@ private:
AMM amm(env, gw, XRP(10'000), BTC(10'000));
amm.deposit({.account = alice, .asset1In = BTC(10), .err = ter(tecNO_PERMISSION)});
amm.deposit({.account = alice, .asset1In = BTC(10), .err = ter(tecNO_AUTH)});
}
// Insufficient XRP balance
@@ -2211,7 +2210,7 @@ private:
.account = alice,
.asset1Out = BTC(100),
.assets = {{XRP, BTC}},
.err = ter(tecNO_PERMISSION)});
.err = ter(tecNO_AUTH)});
}
// Globally locked MPT
@@ -2232,8 +2231,8 @@ private:
BTC.set({.flags = tfMPTLock});
ammAlice.withdraw(
alice, MPT(ammAlice[1])(100), std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
alice, MPT(ammAlice[1])(100), std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecLOCKED));
// can single withdraw the other asset
ammAlice.withdraw({.account = alice, .asset1Out = XRP(100)});
@@ -2261,8 +2260,8 @@ private:
// Alice's BTC is locked
BTC.set({.holder = alice, .flags = tfMPTLock});
ammAlice.withdraw(alice, 1000, std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.withdraw(alice, BTC(100), std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.withdraw(alice, 1000, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.withdraw(alice, BTC(100), std::nullopt, std::nullopt, ter(tecLOCKED));
// can withdraw the other asset
ammAlice.withdraw(alice, USD(100), std::nullopt, std::nullopt);
@@ -2290,8 +2289,8 @@ private:
// Alice's BTC is locked
BTC.set({.holder = alice, .flags = tfMPTLock});
ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.withdraw(alice, BTC(100), std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.withdraw(alice, BTC(100), std::nullopt, std::nullopt, ter(tecLOCKED));
// can still single withdraw the unlocked other asset
ammAlice.withdraw(alice, USD(100), std::nullopt, std::nullopt);
@@ -2310,8 +2309,8 @@ private:
ammAlice.withdraw(alice, USD(100), std::nullopt, std::nullopt);
// Can not withdraw locked token BTC
ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.withdraw(alice, BTC(100), std::nullopt, std::nullopt, ter(tecFROZEN));
ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecLOCKED));
ammAlice.withdraw(alice, BTC(100), std::nullopt, std::nullopt, ter(tecLOCKED));
// Unlock AMM MPT
BTC.set({.holder = ammAlice.ammAccount(), .flags = tfMPTUnlock});
@@ -6807,33 +6806,33 @@ private:
cb(amm, BTC);
};
// Deposit two assets, one of which is frozen,
// then we should get tecFROZEN error.
// Deposit two assets, one of which is locked,
// then we should get tecLOCKED error.
{
Env env(*this);
testAMMDeposit(env, [&](AMM& amm, MPTTester& BTC) {
amm.deposit(alice, BTC(100), XRP(100), std::nullopt, tfTwoAsset, ter(tecFROZEN));
amm.deposit(alice, BTC(100), XRP(100), std::nullopt, tfTwoAsset, ter(tecLOCKED));
});
}
// Deposit one asset, which is the frozen token,
// then we should get tecFROZEN error.
// Deposit one asset, which is the frozen locked,
// then we should get tecLOCKED error.
{
Env env(*this);
testAMMDeposit(env, [&](AMM& amm, MPTTester& BTC) {
amm.deposit(
alice, BTC(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecFROZEN));
alice, BTC(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecLOCKED));
});
}
// Deposit one asset which is not the frozen token,
// but the other asset is frozen. We should get tecFROZEN error
// but the other asset is frozen. We should get tecLOCKED error
// when feature AMMClawback is enabled.
{
Env env(*this);
testAMMDeposit(env, [&](AMM& amm, MPTTester& BTC) {
amm.deposit(
alice, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecFROZEN));
alice, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecLOCKED));
});
}
}

View File

@@ -1813,10 +1813,10 @@ class CheckMPT_test : public beast::unit_test::suite
// Use offers to automatically create MPT.
MPT const OF4 = gw1["OF4"];
gw1.set(OF4, tfMPTLock);
env(offer(gw1, XRP(92), OF4(92)), ter(tecFROZEN));
env(offer(gw1, XRP(92), OF4(92)), ter(tecLOCKED));
env.close();
BEAST_EXPECT(env.le(keylet::mptoken(OF4, alice)) == nullptr);
env(offer(alice, OF4(92), XRP(92)), ter(tecFROZEN));
env(offer(alice, OF4(92), XRP(92)), ter(tecLOCKED));
env.close();
// No one's owner count should have changed.
@@ -1904,10 +1904,10 @@ class CheckMPT_test : public beast::unit_test::suite
// Use offers to automatically create MPT.
MPT const OF4 = gw1["OF4"];
gw1.set(OF4, tfMPTLock);
env(offer(alice, XRP(91), OF4(91)), ter(tecFROZEN));
env(offer(alice, XRP(91), OF4(91)), ter(tecLOCKED));
env.close();
BEAST_EXPECT(env.le(keylet::mptoken(OF4, alice)) == nullptr);
env(offer(bob, OF4(91), XRP(91)), ter(tecFROZEN));
env(offer(bob, OF4(91), XRP(91)), ter(tecLOCKED));
env.close();
// No one's owner count should have changed.

View File

@@ -3395,10 +3395,10 @@ class MPToken_test : public beast::unit_test::suite
auto const [errBuy, errSell] = [&]() -> std::pair<TER, TER> {
// Global lock
if (lockMPTIssue)
return std::make_pair(tecFROZEN, tecFROZEN);
return std::make_pair(tecLOCKED, tecLOCKED);
// Local lock
if (lockMPToken)
return std::make_pair(tesSUCCESS, error(tecUNFUNDED_OFFER));
return std::make_pair(error(tecLOCKED), error(tecUNFUNDED_OFFER));
// MPToken doesn't exist
if (requireAuth)
return std::make_pair(error(tecNO_AUTH), error(tecUNFUNDED_OFFER));
@@ -3496,10 +3496,9 @@ class MPToken_test : public beast::unit_test::suite
else
{
auto const err = flag == tfMPTLock ? ter(tecUNFUNDED_OFFER) : ter(tesSUCCESS);
auto const err1 = flag == tfMPTLock ? ter(tecLOCKED) : ter(tesSUCCESS);
env(offer(alice, ETH(1), BTC(1)), err);
// Offer created by not crossed
env(offer(carol, BTC(1), ETH(1)));
BEAST_EXPECT(expectOffers(env, carol, 1, {{BTC(1), ETH(1)}}));
env(offer(carol, BTC(1), ETH(1)), err1);
}
};
@@ -6126,8 +6125,7 @@ class MPToken_test : public beast::unit_test::suite
AMM amm(env, gw, BTC(100), USD(100));
env.close();
// alice can't deposit since MPTCanTransfer is not set
amm.deposit(
DepositArg{.account = alice, .tokens = 1'000, .err = ter(tecNO_PERMISSION)});
amm.deposit(DepositArg{.account = alice, .tokens = 1'000, .err = ter(tecNO_AUTH)});
env.close();
// can't clawback since alice is not an LP
@@ -6360,8 +6358,8 @@ class MPToken_test : public beast::unit_test::suite
// alice and issuer can't create
USD.set({.flags = tfMPTLock});
createFail(alice, tecFROZEN);
createFail(gw, tecFROZEN);
createFail(alice, tecLOCKED);
createFail(gw, tecLOCKED);
// MPTRequireAuth is set
@@ -6381,7 +6379,7 @@ class MPToken_test : public beast::unit_test::suite
USD.set({.mutableFlags = tmfMPTClearRequireAuth});
USD.set({.mutableFlags = tmfMPTClearCanTransfer});
// alice can't create
createFail(alice, tecNO_PERMISSION);
createFail(alice, tecNO_AUTH);
// issuer can create
createDeleteAMM(gw);
USD.set({.mutableFlags = tmfMPTSetCanTransfer});
@@ -6427,12 +6425,12 @@ class MPToken_test : public beast::unit_test::suite
{.account = account,
.asset1In = USD(1),
.asset2In = EUR(1),
.err = ter(tecFROZEN)});
.err = ter(tecLOCKED)});
amm.deposit(
{.account = account,
.asset1In = EUR(1),
.assets = std::make_pair(EUR, USD),
.err = ter(tecFROZEN)});
.err = ter(tecLOCKED)});
}
USD.set({.flags = tfMPTUnlock});
@@ -6467,15 +6465,12 @@ class MPToken_test : public beast::unit_test::suite
USD.set({.mutableFlags = tmfMPTClearCanTransfer});
// carol can't deposit
amm.deposit(
{.account = carol,
.asset1In = USD(1),
.asset2In = EUR(1),
.err = ter(tecNO_PERMISSION)});
{.account = carol, .asset1In = USD(1), .asset2In = EUR(1), .err = ter(tecNO_AUTH)});
amm.deposit(
{.account = carol,
.asset1In = EUR(1),
.assets = std::make_pair(EUR, USD),
.err = ter(tecNO_PERMISSION)});
.err = ter(tecNO_AUTH)});
// issuer can deposit
amm.deposit({.account = gw, .tokens = 1'000});
// carol can deposit
@@ -6517,8 +6512,8 @@ class MPToken_test : public beast::unit_test::suite
{.account = account,
.asset1Out = USD(1),
.asset2Out = EUR(1),
.err = ter(tecFROZEN)});
amm.withdraw({.account = account, .tokens = 1'000, .err = ter(tecFROZEN)});
.err = ter(tecLOCKED)});
amm.withdraw({.account = account, .tokens = 1'000, .err = ter(tecLOCKED)});
// can single withdraw another asset
amm.withdraw(
{.account = account, .asset1Out = EUR(1), .assets = std::make_pair(EUR, USD)});
@@ -6544,7 +6539,7 @@ class MPToken_test : public beast::unit_test::suite
USD.authorize({.account = gw, .holder = carol});
amm.withdraw({.account = carol, .asset1Out = USD(1), .asset2Out = EUR(1)});
// MPTCanTransfer is set
// MPTCanTransfer is not set
USD.set({.mutableFlags = tmfMPTClearRequireAuth});
USD.set({.mutableFlags = tmfMPTClearCanTransfer});
@@ -6553,7 +6548,7 @@ class MPToken_test : public beast::unit_test::suite
{.account = carol,
.asset1Out = USD(1),
.asset2Out = EUR(1),
.err = ter(tecNO_PERMISSION)});
.err = ter(tecNO_AUTH)});
// can withdraw another asset
amm.withdraw(
{.account = carol, .asset1Out = EUR(1), .assets = std::make_pair(EUR, USD)});
@@ -6564,6 +6559,9 @@ class MPToken_test : public beast::unit_test::suite
amm.withdraw({.account = carol, .asset1Out = USD(1), .asset2Out = EUR(1)});
USD.set({.mutableFlags = tmfMPTSetCanTransfer});
// MPTCanTrade is not set
USD.set({.mutableFlags = tmfMPTClearCanTrade});
amm.withdraw({.account = gw, .tokens = 1'000, .err = ter(tecNO_PERMISSION)});
amm.withdraw({.account = carol, .tokens = 1'000, .err = ter(tecNO_PERMISSION)});