mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-04 01:06:48 +00:00
feat: Add MPT support to DEX (#5285)
This commit is contained in:
committed by
GitHub
parent
6d1a5be8d2
commit
dfcad69155
627
src/test/app/PayStrandMPT_test.cpp
Normal file
627
src/test/app/PayStrandMPT_test.cpp
Normal file
@@ -0,0 +1,627 @@
|
||||
#include <test/jtx.h>
|
||||
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/tx/paths/RippleCalc.h>
|
||||
#include <xrpl/tx/transactors/dex/AMMContext.h>
|
||||
|
||||
namespace xrpl {
|
||||
namespace test {
|
||||
|
||||
struct PayStrandMPT_test : public beast::unit_test::suite
|
||||
{
|
||||
static jtx::DirectStepInfo
|
||||
makeEndpointStep(jtx::Account const& src, jtx::Account const& dst, jtx::IOU const& iou)
|
||||
{
|
||||
return jtx::DirectStepInfo{src, dst, iou.currency};
|
||||
}
|
||||
static jtx::MPTEndpointStepInfo
|
||||
makeEndpointStep(jtx::Account const& src, jtx::Account const& dst, jtx::MPT const& mpt)
|
||||
{
|
||||
return jtx::MPTEndpointStepInfo{src, dst, mpt.mpt()};
|
||||
}
|
||||
|
||||
void
|
||||
testToStrand(FeatureBitset features)
|
||||
{
|
||||
testcase("To Strand");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const carol = Account("carol");
|
||||
auto const gw = Account("gw");
|
||||
|
||||
using M = MPTEndpointStepInfo;
|
||||
using B = xrpl::Book;
|
||||
using XRPS = XRPEndpointStepInfo;
|
||||
|
||||
AMMContext ammContext(alice, false);
|
||||
|
||||
auto test = [&, this](
|
||||
jtx::Env& env,
|
||||
Asset const& deliver,
|
||||
std::optional<Asset> const& sendMaxIssue,
|
||||
STPath const& path,
|
||||
TER expTer,
|
||||
auto&&... expSteps) {
|
||||
auto [ter, strand] = toStrand(
|
||||
*env.current(),
|
||||
alice,
|
||||
bob,
|
||||
deliver,
|
||||
std::nullopt,
|
||||
sendMaxIssue,
|
||||
path,
|
||||
true,
|
||||
OfferCrossing::no,
|
||||
ammContext,
|
||||
std::nullopt,
|
||||
env.app().getLogs().journal("Flow"));
|
||||
BEAST_EXPECT(ter == expTer);
|
||||
if (sizeof...(expSteps) != 0)
|
||||
BEAST_EXPECT(jtx::equal(strand, std::forward<decltype(expSteps)>(expSteps)...));
|
||||
};
|
||||
|
||||
{
|
||||
auto testMultiToken = [&](auto&& issue1, auto&& issue2) {
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10'000), alice, bob, gw);
|
||||
MPT const USD =
|
||||
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt = 1'000});
|
||||
auto const bobUSD = issue1(
|
||||
{.env = env,
|
||||
.token = "USD",
|
||||
.issuer = bob,
|
||||
.holders = {alice},
|
||||
.limit = 1'000});
|
||||
MPT const EUR =
|
||||
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt = 1'000});
|
||||
auto const bobEUR = issue2(
|
||||
{.env = env,
|
||||
.token = "EUR",
|
||||
.issuer = bob,
|
||||
.holders = {alice},
|
||||
.limit = 1'000});
|
||||
env(pay(gw, alice, EUR(100)));
|
||||
|
||||
{
|
||||
// Original test is
|
||||
// STPath({ipe(bob["USD"]), cpe(EUR.currency)});
|
||||
// which ripples through same currency, different issuer.
|
||||
// This results in 5 steps:
|
||||
// 1 DirectStep alice -> gw EUR/gw
|
||||
// 2 Book EUR/gw USD/bob
|
||||
// 3 Book USD/bob EUR/bob
|
||||
// 4 Book EUR/bob XRP
|
||||
// 5 XRPEndpoint
|
||||
// This is somewhat equivalent path with MPT
|
||||
STPath const path = STPath({ipe(bobUSD), ipe(bobEUR), cpe(xrpCurrency())});
|
||||
auto [ter, _] = toStrand(
|
||||
*env.current(),
|
||||
alice,
|
||||
alice,
|
||||
/*deliver*/ xrpIssue(),
|
||||
/*limitQuality*/ std::nullopt,
|
||||
/*sendMaxIssue*/ EUR,
|
||||
path,
|
||||
true,
|
||||
OfferCrossing::no,
|
||||
ammContext,
|
||||
std::nullopt,
|
||||
env.app().getLogs().journal("Flow"));
|
||||
(void)_;
|
||||
BEAST_EXPECT(ter == tesSUCCESS);
|
||||
}
|
||||
{
|
||||
STPath const path = STPath({ipe(USD), cpe(xrpCurrency())});
|
||||
auto [ter, _] = toStrand(
|
||||
*env.current(),
|
||||
alice,
|
||||
alice,
|
||||
/*deliver*/ xrpIssue(),
|
||||
/*limitQuality*/ std::nullopt,
|
||||
/*sendMaxIssue*/ EUR,
|
||||
path,
|
||||
true,
|
||||
OfferCrossing::no,
|
||||
ammContext,
|
||||
std::nullopt,
|
||||
env.app().getLogs().journal("Flow"));
|
||||
(void)_;
|
||||
BEAST_EXPECT(ter == tesSUCCESS);
|
||||
}
|
||||
};
|
||||
testHelper2TokensMix(testMultiToken);
|
||||
}
|
||||
{
|
||||
auto testMultiToken = [&](auto&& issue1, auto&& issue2) {
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10'000), alice, bob, carol, gw);
|
||||
auto USD = issue1({.env = env, .token = "USD", .issuer = gw, .limit = 1'000});
|
||||
using tUSD = std::decay_t<decltype(USD)>;
|
||||
auto EUR = issue2({.env = env, .token = "EUR", .issuer = gw, .limit = 1'000});
|
||||
using tEUR = std::decay_t<decltype(EUR)>;
|
||||
|
||||
auto const err = [&]() {
|
||||
if constexpr (std::is_same_v<tUSD, MPT>)
|
||||
{
|
||||
return tecNO_AUTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
return terNO_LINE;
|
||||
}
|
||||
}();
|
||||
test(env, USD, std::nullopt, STPath(), err);
|
||||
|
||||
if constexpr (std::is_same_v<tUSD, MPT>)
|
||||
{
|
||||
MPTTester(env, gw, USD).authorizeHolders({alice, bob, carol});
|
||||
}
|
||||
else
|
||||
{
|
||||
env.trust(USD(1'000), alice, bob, carol);
|
||||
}
|
||||
|
||||
test(env, USD, std::nullopt, STPath(), tecPATH_DRY);
|
||||
|
||||
env(pay(gw, alice, USD(100)));
|
||||
env(pay(gw, carol, USD(100)));
|
||||
|
||||
// Insert implied account
|
||||
test(
|
||||
env,
|
||||
USD,
|
||||
std::nullopt,
|
||||
STPath(),
|
||||
tesSUCCESS,
|
||||
makeEndpointStep(alice, gw, USD),
|
||||
makeEndpointStep(gw, bob, USD));
|
||||
if constexpr (std::is_same_v<tEUR, MPT>)
|
||||
{
|
||||
MPTTester(env, gw, EUR).authorizeHolders({alice, bob});
|
||||
}
|
||||
else
|
||||
{
|
||||
env.trust(EUR(1'000), alice, bob);
|
||||
}
|
||||
|
||||
// Insert implied offer
|
||||
test(
|
||||
env,
|
||||
EUR,
|
||||
USD,
|
||||
STPath(),
|
||||
tesSUCCESS,
|
||||
makeEndpointStep(alice, gw, USD),
|
||||
B{USD, EUR, std::nullopt},
|
||||
makeEndpointStep(gw, bob, EUR));
|
||||
|
||||
// Path with explicit offer
|
||||
test(
|
||||
env,
|
||||
EUR,
|
||||
USD,
|
||||
STPath({ipe(EUR)}),
|
||||
tesSUCCESS,
|
||||
makeEndpointStep(alice, gw, USD),
|
||||
B{USD, EUR, std::nullopt},
|
||||
makeEndpointStep(gw, bob, EUR));
|
||||
|
||||
// Path with XRP src currency
|
||||
test(
|
||||
env,
|
||||
USD,
|
||||
xrpIssue(),
|
||||
STPath({ipe(USD)}),
|
||||
tesSUCCESS,
|
||||
XRPS{alice},
|
||||
B{XRP, USD, std::nullopt},
|
||||
makeEndpointStep(gw, bob, USD));
|
||||
|
||||
// Path with XRP dst currency.
|
||||
test(
|
||||
env,
|
||||
xrpIssue(),
|
||||
USD,
|
||||
STPath({STPathElement{
|
||||
STPathElement::typeCurrency, xrpAccount(), xrpCurrency(), xrpAccount()}}),
|
||||
tesSUCCESS,
|
||||
makeEndpointStep(alice, gw, USD),
|
||||
B{USD, XRP, std::nullopt},
|
||||
XRPS{bob});
|
||||
|
||||
// Path with XRP cross currency bridged payment
|
||||
test(
|
||||
env,
|
||||
EUR,
|
||||
USD,
|
||||
STPath({cpe(xrpCurrency())}),
|
||||
tesSUCCESS,
|
||||
makeEndpointStep(alice, gw, USD),
|
||||
B{USD, XRP, std::nullopt},
|
||||
B{XRP, EUR, std::nullopt},
|
||||
makeEndpointStep(gw, bob, EUR));
|
||||
|
||||
// Create an offer with the same in/out issue
|
||||
test(env, EUR, USD, STPath({ipe(USD), ipe(EUR)}), temBAD_PATH);
|
||||
|
||||
// The same offer can't appear more than once on a path
|
||||
test(env, EUR, USD, STPath({ipe(EUR), ipe(USD), ipe(EUR)}), temBAD_PATH_LOOP);
|
||||
};
|
||||
testHelper2TokensMix(testMultiToken);
|
||||
}
|
||||
|
||||
{
|
||||
// cannot have more than one offer with the same output issue
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
auto testMultiToken = [&](auto&& issue1, auto&& issue2) {
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10'000), alice, bob, carol, gw);
|
||||
|
||||
auto const USD = issue1(
|
||||
{.env = env,
|
||||
.token = "USD",
|
||||
.issuer = gw,
|
||||
.holders = {alice, bob, carol},
|
||||
.limit = 10'000});
|
||||
auto const EUR = issue2(
|
||||
{.env = env,
|
||||
.token = "EUR",
|
||||
.issuer = gw,
|
||||
.holders = {alice, bob, carol},
|
||||
.limit = 10'000});
|
||||
|
||||
env(pay(gw, bob, USD(100)));
|
||||
env(pay(gw, bob, EUR(100)));
|
||||
|
||||
env(offer(bob, XRP(100), USD(100)));
|
||||
env(offer(bob, USD(100), EUR(100)), txflags(tfPassive));
|
||||
env(offer(bob, EUR(100), USD(100)), txflags(tfPassive));
|
||||
|
||||
// payment path: XRP -> XRP/USD -> USD/EUR -> EUR/USD
|
||||
env(pay(alice, carol, USD(100)),
|
||||
path(~USD, ~EUR, ~USD),
|
||||
sendmax(XRP(200)),
|
||||
txflags(tfNoRippleDirect),
|
||||
ter(temBAD_PATH_LOOP));
|
||||
};
|
||||
testHelper2TokensMix(testMultiToken);
|
||||
}
|
||||
|
||||
{
|
||||
// check global freeze
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10000), alice, bob, gw);
|
||||
auto USDM = MPTTester(
|
||||
{.env = env,
|
||||
.issuer = gw,
|
||||
.holders = {alice, bob},
|
||||
.flags = MPTDEXFlags | tfMPTCanLock,
|
||||
.maxAmt = 1'000});
|
||||
MPT const USD = USDM;
|
||||
env(pay(gw, alice, USD(100)));
|
||||
|
||||
// Account can't issue payments
|
||||
USDM.set({.holder = alice, .flags = tfMPTLock});
|
||||
test(env, USD, std::nullopt, STPath(), terLOCKED);
|
||||
USDM.set({.holder = alice, .flags = tfMPTUnlock});
|
||||
test(env, USD, std::nullopt, STPath(), tesSUCCESS);
|
||||
|
||||
// Account can not issue funds
|
||||
USDM.set({.flags = tfMPTLock});
|
||||
test(env, USD, std::nullopt, STPath(), terLOCKED);
|
||||
USDM.set({.flags = tfMPTUnlock});
|
||||
test(env, USD, std::nullopt, STPath(), tesSUCCESS);
|
||||
|
||||
// Account can not receive funds
|
||||
USDM.set({.holder = bob, .flags = tfMPTLock});
|
||||
test(env, USD, std::nullopt, STPath(), terLOCKED);
|
||||
USDM.set({.holder = bob, .flags = tfMPTUnlock});
|
||||
test(env, USD, std::nullopt, STPath(), tesSUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
// check no auth
|
||||
// An account may require authorization to receive MPTs from an
|
||||
// issuer
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10'000), alice, bob, gw);
|
||||
auto USDM = MPTTester(
|
||||
{.env = env,
|
||||
.issuer = gw,
|
||||
.flags = MPTDEXFlags | tfMPTRequireAuth,
|
||||
.maxAmt = 1'000});
|
||||
MPT const USD = USDM;
|
||||
|
||||
// Authorize alice but not bob
|
||||
USDM.authorize({.account = alice});
|
||||
USDM.authorize({.holder = alice});
|
||||
env(pay(gw, alice, USD(100)));
|
||||
env.require(balance(alice, USD(100)));
|
||||
test(env, USD, std::nullopt, STPath(), tecNO_AUTH);
|
||||
|
||||
// Check pure issue redeem still works
|
||||
auto [ter, strand] = toStrand(
|
||||
*env.current(),
|
||||
alice,
|
||||
gw,
|
||||
USD,
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
STPath(),
|
||||
true,
|
||||
OfferCrossing::no,
|
||||
ammContext,
|
||||
std::nullopt,
|
||||
env.app().getLogs().journal("Flow"));
|
||||
BEAST_EXPECT(ter == tesSUCCESS);
|
||||
BEAST_EXPECT(equal(strand, M{alice, gw, USD}));
|
||||
}
|
||||
|
||||
{
|
||||
// last step xrp from offer
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10'000), alice, bob, gw);
|
||||
MPT const USD =
|
||||
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt = 1'000});
|
||||
env(pay(gw, alice, USD(100)));
|
||||
|
||||
// alice -> USD/XRP -> bob
|
||||
STPath path;
|
||||
path.emplace_back(std::nullopt, xrpCurrency(), std::nullopt);
|
||||
|
||||
auto [ter, strand] = toStrand(
|
||||
*env.current(),
|
||||
alice,
|
||||
bob,
|
||||
XRP,
|
||||
std::nullopt,
|
||||
USD,
|
||||
path,
|
||||
false,
|
||||
OfferCrossing::no,
|
||||
ammContext,
|
||||
std::nullopt,
|
||||
env.app().getLogs().journal("Flow"));
|
||||
BEAST_EXPECT(ter == tesSUCCESS);
|
||||
BEAST_EXPECT(
|
||||
equal(strand, M{alice, gw, USD}, B{USD, xrpIssue(), std::nullopt}, XRPS{bob}));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testRIPD1373(FeatureBitset features)
|
||||
{
|
||||
using namespace jtx;
|
||||
testcase("RIPD1373");
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const carol = Account("carol");
|
||||
auto const gw = Account("gw");
|
||||
|
||||
{
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
MPT const USD = MPTTester(
|
||||
{.env = env, .issuer = gw, .holders = {alice, bob, carol}, .maxAmt = 10'000});
|
||||
|
||||
env(pay(gw, bob, USD(100)));
|
||||
|
||||
env(offer(bob, XRP(100), USD(100)), txflags(tfPassive));
|
||||
env(offer(bob, USD(100), XRP(100)), txflags(tfPassive));
|
||||
|
||||
// payment path: XRP -> XRP/USD -> USD/XRP
|
||||
env(pay(alice, carol, XRP(100)),
|
||||
path(~USD, ~XRP),
|
||||
txflags(tfNoRippleDirect),
|
||||
ter(temBAD_SEND_XRP_PATHS));
|
||||
}
|
||||
|
||||
{
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
MPT const USD = MPTTester(
|
||||
{.env = env, .issuer = gw, .holders = {alice, bob, carol}, .maxAmt = 10'000});
|
||||
|
||||
env(pay(gw, bob, USD(100)));
|
||||
|
||||
env(offer(bob, XRP(100), USD(100)), txflags(tfPassive));
|
||||
env(offer(bob, USD(100), XRP(100)), txflags(tfPassive));
|
||||
|
||||
// payment path: XRP -> XRP/USD -> USD/XRP
|
||||
env(pay(alice, carol, XRP(100)),
|
||||
path(~USD, ~XRP),
|
||||
sendmax(XRP(200)),
|
||||
txflags(tfNoRippleDirect),
|
||||
ter(temBAD_SEND_XRP_MAX));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testLoop(FeatureBitset features)
|
||||
{
|
||||
testcase("test loop");
|
||||
using namespace jtx;
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const carol = Account("carol");
|
||||
auto const gw = Account("gw");
|
||||
auto const EUR = gw["EUR"];
|
||||
auto const CNY = gw["CNY"];
|
||||
|
||||
{
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10'000), alice, bob, carol, gw);
|
||||
MPT const USD = MPTTester(
|
||||
{.env = env, .issuer = gw, .holders = {alice, bob, carol}, .maxAmt = 10'000});
|
||||
|
||||
env(pay(gw, bob, USD(100)));
|
||||
env(pay(gw, alice, USD(100)));
|
||||
|
||||
env(offer(bob, XRP(100), USD(100)), txflags(tfPassive));
|
||||
env(offer(bob, USD(100), XRP(100)), txflags(tfPassive));
|
||||
|
||||
// payment path: USD -> USD/XRP -> XRP/USD
|
||||
env(pay(alice, carol, USD(100)),
|
||||
sendmax(USD(100)),
|
||||
path(~XRP, ~USD),
|
||||
txflags(tfNoRippleDirect),
|
||||
ter(temBAD_PATH_LOOP));
|
||||
}
|
||||
{
|
||||
auto testMultiToken = [&](auto&& issue1, auto&& issue2, auto&& issue3) {
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10'000), alice, bob, carol, gw);
|
||||
auto const USD = issue1(
|
||||
{.env = env,
|
||||
.token = "USD",
|
||||
.issuer = gw,
|
||||
.holders = {alice, bob, carol},
|
||||
.limit = 10'000});
|
||||
auto const EUR = issue2(
|
||||
{.env = env,
|
||||
.token = "EUR",
|
||||
.issuer = gw,
|
||||
.holders = {alice, bob, carol},
|
||||
.limit = 10'000});
|
||||
auto const CNY = issue3(
|
||||
{.env = env,
|
||||
.token = "CNY",
|
||||
.issuer = gw,
|
||||
.holders = {alice, bob, carol},
|
||||
.limit = 10'000});
|
||||
|
||||
env(pay(gw, bob, USD(100)));
|
||||
env(pay(gw, bob, EUR(100)));
|
||||
env(pay(gw, bob, CNY(100)));
|
||||
|
||||
env(offer(bob, XRP(100), USD(100)), txflags(tfPassive));
|
||||
env(offer(bob, USD(100), EUR(100)), txflags(tfPassive));
|
||||
env(offer(bob, EUR(100), CNY(100)), txflags(tfPassive));
|
||||
|
||||
// payment path: XRP->XRP/USD->USD/EUR->USD/CNY
|
||||
env(pay(alice, carol, CNY(100)),
|
||||
sendmax(XRP(100)),
|
||||
path(~USD, ~EUR, ~USD, ~CNY),
|
||||
txflags(tfNoRippleDirect),
|
||||
ter(temBAD_PATH_LOOP));
|
||||
};
|
||||
testHelper3TokensMix(testMultiToken);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testNoAccount(FeatureBitset features)
|
||||
{
|
||||
testcase("test no account");
|
||||
using namespace jtx;
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const gw = Account("gw");
|
||||
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10'000), alice, bob, gw);
|
||||
MPT const USD = MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}});
|
||||
|
||||
STAmount const sendMax{USD, 100, 1};
|
||||
STAmount const noAccountAmount{MPTIssue{0, noAccount()}, 100, 1};
|
||||
STAmount const deliver;
|
||||
AccountID const srcAcc = alice.id();
|
||||
AccountID const dstAcc = bob.id();
|
||||
STPathSet const pathSet;
|
||||
xrpl::path::RippleCalc::Input inputs;
|
||||
inputs.defaultPathsAllowed = true;
|
||||
try
|
||||
{
|
||||
PaymentSandbox sb{env.current().get(), tapNONE};
|
||||
{
|
||||
auto const r = ::xrpl::path::RippleCalc::rippleCalculate(
|
||||
sb,
|
||||
sendMax,
|
||||
deliver,
|
||||
dstAcc,
|
||||
noAccount(),
|
||||
pathSet,
|
||||
std::nullopt,
|
||||
env.app(),
|
||||
&inputs);
|
||||
BEAST_EXPECT(r.result() == temBAD_PATH);
|
||||
}
|
||||
{
|
||||
auto const r = ::xrpl::path::RippleCalc::rippleCalculate(
|
||||
sb,
|
||||
sendMax,
|
||||
deliver,
|
||||
noAccount(),
|
||||
srcAcc,
|
||||
pathSet,
|
||||
std::nullopt,
|
||||
env.app(),
|
||||
&inputs);
|
||||
BEAST_EXPECT(r.result() == temBAD_PATH);
|
||||
}
|
||||
{
|
||||
auto const r = ::xrpl::path::RippleCalc::rippleCalculate(
|
||||
sb,
|
||||
noAccountAmount,
|
||||
deliver,
|
||||
dstAcc,
|
||||
srcAcc,
|
||||
pathSet,
|
||||
std::nullopt,
|
||||
env.app(),
|
||||
&inputs);
|
||||
BEAST_EXPECT(r.result() == temBAD_PATH);
|
||||
}
|
||||
{
|
||||
auto const r = ::xrpl::path::RippleCalc::rippleCalculate(
|
||||
sb,
|
||||
sendMax,
|
||||
noAccountAmount,
|
||||
dstAcc,
|
||||
srcAcc,
|
||||
pathSet,
|
||||
std::nullopt,
|
||||
env.app(),
|
||||
&inputs);
|
||||
BEAST_EXPECT(r.result() == temBAD_PATH);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
this->fail();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using namespace jtx;
|
||||
auto const sa = testable_amendments();
|
||||
testToStrand(sa);
|
||||
|
||||
testRIPD1373(sa);
|
||||
|
||||
testLoop(sa);
|
||||
|
||||
testNoAccount(sa);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(PayStrandMPT, app, xrpl);
|
||||
|
||||
} // namespace test
|
||||
} // namespace xrpl
|
||||
Reference in New Issue
Block a user