46 using namespace test::jtx;
47 Account issuer{
"issuer"};
48 Account owner{
"owner"};
49 Account depositor{
"depositor"};
50 Account charlie{
"charlie"};
53 auto const testSequence = [&,
this](
58 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
59 tx[sfData] =
"AFEED00E";
60 tx[sfAssetsMaximum] = asset(100).number();
63 BEAST_EXPECT(env.le(keylet));
66 auto const [share, vaultAccount] =
71 auto const vault = env.le(keylet);
72 BEAST_EXPECT(vault !=
nullptr);
73 if (!asset.integral())
74 BEAST_EXPECT(vault->at(sfScale) == 6);
76 BEAST_EXPECT(vault->at(sfScale) == 0);
79 BEAST_EXPECT(shares !=
nullptr);
80 if (!asset.integral())
81 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
83 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
86 Account(
"vault", vault->at(sfAccount))};
88 auto const shares = share.raw().get<
MPTIssue>();
89 env.memoize(vaultAccount);
92 Account alice{
"alice"};
94 env.fund(XRP(1000), alice, erin);
99 testcase(prefix +
" fail to deposit more than assets held");
100 auto tx = vault.deposit(
101 {.depositor = depositor,
103 .amount = asset(10000)});
109 testcase(prefix +
" deposit non-zero amount");
110 auto tx = vault.deposit(
111 {.depositor = depositor,
113 .amount = asset(50)});
117 env.balance(depositor, shares) == share(50 * scale));
121 testcase(prefix +
" deposit non-zero amount again");
122 auto tx = vault.deposit(
123 {.depositor = depositor,
125 .amount = asset(50)});
129 env.balance(depositor, shares) == share(100 * scale));
133 testcase(prefix +
" fail to delete non-empty vault");
134 auto tx = vault.del({.owner = owner, .id = keylet.
key});
140 testcase(prefix +
" fail to update because wrong owner");
141 auto tx = vault.set({.owner = issuer, .id = keylet.
key});
142 tx[sfAssetsMaximum] = asset(50).number();
149 prefix +
" fail to set maximum lower than current amount");
150 auto tx = vault.set({.owner = owner, .id = keylet.
key});
151 tx[sfAssetsMaximum] = asset(50).number();
157 testcase(prefix +
" set maximum higher than current amount");
158 auto tx = vault.set({.owner = owner, .id = keylet.
key});
159 tx[sfAssetsMaximum] = asset(150).number();
165 testcase(prefix +
" set maximum is idempotent, set it again");
166 auto tx = vault.set({.owner = owner, .id = keylet.
key});
167 tx[sfAssetsMaximum] = asset(150).number();
174 auto tx = vault.set({.owner = owner, .id = keylet.
key});
181 testcase(prefix +
" fail to set domain on public vault");
182 auto tx = vault.set({.owner = owner, .id = keylet.
key});
189 testcase(prefix +
" fail to deposit more than maximum");
190 auto tx = vault.deposit(
191 {.depositor = depositor,
193 .amount = asset(100)});
199 testcase(prefix +
" reset maximum to zero i.e. not enforced");
200 auto tx = vault.set({.owner = owner, .id = keylet.
key});
201 tx[sfAssetsMaximum] = asset(0).number();
207 testcase(prefix +
" fail to withdraw more than assets held");
208 auto tx = vault.withdraw(
209 {.depositor = depositor,
211 .amount = asset(1000)});
217 testcase(prefix +
" deposit some more");
218 auto tx = vault.deposit(
219 {.depositor = depositor,
221 .amount = asset(100)});
225 env.balance(depositor, shares) == share(200 * scale));
229 testcase(prefix +
" clawback some");
232 auto tx = vault.clawback(
236 .amount = asset(10)});
239 if (!asset.raw().native())
242 env.balance(depositor, shares) == share(190 * scale));
250 auto tx = vault.clawback(
251 {.issuer = issuer, .id = keylet.
key, .holder = depositor});
254 if (!asset.raw().native())
256 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
259 auto tx = vault.clawback(
263 .amount = asset(10)});
269 auto tx = vault.withdraw(
270 {.depositor = depositor,
272 .amount = asset(10)});
279 if (!asset.raw().native())
281 testcase(prefix +
" deposit again");
282 auto tx = vault.deposit(
283 {.depositor = depositor,
285 .amount = asset(200)});
289 env.balance(depositor, shares) == share(200 * scale));
293 testcase(prefix +
" deposit/withdrawal same or less than fee");
294 auto const amount = env.current()->fees().base;
296 auto tx = vault.deposit(
297 {.depositor = depositor,
304 {.depositor = depositor,
311 {.depositor = depositor,
319 {.depositor = depositor,
322 tx[sfDestination] = charlie.human();
327 {.depositor = depositor,
329 .amount = amount - 1});
334 {.depositor = depositor,
336 .amount = amount - 1});
343 prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
344 auto tx = vault.withdraw(
345 {.depositor = depositor,
347 .amount = asset(100)});
348 tx[sfDestination] = alice.human();
354 testcase(prefix +
" fail to withdraw to zero destination");
355 auto tx = vault.withdraw(
356 {.depositor = depositor,
358 .amount = asset(1000)});
359 tx[sfDestination] =
"0";
364 if (!asset.raw().native())
367 prefix +
" fail to withdraw to 3rd party no authorization");
368 auto tx = vault.withdraw(
369 {.depositor = depositor,
371 .amount = asset(100)});
372 tx[sfDestination] = erin.human();
381 " fail to withdraw to 3rd party lsfRequireDestTag");
382 auto tx = vault.withdraw(
383 {.depositor = depositor,
385 .amount = asset(100)});
386 tx[sfDestination] = dave.human();
392 testcase(prefix +
" withdraw to 3rd party lsfRequireDestTag");
393 auto tx = vault.withdraw(
394 {.depositor = depositor,
396 .amount = asset(50)});
397 tx[sfDestination] = dave.human();
398 tx[sfDestinationTag] =
"0";
404 testcase(prefix +
" deposit again");
405 auto tx = vault.deposit(
406 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
412 testcase(prefix +
" fail to withdraw lsfRequireDestTag");
413 auto tx = vault.withdraw(
414 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
420 testcase(prefix +
" withdraw with tag");
421 auto tx = vault.withdraw(
422 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
423 tx[sfDestinationTag] =
"0";
429 testcase(prefix +
" withdraw to authorized 3rd party");
430 auto tx = vault.withdraw(
431 {.depositor = depositor,
433 .amount = asset(50)});
434 tx[sfDestination] = charlie.human();
438 env.balance(depositor, shares) == share(100 * scale));
442 testcase(prefix +
" withdraw to issuer");
443 auto tx = vault.withdraw(
444 {.depositor = depositor,
446 .amount = asset(50)});
447 tx[sfDestination] = issuer.human();
451 env.balance(depositor, shares) == share(50 * scale));
454 if (!asset.raw().native())
456 testcase(prefix +
" issuer deposits");
457 auto tx = vault.deposit(
458 {.depositor = issuer,
460 .amount = asset(10)});
463 BEAST_EXPECT(env.balance(issuer, shares) == share(10 * scale));
465 testcase(prefix +
" issuer withdraws");
467 {.depositor = issuer,
469 .amount = share(10 * scale)});
472 BEAST_EXPECT(env.balance(issuer, shares) == share(0 * scale));
476 testcase(prefix +
" withdraw remaining assets");
477 auto tx = vault.withdraw(
478 {.depositor = depositor,
480 .amount = asset(50)});
483 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
485 if (!asset.raw().native())
487 auto tx = vault.clawback(
491 .amount = asset(0)});
497 auto tx = vault.withdraw(
498 {.depositor = depositor,
500 .amount = share(10)});
506 if (!asset.integral())
508 testcase(prefix +
" temporary authorization for 3rd party");
509 env(trust(erin, asset(1000)));
510 env(trust(issuer, asset(0), erin,
tfSetfAuth));
511 env(pay(issuer, erin, asset(10)));
514 auto tx = vault.deposit(
515 {.depositor = erin, .id = keylet.
key, .amount = asset(10)});
519 auto tx = pay(erin, depositor, share(10 * scale));
527 {.depositor = depositor,
529 .amount = asset(1)}));
536 testcase(prefix +
" withdraw to authorized 3rd party");
539 {.depositor = depositor,
541 .amount = asset(10)});
542 tx[sfDestination] = erin.human();
547 env(pay(erin, issuer, asset(10)));
550 testcase(prefix +
" fail to pay to unauthorized 3rd party");
551 env(trust(erin, asset(0)));
555 env(pay(depositor, erin, share(1)), ter{
tecNO_LINE});
560 {.depositor = depositor,
562 .amount = asset(1)});
568 testcase(prefix +
" fail to delete because wrong owner");
569 auto tx = vault.del({.owner = issuer, .id = keylet.
key});
575 testcase(prefix +
" delete empty vault");
576 auto tx = vault.del({.owner = owner, .id = keylet.
key});
579 BEAST_EXPECT(!env.le(keylet));
583 auto testCases = [&,
this](
586 Env env{*
this, testable_amendments() | featureSingleAssetVault};
589 env.fund(XRP(1000), issuer, owner, depositor, charlie, dave);
599 testSequence(prefix, env, vault, asset);
606 testCases(
"IOU", [&](Env& env) ->
Asset {
608 env(trust(owner, asset(1000)));
609 env(trust(depositor, asset(1000)));
610 env(trust(charlie, asset(1000)));
611 env(trust(dave, asset(1000)));
612 env(trust(issuer, asset(0), owner,
tfSetfAuth));
613 env(trust(issuer, asset(0), depositor,
tfSetfAuth));
614 env(trust(issuer, asset(0), charlie,
tfSetfAuth));
615 env(trust(issuer, asset(0), dave,
tfSetfAuth));
616 env(pay(issuer, depositor, asset(1000)));
621 testCases(
"MPT", [&](Env& env) ->
Asset {
622 MPTTester mptt{env, issuer, mptInitNoFund};
626 mptt.authorize({.account = depositor});
627 mptt.authorize({.account = charlie});
628 mptt.authorize({.account = dave});
629 env(pay(issuer, depositor, asset(1000)));
638 using namespace test::jtx;
643 testable_amendments() | featureSingleAssetVault;
646 auto testCase = [&,
this](
649 Account
const& issuer,
650 Account
const& owner,
653 CaseArgs args = {}) {
654 Env env{*
this, args.features};
655 Account issuer{
"issuer"};
656 Account owner{
"owner"};
658 env.fund(XRP(1000), issuer, owner);
666 env(trust(owner, asset(1000)));
667 env(trust(issuer, asset(0), owner,
tfSetfAuth));
668 env(pay(issuer, owner, asset(1000)));
671 test(env, issuer, owner, asset, vault);
675 return [&, resultAfterCreate](
677 Account
const& issuer,
678 Account
const& owner,
681 testcase(
"disabled single asset vault");
684 vault.create({.owner = owner, .asset = asset});
688 auto tx = vault.set({.owner = owner, .id = keylet.
key});
689 env(tx, data(
"test"), ter{resultAfterCreate});
693 auto tx = vault.deposit(
696 .amount = asset(10)});
697 env(tx, ter{resultAfterCreate});
701 auto tx = vault.withdraw(
704 .amount = asset(10)});
705 env(tx, ter{resultAfterCreate});
709 auto tx = vault.clawback(
713 .amount = asset(10)});
714 env(tx, ter{resultAfterCreate});
718 auto tx = vault.del({.owner = owner, .id = keylet.
key});
719 env(tx, ter{resultAfterCreate});
726 {.features = testable_amendments() - featureSingleAssetVault});
730 {.features = testable_amendments() - featureMPTokensV1});
734 Account
const& issuer,
735 Account
const& owner,
738 testcase(
"disabled permissioned domains");
741 vault.create({.owner = owner, .asset = asset});
749 auto tx = vault.set({.owner = owner, .id = keylet.
key});
750 env(tx, data(
"Test"));
756 {.features = testable_amendments() - featurePermissionedDomains});
758 testCase([&](Env& env,
759 Account
const& issuer,
760 Account
const& owner,
765 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
770 auto tx = vault.set({.owner = owner, .id = keylet.
key});
776 auto tx = vault.deposit(
779 .amount = asset(10)});
785 auto tx = vault.withdraw(
788 .amount = asset(10)});
794 auto tx = vault.clawback(
798 .amount = asset(10)});
804 auto tx = vault.del({.owner = owner, .id = keylet.
key});
810 testCase([&](Env& env,
811 Account
const& issuer,
812 Account
const& owner,
817 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
822 auto tx = vault.set({.owner = owner, .id = keylet.
key});
828 auto tx = vault.deposit(
831 .amount = asset(10)});
837 auto tx = vault.withdraw(
840 .amount = asset(10)});
846 auto tx = vault.clawback(
850 .amount = asset(10)});
856 auto tx = vault.del({.owner = owner, .id = keylet.
key});
865 Account
const& owner,
868 testcase(
"disabled permissioned domain");
871 vault.create({.owner = owner, .asset =
xrpIssue()});
876 auto tx = vault.set({.owner = owner, .id = keylet.
key});
882 auto tx = vault.set({.owner = owner, .id = keylet.
key});
883 tx[sfDomainID] =
"0";
887 {.features = (testable_amendments() | featureSingleAssetVault) -
888 featurePermissionedDomains});
890 testCase([&](Env& env,
891 Account
const& issuer,
892 Account
const& owner,
898 vault.create({.owner = owner, .asset =
xrpIssue()});
901 auto tx = vault.set({
909 auto tx = vault.deposit(
912 .amount = asset(10)});
917 auto tx = vault.withdraw(
920 .amount = asset(10)});
925 auto tx = vault.clawback(
929 .amount = asset(10)});
934 auto tx = vault.del({
942 testCase([&](Env& env,
944 Account
const& owner,
947 testcase(
"withdraw to bad destination");
949 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
952 auto tx = vault.withdraw(
955 .amount = asset(10)});
956 tx[jss::Destination] =
"0";
961 testCase([&](Env& env,
963 Account
const& owner,
970 vault.create({.owner = owner, .asset = asset});
977 vault.create({.owner = owner, .asset = asset});
985 vault.create({.owner = owner, .asset = asset});
989 auto const sleVault = env.le(keylet);
990 BEAST_EXPECT(sleVault);
991 BEAST_EXPECT((*sleVault)[sfScale] == 18);
996 vault.create({.owner = owner, .asset = asset});
1000 auto const sleVault = env.le(keylet);
1001 BEAST_EXPECT(sleVault);
1002 BEAST_EXPECT((*sleVault)[sfScale] == 0);
1007 vault.create({.owner = owner, .asset = asset});
1010 auto const sleVault = env.le(keylet);
1011 BEAST_EXPECT(sleVault);
1012 BEAST_EXPECT((*sleVault)[sfScale] == 6);
1016 testCase([&](Env& env,
1018 Account
const& owner,
1021 testcase(
"create or set invalid data");
1023 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1039 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1045 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1052 testCase([&](Env& env,
1054 Account
const& owner,
1059 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1062 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1067 testCase([&](Env& env,
1069 Account
const& owner,
1072 testcase(
"create with invalid metadata");
1074 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1078 tx[sfMPTokenMetadata] =
"";
1091 testCase([&](Env& env,
1093 Account
const& owner,
1098 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1101 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1107 testCase([&](Env& env,
1109 Account
const& owner,
1112 testcase(
"invalid deposit amount");
1114 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1117 auto tx = vault.deposit(
1118 {.depositor = owner,
1125 auto tx = vault.deposit(
1126 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1131 testCase([&](Env& env,
1133 Account
const& owner,
1136 testcase(
"invalid set immutable flag");
1138 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1141 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1147 testCase([&](Env& env,
1149 Account
const& owner,
1152 testcase(
"invalid withdraw amount");
1154 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1157 auto tx = vault.withdraw(
1158 {.depositor = owner,
1165 auto tx = vault.withdraw(
1166 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1171 testCase([&](Env& env,
1172 Account
const& issuer,
1173 Account
const& owner,
1178 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1183 auto tx = vault.clawback(
1187 .amount = asset(50)});
1192 auto tx = vault.clawback(
1201 testCase([&](Env& env,
1203 Account
const& owner,
1208 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1212 tx[sfWithdrawalPolicy] = 0;
1231 tx[sfDomainID] =
"0";
1720 using namespace test::jtx;
1724 bool enableClawback =
true;
1726 int initialXRP = 1000;
1729 auto testCase = [
this](
1732 Account
const& issuer,
1733 Account
const& owner,
1734 Account
const& depositor,
1737 MPTTester& mptt)> test,
1738 CaseArgs args = {}) {
1739 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1740 Account issuer{
"issuer"};
1741 Account owner{
"owner"};
1742 Account depositor{
"depositor"};
1743 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
1747 MPTTester mptt{env, issuer, mptInitNoFund};
1755 mptt.authorize({.account = owner});
1756 mptt.authorize({.account = depositor});
1757 if (args.requireAuth)
1759 mptt.authorize({.account = issuer, .holder = owner});
1760 mptt.authorize({.account = issuer, .holder = depositor});
1763 env(pay(issuer, depositor, asset(1000)));
1766 test(env, issuer, owner, depositor, asset, vault, mptt);
1771 Account
const& issuer,
1772 Account
const& owner,
1773 Account
const& depositor,
1777 testcase(
"MPT nothing to clawback from");
1778 auto tx = vault.clawback(
1781 .holder = depositor,
1782 .amount = asset(10)});
1788 Account
const& issuer,
1789 Account
const& owner,
1790 Account
const& depositor,
1794 testcase(
"MPT global lock blocks create");
1795 mptt.set({.account = issuer, .flags =
tfMPTLock});
1796 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1802 Account
const& issuer,
1803 Account
const& owner,
1804 Account
const& depositor,
1808 testcase(
"MPT global lock blocks deposit");
1809 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1813 mptt.set({.account = issuer, .flags =
tfMPTLock});
1817 {.depositor = depositor,
1819 .amount = asset(100)});
1824 tx = vault.del({.owner = owner, .id = keylet.
key});
1830 Account
const& issuer,
1831 Account
const& owner,
1832 Account
const& depositor,
1836 testcase(
"MPT global lock blocks withdrawal");
1837 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1841 {.depositor = depositor,
1843 .amount = asset(100)});
1849 auto v = env.le(keylet);
1851 MPTID share = (*v)[sfShareMPTID];
1853 BEAST_EXPECT(issuance);
1854 Number outstandingShares = issuance->at(sfOutstandingAmount);
1855 BEAST_EXPECT(outstandingShares == 100);
1857 mptt.set({.account = issuer, .flags =
tfMPTLock});
1860 tx = vault.withdraw(
1861 {.depositor = depositor,
1863 .amount = asset(100)});
1866 tx[sfDestination] = issuer.human();
1870 tx = vault.clawback(
1873 .holder = depositor,
1874 .amount = asset(0)});
1880 BEAST_EXPECT(mptSle ==
nullptr);
1883 tx = vault.del({.owner = owner, .id = keylet.
key});
1889 Account
const& issuer,
1890 Account
const& owner,
1891 Account
const& depositor,
1895 testcase(
"MPT only issuer can clawback");
1897 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1902 {.depositor = depositor,
1904 .amount = asset(100)});
1909 auto tx = vault.clawback({
1910 .issuer = depositor,
1912 .holder = depositor,
1918 auto tx = vault.clawback({
1921 .holder = depositor,
1930 Account
const& issuer,
1931 Account
const& owner,
1932 Account
const& depositor,
1936 testcase(
"MPT depositor without MPToken, auth required");
1939 vault.create({.owner = owner, .asset = asset});
1944 {.depositor = depositor,
1946 .amount = asset(1000)});
1956 auto const mptoken =
1958 auto const sleMPT1 = env.le(mptoken);
1959 BEAST_EXPECT(sleMPT1 ==
nullptr);
1961 tx = vault.withdraw(
1962 {.depositor = depositor,
1964 .amount = asset(100)});
1968 auto const sleMPT2 = env.le(mptoken);
1969 BEAST_EXPECT(sleMPT2 ==
nullptr);
1974 Account charlie{
"charlie"};
1975 env.fund(XRP(1000), charlie);
1978 tx = vault.withdraw(
1979 {.depositor = depositor,
1981 .amount = asset(100)});
1982 tx[sfDestination] = charlie.human();
1986 {.requireAuth =
true});
1991 Account
const& issuer,
1992 Account
const& owner,
1993 Account
const& depositor,
1997 testcase(
"MPT depositor without MPToken, no auth required");
2000 vault.create({.owner = owner, .asset = asset});
2003 auto v = env.le(keylet);
2007 {.depositor = depositor,
2009 .amount = asset(1000)});
2019 auto const mptoken =
2021 auto const sleMPT1 = env.le(mptoken);
2022 BEAST_EXPECT(sleMPT1 ==
nullptr);
2024 tx = vault.withdraw(
2025 {.depositor = depositor,
2027 .amount = asset(100)});
2031 auto const sleMPT2 = env.le(mptoken);
2032 BEAST_EXPECT(sleMPT2 !=
nullptr);
2033 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
2042 auto const mptoken =
2044 auto const sleMPT1 = env.le(mptoken);
2045 BEAST_EXPECT(sleMPT1 ==
nullptr);
2047 tx = vault.withdraw(
2048 {.depositor = depositor,
2050 .amount = asset(100)});
2051 tx[sfDestination] = owner.human();
2055 auto const sleMPT2 = env.le(mptoken);
2056 BEAST_EXPECT(sleMPT2 ==
nullptr);
2059 {.requireAuth =
false});
2062 Env env{*
this, testable_amendments()};
2064 env.current()->fees().accountReserve(0).drops() /
2066 env.current()->fees().increment.drops() /
2073 Account
const& issuer,
2074 Account
const& owner,
2075 Account
const& depositor,
2079 testcase(
"MPT failed reserve to re-create MPToken");
2082 vault.create({.owner = owner, .asset = asset});
2085 auto v = env.le(keylet);
2088 env(pay(depositor, owner, asset(1000)));
2092 {.depositor = owner,
2094 .amount = asset(1000)});
2104 auto const mptoken =
2106 auto const sleMPT = env.le(mptoken);
2107 BEAST_EXPECT(sleMPT ==
nullptr);
2110 env(ticket::create(owner, 1));
2114 tx = vault.withdraw(
2115 {.depositor = owner,
2117 .amount = asset(100)});
2121 env(pay(depositor, owner, XRP(incReserve)));
2129 {.requireAuth =
false,
2130 .initialXRP = acctReserve + incReserve * 4 + 1});
2134 Account
const& issuer,
2135 Account
const& owner,
2136 Account
const& depositor,
2142 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2147 {.depositor = depositor,
2149 .amount = asset(1000)});
2154 auto tx = vault.clawback(
2157 .holder = depositor,
2158 .amount = asset(0)});
2162 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
2167 vault.create({.owner = depositor, .asset = asset});
2172 auto tx = vault.deposit(
2173 {.depositor = depositor,
2175 .amount = asset(10)});
2180 auto tx = vault.withdraw(
2181 {.depositor = depositor,
2183 .amount = asset(10)});
2188 auto tx = vault.clawback(
2191 .holder = depositor,
2192 .amount = asset(0)});
2196 env(vault.del({.owner = owner, .id = keylet.key}));
2201 Account
const& issuer,
2202 Account
const& owner,
2203 Account
const& depositor,
2207 testcase(
"MPT vault owner can receive shares unless unauthorized");
2209 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2214 {.depositor = depositor,
2216 .amount = asset(1000)});
2221 auto const vault = env.le(keylet);
2222 return vault->at(sfShareMPTID);
2228 env(pay(depositor, owner, shares(1)));
2231 tx = vault.withdraw(
2232 {.depositor = owner,
2234 .amount = shares(1)});
2239 env(pay(depositor, owner, shares(1)));
2242 tx = vault.clawback(
2246 .amount = asset(0)});
2251 env(pay(depositor, owner, shares(1)));
2255 env(pay(owner, depositor, shares(1)));
2261 jv[sfAccount] = owner.human();
2262 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
2264 jv[sfTransactionType] = jss::MPTokenAuthorize;
2270 tx = pay(depositor, owner, shares(1));
2275 tx = vault.clawback(
2278 .holder = depositor,
2279 .amount = asset(0)});
2284 env(vault.del({.owner = owner, .id = keylet.key}));
2292 Account
const& issuer,
2293 Account
const& owner,
2294 Account
const& depositor,
2301 vault.create({.owner = owner, .asset = asset});
2306 {.depositor = depositor,
2308 .amount = asset(1000)});
2313 auto tx = vault.clawback(
2316 .holder = depositor,
2317 .amount = asset(0)});
2321 {.enableClawback =
false});
2325 Account
const& issuer,
2326 Account
const& owner,
2327 Account
const& depositor,
2332 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2336 {.depositor = depositor,
2338 .amount = asset(1000)});
2344 .holder = depositor,
2349 auto tx = vault.withdraw(
2350 {.depositor = depositor,
2352 .amount = asset(100)});
2356 tx[sfDestination] = issuer.human();
2360 tx[sfDestination] = owner.human();
2367 auto tx = vault.deposit(
2368 {.depositor = depositor,
2370 .amount = asset(100)});
2376 tx = vault.clawback(
2380 .amount = asset(800)});
2384 tx = vault.clawback(
2387 .holder = depositor,
2388 .amount = asset(800)});
2392 env(vault.del({.owner = owner, .id = keylet.key}));
2397 Account
const& issuer,
2398 Account
const& owner,
2399 Account
const& depositor,
2403 testcase(
"MPT lock of vault pseudo-account");
2404 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2408 auto const vaultAccount =
2409 [&env, keylet = keylet,
this]() ->
AccountID {
2410 auto const vault = env.le(keylet);
2411 BEAST_EXPECT(vault !=
nullptr);
2412 return vault->at(sfAccount);
2416 {.depositor = depositor,
2418 .amount = asset(100)});
2424 jv[jss::Account] = issuer.human();
2425 jv[sfMPTokenIssuanceID] =
2427 jv[jss::Holder] =
toBase58(vaultAccount);
2428 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
2436 {.depositor = depositor,
2438 .amount = asset(100)});
2441 tx = vault.withdraw(
2442 {.depositor = depositor,
2444 .amount = asset(100)});
2448 tx = vault.clawback(
2451 .holder = depositor,
2452 .amount = asset(100)});
2456 tx = vault.del({.owner = owner, .id = keylet.
key});
2463 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2464 Account owner{
"owner"};
2465 Account issuer{
"issuer"};
2466 env.fund(XRP(1000000), owner, issuer);
2470 MPTTester mptt{env, issuer, mptInitNoFund};
2474 mptt.authorize({.account = owner});
2475 mptt.authorize({.account = issuer, .holder = owner});
2477 env(pay(issuer, owner, asset(100)));
2478 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
2482 auto const shares = [&env, keylet = k1,
this]() ->
Asset {
2483 auto const vault = env.le(keylet);
2484 BEAST_EXPECT(vault !=
nullptr);
2485 return MPTIssue(vault->at(sfShareMPTID));
2488 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
2496 Account
const& owner,
2497 Account
const& depositor,
2503 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2508 {.depositor = depositor,
2510 .amount = asset(100)});
2521 tx = vault.withdraw(
2522 {.depositor = depositor,
2524 .amount = asset(100)});
2537 env(vault.del({.owner = owner, .id = keylet.key}));
2544 using namespace test::jtx;
2548 int initialXRP = 1000;
2551 bool charlieRipple =
true;
2558 Account
const& owner,
2559 Account
const& issuer,
2560 Account
const& charlie,
2565 CaseArgs args = {}) {
2566 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2567 Account
const owner{
"owner"};
2568 Account
const issuer{
"issuer"};
2569 Account
const charlie{
"charlie"};
2571 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
2576 env.trust(asset(1000), owner);
2577 env(pay(issuer, owner, asset(args.initialIOU)));
2579 if (!args.charlieRipple)
2583 env.trust(asset(1000), charlie);
2585 env(pay(issuer, charlie, asset(args.initialIOU)));
2590 env.trust(asset(1000), charlie);
2592 env(rate(issuer, args.transferRate));
2595 auto const vaultAccount =
2597 return Account(
"vault", env.le(keylet)->at(sfAccount));
2600 return env.le(keylet)->at(sfShareMPTID);
2616 Account
const& owner,
2617 Account
const& issuer,
2623 testcase(
"IOU cannot use different asset");
2626 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2632 auto tx = [&, account = vaultAccount(keylet)]() {
2634 jv[jss::Account] = issuer.human();
2636 auto& ja = jv[jss::LimitAmount] =
2638 ja[jss::issuer] =
toBase58(account);
2640 jv[jss::TransactionType] = jss::TrustSet;
2649 auto tx = vault.deposit(
2650 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2656 auto tx = vault.withdraw(
2657 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2662 env(vault.del({.owner = owner, .id = keylet.key}));
2668 Account
const& owner,
2669 Account
const& issuer,
2670 Account
const& charlie,
2675 testcase(
"IOU frozen trust line to vault account");
2677 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2682 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2685 Asset const share =
Asset(issuanceId(keylet));
2688 auto trustSet = [&, account = vaultAccount(keylet)]() {
2690 jv[jss::Account] = issuer.human();
2692 auto& ja = jv[jss::LimitAmount] =
2694 ja[jss::issuer] =
toBase58(account);
2696 jv[jss::TransactionType] = jss::TrustSet;
2708 auto tx = vault.deposit(
2709 {.depositor = owner,
2711 .amount = asset(80)});
2716 auto tx = vault.withdraw(
2717 {.depositor = owner,
2719 .amount = asset(100)});
2723 tx[sfDestination] = charlie.human();
2730 auto tx = vault.clawback(
2734 .amount = asset(50)});
2745 {.depositor = owner,
2747 .amount = share(50'000'000)}));
2749 env(vault.del({.owner = owner, .id = keylet.key}));
2756 Account
const& owner,
2757 Account
const& issuer,
2758 Account
const& charlie,
2763 testcase(
"IOU transfer fees not applied");
2766 vault.create({.owner = owner, .asset = asset});
2771 {.depositor = owner,
2773 .amount = asset(100)}));
2777 Asset const share =
Asset(issuanceId(keylet));
2780 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2782 env.balance(vaultAccount(keylet), issue) == asset(100));
2785 auto tx = vault.clawback(
2789 .amount = asset(50)});
2795 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2797 env.balance(vaultAccount(keylet), issue) == asset(50));
2800 {.depositor = owner,
2802 .amount = share(20'000'000)}));
2805 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2807 env.balance(vaultAccount(keylet), issue) == asset(30));
2810 auto tx = vault.withdraw(
2811 {.depositor = owner,
2813 .amount = share(30'000'000)});
2814 tx[sfDestination] = charlie.human();
2819 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2820 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2822 env.balance(vaultAccount(keylet), issue) == asset(0));
2824 env(vault.del({.owner = owner, .id = keylet.key}));
2827 CaseArgs{.transferRate = 1.25});
2831 Account
const& owner,
2832 Account
const& issuer,
2833 Account
const& charlie,
2838 testcase(
"IOU frozen trust line to depositor");
2840 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2845 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2849 auto const withdrawToCharlie = [&](
xrpl::Keylet keylet) {
2850 auto tx = vault.withdraw(
2851 {.depositor = owner,
2853 .amount = asset(10)});
2854 tx[sfDestination] = charlie.human();
2857 env(withdrawToCharlie);
2864 auto const withdraw = vault.withdraw(
2865 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2874 auto tx = vault.deposit(
2875 {.depositor = owner,
2877 .amount = asset(10)});
2883 auto tx = vault.clawback(
2887 .amount = asset(0)});
2892 env(vault.del({.owner = owner, .id = keylet.key}));
2898 Account
const& owner,
2899 Account
const& issuer,
2900 Account
const& charlie,
2905 testcase(
"IOU no trust line to 3rd party");
2907 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2912 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2915 Account
const erin{
"erin"};
2916 env.fund(XRP(1000), erin);
2921 auto tx = vault.withdraw(
2922 {.depositor = owner,
2924 .amount = asset(10)});
2925 tx[sfDestination] = erin.human();
2933 Account
const& owner,
2934 Account
const& issuer,
2935 Account
const& charlie,
2940 testcase(
"IOU no trust line to depositor");
2942 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2947 env.trust(asset(0), owner);
2951 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2956 BEAST_EXPECT(trustline ==
nullptr);
2960 auto tx = vault.withdraw(
2961 {.depositor = owner,
2963 .amount = asset(10)});
2972 Account
const& owner,
2973 Account
const& issuer,
2974 Account
const& charlie,
2982 vault.create({.owner = owner, .asset = asset});
2989 env(trust(issuer, vaultAccount(keylet)[
"IOU"],
tfSetNoRipple),
2994 auto tx = vault.deposit(
2995 {.depositor = charlie,
2997 .amount = asset(100)});
3004 auto tx1 = vault.deposit(
3005 {.depositor = owner,
3007 .amount = asset(100)});
3012 auto tx2 = vault.withdraw(
3013 {.depositor = owner,
3015 .amount = shares(100)});
3016 tx2[sfDestination] = charlie.human();
3023 tx[sfAccount] = charlie.human();
3024 tx[sfMPTokenIssuanceID] =
3026 tx[sfTransactionType] = jss::MPTokenAuthorize;
3030 env(pay(owner, charlie, shares(100)), THISLINE);
3034 auto tx3 = vault.withdraw(
3035 {.depositor = charlie,
3037 .amount = shares(100)});
3041 env(pay(charlie, owner, shares(100)), THISLINE);
3045 tx = vault.withdraw(
3046 {.depositor = owner,
3048 .amount = asset(100)});
3053 env(vault.del({.owner = owner, .id = keylet.key}), THISLINE);
3055 {.charlieRipple =
false});
3060 Account
const& owner,
3061 Account
const& issuer,
3062 Account
const& charlie,
3063 auto const& vaultAccount,
3067 testcase(
"IOU calculation rounding");
3070 vault.create({.owner = owner, .asset = asset});
3075 auto const startingOwnerBalance = env.balance(owner, asset);
3077 (startingOwnerBalance.value() ==
3085 {.depositor = owner,
3087 .amount = asset(100)}));
3089 auto const tx1 = vault.deposit(
3090 {.depositor = owner,
3092 .amount = asset(
Number(375, -2))});
3093 for (
auto i = 0; i < 5; ++i)
3100 STAmount const xfer{asset, 1185, -1};
3102 env.balance(owner, asset) ==
3103 startingOwnerBalance.value() - xfer);
3105 env.balance(vaultAccount(keylet), asset) == xfer);
3107 auto const vault = env.le(keylet);
3108 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
3109 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
3116 {.depositor = owner,
3118 .amount = asset(Number(1000 + 37 * 5, -1))}));
3122 env.balance(owner, asset) ==
3123 startingOwnerBalance.value());
3125 env.balance(vaultAccount(keylet), asset) ==
3127 auto const vault = env.le(keylet);
3128 BEAST_EXPECT(vault->at(sfAssetsAvailable) == beast::zero);
3129 BEAST_EXPECT(vault->at(sfAssetsTotal) == beast::zero);
3132 env(vault.del({.owner = owner, .id = keylet.key}));
3135 {.initialIOU =
Number(11875, -2)});
3138 Env env{*
this, testable_amendments()};
3140 env.current()->fees().accountReserve(0).drops() /
3142 env.current()->fees().increment.drops() /
3149 Account
const& owner,
3150 Account
const& issuer,
3151 Account
const& charlie,
3156 testcase(
"IOU no trust line to depositor no reserve");
3158 vault.create({.owner = owner, .asset = asset});
3164 env.trust(asset(0), owner);
3168 {.depositor = owner,
3170 .amount = asset(200)}));
3175 BEAST_EXPECT(trustline ==
nullptr);
3177 env(ticket::create(owner, 1));
3181 tx = vault.withdraw(
3182 {.depositor = owner,
3184 .amount = asset(10)});
3188 env(pay(charlie, owner, XRP(incReserve)));
3195 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
3200 Account
const& owner,
3201 Account
const& issuer,
3202 Account
const& charlie,
3207 testcase(
"IOU no reserve for share MPToken");
3209 vault.create({.owner = owner, .asset = asset});
3213 env(pay(owner, charlie, asset(100)));
3216 env(ticket::create(charlie, 3));
3221 {.depositor = charlie,
3223 .amount = asset(100)});
3227 env(pay(issuer, charlie, XRP(incReserve)));
3234 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
3238 Account
const& owner,
3239 Account
const& issuer,
3240 Account
const& charlie,
3245 testcase(
"IOU frozen trust line to 3rd party");
3247 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3252 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3256 auto const withdrawToCharlie = [&](
xrpl::Keylet keylet) {
3257 auto tx = vault.withdraw(
3258 {.depositor = owner,
3260 .amount = asset(10)});
3261 tx[sfDestination] = charlie.human();
3264 env(withdrawToCharlie);
3267 env(trust(issuer, asset(0), charlie,
tfSetFreeze));
3271 auto const withdraw = vault.withdraw(
3272 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
3284 .amount = asset(0)}));
3287 env(vault.del({.owner = owner, .id = keylet.key}));
3293 Account
const& owner,
3294 Account
const& issuer,
3295 Account
const& charlie,
3302 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3307 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3315 auto tx = vault.withdraw(
3316 {.depositor = owner,
3318 .amount = asset(10)});
3322 tx[sfDestination] = charlie.human();
3328 {.depositor = owner,
3330 .amount = asset(10)});
3340 .amount = asset(0)}));
3343 env(vault.del({.owner = owner, .id = keylet.key}));
3351 using namespace test::jtx;
3355 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3356 Account issuer{
"issuer"};
3357 Account owner{
"owner"};
3358 Account depositor{
"depositor"};
3359 Account charlie{
"charlie"};
3360 Account pdOwner{
"pdOwner"};
3361 Account credIssuer1{
"credIssuer1"};
3362 Account credIssuer2{
"credIssuer2"};
3380 env.trust(asset(1000), owner);
3381 env(pay(issuer, owner, asset(500)));
3382 env.trust(asset(1000), depositor);
3383 env(pay(issuer, depositor, asset(500)));
3384 env.trust(asset(1000), charlie);
3385 env(pay(issuer, charlie, asset(5)));
3388 auto [tx, keylet] = vault.create(
3392 BEAST_EXPECT(env.le(keylet));
3395 testcase(
"private vault owner can deposit");
3396 auto tx = vault.deposit(
3397 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
3402 testcase(
"private vault depositor not authorized yet");
3403 auto tx = vault.deposit(
3404 {.depositor = depositor,
3406 .amount = asset(50)});
3411 testcase(
"private vault cannot set non-existing domain");
3412 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3418 testcase(
"private vault set domainId");
3421 pdomain::Credentials
const credentials1{
3422 {.issuer = credIssuer1, .credType = credType}};
3424 env(pdomain::setTx(pdOwner, credentials1));
3425 auto const domainId1 = [&]() {
3427 return pdomain::getNewDomain(env.meta());
3430 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3441 pdomain::Credentials
const credentials{
3442 {.issuer = credIssuer1, .credType = credType},
3443 {.issuer = credIssuer2, .credType = credType}};
3445 env(pdomain::setTx(pdOwner, credentials));
3446 auto const domainId = [&]() {
3448 return pdomain::getNewDomain(env.meta());
3451 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3457 tx = vault.set({.owner = owner, .id = keylet.
key});
3465 testcase(
"private vault depositor still not authorized");
3466 auto tx = vault.deposit(
3467 {.depositor = depositor,
3469 .amount = asset(50)});
3474 auto const credKeylet =
3475 credentials::keylet(depositor, credIssuer1, credType);
3477 testcase(
"private vault depositor now authorized");
3478 env(credentials::create(depositor, credIssuer1, credType));
3479 env(credentials::accept(depositor, credIssuer1, credType));
3480 env(credentials::create(charlie, credIssuer1, credType));
3483 auto credSle = env.le(credKeylet);
3484 BEAST_EXPECT(credSle !=
nullptr);
3486 auto tx = vault.deposit(
3487 {.depositor = depositor,
3489 .amount = asset(50)});
3494 {.depositor = charlie, .id = keylet.
key, .amount = asset(50)});
3500 testcase(
"private vault depositor lost authorization");
3501 env(credentials::deleteCred(
3502 credIssuer1, depositor, credIssuer1, credType));
3503 env(credentials::deleteCred(
3504 credIssuer1, charlie, credIssuer1, credType));
3506 auto credSle = env.le(credKeylet);
3507 BEAST_EXPECT(credSle ==
nullptr);
3509 auto tx = vault.deposit(
3510 {.depositor = depositor,
3512 .amount = asset(50)});
3517 auto const shares = [&env, keylet = keylet,
this]() ->
Asset {
3518 auto const vault = env.le(keylet);
3519 BEAST_EXPECT(vault !=
nullptr);
3520 return MPTIssue(vault->at(sfShareMPTID));
3524 testcase(
"private vault expired authorization");
3525 uint32_t
const closeTime = env.current()
3527 .parentCloseTime.time_since_epoch()
3531 credentials::create(depositor, credIssuer2, credType);
3532 tx0[sfExpiration] = closeTime + 20;
3534 tx0 = credentials::create(charlie, credIssuer2, credType);
3535 tx0[sfExpiration] = closeTime + 20;
3539 env(credentials::accept(depositor, credIssuer2, credType));
3540 env(credentials::accept(charlie, credIssuer2, credType));
3545 auto tx1 = vault.deposit(
3546 {.depositor = depositor,
3548 .amount = asset(50)});
3554 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
3563 auto const credsKeylet =
3564 credentials::keylet(depositor, credIssuer2, credType);
3565 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3567 auto tx2 = vault.deposit(
3568 {.depositor = depositor,
3570 .amount = asset(1)});
3574 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3578 auto const credsKeylet =
3579 credentials::keylet(charlie, credIssuer2, credType);
3580 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3583 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3585 auto tx3 = vault.deposit(
3586 {.depositor = charlie,
3588 .amount = asset(2)});
3592 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3593 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3598 testcase(
"private vault reset domainId");
3599 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3600 tx[sfDomainID] =
"0";
3605 {.depositor = depositor,
3607 .amount = asset(50)});
3611 tx = vault.withdraw(
3612 {.depositor = depositor,
3614 .amount = asset(50)});
3618 tx = vault.clawback(
3621 .holder = depositor,
3622 .amount = asset(0)});
3625 tx = vault.clawback(
3629 .amount = asset(0)});
3778 using namespace test::jtx;
3782 Account
const& owner;
3783 Account
const& issuer;
3784 Account
const& depositor;
3785 Account
const& vaultAccount;
3795 auto testCase = [&,
this](
3798 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3799 Account
const owner{
"owner"};
3800 Account
const issuer{
"issuer"};
3801 Account
const depositor{
"depositor"};
3803 env.fund(XRP(1000), issuer, owner, depositor);
3808 env.trust(asset(1000), owner);
3809 env.trust(asset(1000), depositor);
3810 env(pay(issuer, owner, asset(200)));
3811 env(pay(issuer, depositor, asset(200)));
3814 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3815 tx[sfScale] = scale;
3818 auto const [vaultAccount, issuanceId] =
3820 auto const vault = env.le(keylet);
3822 Account(
"vault", vault->at(sfAccount)),
3823 vault->at(sfShareMPTID)};
3826 env.memoize(vaultAccount);
3830 return env.app().openLedger().modify(
3834 if (!BEAST_EXPECT(vault !=
nullptr))
3836 auto shares = sb.
peek(
3838 if (!BEAST_EXPECT(shares !=
nullptr))
3840 if (fn(*vault, *shares))
3855 .depositor = depositor,
3856 .vaultAccount = vaultAccount,
3866 testCase(18, [&,
this](Env& env, Data d) {
3867 testcase(
"Scale deposit overflow on first deposit");
3868 auto tx = d.vault.deposit(
3869 {.depositor = d.depositor,
3871 .amount = d.asset(10)});
3876 testCase(18, [&,
this](Env& env, Data d) {
3877 testcase(
"Scale deposit overflow on second deposit");
3880 auto tx = d.vault.deposit(
3881 {.depositor = d.depositor,
3883 .amount = d.asset(5)});
3889 auto tx = d.vault.deposit(
3890 {.depositor = d.depositor,
3892 .amount = d.asset(10)});
3898 testCase(18, [&,
this](Env& env, Data d) {
3899 testcase(
"Scale deposit overflow on total shares");
3902 auto tx = d.vault.deposit(
3903 {.depositor = d.depositor,
3905 .amount = d.asset(5)});
3911 auto tx = d.vault.deposit(
3912 {.depositor = d.depositor,
3914 .amount = d.asset(5)});
3920 testCase(1, [&,
this](Env& env, Data d) {
3923 auto const start = env.balance(d.depositor, d.assets).number();
3924 auto tx = d.vault.deposit(
3925 {.depositor = d.depositor,
3927 .amount = d.asset(1)});
3930 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3932 env.balance(d.depositor, d.assets) ==
3936 testCase(1, [&,
this](Env& env, Data d) {
3937 testcase(
"Scale deposit insignificant amount");
3939 auto tx = d.vault.deposit(
3940 {.depositor = d.depositor,
3946 testCase(1, [&,
this](Env& env, Data d) {
3947 testcase(
"Scale deposit exact, using full precision");
3949 auto const start = env.balance(d.depositor, d.assets).number();
3950 auto tx = d.vault.deposit(
3951 {.depositor = d.depositor,
3956 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3958 env.balance(d.depositor, d.assets) ==
3962 testCase(1, [&,
this](Env& env, Data d) {
3963 testcase(
"Scale deposit exact, truncating from .5");
3965 auto const start = env.balance(d.depositor, d.assets).number();
3969 auto tx = d.vault.deposit(
3970 {.depositor = d.depositor,
3975 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3977 env.balance(d.depositor, d.assets) ==
3982 auto tx = d.vault.deposit(
3983 {.depositor = d.depositor,
3988 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3990 env.balance(d.depositor, d.assets) ==
3995 auto tx = d.vault.deposit(
3996 {.depositor = d.depositor,
4001 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
4003 env.balance(d.depositor, d.assets) ==
4008 testCase(1, [&,
this](Env& env, Data d) {
4009 testcase(
"Scale deposit exact, truncating from .01");
4011 auto const start = env.balance(d.depositor, d.assets).number();
4013 auto tx = d.vault.deposit(
4014 {.depositor = d.depositor,
4019 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
4021 env.balance(d.depositor, d.assets) ==
4026 auto tx = d.vault.deposit(
4027 {.depositor = d.depositor,
4032 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
4034 env.balance(d.depositor, d.assets) ==
4039 testCase(1, [&,
this](Env& env, Data d) {
4040 testcase(
"Scale deposit exact, truncating from .99");
4042 auto const start = env.balance(d.depositor, d.assets).number();
4044 auto tx = d.vault.deposit(
4045 {.depositor = d.depositor,
4050 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
4052 env.balance(d.depositor, d.assets) ==
4057 auto tx = d.vault.deposit(
4058 {.depositor = d.depositor,
4063 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
4065 env.balance(d.depositor, d.assets) ==
4070 testCase(1, [&,
this](Env& env, Data d) {
4072 auto const start = env.balance(d.depositor, d.assets).number();
4073 auto tx = d.vault.deposit(
4074 {.depositor = d.depositor,
4079 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4081 env.balance(d.depositor, d.assets) ==
4084 env.balance(d.vaultAccount, d.assets) ==
4087 env.balance(d.vaultAccount, d.shares) ==
4096 auto const start = env.balance(d.depositor, d.assets).number();
4097 auto tx = d.vault.withdraw(
4098 {.depositor = d.depositor,
4104 env.balance(d.depositor, d.shares) == d.share(900));
4106 env.balance(d.depositor, d.assets) ==
4109 env.balance(d.vaultAccount, d.assets) ==
4112 env.balance(d.vaultAccount, d.shares) ==
4117 testcase(
"Scale redeem with rounding");
4122 auto const start = env.balance(d.depositor, d.assets).number();
4123 d.peek([](
SLE& vault,
auto&) ->
bool {
4124 vault[sfAssetsAvailable] =
Number(1);
4132 auto tx = d.vault.withdraw(
4133 {.depositor = d.depositor,
4139 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4141 env.balance(d.depositor, d.assets) ==
4144 env.balance(d.vaultAccount, d.assets) ==
4147 env.balance(d.vaultAccount, d.shares) ==
4157 auto const start = env.balance(d.depositor, d.assets).number();
4159 tx = d.vault.withdraw(
4160 {.depositor = d.depositor,
4166 env.balance(d.depositor, d.shares) == d.share(875 - 21));
4168 env.balance(d.depositor, d.assets) ==
4171 env.balance(d.vaultAccount, d.assets) ==
4174 env.balance(d.vaultAccount, d.shares) ==
4180 auto const rest = env.balance(d.depositor, d.shares).number();
4182 tx = d.vault.withdraw(
4183 {.depositor = d.depositor,
4185 .amount =
STAmount(d.share, rest)});
4188 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4190 env.balance(d.vaultAccount, d.assets).number() == 0);
4192 env.balance(d.vaultAccount, d.shares).number() == 0);
4196 testCase(18, [&,
this](Env& env, Data d) {
4197 testcase(
"Scale withdraw overflow");
4200 auto tx = d.vault.deposit(
4201 {.depositor = d.depositor,
4203 .amount = d.asset(5)});
4209 auto tx = d.vault.withdraw(
4210 {.depositor = d.depositor,
4218 testCase(1, [&,
this](Env& env, Data d) {
4220 auto const start = env.balance(d.depositor, d.assets).number();
4221 auto tx = d.vault.deposit(
4222 {.depositor = d.depositor,
4227 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4229 env.balance(d.depositor, d.assets) ==
4232 env.balance(d.vaultAccount, d.assets) ==
4235 env.balance(d.vaultAccount, d.shares) ==
4247 auto const start = env.balance(d.depositor, d.assets).number();
4248 auto tx = d.vault.withdraw(
4249 {.depositor = d.depositor,
4255 env.balance(d.depositor, d.shares) == d.share(900));
4257 env.balance(d.depositor, d.assets) ==
4260 env.balance(d.vaultAccount, d.assets) ==
4263 env.balance(d.vaultAccount, d.shares) ==
4268 testcase(
"Scale withdraw insignificant amount");
4269 auto tx = d.vault.withdraw(
4270 {.depositor = d.depositor,
4277 testcase(
"Scale withdraw with rounding assets");
4285 auto const start = env.balance(d.depositor, d.assets).number();
4286 d.peek([](
SLE& vault,
auto&) ->
bool {
4287 vault[sfAssetsAvailable] =
Number(1);
4295 auto tx = d.vault.withdraw(
4296 {.depositor = d.depositor,
4302 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4304 env.balance(d.depositor, d.assets) ==
4307 env.balance(d.vaultAccount, d.assets) ==
4310 env.balance(d.vaultAccount, d.shares) ==
4315 testcase(
"Scale withdraw with rounding shares up");
4323 auto const start = env.balance(d.depositor, d.assets).number();
4324 auto tx = d.vault.withdraw(
4325 {.depositor = d.depositor,
4331 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4333 env.balance(d.depositor, d.assets) ==
4336 env.balance(d.vaultAccount, d.assets) ==
4339 env.balance(d.vaultAccount, d.shares) ==
4344 testcase(
"Scale withdraw with rounding shares down");
4352 auto const start = env.balance(d.depositor, d.assets).number();
4353 auto tx = d.vault.withdraw(
4354 {.depositor = d.depositor,
4360 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4362 env.balance(d.depositor, d.assets) ==
4365 env.balance(d.vaultAccount, d.assets) ==
4368 env.balance(d.vaultAccount, d.shares) ==
4373 testcase(
"Scale withdraw tiny amount");
4375 auto const start = env.balance(d.depositor, d.assets).number();
4376 auto tx = d.vault.withdraw(
4377 {.depositor = d.depositor,
4383 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4385 env.balance(d.depositor, d.assets) ==
4388 env.balance(d.vaultAccount, d.assets) ==
4391 env.balance(d.vaultAccount, d.shares) ==
4398 env.balance(d.vaultAccount, d.assets).number();
4400 tx = d.vault.withdraw(
4401 {.depositor = d.depositor,
4403 .amount =
STAmount(d.asset, rest)});
4406 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4408 env.balance(d.vaultAccount, d.assets).number() == 0);
4410 env.balance(d.vaultAccount, d.shares).number() == 0);
4414 testCase(18, [&,
this](Env& env, Data d) {
4415 testcase(
"Scale clawback overflow");
4418 auto tx = d.vault.deposit(
4419 {.depositor = d.depositor,
4421 .amount = d.asset(5)});
4427 auto tx = d.vault.clawback(
4428 {.issuer = d.issuer,
4430 .holder = d.depositor,
4437 testCase(1, [&,
this](Env& env, Data d) {
4439 auto const start = env.balance(d.depositor, d.assets).number();
4440 auto tx = d.vault.deposit(
4441 {.depositor = d.depositor,
4446 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4448 env.balance(d.depositor, d.assets) ==
4451 env.balance(d.vaultAccount, d.assets) ==
4454 env.balance(d.vaultAccount, d.shares) ==
4465 auto const start = env.balance(d.depositor, d.assets).number();
4466 auto tx = d.vault.clawback(
4467 {.issuer = d.issuer,
4469 .holder = d.depositor,
4474 env.balance(d.depositor, d.shares) == d.share(900));
4476 env.balance(d.depositor, d.assets) ==
4479 env.balance(d.vaultAccount, d.assets) ==
4482 env.balance(d.vaultAccount, d.shares) ==
4487 testcase(
"Scale clawback insignificant amount");
4488 auto tx = d.vault.clawback(
4489 {.issuer = d.issuer,
4491 .holder = d.depositor,
4497 testcase(
"Scale clawback with rounding assets");
4505 auto const start = env.balance(d.depositor, d.assets).number();
4506 auto tx = d.vault.clawback(
4507 {.issuer = d.issuer,
4509 .holder = d.depositor,
4514 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4516 env.balance(d.depositor, d.assets) ==
4519 env.balance(d.vaultAccount, d.assets) ==
4522 env.balance(d.vaultAccount, d.shares) ==
4527 testcase(
"Scale clawback with rounding shares up");
4535 auto const start = env.balance(d.depositor, d.assets).number();
4536 auto tx = d.vault.clawback(
4537 {.issuer = d.issuer,
4539 .holder = d.depositor,
4544 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4546 env.balance(d.depositor, d.assets) ==
4549 env.balance(d.vaultAccount, d.assets) ==
4552 env.balance(d.vaultAccount, d.shares) ==
4557 testcase(
"Scale clawback with rounding shares down");
4565 auto const start = env.balance(d.depositor, d.assets).number();
4566 auto tx = d.vault.clawback(
4567 {.issuer = d.issuer,
4569 .holder = d.depositor,
4574 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4576 env.balance(d.depositor, d.assets) ==
4579 env.balance(d.vaultAccount, d.assets) ==
4582 env.balance(d.vaultAccount, d.shares) ==
4587 testcase(
"Scale clawback tiny amount");
4589 auto const start = env.balance(d.depositor, d.assets).number();
4590 auto tx = d.vault.clawback(
4591 {.issuer = d.issuer,
4593 .holder = d.depositor,
4598 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4600 env.balance(d.depositor, d.assets) ==
4603 env.balance(d.vaultAccount, d.assets) ==
4606 env.balance(d.vaultAccount, d.shares) ==
4613 env.balance(d.vaultAccount, d.assets).number();
4614 d.peek([](
SLE& vault,
auto&) ->
bool {
4615 vault[sfAssetsAvailable] =
Number(5);
4623 tx = d.vault.clawback(
4624 {.issuer = d.issuer,
4626 .holder = d.depositor,
4627 .amount =
STAmount(d.asset, rest)});
4630 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4632 env.balance(d.vaultAccount, d.assets).number() == 0);
4634 env.balance(d.vaultAccount, d.shares).number() == 0);
4642 using namespace test::jtx;
4645 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4646 Account
const owner{
"owner"};
4647 Account
const issuer{
"issuer"};
4649 env.fund(XRP(1000), issuer, owner);
4653 env.trust(asset(1000), owner);
4654 env(pay(issuer, owner, asset(200)));
4657 auto const sequence = env.seq(owner);
4658 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
4664 auto tx1 = vault.deposit(
4665 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
4668 auto tx2 = vault.set({.owner = owner, .id = keylet.
key});
4669 tx2[sfAssetsMaximum] = asset(1000).number();
4674 auto const sleVault = [&env, keylet = keylet,
this]() {
4675 auto const vault = env.le(keylet);
4676 BEAST_EXPECT(vault !=
nullptr);
4680 auto const check = [&, keylet = keylet, sle = sleVault,
this](
4683 BEAST_EXPECT(vault.isObject());
4685 constexpr auto checkString =
4687 return node.isMember(field.fieldName) &&
4688 node[field.fieldName].isString() &&
4689 node[field.fieldName] == v;
4691 constexpr auto checkObject =
4693 return node.isMember(field.fieldName) &&
4694 node[field.fieldName].isObject() &&
4695 node[field.fieldName] == v;
4697 constexpr auto checkInt =
4698 [](
auto& node,
SField const& field,
int v) ->
bool {
4699 return node.isMember(field.fieldName) &&
4700 ((node[field.fieldName].isInt() &&
4701 node[field.fieldName] ==
Json::Int(v)) ||
4702 (node[field.fieldName].isUInt() &&
4706 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
4707 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(keylet.
key));
4708 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
4712 checkString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
4714 checkObject(vault, sfAsset,
to_json(sle->at(sfAsset))));
4715 BEAST_EXPECT(checkString(vault, sfAssetsAvailable,
"50"));
4716 BEAST_EXPECT(checkString(vault, sfAssetsMaximum,
"1000"));
4717 BEAST_EXPECT(checkString(vault, sfAssetsTotal,
"50"));
4718 BEAST_EXPECT(!vault.isMember(sfLossUnrealized.getJsonName()));
4720 auto const strShareID =
strHex(sle->at(sfShareMPTID));
4721 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
4722 BEAST_EXPECT(checkString(vault, sfOwner,
toBase58(owner.id())));
4723 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
4724 BEAST_EXPECT(checkInt(
4727 if (issuance.isObject())
4730 issuance[
"LedgerEntryType"].asString() ==
4733 issuance[jss::mpt_issuance_id].asString() == strShareID);
4734 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
4735 BEAST_EXPECT(checkInt(
4740 checkString(issuance, sfOutstandingAmount,
"50000000"));
4745 testcase(
"RPC ledger_entry selected by key");
4747 jvParams[jss::ledger_index] = jss::validated;
4748 jvParams[jss::vault] =
strHex(keylet.
key);
4749 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4751 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4752 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4753 check(jvVault[jss::result][jss::node]);
4757 testcase(
"RPC ledger_entry selected by owner and seq");
4759 jvParams[jss::ledger_index] = jss::validated;
4760 jvParams[jss::vault][jss::owner] = owner.human();
4761 jvParams[jss::vault][jss::seq] = sequence;
4762 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4764 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4765 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4766 check(jvVault[jss::result][jss::node]);
4770 testcase(
"RPC ledger_entry cannot find vault by key");
4772 jvParams[jss::ledger_index] = jss::validated;
4774 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4776 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4780 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
4782 jvParams[jss::ledger_index] = jss::validated;
4783 jvParams[jss::vault][jss::owner] = issuer.human();
4784 jvParams[jss::vault][jss::seq] = 1'000'000;
4785 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4787 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4791 testcase(
"RPC ledger_entry malformed key");
4793 jvParams[jss::ledger_index] = jss::validated;
4794 jvParams[jss::vault] = 42;
4795 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4797 jvVault[jss::result][jss::error].asString() ==
4798 "malformedRequest");
4802 testcase(
"RPC ledger_entry malformed owner");
4804 jvParams[jss::ledger_index] = jss::validated;
4805 jvParams[jss::vault][jss::owner] = 42;
4806 jvParams[jss::vault][jss::seq] = sequence;
4807 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4809 jvVault[jss::result][jss::error].asString() ==
4814 testcase(
"RPC ledger_entry malformed seq");
4816 jvParams[jss::ledger_index] = jss::validated;
4817 jvParams[jss::vault][jss::owner] = issuer.human();
4818 jvParams[jss::vault][jss::seq] =
"foo";
4819 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4821 jvVault[jss::result][jss::error].asString() ==
4822 "malformedRequest");
4826 testcase(
"RPC ledger_entry negative seq");
4828 jvParams[jss::ledger_index] = jss::validated;
4829 jvParams[jss::vault][jss::owner] = issuer.human();
4830 jvParams[jss::vault][jss::seq] = -1;
4831 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4833 jvVault[jss::result][jss::error].asString() ==
4834 "malformedRequest");
4838 testcase(
"RPC ledger_entry oversized seq");
4840 jvParams[jss::ledger_index] = jss::validated;
4841 jvParams[jss::vault][jss::owner] = issuer.human();
4842 jvParams[jss::vault][jss::seq] = 1e20;
4843 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4845 jvVault[jss::result][jss::error].asString() ==
4846 "malformedRequest");
4850 testcase(
"RPC ledger_entry bool seq");
4852 jvParams[jss::ledger_index] = jss::validated;
4853 jvParams[jss::vault][jss::owner] = issuer.human();
4854 jvParams[jss::vault][jss::seq] =
true;
4855 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4857 jvVault[jss::result][jss::error].asString() ==
4858 "malformedRequest");
4865 jvParams[jss::account] = owner.human();
4866 jvParams[jss::type] = jss::vault;
4868 "json",
"account_objects",
to_string(jvParams))[jss::result];
4870 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
4871 check(jv[jss::account_objects][0u]);
4878 jvParams[jss::ledger_index] = jss::validated;
4879 jvParams[jss::binary] =
false;
4880 jvParams[jss::type] = jss::vault;
4882 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
4883 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
4884 check(jv[jss::result][jss::state][0u]);
4888 testcase(
"RPC vault_info command line");
4890 env.rpc(
"vault_info",
strHex(keylet.
key),
"validated");
4892 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4893 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4895 jv[jss::result][jss::vault],
4896 jv[jss::result][jss::vault][jss::shares]);
4902 jvParams[jss::ledger_index] = jss::validated;
4903 jvParams[jss::vault_id] =
strHex(keylet.
key);
4904 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4906 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4907 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4909 jv[jss::result][jss::vault],
4910 jv[jss::result][jss::vault][jss::shares]);
4914 testcase(
"RPC vault_info invalid vault_id");
4916 jvParams[jss::ledger_index] = jss::validated;
4917 jvParams[jss::vault_id] =
"foobar";
4918 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4920 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4924 testcase(
"RPC vault_info json invalid index");
4926 jvParams[jss::ledger_index] = jss::validated;
4927 jvParams[jss::vault_id] = 0;
4928 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4930 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4934 testcase(
"RPC vault_info json by owner and sequence");
4936 jvParams[jss::ledger_index] = jss::validated;
4937 jvParams[jss::owner] = owner.human();
4938 jvParams[jss::seq] = sequence;
4939 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4941 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4942 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4944 jv[jss::result][jss::vault],
4945 jv[jss::result][jss::vault][jss::shares]);
4949 testcase(
"RPC vault_info json malformed sequence");
4951 jvParams[jss::ledger_index] = jss::validated;
4952 jvParams[jss::owner] = owner.human();
4953 jvParams[jss::seq] =
"foobar";
4954 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4956 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4960 testcase(
"RPC vault_info json invalid sequence");
4962 jvParams[jss::ledger_index] = jss::validated;
4963 jvParams[jss::owner] = owner.human();
4964 jvParams[jss::seq] = 0;
4965 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4967 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4971 testcase(
"RPC vault_info json negative sequence");
4973 jvParams[jss::ledger_index] = jss::validated;
4974 jvParams[jss::owner] = owner.human();
4975 jvParams[jss::seq] = -1;
4976 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4978 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4982 testcase(
"RPC vault_info json oversized sequence");
4984 jvParams[jss::ledger_index] = jss::validated;
4985 jvParams[jss::owner] = owner.human();
4986 jvParams[jss::seq] = 1e20;
4987 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4989 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4993 testcase(
"RPC vault_info json bool sequence");
4995 jvParams[jss::ledger_index] = jss::validated;
4996 jvParams[jss::owner] = owner.human();
4997 jvParams[jss::seq] =
true;
4998 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5000 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5004 testcase(
"RPC vault_info json malformed owner");
5006 jvParams[jss::ledger_index] = jss::validated;
5007 jvParams[jss::owner] =
"foobar";
5008 jvParams[jss::seq] = sequence;
5009 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5011 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5015 testcase(
"RPC vault_info json invalid combination only owner");
5017 jvParams[jss::ledger_index] = jss::validated;
5018 jvParams[jss::owner] = owner.human();
5019 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5021 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5025 testcase(
"RPC vault_info json invalid combination only seq");
5027 jvParams[jss::ledger_index] = jss::validated;
5028 jvParams[jss::seq] = sequence;
5029 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5031 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5035 testcase(
"RPC vault_info json invalid combination seq vault_id");
5037 jvParams[jss::ledger_index] = jss::validated;
5038 jvParams[jss::vault_id] =
strHex(keylet.
key);
5039 jvParams[jss::seq] = sequence;
5040 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5042 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5046 testcase(
"RPC vault_info json invalid combination owner vault_id");
5048 jvParams[jss::ledger_index] = jss::validated;
5049 jvParams[jss::vault_id] =
strHex(keylet.
key);
5050 jvParams[jss::owner] = owner.human();
5051 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5053 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5058 "RPC vault_info json invalid combination owner seq "
5061 jvParams[jss::ledger_index] = jss::validated;
5062 jvParams[jss::vault_id] =
strHex(keylet.
key);
5063 jvParams[jss::seq] = sequence;
5064 jvParams[jss::owner] = owner.human();
5065 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5067 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5071 testcase(
"RPC vault_info json no input");
5073 jvParams[jss::ledger_index] = jss::validated;
5074 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5076 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5080 testcase(
"RPC vault_info command line invalid index");
5081 Json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
5082 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
5086 testcase(
"RPC vault_info command line invalid index");
5087 Json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
5089 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5093 testcase(
"RPC vault_info command line invalid index");
5097 jv[jss::result][jss::error].asString() ==
"entryNotFound");
5101 testcase(
"RPC vault_info command line invalid ledger");
5104 jv[jss::result][jss::error].asString() ==
"lgrNotFound");
5111 using namespace test::jtx;
5113 Env env(*
this, testable_amendments());
5114 Account alice{
"alice"};
5116 Account carol{
"carol"};
5123 auto const xrpBalance =
5127 if (BEAST_EXPECT(sle !=
nullptr))
5128 return sle->getFieldAmount(sfBalance).xrp().drops();
5132 auto testCase = [&,
this](
auto test, CaseArgs args = {}) {
5133 Env env{*
this, testable_amendments() | featureSingleAssetVault};
5138 env.fund(XRP(10000), alice);
5139 env.fund(XRP(20000), bob);
5140 env.fund(XRP(30000), carol);
5154 test(env, vault, args.asset);
5157 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
5158 testcase(
"delegated vault creation");
5159 auto startBalance = xrpBalance(env, carol);
5160 if (!BEAST_EXPECT(startBalance.has_value()))
5163 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
5164 env(tx, delegate::as(alice));
5166 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
5169 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
5170 testcase(
"delegated deposit and withdrawal");
5171 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
5175 auto const amount = 1513;
5176 auto const baseFee = env.current()->fees().base;
5178 auto startBalance = xrpBalance(env, carol);
5179 if (!BEAST_EXPECT(startBalance.has_value()))
5183 {.depositor = carol,
5185 .amount = asset(amount)});
5186 env(tx, delegate::as(alice));
5188 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
5190 tx = vault.withdraw(
5191 {.depositor = carol,
5193 .amount = asset(amount - 1)});
5194 env(tx, delegate::as(alice));
5196 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
5198 tx = vault.withdraw(
5199 {.depositor = carol, .id = keylet.
key, .amount = asset(1)});
5202 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
5205 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
5206 testcase(
"delegated withdrawal same as base fee and deletion");
5207 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
5211 auto const amount = 25537;
5212 auto const baseFee = env.current()->fees().base;
5214 auto startBalance = xrpBalance(env, carol);
5215 if (!BEAST_EXPECT(startBalance.has_value()))
5219 {.depositor = carol,
5221 .amount = asset(amount)});
5225 xrpBalance(env, carol) == *startBalance - amount - baseFee);
5227 tx = vault.withdraw(
5228 {.depositor = carol,
5230 .amount = asset(baseFee)});
5231 env(tx, delegate::as(alice));
5233 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
5235 tx = vault.withdraw(
5236 {.depositor = carol,
5238 .amount = asset(amount - baseFee)});
5239 env(tx, delegate::as(alice));
5241 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
5243 tx = vault.del({.owner = carol, .id = keylet.
key});
5244 env(tx, delegate::as(alice));
5252 using namespace test::jtx;
5253 using namespace loanBroker;
5254 using namespace loan;
5257 auto const vaultAssetBalance = [&](
Keylet const& vaultKeylet) {
5258 auto const sleVault = env.le(vaultKeylet);
5259 BEAST_EXPECT(sleVault !=
nullptr);
5262 sleVault->at(sfAssetsAvailable), sleVault->at(sfAssetsTotal));
5265 auto const vaultShareBalance = [&](
Keylet const& vaultKeylet) {
5266 auto const sleVault = env.le(vaultKeylet);
5267 BEAST_EXPECT(sleVault !=
nullptr);
5269 auto const sleIssuance =
5271 BEAST_EXPECT(sleIssuance !=
nullptr);
5273 return sleIssuance->at(sfOutstandingAmount);
5276 auto const setupVault =
5278 Account
const& owner,
5282 auto const& [tx, vaultKeylet] =
5283 vault.create({.owner = owner, .asset = asset});
5287 auto const& vaultSle = env.le(vaultKeylet);
5288 BEAST_EXPECT(vaultSle !=
nullptr);
5290 Asset share = vaultSle->at(sfShareMPTID);
5293 {.depositor = depositor,
5294 .id = vaultKeylet.key,
5295 .amount = asset(100)}),
5300 auto const& [availablePreDefault, totalPreDefault] =
5301 vaultAssetBalance(vaultKeylet);
5302 BEAST_EXPECT(availablePreDefault == totalPreDefault);
5303 BEAST_EXPECT(availablePreDefault == asset(100).value());
5308 .id = vaultKeylet.key,
5309 .holder = depositor,
5310 .amount = share(0).value()}),
5315 auto const& sharesAvailable = vaultShareBalance(vaultKeylet);
5316 auto const& brokerKeylet =
5319 env(
set(owner, vaultKeylet.key), THISLINE);
5322 auto const& loanKeylet =
keylet::loan(brokerKeylet.key, 1);
5325 env(
set(depositor, brokerKeylet.key, asset(100).value()),
5328 paymentInterval(120),
5330 sig(sfCounterpartySignature, owner),
5331 fee(env.current()->fees().base * 2),
5340 .id = vaultKeylet.key,
5341 .holder = depositor,
5342 .amount = share(0).value()}),
5353 auto const& [availablePostDefault, totalPostDefault] =
5354 vaultAssetBalance(vaultKeylet);
5356 BEAST_EXPECT(availablePostDefault == totalPostDefault);
5357 BEAST_EXPECT(availablePostDefault == asset(0).value());
5358 BEAST_EXPECT(vaultShareBalance(vaultKeylet) == sharesAvailable);
5363 auto const testCase = [&](
PrettyAsset const& asset,
5365 Account
const& owner,
5366 Account
const& depositor) {
5369 "VaultClawback (share) - " + prefix +
5370 " owner asset clawback fails");
5371 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
5372 env(vault.clawback({
5374 .id = vaultKeylet.key,
5375 .holder = depositor,
5376 .amount = asset(100).value(),
5391 "VaultClawback (share) - " + prefix +
5392 " owner incomplete share clawback fails");
5393 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
5394 auto const& vaultSle = env.le(vaultKeylet);
5395 BEAST_EXPECT(vaultSle !=
nullptr);
5398 Asset share = vaultSle->at(sfShareMPTID);
5399 env(vault.clawback({
5401 .id = vaultKeylet.key,
5402 .holder = depositor,
5403 .amount = share(1).value(),
5412 "VaultClawback (share) - " + prefix +
5413 " owner implicit complete share clawback");
5414 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
5415 env(vault.clawback({
5417 .id = vaultKeylet.key,
5418 .holder = depositor,
5430 "VaultClawback (share) - " + prefix +
5431 " owner explicit complete share clawback succeeds");
5432 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
5433 auto const& vaultSle = env.le(vaultKeylet);
5434 BEAST_EXPECT(vaultSle !=
nullptr);
5437 Asset share = vaultSle->at(sfShareMPTID);
5438 env(vault.clawback({
5440 .id = vaultKeylet.key,
5441 .holder = depositor,
5442 .amount = share(vaultShareBalance(vaultKeylet)).value(),
5450 "VaultClawback (share) - " + prefix +
5451 " owner can clawback own shares");
5452 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
5453 auto const& vaultSle = env.le(vaultKeylet);
5454 BEAST_EXPECT(vaultSle !=
nullptr);
5457 Asset share = vaultSle->at(sfShareMPTID);
5458 env(vault.clawback({
5460 .id = vaultKeylet.key,
5462 .amount = share(vaultShareBalance(vaultKeylet)).value(),
5471 "VaultClawback (share) - " + prefix +
5472 " empty vault share clawback fails");
5473 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
5474 auto const& vaultSle = env.le(vaultKeylet);
5475 if (BEAST_EXPECT(vaultSle !=
nullptr))
5477 Asset share = vaultSle->at(sfShareMPTID);
5478 env(vault.clawback({
5480 .id = vaultKeylet.key,
5482 .amount = share(vaultShareBalance(vaultKeylet)).value(),
5488 env(vault.clawback({
5490 .id = vaultKeylet.key,
5499 Account owner{
"alice"};
5500 Account depositor{
"bob"};
5501 Account issuer{
"issuer"};
5503 env.fund(XRP(10000), issuer, owner, depositor);
5508 testCase(xrp,
"XRP", owner, depositor);
5509 testCase(xrp,
"XRP (depositor is owner)", owner, owner);
5516 env.trust(IOU(1000), owner);
5517 env.trust(IOU(1000), depositor);
5518 env(pay(issuer, owner, IOU(100)));
5519 env(pay(issuer, depositor, IOU(100)));
5521 testCase(IOU,
"IOU", owner, depositor);
5522 testCase(IOU,
"IOU (owner is issuer)", issuer, depositor);
5525 MPTTester mptt{env, issuer, mptInitNoFund};
5529 mptt.authorize({.account = owner});
5530 mptt.authorize({.account = depositor});
5531 env(pay(issuer, owner, MPT(1000)));
5532 env(pay(issuer, depositor, MPT(1000)));
5534 testCase(MPT,
"MPT", owner, depositor);
5535 testCase(MPT,
"MPT (owner is issuer)", issuer, depositor);
5541 using namespace test::jtx;
5542 using namespace loanBroker;
5543 using namespace loan;
5546 auto const setupVault =
5548 Account
const& owner,
5549 Account
const& depositor,
5553 auto const& [tx, vaultKeylet] =
5554 vault.create({.owner = owner, .asset = asset});
5558 auto const& vaultSle = env.le(vaultKeylet);
5559 BEAST_EXPECT(vaultSle !=
nullptr);
5561 {.depositor = depositor,
5562 .id = vaultKeylet.key,
5563 .amount = asset(100)}),
5571 auto const testCase = [&](
PrettyAsset const& asset,
5573 Account
const& owner,
5574 Account
const& depositor,
5575 Account
const& issuer) {
5579 "VaultClawback (asset) - " + prefix +
5580 " issuer XRP clawback fails");
5581 auto [vault, vaultKeylet] =
5582 setupVault(asset, owner, depositor, issuer);
5585 env(vault.clawback({
5587 .id = vaultKeylet.key,
5589 .amount = asset(1).value(),
5594 env(vault.clawback({
5596 .id = vaultKeylet.key,
5606 "VaultClawback (asset) - " + prefix +
5607 " clawback for different asset fails");
5608 auto [vault, vaultKeylet] =
5609 setupVault(asset, owner, depositor, issuer);
5611 Account issuer2{
"issuer2"};
5613 env(vault.clawback({
5615 .id = vaultKeylet.key,
5616 .holder = depositor,
5617 .amount = asset2(1).value(),
5625 "VaultClawback (asset) - " + prefix +
5626 " ambiguous owner/issuer asset clawback fails");
5627 auto [vault, vaultKeylet] =
5628 setupVault(asset, issuer, depositor, issuer);
5629 env(vault.clawback({
5631 .id = vaultKeylet.key,
5640 "VaultClawback (asset) - " + prefix +
5641 " non-issuer asset clawback fails");
5642 auto [vault, vaultKeylet] =
5643 setupVault(asset, owner, depositor, issuer);
5645 env(vault.clawback({
5647 .id = vaultKeylet.key,
5648 .holder = depositor,
5653 env(vault.clawback({
5655 .id = vaultKeylet.key,
5656 .holder = depositor,
5657 .amount = asset(1).value(),
5665 "VaultClawback (asset) - " + prefix +
5666 " issuer clawback from self fails");
5667 auto [vault, vaultKeylet] =
5668 setupVault(asset, owner, issuer, issuer);
5669 env(vault.clawback({
5671 .id = vaultKeylet.key,
5680 "VaultClawback (asset) - " + prefix +
5681 " issuer share clawback fails");
5682 auto [vault, vaultKeylet] =
5683 setupVault(asset, owner, depositor, issuer);
5684 auto const& vaultSle = env.le(vaultKeylet);
5685 BEAST_EXPECT(vaultSle !=
nullptr);
5688 Asset share = vaultSle->at(sfShareMPTID);
5690 env(vault.clawback({
5692 .id = vaultKeylet.key,
5693 .holder = depositor,
5694 .amount = share(1).value(),
5702 "VaultClawback (asset) - " + prefix +
5703 " partial issuer asset clawback succeeds");
5704 auto [vault, vaultKeylet] =
5705 setupVault(asset, owner, depositor, issuer);
5707 env(vault.clawback({
5709 .id = vaultKeylet.key,
5710 .holder = depositor,
5711 .amount = asset(1).value(),
5719 "VaultClawback (asset) - " + prefix +
5720 " full issuer asset clawback succeeds");
5721 auto [vault, vaultKeylet] =
5722 setupVault(asset, owner, depositor, issuer);
5724 env(vault.clawback({
5726 .id = vaultKeylet.key,
5727 .holder = depositor,
5728 .amount = asset(100).value(),
5736 "VaultClawback (asset) - " + prefix +
5737 " implicit full issuer asset clawback succeeds");
5738 auto [vault, vaultKeylet] =
5739 setupVault(asset, owner, depositor, issuer);
5741 env(vault.clawback({
5743 .id = vaultKeylet.key,
5744 .holder = depositor,
5751 Account owner{
"alice"};
5752 Account depositor{
"bob"};
5753 Account issuer{
"issuer"};
5755 env.fund(XRP(10000), issuer, owner, depositor);
5760 testCase(xrp,
"XRP", owner, depositor, issuer);
5766 env.trust(IOU(1000), owner);
5767 env.trust(IOU(1000), depositor);
5768 env(pay(issuer, owner, IOU(1000)));
5769 env(pay(issuer, depositor, IOU(1000)));
5771 testCase(IOU,
"IOU", owner, depositor, issuer);
5774 MPTTester mptt{env, issuer, mptInitNoFund};
5778 mptt.authorize({.account = owner});
5779 mptt.authorize({.account = depositor});
5780 env(pay(issuer, depositor, MPT(1000)));
5782 testCase(MPT,
"MPT", owner, depositor, issuer);