889 auto const mpTokensV2 = features[featureMPTokensV2];
895 Env env{*
this, features - featureMPTokensV1};
908 Env env{*
this, features - featureMPTokensV1};
911 auto const usd = alice[
"USD"];
918 jv[jss::secret] = alice.
name();
919 jv[jss::tx_json] =
pay(alice, carol, mpt);
921 auto const jrr = env.
rpc(
"json",
"submit",
to_string(jv));
922 BEAST_EXPECT(jrr[jss::result][jss::engine_result] ==
"temDISABLED");
927 Env env{*
this, features};
929 MPTTester mptAlice(env, alice, {.holders = {bob}});
931 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
932 auto const mpt = mptAlice[
"MPT"];
934 mptAlice.authorize({.account = bob});
937 env(
pay(alice, bob, mpt(10)),
Txflags(tfNoRippleDirect), err);
939 env(
pay(alice, bob, mpt(10)),
Txflags(tfLimitQuality), err);
944 Env env{*
this, features};
948 MPTTester mptAlice(env, alice, {.holders = {carol}});
951 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
953 mptAlice.authorize({.account = carol});
957 auto const mpt = mptAlice[
"MPT"];
962 auto const usd = alice[
"USD"];
964 env(
pay(alice, carol, usd(100)),
Sendmax(mpt(100)), err);
972 env(
pay(alice, carol, mpT1(100)),
Sendmax(mpt(100)), err);
975 env(
pay(alice, carol, mpt(100)),
Path(~usd), err);
980 Env env{*
this, features - featureMPTokensV2};
984 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
986 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
987 auto const mpt = mptAlice[
"MPT"];
989 mptAlice.authorize({.account = carol});
992 payment[jss::secret] = alice.
name();
993 payment[jss::tx_json] =
pay(alice, carol, mpt(100));
995 payment[jss::build_path] =
true;
996 auto jrr = env.
rpc(
"json",
"submit",
to_string(payment));
997 BEAST_EXPECT(jrr[jss::result][jss::error] ==
"invalidParams");
999 jrr[jss::result][jss::error_message] ==
1000 "Field 'build_path' not allowed in this context.");
1005 Env env{*
this, features};
1007 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1009 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1010 auto const mpt = mptAlice[
"MPT"];
1012 mptAlice.authorize({.account = bob});
1013 mptAlice.authorize({.account = carol});
1026 Env env{*
this, features};
1028 MPTTester mptAlice(env, alice, {.holders = {bob}});
1030 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1032 mptAlice.authorize({.account = bob});
1041 Env env{*
this, features};
1043 MPTTester mptAlice(env, alice, {.holders = {bob}});
1045 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1047 mptAlice.authorize({.account = bob});
1060 Env env{*
this, features};
1062 MPTTester mptAlice(env, alice, {.holders = {bob}});
1065 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer});
1067 mptAlice.authorize({.account = bob});
1075 Env env{*
this, features};
1077 MPTTester mptAlice(env, alice, {.holders = {bob}});
1080 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer});
1083 mptAlice.authorize({.account = bob});
1086 mptAlice.authorize({.account = alice, .holder = bob});
1089 mptAlice.pay(alice, bob, 100);
1092 mptAlice.authorize({.account = alice, .holder = bob, .flags = tfMPTUnauthorize});
1099 if (features[featureSingleAssetVault] && features[featurePermissionedDomains])
1103 Env env{*
this, features};
1105 Account const credIssuer1{
"credIssuer1"};
1106 env.
fund(
XRP(1000), credIssuer1, bob);
1108 auto const domainId1 = [&]() {
1110 {.issuer = credIssuer1, .credType = credType}};
1129 .flags = tfMPTRequireAuth | tfMPTCanTransfer,
1130 .domainID = domainId1,
1133 mptAlice.authorize({.account = bob});
1137 mptAlice.pay(alice, bob, 100);
1138 mptAlice.set({.domainID =
uint256{}});
1145 Env env{*
this, features};
1147 Account const credIssuer1{
"credIssuer1"};
1148 env.
fund(
XRP(1000), credIssuer1, bob);
1150 auto const domainId1 = [&]() {
1152 {.issuer = credIssuer1, .credType = credType}};
1171 .flags = tfMPTRequireAuth | tfMPTCanTransfer,
1172 .domainID = domainId1,
1176 mptAlice.authorize({.account = bob});
1179 mptAlice.authorize({.account = alice, .holder = bob});
1182 mptAlice.pay(alice, bob, 100);
1185 mptAlice.authorize({.account = alice, .holder = bob, .flags = tfMPTUnauthorize});
1188 mptAlice.pay(bob, alice, 10);
1190 mptAlice.set({.domainID =
uint256{}});
1198 Env env{*
this, features};
1201 Account const credIssuer1{
"credIssuer1"};
1204 Account const credIssuer2{
"credIssuer2"};
1205 env.
fund(
XRP(1000), credIssuer1, credIssuer2, bob, carol);
1207 auto const domainId1 = [&]() {
1209 {.issuer = credIssuer1, .credType = credType}};
1218 auto const domainId2 = [&]() {
1220 {.issuer = credIssuer1, .credType = credType},
1221 {.issuer = credIssuer2, .credType = credType}};
1248 .flags = tfMPTRequireAuth | tfMPTCanTransfer,
1249 .domainID = domainId1,
1253 mptAlice.authorize({.account = bob});
1254 mptAlice.authorize({.account = carol});
1258 mptAlice.pay(alice, bob, 50);
1269 mptAlice.set({.domainID = domainId2});
1271 mptAlice.pay(alice, carol, 10);
1276 mptAlice.pay(bob, carol, 10);
1290 Env env(*
this, features);
1295 MPTTester mptAlice(env, alice, {.holders = {bob, cindy}});
1298 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1301 mptAlice.authorize({.account = bob});
1304 mptAlice.authorize({.account = cindy});
1307 mptAlice.pay(alice, bob, 100);
1314 mptAlice.pay(bob, alice, 10);
1319 Env env{*
this, features};
1321 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1323 mptAlice.
create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
1337 Env env{*
this, features};
1339 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1341 mptAlice.
create({.ownerCount = 1, .flags = tfMPTCanTransfer});
1343 mptAlice.authorize({.account = bob});
1344 mptAlice.authorize({.account = carol});
1346 mptAlice.pay(alice, bob, 100);
1357 Env env{*
this, features};
1359 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1361 mptAlice.
create({.ownerCount = 1, .flags = tfMPTCanLock | tfMPTCanTransfer});
1363 mptAlice.authorize({.account = bob});
1364 mptAlice.authorize({.account = carol});
1366 mptAlice.pay(alice, bob, 100);
1367 mptAlice.pay(alice, carol, 100);
1373 mptAlice.set({.account = alice, .flags = tfMPTLock});
1375 mptAlice.pay(bob, carol, 1, err);
1376 mptAlice.pay(carol, bob, 2, err);
1378 mptAlice.pay(alice, bob, 3);
1380 mptAlice.pay(bob, alice, 4);
1383 mptAlice.set({.account = alice, .flags = tfMPTUnlock});
1385 mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
1387 mptAlice.pay(bob, carol, 5, err);
1388 mptAlice.pay(carol, bob, 6, err);
1390 mptAlice.pay(alice, bob, 7);
1392 mptAlice.pay(bob, alice, 8);
1397 Env env{*
this, features};
1399 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1403 {.transferFee = 10'000,
1406 .flags = tfMPTCanTransfer});
1409 mptAlice.authorize({.account = bob});
1410 mptAlice.authorize({.account = carol});
1413 mptAlice.pay(alice, bob, 2'000);
1416 mptAlice.pay(bob, alice, 1'000);
1417 BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 1'000));
1427 auto const mpt = mptAlice[
"MPT"];
1433 env(
pay(bob, carol, mpt(100)),
Sendmax(mpt(110)));
1435 env(
pay(bob, carol, mpt(100)),
Sendmax(mpt(115)));
1436 BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 780));
1437 BEAST_EXPECT(mptAlice.checkMPTokenAmount(carol, 200));
1443 BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 690));
1447 mptAlice.checkMPTokenAmount(carol, !features[featureMPTokensV2] ? 282 : 281));
1452 Env env{*
this, features};
1454 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1456 mptAlice.
create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
1459 mptAlice.authorize({.account = bob});
1460 mptAlice.authorize({.account = carol});
1461 mptAlice.pay(alice, bob, 1'000);
1463 auto const mpt = mptAlice[
"MPT"];
1469 env(
pay(bob, carol, mpt(100)),
Sendmax(mpt(100)));
1470 BEAST_EXPECT(mptAlice.checkMPTokenAmount(carol, 100));
1473 BEAST_EXPECT(mptAlice.checkMPTokenAmount(carol, 199));
1478 Env env{*
this, features};
1480 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1482 mptAlice.
create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
1485 mptAlice.authorize({.account = bob});
1486 mptAlice.authorize({.account = carol});
1487 mptAlice.pay(alice, bob, 1'000);
1489 auto const mpt = mptAlice[
"MPT"];
1492 env(
pay(bob, alice, mpt(100)),
1498 env(
pay(bob, alice, mpt(100)),
1506 Env env{*
this, features};
1508 MPTTester mptAlice(env, alice, {.holders = {bob}});
1511 {.maxAmt = 100, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
1513 mptAlice.authorize({.account = bob});
1516 mptAlice.pay(alice, bob, 100);
1520 mptAlice.pay(alice, bob, 1, err);
1526 Env env{*
this, features};
1528 MPTTester mptAlice(env, alice, {.holders = {bob}});
1530 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1532 mptAlice.authorize({.account = bob});
1539 mptAlice.pay(alice, bob, 1, err);
1545 Env env{*
this, features};
1546 env.
fund(
XRP(1'000), alice, bob);
1549 jv[jss::secret] = alice.
name();
1550 jv[jss::tx_json] =
pay(alice, bob, mpt);
1552 auto const jrr = env.
rpc(
"json",
"submit",
to_string(jv));
1553 BEAST_EXPECT(jrr[jss::result][jss::error] ==
"invalidParams");
1559 Env env{*
this, features};
1561 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1568 .flags = tfMPTCanTransfer});
1569 auto const mpt = mptAlice[
"MPT"];
1571 mptAlice.authorize({.account = bob});
1572 mptAlice.authorize({.account = carol});
1575 mptAlice.pay(alice, bob, 10'000);
1578 env(
pay(bob, carol, mpt(10'000)),
Sendmax(mpt(10'000)),
Txflags(tfPartialPayment));
1584 meta[0u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
1585 [sfOutstandingAmount.fieldName] ==
"9990");
1588 meta[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
1589 [sfMPTAmount.fieldName] ==
"9990");
1592 meta[2u][sfModifiedNode.fieldName][sfPreviousFields.fieldName]
1593 [sfMPTAmount.fieldName] ==
"10000");
1594 BEAST_EXPECT(!meta[2u][sfModifiedNode.fieldName][sfFinalFields.fieldName].isMember(
1595 sfMPTAmount.fieldName));
1600 env(
pay(bob, carol, mpt(10'000)),
Sendmax(mpt(10'000)),
Ter(err));
1605 Env env{*
this, features};
1607 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1613 .flags = tfMPTCanTransfer});
1614 auto const mpt = mptAlice[
"MPT"];
1616 mptAlice.authorize({.account = bob});
1617 mptAlice.authorize({.account = carol});
1628 BEAST_EXPECT(mptAlice.checkMPTokenOutstandingAmount(0));
1633 Env env{*
this, features};
1635 MPTTester mptAlice(env, alice, {.holders = {bob}});
1637 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1639 mptAlice.authorize({.account = bob});
1642 mptAlice.destroy({.ownerCount = 0});
1651 Env env{*
this, features};
1653 env.
fund(
XRP(1'000), alice, bob);
1658 env(
pay(alice, bob, mpt), err);
1664 Env env{*
this, features};
1666 MPTTester mptAlice(env, alice, {.holders = {bob}});
1668 mptAlice.
create({.ownerCount = 1, .holderCount = 0});
1671 mptAlice.destroy({.ownerCount = 0});
1681 Env env{*
this, features};
1686 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1688 mptAlice.
create({.maxAmt = 100, .ownerCount = 1, .flags = tfMPTCanTransfer});
1690 mptAlice.authorize({.account = bob});
1691 mptAlice.authorize({.account = carol});
1693 mptAlice.pay(alice, bob, 100);
1696 mptAlice.pay(bob, carol, 100);
1701 Env env{*
this, features};
1703 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1705 mptAlice.
create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
1707 mptAlice.authorize({.account = bob});
1708 mptAlice.authorize({.account = carol});
1711 mptAlice.pay(alice, bob, 100);
1714 mptAlice.pay(bob, alice, 100);
1717 mptAlice.pay(alice, bob, 100);
1718 mptAlice.pay(bob, carol, 50);
2120 FeatureBitset const withoutFix = features - fixCleanup3_2_0;
2122 FeatureBitset const withoutFixAndV2 = withoutFix - featureMPTokensV2;
2123 FeatureBitset const withFixAndWithoutV2 = withFix - featureMPTokensV2;
2134 MPTValue
const alice0 = 10'000;
2135 MPTValue
const gw0 = -20'000;
2147 TER issuerToHolderPreFixTer;
2148 TER holderSourcePreFixTer;
2149 MPTValue issuerToHolderAliceAfterPreFix;
2150 MPTValue issuerToHolderIssuerAfterPreFix;
2151 MPTValue issuerToHolderAliceAfterPostFix;
2152 MPTValue issuerToHolderIssuerAfterPostFix;
2156 { .name=
"INT64_MAX + 1", .mantissa=firstInvalidMPTMantissa, .negative=
false, .mptValue=mptMin, .issuerToHolderPreFixTer=invariantFailed, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0, .issuerToHolderIssuerAfterPreFix=gw0, .issuerToHolderAliceAfterPostFix=alice0 - 1, .issuerToHolderIssuerAfterPostFix=gw0 + 1},
2157 { .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},
2158 { .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},
2159 { .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},
2160 { .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},
2161 { .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},
2162 { .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}
2165 auto const badMPTAmount = [&](
MPTIssue const& issue, BadMPTAmount
const& bad) {
2168 auto const makeIssue = [&](
Env& env) {
2172 .holders = {alice, bob},
2174 .flags = tfMPTCanTransfer | tfMPTCanTrade | tfMPTCanEscrow | tfMPTCanClawback}};
2177 auto const withNonCanonicalMPTAmount =
2181 tx.
sign(signer.pk(), signer.sk());
2185 auto const roundTrip = [](
STTx const& tx) {
2191 auto const expectRoundTripBadMPT =
2192 [&](
JTx const& jt,
SField const& field, BadMPTAmount
const& bad) {
2193 auto const roundTripped = roundTrip(*jt.
stx);
2194 auto const persisted = roundTripped.getFieldAmount(field);
2195 BEAST_EXPECT(persisted.holds<
MPTIssue>());
2196 BEAST_EXPECT(persisted.mantissa() == bad.mantissa);
2197 BEAST_EXPECT(persisted.exponent() == 0);
2198 BEAST_EXPECT(persisted.negative() == bad.negative);
2199 BEAST_EXPECT(persisted.mpt().value() == bad.mptValue);
2204 for (
auto const& bad : badMPTAmounts)
2206 testcase(
"fixCleanup3_2_0 rejects non-canonical MPT Payment amounts");
2208 Env env{*
this, withoutFixAndV2};
2209 env.
fund(
XRP(100'000), alice, bob, gw);
2211 auto const issue = makeIssue(env);
2213 auto const badAmount = badMPTAmount(issue, bad);
2214 auto malformedHolderToHolder = withNonCanonicalMPTAmount(
2219 expectRoundTripBadMPT(malformedHolderToHolder, sfAmount, bad);
2220 malformedHolderToHolder.ter = bad.holderSourcePreFixTer;
2221 env.
submit(malformedHolderToHolder);
2226 (env.balance(bob, issue).value() ==
STAmount{MPTAmount{10'000}, issue}));
2228 (env.balance(gw, issue).value() ==
STAmount{MPTAmount{-20'000}, issue}));
2230 env.enableFeature(fixCleanup3_2_0);
2235 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'001}, issue}));
2237 (env.balance(bob, issue).value() == STAmount{MPTAmount{9'999}, issue}));
2239 (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue}));
2243 env.fund(
XRP(100'000), alice, bob, gw);
2245 auto const issue = makeIssue(env);
2247 auto const badAmount = badMPTAmount(issue, bad);
2248 auto malformedIssuerToHolder = withNonCanonicalMPTAmount(
2253 expectRoundTripBadMPT(malformedIssuerToHolder, sfAmount, bad);
2254 malformedIssuerToHolder.ter = bad.issuerToHolderPreFixTer;
2255 env.submit(malformedIssuerToHolder);
2258 (env.balance(alice, issue).value() ==
2259 STAmount{MPTAmount{bad.issuerToHolderAliceAfterPreFix}, issue}));
2261 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2263 (env.balance(gw, issue).value() ==
2264 STAmount{MPTAmount{bad.issuerToHolderIssuerAfterPreFix}, issue}));
2266 env.enableFeature(fixCleanup3_2_0);
2271 (env.balance(alice, issue).value() ==
2272 STAmount{MPTAmount{bad.issuerToHolderAliceAfterPostFix}, issue}));
2274 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2276 (env.balance(gw, issue).value() ==
2277 STAmount{MPTAmount{bad.issuerToHolderIssuerAfterPostFix}, issue}));
2280 Env env{*
this, withoutFixAndV2};
2281 env.fund(
XRP(100'000), alice, bob, gw);
2283 auto const issue = makeIssue(env);
2285 auto const badAmount = badMPTAmount(issue, bad);
2286 auto malformedHolderToIssuer = withNonCanonicalMPTAmount(
2291 expectRoundTripBadMPT(malformedHolderToIssuer, sfAmount, bad);
2292 malformedHolderToIssuer.ter = bad.holderSourcePreFixTer;
2293 env.submit(malformedHolderToIssuer);
2296 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2298 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2300 (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue}));
2302 env.enableFeature(fixCleanup3_2_0);
2307 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'001}, issue}));
2309 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2311 (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'001}, issue}));
2314 Env env{*
this, withFixAndWithoutV2};
2315 env.fund(
XRP(100'000), alice, bob, gw);
2317 auto const issue = makeIssue(env);
2319 auto const badAmount = badMPTAmount(issue, bad);
2320 auto tx = withNonCanonicalMPTAmount(
2329 Env env{*
this, withFixAndWithoutV2};
2330 env.fund(
XRP(100'000), alice, bob, gw);
2332 auto const issue = makeIssue(env);
2334 auto const badAmount = badMPTAmount(issue, bad);
2335 auto tx = withNonCanonicalMPTAmount(
2344 Env env{*
this, withFixAndWithoutV2};
2345 env.fund(
XRP(100'000), alice, bob, gw);
2347 auto const issue = makeIssue(env);
2349 auto const badAmount = badMPTAmount(issue, bad);
2350 auto tx = withNonCanonicalMPTAmount(
2359 testcase(
"fixCleanup3_2_0 rejects non-canonical MPT Check amounts");
2362 env.fund(
XRP(100'000), alice, bob, gw);
2364 auto const issue = makeIssue(env);
2366 auto const badSendMax = badMPTAmount(issue, bad);
2367 auto const checkSeq = env.seq(alice);
2368 auto tx = withNonCanonicalMPTAmount(
2369 env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})),
2377 auto const checkKeylet = keylet::check(alice.id(), checkSeq);
2378 auto const sleCheck = env.le(checkKeylet);
2379 BEAST_EXPECT((sleCheck !=
nullptr) == !bad.negative);
2380 if (sleCheck && !bad.negative)
2382 auto const persisted = sleCheck->getFieldAmount(sfSendMax);
2383 BEAST_EXPECT(persisted.holds<MPTIssue>());
2384 BEAST_EXPECT(persisted.mantissa() == bad.mantissa);
2385 BEAST_EXPECT(persisted.negative() == bad.negative);
2390 env.fund(
XRP(100'000), alice, bob, gw);
2392 auto const issue = makeIssue(env);
2394 auto const badSendMax = badMPTAmount(issue, bad);
2395 auto const checkSeq = env.seq(alice);
2396 auto tx = withNonCanonicalMPTAmount(
2397 env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})),
2405 auto const checkKeylet = keylet::check(alice.id(), checkSeq);
2406 BEAST_EXPECT((env.le(checkKeylet) !=
nullptr) == !bad.negative);
2411 env(env.jt(check::cancel(alice, checkKeylet.key)), Ter{tesSUCCESS});
2413 BEAST_EXPECT(env.le(checkKeylet) ==
nullptr);
2418 env.fund(
XRP(100'000), alice, bob, gw);
2420 auto const issue = makeIssue(env);
2422 auto const badSendMax = badMPTAmount(issue, bad);
2423 auto const checkSeq = env.seq(alice);
2424 auto tx = withNonCanonicalMPTAmount(
2425 env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})),
2433 auto const checkKeylet = keylet::check(alice.id(), checkSeq);
2434 BEAST_EXPECT((env.le(checkKeylet) !=
nullptr) == !bad.negative);
2437 env.enableFeature(fixCleanup3_2_0);
2442 env(env.jt(check::cancel(alice, checkKeylet.key)), Ter{tesSUCCESS});
2444 BEAST_EXPECT(env.le(checkKeylet) ==
nullptr);
2449 env.fund(
XRP(100'000), alice, bob, gw);
2451 auto const issue = makeIssue(env);
2453 auto const badSendMax = badMPTAmount(issue, bad);
2454 auto const checkSeq = env.seq(alice);
2455 auto tx = withNonCanonicalMPTAmount(
2456 env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})),
2464 auto const checkKeylet = keylet::check(alice.id(), checkSeq);
2465 BEAST_EXPECT((env.le(checkKeylet) !=
nullptr) == !bad.negative);
2468 env.enableFeature(fixCleanup3_2_0);
2471 auto const cashAmount = STAmount{sfAmount, issue,
std::uint64_t{1}, 0,
false};
2472 env(env.jt(check::cash(bob, checkKeylet.key, cashAmount)), Ter{tefBAD_LEDGER});
2474 BEAST_EXPECT(env.le(checkKeylet) !=
nullptr);
2478 Env env{*
this, withoutFix};
2479 env.fund(
XRP(100'000), alice, bob, gw);
2481 auto const issue = makeIssue(env);
2483 auto const sendMax = STAmount{sfSendMax, issue,
std::uint64_t{10}, 0,
false};
2484 auto const checkSeq = env.seq(alice);
2485 env(env.jt(check::create(alice, bob, sendMax)), Ter{tesSUCCESS});
2488 auto const badCashAmount = badMPTAmount(issue, bad);
2489 auto tx = withNonCanonicalMPTAmount(
2493 keylet::check(alice.id(), checkSeq).key,
2494 STAmount{issue, std::uint64_t{1}})),
2498 expectRoundTripBadMPT(tx, sfAmount, bad);
2499 tx.ter = bad.holderSourcePreFixTer;
2502 BEAST_EXPECT(env.le(keylet::check(alice.id(), checkSeq)) !=
nullptr);
2504 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2506 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2508 (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue}));
2511 Env env{*
this, withFix};
2512 env.fund(
XRP(100'000), alice, bob, gw);
2514 auto const issue = makeIssue(env);
2516 auto const sendMax = STAmount{sfSendMax, issue,
std::uint64_t{10}, 0,
false};
2517 auto const checkSeq = env.seq(alice);
2518 env(env.jt(check::create(alice, bob, sendMax)), Ter{tesSUCCESS});
2521 auto const badCashAmount = badMPTAmount(issue, bad);
2522 auto tx = withNonCanonicalMPTAmount(
2526 keylet::check(alice.id(), checkSeq).key,
2527 STAmount{issue, std::uint64_t{1}})),
2535 testcase(
"fixCleanup3_2_0 rejects non-canonical MPT Escrow amounts");
2537 Env env{*
this, withoutFix};
2538 env.fund(
XRP(100'000), alice, bob, gw);
2540 auto const issue = makeIssue(env);
2542 auto const escrowSeq = env.seq(alice);
2543 auto const badAmount = badMPTAmount(issue, bad);
2544 auto tx = withNonCanonicalMPTAmount(
2546 escrow::create(alice, bob, STAmount{issue, std::uint64_t{1}}),
2547 escrow::kFinishTime(env.now() + 1s)),
2554 BEAST_EXPECT(env.le(keylet::escrow(alice.id(), escrowSeq)) ==
nullptr);
2557 Env env{*
this, withFix};
2558 env.fund(
XRP(100'000), alice, bob, gw);
2560 auto const issue = makeIssue(env);
2562 auto const badAmount = badMPTAmount(issue, bad);
2563 auto tx = withNonCanonicalMPTAmount(
2565 escrow::create(alice, bob, STAmount{issue, std::uint64_t{1}}),
2566 escrow::kFinishTime(env.now() + 1s)),
2574 testcase(
"fixCleanup3_2_0 rejects non-canonical MPT Clawback amounts");
2576 Env env{*
this, withoutFix};
2577 env.fund(
XRP(100'000), alice, bob, gw);
2579 auto const issue = makeIssue(env);
2581 auto const badAmount = badMPTAmount(issue, bad);
2582 auto tx = withNonCanonicalMPTAmount(
2587 expectRoundTripBadMPT(tx, sfAmount, bad);
2592 MPTValue
const bobAfter = bad.negative ? MPTValue{10'000} : MPTValue{0};
2593 MPTValue
const gwAfter = bad.negative ? MPTValue{-20'000} : MPTValue{-10'000};
2595 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2597 (env.balance(bob, issue).value() == STAmount{MPTAmount{bobAfter}, issue}));
2599 (env.balance(gw, issue).value() == STAmount{MPTAmount{gwAfter}, issue}));
2602 Env env{*
this, withFix};
2603 env.fund(
XRP(100'000), alice, bob, gw);
2605 auto const issue = makeIssue(env);
2607 auto const badAmount = badMPTAmount(issue, bad);
2608 auto tx = withNonCanonicalMPTAmount(
2618 (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2620 (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue}));
2622 (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue}));
2625 testcase(
"featureMPTokensV2 disabled rejects MPT OfferCreate amounts");
2627 Env env{*
this, withoutFixAndV2};
2628 env.fund(
XRP(100'000), alice, bob, gw);
2630 auto const issue = makeIssue(env);
2632 auto const badTakerPays = badMPTAmount(issue, bad);
2633 auto tx = withNonCanonicalMPTAmount(
2638 expectRoundTripBadMPT(tx, sfTakerPays, bad);
2643 Env env{*
this, withFixAndWithoutV2};
2644 env.fund(
XRP(100'000), alice, bob, gw);
2646 auto const issue = makeIssue(env);
2648 auto const badTakerPays = badMPTAmount(issue, bad);
2649 auto tx = withNonCanonicalMPTAmount(
2661 Env env{*
this, withFix};
2662 env.fund(
XRP(100'000), alice, bob, gw);
2664 auto const issue = makeIssue(env);
2666 auto const badTakerPays = badMPTAmount(issue, bad);
2667 auto tx = withNonCanonicalMPTAmount(
2679 Env env{*
this, withFix};
2680 env.fund(
XRP(100'000), alice, bob, gw);
2682 auto const issue = makeIssue(env);
2684 auto const badTakerGets = badMPTAmount(issue, bad);
2685 auto tx = withNonCanonicalMPTAmount(
2694 testcase(
"featureMPTokensV2 disabled rejects MPT AMMCreate amounts");
2696 Env env{*
this, withoutFixAndV2};
2697 env.fund(
XRP(100'000), alice, bob, gw);
2699 auto const issue = makeIssue(env);
2701 auto const badAmount = badMPTAmount(issue, bad);
2702 auto tx = withNonCanonicalMPTAmount(
2704 AMM::createJv(alice.id(), STAmount{issue, std::uint64_t{1}},
XRP(1), 0),
2705 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2709 expectRoundTripBadMPT(tx, sfAmount, bad);
2714 Env env{*
this, withFixAndWithoutV2};
2715 env.fund(
XRP(100'000), alice, bob, gw);
2717 auto const issue = makeIssue(env);
2719 auto const badAmount = badMPTAmount(issue, bad);
2720 auto tx = withNonCanonicalMPTAmount(
2722 AMM::createJv(alice.id(), STAmount{issue, std::uint64_t{1}},
XRP(1), 0),
2723 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2732 Env env{*
this, withFix};
2733 env.fund(
XRP(100'000), alice, bob, gw);
2735 auto const issue = makeIssue(env);
2737 auto const badAmount = badMPTAmount(issue, bad);
2738 auto tx = withNonCanonicalMPTAmount(
2740 AMM::createJv(alice.id(), STAmount{issue, std::uint64_t{1}},
XRP(1), 0),
2741 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2749 testcase(
"featureMPTokensV2 disabled rejects MPT AMMDeposit amounts");
2751 Env env{*
this, withoutFixAndV2};
2752 env.fund(
XRP(100'000), alice, bob, gw);
2754 auto const issue = makeIssue(env);
2756 auto const badAmount = badMPTAmount(issue, bad);
2757 auto tx = withNonCanonicalMPTAmount(
2761 .asset1In = STAmount{issue, std::uint64_t{1}},
2763 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2767 expectRoundTripBadMPT(tx, sfAmount, bad);
2772 Env env{*
this, withFixAndWithoutV2};
2773 env.fund(
XRP(100'000), alice, bob, gw);
2775 auto const issue = makeIssue(env);
2777 auto const badAmount = badMPTAmount(issue, bad);
2778 auto tx = withNonCanonicalMPTAmount(
2782 .asset1In = STAmount{issue, std::uint64_t{1}},
2784 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2793 Env env{*
this, withFix};
2794 env.fund(
XRP(100'000), alice, bob, gw);
2796 auto const issue = makeIssue(env);
2798 auto const badAmount = badMPTAmount(issue, bad);
2799 auto tx = withNonCanonicalMPTAmount(
2803 .asset1In = STAmount{issue, std::uint64_t{1}},
2805 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2813 testcase(
"featureMPTokensV2 disabled rejects MPT AMMWithdraw amounts");
2815 Env env{*
this, withoutFixAndV2};
2816 env.fund(
XRP(100'000), alice, bob, gw);
2818 auto const issue = makeIssue(env);
2820 auto const badAmount = badMPTAmount(issue, bad);
2821 auto tx = withNonCanonicalMPTAmount(
2825 .asset1Out = STAmount{issue, std::uint64_t{1}},
2827 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2831 expectRoundTripBadMPT(tx, sfAmount, bad);
2836 Env env{*
this, withFixAndWithoutV2};
2837 env.fund(
XRP(100'000), alice, bob, gw);
2839 auto const issue = makeIssue(env);
2841 auto const badAmount = badMPTAmount(issue, bad);
2842 auto tx = withNonCanonicalMPTAmount(
2846 .asset1Out = STAmount{issue, std::uint64_t{1}},
2848 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2857 Env env{*
this, withFix};
2858 env.fund(
XRP(100'000), alice, bob, gw);
2860 auto const issue = makeIssue(env);
2862 auto const badAmount = badMPTAmount(issue, bad);
2863 auto tx = withNonCanonicalMPTAmount(
2867 .asset1Out = STAmount{issue, std::uint64_t{1}},
2869 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2877 testcase(
"featureMPTokensV2 disabled rejects MPT AMMClawback amounts");
2879 Env env{*
this, withoutFixAndV2};
2880 env.fund(
XRP(100'000), alice, bob, gw);
2882 auto const issue = makeIssue(env);
2884 auto const badAmount = badMPTAmount(issue, bad);
2885 auto tx = withNonCanonicalMPTAmount(
2893 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2897 expectRoundTripBadMPT(tx, sfAmount, bad);
2902 Env env{*
this, withFixAndWithoutV2};
2903 env.fund(
XRP(100'000), alice, bob, gw);
2905 auto const issue = makeIssue(env);
2907 auto const badAmount = badMPTAmount(issue, bad);
2908 auto tx = withNonCanonicalMPTAmount(
2916 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2925 Env env{*
this, withFix};
2926 env.fund(
XRP(100'000), alice, bob, gw);
2928 auto const issue = makeIssue(env);
2930 auto const badAmount = badMPTAmount(issue, bad);
2931 auto tx = withNonCanonicalMPTAmount(
2939 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
2947 testcase(
"fixCleanup3_2_0 rejects non-canonical MPT VaultClawback amounts");
2949 Env env{*
this, withFix};
2950 env.fund(
XRP(100'000), alice, bob, gw);
2952 auto const issue = makeIssue(env);
2954 auto const badAmount = badMPTAmount(issue, bad);
2955 uint256 const fakeVaultId = keylet::vault(gw.id(), 1).key;
2956 auto tx = withNonCanonicalMPTAmount(
2962 .amount = STAmount{issue, std::uint64_t{1}}}),
2963 Fee(
static_cast<std::uint64_t>(env.current()->fees().increment.drops()))),
4127 auto const usd = gw[
"USD"];
4131 {tfMPTCanTrade | tfMPTCanLock | tfMPTCanClawback,
4132 tfMPTCanTrade | tfMPTRequireAuth,
4134 tfMPTCanTrade | tfMPTCanLock,
4137 Env env{*
this, features};
4138 env.
fund(
XRP(1'000), gw, alice);
4142 bool const lockMPToken = (flags & (tfMPTCanLock | tfMPTCanClawback)) == tfMPTCanLock;
4143 bool const lockMPTIssue =
4144 (flags & (tfMPTCanLock | tfMPTCanClawback)) == (tfMPTCanLock | tfMPTCanClawback);
4145 bool const requireAuth = (flags & tfMPTRequireAuth) != 0u;
4153 .authHolder =
true});
4154 MPT const btc = mptTester;
4157 mptTester.authorize({.account = gw, .holder = alice, .flags = tfMPTUnauthorize});
4160 mptTester.set({.holder = alice, .flags = tfMPTLock});
4162 else if (lockMPTIssue)
4164 mptTester.set({.flags = tfMPTLock});
4168 [&](
Account const& account,
auto const& buy,
auto const& sell,
bool buyUSD) {
4169 auto error = [&](
auto const err) ->
TER {
4184 if (flags & tfMPTCanTransfer)
4189 auto const err = buyUSD ? errBuy : errSell;
4191 auto seq(env.
seq(account));
4192 env(
offer(account, buy(10), sell(10)),
Ter(err));
4197 auto testOffers = [&](
Account const& account) {
4198 testOffer(account,
XRP, btc,
false);
4199 testOffer(account, btc,
XRP,
true);
4207 Env env{*
this, features - featureMPTokensV2};
4209 MPTTester mptTester(env, gw, {.holders = {alice}});
4211 mptTester.
create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
4213 mptTester.authorize({.account = alice});
4214 mptTester.pay(gw, alice, 200);
4223 env.
fund(
XRP(1'000), gw, alice);
4225 MPT const btc =
MPTTester({.env = env, .issuer = gw, .holders = {alice}, .pay = 100});
4226 MPT const eth =
MPT(gw, 1);
4235 env.
fund(
XRP(1'000), gw, alice);
4236 MPTTester const btc({.env = env, .issuer = gw, .holders = {alice}, .pay = 100});
4237 MPTTester const eth({.env = env, .issuer = gw});
4239 env(
offer(alice, eth(10), btc(10)));
4252 env.
fund(
XRP(1'000), gw, gw2, alice, carol, bob, dan);
4256 .holders = {alice, carol, bob, dan, gw},
4262 .holders = {alice, carol, bob, dan, gw2},
4273 btc.set({.holder = alice, .flags = tfMPTLock});
4275 btc.set({.holder = alice, .flags = tfMPTUnlock});
4277 btc.set({.holder = carol, .flags = tfMPTLock});
4279 btc.set({.holder = carol, .flags = tfMPTUnlock});
4281 env(
offer(gw2, eth(1), btc(1)));
4285 eth.set({.holder = alice, .flags = tfMPTLock});
4287 eth.set({.holder = alice, .flags = tfMPTUnlock});
4289 eth.set({.holder = carol, .flags = tfMPTLock});
4291 eth.set({.holder = carol, .flags = tfMPTUnlock});
4293 env(
offer(gw, btc(1), eth(1)));
4298 btc.set({.flags = tfMPTLock});
4302 btc.set({.flags = tfMPTUnlock});
4305 eth.set({.flags = tfMPTLock});
4309 eth.set({.flags = tfMPTUnlock});
4312 env(
offer(alice, eth(1), btc(1)));
4313 env(
offer(carol, btc(1), eth(1)));
4319 env.
fund(
XRP(1'000), gw, alice);
4326 .authHolder =
true});
4333 .authHolder =
true});
4335 btc.
authorize({.account = gw, .holder = alice, .flags = tfMPTUnauthorize});
4341 env(
offer(gw, eth(10), btc(10)));
4348 env.
fund(
XRP(1'000), gw, alice, carol);
4352 .holders = {alice, carol},
4354 .flags = tfMPTCanTrade | tfMPTCanTransfer});
4358 .holders = {alice, carol},
4360 .flags = tfMPTCanTrade});
4363 env(
offer(alice, eth(10), btc(10)),
Txflags(tfPassive));
4371 env(
offer(carol, btc(10), eth(10)));
4376 env(
offer(alice, eth(10), btc(10)),
Txflags(tfPassive));
4377 env(
offer(carol, btc(10), eth(10)));
4385 env.
fund(
XRP(1'000), gw, alice, carol);
4389 .holders = {alice, carol},
4391 .flags = tfMPTCanTransfer,
4396 .holders = {alice, carol},
4398 .flags = tfMPTCanTrade,
4408 Env env{*
this, features};
4410 MPTTester mptTester(env, gw, {.holders = {alice, carol}});
4413 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4414 auto const mpt = mptTester[
"MPT"];
4416 mptTester.authorize({.account = alice});
4417 mptTester.pay(gw, alice, 200);
4419 mptTester.authorize({.account = carol});
4420 mptTester.pay(gw, carol, 200);
4422 env(
offer(alice,
XRP(100), mpt(101)));
4426 env(
offer(carol, mpt(101),
XRP(100)));
4430 BEAST_EXPECT(mptTester.checkMPTokenOutstandingAmount(400));
4431 BEAST_EXPECT(mptTester.checkMPTokenAmount(alice, 99));
4432 BEAST_EXPECT(mptTester.checkMPTokenAmount(carol, 301));
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 env(
trust(alice, usd(2'000)));
4446 env(
pay(gw, alice, usd(1'000)));
4449 env(
trust(carol, usd(2'000)));
4450 env(
pay(gw, carol, usd(1'000)));
4453 mptTester.authorize({.account = alice});
4454 mptTester.pay(gw, alice, 200);
4456 mptTester.authorize({.account = carol});
4457 mptTester.pay(gw, carol, 200);
4459 env(
offer(alice, usd(100), mpt(101)));
4463 env(
offer(carol, mpt(101), usd(100)));
4466 BEAST_EXPECT(env.
balance(alice, usd) == usd(1'100));
4467 BEAST_EXPECT(env.
balance(carol, usd) == usd(900));
4470 BEAST_EXPECT(mptTester.checkMPTokenOutstandingAmount(400));
4471 BEAST_EXPECT(mptTester.checkMPTokenAmount(alice, 99));
4472 BEAST_EXPECT(mptTester.checkMPTokenAmount(carol, 301));
4477 Env env{*
this, features};
4479 MPTTester mptTester1(env, gw, {.holders = {alice, carol}});
4481 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4482 auto const mpt1 = mptTester1[
"MPT1"];
4484 MPTTester mptTester2(env, gw, {.holders = {alice, carol}, .fund =
false});
4486 {.ownerCount = 2, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4487 auto const mpt2 = mptTester2[
"MPT2"];
4489 mptTester1.authorize({.account = alice});
4490 mptTester1.authorize({.account = carol});
4491 mptTester1.pay(gw, alice, 200);
4492 mptTester1.pay(gw, carol, 200);
4494 mptTester2.authorize({.account = alice});
4495 mptTester2.authorize({.account = carol});
4496 mptTester2.pay(gw, alice, 200);
4497 mptTester2.pay(gw, carol, 200);
4499 env(
offer(alice, mpt2(100), mpt1(101)));
4503 env(
offer(carol, mpt1(101), mpt2(100)));
4508 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(400));
4509 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 99));
4510 BEAST_EXPECT(mptTester1.checkMPTokenAmount(carol, 301));
4511 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(400));
4512 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 300));
4513 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 100));
4527 auto const usd = gw[
"USD"];
4531 Env env{*
this, features};
4532 MPTTester mptTester(env, gw, {.holders = {carol, bob}});
4535 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4536 auto const mpt = mptTester[
"MPT"];
4538 mptTester.authorize({.account = carol});
4539 mptTester.pay(gw, carol, 200);
4541 mptTester.authorize({.account = bob});
4544 env(
pay(carol, bob, mpt(1)),
4552 env(
pay(gw, bob, mpt(1)),
4560 env(
pay(bob, gw, mpt(1)),
4570 Env env{*
this, features};
4571 MPTTester mptTester(env, gw, {.holders = {carol, bob}});
4574 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4575 auto const mpt = mptTester[
"MPT"];
4577 mptTester.authorize({.account = carol});
4578 mptTester.pay(gw, carol, 200);
4580 mptTester.authorize({.account = bob});
4583 env(
pay(carol, bob, mpt(1)),
4591 env(
pay(gw, bob, mpt(1)),
4599 env(
pay(bob, gw, mpt(1)),
4609 Env env{*
this, features - featureMPTokensV2};
4611 MPTTester mptTester(env, gw, {.holders = {alice}});
4614 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
4615 auto const mpt = mptTester[
"MPT"];
4617 mptTester.authorize({.account = alice});
4619 env(
pay(gw, alice, mpt(101)),
4627 auto const ed =
Account{
"ed"};
4628 Env env{*
this, features};
4629 env.
fund(
XRP(1'000), gw, alice, carol, bob, ed);
4633 .holders = {alice, carol, bob},
4641 .holders = {alice, carol, bob},
4648 .holders = {alice, carol, bob},
4655 .holders = {alice, carol, bob},
4660 env(
offer(bob, eth(1'000), btc(1'000)),
Txflags(tfPassive));
4662 env(
offer(bob, btc(1'000), eth(1'000)),
Txflags(tfPassive));
4683 btc.authorize({.account = ed});
4684 eth.authorize({.account = ed});
4685 env(
pay(gw, ed, eth(100)));
4686 env(
pay(gw, ed, btc(100)));
4690 btc.authorize({.account = gw, .holder = bob});
4708 auto const ed =
Account{
"ed"};
4709 Env env{*
this, features};
4710 env.
fund(
XRP(1'000), gw, alice, carol, bob, ed);
4714 .holders = {alice, carol, bob, ed},
4716 .flags = tfMPTCanTrade});
4720 .holders = {alice, carol, bob, ed},
4724 env(
offer(bob, eth(1'000), btc(1'000)),
Txflags(tfPassive));
4726 env(
offer(bob, btc(1'000), eth(1'000)),
Txflags(tfPassive));
4742 env(
offer(gw, eth(100), btc(100)),
Txflags(tfPassive));
4744 env(
offer(gw, btc(100), eth(100)),
Txflags(tfPassive));
4769 enum class NoTransferMPT { BTC, ETH, USD, CAD };
4772 auto const testMultiStepMPTCanTransfer = [&](NoTransferMPT
const noTransferMPT,
4774 auto const getFlags = [&](NoTransferMPT
const mpt) {
4775 return mpt == noTransferMPT ? tfMPTCanTrade :
kMptDexFlags;
4778 Env env{*
this, features};
4779 env.
fund(
XRP(1'000), gw, alice, carol, bob);
4784 .holders = {alice, carol, bob},
4786 .flags = getFlags(NoTransferMPT::BTC)});
4790 .holders = {alice, carol, bob},
4792 .flags = getFlags(NoTransferMPT::ETH)});
4796 .holders = {alice, carol, bob},
4798 .flags = getFlags(NoTransferMPT::USD)});
4802 .holders = {alice, carol, bob},
4804 .flags = getFlags(NoTransferMPT::CAD)});
4806 env(
offer(bob, cad(100), usd(100)),
Txflags(tfPassive));
4807 env(
offer(bob, usd(100), btc(100)),
Txflags(tfPassive));
4808 env(
offer(bob, btc(100), eth(100)),
Txflags(tfPassive));
4811 test(env, btc, eth, usd, cad);
4815 testMultiStepMPTCanTransfer(
4825 env(
pay(alice, carol, eth(1)),
4826 Path(~usd, ~btc, ~eth),
4830 auto seq(env.
seq(gw));
4834 env(
pay(alice, carol, eth(1)),
4835 Path(~usd, ~btc, ~eth),
4844 env(
pay(alice, carol, eth(1)),
Path(~usd, ~btc, ~eth),
Sendmax(cad(1)));
4848 env(
offer(bob, cad(100), usd(100)),
Txflags(tfPassive));
4853 testMultiStepMPTCanTransfer(
4861 env(
pay(alice, carol, eth(1)),
4862 Path(~usd, ~btc, ~eth),
4869 env(
pay(alice, gw, eth(1)),
Path(~usd, ~btc, ~eth),
Sendmax(cad(1)));
4875 testMultiStepMPTCanTransfer(
4883 env(
pay(alice, carol, eth(1)),
4884 Path(~usd, ~btc, ~eth),
4888 env(
pay(gw, carol, eth(1)),
Path(~usd, ~btc, ~eth),
Sendmax(cad(1)));
4893 env(
pay(alice, carol, eth(1)),
Path(~usd, ~btc, ~eth),
Sendmax(cad(1)));
4900 testMultiStepMPTCanTransfer(
4910 env(
pay(alice, carol, eth(1)),
Path(~usd, ~btc, ~eth),
Sendmax(cad(1)));
4918 Env env{*
this, features};
4919 env.
fund(
XRP(1'000), gw, alice, carol, bob);
4924 .holders = {alice, carol, bob},
4926 .flags = tfMPTCanTransfer,
4931 .holders = {alice, carol, bob},
4937 .holders = {alice, carol, bob},
4948 env(
offer(bob, btc(1), eth(1)));
4949 env(
offer(bob, eth(1), usd(1)));
4953 env(
pay(gw, carol, usd(1)),
4954 Path(~btc, ~eth, ~usd),
4956 Txflags(tfPartialPayment | tfNoRippleDirect));
4963 enum class LockType { Global, Individual,
None };
4969 LockType srcFlag = LockType::None;
4970 LockType dstFlag = LockType::None;
4971 LockType offerFlagBuy = LockType::None;
4972 LockType offerFlagSell = LockType::None;
4973 LockType globalFlagBuy = LockType::None;
4974 LockType globalFlagSell = LockType::None;
4978 auto getErr = [&]<
typename Token>(Token
const&, TestArg
const&
arg) {
4981 return arg.errIOU.value_or(
arg.err);
4988 auto getMPT = [&](
Env& env) {
4992 .holders = {alice, carol, bob, gw},
4998 .holders = {alice, carol, bob, gw2},
5003 auto getIOU = [&](
Env& env) {
5004 for (
auto const& a : {alice, carol, bob})
5006 env(
fset(a, asfDefaultRipple));
5008 env(
trust(a, gw[
"ETH"](200)));
5009 env(
pay(gw, a, gw[
"ETH"](100)));
5010 env(
trust(a, gw2[
"BTC"](200)));
5011 env(
pay(gw2, a, gw2[
"BTC"](100)));
5016 env(
trust(gw2, gw[
"ETH"](200)));
5017 env(
trust(gw, gw2[
"BTC"](200)));
5018 env(
pay(gw2, gw, gw2[
"BTC"](100)));
5022 auto lock = [&]<
typename Token>(
5024 if (
lock == LockType::None)
5028 if (
lock == LockType::Global)
5030 env(
fset(
token.account, asfGlobalFreeze));
5034 IOU const iou{account,
token.currency};
5035 env(
trust(
token.account, iou(0), tfSetFreeze));
5040 if (
lock == LockType::Global)
5042 token.set({.flags = tfMPTLock});
5044 else if (
token.issuer() != account)
5046 token.set({.holder = account, .flags = tfMPTLock});
5050 auto test = [&](
auto&& getTokens, TestArg
const&
arg) {
5052 env.
fund(
XRP(1'000), gw, gw2, alice, carol, bob);
5054 auto [btc, eth] = getTokens(env);
5059 if (
arg.globalFlagBuy != LockType::None)
5061 lock(env, gw, eth, LockType::Global);
5065 lock(env,
arg.offerOwner, eth,
arg.offerFlagBuy);
5068 if (
arg.globalFlagSell != LockType::None)
5070 lock(env, gw2, btc, LockType::Global);
5074 lock(env,
arg.offerOwner, btc,
arg.offerFlagSell);
5078 auto const err = getErr(eth,
arg);
5090 {.src = alice, .dst = carol, .offerOwner = bob, .srcFlag = LockType::Individual, .err =
tecPATH_DRY},
5092 {.src = alice, .dst = carol, .offerOwner = bob, .dstFlag = LockType::Individual, .err =
tecPATH_DRY, .errIOU =
tesSUCCESS},
5094 {.src = alice, .dst = carol, .offerOwner = bob, .globalFlagBuy = LockType::Global, .err =
tecPATH_DRY},
5096 {.src = alice, .dst = carol, .offerOwner = bob, .globalFlagSell = LockType::Global, .err =
tecPATH_PARTIAL},
5098 {.src = alice, .dst = carol, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err =
tecPATH_PARTIAL, .errIOU =
tesSUCCESS},
5100 {.src = alice, .dst = carol, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err =
tecPATH_PARTIAL},
5103 {.src = alice, .dst = carol, .offerOwner = gw2, .srcFlag = LockType::Individual, .err =
tecPATH_DRY},
5105 {.src = alice, .dst = carol, .offerOwner = gw2, .dstFlag = LockType::Individual, .err =
tecPATH_DRY, .errIOU =
tesSUCCESS},
5107 {.src = alice, .dst = carol, .offerOwner = gw2, .globalFlagBuy = LockType::Global, .err =
tecPATH_DRY},
5109 {.src = alice, .dst = carol, .offerOwner = gw2, .globalFlagSell = LockType::Global, .err =
tesSUCCESS},
5112 {.src = alice, .dst = carol, .offerOwner = gw, .srcFlag = LockType::Individual, .err =
tecPATH_DRY},
5114 {.src = alice, .dst = carol, .offerOwner = gw, .dstFlag = LockType::Individual, .err =
tecPATH_DRY, .errIOU =
tesSUCCESS},
5116 {.src = alice, .dst = carol, .offerOwner = gw, .globalFlagBuy = LockType::Global, .err =
tecPATH_DRY},
5118 {.src = alice, .dst = carol, .offerOwner = gw, .globalFlagSell = LockType::Global, .err =
tecPATH_PARTIAL},
5122 {.src = gw, .dst = carol, .offerOwner = bob, .srcFlag = LockType::Global, .err =
tecPATH_PARTIAL, .errIOU =
tesSUCCESS},
5124 {.src = gw, .dst = carol, .offerOwner = bob, .dstFlag = LockType::Global, .err =
tecPATH_PARTIAL},
5126 {.src = gw, .dst = carol, .offerOwner = bob, .dstFlag = LockType::Individual, .err =
tecPATH_DRY, .errIOU =
tesSUCCESS},
5128 {.src = gw, .dst = carol, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err =
tecPATH_PARTIAL, .errIOU =
tesSUCCESS},
5130 {.src = gw, .dst = carol, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err =
tecPATH_PARTIAL},
5133 {.src = alice, .dst = gw2, .offerOwner = bob, .srcFlag = LockType::Individual, .err =
tecPATH_DRY},
5135 {.src = alice, .dst = gw2, .offerOwner = bob, .dstFlag = LockType::Global, .err =
tecPATH_PARTIAL},
5137 {.src = alice, .dst = gw2, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err =
tecPATH_PARTIAL, .errIOU =
tesSUCCESS},
5139 {.src = alice, .dst = gw2, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err =
tecPATH_PARTIAL},
5143 for (
auto const& t :
tests)
5151 auto const usd = gw[
"USD"];
5152 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5156 .holders = {alice, carol, bob},
5162 .holders = {alice, carol, bob},
5166 env(
trust(alice, usd(100)));
5167 env(
pay(gw, alice, usd(100)));
5168 env(
trust(carol, usd(100)));
5170 env(
offer(alice,
XRP(10), eth(10)));
5171 env(
offer(bob, eth(10), btc(10)));
5172 env(
offer(alice, btc(10), usd(10)));
5175 btc.set({.holder = bob, .flags = tfMPTLock});
5178 env(
pay(alice, carol, usd(1)),
5180 Txflags(tfNoRippleDirect | tfPartialPayment),
5185 btc.set({.holder = bob, .flags = tfMPTUnlock});
5186 eth.set({.holder = bob, .flags = tfMPTLock});
5188 env(
pay(alice, carol, usd(1)),
5190 Txflags(tfNoRippleDirect | tfPartialPayment),
5198 Env env(*
this, features);
5199 Account const domainOwner(
"DomainOwner");
5200 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5201 auto const domainID =
5202 setupDomain(env, {alice, bob, carol, gw}, domainOwner,
"permdex-cred");
5204 MPTTester btc({.env = env, .issuer = gw, .holders = {alice, carol, bob}, .pay = 100});
5205 MPTTester eth({.env = env, .issuer = gw, .holders = {alice, carol, bob}, .pay = 100});
5207 auto test = [&](
bool withDomain) {
5210 env(
offer(bob, eth(1), btc(1)),
Domain(domainID));
5214 env(
offer(bob, eth(1), btc(1)));
5218 env(
pay(alice, carol, btc(1)),
5232 Env env(*
this, features);
5233 Account const domainOwner(
"DomainOwner");
5234 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5235 auto const domainID =
5236 setupDomain(env, {alice, bob, carol, gw}, domainOwner,
"permdex-cred");
5238 MPTTester btc({.env = env, .issuer = gw, .holders = {alice, carol, bob}, .pay = 100});
5239 MPTTester eth({.env = env, .issuer = gw, .holders = {alice, carol, bob}, .pay = 100});
5241 auto test = [&](
bool isHybrid) {
5242 auto const flags = isHybrid ? tfHybrid : 0;
5246 env(
pay(alice, carol, btc(1)),
5258 Env env{*
this, features};
5259 MPTTester mptTester(env, gw, {.holders = {alice, carol, bob}});
5262 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5263 auto const mpt = mptTester[
"MPT"];
5265 mptTester.authorize({.account = alice});
5266 mptTester.pay(gw, alice, 200);
5268 mptTester.authorize({.account = carol});
5269 mptTester.pay(gw, carol, 200);
5271 mptTester.authorize({.account = bob});
5273 env(
offer(alice,
XRP(100), mpt(101)));
5277 env(
pay(carol, bob, mpt(101)),
5284 BEAST_EXPECT(mptTester.checkMPTokenOutstandingAmount(400));
5285 BEAST_EXPECT(mptTester.checkMPTokenAmount(alice, 99));
5286 BEAST_EXPECT(mptTester.checkMPTokenAmount(bob, 101));
5291 Env env{*
this, features};
5293 MPTTester mptTester(env, gw, {.holders = {alice, carol, bob}});
5296 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5297 auto const mpt = mptTester[
"MPT"];
5299 env(
trust(alice, usd(2'000)));
5300 env(
pay(gw, alice, usd(1'000)));
5301 env(
trust(bob, usd(2'000)));
5302 env(
pay(gw, bob, usd(1'000)));
5303 env(
trust(carol, usd(2'000)));
5304 env(
pay(gw, carol, usd(1'000)));
5307 mptTester.authorize({.account = alice});
5308 mptTester.pay(gw, alice, 200);
5310 mptTester.authorize({.account = carol});
5311 mptTester.pay(gw, carol, 200);
5313 mptTester.authorize({.account = bob});
5315 env(
offer(alice, usd(100), mpt(101)));
5319 env(
pay(carol, bob, mpt(101)),
5326 BEAST_EXPECT(env.
balance(carol, usd) == usd(900));
5327 BEAST_EXPECT(mptTester.checkMPTokenOutstandingAmount(400));
5328 BEAST_EXPECT(mptTester.checkMPTokenAmount(alice, 99));
5329 BEAST_EXPECT(mptTester.checkMPTokenAmount(bob, 101));
5334 Env env{*
this, features};
5336 MPTTester mptTester(env, gw, {.holders = {alice, carol, bob}});
5339 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5340 auto const mpt = mptTester[
"MPT"];
5342 env(
trust(alice, usd(2'000)),
Txflags(tfClearNoRipple));
5343 env(
pay(gw, alice, usd(1'000)));
5347 mptTester.authorize({.account = alice});
5348 env(
pay(gw, alice, mpt(200)));
5350 mptTester.authorize({.account = carol});
5351 env(
pay(gw, carol, mpt(200)));
5353 env(
offer(alice, mpt(101), usd(100)));
5357 env(
pay(carol, bob, usd(100)),
5360 Txflags(tfPartialPayment | tfNoRippleDirect));
5364 BEAST_EXPECT(env.
balance(alice, usd) == usd(900));
5365 BEAST_EXPECT(mptTester.checkMPTokenAmount(alice, 301));
5366 BEAST_EXPECT(mptTester.checkMPTokenOutstandingAmount(400));
5367 BEAST_EXPECT(mptTester.checkMPTokenAmount(carol, 99));
5368 BEAST_EXPECT(env.
balance(bob, usd) == usd(100));
5373 Env env{*
this, features};
5375 MPTTester mptTester1(env, gw, {.holders = {alice, carol, bob}});
5377 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5378 auto const mpt1 = mptTester1[
"MPT1"];
5380 MPTTester mptTester2(env, gw, {.holders = {alice, carol, bob}, .fund =
false});
5382 {.ownerCount = 2, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5383 auto const mpt2 = mptTester2[
"MPT2"];
5385 mptTester1.authorize({.account = alice});
5386 mptTester1.pay(gw, alice, 200);
5387 mptTester2.authorize({.account = alice});
5389 mptTester2.authorize({.account = carol});
5390 mptTester2.pay(gw, carol, 200);
5392 mptTester1.authorize({.account = bob});
5393 mptTester2.authorize({.account = bob});
5394 mptTester2.pay(gw, bob, 200);
5396 env(
offer(alice, mpt2(100), mpt1(100)));
5402 env(
pay(carol, bob, mpt1(10)),
5409 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 190));
5410 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 10));
5411 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(200));
5412 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(400));
5413 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5414 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 10));
5417 env(
pay(gw, bob, mpt1(20)),
5424 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 170));
5425 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 30));
5426 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(200));
5427 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(420));
5428 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5429 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 30));
5432 env(
pay(bob, gw, mpt1(70)),
5439 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 100));
5440 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 100));
5441 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(130));
5442 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(420));
5443 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5444 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 30));
5445 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 130));
5450 Env env{*
this, features};
5452 MPTTester mptTester1(env, gw, {.holders = {carol, bob}});
5454 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5455 auto const mpt1 = mptTester1[
"MPT1"];
5457 MPTTester mptTester2(env, gw, {.holders = {carol, bob}, .fund =
false});
5459 {.ownerCount = 2, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5460 auto const mpt2 = mptTester2[
"MPT2"];
5462 mptTester2.authorize({.account = carol});
5463 mptTester2.pay(gw, carol, 200);
5465 mptTester1.authorize({.account = bob});
5466 mptTester2.authorize({.account = bob});
5467 mptTester2.pay(gw, bob, 200);
5469 env(
offer(gw, mpt2(100), mpt1(100)));
5474 env(
pay(carol, bob, mpt1(10)),
5481 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(10));
5482 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(390));
5483 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5484 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 10));
5487 env(
pay(gw, bob, mpt1(20)),
5494 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(30));
5495 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(390));
5496 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5497 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 30));
5500 env(
pay(bob, gw, mpt1(70)),
5507 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(30));
5508 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(320));
5509 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5510 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 30));
5511 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 130));
5516 Env env{*
this, features};
5519 MPTTester mptTester1(env, gw, {.holders = {alice, carol, bob}});
5521 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5522 auto const mpt1 = mptTester1[
"MPT1"];
5525 MPTTester mptTester2(env, gw1, {.holders = {alice, carol, bob}, .fund =
false});
5527 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5528 auto const mpt2 = mptTester2[
"MPT2"];
5530 mptTester1.authorize({.account = alice});
5531 mptTester1.pay(gw, alice, 200);
5532 mptTester2.authorize({.account = alice});
5534 mptTester2.authorize({.account = carol});
5535 mptTester2.pay(gw1, carol, 200);
5537 mptTester1.authorize({.account = bob});
5538 mptTester1.pay(gw, bob, 200);
5539 mptTester2.authorize({.account = bob});
5540 mptTester2.pay(gw1, bob, 200);
5542 mptTester1.authorize({.account = gw1});
5543 mptTester1.pay(gw, gw1, 200);
5545 mptTester2.authorize({.account = gw});
5546 mptTester2.pay(gw1, gw, 200);
5548 env(
offer(alice, mpt2(100), mpt1(100)));
5552 env(
pay(carol, bob, mpt1(10)),
5558 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(600));
5559 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(600));
5560 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 200));
5561 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 200));
5562 BEAST_EXPECT(mptTester2.checkMPTokenAmount(carol, 190));
5563 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 210));
5564 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 200));
5565 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 190));
5566 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 10));
5568 env(
pay(bob, gw, mpt1(10)),
5574 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(590));
5575 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(600));
5576 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 200));
5577 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 200));
5578 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 210));
5579 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 190));
5580 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 180));
5581 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 20));
5583 env(
pay(gw, bob, mpt1(10)),
5589 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(590));
5590 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(600));
5591 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 200));
5592 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 190));
5593 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 220));
5594 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 190));
5595 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 170));
5596 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 30));
5598 env(
pay(bob, gw1, mpt1(10)),
5604 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(590));
5605 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(600));
5606 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 210));
5607 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 190));
5608 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 220));
5609 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 180));
5610 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 160));
5611 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 40));
5613 env(
pay(gw1, bob, mpt1(10)),
5619 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(590));
5620 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(610));
5621 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 210));
5622 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 190));
5623 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 230));
5624 BEAST_EXPECT(mptTester2.checkMPTokenAmount(bob, 180));
5625 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 150));
5626 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 50));
5628 env(
pay(gw, gw1, mpt1(10)),
5634 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(590));
5635 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(610));
5636 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 220));
5637 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 180));
5638 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 140));
5639 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 60));
5641 env(
pay(gw1, gw, mpt1(40)),
5647 BEAST_EXPECT(mptTester1.checkMPTokenOutstandingAmount(550));
5648 BEAST_EXPECT(mptTester2.checkMPTokenOutstandingAmount(650));
5649 BEAST_EXPECT(mptTester1.checkMPTokenAmount(gw1, 220));
5650 BEAST_EXPECT(mptTester2.checkMPTokenAmount(gw, 180));
5651 BEAST_EXPECT(mptTester1.checkMPTokenAmount(alice, 100));
5652 BEAST_EXPECT(mptTester2.checkMPTokenAmount(alice, 100));
5662 auto const usd = gw2[
"USD"];
5664 MPTTester mptTester(env, gw, {.holders = {alice, carol}});
5666 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5667 auto const mpt = mptTester[
"MPT"];
5668 mptTester.authorize({.account = alice});
5669 mptTester.authorize({.account = carol});
5670 mptTester.pay(gw, carol, 200);
5672 MPTTester mptTester1(env, gw1, {.holders = {bob, dan}});
5674 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5675 auto const mpt1 = mptTester1[
"MPT1"];
5676 mptTester1.authorize({.account = bob});
5677 mptTester1.pay(gw1, bob, 200);
5678 mptTester1.authorize({.account = dan});
5680 env(
trust(alice, usd(400)));
5681 env(
pay(gw2, alice, usd(200)));
5682 env(
trust(bob, usd(400)));
5684 env(
offer(alice, mpt(100), usd(100)));
5685 env(
offer(bob, usd(100), mpt1(100)));
5688 env(
pay(carol, dan, mpt1(100)),
5691 Txflags(tfPartialPayment | tfNoRippleDirect));
5695 BEAST_EXPECT(mptTester.checkMPTokenAmount(carol, 100));
5696 BEAST_EXPECT(mptTester1.checkMPTokenAmount(dan, 100));
5701 Env env{*
this, features};
5703 fund(env, gw, {alice, carol, bob},
XRP(11'000), {usd(20'000)});
5705 MPTTester mptTester(env, gw, {.fund =
false});
5708 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5709 auto const mpt = mptTester[
"MPT"];
5711 mptTester.authorize({.account = alice});
5712 mptTester.authorize({.account = bob});
5713 mptTester.pay(gw, alice, 10'100);
5715 AMM const amm(env, alice,
XRP(10'000), mpt(10'100));
5717 env(
pay(carol, bob, mpt(100)),
5720 Txflags(tfPartialPayment | tfNoRippleDirect));
5723 BEAST_EXPECT(
amm.expectBalances(
XRP(10'100), mpt(10'000),
amm.tokens()));
5724 BEAST_EXPECT(mptTester.checkMPTokenAmount(bob, 100));
5729 Env env{*
this, features};
5731 fund(env, gw, {alice, carol, bob},
XRP(11'000), {usd(20'000)});
5733 MPTTester mptTester(env, gw, {.fund =
false});
5736 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5737 auto const mpt = mptTester[
"MPT"];
5739 mptTester.authorize({.account = alice});
5740 mptTester.authorize({.account = bob});
5741 mptTester.pay(gw, alice, 10'100);
5743 AMM const amm(env, alice, usd(10'000), mpt(10'100));
5745 env(
pay(carol, bob, mpt(100)),
5748 Txflags(tfPartialPayment | tfNoRippleDirect));
5751 BEAST_EXPECT(
amm.expectBalances(usd(10'100), mpt(10'000),
amm.tokens()));
5752 BEAST_EXPECT(mptTester.checkMPTokenAmount(bob, 100));
5757 Env env{*
this, features};
5758 env.
fund(
XRP(20'000), gw, alice, carol, bob);
5761 MPTTester mptTester1(env, gw, {.fund =
false});
5762 mptTester1.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
5763 auto const mpt1 = mptTester1[
"MPT1"];
5764 mptTester1.authorize({.account = alice});
5765 mptTester1.authorize({.account = bob});
5766 mptTester1.pay(gw, alice, 10'100);
5768 MPTTester mptTester2(env, gw, {.fund =
false});
5769 mptTester2.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
5770 auto const mpt2 = mptTester2[
"MPT1"];
5771 mptTester2.authorize({.account = alice});
5772 mptTester2.authorize({.account = bob});
5773 mptTester2.authorize({.account = carol});
5774 mptTester2.pay(gw, alice, 10'100);
5775 mptTester2.pay(gw, carol, 100);
5777 AMM const amm(env, alice, mpt2(10'000), mpt1(10'100));
5779 env(
pay(carol, bob, mpt1(100)),
5782 Txflags(tfPartialPayment | tfNoRippleDirect));
5785 BEAST_EXPECT(
amm.expectBalances(mpt2(10'100), mpt1(10'000),
amm.tokens()));
5786 BEAST_EXPECT(mptTester1.checkMPTokenAmount(bob, 100));
5792 Env env{*
this, features};
5793 auto const usd = gw[
"USD"];
5794 auto const eur = gw[
"EUR"];
5795 auto const crn = gw[
"CRN"];
5796 auto const yan = gw[
"YAN"];
5801 {alice, carol, bob},
5803 {usd(1'000), eur(1'000), crn(2'000), yan(1'000)});
5806 MPTTester mptTester(env, gw, {.fund =
false});
5807 mptTester.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
5808 mptTester.authorize({.account = alice});
5809 mptTester.pay(gw, alice, 2'000);
5810 return {mptTester, mptTester[
"MPT"]};
5813 auto const [mptTester1, mpt1] = createMPT();
5814 auto const [mptTester2, mpt2] = createMPT();
5815 auto const [mptTester3, mpt3] = createMPT();
5817 env(
offer(alice, eur(100), mpt1(101)));
5818 env(
offer(alice, mpt1(101), mpt2(102)));
5819 env(
offer(alice, mpt2(102), usd(103)));
5820 env(
offer(alice, usd(103), crn(104)));
5822 AMM const amm(env, alice, crn(1'000), mpt3(1'104));
5823 env(
offer(alice, mpt3(104), yan(100)));
5825 env(
pay(carol, bob, yan(100)),
5828 Txflags(tfPartialPayment | tfNoRippleDirect));
5831 BEAST_EXPECT(env.
balance(carol, eur) == eur(900));
5832 BEAST_EXPECT(env.
balance(bob, yan) == yan(1'100));
5833 BEAST_EXPECT(
amm.expectBalances(crn(1'104), mpt3(1'000),
amm.tokens()));
5840 Env env{*
this, features};
5841 auto const usd = gw[
"USD"];
5842 auto const eur = gw[
"EUR"];
5843 auto const crn = gw[
"CRN"];
5845 fund(env, gw, {alice, carol, bob},
XRP(1'000), {usd(1'000), eur(1'000), crn(2'000)});
5848 MPTTester mptTester(env, gw, {.fund =
false});
5849 mptTester.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
5850 mptTester.authorize({.account = alice});
5851 mptTester.pay(gw, alice, 2'000);
5852 return {mptTester, mptTester[
"MPT"]};
5855 auto const [mptTester1, mpt1] = createMPT();
5856 auto const [mptTester2, mpt2] = createMPT();
5857 auto const [mptTester3, mpt3] = createMPT();
5858 auto [mptTester4, mpt4] = createMPT();
5859 mptTester4.authorize({.account = bob});
5861 env(
offer(alice, eur(100), mpt1(101)));
5862 env(
offer(alice, mpt1(101), mpt2(102)));
5863 env(
offer(alice, mpt2(102), usd(103)));
5864 env(
offer(alice, usd(103), crn(104)));
5866 AMM const amm(env, alice, crn(1'000), mpt3(1'104));
5867 env(
offer(alice, mpt3(104), mpt4(100)));
5869 env(
pay(carol, bob, mpt4(100)),
5872 Txflags(tfPartialPayment | tfNoRippleDirect));
5875 BEAST_EXPECT(env.
balance(carol, eur) == eur(900));
5876 BEAST_EXPECT(mptTester4.checkMPTokenAmount(bob, 100));
5877 BEAST_EXPECT(
amm.expectBalances(crn(1'104), mpt3(1'000),
amm.tokens()));
5884 Env env(*
this, features);
5886 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5888 MPTTester usdTester(env, gw, {.holders = {alice, carol, bob}, .fund =
false});
5892 .pay = {{{alice}, 1'000}},
5893 .flags = tfMPTCanTransfer | tfMPTCanTrade});
5894 auto const usd = usdTester[
"USD"];
5896 MPTTester eurTester(env, gw, {.holders = {alice, carol, bob}, .fund =
false});
5899 .authorize = {{alice, carol}},
5900 .pay = {{{carol}, 100}},
5901 .flags = tfMPTCanTransfer | tfMPTCanTrade});
5902 auto const eur = eurTester[
"EUR"];
5904 env(
offer(alice, eur(10), usd(10)));
5906 env(
pay(carol, bob, usd(10)),
5909 Txflags(tfNoRippleDirect | tfPartialPayment));
5912 Env env(*
this, features);
5913 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5916 {.env = env, .issuer = gw, .holders = {alice, carol, bob}, .maxAmt = 1'000});
5917 MPT const usd = musd;
5918 env(
pay(gw, alice, usd(800)));
5919 env(
offer(gw,
XRP(300), usd(300)));
5920 env(
pay(carol, bob, usd(300)),
5924 BEAST_EXPECT(musd.checkMPTokenAmount(bob, 200));
5925 BEAST_EXPECT(musd.checkMPTokenOutstandingAmount(1'000));
5930 Env env(*
this, features);
5931 auto const eur = gw[
"EUR"];
5932 env.
fund(
XRP(1'000), gw, alice, carol, bob);
5935 env(
trust(alice, eur(1'000)));
5936 env(
pay(gw, alice, eur(300)));
5937 env(
trust(bob, eur(1'000)));
5940 {.env = env, .issuer = gw, .holders = {alice, carol, bob}, .maxAmt = 1'000});
5941 MPT const usd = musd;
5943 env(
pay(gw, alice, usd(800)));
5944 env(
offer(gw,
XRP(300), usd(300)));
5945 env(
offer(alice, usd(300), eur(300)));
5946 env(
pay(carol, bob, eur(300)),
5950 BEAST_EXPECT(musd.checkMPTokenAmount(alice, 1'000));
5951 BEAST_EXPECT(musd.checkMPTokenOutstandingAmount(1'000));
5954 BEAST_EXPECT(env.
balance(bob, eur) == eur(200));
5969 auto const usd = gw[
"USD"];
5970 auto const eur = gw1[
"EUR"];
5978 MPTTester mptTester(env, gw, {.holders = {dan, carol}});
5980 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
5981 auto const mpt = mptTester[
"MPT"];
5982 mptTester.authorize({.account = dan});
5983 mptTester.authorize({.account = carol});
5984 mptTester.pay(gw, carol, 200);
5986 auto const [pathSet, srcAmt, dstAmt] =
findPaths(env, carol, dan, mpt(-1));
5987 BEAST_EXPECT(srcAmt == mpt(200));
5988 BEAST_EXPECT(dstAmt == mpt(200));
5990 BEAST_EXPECT(pathSet.empty());
5999 MPTTester mptTester(env, gw, {.holders = {alice, dan}});
6002 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6003 auto const mpt = mptTester[
"MPT"];
6005 mptTester.authorize({.account = alice});
6006 mptTester.authorize({.account = dan});
6007 mptTester.pay(gw, alice, 200);
6009 env(
offer(alice,
XRP(100), mpt(100)));
6012 auto const [pathSet, srcAmt, dstAmt] =
findPaths(env, carol, dan, mpt(-1));
6013 BEAST_EXPECT(srcAmt ==
XRP(100));
6014 BEAST_EXPECT(dstAmt == mpt(100));
6015 if (BEAST_EXPECT(
same(pathSet,
stpath(
ipe(mptTester.issuanceID())))))
6018 env(
pay(carol, dan, mpt(10)),
6021 Txflags(tfNoRippleDirect | tfPartialPayment));
6032 MPTTester mptTester(env, gw1, {.holders = {alice, dan}});
6035 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6036 auto const mpt = mptTester[
"MPT"];
6038 mptTester.authorize({.account = alice});
6039 mptTester.authorize({.account = dan});
6040 mptTester.pay(gw1, alice, 200);
6042 env(
trust(alice, usd(400)));
6043 env(
trust(carol, usd(400)));
6044 env(
pay(gw, carol, usd(200)));
6046 env(
offer(alice, usd(100), mpt(100)));
6054 BEAST_EXPECT(srcAmt == usd(100));
6055 BEAST_EXPECT(dstAmt == mpt(100));
6060 env(
pay(carol, dan, mpt(10)),
6063 Txflags(tfNoRippleDirect | tfPartialPayment));
6067 std::tie(pathSet, srcAmt, dstAmt) =
findPaths(env, carol, dan, mpt(-1), usd(-1));
6068 BEAST_EXPECT(srcAmt == usd(90));
6069 BEAST_EXPECT(dstAmt == mpt(90));
6074 env(
pay(carol, dan, mpt(10)),
6077 Txflags(tfNoRippleDirect | tfPartialPayment));
6081 std::tie(pathSet, srcAmt, dstAmt) =
6082 findPaths(env, carol, dan, mpt(-1), std::nullopt, usd.currency);
6083 BEAST_EXPECT(srcAmt == usd(80));
6084 BEAST_EXPECT(dstAmt == mpt(80));
6089 env(
pay(carol, dan, mpt(10)),
6092 Txflags(tfNoRippleDirect | tfPartialPayment));
6103 MPTTester mptTester(env, gw1, {.holders = {carol, alice}});
6106 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6107 auto const mpt = mptTester[
"MPT"];
6109 mptTester.authorize({.account = carol});
6110 mptTester.authorize({.account = alice});
6111 mptTester.pay(gw1, carol, 200);
6113 env(
trust(dan, usd(400)));
6114 env(
trust(alice, usd(400)));
6115 env(
pay(gw, alice, usd(200)));
6117 env(
offer(alice, mpt(100), usd(100)));
6125 BEAST_EXPECT(srcAmt == mpt(100));
6126 BEAST_EXPECT(dstAmt == usd(100));
6130 env(
pay(carol, dan, usd(10)),
6133 Txflags(tfNoRippleDirect | tfPartialPayment));
6137 std::tie(pathSet, srcAmt, dstAmt) =
findPaths(env, carol, dan, usd(-1), mpt(-1));
6138 BEAST_EXPECT(srcAmt == mpt(90));
6139 BEAST_EXPECT(dstAmt == usd(90));
6143 env(
pay(carol, dan, usd(10)),
6146 Txflags(tfNoRippleDirect | tfPartialPayment));
6150 std::tie(pathSet, srcAmt, dstAmt) =
6151 findPaths(env, carol, dan, usd(-1), std::nullopt, mpt.mpt());
6152 BEAST_EXPECT(srcAmt == mpt(80));
6153 BEAST_EXPECT(dstAmt == usd(80));
6157 env(
pay(carol, dan, usd(10)),
6160 Txflags(tfNoRippleDirect | tfPartialPayment));
6168 MPTTester mptTester(env, gw, {.holders = {alice, dan}});
6169 MPTTester mptTester1(env, gw1, {.holders = {carol}});
6172 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6173 auto const mpt = mptTester[
"MPT"];
6175 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6176 auto const mpt1 = mptTester1[
"MPT1"];
6178 mptTester.authorize({.account = alice});
6179 mptTester.authorize({.account = dan});
6180 mptTester.pay(gw, alice, 200);
6182 mptTester1.authorize({.account = carol});
6183 mptTester1.authorize({.account = alice});
6184 mptTester1.pay(gw1, carol, 200);
6186 env(
offer(alice, mpt1(100), mpt(100)));
6194 BEAST_EXPECT(srcAmt == mpt1(100));
6195 BEAST_EXPECT(dstAmt == mpt(100));
6200 env(
pay(carol, dan, mpt(10)),
6203 Txflags(tfNoRippleDirect | tfPartialPayment));
6207 std::tie(pathSet, srcAmt, dstAmt) =
findPaths(env, carol, dan, mpt(-1), mpt1(-1));
6208 BEAST_EXPECT(srcAmt == mpt1(90));
6209 BEAST_EXPECT(dstAmt == mpt(90));
6214 env(
pay(carol, dan, mpt(10)),
6217 Txflags(tfNoRippleDirect | tfPartialPayment));
6221 std::tie(pathSet, srcAmt, dstAmt) =
6222 findPaths(env, carol, dan, mpt(-1), std::nullopt, mpt1.mpt());
6223 BEAST_EXPECT(srcAmt == mpt1(80));
6224 BEAST_EXPECT(dstAmt == mpt(80));
6229 env(
pay(carol, dan, mpt(10)),
6232 Txflags(tfNoRippleDirect | tfPartialPayment));
6243 MPTTester mptTester(env, gw, {.holders = {alice, bob}});
6246 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6247 auto const mpt = mptTester[
"MPT"];
6249 mptTester.authorize({.account = alice});
6250 mptTester.authorize({.account = bob});
6251 mptTester.pay(gw, alice, 200);
6252 mptTester.pay(gw, bob, 200);
6254 env(
trust(bob, usd(200)));
6255 env(
pay(gw, bob, usd(100)));
6256 env(
trust(dan, usd(200)));
6257 env(
trust(alice, usd(200)));
6259 env(
offer(alice,
XRP(100), mpt(100)));
6260 env(
offer(bob, mpt(100), usd(100)));
6268 BEAST_EXPECT(srcAmt ==
XRP(100));
6269 BEAST_EXPECT(dstAmt == usd(100));
6271 pathSet.
size() == 1 &&
6275 env(
pay(carol, dan, usd(10)),
6278 Txflags(tfNoRippleDirect | tfPartialPayment));
6283 BEAST_EXPECT(srcAmt ==
XRP(90));
6284 BEAST_EXPECT(dstAmt == usd(90));
6286 pathSet.
size() == 1 &&
6290 env(
pay(carol, dan, usd(10)),
6293 Txflags(tfNoRippleDirect | tfPartialPayment));
6303 auto const usd2 = gw2[
"USD"];
6305 MPTTester mptTester(env, gw, {.holders = {alice, carol}});
6307 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6308 auto const mpt = mptTester[
"MPT"];
6309 mptTester.authorize({.account = alice});
6310 mptTester.authorize({.account = carol});
6311 mptTester.pay(gw, carol, 200);
6313 MPTTester mptTester1(env, gw1, {.holders = {bob, dan}});
6315 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6316 auto const mpt1 = mptTester1[
"MPT1"];
6317 mptTester1.authorize({.account = bob});
6318 mptTester1.pay(gw1, bob, 200);
6319 mptTester1.authorize({.account = dan});
6321 env(
trust(alice, usd2(400)));
6322 env(
pay(gw2, alice, usd2(200)));
6323 env(
trust(bob, usd2(400)));
6325 env(
offer(alice, mpt(100), usd2(100)));
6326 env(
offer(bob, usd2(100), mpt1(100)));
6334 BEAST_EXPECT(srcAmt == mpt(100));
6335 BEAST_EXPECT(dstAmt == mpt1(100));
6337 pathSet.
size() == 1 &&
6341 env(
pay(carol, dan, mpt1(10)),
6344 Txflags(tfNoRippleDirect | tfPartialPayment));
6348 std::tie(pathSet, srcAmt, dstAmt) =
findPaths(env, carol, dan, mpt1(-1), mpt(-1));
6349 BEAST_EXPECT(srcAmt == mpt(90));
6350 BEAST_EXPECT(dstAmt == mpt1(90));
6352 pathSet.
size() == 1 &&
6356 env(
pay(carol, dan, mpt1(10)),
6359 Txflags(tfNoRippleDirect | tfPartialPayment));
6363 std::tie(pathSet, srcAmt, dstAmt) =
6364 findPaths(env, carol, dan, mpt1(-1), std::nullopt, mpt.mpt());
6365 BEAST_EXPECT(srcAmt == mpt(80));
6366 BEAST_EXPECT(dstAmt == mpt1(80));
6368 pathSet.
size() == 1 &&
6372 env(
pay(carol, dan, mpt1(10)),
6375 Txflags(tfNoRippleDirect | tfPartialPayment));
6385 env.
fund(
XRP(1'000), gw, gw1, gw2, alice, bob, carol, dan);
6387 MPTTester mptTester(env, gw, {.holders = {alice, carol}, .fund =
false});
6389 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6390 auto const mpt = mptTester[
"MPT"];
6391 mptTester.authorize({.account = alice});
6392 mptTester.authorize({.account = carol});
6393 mptTester.pay(gw, carol, 200);
6395 MPTTester mptTester1(env, gw1, {.holders = {bob, alice}, .fund =
false});
6396 mptTester1.
create({.ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6397 auto const mpt1 = mptTester1[
"MPT1"];
6398 mptTester1.authorize({.account = alice});
6399 mptTester1.pay(gw1, alice, 200);
6400 mptTester1.authorize({.account = bob});
6402 MPTTester mptTester2(env, gw2, {.holders = {bob, dan}, .fund =
false});
6403 mptTester2.
create({.ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanTrade});
6404 auto const mpt2 = mptTester2[
"MPT2"];
6405 mptTester2.authorize({.account = bob});
6406 mptTester2.pay(gw2, bob, 200);
6407 mptTester2.authorize({.account = dan});
6409 env(
offer(alice, mpt(100), mpt1(100)));
6410 env(
offer(bob, mpt1(100), mpt2(100)));
6418 BEAST_EXPECT(srcAmt == mpt(100));
6419 BEAST_EXPECT(dstAmt == mpt2(100));
6421 pathSet.
size() == 1 &&
6424 stpath(
ipe(mptTester1.issuanceID()),
ipe(mptTester2.issuanceID())))))
6427 env(
pay(carol, dan, mpt2(10)),
6430 Txflags(tfNoRippleDirect | tfPartialPayment));
6434 std::tie(pathSet, srcAmt, dstAmt) =
findPaths(env, carol, dan, mpt2(-1), mpt(-1));
6435 BEAST_EXPECT(srcAmt == mpt(90));
6436 BEAST_EXPECT(dstAmt == mpt2(90));
6438 pathSet.
size() == 1 &&
6441 stpath(
ipe(mptTester1.issuanceID()),
ipe(mptTester2.issuanceID())))))
6444 env(
pay(carol, dan, mpt2(10)),
6447 Txflags(tfNoRippleDirect | tfPartialPayment));
6451 std::tie(pathSet, srcAmt, dstAmt) =
6452 findPaths(env, carol, dan, mpt2(-1), std::nullopt, mpt.mpt());
6453 BEAST_EXPECT(srcAmt == mpt(80));
6454 BEAST_EXPECT(dstAmt == mpt2(80));
6456 pathSet.
size() == 1 &&
6459 stpath(
ipe(mptTester1.issuanceID()),
ipe(mptTester2.issuanceID())))))
6462 env(
pay(carol, dan, mpt2(10)),
6465 Txflags(tfNoRippleDirect | tfPartialPayment));
6481 env.
fund(
XRP(1'000'000), gw1);
6482 env.
fund(
XRP(1'000'000), carol);
6483 env.
fund(
XRP(1'000'000), dan);
6484 env.
fund(
XRP(1'000'000), bob);
6485 env.
fund(
XRP(1'000'000), john);
6486 env.
fund(
XRP(1'000'000), sean);
6489 MPTTester usdTester(env, gw, {.holders = {carol, dan}, .fund =
false});
6493 .flags = tfMPTCanTransfer | tfMPTCanTrade});
6494 auto const usd = usdTester[
"USD"];
6495 env(
offer(carol,
XRP(100), usd(100)));
6497 MPTTester gbpTester(env, gw, {.holders = {bob, sean}, .fund =
false});
6500 .pay = {{{bob}, 100}},
6501 .flags = tfMPTCanTransfer | tfMPTCanTrade});
6502 auto const gbp = gbpTester[
"GBP"];
6504 MPTTester usd1(env, gw1, {.holders = {bob, dan}, .fund =
false});
6507 .pay = {{{dan}, 100}},
6508 .flags = tfMPTCanTransfer | tfMPTCanTrade});
6509 auto const usD1 = usd1[
"USD1"];
6510 env(
offer(bob, usd1(100), gbp(100)));
6514 auto const [pathSet, srcAmt, dstAmt] =
findPaths(env, john, sean, gbp(-1),
XRP(-1));
6515 BEAST_EXPECT(pathSet.empty());
6517 env(
pay(john, sean, gbp(10)),
6519 Path(~usd, dan, gw1, ~gbp),
6520 Txflags(tfNoRippleDirect | tfPartialPayment),
7092 using namespace jtx;
7097 auto const usd = gw[
"USD"];
7103 fund(env, gw, {alice, carol, bob},
XRP(1'000), {usd(1'000)});
7105 MPTTester mptTester(env, gw, {.fund =
false});
7106 mptTester.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
7107 auto const mpt = mptTester[
"MPT"];
7108 mptTester.authorize({.account = alice});
7109 mptTester.authorize({.account = carol});
7110 mptTester.pay(gw, alice, 1'000);
7111 mptTester.pay(gw, carol, 1'000);
7113 MPTTester mptTester1(env, gw, {.fund =
false});
7114 mptTester1.
create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
7115 auto const mpt1 = mptTester1[
"MPT1"];
7116 mptTester1.authorize({.account = alice});
7117 mptTester1.authorize({.account = carol});
7118 mptTester1.pay(gw, alice, 1'000);
7119 mptTester1.pay(gw, carol, 1'000);
7125 for (
auto& pool : pools)
7127 AMM amm(env, gw, std::get<0>(pool), std::get<1>(pool));
7128 amm.deposit(alice, std::get<2>(pool));
7129 amm.deposit(carol, std::get<2>(pool));
7133 .account = bob, .tokens = std::get<2>(pool), .err =
Ter(
tecNO_AUTH)});
7134 amm.withdrawAll(alice);
7135 amm.withdrawAll(carol);
7136 amm.withdrawAll(gw);
7137 BEAST_EXPECT(!
amm.ammExists());
7145 env.
fund(
XRP(1'000), gw, alice, carol);
7148 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, carol}});
7149 MPT const eur =
MPTTester({.env = env, .issuer = gw, .holders = {alice, carol}});
7151 env(
pay(gw, alice, eur(100)));
7153 AMM const amm(env, gw, usd(1'100), eur(1'000));
7155 env(
pay(alice, carol, usd(100)),
Sendmax(eur(100)));
7157 BEAST_EXPECT(
amm.expectBalances(usd(1'000), eur(1'100),
amm.tokens()));
7158 BEAST_EXPECT(env.
balance(carol, usd) == usd(100));
7159 BEAST_EXPECT(env.
balance(alice, eur) == eur(0));
7166 env.
fund(
XRP(1'000), gw, alice, carol);
7169 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, carol}});
7170 MPT const eur =
MPTTester({.env = env, .issuer = gw, .holders = {alice, carol}});
7171 MPT const btc =
MPTTester({.env = env, .issuer = gw, .holders = {alice, carol}});
7172 env(
pay(gw, alice, eur(100)));
7174 AMM const ammEurUsd(env, gw, eur(1'000), usd(1'100));
7175 AMM const ammUsdBtc(env, gw, usd(1'000), btc(1'100));
7177 env(
pay(alice, carol, btc(100)),
7184 BEAST_EXPECT(env.
balance(carol, btc) == btc(100));
7185 BEAST_EXPECT(env.
balance(alice, eur) == eur(0));
7192 env.
fund(
XRP(1'000), gw, alice);
7195 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
7196 MPT const eur =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
7198 env(
pay(gw, alice, eur(1'000)));
7200 AMM const amm(env, gw, eur(1'000'000), usd(1'001'000));
7202 env(
offer(alice, usd(1'000), eur(1'000)));
7204 BEAST_EXPECT(
amm.expectBalances(usd(1'000'000), eur(1'001'000),
amm.tokens()));
7205 BEAST_EXPECT(env.
balance(alice, usd) == usd(1'000));
7206 BEAST_EXPECT(env.
balance(alice, eur) == eur(0));
7211 env.
fund(
XRP(1'000'000), gw, alice, carol);
7213 auto const increment = env.
current()->fees().increment;
7214 auto const txfee =
Fee(
drops(increment));
7228 .mutableFlags = mutableFlags});
7252 auto usd = makeDexMPT();
7253 auto eur = makeDexMPT({alice}, 1'000'000);
7255 auto createDeleteAMM = [&](
auto const& asset,
Account const& lp) {
7262 amm.withdrawAll(lp);
7263 BEAST_EXPECT(!
amm.ammExists());
7266 auto createFail = [&](
auto const& asset,
Account const& account,
auto const& err) {
7267 auto const createJv =
AMM::createJv(account, asset(1'000), eur(1'000), 0);
7268 env(createJv, txfee,
Ter(err));
7279 usd.authorize({.account = alice});
7280 env(
pay(gw, alice, usd(1'000'000)), txfee);
7282 createDeleteAMM(usd, alice);
7286 usd.set({.flags = tfMPTLock});
7292 usd.set({.flags = tfMPTUnlock});
7296 createDeleteAMM(usd, gw);
7299 usd.authorize({.account = gw, .holder = alice});
7300 createDeleteAMM(usd, alice);
7304 auto usd2 = makeNoTransferMPT({alice}, 1'000'000);
7309 createDeleteAMM(usd2, gw);
7312 createDeleteAMM(usd2, alice);
7317 auto usd3 = makeNoTradeMPT({alice}, 1'000'000);
7324 createDeleteAMM(usd3, alice);
7330 auto usd = makeDexMPT();
7331 auto eur = makeDexMPT({alice}, 1'000'000);
7332 AMM amm(env, gw, usd(1'000), eur(1'000));
7350 usd.authorize({.account = carol});
7351 env(
pay(gw, carol, usd(1'000'000)));
7352 eur.authorize({.account = carol});
7353 env(
pay(gw, carol, eur(1'000'000)));
7357 usd.set({.flags = tfMPTLock});
7360 for (
auto const& account : {carol, gw})
7363 {.account = account,
7368 {.account = account,
7373 usd.set({.flags = tfMPTUnlock});
7390 amm.deposit({.account = gw, .tokens = 1'000});
7392 usd.authorize({.account = gw, .holder = carol});
7393 amm.deposit({.account = carol, .tokens = 1'000});
7402 .flags = tfMPTUnauthorize,
7407 auto usd2 = makeNoTransferMPT({carol}, 1'000'000);
7408 AMM amm2(env, gw, usd2(1'000), eur(1'000));
7413 .asset1In = usd2(1),
7422 amm2.deposit({.account = gw, .tokens = 1'000});
7425 amm2.deposit({.account = carol, .tokens = 1'000});
7431 auto usd = makeDexMPT();
7432 auto eur = makeDexMPT({carol}, 1'000'000);
7433 AMM amm(env, gw, usd(1'000), eur(1'000));
7435 usd.authorize({.account = carol});
7436 env(
pay(gw, carol, usd(1'000'000)));
7438 amm.deposit({.account = carol, .tokens = 1'000});
7445 .asset2Out = eur(1),
7453 usd.set({.flags = tfMPTLock});
7454 auto const fix330 = env.
current()->rules().enabled(fixCleanup3_3_0);
7459 .asset1Out = usd(1),
7460 .asset2Out = eur(1),
7462 amm.withdraw({.account = carol, .tokens = 1'000, .err =
Ter(
tecLOCKED)});
7465 {.account = carol, .asset1Out = eur(1), .assets =
std::make_pair(eur, usd)});
7471 auto const replenish = [&](
IOUAmount tokens) {
7472 usd.set({.flags = tfMPTUnlock});
7473 amm.deposit({.account = gw, .tokens = tokens});
7474 usd.set({.flags = tfMPTLock});
7478 {.account = gw, .asset1Out = usd(1), .asset2Out = eur(1), .err = gwLockErr});
7482 amm.withdraw({.account = gw, .tokens = 1'000, .err = gwLockErr});
7488 {.account = gw, .asset1Out = eur(1), .assets =
std::make_pair(eur, usd)});
7490 usd.set({.flags = tfMPTUnlock});
7494 usd.authorize({.account = gw, .holder = carol, .flags = tfMPTUnauthorize});
7498 .asset1Out = usd(1),
7499 .asset2Out = eur(1),
7503 {.account = carol, .asset1Out = eur(1), .assets =
std::make_pair(eur, usd)});
7505 amm.withdraw({.account = gw, .asset1Out = usd(1), .asset2Out = eur(1)});
7507 usd.authorize({.account = gw, .holder = carol});
7508 amm.withdraw({.account = carol, .asset1Out = usd(1), .asset2Out = eur(1)});
7512 auto usd2 = makeNoTransferMPT({carol}, 1'000'000);
7513 AMM amm2(env, gw, usd2(1'000), eur(1'000));
7518 env(
pay(gw, carol,
STAmount{amm2.lptIssue(), 100}));
7522 amm2.withdraw({.account = carol, .asset1Out = usd2(1), .asset2Out = eur(1)});
7526 .asset1Out = eur(1),
7529 amm2.withdraw({.account = gw, .asset1Out = usd2(1), .asset2Out = eur(1)});
7532 usd2.authorize({.account = bob});
7534 usd2.authorize({.account = bob, .flags = tfMPTUnauthorize});
7536 env(
pay(carol, gw, usd2(1)));
7539 amm2.withdraw({.account = carol, .asset1Out = usd2(1), .asset2Out = eur(1)});
7544 auto usd3 = makeDexMPT();
7545 auto eur3 = makeDexMPT({carol}, 1'000'000);
7546 AMM amm3(env, gw, usd3(1'000), eur3(1'000));
7552 .asset1In = eur3(1'000),
7556 amm3.withdraw({.account = carol, .asset1Out = usd3(100)});