892 auto const mpTokensV2 = features[featureMPTokensV2];
898 Env env{*
this, features - featureMPTokensV1};
911 Env env{*
this, features - featureMPTokensV1};
914 auto const usd = alice[
"USD"];
921 jv[jss::secret] = alice.
name();
922 jv[jss::tx_json] =
pay(alice, carol, mpt);
924 auto const jrr = env.
rpc(
"json",
"submit",
to_string(jv));
925 BEAST_EXPECT(jrr[jss::result][jss::engine_result] ==
"temDISABLED");
930 Env env{*
this, features};
932 MPTTester mptAlice(env, alice, {.holders = {bob}});
934 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
935 auto const mpt = mptAlice[
"MPT"];
937 mptAlice.authorize({.account = bob});
940 env(
pay(alice, bob, mpt(10)),
Txflags(tfNoRippleDirect), err);
942 env(
pay(alice, bob, mpt(10)),
Txflags(tfLimitQuality), err);
947 Env env{*
this, features};
951 MPTTester mptAlice(env, alice, {.holders = {carol}});
954 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
956 mptAlice.authorize({.account = carol});
960 auto const mpt = mptAlice[
"MPT"];
965 auto const usd = alice[
"USD"];
967 env(
pay(alice, carol, usd(100)),
Sendmax(mpt(100)), err);
975 env(
pay(alice, carol, mpT1(100)),
Sendmax(mpt(100)), err);
978 env(
pay(alice, carol, mpt(100)),
Path(~usd), err);
983 Env env{*
this, features - featureMPTokensV2};
987 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
989 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
990 auto const mpt = mptAlice[
"MPT"];
992 mptAlice.authorize({.account = carol});
995 payment[jss::secret] = alice.
name();
996 payment[jss::tx_json] =
pay(alice, carol, mpt(100));
998 payment[jss::build_path] =
true;
999 auto jrr = env.
rpc(
"json",
"submit",
to_string(payment));
1000 BEAST_EXPECT(jrr[jss::result][jss::error] ==
"invalidParams");
1002 jrr[jss::result][jss::error_message] ==
1003 "Field 'build_path' not allowed in this context.");
1008 Env env{*
this, features};
1010 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1012 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1013 auto const mpt = mptAlice[
"MPT"];
1015 mptAlice.authorize({.account = bob});
1016 mptAlice.authorize({.account = carol});
1029 Env env{*
this, features};
1031 MPTTester mptAlice(env, alice, {.holders = {bob}});
1033 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1035 mptAlice.authorize({.account = bob});
1044 Env env{*
this, features};
1046 MPTTester mptAlice(env, alice, {.holders = {bob}});
1048 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1050 mptAlice.authorize({.account = bob});
1063 Env env{*
this, features};
1065 MPTTester mptAlice(env, alice, {.holders = {bob}});
1068 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer});
1070 mptAlice.authorize({.account = bob});
1078 Env env{*
this, features};
1080 MPTTester mptAlice(env, alice, {.holders = {bob}});
1083 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer});
1086 mptAlice.authorize({.account = bob});
1089 mptAlice.authorize({.account = alice, .holder = bob});
1092 mptAlice.pay(alice, bob, 100);
1095 mptAlice.authorize({.account = alice, .holder = bob, .flags = tfMPTUnauthorize});
1102 if (features[featureSingleAssetVault] && features[featurePermissionedDomains])
1106 Env env{*
this, features};
1108 Account const credIssuer1{
"credIssuer1"};
1109 env.
fund(
XRP(1000), credIssuer1, bob);
1111 auto const domainId1 = [&]() {
1113 {.issuer = credIssuer1, .credType = credType}};
1132 .flags = tfMPTRequireAuth | tfMPTCanTransfer,
1133 .domainID = domainId1,
1136 mptAlice.authorize({.account = bob});
1140 mptAlice.pay(alice, bob, 100);
1141 mptAlice.set({.domainID =
uint256{}});
1148 Env env{*
this, features};
1150 Account const credIssuer1{
"credIssuer1"};
1151 env.
fund(
XRP(1000), credIssuer1, bob);
1153 auto const domainId1 = [&]() {
1155 {.issuer = credIssuer1, .credType = credType}};
1174 .flags = tfMPTRequireAuth | tfMPTCanTransfer,
1175 .domainID = domainId1,
1179 mptAlice.authorize({.account = bob});
1182 mptAlice.authorize({.account = alice, .holder = bob});
1185 mptAlice.pay(alice, bob, 100);
1188 mptAlice.authorize({.account = alice, .holder = bob, .flags = tfMPTUnauthorize});
1191 mptAlice.pay(bob, alice, 10);
1193 mptAlice.set({.domainID =
uint256{}});
1201 Env env{*
this, features};
1204 Account const credIssuer1{
"credIssuer1"};
1207 Account const credIssuer2{
"credIssuer2"};
1208 env.
fund(
XRP(1000), credIssuer1, credIssuer2, bob, carol);
1210 auto const domainId1 = [&]() {
1212 {.issuer = credIssuer1, .credType = credType}};
1221 auto const domainId2 = [&]() {
1223 {.issuer = credIssuer1, .credType = credType},
1224 {.issuer = credIssuer2, .credType = credType}};
1251 .flags = tfMPTRequireAuth | tfMPTCanTransfer,
1252 .domainID = domainId1,
1256 mptAlice.authorize({.account = bob});
1257 mptAlice.authorize({.account = carol});
1261 mptAlice.pay(alice, bob, 50);
1272 mptAlice.set({.domainID = domainId2});
1274 mptAlice.pay(alice, carol, 10);
1279 mptAlice.pay(bob, carol, 10);
1293 Env env(*
this, features);
1298 MPTTester mptAlice(env, alice, {.holders = {bob, cindy}});
1301 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1304 mptAlice.authorize({.account = bob});
1307 mptAlice.authorize({.account = cindy});
1310 mptAlice.pay(alice, bob, 100);
1317 mptAlice.pay(bob, alice, 10);
1322 Env env{*
this, features};
1324 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1326 mptAlice.
create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
1340 Env env{*
this, features};
1342 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1344 mptAlice.
create({.ownerCount = 1, .flags = tfMPTCanTransfer});
1346 mptAlice.authorize({.account = bob});
1347 mptAlice.authorize({.account = carol});
1349 mptAlice.pay(alice, bob, 100);
1360 Env env{*
this, features};
1362 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1364 mptAlice.
create({.ownerCount = 1, .flags = tfMPTCanLock | tfMPTCanTransfer});
1366 mptAlice.authorize({.account = bob});
1367 mptAlice.authorize({.account = carol});
1369 mptAlice.pay(alice, bob, 100);
1370 mptAlice.pay(alice, carol, 100);
1376 mptAlice.set({.account = alice, .flags = tfMPTLock});
1378 mptAlice.pay(bob, carol, 1, err);
1379 mptAlice.pay(carol, bob, 2, err);
1381 mptAlice.pay(alice, bob, 3);
1383 mptAlice.pay(bob, alice, 4);
1386 mptAlice.set({.account = alice, .flags = tfMPTUnlock});
1388 mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
1390 mptAlice.pay(bob, carol, 5, err);
1391 mptAlice.pay(carol, bob, 6, err);
1393 mptAlice.pay(alice, bob, 7);
1395 mptAlice.pay(bob, alice, 8);
1400 Env env{*
this, features};
1402 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1406 {.transferFee = 10'000,
1409 .flags = tfMPTCanTransfer});
1412 mptAlice.authorize({.account = bob});
1413 mptAlice.authorize({.account = carol});
1416 mptAlice.pay(alice, bob, 2'000);
1419 mptAlice.pay(bob, alice, 1'000);
1420 BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 1'000));
1430 auto const mpt = mptAlice[
"MPT"];
1436 env(
pay(bob, carol, mpt(100)),
Sendmax(mpt(110)));
1438 env(
pay(bob, carol, mpt(100)),
Sendmax(mpt(115)));
1439 BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 780));
1440 BEAST_EXPECT(mptAlice.checkMPTokenAmount(carol, 200));
1446 BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 690));
1450 mptAlice.checkMPTokenAmount(carol, !features[featureMPTokensV2] ? 282 : 281));
1455 Env env{*
this, features};
1457 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1459 mptAlice.
create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
1462 mptAlice.authorize({.account = bob});
1463 mptAlice.authorize({.account = carol});
1464 mptAlice.pay(alice, bob, 1'000);
1466 auto const mpt = mptAlice[
"MPT"];
1472 env(
pay(bob, carol, mpt(100)),
Sendmax(mpt(100)));
1473 BEAST_EXPECT(mptAlice.checkMPTokenAmount(carol, 100));
1476 BEAST_EXPECT(mptAlice.checkMPTokenAmount(carol, 199));
1481 Env env{*
this, features};
1483 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1485 mptAlice.
create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
1488 mptAlice.authorize({.account = bob});
1489 mptAlice.authorize({.account = carol});
1490 mptAlice.pay(alice, bob, 1'000);
1492 auto const mpt = mptAlice[
"MPT"];
1495 env(
pay(bob, alice, mpt(100)),
1501 env(
pay(bob, alice, mpt(100)),
1509 Env env{*
this, features};
1511 MPTTester mptAlice(env, alice, {.holders = {bob}});
1514 {.maxAmt = 100, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
1516 mptAlice.authorize({.account = bob});
1519 mptAlice.pay(alice, bob, 100);
1523 mptAlice.pay(alice, bob, 1, err);
1529 Env env{*
this, features};
1531 MPTTester mptAlice(env, alice, {.holders = {bob}});
1533 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1535 mptAlice.authorize({.account = bob});
1542 mptAlice.pay(alice, bob, 1, err);
1548 Env env{*
this, features};
1549 env.
fund(
XRP(1'000), alice, bob);
1552 jv[jss::secret] = alice.
name();
1553 jv[jss::tx_json] =
pay(alice, bob, mpt);
1555 auto const jrr = env.
rpc(
"json",
"submit",
to_string(jv));
1556 BEAST_EXPECT(jrr[jss::result][jss::error] ==
"invalidParams");
1562 Env env{*
this, features};
1564 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1571 .flags = tfMPTCanTransfer});
1572 auto const mpt = mptAlice[
"MPT"];
1574 mptAlice.authorize({.account = bob});
1575 mptAlice.authorize({.account = carol});
1578 mptAlice.pay(alice, bob, 10'000);
1581 env(
pay(bob, carol, mpt(10'000)),
Sendmax(mpt(10'000)),
Txflags(tfPartialPayment));
1587 meta[0u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
1588 [sfOutstandingAmount.fieldName] ==
"9990");
1591 meta[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
1592 [sfMPTAmount.fieldName] ==
"9990");
1595 meta[2u][sfModifiedNode.fieldName][sfPreviousFields.fieldName]
1596 [sfMPTAmount.fieldName] ==
"10000");
1597 BEAST_EXPECT(!meta[2u][sfModifiedNode.fieldName][sfFinalFields.fieldName].isMember(
1598 sfMPTAmount.fieldName));
1603 env(
pay(bob, carol, mpt(10'000)),
Sendmax(mpt(10'000)),
Ter(err));
1608 Env env{*
this, features};
1610 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1616 .flags = tfMPTCanTransfer});
1617 auto const mpt = mptAlice[
"MPT"];
1619 mptAlice.authorize({.account = bob});
1620 mptAlice.authorize({.account = carol});
1631 BEAST_EXPECT(mptAlice.checkMPTokenOutstandingAmount(0));
1636 Env env{*
this, features};
1638 MPTTester mptAlice(env, alice, {.holders = {bob}});
1640 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1642 mptAlice.authorize({.account = bob});
1645 mptAlice.destroy({.ownerCount = 0});
1654 Env env{*
this, features};
1656 env.
fund(
XRP(1'000), alice, bob);
1661 env(
pay(alice, bob, mpt), err);
1667 Env env{*
this, features};
1669 MPTTester mptAlice(env, alice, {.holders = {bob}});
1671 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1674 mptAlice.destroy({.ownerCount = 0});
1684 Env env{*
this, features};
1689 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1691 mptAlice.
create({.maxAmt = 100, .ownerCount = 1, .flags = tfMPTCanTransfer});
1693 mptAlice.authorize({.account = bob});
1694 mptAlice.authorize({.account = carol});
1696 mptAlice.pay(alice, bob, 100);
1699 mptAlice.pay(bob, carol, 100);
1704 Env env{*
this, features};
1706 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1708 mptAlice.
create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
1710 mptAlice.authorize({.account = bob});
1711 mptAlice.authorize({.account = carol});
1714 mptAlice.pay(alice, bob, 100);
1717 mptAlice.pay(bob, alice, 100);
1720 mptAlice.pay(alice, bob, 100);
1721 mptAlice.pay(bob, carol, 50);
2132 FeatureBitset const withoutFix = features - fixCleanup3_2_0;
2134 FeatureBitset const withoutFixAndV2 = withoutFix - featureMPTokensV2;
2135 FeatureBitset const withFixAndWithoutV2 = withFix - featureMPTokensV2;
2146 MPTValue
const alice0 = 10'000;
2147 MPTValue
const gw0 = -20'000;
2159 TER issuerToHolderPreFixTer;
2160 TER holderSourcePreFixTer;
2161 MPTValue issuerToHolderAliceAfterPreFix;
2162 MPTValue issuerToHolderIssuerAfterPreFix;
2163 MPTValue issuerToHolderAliceAfterPostFix;
2164 MPTValue issuerToHolderIssuerAfterPostFix;
2168 { .name=
"INT64_MAX + 1", .mantissa=firstInvalidMPTMantissa, .negative=
false, .mptValue=mptMin, .issuerToHolderPreFixTer=invariantFailed, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0, .issuerToHolderIssuerAfterPreFix=gw0, .issuerToHolderAliceAfterPostFix=alice0 - 1, .issuerToHolderIssuerAfterPostFix=gw0 + 1},
2169 { .name=
"INT64_MAX + 10", .mantissa=firstInvalidMPTMantissa + 9, .negative=
false, .mptValue=mptMin + 9, .issuerToHolderPreFixTer=invariantFailed, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0, .issuerToHolderIssuerAfterPreFix=gw0, .issuerToHolderAliceAfterPostFix=alice0 - 1, .issuerToHolderIssuerAfterPostFix=gw0 + 1},
2170 { .name=
"UINT64_MAX - 9998", .mantissa=u64Max - 9'998, .negative=
false, .mptValue=MPTValue{-9'999}, .issuerToHolderPreFixTer=success, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0 - 9'999, .issuerToHolderIssuerAfterPreFix=gw0 + 9'999, .issuerToHolderAliceAfterPostFix=alice0 - 10'000, .issuerToHolderIssuerAfterPostFix=gw0 + 10'000},
2171 { .name=
"UINT64_MAX - 9", .mantissa=u64Max - 9, .negative=
false, .mptValue=MPTValue{-10}, .issuerToHolderPreFixTer=success, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0 - 10, .issuerToHolderIssuerAfterPreFix=gw0 + 10, .issuerToHolderAliceAfterPostFix=alice0 - 11, .issuerToHolderIssuerAfterPostFix=gw0 + 11},
2172 { .name=
"UINT64_MAX - 1", .mantissa=u64Max - 1, .negative=
false, .mptValue=MPTValue{-2}, .issuerToHolderPreFixTer=success, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0 - 2, .issuerToHolderIssuerAfterPreFix=gw0 + 2, .issuerToHolderAliceAfterPostFix=alice0 - 3, .issuerToHolderIssuerAfterPostFix=gw0 + 3},
2173 { .name=
"UINT64_MAX", .mantissa=u64Max, .negative=
false, .mptValue=MPTValue{-1}, .issuerToHolderPreFixTer=success, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0 - 1, .issuerToHolderIssuerAfterPreFix=gw0 + 1, .issuerToHolderAliceAfterPostFix=alice0 - 2, .issuerToHolderIssuerAfterPostFix=gw0 + 2},
2174 { .name=
"-2", .mantissa=
std::uint64_t{2}, .negative=
true, .mptValue=MPTValue{-2}, .issuerToHolderPreFixTer=badAmountTer, .holderSourcePreFixTer=badAmountTer, .issuerToHolderAliceAfterPreFix=alice0, .issuerToHolderIssuerAfterPreFix=gw0, .issuerToHolderAliceAfterPostFix=alice0 - 1, .issuerToHolderIssuerAfterPostFix=gw0 + 1}
2177 auto const badMPTAmount = [&](
MPTIssue const& issue, BadMPTAmount
const& bad) {
2180 auto const makeIssue = [&](
Env& env) {
2184 .holders = {alice, bob},
2186 .flags = tfMPTCanTransfer | tfMPTCanTrade | tfMPTCanEscrow | tfMPTCanClawback}};
2189 auto const withNonCanonicalMPTAmount =
2193 tx.
sign(signer.pk(), signer.sk());
2197 auto const roundTrip = [](
STTx const& tx) {
2203 auto const expectRoundTripBadMPT =
2204 [&](
JTx const& jt,
SField const& field, BadMPTAmount
const& bad) {
2205 auto const roundTripped = roundTrip(*jt.
stx);
2206 auto const persisted = roundTripped.getFieldAmount(field);
2207 BEAST_EXPECT(persisted.holds<
MPTIssue>());
2208 BEAST_EXPECT(persisted.mantissa() == bad.mantissa);
2209 BEAST_EXPECT(persisted.exponent() == 0);
2210 BEAST_EXPECT(persisted.negative() == bad.negative);
2211 BEAST_EXPECT(persisted.mpt().value() == bad.mptValue);
2216 for (
auto const& bad : badMPTAmounts)
2218 testcase(
"fixCleanup3_2_0 rejects non-canonical MPT Payment amounts");
2220 Env env{*
this, withoutFixAndV2};
2221 env.
fund(
XRP(100'000), alice, bob, gw);
2223 auto const issue = makeIssue(env);
2225 auto const badAmount = badMPTAmount(issue, bad);
2226 auto malformedHolderToHolder = withNonCanonicalMPTAmount(
2231 expectRoundTripBadMPT(malformedHolderToHolder, sfAmount, bad);
2232 malformedHolderToHolder.ter = bad.holderSourcePreFixTer;
2233 env.
submit(malformedHolderToHolder);
2238 (env.balance(bob, issue).value() ==
STAmount{MPTAmount{10'000}, issue}));
2240 (env.balance(gw, issue).value() ==
STAmount{MPTAmount{-20'000}, issue}));
2242 env.enableFeature(fixCleanup3_2_0);
2247 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'001}, issue}));
2249 (env.balance(bob, issue).value() == STAmount{MPTAmount{9'999}, issue}));
2251 (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue}));
2255 env.fund(
XRP(100'000), alice, bob, gw);
2257 auto const issue = makeIssue(env);
2259 auto const badAmount = badMPTAmount(issue, bad);
2260 auto malformedIssuerToHolder = withNonCanonicalMPTAmount(
2265 expectRoundTripBadMPT(malformedIssuerToHolder, sfAmount, bad);
2266 malformedIssuerToHolder.ter = bad.issuerToHolderPreFixTer;
2267 env.submit(malformedIssuerToHolder);
2270 (env.balance(alice, issue).value() ==
2271 STAmount{MPTAmount{bad.issuerToHolderAliceAfterPreFix}, issue}));
2273 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2275 (env.balance(gw, issue).value() ==
2276 STAmount{MPTAmount{bad.issuerToHolderIssuerAfterPreFix}, issue}));
2278 env.enableFeature(fixCleanup3_2_0);
2283 (env.balance(alice, issue).value() ==
2284 STAmount{MPTAmount{bad.issuerToHolderAliceAfterPostFix}, issue}));
2286 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2288 (env.balance(gw, issue).value() ==
2289 STAmount{MPTAmount{bad.issuerToHolderIssuerAfterPostFix}, issue}));
2292 Env env{*
this, withoutFixAndV2};
2293 env.fund(
XRP(100'000), alice, bob, gw);
2295 auto const issue = makeIssue(env);
2297 auto const badAmount = badMPTAmount(issue, bad);
2298 auto malformedHolderToIssuer = withNonCanonicalMPTAmount(
2303 expectRoundTripBadMPT(malformedHolderToIssuer, sfAmount, bad);
2304 malformedHolderToIssuer.ter = bad.holderSourcePreFixTer;
2305 env.submit(malformedHolderToIssuer);
2308 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2310 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2312 (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue}));
2314 env.enableFeature(fixCleanup3_2_0);
2319 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'001}, issue}));
2321 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2323 (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'001}, issue}));
2326 Env env{*
this, withFixAndWithoutV2};
2327 env.fund(
XRP(100'000), alice, bob, gw);
2329 auto const issue = makeIssue(env);
2331 auto const badAmount = badMPTAmount(issue, bad);
2332 auto tx = withNonCanonicalMPTAmount(
2341 Env env{*
this, withFixAndWithoutV2};
2342 env.fund(
XRP(100'000), alice, bob, gw);
2344 auto const issue = makeIssue(env);
2346 auto const badAmount = badMPTAmount(issue, bad);
2347 auto tx = withNonCanonicalMPTAmount(
2356 Env env{*
this, withFixAndWithoutV2};
2357 env.fund(
XRP(100'000), alice, bob, gw);
2359 auto const issue = makeIssue(env);
2361 auto const badAmount = badMPTAmount(issue, bad);
2362 auto tx = withNonCanonicalMPTAmount(
2371 testcase(
"fixCleanup3_2_0 rejects non-canonical MPT Check amounts");
2374 env.fund(
XRP(100'000), alice, bob, gw);
2376 auto const issue = makeIssue(env);
2378 auto const badSendMax = badMPTAmount(issue, bad);
2379 auto const checkSeq = env.seq(alice);
2380 auto tx = withNonCanonicalMPTAmount(
2381 env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})),
2389 auto const checkKeylet = keylet::check(alice.id(), SeqProxy::rawSequence(checkSeq));
2390 auto const sleCheck = env.le(checkKeylet);
2391 BEAST_EXPECT((sleCheck !=
nullptr) == !bad.negative);
2392 if (sleCheck && !bad.negative)
2394 auto const persisted = sleCheck->getFieldAmount(sfSendMax);
2395 BEAST_EXPECT(persisted.holds<MPTIssue>());
2396 BEAST_EXPECT(persisted.mantissa() == bad.mantissa);
2397 BEAST_EXPECT(persisted.negative() == bad.negative);
2402 env.fund(
XRP(100'000), alice, bob, gw);
2404 auto const issue = makeIssue(env);
2406 auto const badSendMax = badMPTAmount(issue, bad);
2407 auto const checkSeq = env.seq(alice);
2408 auto tx = withNonCanonicalMPTAmount(
2409 env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})),
2417 auto const checkKeylet = keylet::check(alice.id(), SeqProxy::rawSequence(checkSeq));
2418 BEAST_EXPECT((env.le(checkKeylet) !=
nullptr) == !bad.negative);
2423 env(env.jt(check::cancel(alice, checkKeylet.key)), Ter{tesSUCCESS});
2425 BEAST_EXPECT(env.le(checkKeylet) ==
nullptr);
2430 env.fund(
XRP(100'000), alice, bob, gw);
2432 auto const issue = makeIssue(env);
2434 auto const badSendMax = badMPTAmount(issue, bad);
2435 auto const checkSeq = env.seq(alice);
2436 auto tx = withNonCanonicalMPTAmount(
2437 env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})),
2445 auto const checkKeylet = keylet::check(alice.id(), SeqProxy::rawSequence(checkSeq));
2446 BEAST_EXPECT((env.le(checkKeylet) !=
nullptr) == !bad.negative);
2449 env.enableFeature(fixCleanup3_2_0);
2454 env(env.jt(check::cancel(alice, checkKeylet.key)), Ter{tesSUCCESS});
2456 BEAST_EXPECT(env.le(checkKeylet) ==
nullptr);
2461 env.fund(
XRP(100'000), alice, bob, gw);
2463 auto const issue = makeIssue(env);
2465 auto const badSendMax = badMPTAmount(issue, bad);
2466 auto const checkSeq = env.seq(alice);
2467 auto tx = withNonCanonicalMPTAmount(
2468 env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})),
2476 auto const checkKeylet = keylet::check(alice.id(), SeqProxy::rawSequence(checkSeq));
2477 BEAST_EXPECT((env.le(checkKeylet) !=
nullptr) == !bad.negative);
2480 env.enableFeature(fixCleanup3_2_0);
2483 auto const cashAmount = STAmount{sfAmount, issue,
std::uint64_t{1}, 0,
false};
2484 env(env.jt(check::cash(bob, checkKeylet.key, cashAmount)), Ter{tefBAD_LEDGER});
2486 BEAST_EXPECT(env.le(checkKeylet) !=
nullptr);
2490 Env env{*
this, withoutFix};
2491 env.fund(
XRP(100'000), alice, bob, gw);
2493 auto const issue = makeIssue(env);
2495 auto const sendMax = STAmount{sfSendMax, issue,
std::uint64_t{10}, 0,
false};
2496 auto const checkSeq = env.seq(alice);
2497 env(env.jt(check::create(alice, bob, sendMax)), Ter{tesSUCCESS});
2500 auto const badCashAmount = badMPTAmount(issue, bad);
2501 auto tx = withNonCanonicalMPTAmount(
2505 keylet::check(alice.id(), SeqProxy::rawSequence(checkSeq)).key,
2506 STAmount{issue, std::uint64_t{1}})),
2510 expectRoundTripBadMPT(tx, sfAmount, bad);
2511 tx.ter = bad.holderSourcePreFixTer;
2515 env.le(keylet::check(alice.id(), SeqProxy::rawSequence(checkSeq))) !=
nullptr);
2517 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2519 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2521 (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue}));
2524 Env env{*
this, withFix};
2525 env.fund(
XRP(100'000), alice, bob, gw);
2527 auto const issue = makeIssue(env);
2529 auto const sendMax = STAmount{sfSendMax, issue,
std::uint64_t{10}, 0,
false};
2530 auto const checkSeq = env.seq(alice);
2531 env(env.jt(check::create(alice, bob, sendMax)), Ter{tesSUCCESS});
2534 auto const badCashAmount = badMPTAmount(issue, bad);
2535 auto tx = withNonCanonicalMPTAmount(
2539 keylet::check(alice.id(), SeqProxy::rawSequence(checkSeq)).key,
2540 STAmount{issue, std::uint64_t{1}})),
2548 testcase(
"fixCleanup3_2_0 rejects non-canonical MPT Escrow amounts");
2550 Env env{*
this, withoutFix};
2551 env.fund(
XRP(100'000), alice, bob, gw);
2553 auto const issue = makeIssue(env);
2555 auto const escrowSeq = env.seq(alice);
2556 auto const badAmount = badMPTAmount(issue, bad);
2557 auto tx = withNonCanonicalMPTAmount(
2559 escrow::create(alice, bob, STAmount{issue, std::uint64_t{1}}),
2560 escrow::kFinishTime(env.now() + 1s)),
2568 env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(escrowSeq))) ==
2572 Env env{*
this, withFix};
2573 env.fund(
XRP(100'000), alice, bob, gw);
2575 auto const issue = makeIssue(env);
2577 auto const badAmount = badMPTAmount(issue, bad);
2578 auto tx = withNonCanonicalMPTAmount(
2580 escrow::create(alice, bob, STAmount{issue, std::uint64_t{1}}),
2581 escrow::kFinishTime(env.now() + 1s)),
2589 testcase(
"fixCleanup3_2_0 rejects non-canonical MPT Clawback amounts");
2591 Env env{*
this, withoutFix};
2592 env.fund(
XRP(100'000), alice, bob, gw);
2594 auto const issue = makeIssue(env);
2596 auto const badAmount = badMPTAmount(issue, bad);
2597 auto tx = withNonCanonicalMPTAmount(
2602 expectRoundTripBadMPT(tx, sfAmount, bad);
2607 MPTValue
const bobAfter = bad.negative ? MPTValue{10'000} : MPTValue{0};
2608 MPTValue
const gwAfter = bad.negative ? MPTValue{-20'000} : MPTValue{-10'000};
2610 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2612 (env.balance(bob, issue).value() == STAmount{MPTAmount{bobAfter}, issue}));
2614 (env.balance(gw, issue).value() == STAmount{MPTAmount{gwAfter}, issue}));
2617 Env env{*
this, withFix};
2618 env.fund(
XRP(100'000), alice, bob, gw);
2620 auto const issue = makeIssue(env);
2622 auto const badAmount = badMPTAmount(issue, bad);
2623 auto tx = withNonCanonicalMPTAmount(
2633 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2635 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2637 (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue}));
2640 testcase(
"featureMPTokensV2 disabled rejects MPT OfferCreate amounts");
2642 Env env{*
this, withoutFixAndV2};
2643 env.fund(
XRP(100'000), alice, bob, gw);
2645 auto const issue = makeIssue(env);
2647 auto const badTakerPays = badMPTAmount(issue, bad);
2648 auto tx = withNonCanonicalMPTAmount(
2653 expectRoundTripBadMPT(tx, sfTakerPays, bad);
2658 Env env{*
this, withFixAndWithoutV2};
2659 env.fund(
XRP(100'000), alice, bob, gw);
2661 auto const issue = makeIssue(env);
2663 auto const badTakerPays = badMPTAmount(issue, bad);
2664 auto tx = withNonCanonicalMPTAmount(
2676 Env env{*
this, withFix};
2677 env.fund(
XRP(100'000), alice, bob, gw);
2679 auto const issue = makeIssue(env);
2681 auto const badTakerPays = badMPTAmount(issue, bad);
2682 auto tx = withNonCanonicalMPTAmount(
2694 Env env{*
this, withFix};
2695 env.fund(
XRP(100'000), alice, bob, gw);
2697 auto const issue = makeIssue(env);
2699 auto const badTakerGets = badMPTAmount(issue, bad);
2700 auto tx = withNonCanonicalMPTAmount(
2709 testcase(
"featureMPTokensV2 disabled rejects MPT AMMCreate amounts");
2711 Env env{*
this, withoutFixAndV2};
2712 env.fund(
XRP(100'000), alice, bob, gw);
2714 auto const issue = makeIssue(env);
2716 auto const badAmount = badMPTAmount(issue, bad);
2717 auto tx = withNonCanonicalMPTAmount(
2719 AMM::createJv(alice.id(), STAmount{issue, std::uint64_t{1}},
XRP(1), 0),
2720 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2724 expectRoundTripBadMPT(tx, sfAmount, bad);
2729 Env env{*
this, withFixAndWithoutV2};
2730 env.fund(
XRP(100'000), alice, bob, gw);
2732 auto const issue = makeIssue(env);
2734 auto const badAmount = badMPTAmount(issue, bad);
2735 auto tx = withNonCanonicalMPTAmount(
2737 AMM::createJv(alice.id(), STAmount{issue, std::uint64_t{1}},
XRP(1), 0),
2738 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2747 Env env{*
this, withFix};
2748 env.fund(
XRP(100'000), alice, bob, gw);
2750 auto const issue = makeIssue(env);
2752 auto const badAmount = badMPTAmount(issue, bad);
2753 auto tx = withNonCanonicalMPTAmount(
2755 AMM::createJv(alice.id(), STAmount{issue, std::uint64_t{1}},
XRP(1), 0),
2756 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2764 testcase(
"featureMPTokensV2 disabled rejects MPT AMMDeposit amounts");
2766 Env env{*
this, withoutFixAndV2};
2767 env.fund(
XRP(100'000), alice, bob, gw);
2769 auto const issue = makeIssue(env);
2771 auto const badAmount = badMPTAmount(issue, bad);
2772 auto tx = withNonCanonicalMPTAmount(
2776 .asset1In = STAmount{issue, std::uint64_t{1}},
2778 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2782 expectRoundTripBadMPT(tx, sfAmount, bad);
2787 Env env{*
this, withFixAndWithoutV2};
2788 env.fund(
XRP(100'000), alice, bob, gw);
2790 auto const issue = makeIssue(env);
2792 auto const badAmount = badMPTAmount(issue, bad);
2793 auto tx = withNonCanonicalMPTAmount(
2797 .asset1In = STAmount{issue, std::uint64_t{1}},
2799 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2808 Env env{*
this, withFix};
2809 env.fund(
XRP(100'000), alice, bob, gw);
2811 auto const issue = makeIssue(env);
2813 auto const badAmount = badMPTAmount(issue, bad);
2814 auto tx = withNonCanonicalMPTAmount(
2818 .asset1In = STAmount{issue, std::uint64_t{1}},
2820 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2828 testcase(
"featureMPTokensV2 disabled rejects MPT AMMWithdraw amounts");
2830 Env env{*
this, withoutFixAndV2};
2831 env.fund(
XRP(100'000), alice, bob, gw);
2833 auto const issue = makeIssue(env);
2835 auto const badAmount = badMPTAmount(issue, bad);
2836 auto tx = withNonCanonicalMPTAmount(
2840 .asset1Out = STAmount{issue, std::uint64_t{1}},
2842 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2846 expectRoundTripBadMPT(tx, sfAmount, bad);
2851 Env env{*
this, withFixAndWithoutV2};
2852 env.fund(
XRP(100'000), alice, bob, gw);
2854 auto const issue = makeIssue(env);
2856 auto const badAmount = badMPTAmount(issue, bad);
2857 auto tx = withNonCanonicalMPTAmount(
2861 .asset1Out = STAmount{issue, std::uint64_t{1}},
2863 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2872 Env env{*
this, withFix};
2873 env.fund(
XRP(100'000), alice, bob, gw);
2875 auto const issue = makeIssue(env);
2877 auto const badAmount = badMPTAmount(issue, bad);
2878 auto tx = withNonCanonicalMPTAmount(
2882 .asset1Out = STAmount{issue, std::uint64_t{1}},
2884 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2892 testcase(
"featureMPTokensV2 disabled rejects MPT AMMClawback amounts");
2894 Env env{*
this, withoutFixAndV2};
2895 env.fund(
XRP(100'000), alice, bob, gw);
2897 auto const issue = makeIssue(env);
2899 auto const badAmount = badMPTAmount(issue, bad);
2900 auto tx = withNonCanonicalMPTAmount(
2908 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2912 expectRoundTripBadMPT(tx, sfAmount, bad);
2917 Env env{*
this, withFixAndWithoutV2};
2918 env.fund(
XRP(100'000), alice, bob, gw);
2920 auto const issue = makeIssue(env);
2922 auto const badAmount = badMPTAmount(issue, bad);
2923 auto tx = withNonCanonicalMPTAmount(
2931 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2940 Env env{*
this, withFix};
2941 env.fund(
XRP(100'000), alice, bob, gw);
2943 auto const issue = makeIssue(env);
2945 auto const badAmount = badMPTAmount(issue, bad);
2946 auto tx = withNonCanonicalMPTAmount(
2954 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2962 testcase(
"fixCleanup3_2_0 rejects non-canonical MPT VaultClawback amounts");
2964 Env env{*
this, withFix};
2965 env.fund(
XRP(100'000), alice, bob, gw);
2967 auto const issue = makeIssue(env);
2969 auto const badAmount = badMPTAmount(issue, bad);
2970 uint256 const fakeVaultId = keylet::vault(gw.id(), SeqProxy::rawSequence(1)).key;
2971 auto tx = withNonCanonicalMPTAmount(
2977 .amount = STAmount{issue, std::uint64_t{1}}}),
2978 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
4156 auto const usd = gw[
"USD"];
4160 {tfMPTCanTrade | tfMPTCanLock | tfMPTCanClawback,
4161 tfMPTCanTrade | tfMPTRequireAuth,
4163 tfMPTCanTrade | tfMPTCanLock,
4166 Env env{*
this, features};
4167 env.
fund(
XRP(1'000), gw, alice);
4171 bool const lockMPToken = (flags & (tfMPTCanLock | tfMPTCanClawback)) == tfMPTCanLock;
4172 bool const lockMPTIssue =
4173 (flags & (tfMPTCanLock | tfMPTCanClawback)) == (tfMPTCanLock | tfMPTCanClawback);
4174 bool const requireAuth = (flags & tfMPTRequireAuth) != 0u;
4182 .authHolder =
true});
4183 MPT const btc = mptTester;
4186 mptTester.authorize({.account = gw, .holder = alice, .flags = tfMPTUnauthorize});
4189 mptTester.set({.holder = alice, .flags = tfMPTLock});
4191 else if (lockMPTIssue)
4193 mptTester.set({.flags = tfMPTLock});
4197 [&](
Account const& account,
auto const& buy,
auto const& sell,
bool buyUSD) {
4198 auto error = [&](
auto const err) ->
TER {
4213 if (flags & tfMPTCanTransfer)
4218 auto const err = buyUSD ? errBuy : errSell;
4220 auto seq(env.
seq(account));
4221 env(
offer(account, buy(10), sell(10)),
Ter(err));
4226 auto testOffers = [&](
Account const& account) {
4227 testOffer(account,
XRP, btc,
false);
4228 testOffer(account, btc,
XRP,
true);
4236 Env env{*
this, features - featureMPTokensV2};
4238 MPTTester mptTester(env, gw, {.holders = {alice}});
4240 mptTester.
create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
4242 mptTester.authorize({.account = alice});
4243 mptTester.pay(gw, alice, 200);
4252 env.
fund(
XRP(1'000), gw, alice);
4254 MPT const btc =
MPTTester({.env = env, .issuer = gw, .holders = {alice}, .pay = 100});
4255 MPT const eth =
MPT(gw, 1);
4264 env.
fund(
XRP(1'000), gw, alice);
4265 MPTTester const btc({.env = env, .issuer = gw, .holders = {alice}, .pay = 100});
4266 MPTTester const eth({.env = env, .issuer = gw});
4268 env(
offer(alice, eth(10), btc(10)));
4281 env.
fund(
XRP(1'000), gw, gw2, alice, carol, bob, dan);
4285 .holders = {alice, carol, bob, dan, gw},
4291 .holders = {alice, carol, bob, dan, gw2},
4302 btc.set({.holder = alice, .flags = tfMPTLock});
4304 btc.set({.holder = alice, .flags = tfMPTUnlock});
4306 btc.set({.holder = carol, .flags = tfMPTLock});
4308 btc.set({.holder = carol, .flags = tfMPTUnlock});
4310 env(
offer(gw2, eth(1), btc(1)));
4314 eth.set({.holder = alice, .flags = tfMPTLock});
4316 eth.set({.holder = alice, .flags = tfMPTUnlock});
4318 eth.set({.holder = carol, .flags = tfMPTLock});
4320 eth.set({.holder = carol, .flags = tfMPTUnlock});
4322 env(
offer(gw, btc(1), eth(1)));
4327 btc.set({.flags = tfMPTLock});
4331 btc.set({.flags = tfMPTUnlock});
4334 eth.set({.flags = tfMPTLock});
4338 eth.set({.flags = tfMPTUnlock});
4341 env(
offer(alice, eth(1), btc(1)));
4342 env(
offer(carol, btc(1), eth(1)));
4348 env.
fund(
XRP(1'000), gw, alice);
4355 .authHolder =
true});
4362 .authHolder =
true});
4364 btc.
authorize({.account = gw, .holder = alice, .flags = tfMPTUnauthorize});
4370 env(
offer(gw, eth(10), btc(10)));
4377 env.
fund(
XRP(1'000), gw, alice, carol);
4381 .holders = {alice, carol},
4383 .flags = tfMPTCanTrade | tfMPTCanTransfer});
4387 .holders = {alice, carol},
4389 .flags = tfMPTCanTrade});
4392 env(
offer(alice, eth(10), btc(10)),
Txflags(tfPassive));
4400 env(
offer(carol, btc(10), eth(10)));
4405 env(
offer(alice, eth(10), btc(10)),
Txflags(tfPassive));
4406 env(
offer(carol, btc(10), eth(10)));
4414 env.
fund(
XRP(1'000), gw, alice, carol);
4418 .holders = {alice, carol},
4420 .flags = tfMPTCanTransfer,
4425 .holders = {alice, carol},
4427 .flags = tfMPTCanTrade,
4437 Env env{*
this, features};
4439 MPTTester mptTester(env, gw, {.holders = {alice, carol}});
4442 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4443 auto const mpt = mptTester[
"MPT"];
4445 mptTester.authorize({.account = alice});
4446 mptTester.pay(gw, alice, 200);
4448 mptTester.authorize({.account = carol});
4449 mptTester.pay(gw, carol, 200);
4451 env(
offer(alice,
XRP(100), mpt(101)));
4455 env(
offer(carol, mpt(101),
XRP(100)));
4459 BEAST_EXPECT(mptTester.checkMPTokenOutstandingAmount(400));
4460 BEAST_EXPECT(mptTester.checkMPTokenAmount(alice, 99));
4461 BEAST_EXPECT(mptTester.checkMPTokenAmount(carol, 301));
4466 Env env{*
this, features};
4468 MPTTester mptTester(env, gw, {.holders = {alice, carol}});
4471 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4472 auto const mpt = mptTester[
"MPT"];
4474 env(
trust(alice, usd(2'000)));
4475 env(
pay(gw, alice, usd(1'000)));
4478 env(
trust(carol, usd(2'000)));
4479 env(
pay(gw, carol, usd(1'000)));
4482 mptTester.authorize({.account = alice});
4483 mptTester.pay(gw, alice, 200);
4485 mptTester.authorize({.account = carol});
4486 mptTester.pay(gw, carol, 200);
4488 env(
offer(alice, usd(100), mpt(101)));
4492 env(
offer(carol, mpt(101), usd(100)));
4495 BEAST_EXPECT(env.
balance(alice, usd) == usd(1'100));
4496 BEAST_EXPECT(env.
balance(carol, usd) == usd(900));
4499 BEAST_EXPECT(mptTester.checkMPTokenOutstandingAmount(400));
4500 BEAST_EXPECT(mptTester.checkMPTokenAmount(alice, 99));
4501 BEAST_EXPECT(mptTester.checkMPTokenAmount(carol, 301));
4506 Env env{*
this, features};
4508 MPTTester mptTester1(env, gw, {.holders = {alice, carol}});
4510 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4511 auto const mpt1 = mptTester1[
"MPT1"];
4513 MPTTester mptTester2(env, gw, {.holders = {alice, carol}, .fund =
false});
4515 {.ownerCount = 2, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4516 auto const mpt2 = mptTester2[
"MPT2"];
4518 mptTester1.authorize({.account = alice});
4519 mptTester1.authorize({.account = carol});
4520 mptTester1.pay(gw, alice, 200);
4521 mptTester1.pay(gw, carol, 200);
4523 mptTester2.authorize({.account = alice});
4524 mptTester2.authorize({.account = carol});
4525 mptTester2.pay(gw, alice, 200);
4526 mptTester2.pay(gw, carol, 200);
4528 env(
offer(alice, mpt2(100), mpt1(101)));
4532 env(
offer(carol, mpt1(101), mpt2(100)));
4537 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(400));
4538 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 99));
4539 BEAST_EXPECT(mptTester1.checkMPTokenAmount(carol, 301));
4540 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(400));
4541 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 300));
4542 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 100));
4556 auto const usd = gw[
"USD"];
4560 Env env{*
this, features};
4561 MPTTester mptTester(env, gw, {.holders = {carol, bob}});
4564 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4565 auto const mpt = mptTester[
"MPT"];
4567 mptTester.authorize({.account = carol});
4568 mptTester.pay(gw, carol, 200);
4570 mptTester.authorize({.account = bob});
4573 env(
pay(carol, bob, mpt(1)),
4581 env(
pay(gw, bob, mpt(1)),
4589 env(
pay(bob, gw, mpt(1)),
4599 Env env{*
this, features};
4600 MPTTester mptTester(env, gw, {.holders = {carol, bob}});
4603 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4604 auto const mpt = mptTester[
"MPT"];
4606 mptTester.authorize({.account = carol});
4607 mptTester.pay(gw, carol, 200);
4609 mptTester.authorize({.account = bob});
4612 env(
pay(carol, bob, mpt(1)),
4620 env(
pay(gw, bob, mpt(1)),
4628 env(
pay(bob, gw, mpt(1)),
4638 Env env{*
this, features - featureMPTokensV2};
4640 MPTTester mptTester(env, gw, {.holders = {alice}});
4643 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4644 auto const mpt = mptTester[
"MPT"];
4646 mptTester.authorize({.account = alice});
4648 env(
pay(gw, alice, mpt(101)),
4656 auto const ed =
Account{
"ed"};
4657 Env env{*
this, features};
4658 env.
fund(
XRP(1'000), gw, alice, carol, bob, ed);
4662 .holders = {alice, carol, bob},
4668 .holders = {alice, carol, bob},
4674 .holders = {alice, carol, bob},
4680 .holders = {alice, carol, bob},
4684 env(
offer(bob, eth(1'000), btc(1'000)),
Txflags(tfPassive));
4686 env(
offer(bob, btc(1'000), eth(1'000)),
Txflags(tfPassive));
4707 btc.authorize({.account = ed});
4708 eth.authorize({.account = ed});
4709 env(
pay(gw, ed, eth(100)));
4710 env(
pay(gw, ed, btc(100)));
4712 btc.set({.flags = tfMPTSetRequireAuth});
4714 btc.authorize({.account = gw, .holder = bob});
4732 auto const ed =
Account{
"ed"};
4733 Env env{*
this, features};
4734 env.
fund(
XRP(1'000), gw, alice, carol, bob, ed);
4738 .holders = {alice, carol, bob, ed},
4740 .flags = tfMPTCanTrade});
4744 .holders = {alice, carol, bob, ed},
4748 env(
offer(bob, eth(1'000), btc(1'000)),
Txflags(tfPassive));
4750 env(
offer(bob, btc(1'000), eth(1'000)),
Txflags(tfPassive));
4766 env(
offer(gw, eth(100), btc(100)),
Txflags(tfPassive));
4768 env(
offer(gw, btc(100), eth(100)),
Txflags(tfPassive));
4793 enum class NoTransferMPT { BTC, ETH, USD, CAD };
4796 auto const testMultiStepMPTCanTransfer = [&](NoTransferMPT
const noTransferMPT,
4798 auto const getFlags = [&](NoTransferMPT
const mpt) {
4799 return mpt == noTransferMPT ? tfMPTCanTrade :
kMptDexFlags;
4802 Env env{*
this, features};
4803 env.
fund(
XRP(1'000), gw, alice, carol, bob);
4808 .holders = {alice, carol, bob},
4810 .flags = getFlags(NoTransferMPT::BTC)});
4814 .holders = {alice, carol, bob},
4816 .flags = getFlags(NoTransferMPT::ETH)});
4820 .holders = {alice, carol, bob},
4822 .flags = getFlags(NoTransferMPT::USD)});
4826 .holders = {alice, carol, bob},
4828 .flags = getFlags(NoTransferMPT::CAD)});
4830 env(
offer(bob, cad(100), usd(100)),
Txflags(tfPassive));
4831 env(
offer(bob, usd(100), btc(100)),
Txflags(tfPassive));
4832 env(
offer(bob, btc(100), eth(100)),
Txflags(tfPassive));
4835 test(env, btc, eth, usd, cad);
4839 testMultiStepMPTCanTransfer(
4849 env(
pay(alice, carol, eth(1)),
4850 Path(~usd, ~btc, ~eth),
4854 auto seq(env.
seq(gw));
4858 env(
pay(alice, carol, eth(1)),
4859 Path(~usd, ~btc, ~eth),
4868 env(
pay(alice, carol, eth(1)),
Path(~usd, ~btc, ~eth),
Sendmax(cad(1)));
4872 env(
offer(bob, cad(100), usd(100)),
Txflags(tfPassive));
4877 testMultiStepMPTCanTransfer(
4885 env(
pay(alice, carol, eth(1)),
4886 Path(~usd, ~btc, ~eth),
4893 env(
pay(alice, gw, eth(1)),
Path(~usd, ~btc, ~eth),
Sendmax(cad(1)));
4899 testMultiStepMPTCanTransfer(
4907 env(
pay(alice, carol, eth(1)),
4908 Path(~usd, ~btc, ~eth),
4912 env(
pay(gw, carol, eth(1)),
Path(~usd, ~btc, ~eth),
Sendmax(cad(1)));
4917 env(
pay(alice, carol, eth(1)),
Path(~usd, ~btc, ~eth),
Sendmax(cad(1)));
4924 testMultiStepMPTCanTransfer(
4934 env(
pay(alice, carol, eth(1)),
Path(~usd, ~btc, ~eth),
Sendmax(cad(1)));
4942 Env env{*
this, features};
4943 env.
fund(
XRP(1'000), gw, alice, carol, bob);
4948 .holders = {alice, carol, bob},
4950 .flags = tfMPTCanTransfer});
4954 .holders = {alice, carol, bob},
4960 .holders = {alice, carol, bob},
4969 btc.set({.flags = tfMPTSetCanTrade});
4971 env(
offer(bob, btc(1), eth(1)));
4972 env(
offer(bob, eth(1), usd(1)));
4976 env(
pay(gw, carol, usd(1)),
4977 Path(~btc, ~eth, ~usd),
4979 Txflags(tfPartialPayment | tfNoRippleDirect));
4986 enum class LockType { Global, Individual,
None };
4992 LockType srcFlag = LockType::None;
4993 LockType dstFlag = LockType::None;
4994 LockType offerFlagBuy = LockType::None;
4995 LockType offerFlagSell = LockType::None;
4996 LockType globalFlagBuy = LockType::None;
4997 LockType globalFlagSell = LockType::None;
5001 auto getErr = [&]<
typename Token>(Token
const&, TestArg
const&
arg) {
5004 return arg.errIOU.value_or(
arg.err);
5011 auto getMPT = [&](
Env& env) {
5015 .holders = {alice, carol, bob, gw},
5021 .holders = {alice, carol, bob, gw2},
5026 auto getIOU = [&](
Env& env) {
5027 for (
auto const& a : {alice, carol, bob})
5029 env(
fset(a, asfDefaultRipple));
5031 env(
trust(a, gw[
"ETH"](200)));
5032 env(
pay(gw, a, gw[
"ETH"](100)));
5033 env(
trust(a, gw2[
"BTC"](200)));
5034 env(
pay(gw2, a, gw2[
"BTC"](100)));
5039 env(
trust(gw2, gw[
"ETH"](200)));
5040 env(
trust(gw, gw2[
"BTC"](200)));
5041 env(
pay(gw2, gw, gw2[
"BTC"](100)));
5045 auto lock = [&]<
typename Token>(
5047 if (
lock == LockType::None)
5051 if (
lock == LockType::Global)
5053 env(
fset(
token.account, asfGlobalFreeze));
5057 IOU const iou{account,
token.currency};
5058 env(
trust(
token.account, iou(0), tfSetFreeze));
5063 if (
lock == LockType::Global)
5065 token.set({.flags = tfMPTLock});
5067 else if (
token.issuer() != account)
5069 token.set({.holder = account, .flags = tfMPTLock});
5073 auto test = [&](
auto&& getTokens, TestArg
const&
arg) {
5075 env.
fund(
XRP(1'000), gw, gw2, alice, carol, bob);
5077 auto [btc, eth] = getTokens(env);
5082 if (
arg.globalFlagBuy != LockType::None)
5084 lock(env, gw, eth, LockType::Global);
5088 lock(env,
arg.offerOwner, eth,
arg.offerFlagBuy);
5091 if (
arg.globalFlagSell != LockType::None)
5093 lock(env, gw2, btc, LockType::Global);
5097 lock(env,
arg.offerOwner, btc,
arg.offerFlagSell);
5101 auto const err = getErr(eth,
arg);
5113 {.src = alice, .dst = carol, .offerOwner = bob, .srcFlag = LockType::Individual, .err =
tecPATH_DRY},
5115 {.src = alice, .dst = carol, .offerOwner = bob, .dstFlag = LockType::Individual, .err =
tecPATH_DRY, .errIOU =
tesSUCCESS},
5117 {.src = alice, .dst = carol, .offerOwner = bob, .globalFlagBuy = LockType::Global, .err =
tecPATH_DRY},
5119 {.src = alice, .dst = carol, .offerOwner = bob, .globalFlagSell = LockType::Global, .err =
tecPATH_PARTIAL},
5121 {.src = alice, .dst = carol, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err =
tecPATH_PARTIAL, .errIOU =
tesSUCCESS},
5123 {.src = alice, .dst = carol, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err =
tecPATH_PARTIAL},
5126 {.src = alice, .dst = carol, .offerOwner = gw2, .srcFlag = LockType::Individual, .err =
tecPATH_DRY},
5128 {.src = alice, .dst = carol, .offerOwner = gw2, .dstFlag = LockType::Individual, .err =
tecPATH_DRY, .errIOU =
tesSUCCESS},
5130 {.src = alice, .dst = carol, .offerOwner = gw2, .globalFlagBuy = LockType::Global, .err =
tecPATH_DRY},
5132 {.src = alice, .dst = carol, .offerOwner = gw2, .globalFlagSell = LockType::Global, .err =
tesSUCCESS},
5135 {.src = alice, .dst = carol, .offerOwner = gw, .srcFlag = LockType::Individual, .err =
tecPATH_DRY},
5137 {.src = alice, .dst = carol, .offerOwner = gw, .dstFlag = LockType::Individual, .err =
tecPATH_DRY, .errIOU =
tesSUCCESS},
5139 {.src = alice, .dst = carol, .offerOwner = gw, .globalFlagBuy = LockType::Global, .err =
tecPATH_DRY},
5141 {.src = alice, .dst = carol, .offerOwner = gw, .globalFlagSell = LockType::Global, .err =
tecPATH_PARTIAL},
5145 {.src = gw, .dst = carol, .offerOwner = bob, .srcFlag = LockType::Global, .err =
tecPATH_PARTIAL, .errIOU =
tesSUCCESS},
5147 {.src = gw, .dst = carol, .offerOwner = bob, .dstFlag = LockType::Global, .err =
tecPATH_PARTIAL},
5149 {.src = gw, .dst = carol, .offerOwner = bob, .dstFlag = LockType::Individual, .err =
tecPATH_DRY, .errIOU =
tesSUCCESS},
5151 {.src = gw, .dst = carol, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err =
tecPATH_PARTIAL, .errIOU =
tesSUCCESS},
5153 {.src = gw, .dst = carol, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err =
tecPATH_PARTIAL},
5156 {.src = alice, .dst = gw2, .offerOwner = bob, .srcFlag = LockType::Individual, .err =
tecPATH_DRY},
5158 {.src = alice, .dst = gw2, .offerOwner = bob, .dstFlag = LockType::Global, .err =
tecPATH_PARTIAL},
5160 {.src = alice, .dst = gw2, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err =
tecPATH_PARTIAL, .errIOU =
tesSUCCESS},
5162 {.src = alice, .dst = gw2, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err =
tecPATH_PARTIAL},
5166 for (
auto const& t :
tests)
5174 auto const usd = gw[
"USD"];
5175 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5179 .holders = {alice, carol, bob},
5185 .holders = {alice, carol, bob},
5189 env(
trust(alice, usd(100)));
5190 env(
pay(gw, alice, usd(100)));
5191 env(
trust(carol, usd(100)));
5193 env(
offer(alice,
XRP(10), eth(10)));
5194 env(
offer(bob, eth(10), btc(10)));
5195 env(
offer(alice, btc(10), usd(10)));
5198 btc.set({.holder = bob, .flags = tfMPTLock});
5201 env(
pay(alice, carol, usd(1)),
5203 Txflags(tfNoRippleDirect | tfPartialPayment),
5208 btc.set({.holder = bob, .flags = tfMPTUnlock});
5209 eth.set({.holder = bob, .flags = tfMPTLock});
5211 env(
pay(alice, carol, usd(1)),
5213 Txflags(tfNoRippleDirect | tfPartialPayment),
5221 Env env(*
this, features);
5222 Account const domainOwner(
"DomainOwner");
5223 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5224 auto const domainID =
5225 setupDomain(env, {alice, bob, carol, gw}, domainOwner,
"permdex-cred");
5227 MPTTester btc({.env = env, .issuer = gw, .holders = {alice, carol, bob}, .pay = 100});
5228 MPTTester eth({.env = env, .issuer = gw, .holders = {alice, carol, bob}, .pay = 100});
5230 auto test = [&](
bool withDomain) {
5233 env(
offer(bob, eth(1), btc(1)),
Domain(domainID));
5237 env(
offer(bob, eth(1), btc(1)));
5241 env(
pay(alice, carol, btc(1)),
5255 Env env(*
this, features);
5256 Account const domainOwner(
"DomainOwner");
5257 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5258 auto const domainID =
5259 setupDomain(env, {alice, bob, carol, gw}, domainOwner,
"permdex-cred");
5261 MPTTester btc({.env = env, .issuer = gw, .holders = {alice, carol, bob}, .pay = 100});
5262 MPTTester eth({.env = env, .issuer = gw, .holders = {alice, carol, bob}, .pay = 100});
5264 auto test = [&](
bool isHybrid) {
5265 auto const flags = isHybrid ? tfHybrid : 0;
5269 env(
pay(alice, carol, btc(1)),
5281 Env env{*
this, features};
5282 MPTTester mptTester(env, gw, {.holders = {alice, carol, bob}});
5285 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5286 auto const mpt = mptTester[
"MPT"];
5288 mptTester.authorize({.account = alice});
5289 mptTester.pay(gw, alice, 200);
5291 mptTester.authorize({.account = carol});
5292 mptTester.pay(gw, carol, 200);
5294 mptTester.authorize({.account = bob});
5296 env(
offer(alice,
XRP(100), mpt(101)));
5300 env(
pay(carol, bob, mpt(101)),
5307 BEAST_EXPECT(mptTester.checkMPTokenOutstandingAmount(400));
5308 BEAST_EXPECT(mptTester.checkMPTokenAmount(alice, 99));
5309 BEAST_EXPECT(mptTester.checkMPTokenAmount(bob, 101));
5314 Env env{*
this, features};
5316 MPTTester mptTester(env, gw, {.holders = {alice, carol, bob}});
5319 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5320 auto const mpt = mptTester[
"MPT"];
5322 env(
trust(alice, usd(2'000)));
5323 env(
pay(gw, alice, usd(1'000)));
5324 env(
trust(bob, usd(2'000)));
5325 env(
pay(gw, bob, usd(1'000)));
5326 env(
trust(carol, usd(2'000)));
5327 env(
pay(gw, carol, usd(1'000)));
5330 mptTester.authorize({.account = alice});
5331 mptTester.pay(gw, alice, 200);
5333 mptTester.authorize({.account = carol});
5334 mptTester.pay(gw, carol, 200);
5336 mptTester.authorize({.account = bob});
5338 env(
offer(alice, usd(100), mpt(101)));
5342 env(
pay(carol, bob, mpt(101)),
5349 BEAST_EXPECT(env.
balance(carol, usd) == usd(900));
5350 BEAST_EXPECT(mptTester.checkMPTokenOutstandingAmount(400));
5351 BEAST_EXPECT(mptTester.checkMPTokenAmount(alice, 99));
5352 BEAST_EXPECT(mptTester.checkMPTokenAmount(bob, 101));
5357 Env env{*
this, features};
5359 MPTTester mptTester(env, gw, {.holders = {alice, carol, bob}});
5362 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5363 auto const mpt = mptTester[
"MPT"];
5365 env(
trust(alice, usd(2'000)),
Txflags(tfClearNoRipple));
5366 env(
pay(gw, alice, usd(1'000)));
5370 mptTester.authorize({.account = alice});
5371 env(
pay(gw, alice, mpt(200)));
5373 mptTester.authorize({.account = carol});
5374 env(
pay(gw, carol, mpt(200)));
5376 env(
offer(alice, mpt(101), usd(100)));
5380 env(
pay(carol, bob, usd(100)),
5383 Txflags(tfPartialPayment | tfNoRippleDirect));
5387 BEAST_EXPECT(env.
balance(alice, usd) == usd(900));
5388 BEAST_EXPECT(mptTester.checkMPTokenAmount(alice, 301));
5389 BEAST_EXPECT(mptTester.checkMPTokenOutstandingAmount(400));
5390 BEAST_EXPECT(mptTester.checkMPTokenAmount(carol, 99));
5391 BEAST_EXPECT(env.
balance(bob, usd) == usd(100));
5396 Env env{*
this, features};
5398 MPTTester mptTester1(env, gw, {.holders = {alice, carol, bob}});
5400 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5401 auto const mpt1 = mptTester1[
"MPT1"];
5403 MPTTester mptTester2(env, gw, {.holders = {alice, carol, bob}, .fund =
false});
5405 {.ownerCount = 2, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5406 auto const mpt2 = mptTester2[
"MPT2"];
5408 mptTester1.authorize({.account = alice});
5409 mptTester1.pay(gw, alice, 200);
5410 mptTester2.authorize({.account = alice});
5412 mptTester2.authorize({.account = carol});
5413 mptTester2.pay(gw, carol, 200);
5415 mptTester1.authorize({.account = bob});
5416 mptTester2.authorize({.account = bob});
5417 mptTester2.pay(gw, bob, 200);
5419 env(
offer(alice, mpt2(100), mpt1(100)));
5425 env(
pay(carol, bob, mpt1(10)),
5432 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 190));
5433 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 10));
5434 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(200));
5435 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(400));
5436 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5437 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 10));
5440 env(
pay(gw, bob, mpt1(20)),
5447 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 170));
5448 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 30));
5449 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(200));
5450 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(420));
5451 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5452 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 30));
5455 env(
pay(bob, gw, mpt1(70)),
5462 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 100));
5463 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 100));
5464 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(130));
5465 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(420));
5466 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5467 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 30));
5468 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 130));
5473 Env env{*
this, features};
5475 MPTTester mptTester1(env, gw, {.holders = {carol, bob}});
5477 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5478 auto const mpt1 = mptTester1[
"MPT1"];
5480 MPTTester mptTester2(env, gw, {.holders = {carol, bob}, .fund =
false});
5482 {.ownerCount = 2, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5483 auto const mpt2 = mptTester2[
"MPT2"];
5485 mptTester2.authorize({.account = carol});
5486 mptTester2.pay(gw, carol, 200);
5488 mptTester1.authorize({.account = bob});
5489 mptTester2.authorize({.account = bob});
5490 mptTester2.pay(gw, bob, 200);
5492 env(
offer(gw, mpt2(100), mpt1(100)));
5497 env(
pay(carol, bob, mpt1(10)),
5504 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(10));
5505 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(390));
5506 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5507 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 10));
5510 env(
pay(gw, bob, mpt1(20)),
5517 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(30));
5518 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(390));
5519 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5520 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 30));
5523 env(
pay(bob, gw, mpt1(70)),
5530 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(30));
5531 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(320));
5532 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5533 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 30));
5534 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 130));
5539 Env env{*
this, features};
5542 MPTTester mptTester1(env, gw, {.holders = {alice, carol, bob}});
5544 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5545 auto const mpt1 = mptTester1[
"MPT1"];
5548 MPTTester mptTester2(env, gw1, {.holders = {alice, carol, bob}, .fund =
false});
5550 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5551 auto const mpt2 = mptTester2[
"MPT2"];
5553 mptTester1.authorize({.account = alice});
5554 mptTester1.pay(gw, alice, 200);
5555 mptTester2.authorize({.account = alice});
5557 mptTester2.authorize({.account = carol});
5558 mptTester2.pay(gw1, carol, 200);
5560 mptTester1.authorize({.account = bob});
5561 mptTester1.pay(gw, bob, 200);
5562 mptTester2.authorize({.account = bob});
5563 mptTester2.pay(gw1, bob, 200);
5565 mptTester1.authorize({.account = gw1});
5566 mptTester1.pay(gw, gw1, 200);
5568 mptTester2.authorize({.account = gw});
5569 mptTester2.pay(gw1, gw, 200);
5571 env(
offer(alice, mpt2(100), mpt1(100)));
5575 env(
pay(carol, bob, mpt1(10)),
5581 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(600));
5582 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(600));
5583 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 200));
5584 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 200));
5585 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5586 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 210));
5587 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 200));
5588 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 190));
5589 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 10));
5591 env(
pay(bob, gw, mpt1(10)),
5597 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(590));
5598 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(600));
5599 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 200));
5600 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 200));
5601 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 210));
5602 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 190));
5603 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 180));
5604 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 20));
5606 env(
pay(gw, bob, mpt1(10)),
5612 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(590));
5613 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(600));
5614 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 200));
5615 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 190));
5616 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 220));
5617 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 190));
5618 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 170));
5619 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 30));
5621 env(
pay(bob, gw1, mpt1(10)),
5627 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(590));
5628 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(600));
5629 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 210));
5630 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 190));
5631 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 220));
5632 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 180));
5633 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 160));
5634 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 40));
5636 env(
pay(gw1, bob, mpt1(10)),
5642 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(590));
5643 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(610));
5644 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 210));
5645 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 190));
5646 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 230));
5647 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 180));
5648 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 150));
5649 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 50));
5651 env(
pay(gw, gw1, mpt1(10)),
5657 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(590));
5658 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(610));
5659 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 220));
5660 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 180));
5661 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 140));
5662 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 60));
5664 env(
pay(gw1, gw, mpt1(40)),
5670 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(550));
5671 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(650));
5672 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 220));
5673 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 180));
5674 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 100));
5675 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 100));
5685 auto const usd = gw2[
"USD"];
5687 MPTTester mptTester(env, gw, {.holders = {alice, carol}});
5689 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5690 auto const mpt = mptTester[
"MPT"];
5691 mptTester.authorize({.account = alice});
5692 mptTester.authorize({.account = carol});
5693 mptTester.pay(gw, carol, 200);
5695 MPTTester mptTester1(env, gw1, {.holders = {bob, dan}});
5697 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5698 auto const mpt1 = mptTester1[
"MPT1"];
5699 mptTester1.authorize({.account = bob});
5700 mptTester1.pay(gw1, bob, 200);
5701 mptTester1.authorize({.account = dan});
5703 env(
trust(alice, usd(400)));
5704 env(
pay(gw2, alice, usd(200)));
5705 env(
trust(bob, usd(400)));
5707 env(
offer(alice, mpt(100), usd(100)));
5708 env(
offer(bob, usd(100), mpt1(100)));
5711 env(
pay(carol, dan, mpt1(100)),
5714 Txflags(tfPartialPayment | tfNoRippleDirect));
5718 BEAST_EXPECT(mptTester.checkMPTokenAmount(carol, 100));
5719 BEAST_EXPECT(mptTester1.checkMPTokenAmount(dan, 100));
5724 Env env{*
this, features};
5726 fund(env, gw, {alice, carol, bob},
XRP(11'000), {usd(20'000)});
5728 MPTTester mptTester(env, gw, {.fund =
false});
5731 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5732 auto const mpt = mptTester[
"MPT"];
5734 mptTester.authorize({.account = alice});
5735 mptTester.authorize({.account = bob});
5736 mptTester.pay(gw, alice, 10'100);
5738 AMM const amm(env, alice,
XRP(10'000), mpt(10'100));
5740 env(
pay(carol, bob, mpt(100)),
5743 Txflags(tfPartialPayment | tfNoRippleDirect));
5746 BEAST_EXPECT(
amm.expectBalances(
XRP(10'100), mpt(10'000),
amm.tokens()));
5747 BEAST_EXPECT(mptTester.checkMPTokenAmount(bob, 100));
5752 Env env{*
this, features};
5754 fund(env, gw, {alice, carol, bob},
XRP(11'000), {usd(20'000)});
5756 MPTTester mptTester(env, gw, {.fund =
false});
5759 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5760 auto const mpt = mptTester[
"MPT"];
5762 mptTester.authorize({.account = alice});
5763 mptTester.authorize({.account = bob});
5764 mptTester.pay(gw, alice, 10'100);
5766 AMM const amm(env, alice, usd(10'000), mpt(10'100));
5768 env(
pay(carol, bob, mpt(100)),
5771 Txflags(tfPartialPayment | tfNoRippleDirect));
5774 BEAST_EXPECT(
amm.expectBalances(usd(10'100), mpt(10'000),
amm.tokens()));
5775 BEAST_EXPECT(mptTester.checkMPTokenAmount(bob, 100));
5780 Env env{*
this, features};
5781 env.
fund(
XRP(20'000), gw, alice, carol, bob);
5784 MPTTester mptTester1(env, gw, {.fund =
false});
5785 mptTester1.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
5786 auto const mpt1 = mptTester1[
"MPT1"];
5787 mptTester1.authorize({.account = alice});
5788 mptTester1.authorize({.account = bob});
5789 mptTester1.pay(gw, alice, 10'100);
5791 MPTTester mptTester2(env, gw, {.fund =
false});
5792 mptTester2.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
5793 auto const mpt2 = mptTester2[
"MPT1"];
5794 mptTester2.authorize({.account = alice});
5795 mptTester2.authorize({.account = bob});
5796 mptTester2.authorize({.account = carol});
5797 mptTester2.pay(gw, alice, 10'100);
5798 mptTester2.pay(gw, carol, 100);
5800 AMM const amm(env, alice, mpt2(10'000), mpt1(10'100));
5802 env(
pay(carol, bob, mpt1(100)),
5805 Txflags(tfPartialPayment | tfNoRippleDirect));
5808 BEAST_EXPECT(
amm.expectBalances(mpt2(10'100), mpt1(10'000),
amm.tokens()));
5809 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 100));
5815 Env env{*
this, features};
5816 auto const usd = gw[
"USD"];
5817 auto const eur = gw[
"EUR"];
5818 auto const crn = gw[
"CRN"];
5819 auto const yan = gw[
"YAN"];
5824 {alice, carol, bob},
5826 {usd(1'000), eur(1'000), crn(2'000), yan(1'000)});
5829 MPTTester mptTester(env, gw, {.fund =
false});
5830 mptTester.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
5831 mptTester.authorize({.account = alice});
5832 mptTester.pay(gw, alice, 2'000);
5833 return {mptTester, mptTester[
"MPT"]};
5836 auto const [mptTester1, mpt1] = createMPT();
5837 auto const [mptTester2, mpt2] = createMPT();
5838 auto const [mptTester3, mpt3] = createMPT();
5840 env(
offer(alice, eur(100), mpt1(101)));
5841 env(
offer(alice, mpt1(101), mpt2(102)));
5842 env(
offer(alice, mpt2(102), usd(103)));
5843 env(
offer(alice, usd(103), crn(104)));
5845 AMM const amm(env, alice, crn(1'000), mpt3(1'104));
5846 env(
offer(alice, mpt3(104), yan(100)));
5848 env(
pay(carol, bob, yan(100)),
5851 Txflags(tfPartialPayment | tfNoRippleDirect));
5854 BEAST_EXPECT(env.
balance(carol, eur) == eur(900));
5855 BEAST_EXPECT(env.
balance(bob, yan) == yan(1'100));
5856 BEAST_EXPECT(
amm.expectBalances(crn(1'104), mpt3(1'000),
amm.tokens()));
5863 Env env{*
this, features};
5864 auto const usd = gw[
"USD"];
5865 auto const eur = gw[
"EUR"];
5866 auto const crn = gw[
"CRN"];
5868 fund(env, gw, {alice, carol, bob},
XRP(1'000), {usd(1'000), eur(1'000), crn(2'000)});
5871 MPTTester mptTester(env, gw, {.fund =
false});
5872 mptTester.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
5873 mptTester.authorize({.account = alice});
5874 mptTester.pay(gw, alice, 2'000);
5875 return {mptTester, mptTester[
"MPT"]};
5878 auto const [mptTester1, mpt1] = createMPT();
5879 auto const [mptTester2, mpt2] = createMPT();
5880 auto const [mptTester3, mpt3] = createMPT();
5881 auto [mptTester4, mpt4] = createMPT();
5882 mptTester4.authorize({.account = bob});
5884 env(
offer(alice, eur(100), mpt1(101)));
5885 env(
offer(alice, mpt1(101), mpt2(102)));
5886 env(
offer(alice, mpt2(102), usd(103)));
5887 env(
offer(alice, usd(103), crn(104)));
5889 AMM const amm(env, alice, crn(1'000), mpt3(1'104));
5890 env(
offer(alice, mpt3(104), mpt4(100)));
5892 env(
pay(carol, bob, mpt4(100)),
5895 Txflags(tfPartialPayment | tfNoRippleDirect));
5898 BEAST_EXPECT(env.
balance(carol, eur) == eur(900));
5899 BEAST_EXPECT(mptTester4.checkMPTokenAmount(bob, 100));
5900 BEAST_EXPECT(
amm.expectBalances(crn(1'104), mpt3(1'000),
amm.tokens()));
5907 Env env(*
this, features);
5909 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5911 MPTTester usdTester(env, gw, {.holders = {alice, carol, bob}, .fund =
false});
5915 .pay = {{{alice}, 1'000}},
5916 .flags = tfMPTCanTransfer | tfMPTCanTrade});
5917 auto const usd = usdTester[
"USD"];
5919 MPTTester eurTester(env, gw, {.holders = {alice, carol, bob}, .fund =
false});
5922 .authorize = {{alice, carol}},
5923 .pay = {{{carol}, 100}},
5924 .flags = tfMPTCanTransfer | tfMPTCanTrade});
5925 auto const eur = eurTester[
"EUR"];
5927 env(
offer(alice, eur(10), usd(10)));
5929 env(
pay(carol, bob, usd(10)),
5932 Txflags(tfNoRippleDirect | tfPartialPayment));
5935 Env env(*
this, features);
5936 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5939 {.env = env, .issuer = gw, .holders = {alice, carol, bob}, .maxAmt = 1'000});
5940 MPT const usd = musd;
5941 env(
pay(gw, alice, usd(800)));
5942 env(
offer(gw,
XRP(300), usd(300)));
5943 env(
pay(carol, bob, usd(300)),
5947 BEAST_EXPECT(musd.checkMPTokenAmount(bob, 200));
5948 BEAST_EXPECT(musd.checkMPTokenOutstandingAmount(1'000));
5953 Env env(*
this, features);
5954 auto const eur = gw[
"EUR"];
5955 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5958 env(
trust(alice, eur(1'000)));
5959 env(
pay(gw, alice, eur(300)));
5960 env(
trust(bob, eur(1'000)));
5963 {.env = env, .issuer = gw, .holders = {alice, carol, bob}, .maxAmt = 1'000});
5964 MPT const usd = musd;
5966 env(
pay(gw, alice, usd(800)));
5967 env(
offer(gw,
XRP(300), usd(300)));
5968 env(
offer(alice, usd(300), eur(300)));
5969 env(
pay(carol, bob, eur(300)),
5973 BEAST_EXPECT(musd.checkMPTokenAmount(alice, 1'000));
5974 BEAST_EXPECT(musd.checkMPTokenOutstandingAmount(1'000));
5977 BEAST_EXPECT(env.
balance(bob, eur) == eur(200));
5992 auto const usd = gw[
"USD"];
5993 auto const eur = gw1[
"EUR"];
6001 MPTTester mptTester(env, gw, {.holders = {dan, carol}});
6003 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6004 auto const mpt = mptTester[
"MPT"];
6005 mptTester.authorize({.account = dan});
6006 mptTester.authorize({.account = carol});
6007 mptTester.pay(gw, carol, 200);
6009 auto const [pathSet, srcAmt, dstAmt] =
findPaths(env, carol, dan, mpt(-1));
6010 BEAST_EXPECT(srcAmt == mpt(200));
6011 BEAST_EXPECT(dstAmt == mpt(200));
6013 BEAST_EXPECT(pathSet.empty());
6022 MPTTester mptTester(env, gw, {.holders = {alice, dan}});
6025 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6026 auto const mpt = mptTester[
"MPT"];
6028 mptTester.authorize({.account = alice});
6029 mptTester.authorize({.account = dan});
6030 mptTester.pay(gw, alice, 200);
6032 env(
offer(alice,
XRP(100), mpt(100)));
6035 auto const [pathSet, srcAmt, dstAmt] =
findPaths(env, carol, dan, mpt(-1));
6036 BEAST_EXPECT(srcAmt ==
XRP(100));
6037 BEAST_EXPECT(dstAmt == mpt(100));
6038 if (BEAST_EXPECT(
same(pathSet,
stpath(
ipe(mptTester.issuanceID())))))
6041 env(
pay(carol, dan, mpt(10)),
6044 Txflags(tfNoRippleDirect | tfPartialPayment));
6055 MPTTester mptTester(env, gw1, {.holders = {alice, dan}});
6058 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6059 auto const mpt = mptTester[
"MPT"];
6061 mptTester.authorize({.account = alice});
6062 mptTester.authorize({.account = dan});
6063 mptTester.pay(gw1, alice, 200);
6065 env(
trust(alice, usd(400)));
6066 env(
trust(carol, usd(400)));
6067 env(
pay(gw, carol, usd(200)));
6069 env(
offer(alice, usd(100), mpt(100)));
6077 BEAST_EXPECT(srcAmt == usd(100));
6078 BEAST_EXPECT(dstAmt == mpt(100));
6083 env(
pay(carol, dan, mpt(10)),
6086 Txflags(tfNoRippleDirect | tfPartialPayment));
6090 std::tie(pathSet, srcAmt, dstAmt) =
findPaths(env, carol, dan, mpt(-1), usd(-1));
6091 BEAST_EXPECT(srcAmt == usd(90));
6092 BEAST_EXPECT(dstAmt == mpt(90));
6097 env(
pay(carol, dan, mpt(10)),
6100 Txflags(tfNoRippleDirect | tfPartialPayment));
6104 std::tie(pathSet, srcAmt, dstAmt) =
6105 findPaths(env, carol, dan, mpt(-1), std::nullopt, usd.currency);
6106 BEAST_EXPECT(srcAmt == usd(80));
6107 BEAST_EXPECT(dstAmt == mpt(80));
6112 env(
pay(carol, dan, mpt(10)),
6115 Txflags(tfNoRippleDirect | tfPartialPayment));
6126 MPTTester mptTester(env, gw1, {.holders = {carol, alice}});
6129 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6130 auto const mpt = mptTester[
"MPT"];
6132 mptTester.authorize({.account = carol});
6133 mptTester.authorize({.account = alice});
6134 mptTester.pay(gw1, carol, 200);
6136 env(
trust(dan, usd(400)));
6137 env(
trust(alice, usd(400)));
6138 env(
pay(gw, alice, usd(200)));
6140 env(
offer(alice, mpt(100), usd(100)));
6148 BEAST_EXPECT(srcAmt == mpt(100));
6149 BEAST_EXPECT(dstAmt == usd(100));
6153 env(
pay(carol, dan, usd(10)),
6156 Txflags(tfNoRippleDirect | tfPartialPayment));
6160 std::tie(pathSet, srcAmt, dstAmt) =
findPaths(env, carol, dan, usd(-1), mpt(-1));
6161 BEAST_EXPECT(srcAmt == mpt(90));
6162 BEAST_EXPECT(dstAmt == usd(90));
6166 env(
pay(carol, dan, usd(10)),
6169 Txflags(tfNoRippleDirect | tfPartialPayment));
6173 std::tie(pathSet, srcAmt, dstAmt) =
6174 findPaths(env, carol, dan, usd(-1), std::nullopt, mpt.mpt());
6175 BEAST_EXPECT(srcAmt == mpt(80));
6176 BEAST_EXPECT(dstAmt == usd(80));
6180 env(
pay(carol, dan, usd(10)),
6183 Txflags(tfNoRippleDirect | tfPartialPayment));
6191 MPTTester mptTester(env, gw, {.holders = {alice, dan}});
6192 MPTTester mptTester1(env, gw1, {.holders = {carol}});
6195 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6196 auto const mpt = mptTester[
"MPT"];
6198 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6199 auto const mpt1 = mptTester1[
"MPT1"];
6201 mptTester.authorize({.account = alice});
6202 mptTester.authorize({.account = dan});
6203 mptTester.pay(gw, alice, 200);
6205 mptTester1.authorize({.account = carol});
6206 mptTester1.authorize({.account = alice});
6207 mptTester1.pay(gw1, carol, 200);
6209 env(
offer(alice, mpt1(100), mpt(100)));
6217 BEAST_EXPECT(srcAmt == mpt1(100));
6218 BEAST_EXPECT(dstAmt == mpt(100));
6223 env(
pay(carol, dan, mpt(10)),
6226 Txflags(tfNoRippleDirect | tfPartialPayment));
6230 std::tie(pathSet, srcAmt, dstAmt) =
findPaths(env, carol, dan, mpt(-1), mpt1(-1));
6231 BEAST_EXPECT(srcAmt == mpt1(90));
6232 BEAST_EXPECT(dstAmt == mpt(90));
6237 env(
pay(carol, dan, mpt(10)),
6240 Txflags(tfNoRippleDirect | tfPartialPayment));
6244 std::tie(pathSet, srcAmt, dstAmt) =
6245 findPaths(env, carol, dan, mpt(-1), std::nullopt, mpt1.mpt());
6246 BEAST_EXPECT(srcAmt == mpt1(80));
6247 BEAST_EXPECT(dstAmt == mpt(80));
6252 env(
pay(carol, dan, mpt(10)),
6255 Txflags(tfNoRippleDirect | tfPartialPayment));
6266 MPTTester mptTester(env, gw, {.holders = {alice, bob}});
6269 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6270 auto const mpt = mptTester[
"MPT"];
6272 mptTester.authorize({.account = alice});
6273 mptTester.authorize({.account = bob});
6274 mptTester.pay(gw, alice, 200);
6275 mptTester.pay(gw, bob, 200);
6277 env(
trust(bob, usd(200)));
6278 env(
pay(gw, bob, usd(100)));
6279 env(
trust(dan, usd(200)));
6280 env(
trust(alice, usd(200)));
6282 env(
offer(alice,
XRP(100), mpt(100)));
6283 env(
offer(bob, mpt(100), usd(100)));
6291 BEAST_EXPECT(srcAmt ==
XRP(100));
6292 BEAST_EXPECT(dstAmt == usd(100));
6294 pathSet.
size() == 1 &&
6298 env(
pay(carol, dan, usd(10)),
6301 Txflags(tfNoRippleDirect | tfPartialPayment));
6306 BEAST_EXPECT(srcAmt ==
XRP(90));
6307 BEAST_EXPECT(dstAmt == usd(90));
6309 pathSet.
size() == 1 &&
6313 env(
pay(carol, dan, usd(10)),
6316 Txflags(tfNoRippleDirect | tfPartialPayment));
6326 auto const usd2 = gw2[
"USD"];
6328 MPTTester mptTester(env, gw, {.holders = {alice, carol}});
6330 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6331 auto const mpt = mptTester[
"MPT"];
6332 mptTester.authorize({.account = alice});
6333 mptTester.authorize({.account = carol});
6334 mptTester.pay(gw, carol, 200);
6336 MPTTester mptTester1(env, gw1, {.holders = {bob, dan}});
6338 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6339 auto const mpt1 = mptTester1[
"MPT1"];
6340 mptTester1.authorize({.account = bob});
6341 mptTester1.pay(gw1, bob, 200);
6342 mptTester1.authorize({.account = dan});
6344 env(
trust(alice, usd2(400)));
6345 env(
pay(gw2, alice, usd2(200)));
6346 env(
trust(bob, usd2(400)));
6348 env(
offer(alice, mpt(100), usd2(100)));
6349 env(
offer(bob, usd2(100), mpt1(100)));
6357 BEAST_EXPECT(srcAmt == mpt(100));
6358 BEAST_EXPECT(dstAmt == mpt1(100));
6360 pathSet.
size() == 1 &&
6364 env(
pay(carol, dan, mpt1(10)),
6367 Txflags(tfNoRippleDirect | tfPartialPayment));
6371 std::tie(pathSet, srcAmt, dstAmt) =
findPaths(env, carol, dan, mpt1(-1), mpt(-1));
6372 BEAST_EXPECT(srcAmt == mpt(90));
6373 BEAST_EXPECT(dstAmt == mpt1(90));
6375 pathSet.
size() == 1 &&
6379 env(
pay(carol, dan, mpt1(10)),
6382 Txflags(tfNoRippleDirect | tfPartialPayment));
6386 std::tie(pathSet, srcAmt, dstAmt) =
6387 findPaths(env, carol, dan, mpt1(-1), std::nullopt, mpt.mpt());
6388 BEAST_EXPECT(srcAmt == mpt(80));
6389 BEAST_EXPECT(dstAmt == mpt1(80));
6391 pathSet.
size() == 1 &&
6395 env(
pay(carol, dan, mpt1(10)),
6398 Txflags(tfNoRippleDirect | tfPartialPayment));
6408 env.
fund(
XRP(1'000), gw, gw1, gw2, alice, bob, carol, dan);
6410 MPTTester mptTester(env, gw, {.holders = {alice, carol}, .fund =
false});
6412 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6413 auto const mpt = mptTester[
"MPT"];
6414 mptTester.authorize({.account = alice});
6415 mptTester.authorize({.account = carol});
6416 mptTester.pay(gw, carol, 200);
6418 MPTTester mptTester1(env, gw1, {.holders = {bob, alice}, .fund =
false});
6419 mptTester1.
create({.ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6420 auto const mpt1 = mptTester1[
"MPT1"];
6421 mptTester1.authorize({.account = alice});
6422 mptTester1.pay(gw1, alice, 200);
6423 mptTester1.authorize({.account = bob});
6425 MPTTester mptTester2(env, gw2, {.holders = {bob, dan}, .fund =
false});
6426 mptTester2.
create({.ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6427 auto const mpt2 = mptTester2[
"MPT2"];
6428 mptTester2.authorize({.account = bob});
6429 mptTester2.pay(gw2, bob, 200);
6430 mptTester2.authorize({.account = dan});
6432 env(
offer(alice, mpt(100), mpt1(100)));
6433 env(
offer(bob, mpt1(100), mpt2(100)));
6441 BEAST_EXPECT(srcAmt == mpt(100));
6442 BEAST_EXPECT(dstAmt == mpt2(100));
6444 pathSet.
size() == 1 &&
6447 stpath(
ipe(mptTester1.issuanceID()),
ipe(mptTester2.issuanceID())))))
6450 env(
pay(carol, dan, mpt2(10)),
6453 Txflags(tfNoRippleDirect | tfPartialPayment));
6457 std::tie(pathSet, srcAmt, dstAmt) =
findPaths(env, carol, dan, mpt2(-1), mpt(-1));
6458 BEAST_EXPECT(srcAmt == mpt(90));
6459 BEAST_EXPECT(dstAmt == mpt2(90));
6461 pathSet.
size() == 1 &&
6464 stpath(
ipe(mptTester1.issuanceID()),
ipe(mptTester2.issuanceID())))))
6467 env(
pay(carol, dan, mpt2(10)),
6470 Txflags(tfNoRippleDirect | tfPartialPayment));
6474 std::tie(pathSet, srcAmt, dstAmt) =
6475 findPaths(env, carol, dan, mpt2(-1), std::nullopt, mpt.mpt());
6476 BEAST_EXPECT(srcAmt == mpt(80));
6477 BEAST_EXPECT(dstAmt == mpt2(80));
6479 pathSet.
size() == 1 &&
6482 stpath(
ipe(mptTester1.issuanceID()),
ipe(mptTester2.issuanceID())))))
6485 env(
pay(carol, dan, mpt2(10)),
6488 Txflags(tfNoRippleDirect | tfPartialPayment));
6504 env.
fund(
XRP(1'000'000), gw1);
6505 env.
fund(
XRP(1'000'000), carol);
6506 env.
fund(
XRP(1'000'000), dan);
6507 env.
fund(
XRP(1'000'000), bob);
6508 env.
fund(
XRP(1'000'000), john);
6509 env.
fund(
XRP(1'000'000), sean);
6512 MPTTester usdTester(env, gw, {.holders = {carol, dan}, .fund =
false});
6516 .flags = tfMPTCanTransfer | tfMPTCanTrade});
6517 auto const usd = usdTester[
"USD"];
6518 env(
offer(carol,
XRP(100), usd(100)));
6520 MPTTester gbpTester(env, gw, {.holders = {bob, sean}, .fund =
false});
6523 .pay = {{{bob}, 100}},
6524 .flags = tfMPTCanTransfer | tfMPTCanTrade});
6525 auto const gbp = gbpTester[
"GBP"];
6527 MPTTester usd1(env, gw1, {.holders = {bob, dan}, .fund =
false});
6530 .pay = {{{dan}, 100}},
6531 .flags = tfMPTCanTransfer | tfMPTCanTrade});
6532 auto const usD1 = usd1[
"USD1"];
6533 env(
offer(bob, usd1(100), gbp(100)));
6537 auto const [pathSet, srcAmt, dstAmt] =
findPaths(env, john, sean, gbp(-1),
XRP(-1));
6538 BEAST_EXPECT(pathSet.empty());
6540 env(
pay(john, sean, gbp(10)),
6542 Path(~usd, dan, gw1, ~gbp),
6543 Txflags(tfNoRippleDirect | tfPartialPayment),
7111 using namespace jtx;
7116 auto const usd = gw[
"USD"];
7122 fund(env, gw, {alice, carol, bob},
XRP(1'000), {usd(1'000)});
7124 MPTTester mptTester(env, gw, {.fund =
false});
7125 mptTester.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
7126 auto const mpt = mptTester[
"MPT"];
7127 mptTester.authorize({.account = alice});
7128 mptTester.authorize({.account = carol});
7129 mptTester.pay(gw, alice, 1'000);
7130 mptTester.pay(gw, carol, 1'000);
7132 MPTTester mptTester1(env, gw, {.fund =
false});
7133 mptTester1.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
7134 auto const mpt1 = mptTester1[
"MPT1"];
7135 mptTester1.authorize({.account = alice});
7136 mptTester1.authorize({.account = carol});
7137 mptTester1.pay(gw, alice, 1'000);
7138 mptTester1.pay(gw, carol, 1'000);
7144 for (
auto& pool : pools)
7146 AMM amm(env, gw, std::get<0>(pool), std::get<1>(pool));
7147 amm.deposit(alice, std::get<2>(pool));
7148 amm.deposit(carol, std::get<2>(pool));
7152 .account = bob, .tokens = std::get<2>(pool), .err =
Ter(
tecNO_AUTH)});
7153 amm.withdrawAll(alice);
7154 amm.withdrawAll(carol);
7155 amm.withdrawAll(gw);
7156 BEAST_EXPECT(!
amm.ammExists());
7164 env.
fund(
XRP(1'000), gw, alice, carol);
7167 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, carol}});
7168 MPT const eur =
MPTTester({.env = env, .issuer = gw, .holders = {alice, carol}});
7170 env(
pay(gw, alice, eur(100)));
7172 AMM const amm(env, gw, usd(1'100), eur(1'000));
7174 env(
pay(alice, carol, usd(100)),
Sendmax(eur(100)));
7176 BEAST_EXPECT(
amm.expectBalances(usd(1'000), eur(1'100),
amm.tokens()));
7177 BEAST_EXPECT(env.
balance(carol, usd) == usd(100));
7178 BEAST_EXPECT(env.
balance(alice, eur) == eur(0));
7185 env.
fund(
XRP(1'000), gw, alice, carol);
7188 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, carol}});
7189 MPT const eur =
MPTTester({.env = env, .issuer = gw, .holders = {alice, carol}});
7190 MPT const btc =
MPTTester({.env = env, .issuer = gw, .holders = {alice, carol}});
7191 env(
pay(gw, alice, eur(100)));
7193 AMM const ammEurUsd(env, gw, eur(1'000), usd(1'100));
7194 AMM const ammUsdBtc(env, gw, usd(1'000), btc(1'100));
7196 env(
pay(alice, carol, btc(100)),
7203 BEAST_EXPECT(env.
balance(carol, btc) == btc(100));
7204 BEAST_EXPECT(env.
balance(alice, eur) == eur(0));
7211 env.
fund(
XRP(1'000), gw, alice);
7214 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
7215 MPT const eur =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
7217 env(
pay(gw, alice, eur(1'000)));
7219 AMM const amm(env, gw, eur(1'000'000), usd(1'001'000));
7221 env(
offer(alice, usd(1'000), eur(1'000)));
7223 BEAST_EXPECT(
amm.expectBalances(usd(1'000'000), eur(1'001'000),
amm.tokens()));
7224 BEAST_EXPECT(env.
balance(alice, usd) == usd(1'000));
7225 BEAST_EXPECT(env.
balance(alice, eur) == eur(0));
7230 env.
fund(
XRP(1'000'000), gw, alice, carol);
7232 auto const increment = env.
current()->fees().increment;
7233 auto const txfee =
Fee(
drops(increment));
7236 auto const makeMPT =
7251 return makeMPT(tfMPTCanLock | tfMPTCanTrade, holders,
pay);
7255 return makeMPT(tfMPTCanLock | tfMPTCanTransfer, holders,
pay);
7260 auto usd = makeDexMPT();
7261 auto eur = makeDexMPT({alice}, 1'000'000);
7263 auto createDeleteAMM = [&](
auto const& asset,
Account const& lp) {
7270 amm.withdrawAll(lp);
7271 BEAST_EXPECT(!
amm.ammExists());
7274 auto createFail = [&](
auto const& asset,
Account const& account,
auto const& err) {
7275 auto const createJv =
AMM::createJv(account, asset(1'000), eur(1'000), 0);
7276 env(createJv, txfee,
Ter(err));
7287 usd.authorize({.account = alice});
7288 env(
pay(gw, alice, usd(1'000'000)), txfee);
7290 createDeleteAMM(usd, alice);
7294 usd.set({.flags = tfMPTLock});
7300 usd.set({.flags = tfMPTUnlock});
7301 usd.set({.flags = tfMPTSetRequireAuth});
7304 createDeleteAMM(usd, gw);
7307 usd.authorize({.account = gw, .holder = alice});
7308 createDeleteAMM(usd, alice);
7312 auto usd2 = makeNoTransferMPT({alice}, 1'000'000);
7317 createDeleteAMM(usd2, gw);
7318 usd2.set({.flags = tfMPTSetCanTransfer});
7320 createDeleteAMM(usd2, alice);
7325 auto usd3 = makeNoTradeMPT({alice}, 1'000'000);
7330 usd3.set({.flags = tfMPTSetCanTrade});
7332 createDeleteAMM(usd3, alice);
7338 auto usd = makeDexMPT();
7339 auto eur = makeDexMPT({alice}, 1'000'000);
7340 AMM amm(env, gw, usd(1'000), eur(1'000));
7358 usd.authorize({.account = carol});
7359 env(
pay(gw, carol, usd(1'000'000)));
7360 eur.authorize({.account = carol});
7361 env(
pay(gw, carol, eur(1'000'000)));
7365 usd.set({.flags = tfMPTLock});
7368 for (
auto const& account : {carol, gw})
7371 {.account = account,
7376 {.account = account,
7381 usd.set({.flags = tfMPTUnlock});
7385 usd.set({.flags = tfMPTSetRequireAuth});
7398 amm.deposit({.account = gw, .tokens = 1'000});
7400 usd.authorize({.account = gw, .holder = carol});
7401 amm.deposit({.account = carol, .tokens = 1'000});
7410 .flags = tfMPTUnauthorize,
7415 auto usd2 = makeNoTransferMPT({carol}, 1'000'000);
7416 AMM amm2(env, gw, usd2(1'000), eur(1'000));
7421 .asset1In = usd2(1),
7430 amm2.deposit({.account = gw, .tokens = 1'000});
7431 usd2.set({.flags = tfMPTSetCanTransfer});
7433 amm2.deposit({.account = carol, .tokens = 1'000});
7439 auto usd = makeDexMPT();
7440 auto eur = makeDexMPT({carol}, 1'000'000);
7441 AMM amm(env, gw, usd(1'000), eur(1'000));
7443 usd.authorize({.account = carol});
7444 env(
pay(gw, carol, usd(1'000'000)));
7446 amm.deposit({.account = carol, .tokens = 1'000});
7453 .asset2Out = eur(1),
7461 usd.set({.flags = tfMPTLock});
7462 auto const fix330 = env.
current()->rules().enabled(fixCleanup3_3_0);
7467 .asset1Out = usd(1),
7468 .asset2Out = eur(1),
7470 amm.withdraw({.account = carol, .tokens = 1'000, .err =
Ter(
tecLOCKED)});
7473 {.account = carol, .asset1Out = eur(1), .assets =
std::make_pair(eur, usd)});
7479 auto const replenish = [&](
IOUAmount tokens) {
7480 usd.set({.flags = tfMPTUnlock});
7481 amm.deposit({.account = gw, .tokens = tokens});
7482 usd.set({.flags = tfMPTLock});
7486 {.account = gw, .asset1Out = usd(1), .asset2Out = eur(1), .err = gwLockErr});
7490 amm.withdraw({.account = gw, .tokens = 1'000, .err = gwLockErr});
7496 {.account = gw, .asset1Out = eur(1), .assets =
std::make_pair(eur, usd)});
7498 usd.set({.flags = tfMPTUnlock});
7501 usd.set({.flags = tfMPTSetRequireAuth});
7502 usd.authorize({.account = gw, .holder = carol, .flags = tfMPTUnauthorize});
7506 .asset1Out = usd(1),
7507 .asset2Out = eur(1),
7511 {.account = carol, .asset1Out = eur(1), .assets =
std::make_pair(eur, usd)});
7513 amm.withdraw({.account = gw, .asset1Out = usd(1), .asset2Out = eur(1)});
7515 usd.authorize({.account = gw, .holder = carol});
7516 amm.withdraw({.account = carol, .asset1Out = usd(1), .asset2Out = eur(1)});
7520 auto usd2 = makeNoTransferMPT({carol}, 1'000'000);
7521 AMM amm2(env, gw, usd2(1'000), eur(1'000));
7526 env(
pay(gw, carol,
STAmount{amm2.lptIssue(), 100}));
7530 amm2.withdraw({.account = carol, .asset1Out = usd2(1), .asset2Out = eur(1)});
7534 .asset1Out = eur(1),
7537 amm2.withdraw({.account = gw, .asset1Out = usd2(1), .asset2Out = eur(1)});
7540 usd2.authorize({.account = bob});
7542 usd2.authorize({.account = bob, .flags = tfMPTUnauthorize});
7544 env(
pay(carol, gw, usd2(1)));
7545 usd2.set({.flags = tfMPTSetCanTransfer});
7547 amm2.withdraw({.account = carol, .asset1Out = usd2(1), .asset2Out = eur(1)});
7552 auto usd3 = makeDexMPT();
7553 auto eur3 = makeDexMPT({carol}, 1'000'000);
7554 AMM amm3(env, gw, usd3(1'000), eur3(1'000));
7560 .asset1In = eur3(1'000),
7564 amm3.withdraw({.account = carol, .asset1Out = usd3(100)});