59 Account const depositor{
"depositor"};
60 Account const charlie{
"charlie"};
63 auto const testSequence = [&,
this](
68 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
69 tx[sfData] =
"AFEED00E";
70 tx[sfAssetsMaximum] = asset(100).number();
73 BEAST_EXPECT(env.le(
keylet));
76 auto const [share, vaultAccount] =
78 auto const vault = env.le(
keylet);
79 BEAST_EXPECT(vault !=
nullptr);
80 if (!asset.integral())
82 BEAST_EXPECT(vault->at(sfScale) == 6);
86 BEAST_EXPECT(vault->at(sfScale) == 0);
89 BEAST_EXPECT(shares !=
nullptr);
90 if (!asset.integral())
92 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
96 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
98 return {
MPTIssue(vault->at(sfShareMPTID)),
Account(
"vault", vault->at(sfAccount))};
100 auto const shares = share.raw().get<
MPTIssue>();
101 env.memoize(vaultAccount);
106 env.fund(XRP(1000), alice, erin);
107 env(fset(alice, asfDepositAuth));
111 testcase(prefix +
" fail to deposit more than assets held");
112 auto tx = vault.deposit(
113 {.depositor = depositor, .id =
keylet.key, .amount = asset(10000)});
119 testcase(prefix +
" deposit non-zero amount");
121 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
124 BEAST_EXPECT(env.balance(depositor, shares) == share(50 *
scale));
128 testcase(prefix +
" deposit non-zero amount again");
130 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
133 BEAST_EXPECT(env.balance(depositor, shares) == share(100 *
scale));
137 testcase(prefix +
" fail to delete non-empty vault");
138 auto tx = vault.del({.owner = owner, .id =
keylet.key});
144 testcase(prefix +
" fail to update because wrong owner");
145 auto tx = vault.set({.owner = issuer, .id =
keylet.key});
146 tx[sfAssetsMaximum] = asset(50).number();
152 testcase(prefix +
" fail to set maximum lower than current amount");
153 auto tx = vault.set({.owner = owner, .id =
keylet.key});
154 tx[sfAssetsMaximum] = asset(50).number();
160 testcase(prefix +
" set maximum higher than current amount");
161 auto tx = vault.set({.owner = owner, .id =
keylet.key});
162 tx[sfAssetsMaximum] = asset(150).number();
168 testcase(prefix +
" set maximum is idempotent, set it again");
169 auto tx = vault.set({.owner = owner, .id =
keylet.key});
170 tx[sfAssetsMaximum] = asset(150).number();
177 auto tx = vault.set({.owner = owner, .id =
keylet.key});
184 testcase(prefix +
" fail to set domain on public vault");
185 auto tx = vault.set({.owner = owner, .id =
keylet.key});
192 testcase(prefix +
" fail to deposit more than maximum");
194 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
200 testcase(prefix +
" reset maximum to zero i.e. not enforced");
201 auto tx = vault.set({.owner = owner, .id =
keylet.key});
202 tx[sfAssetsMaximum] = asset(0).number();
208 testcase(prefix +
" fail to withdraw more than assets held");
209 auto tx = vault.withdraw(
210 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
216 testcase(prefix +
" deposit some more");
218 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
221 BEAST_EXPECT(env.balance(depositor, shares) == share(200 *
scale));
225 testcase(prefix +
" clawback some");
227 auto tx = vault.clawback(
228 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(10)});
231 if (!asset.raw().native())
233 BEAST_EXPECT(env.balance(depositor, shares) == share(190 *
scale));
240 auto tx = vault.clawback({.issuer = issuer, .id =
keylet.key, .holder = depositor});
243 if (!asset.raw().native())
245 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
248 auto tx = vault.clawback(
252 .amount = asset(10)});
258 auto tx = vault.withdraw(
259 {.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
266 if (!asset.raw().native())
268 testcase(prefix +
" deposit again");
270 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(200)});
273 BEAST_EXPECT(env.balance(depositor, shares) == share(200 *
scale));
277 testcase(prefix +
" deposit/withdrawal same or less than fee");
278 auto const amount = env.current()->fees().base;
281 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = amount});
285 tx = vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = amount});
289 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = amount});
294 tx = vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = amount});
295 tx[sfDestination] = charlie.human();
300 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = amount - 1});
305 {.depositor = depositor, .id =
keylet.key, .amount = amount - 1});
311 testcase(prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
312 auto tx = vault.withdraw(
313 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
314 tx[sfDestination] = alice.human();
320 testcase(prefix +
" fail to withdraw to zero destination");
321 auto tx = vault.withdraw(
322 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
323 tx[sfDestination] =
"0";
328 if (!asset.raw().native())
330 testcase(prefix +
" fail to withdraw to 3rd party no authorization");
331 auto tx = vault.withdraw(
332 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
333 tx[sfDestination] = erin.human();
339 testcase(prefix +
" fail to withdraw to 3rd party lsfRequireDestTag");
340 auto tx = vault.withdraw(
341 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
342 tx[sfDestination] = dave.human();
348 testcase(prefix +
" withdraw to 3rd party lsfRequireDestTag");
350 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
351 tx[sfDestination] = dave.human();
352 tx[sfDestinationTag] =
"0";
358 testcase(prefix +
" deposit again");
359 auto tx = vault.deposit({.depositor = dave, .id =
keylet.key, .amount = asset(50)});
365 testcase(prefix +
" fail to withdraw lsfRequireDestTag");
367 vault.withdraw({.depositor = dave, .id =
keylet.key, .amount = asset(50)});
373 testcase(prefix +
" withdraw with tag");
375 vault.withdraw({.depositor = dave, .id =
keylet.key, .amount = asset(50)});
376 tx[sfDestinationTag] =
"0";
382 testcase(prefix +
" withdraw to authorized 3rd party");
384 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
385 tx[sfDestination] = charlie.human();
388 BEAST_EXPECT(env.balance(depositor, shares) == share(100 *
scale));
392 testcase(prefix +
" withdraw to issuer");
394 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
395 tx[sfDestination] = issuer.human();
398 BEAST_EXPECT(env.balance(depositor, shares) == share(50 *
scale));
401 if (!asset.raw().native())
403 testcase(prefix +
" issuer deposits");
405 vault.deposit({.depositor = issuer, .id =
keylet.key, .amount = asset(10)});
408 BEAST_EXPECT(env.balance(issuer, shares) == share(10 *
scale));
410 testcase(prefix +
" issuer withdraws");
412 {.depositor = issuer, .id =
keylet.key, .amount = share(10 *
scale)});
415 BEAST_EXPECT(env.balance(issuer, shares) == share(0 *
scale));
419 testcase(prefix +
" withdraw remaining assets");
421 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
424 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
426 if (!asset.raw().native())
428 auto tx = vault.clawback(
432 .amount = asset(0)});
438 auto tx = vault.withdraw(
439 {.depositor = depositor, .id =
keylet.key, .amount = share(10)});
445 if (!asset.integral())
447 testcase(prefix +
" temporary authorization for 3rd party");
448 env(trust(erin, asset(1000)));
449 env(trust(issuer, asset(0), erin, tfSetfAuth));
450 env(pay(issuer, erin, asset(10)));
453 auto tx = vault.deposit({.depositor = erin, .id =
keylet.key, .amount = asset(10)});
457 auto tx = pay(erin, depositor, share(10 *
scale));
465 {.depositor = depositor, .id = keylet.key, .amount = asset(1)}));
472 testcase(prefix +
" withdraw to authorized 3rd party");
475 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
476 tx[sfDestination] = erin.human();
481 env(pay(erin, issuer, asset(10)));
484 testcase(prefix +
" fail to pay to unauthorized 3rd party");
485 env(trust(erin, asset(0)));
489 env(pay(depositor, erin, share(1)), Ter{
tecNO_LINE});
493 tx = vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(1)});
499 testcase(prefix +
" fail to delete because wrong owner");
500 auto tx = vault.del({.owner = issuer, .id =
keylet.key});
506 testcase(prefix +
" delete empty vault");
507 auto tx = vault.del({.owner = owner, .id =
keylet.key});
510 BEAST_EXPECT(!env.le(
keylet));
514 auto testCases = [&,
this](
516 Env env{*
this, testableAmendments()};
519 env.fund(XRP(1000), issuer, owner, depositor, charlie, dave);
521 env(fset(issuer, asfAllowTrustLineClawback));
522 env(fset(issuer, asfRequireAuth));
523 env(fset(dave, asfRequireDest));
525 env.require(Flags(issuer, asfAllowTrustLineClawback));
526 env.require(Flags(issuer, asfRequireAuth));
529 testSequence(prefix, env, vault, asset);
534 testCases(
"IOU", [&](Env& env) ->
Asset {
536 env(trust(owner, asset(1000)));
537 env(trust(depositor, asset(1000)));
538 env(trust(charlie, asset(1000)));
539 env(trust(dave, asset(1000)));
540 env(trust(issuer, asset(0), owner, tfSetfAuth));
541 env(trust(issuer, asset(0), depositor, tfSetfAuth));
542 env(trust(issuer, asset(0), charlie, tfSetfAuth));
543 env(trust(issuer, asset(0), dave, tfSetfAuth));
544 env(pay(issuer, depositor, asset(1000)));
549 testCases(
"MPT", [&](Env& env) ->
Asset {
550 MPTTester mptt{env, issuer, kMptInitNoFund};
551 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
553 mptt.authorize({.account = depositor});
554 mptt.authorize({.account = charlie});
555 mptt.authorize({.account = dave});
556 env(pay(issuer, depositor, asset(1000)));
569 bool enableClawback =
true;
571 int initialXRP = 1000;
575 auto testCase = [
this](
583 MPTTester& mptt)>
test,
584 CaseArgs args = {}) {
585 Env env{*
this, args.features};
586 Account const issuer{
"issuer"};
588 Account const depositor{
"depositor"};
589 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
593 MPTTester mptt{env, issuer, kMptInitNoFund};
596 {.flags = tfMPTCanTransfer | tfMPTCanLock |
597 (args.enableClawback ? tfMPTCanClawback : kNone) |
598 (args.requireAuth ? tfMPTRequireAuth : kNone)});
600 mptt.authorize({.account = owner});
601 mptt.authorize({.account = depositor});
602 if (args.requireAuth)
604 mptt.authorize({.account = issuer, .holder = owner});
605 mptt.authorize({.account = issuer, .holder = depositor});
608 env(pay(issuer, depositor, asset(1000)));
611 test(env, issuer, owner, depositor, asset, vault, mptt);
622 testcase(
"MPT nothing to clawback from");
623 auto tx = vault.clawback(
627 .amount = asset(10)});
639 testcase(
"MPT global lock blocks create");
640 mptt.set({.account = issuer, .flags = tfMPTLock});
641 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
653 testcase(
"MPT only issuer can clawback");
655 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
659 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
664 auto tx = vault.clawback({
673 auto tx = vault.clawback({
691 testcase(
"MPT depositor without MPToken, auth required");
693 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
698 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
704 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
708 auto const sleMPT1 = env.le(mptoken);
709 BEAST_EXPECT(sleMPT1 ==
nullptr);
712 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
716 auto const sleMPT2 = env.le(mptoken);
717 BEAST_EXPECT(sleMPT2 ==
nullptr);
722 Account const charlie{
"charlie"};
723 env.fund(XRP(1000), charlie);
727 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
728 tx[sfDestination] = charlie.human();
732 {.requireAuth =
true});
743 testcase(
"MPT depositor without MPToken, no auth required");
745 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
752 {.depositor = depositor,
754 .amount = asset(1000)});
760 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
764 auto const sleMPT1 = env.le(mptoken);
765 BEAST_EXPECT(sleMPT1 ==
nullptr);
768 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
772 auto const sleMPT2 = env.le(mptoken);
773 BEAST_EXPECT(sleMPT2 !=
nullptr);
774 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
779 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
783 auto const sleMPT1 = env.le(mptoken);
784 BEAST_EXPECT(sleMPT1 ==
nullptr);
787 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
788 tx[sfDestination] = owner.human();
792 auto const sleMPT2 = env.le(mptoken);
793 BEAST_EXPECT(sleMPT2 ==
nullptr);
796 {.requireAuth =
false});
798 auto const redeemAllNoAssetMpt = [
this](
TER expected) {
799 return [
this, expected](
807 testcase <<
"MPT non-owner redeems all shares with no asset MPToken"
808 << (
isTesSuccess(expected) ?
"" :
" pre-fixCleanup3_4_0");
810 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
815 {.depositor = depositor,
817 .amount = asset(1000)});
821 auto const vaultSle = env.le(
keylet);
822 if (!BEAST_EXPECT(vaultSle))
824 auto const shareMPTID = vaultSle->at(sfShareMPTID);
827 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
833 auto const sleShareBefore = env.le(shareKeylet);
834 if (!BEAST_EXPECT(sleShareBefore))
836 auto const shareAmountBefore = sleShareBefore->at(sfMPTAmount);
837 auto const assetsTotalBefore = vaultSle->at(sfAssetsTotal);
838 auto const assetsAvailableBefore = vaultSle->at(sfAssetsAvailable);
843 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
844 env(tx, Ter{expected});
847 auto const sleAsset = env.le(mptoken);
848 auto const sleShare = env.le(shareKeylet);
849 auto const vaultAfter = env.le(
keylet);
850 if (!BEAST_EXPECT(vaultAfter))
854 if (!BEAST_EXPECT(sleAsset))
856 BEAST_EXPECT(sleAsset->at(sfMPTAmount) == 1000);
857 BEAST_EXPECT(!sleShare);
858 BEAST_EXPECT(vaultAfter->at(sfAssetsTotal) ==
beast::kZero);
859 BEAST_EXPECT(vaultAfter->at(sfAssetsAvailable) ==
beast::kZero);
863 BEAST_EXPECT(!sleAsset);
864 if (!BEAST_EXPECT(sleShare))
866 BEAST_EXPECT(sleShare->at(sfMPTAmount) == shareAmountBefore);
867 BEAST_EXPECT(vaultAfter->at(sfAssetsTotal) == assetsTotalBefore);
868 BEAST_EXPECT(vaultAfter->at(sfAssetsAvailable) == assetsAvailableBefore);
873 testCase(redeemAllNoAssetMpt(
tesSUCCESS), {.requireAuth =
false});
876 {.requireAuth =
false, .features = testableAmendments() - fixCleanup3_4_0});
879 Env
const env{*
this, testableAmendments()};
881 env.current()->fees().accountReserve(0, 1).drops() /
kDropsPerXrp.drops(),
882 env.current()->fees().increment.drops() /
kDropsPerXrp.drops()};
894 testcase(
"MPT fail reserve to re-create MPToken");
896 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
902 env(pay(depositor, owner, asset(1000)));
908 .amount = asset(1000)});
914 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
918 auto const sleMPT = env.le(mptoken);
919 BEAST_EXPECT(sleMPT ==
nullptr);
922 env(ticket::create(owner, 1));
927 {.depositor = owner, .id =
keylet.key, .amount = asset(100)});
931 env(pay(depositor, owner, XRP(incReserve)));
939 {.requireAuth =
false, .initialXRP = acctReserve + (incReserve * 4) + 1});
951 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
955 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
960 auto tx = vault.clawback(
961 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(0)});
965 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
969 auto [tx,
keylet] = vault.create({.owner = depositor, .asset = asset});
975 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
981 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
986 auto tx = vault.clawback(
987 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(0)});
991 env(vault.del({.owner = owner, .id = keylet.key}));
1002 testcase(
"MPT vault owner can receive shares unless unauthorized");
1004 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1008 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
1013 auto const vault = env.le(
keylet);
1014 return vault->at(sfShareMPTID);
1020 env(pay(depositor, owner, shares(1)));
1023 tx = vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = shares(1)});
1028 env(pay(depositor, owner, shares(1)));
1031 tx = vault.clawback(
1032 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(0)});
1037 env(pay(depositor, owner, shares(1)));
1041 env(pay(owner, depositor, shares(1)));
1047 jv[sfAccount] = owner.human();
1048 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
1049 jv[sfFlags] = tfMPTUnauthorize;
1050 jv[sfTransactionType] = jss::MPTokenAuthorize;
1056 tx = pay(depositor, owner, shares(1));
1061 tx = vault.clawback(
1062 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(0)});
1067 env(vault.del({.owner = owner, .id = keylet.key}));
1083 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1088 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
1093 auto tx = vault.clawback(
1096 .holder = depositor,
1097 .amount = asset(0)});
1101 {.enableClawback =
false});
1112 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1115 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
1119 mptt.authorize({.account = issuer, .holder = depositor, .flags = tfMPTUnauthorize});
1123 auto tx = vault.withdraw(
1124 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
1128 tx[sfDestination] = issuer.human();
1132 tx[sfDestination] = owner.human();
1140 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
1146 tx = vault.clawback(
1147 {.issuer = issuer, .id =
keylet.key, .holder = issuer, .amount = asset(800)});
1151 tx = vault.clawback(
1152 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(800)});
1156 env(vault.del({.owner = owner, .id = keylet.key}));
1162 Env env{*
this, testableAmendments()};
1164 Account const issuer{
"issuer"};
1165 env.fund(XRP(1000000), owner, issuer);
1167 Vault const vault{env};
1169 MPTTester mptt{env, issuer, kMptInitNoFund};
1171 {.flags = tfMPTCanTransfer | tfMPTCanLock | lsfMPTCanClawback | tfMPTRequireAuth});
1172 mptt.authorize({.account = owner});
1173 mptt.authorize({.account = issuer, .holder = owner});
1175 env(pay(issuer, owner, asset(100)));
1176 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
1180 auto const shares = [&env,
keylet = k1,
this]() ->
Asset {
1181 auto const vault = env.le(
keylet);
1182 BEAST_EXPECT(vault !=
nullptr);
1183 return MPTIssue(vault->at(sfShareMPTID));
1186 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
1192 testcase(
"MPT locked: vault shares inherit underlying lock");
1194 Env env{*
this, testableAmendments()};
1195 Account const issuer{
"issuer"};
1200 env.fund(XRP(10'000), issuer, owner, alice, bob, carol);
1202 Vault const vault{env};
1207 .holders = {owner, alice, bob, carol},
1208 .flags = tfMPTCanTransfer | tfMPTCanTrade | tfMPTCanLock}};
1209 env(pay(issuer, alice, asset(1'000)));
1210 env(pay(issuer, bob, asset(1'000)));
1213 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1217 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(500)}));
1219 env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(500)}));
1223 auto const sle = env.le(
keylet);
1224 BEAST_EXPECT(sle !=
nullptr);
1225 return MPTIssue(sle->at(sfShareMPTID));
1227 auto const shareMptID = shares.raw().get<
MPTIssue>().getMptID();
1228 auto const shareBalance = [&](
Account const& account) {
1230 return sle ? sle->at(sfMPTAmount) : 0;
1235 env(pay(alice, bob, shares(1)));
1240 env(offer(alice, XRP(1), shares(1)));
1244 asset.set({.account = issuer, .flags = tfMPTLock});
1249 BEAST_EXPECT(shareBalance(alice) == 499);
1250 BEAST_EXPECT(shareBalance(bob) == 501);
1251 env(pay(alice, bob, shares(1)), Ter{
tecLOCKED});
1253 BEAST_EXPECT(shareBalance(alice) == 499);
1254 BEAST_EXPECT(shareBalance(bob) == 501);
1258 env(pay(carol, bob, shares(1)),
1260 Path(BookSpec{shares.raw()}),
1263 BEAST_EXPECT(shareBalance(alice) == 499);
1264 BEAST_EXPECT(shareBalance(bob) == 501);
1265 BEAST_EXPECT(expectOffers(env, alice, 1));
1269 testcase(
"MPT CanTrade governance: share inherits underlying on DEX and AMM");
1271 Env env{*
this, testableAmendments()};
1272 Account const issuer{
"issuer"};
1276 env.fund(XRP(100'000), issuer, owner, alice, bob);
1278 Vault const vault{env};
1280 MPTTester mptt{env, issuer, kMptInitNoFund};
1281 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
1283 mptt.authorize({.account = owner});
1284 mptt.authorize({.account = alice});
1285 mptt.authorize({.account = bob});
1286 env(pay(issuer, alice, asset(10'000)));
1287 env(pay(issuer, bob, asset(10'000)));
1290 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1295 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(5'000)}));
1296 env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(5'000)}));
1300 auto const sle = env.le(
keylet);
1301 BEAST_EXPECT(sle !=
nullptr);
1302 return MPTIssue(sle->at(sfShareMPTID));
1312 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(100)}));
1317 env(pay(alice, bob, shares(1)));
1321 env(vault.withdraw({.depositor = alice, .id = keylet.key, .amount = asset(100)}));
1325 mptt.set({.flags = tfMPTSetCanTrade});
1328 env(offer(alice, XRP(1), asset(10)));
1329 env(offer(alice, XRP(1), shares(1)));
1332 AMM
const ammUnderlying(env, alice, XRP(1'000), asset(1'000));
1336 testcase(
"MPT OutstandingAmount > MaximumAmount");
1338 Env env{*
this, testableAmendments() | featureSingleAssetVault};
1340 Account const issuer{
"issuer"};
1341 env.fund(XRP(1'000), alice, issuer);
1343 Vault const vault{env};
1345 MPTTester
const btc({.env = env, .issuer = issuer, .holders = {alice}, .maxAmt = 100});
1347 auto [tx, k] = vault.create({.owner = issuer, .asset = btc});
1351 tx = vault.deposit({.depositor = issuer, .id = k.key, .amount = btc(110)});
1358 env(pay(issuer, alice, btc(100)));
1361 tx = vault.deposit({.depositor = issuer, .id = k.key, .amount = btc(100)});
1366 tx = vault.deposit({.depositor = alice, .id = k.key, .amount = btc(100)});
1380 int initialXRP = 1000;
1383 bool charlieRipple =
true;
1387 auto testCase = [&,
this](
1397 CaseArgs args = {}) {
1398 Env env{*
this, args.features};
1400 Account const issuer{
"issuer"};
1401 Account const charlie{
"charlie"};
1403 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
1404 env(fset(issuer, asfAllowTrustLineClawback));
1408 env.trust(asset(1000), owner);
1409 env(pay(issuer, owner, asset(args.initialIOU)));
1411 if (!args.charlieRipple)
1413 env(fset(issuer, 0, asfDefaultRipple));
1415 env.trust(asset(1000), charlie);
1417 env(pay(issuer, charlie, asset(args.initialIOU)));
1419 env(fset(issuer, asfDefaultRipple));
1423 env.trust(asset(1000), charlie);
1426 env(rate(issuer, args.transferRate));
1433 return env.le(
keylet)->at(sfShareMPTID);
1436 test(env, owner, issuer, charlie, vaultAccount, vault, asset, issuanceId);
1448 testcase(
"IOU cannot use different asset");
1451 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1457 auto tx = [&, account = vaultAccount(
keylet)]() {
1459 jv[jss::Account] = issuer.human();
1461 auto& ja = jv[jss::LimitAmount] =
1463 ja[jss::issuer] =
toBase58(account);
1465 jv[jss::TransactionType] = jss::TrustSet;
1466 jv[jss::Flags] = tfSetFreeze;
1474 auto tx = vault.deposit({.depositor = issuer, .id =
keylet.key, .amount = foo(20)});
1481 vault.withdraw({.depositor = issuer, .id =
keylet.key, .amount = foo(20)});
1486 env(vault.del({.owner = owner, .id = keylet.key}));
1500 testcase(
"IOU transfer fees not applied");
1502 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1506 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
1513 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
1514 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(100));
1517 auto tx = vault.clawback(
1518 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(50)});
1524 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
1525 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(50));
1528 {.depositor = owner, .id = keylet.key, .amount = share(20'000'000)}));
1531 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
1532 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(30));
1535 auto tx = vault.withdraw(
1536 {.depositor = owner, .id =
keylet.key, .amount = share(30'000'000)});
1537 tx[sfDestination] = charlie.human();
1542 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
1543 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
1544 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(0));
1546 env(vault.del({.owner = owner, .id = keylet.key}));
1549 CaseArgs{.transferRate = 1.25});
1560 testcase(
"IOU no trust line to 3rd party");
1562 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1566 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
1570 env.fund(XRP(1000), erin);
1576 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
1577 tx[sfDestination] = erin.human();
1592 testcase(
"IOU no trust line to depositor");
1594 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1599 env.trust(asset(0), owner);
1602 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
1606 BEAST_EXPECT(trustline ==
nullptr);
1611 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
1629 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1636 env(trust(issuer, vaultAccount(
keylet)[
"IOU"], tfSetNoRipple));
1640 auto tx = vault.deposit(
1641 {.depositor = charlie, .id =
keylet.key, .amount = asset(100)});
1649 vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(100)});
1654 auto tx2 = vault.withdraw(
1655 {.depositor = owner, .id =
keylet.key, .amount = shares(100)});
1656 tx2[sfDestination] = charlie.human();
1663 tx[sfAccount] = charlie.human();
1664 tx[sfMPTokenIssuanceID] =
1666 tx[sfTransactionType] = jss::MPTokenAuthorize;
1680 env(pay(owner, charlie, shares(100)), Ter{
tecPATH_DRY});
1684 tx = vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(100)});
1689 env(vault.del({.owner = owner, .id = keylet.key}));
1691 {.charlieRipple =
false});
1699 auto const& vaultAccount,
1703 testcase(
"IOU calculation rounding");
1705 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1710 auto const startingOwnerBalance = env.balance(owner, asset);
1711 BEAST_EXPECT((startingOwnerBalance.value() ==
STAmount{asset, 11875, -2}));
1717 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
1719 auto const tx1 = vault.deposit(
1720 {.depositor = owner, .id =
keylet.key, .amount = asset(
Number(375, -2))});
1721 for (
auto i = 0; i < 5; ++i)
1728 STAmount const xfer{asset, 1185, -1};
1729 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value() - xfer);
1730 BEAST_EXPECT(env.balance(vaultAccount(
keylet), asset) == xfer);
1732 auto const vault = env.le(
keylet);
1733 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
1734 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
1741 {.depositor = owner,
1743 .amount = asset(Number(1000 + (37 * 5), -1))}));
1746 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value());
1748 auto const vault = env.le(
keylet);
1749 BEAST_EXPECT(vault->at(sfAssetsAvailable) ==
beast::kZero);
1750 BEAST_EXPECT(vault->at(sfAssetsTotal) ==
beast::kZero);
1753 env(vault.del({.owner = owner, .id = keylet.key}));
1756 {.initialIOU =
Number(11875, -2)});
1759 Env
const env{*
this, testableAmendments()};
1761 env.current()->fees().accountReserve(0, 1).drops() /
kDropsPerXrp.drops(),
1762 env.current()->fees().increment.drops() /
kDropsPerXrp.drops()};
1775 testcase(
"IOU no trust line to depositor no reserve");
1776 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1782 env.trust(asset(0), owner);
1785 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
1789 BEAST_EXPECT(trustline ==
nullptr);
1791 env(ticket::create(owner, 1));
1795 tx = vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
1799 env(pay(charlie, owner, XRP(incReserve)));
1806 CaseArgs{.initialXRP = acctReserve + (incReserve * 4) + 1});
1818 testcase(
"IOU no reserve for share MPToken");
1819 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1823 env(pay(owner, charlie, asset(100)));
1826 env(ticket::create(charlie, 3));
1830 tx = vault.deposit({.depositor = charlie, .id =
keylet.key, .amount = asset(100)});
1834 env(pay(issuer, charlie, XRP(incReserve)));
1841 CaseArgs{.initialXRP = acctReserve + (incReserve * 4) + 1});