45 using namespace test::jtx;
46 Account issuer{
"issuer"};
47 Account owner{
"owner"};
48 Account depositor{
"depositor"};
49 Account charlie{
"charlie"};
52 auto const testSequence = [&,
this](
57 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
58 tx[sfData] =
"AFEED00E";
59 tx[sfAssetsMaximum] = asset(100).number();
62 BEAST_EXPECT(env.le(keylet));
65 auto const [share, vaultAccount] =
70 auto const vault = env.le(keylet);
71 BEAST_EXPECT(vault !=
nullptr);
72 if (asset.raw().holds<
Issue>() && !asset.raw().
native())
73 BEAST_EXPECT(vault->at(sfScale) == 6);
75 BEAST_EXPECT(vault->at(sfScale) == 0);
78 BEAST_EXPECT(shares !=
nullptr);
79 if (asset.raw().holds<
Issue>() && !asset.raw().
native())
80 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
82 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
85 Account(
"vault", vault->at(sfAccount))};
87 auto const shares = share.raw().get<
MPTIssue>();
88 env.memoize(vaultAccount);
91 Account alice{
"alice"};
93 env.fund(XRP(1000), alice, erin);
98 testcase(prefix +
" fail to deposit more than assets held");
99 auto tx = vault.deposit(
100 {.depositor = depositor,
102 .amount = asset(10000)});
108 testcase(prefix +
" deposit non-zero amount");
109 auto tx = vault.deposit(
110 {.depositor = depositor,
112 .amount = asset(50)});
116 env.balance(depositor, shares) == share(50 * scale));
120 testcase(prefix +
" deposit non-zero amount again");
121 auto tx = vault.deposit(
122 {.depositor = depositor,
124 .amount = asset(50)});
128 env.balance(depositor, shares) == share(100 * scale));
132 testcase(prefix +
" fail to delete non-empty vault");
133 auto tx = vault.del({.owner = owner, .id = keylet.
key});
139 testcase(prefix +
" fail to update because wrong owner");
140 auto tx = vault.set({.owner = issuer, .id = keylet.
key});
141 tx[sfAssetsMaximum] = asset(50).number();
148 prefix +
" fail to set maximum lower than current amount");
149 auto tx = vault.set({.owner = owner, .id = keylet.
key});
150 tx[sfAssetsMaximum] = asset(50).number();
156 testcase(prefix +
" set maximum higher than current amount");
157 auto tx = vault.set({.owner = owner, .id = keylet.
key});
158 tx[sfAssetsMaximum] = asset(150).number();
164 testcase(prefix +
" set maximum is idempotent, set it again");
165 auto tx = vault.set({.owner = owner, .id = keylet.
key});
166 tx[sfAssetsMaximum] = asset(150).number();
173 auto tx = vault.set({.owner = owner, .id = keylet.
key});
180 testcase(prefix +
" fail to set domain on public vault");
181 auto tx = vault.set({.owner = owner, .id = keylet.
key});
188 testcase(prefix +
" fail to deposit more than maximum");
189 auto tx = vault.deposit(
190 {.depositor = depositor,
192 .amount = asset(100)});
198 testcase(prefix +
" reset maximum to zero i.e. not enforced");
199 auto tx = vault.set({.owner = owner, .id = keylet.
key});
200 tx[sfAssetsMaximum] = asset(0).number();
206 testcase(prefix +
" fail to withdraw more than assets held");
207 auto tx = vault.withdraw(
208 {.depositor = depositor,
210 .amount = asset(1000)});
216 testcase(prefix +
" deposit some more");
217 auto tx = vault.deposit(
218 {.depositor = depositor,
220 .amount = asset(100)});
224 env.balance(depositor, shares) == share(200 * scale));
228 testcase(prefix +
" clawback some");
231 auto tx = vault.clawback(
235 .amount = asset(10)});
238 if (!asset.raw().native())
241 env.balance(depositor, shares) == share(190 * scale));
249 auto tx = vault.clawback(
250 {.issuer = issuer, .id = keylet.
key, .holder = depositor});
253 if (!asset.raw().native())
255 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
258 auto tx = vault.clawback(
262 .amount = asset(10)});
268 auto tx = vault.withdraw(
269 {.depositor = depositor,
271 .amount = asset(10)});
278 if (!asset.raw().native())
280 testcase(prefix +
" deposit again");
281 auto tx = vault.deposit(
282 {.depositor = depositor,
284 .amount = asset(200)});
288 env.balance(depositor, shares) == share(200 * scale));
292 testcase(prefix +
" deposit/withdrawal same or less than fee");
293 auto const amount = env.current()->fees().base;
295 auto tx = vault.deposit(
296 {.depositor = depositor,
303 {.depositor = depositor,
310 {.depositor = depositor,
318 {.depositor = depositor,
321 tx[sfDestination] = charlie.human();
326 {.depositor = depositor,
328 .amount = amount - 1});
333 {.depositor = depositor,
335 .amount = amount - 1});
342 prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
343 auto tx = vault.withdraw(
344 {.depositor = depositor,
346 .amount = asset(100)});
347 tx[sfDestination] = alice.human();
353 testcase(prefix +
" fail to withdraw to zero destination");
354 auto tx = vault.withdraw(
355 {.depositor = depositor,
357 .amount = asset(1000)});
358 tx[sfDestination] =
"0";
363 if (!asset.raw().native())
366 prefix +
" fail to withdraw to 3rd party no authorization");
367 auto tx = vault.withdraw(
368 {.depositor = depositor,
370 .amount = asset(100)});
371 tx[sfDestination] = erin.human();
380 " fail to withdraw to 3rd party lsfRequireDestTag");
381 auto tx = vault.withdraw(
382 {.depositor = depositor,
384 .amount = asset(100)});
385 tx[sfDestination] = dave.human();
391 testcase(prefix +
" withdraw to 3rd party lsfRequireDestTag");
392 auto tx = vault.withdraw(
393 {.depositor = depositor,
395 .amount = asset(50)});
396 tx[sfDestination] = dave.human();
397 tx[sfDestinationTag] =
"0";
403 testcase(prefix +
" deposit again");
404 auto tx = vault.deposit(
405 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
411 testcase(prefix +
" fail to withdraw lsfRequireDestTag");
412 auto tx = vault.withdraw(
413 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
419 testcase(prefix +
" withdraw with tag");
420 auto tx = vault.withdraw(
421 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
422 tx[sfDestinationTag] =
"0";
428 testcase(prefix +
" withdraw to authorized 3rd party");
429 auto tx = vault.withdraw(
430 {.depositor = depositor,
432 .amount = asset(50)});
433 tx[sfDestination] = charlie.human();
437 env.balance(depositor, shares) == share(100 * scale));
441 testcase(prefix +
" withdraw to issuer");
442 auto tx = vault.withdraw(
443 {.depositor = depositor,
445 .amount = asset(50)});
446 tx[sfDestination] = issuer.human();
450 env.balance(depositor, shares) == share(50 * scale));
453 if (!asset.raw().native())
455 testcase(prefix +
" issuer deposits");
456 auto tx = vault.deposit(
457 {.depositor = issuer,
459 .amount = asset(10)});
462 BEAST_EXPECT(env.balance(issuer, shares) == share(10 * scale));
464 testcase(prefix +
" issuer withdraws");
466 {.depositor = issuer,
468 .amount = share(10 * scale)});
471 BEAST_EXPECT(env.balance(issuer, shares) == share(0 * scale));
475 testcase(prefix +
" withdraw remaining assets");
476 auto tx = vault.withdraw(
477 {.depositor = depositor,
479 .amount = asset(50)});
482 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
484 if (!asset.raw().native())
486 auto tx = vault.clawback(
490 .amount = asset(0)});
496 auto tx = vault.withdraw(
497 {.depositor = depositor,
499 .amount = share(10)});
505 if (!asset.raw().native() && asset.raw().holds<
Issue>())
507 testcase(prefix +
" temporary authorization for 3rd party");
508 env(trust(erin, asset(1000)));
509 env(trust(issuer, asset(0), erin,
tfSetfAuth));
510 env(pay(issuer, erin, asset(10)));
513 auto tx = vault.deposit(
514 {.depositor = erin, .id = keylet.
key, .amount = asset(10)});
518 auto tx = pay(erin, depositor, share(10 * scale));
526 {.depositor = depositor,
528 .amount = asset(1)}));
535 testcase(prefix +
" withdraw to authorized 3rd party");
538 {.depositor = depositor,
540 .amount = asset(10)});
541 tx[sfDestination] = erin.human();
546 env(pay(erin, issuer, asset(10)));
549 testcase(prefix +
" fail to pay to unauthorized 3rd party");
550 env(trust(erin, asset(0)));
554 env(pay(depositor, erin, share(1)), ter{
tecNO_LINE});
559 {.depositor = depositor,
561 .amount = asset(1)});
567 testcase(prefix +
" fail to delete because wrong owner");
568 auto tx = vault.del({.owner = issuer, .id = keylet.
key});
574 testcase(prefix +
" delete empty vault");
575 auto tx = vault.del({.owner = owner, .id = keylet.
key});
578 BEAST_EXPECT(!env.le(keylet));
582 auto testCases = [&,
this](
585 Env env{*
this, testable_amendments() | featureSingleAssetVault};
588 env.fund(XRP(1000), issuer, owner, depositor, charlie, dave);
598 testSequence(prefix, env, vault, asset);
605 testCases(
"IOU", [&](Env& env) ->
Asset {
607 env(trust(owner, asset(1000)));
608 env(trust(depositor, asset(1000)));
609 env(trust(charlie, asset(1000)));
610 env(trust(dave, asset(1000)));
611 env(trust(issuer, asset(0), owner,
tfSetfAuth));
612 env(trust(issuer, asset(0), depositor,
tfSetfAuth));
613 env(trust(issuer, asset(0), charlie,
tfSetfAuth));
614 env(trust(issuer, asset(0), dave,
tfSetfAuth));
615 env(pay(issuer, depositor, asset(1000)));
620 testCases(
"MPT", [&](Env& env) ->
Asset {
621 MPTTester mptt{env, issuer, mptInitNoFund};
625 mptt.authorize({.account = depositor});
626 mptt.authorize({.account = charlie});
627 mptt.authorize({.account = dave});
628 env(pay(issuer, depositor, asset(1000)));
637 using namespace test::jtx;
642 testable_amendments() | featureSingleAssetVault;
645 auto testCase = [&,
this](
648 Account
const& issuer,
649 Account
const& owner,
652 CaseArgs args = {}) {
653 Env env{*
this, args.features};
654 Account issuer{
"issuer"};
655 Account owner{
"owner"};
657 env.fund(XRP(1000), issuer, owner);
665 env(trust(owner, asset(1000)));
666 env(trust(issuer, asset(0), owner,
tfSetfAuth));
667 env(pay(issuer, owner, asset(1000)));
670 test(env, issuer, owner, asset, vault);
675 Account
const& issuer,
676 Account
const& owner,
679 testcase(
"disabled single asset vault");
682 vault.create({.owner = owner, .asset = asset});
686 auto tx = vault.set({.owner = owner, .id = keylet.
key});
691 auto tx = vault.deposit(
694 .amount = asset(10)});
699 auto tx = vault.withdraw(
702 .amount = asset(10)});
707 auto tx = vault.clawback(
711 .amount = asset(10)});
716 auto tx = vault.del({.owner = owner, .id = keylet.
key});
720 {.features = testable_amendments() - featureSingleAssetVault});
722 testCase([&](Env& env,
723 Account
const& issuer,
724 Account
const& owner,
729 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
734 auto tx = vault.set({.owner = owner, .id = keylet.
key});
740 auto tx = vault.deposit(
743 .amount = asset(10)});
749 auto tx = vault.withdraw(
752 .amount = asset(10)});
758 auto tx = vault.clawback(
762 .amount = asset(10)});
768 auto tx = vault.del({.owner = owner, .id = keylet.
key});
774 testCase([&](Env& env,
775 Account
const& issuer,
776 Account
const& owner,
781 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
786 auto tx = vault.set({.owner = owner, .id = keylet.
key});
792 auto tx = vault.deposit(
795 .amount = asset(10)});
801 auto tx = vault.withdraw(
804 .amount = asset(10)});
810 auto tx = vault.clawback(
814 .amount = asset(10)});
820 auto tx = vault.del({.owner = owner, .id = keylet.
key});
829 Account
const& owner,
832 testcase(
"disabled permissioned domain");
835 vault.create({.owner = owner, .asset =
xrpIssue()});
840 auto tx = vault.set({.owner = owner, .id = keylet.
key});
846 auto tx = vault.set({.owner = owner, .id = keylet.
key});
847 tx[sfDomainID] =
"0";
851 {.features = (testable_amendments() | featureSingleAssetVault) -
852 featurePermissionedDomains});
854 testCase([&](Env& env,
855 Account
const& issuer,
856 Account
const& owner,
862 vault.create({.owner = owner, .asset =
xrpIssue()});
865 auto tx = vault.set({
873 auto tx = vault.deposit(
876 .amount = asset(10)});
881 auto tx = vault.withdraw(
884 .amount = asset(10)});
889 auto tx = vault.clawback(
893 .amount = asset(10)});
898 auto tx = vault.del({
906 testCase([&](Env& env,
907 Account
const& issuer,
908 Account
const& owner,
913 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
916 auto tx = vault.clawback(
920 .amount = asset(10)});
925 testCase([&](Env& env,
927 Account
const& owner,
930 testcase(
"withdraw to bad destination");
932 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
935 auto tx = vault.withdraw(
938 .amount = asset(10)});
939 tx[jss::Destination] =
"0";
944 testCase([&](Env& env,
946 Account
const& owner,
953 vault.create({.owner = owner, .asset = asset});
960 vault.create({.owner = owner, .asset = asset});
968 vault.create({.owner = owner, .asset = asset});
972 auto const sleVault = env.le(keylet);
973 BEAST_EXPECT(sleVault);
974 BEAST_EXPECT((*sleVault)[sfScale] == 18);
979 vault.create({.owner = owner, .asset = asset});
983 auto const sleVault = env.le(keylet);
984 BEAST_EXPECT(sleVault);
985 BEAST_EXPECT((*sleVault)[sfScale] == 0);
990 vault.create({.owner = owner, .asset = asset});
993 auto const sleVault = env.le(keylet);
994 BEAST_EXPECT(sleVault);
995 BEAST_EXPECT((*sleVault)[sfScale] == 6);
999 testCase([&](Env& env,
1001 Account
const& owner,
1004 testcase(
"create or set invalid data");
1006 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1022 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1028 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1035 testCase([&](Env& env,
1037 Account
const& owner,
1042 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1045 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1050 testCase([&](Env& env,
1052 Account
const& owner,
1055 testcase(
"create with invalid metadata");
1057 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1061 tx[sfMPTokenMetadata] =
"";
1074 testCase([&](Env& env,
1076 Account
const& owner,
1081 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1084 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1090 testCase([&](Env& env,
1092 Account
const& owner,
1095 testcase(
"invalid deposit amount");
1097 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1100 auto tx = vault.deposit(
1101 {.depositor = owner,
1108 auto tx = vault.deposit(
1109 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1114 testCase([&](Env& env,
1116 Account
const& owner,
1119 testcase(
"invalid set immutable flag");
1121 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1124 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1130 testCase([&](Env& env,
1132 Account
const& owner,
1135 testcase(
"invalid withdraw amount");
1137 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1140 auto tx = vault.withdraw(
1141 {.depositor = owner,
1148 auto tx = vault.withdraw(
1149 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1154 testCase([&](Env& env,
1155 Account
const& issuer,
1156 Account
const& owner,
1161 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1164 auto tx = vault.clawback(
1168 .amount = asset(50)});
1173 auto tx = vault.clawback(
1182 testCase([&](Env& env,
1184 Account
const& owner,
1189 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1193 tx[sfWithdrawalPolicy] = 0;
1212 tx[sfDomainID] =
"0";
1701 using namespace test::jtx;
1705 bool enableClawback =
true;
1707 int initialXRP = 1000;
1710 auto testCase = [
this](
1713 Account
const& issuer,
1714 Account
const& owner,
1715 Account
const& depositor,
1718 MPTTester& mptt)> test,
1719 CaseArgs args = {}) {
1720 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1721 Account issuer{
"issuer"};
1722 Account owner{
"owner"};
1723 Account depositor{
"depositor"};
1724 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
1728 MPTTester mptt{env, issuer, mptInitNoFund};
1735 mptt.authorize({.account = owner});
1736 mptt.authorize({.account = depositor});
1737 if (args.requireAuth)
1739 mptt.authorize({.account = issuer, .holder = owner});
1740 mptt.authorize({.account = issuer, .holder = depositor});
1743 env(pay(issuer, depositor, asset(1000)));
1746 test(env, issuer, owner, depositor, asset, vault, mptt);
1751 Account
const& issuer,
1752 Account
const& owner,
1753 Account
const& depositor,
1757 testcase(
"MPT nothing to clawback from");
1758 auto tx = vault.clawback(
1761 .holder = depositor,
1762 .amount = asset(10)});
1768 Account
const& issuer,
1769 Account
const& owner,
1770 Account
const& depositor,
1774 testcase(
"MPT global lock blocks create");
1775 mptt.set({.account = issuer, .flags =
tfMPTLock});
1776 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1782 Account
const& issuer,
1783 Account
const& owner,
1784 Account
const& depositor,
1788 testcase(
"MPT global lock blocks deposit");
1789 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1793 mptt.set({.account = issuer, .flags =
tfMPTLock});
1797 {.depositor = depositor,
1799 .amount = asset(100)});
1804 tx = vault.del({.owner = owner, .id = keylet.
key});
1810 Account
const& issuer,
1811 Account
const& owner,
1812 Account
const& depositor,
1816 testcase(
"MPT global lock blocks withdrawal");
1817 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1821 {.depositor = depositor,
1823 .amount = asset(100)});
1829 auto v = env.le(keylet);
1831 MPTID share = (*v)[sfShareMPTID];
1833 BEAST_EXPECT(issuance);
1834 Number outstandingShares = issuance->at(sfOutstandingAmount);
1835 BEAST_EXPECT(outstandingShares == 100);
1837 mptt.set({.account = issuer, .flags =
tfMPTLock});
1840 tx = vault.withdraw(
1841 {.depositor = depositor,
1843 .amount = asset(100)});
1846 tx[sfDestination] = issuer.human();
1850 tx = vault.clawback(
1853 .holder = depositor,
1854 .amount = asset(0)});
1860 BEAST_EXPECT(mptSle ==
nullptr);
1863 tx = vault.del({.owner = owner, .id = keylet.
key});
1869 Account
const& issuer,
1870 Account
const& owner,
1871 Account
const& depositor,
1875 testcase(
"MPT only issuer can clawback");
1877 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1882 {.depositor = depositor,
1884 .amount = asset(100)});
1889 auto tx = vault.clawback(
1890 {.issuer = owner, .id = keylet.
key, .holder = depositor});
1898 Account
const& issuer,
1899 Account
const& owner,
1900 Account
const& depositor,
1904 testcase(
"MPT depositor without MPToken, auth required");
1907 vault.create({.owner = owner, .asset = asset});
1912 {.depositor = depositor,
1914 .amount = asset(1000)});
1924 auto const mptoken =
1926 auto const sleMPT1 = env.le(mptoken);
1927 BEAST_EXPECT(sleMPT1 ==
nullptr);
1929 tx = vault.withdraw(
1930 {.depositor = depositor,
1932 .amount = asset(100)});
1936 auto const sleMPT2 = env.le(mptoken);
1937 BEAST_EXPECT(sleMPT2 ==
nullptr);
1942 Account charlie{
"charlie"};
1943 env.fund(XRP(1000), charlie);
1946 tx = vault.withdraw(
1947 {.depositor = depositor,
1949 .amount = asset(100)});
1950 tx[sfDestination] = charlie.human();
1954 {.requireAuth =
true});
1959 Account
const& issuer,
1960 Account
const& owner,
1961 Account
const& depositor,
1965 testcase(
"MPT depositor without MPToken, no auth required");
1968 vault.create({.owner = owner, .asset = asset});
1971 auto v = env.le(keylet);
1975 {.depositor = depositor,
1977 .amount = asset(1000)});
1987 auto const mptoken =
1989 auto const sleMPT1 = env.le(mptoken);
1990 BEAST_EXPECT(sleMPT1 ==
nullptr);
1992 tx = vault.withdraw(
1993 {.depositor = depositor,
1995 .amount = asset(100)});
1999 auto const sleMPT2 = env.le(mptoken);
2000 BEAST_EXPECT(sleMPT2 !=
nullptr);
2001 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
2010 auto const mptoken =
2012 auto const sleMPT1 = env.le(mptoken);
2013 BEAST_EXPECT(sleMPT1 ==
nullptr);
2015 tx = vault.withdraw(
2016 {.depositor = depositor,
2018 .amount = asset(100)});
2019 tx[sfDestination] = owner.human();
2023 auto const sleMPT2 = env.le(mptoken);
2024 BEAST_EXPECT(sleMPT2 ==
nullptr);
2027 {.requireAuth =
false});
2030 Env env{*
this, testable_amendments()};
2032 env.current()->fees().accountReserve(0).drops() /
2034 env.current()->fees().increment.drops() /
2041 Account
const& issuer,
2042 Account
const& owner,
2043 Account
const& depositor,
2047 testcase(
"MPT failed reserve to re-create MPToken");
2050 vault.create({.owner = owner, .asset = asset});
2053 auto v = env.le(keylet);
2056 env(pay(depositor, owner, asset(1000)));
2060 {.depositor = owner,
2062 .amount = asset(1000)});
2072 auto const mptoken =
2074 auto const sleMPT = env.le(mptoken);
2075 BEAST_EXPECT(sleMPT ==
nullptr);
2078 env(ticket::create(owner, 1));
2082 tx = vault.withdraw(
2083 {.depositor = owner,
2085 .amount = asset(100)});
2089 env(pay(depositor, owner, XRP(incReserve)));
2097 {.requireAuth =
false,
2098 .initialXRP = acctReserve + incReserve * 4 + 1});
2102 Account
const& issuer,
2103 Account
const& owner,
2104 Account
const& depositor,
2110 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2115 {.depositor = depositor,
2117 .amount = asset(1000)});
2122 auto tx = vault.clawback(
2125 .holder = depositor,
2126 .amount = asset(0)});
2130 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
2135 vault.create({.owner = depositor, .asset = asset});
2140 auto tx = vault.deposit(
2141 {.depositor = depositor,
2143 .amount = asset(10)});
2148 auto tx = vault.withdraw(
2149 {.depositor = depositor,
2151 .amount = asset(10)});
2156 auto tx = vault.clawback(
2159 .holder = depositor,
2160 .amount = asset(0)});
2164 env(vault.del({.owner = owner, .id = keylet.key}));
2169 Account
const& issuer,
2170 Account
const& owner,
2171 Account
const& depositor,
2175 testcase(
"MPT vault owner can receive shares unless unauthorized");
2177 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2182 {.depositor = depositor,
2184 .amount = asset(1000)});
2189 auto const vault = env.le(keylet);
2190 return vault->at(sfShareMPTID);
2196 env(pay(depositor, owner, shares(1)));
2199 tx = vault.withdraw(
2200 {.depositor = owner,
2202 .amount = shares(1)});
2207 env(pay(depositor, owner, shares(1)));
2210 tx = vault.clawback(
2214 .amount = asset(0)});
2219 env(pay(depositor, owner, shares(1)));
2223 env(pay(owner, depositor, shares(1)));
2229 jv[sfAccount] = owner.human();
2230 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
2232 jv[sfTransactionType] = jss::MPTokenAuthorize;
2238 tx = pay(depositor, owner, shares(1));
2243 tx = vault.clawback(
2246 .holder = depositor,
2247 .amount = asset(0)});
2252 env(vault.del({.owner = owner, .id = keylet.key}));
2260 Account
const& issuer,
2261 Account
const& owner,
2262 Account
const& depositor,
2269 vault.create({.owner = owner, .asset = asset});
2274 {.depositor = depositor,
2276 .amount = asset(1000)});
2281 auto tx = vault.clawback(
2284 .holder = depositor,
2285 .amount = asset(0)});
2289 {.enableClawback =
false});
2293 Account
const& issuer,
2294 Account
const& owner,
2295 Account
const& depositor,
2300 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2304 {.depositor = depositor,
2306 .amount = asset(1000)});
2312 .holder = depositor,
2317 auto tx = vault.withdraw(
2318 {.depositor = depositor,
2320 .amount = asset(100)});
2324 tx[sfDestination] = issuer.human();
2328 tx[sfDestination] = owner.human();
2335 auto tx = vault.deposit(
2336 {.depositor = depositor,
2338 .amount = asset(100)});
2343 tx = vault.clawback(
2346 .holder = depositor,
2347 .amount = asset(800)});
2351 env(vault.del({.owner = owner, .id = keylet.key}));
2356 Account
const& issuer,
2357 Account
const& owner,
2358 Account
const& depositor,
2362 testcase(
"MPT lock of vault pseudo-account");
2363 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2367 auto const vaultAccount =
2368 [&env, keylet = keylet,
this]() ->
AccountID {
2369 auto const vault = env.le(keylet);
2370 BEAST_EXPECT(vault !=
nullptr);
2371 return vault->at(sfAccount);
2375 {.depositor = depositor,
2377 .amount = asset(100)});
2383 jv[jss::Account] = issuer.human();
2384 jv[sfMPTokenIssuanceID] =
2386 jv[jss::Holder] =
toBase58(vaultAccount);
2387 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
2395 {.depositor = depositor,
2397 .amount = asset(100)});
2400 tx = vault.withdraw(
2401 {.depositor = depositor,
2403 .amount = asset(100)});
2407 tx = vault.clawback(
2410 .holder = depositor,
2411 .amount = asset(100)});
2415 tx = vault.del({.owner = owner, .id = keylet.
key});
2422 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2423 Account owner{
"owner"};
2424 Account issuer{
"issuer"};
2425 env.fund(XRP(1000000), owner, issuer);
2429 MPTTester mptt{env, issuer, mptInitNoFund};
2433 mptt.authorize({.account = owner});
2434 mptt.authorize({.account = issuer, .holder = owner});
2436 env(pay(issuer, owner, asset(100)));
2437 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
2441 auto const shares = [&env, keylet = k1,
this]() ->
Asset {
2442 auto const vault = env.le(keylet);
2443 BEAST_EXPECT(vault !=
nullptr);
2444 return MPTIssue(vault->at(sfShareMPTID));
2447 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
2456 using namespace test::jtx;
2460 int initialXRP = 1000;
2469 Account
const& owner,
2470 Account
const& issuer,
2471 Account
const& charlie,
2476 CaseArgs args = {}) {
2477 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2478 Account
const owner{
"owner"};
2479 Account
const issuer{
"issuer"};
2480 Account
const charlie{
"charlie"};
2482 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
2487 env.trust(asset(1000), owner);
2488 env.trust(asset(1000), charlie);
2489 env(pay(issuer, owner, asset(args.initialIOU)));
2490 env(rate(issuer, args.transferRate));
2493 auto const vaultAccount =
2495 return Account(
"vault", env.le(keylet)->at(sfAccount));
2498 return env.le(keylet)->at(sfShareMPTID);
2514 Account
const& owner,
2515 Account
const& issuer,
2521 testcase(
"IOU cannot use different asset");
2524 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2530 auto tx = [&, account = vaultAccount(keylet)]() {
2532 jv[jss::Account] = issuer.human();
2534 auto& ja = jv[jss::LimitAmount] =
2536 ja[jss::issuer] =
toBase58(account);
2538 jv[jss::TransactionType] = jss::TrustSet;
2547 auto tx = vault.deposit(
2548 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2554 auto tx = vault.withdraw(
2555 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2560 env(vault.del({.owner = owner, .id = keylet.key}));
2566 Account
const& owner,
2567 Account
const& issuer,
2568 Account
const& charlie,
2573 testcase(
"IOU frozen trust line to vault account");
2575 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2580 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2583 Asset const share =
Asset(issuanceId(keylet));
2586 auto trustSet = [&, account = vaultAccount(keylet)]() {
2588 jv[jss::Account] = issuer.human();
2590 auto& ja = jv[jss::LimitAmount] =
2592 ja[jss::issuer] =
toBase58(account);
2594 jv[jss::TransactionType] = jss::TrustSet;
2606 auto tx = vault.deposit(
2607 {.depositor = owner,
2609 .amount = asset(80)});
2614 auto tx = vault.withdraw(
2615 {.depositor = owner,
2617 .amount = asset(100)});
2621 tx[sfDestination] = charlie.human();
2628 auto tx = vault.clawback(
2632 .amount = asset(50)});
2643 {.depositor = owner,
2645 .amount = share(50'000'000)}));
2647 env(vault.del({.owner = owner, .id = keylet.key}));
2654 Account
const& owner,
2655 Account
const& issuer,
2656 Account
const& charlie,
2661 testcase(
"IOU transfer fees not applied");
2664 vault.create({.owner = owner, .asset = asset});
2669 {.depositor = owner,
2671 .amount = asset(100)}));
2675 Asset const share =
Asset(issuanceId(keylet));
2678 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2680 env.balance(vaultAccount(keylet), issue) == asset(100));
2683 auto tx = vault.clawback(
2687 .amount = asset(50)});
2693 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2695 env.balance(vaultAccount(keylet), issue) == asset(50));
2698 {.depositor = owner,
2700 .amount = share(20'000'000)}));
2703 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2705 env.balance(vaultAccount(keylet), issue) == asset(30));
2708 auto tx = vault.withdraw(
2709 {.depositor = owner,
2711 .amount = share(30'000'000)});
2712 tx[sfDestination] = charlie.human();
2717 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2718 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2720 env.balance(vaultAccount(keylet), issue) == asset(0));
2722 env(vault.del({.owner = owner, .id = keylet.key}));
2725 CaseArgs{.transferRate = 1.25});
2729 Account
const& owner,
2730 Account
const& issuer,
2731 Account
const& charlie,
2736 testcase(
"IOU frozen trust line to depositor");
2738 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2743 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2748 auto tx = vault.withdraw(
2749 {.depositor = owner,
2751 .amount = asset(10)});
2752 tx[sfDestination] = charlie.human();
2755 env(withdrawToCharlie);
2762 auto const withdraw = vault.withdraw(
2763 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2772 auto tx = vault.deposit(
2773 {.depositor = owner,
2775 .amount = asset(10)});
2781 auto tx = vault.clawback(
2785 .amount = asset(0)});
2790 env(vault.del({.owner = owner, .id = keylet.key}));
2796 Account
const& owner,
2797 Account
const& issuer,
2798 Account
const& charlie,
2803 testcase(
"IOU no trust line to 3rd party");
2805 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2810 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2813 Account
const erin{
"erin"};
2814 env.fund(XRP(1000), erin);
2819 auto tx = vault.withdraw(
2820 {.depositor = owner,
2822 .amount = asset(10)});
2823 tx[sfDestination] = erin.human();
2831 Account
const& owner,
2832 Account
const& issuer,
2833 Account
const& charlie,
2838 testcase(
"IOU no trust line to depositor");
2840 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2845 env.trust(asset(0), owner);
2849 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2854 BEAST_EXPECT(trustline ==
nullptr);
2858 auto tx = vault.withdraw(
2859 {.depositor = owner,
2861 .amount = asset(10)});
2870 Account
const& owner,
2871 Account
const& issuer,
2872 Account
const& charlie,
2873 auto const& vaultAccount,
2877 testcase(
"IOU calculation rounding");
2880 vault.create({.owner = owner, .asset = asset});
2885 auto const startingOwnerBalance = env.balance(owner, asset);
2887 (startingOwnerBalance.value() ==
2895 {.depositor = owner,
2897 .amount = asset(100)}));
2899 auto const tx1 = vault.deposit(
2900 {.depositor = owner,
2902 .amount = asset(
Number(375, -2))});
2903 for (
auto i = 0; i < 5; ++i)
2910 STAmount const xfer{asset, 1185, -1};
2912 env.balance(owner, asset) ==
2913 startingOwnerBalance.value() - xfer);
2915 env.balance(vaultAccount(keylet), asset) == xfer);
2917 auto const vault = env.le(keylet);
2918 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
2919 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
2926 {.depositor = owner,
2928 .amount = asset(Number(1000 + 37 * 5, -1))}));
2932 env.balance(owner, asset) ==
2933 startingOwnerBalance.value());
2935 env.balance(vaultAccount(keylet), asset) ==
2937 auto const vault = env.le(keylet);
2938 BEAST_EXPECT(vault->at(sfAssetsAvailable) == beast::zero);
2939 BEAST_EXPECT(vault->at(sfAssetsTotal) == beast::zero);
2942 env(vault.del({.owner = owner, .id = keylet.key}));
2945 {.initialIOU =
Number(11875, -2)});
2948 Env env{*
this, testable_amendments()};
2950 env.current()->fees().accountReserve(0).drops() /
2952 env.current()->fees().increment.drops() /
2959 Account
const& owner,
2960 Account
const& issuer,
2961 Account
const& charlie,
2966 testcase(
"IOU no trust line to depositor no reserve");
2968 vault.create({.owner = owner, .asset = asset});
2974 env.trust(asset(0), owner);
2978 {.depositor = owner,
2980 .amount = asset(200)}));
2985 BEAST_EXPECT(trustline ==
nullptr);
2987 env(ticket::create(owner, 1));
2991 tx = vault.withdraw(
2992 {.depositor = owner,
2994 .amount = asset(10)});
2998 env(pay(charlie, owner, XRP(incReserve)));
3005 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
3010 Account
const& owner,
3011 Account
const& issuer,
3012 Account
const& charlie,
3017 testcase(
"IOU no reserve for share MPToken");
3019 vault.create({.owner = owner, .asset = asset});
3023 env(pay(owner, charlie, asset(100)));
3026 env(ticket::create(charlie, 3));
3031 {.depositor = charlie,
3033 .amount = asset(100)});
3037 env(pay(issuer, charlie, XRP(incReserve)));
3044 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
3048 Account
const& owner,
3049 Account
const& issuer,
3050 Account
const& charlie,
3055 testcase(
"IOU frozen trust line to 3rd party");
3057 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3062 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3067 auto tx = vault.withdraw(
3068 {.depositor = owner,
3070 .amount = asset(10)});
3071 tx[sfDestination] = charlie.human();
3074 env(withdrawToCharlie);
3077 env(trust(issuer, asset(0), charlie,
tfSetFreeze));
3081 auto const withdraw = vault.withdraw(
3082 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
3094 .amount = asset(0)}));
3097 env(vault.del({.owner = owner, .id = keylet.key}));
3103 Account
const& owner,
3104 Account
const& issuer,
3105 Account
const& charlie,
3112 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3117 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3125 auto tx = vault.withdraw(
3126 {.depositor = owner,
3128 .amount = asset(10)});
3132 tx[sfDestination] = charlie.human();
3138 {.depositor = owner,
3140 .amount = asset(10)});
3150 .amount = asset(0)}));
3153 env(vault.del({.owner = owner, .id = keylet.key}));
3161 using namespace test::jtx;
3165 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3166 Account issuer{
"issuer"};
3167 Account owner{
"owner"};
3168 Account depositor{
"depositor"};
3169 Account charlie{
"charlie"};
3170 Account pdOwner{
"pdOwner"};
3171 Account credIssuer1{
"credIssuer1"};
3172 Account credIssuer2{
"credIssuer2"};
3190 env.trust(asset(1000), owner);
3191 env(pay(issuer, owner, asset(500)));
3192 env.trust(asset(1000), depositor);
3193 env(pay(issuer, depositor, asset(500)));
3194 env.trust(asset(1000), charlie);
3195 env(pay(issuer, charlie, asset(5)));
3198 auto [tx, keylet] = vault.create(
3202 BEAST_EXPECT(env.le(keylet));
3205 testcase(
"private vault owner can deposit");
3206 auto tx = vault.deposit(
3207 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
3212 testcase(
"private vault depositor not authorized yet");
3213 auto tx = vault.deposit(
3214 {.depositor = depositor,
3216 .amount = asset(50)});
3221 testcase(
"private vault cannot set non-existing domain");
3222 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3228 testcase(
"private vault set domainId");
3231 pdomain::Credentials
const credentials1{
3232 {.issuer = credIssuer1, .credType = credType}};
3234 env(pdomain::setTx(pdOwner, credentials1));
3235 auto const domainId1 = [&]() {
3237 return pdomain::getNewDomain(env.meta());
3240 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3251 pdomain::Credentials
const credentials{
3252 {.issuer = credIssuer1, .credType = credType},
3253 {.issuer = credIssuer2, .credType = credType}};
3255 env(pdomain::setTx(pdOwner, credentials));
3256 auto const domainId = [&]() {
3258 return pdomain::getNewDomain(env.meta());
3261 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3267 tx = vault.set({.owner = owner, .id = keylet.
key});
3275 testcase(
"private vault depositor still not authorized");
3276 auto tx = vault.deposit(
3277 {.depositor = depositor,
3279 .amount = asset(50)});
3284 auto const credKeylet =
3285 credentials::keylet(depositor, credIssuer1, credType);
3287 testcase(
"private vault depositor now authorized");
3288 env(credentials::create(depositor, credIssuer1, credType));
3289 env(credentials::accept(depositor, credIssuer1, credType));
3290 env(credentials::create(charlie, credIssuer1, credType));
3293 auto credSle = env.le(credKeylet);
3294 BEAST_EXPECT(credSle !=
nullptr);
3296 auto tx = vault.deposit(
3297 {.depositor = depositor,
3299 .amount = asset(50)});
3304 {.depositor = charlie, .id = keylet.
key, .amount = asset(50)});
3310 testcase(
"private vault depositor lost authorization");
3311 env(credentials::deleteCred(
3312 credIssuer1, depositor, credIssuer1, credType));
3313 env(credentials::deleteCred(
3314 credIssuer1, charlie, credIssuer1, credType));
3316 auto credSle = env.le(credKeylet);
3317 BEAST_EXPECT(credSle ==
nullptr);
3319 auto tx = vault.deposit(
3320 {.depositor = depositor,
3322 .amount = asset(50)});
3327 auto const shares = [&env, keylet = keylet,
this]() ->
Asset {
3328 auto const vault = env.le(keylet);
3329 BEAST_EXPECT(vault !=
nullptr);
3330 return MPTIssue(vault->at(sfShareMPTID));
3334 testcase(
"private vault expired authorization");
3335 uint32_t
const closeTime = env.current()
3337 .parentCloseTime.time_since_epoch()
3341 credentials::create(depositor, credIssuer2, credType);
3342 tx0[sfExpiration] = closeTime + 20;
3344 tx0 = credentials::create(charlie, credIssuer2, credType);
3345 tx0[sfExpiration] = closeTime + 20;
3349 env(credentials::accept(depositor, credIssuer2, credType));
3350 env(credentials::accept(charlie, credIssuer2, credType));
3355 auto tx1 = vault.deposit(
3356 {.depositor = depositor,
3358 .amount = asset(50)});
3364 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
3373 auto const credsKeylet =
3374 credentials::keylet(depositor, credIssuer2, credType);
3375 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3377 auto tx2 = vault.deposit(
3378 {.depositor = depositor,
3380 .amount = asset(1)});
3384 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3388 auto const credsKeylet =
3389 credentials::keylet(charlie, credIssuer2, credType);
3390 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3393 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3395 auto tx3 = vault.deposit(
3396 {.depositor = charlie,
3398 .amount = asset(2)});
3402 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3403 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3408 testcase(
"private vault reset domainId");
3409 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3410 tx[sfDomainID] =
"0";
3415 {.depositor = depositor,
3417 .amount = asset(50)});
3421 tx = vault.withdraw(
3422 {.depositor = depositor,
3424 .amount = asset(50)});
3428 tx = vault.clawback(
3431 .holder = depositor,
3432 .amount = asset(0)});
3435 tx = vault.clawback(
3439 .amount = asset(0)});
3588 using namespace test::jtx;
3592 Account
const& owner;
3593 Account
const& issuer;
3594 Account
const& depositor;
3595 Account
const& vaultAccount;
3605 auto testCase = [&,
this](
3608 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3609 Account
const owner{
"owner"};
3610 Account
const issuer{
"issuer"};
3611 Account
const depositor{
"depositor"};
3613 env.fund(XRP(1000), issuer, owner, depositor);
3618 env.trust(asset(1000), owner);
3619 env.trust(asset(1000), depositor);
3620 env(pay(issuer, owner, asset(200)));
3621 env(pay(issuer, depositor, asset(200)));
3624 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3625 tx[sfScale] = scale;
3628 auto const [vaultAccount, issuanceId] =
3630 auto const vault = env.le(keylet);
3632 Account(
"vault", vault->at(sfAccount)),
3633 vault->at(sfShareMPTID)};
3636 env.memoize(vaultAccount);
3640 return env.app().openLedger().modify(
3644 if (!BEAST_EXPECT(vault !=
nullptr))
3646 auto shares = sb.
peek(
3648 if (!BEAST_EXPECT(shares !=
nullptr))
3650 if (fn(*vault, *shares))
3665 .depositor = depositor,
3666 .vaultAccount = vaultAccount,
3676 testCase(18, [&,
this](Env& env, Data d) {
3677 testcase(
"Scale deposit overflow on first deposit");
3678 auto tx = d.vault.deposit(
3679 {.depositor = d.depositor,
3681 .amount = d.asset(10)});
3686 testCase(18, [&,
this](Env& env, Data d) {
3687 testcase(
"Scale deposit overflow on second deposit");
3690 auto tx = d.vault.deposit(
3691 {.depositor = d.depositor,
3693 .amount = d.asset(5)});
3699 auto tx = d.vault.deposit(
3700 {.depositor = d.depositor,
3702 .amount = d.asset(10)});
3708 testCase(18, [&,
this](Env& env, Data d) {
3709 testcase(
"Scale deposit overflow on total shares");
3712 auto tx = d.vault.deposit(
3713 {.depositor = d.depositor,
3715 .amount = d.asset(5)});
3721 auto tx = d.vault.deposit(
3722 {.depositor = d.depositor,
3724 .amount = d.asset(5)});
3730 testCase(1, [&,
this](Env& env, Data d) {
3733 auto const start = env.balance(d.depositor, d.assets).number();
3734 auto tx = d.vault.deposit(
3735 {.depositor = d.depositor,
3737 .amount = d.asset(1)});
3740 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3742 env.balance(d.depositor, d.assets) ==
3746 testCase(1, [&,
this](Env& env, Data d) {
3747 testcase(
"Scale deposit insignificant amount");
3749 auto tx = d.vault.deposit(
3750 {.depositor = d.depositor,
3756 testCase(1, [&,
this](Env& env, Data d) {
3757 testcase(
"Scale deposit exact, using full precision");
3759 auto const start = env.balance(d.depositor, d.assets).number();
3760 auto tx = d.vault.deposit(
3761 {.depositor = d.depositor,
3766 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3768 env.balance(d.depositor, d.assets) ==
3772 testCase(1, [&,
this](Env& env, Data d) {
3773 testcase(
"Scale deposit exact, truncating from .5");
3775 auto const start = env.balance(d.depositor, d.assets).number();
3779 auto tx = d.vault.deposit(
3780 {.depositor = d.depositor,
3785 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3787 env.balance(d.depositor, d.assets) ==
3792 auto tx = d.vault.deposit(
3793 {.depositor = d.depositor,
3798 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3800 env.balance(d.depositor, d.assets) ==
3805 auto tx = d.vault.deposit(
3806 {.depositor = d.depositor,
3811 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
3813 env.balance(d.depositor, d.assets) ==
3818 testCase(1, [&,
this](Env& env, Data d) {
3819 testcase(
"Scale deposit exact, truncating from .01");
3821 auto const start = env.balance(d.depositor, d.assets).number();
3823 auto tx = d.vault.deposit(
3824 {.depositor = d.depositor,
3829 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3831 env.balance(d.depositor, d.assets) ==
3836 auto tx = d.vault.deposit(
3837 {.depositor = d.depositor,
3842 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3844 env.balance(d.depositor, d.assets) ==
3849 testCase(1, [&,
this](Env& env, Data d) {
3850 testcase(
"Scale deposit exact, truncating from .99");
3852 auto const start = env.balance(d.depositor, d.assets).number();
3854 auto tx = d.vault.deposit(
3855 {.depositor = d.depositor,
3860 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3862 env.balance(d.depositor, d.assets) ==
3867 auto tx = d.vault.deposit(
3868 {.depositor = d.depositor,
3873 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3875 env.balance(d.depositor, d.assets) ==
3880 testCase(1, [&,
this](Env& env, Data d) {
3882 auto const start = env.balance(d.depositor, d.assets).number();
3883 auto tx = d.vault.deposit(
3884 {.depositor = d.depositor,
3889 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3891 env.balance(d.depositor, d.assets) ==
3894 env.balance(d.vaultAccount, d.assets) ==
3897 env.balance(d.vaultAccount, d.shares) ==
3906 auto const start = env.balance(d.depositor, d.assets).number();
3907 auto tx = d.vault.withdraw(
3908 {.depositor = d.depositor,
3914 env.balance(d.depositor, d.shares) == d.share(900));
3916 env.balance(d.depositor, d.assets) ==
3919 env.balance(d.vaultAccount, d.assets) ==
3922 env.balance(d.vaultAccount, d.shares) ==
3927 testcase(
"Scale redeem with rounding");
3932 auto const start = env.balance(d.depositor, d.assets).number();
3933 d.peek([](
SLE& vault,
auto&) ->
bool {
3934 vault[sfAssetsAvailable] =
Number(1);
3942 auto tx = d.vault.withdraw(
3943 {.depositor = d.depositor,
3949 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3951 env.balance(d.depositor, d.assets) ==
3954 env.balance(d.vaultAccount, d.assets) ==
3957 env.balance(d.vaultAccount, d.shares) ==
3967 auto const start = env.balance(d.depositor, d.assets).number();
3969 tx = d.vault.withdraw(
3970 {.depositor = d.depositor,
3976 env.balance(d.depositor, d.shares) == d.share(875 - 21));
3978 env.balance(d.depositor, d.assets) ==
3981 env.balance(d.vaultAccount, d.assets) ==
3984 env.balance(d.vaultAccount, d.shares) ==
3990 auto const rest = env.balance(d.depositor, d.shares).number();
3992 tx = d.vault.withdraw(
3993 {.depositor = d.depositor,
3995 .amount =
STAmount(d.share, rest)});
3998 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4000 env.balance(d.vaultAccount, d.assets).number() == 0);
4002 env.balance(d.vaultAccount, d.shares).number() == 0);
4006 testCase(18, [&,
this](Env& env, Data d) {
4007 testcase(
"Scale withdraw overflow");
4010 auto tx = d.vault.deposit(
4011 {.depositor = d.depositor,
4013 .amount = d.asset(5)});
4019 auto tx = d.vault.withdraw(
4020 {.depositor = d.depositor,
4028 testCase(1, [&,
this](Env& env, Data d) {
4030 auto const start = env.balance(d.depositor, d.assets).number();
4031 auto tx = d.vault.deposit(
4032 {.depositor = d.depositor,
4037 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4039 env.balance(d.depositor, d.assets) ==
4042 env.balance(d.vaultAccount, d.assets) ==
4045 env.balance(d.vaultAccount, d.shares) ==
4057 auto const start = env.balance(d.depositor, d.assets).number();
4058 auto tx = d.vault.withdraw(
4059 {.depositor = d.depositor,
4065 env.balance(d.depositor, d.shares) == d.share(900));
4067 env.balance(d.depositor, d.assets) ==
4070 env.balance(d.vaultAccount, d.assets) ==
4073 env.balance(d.vaultAccount, d.shares) ==
4078 testcase(
"Scale withdraw insignificant amount");
4079 auto tx = d.vault.withdraw(
4080 {.depositor = d.depositor,
4087 testcase(
"Scale withdraw with rounding assets");
4095 auto const start = env.balance(d.depositor, d.assets).number();
4096 d.peek([](
SLE& vault,
auto&) ->
bool {
4097 vault[sfAssetsAvailable] =
Number(1);
4105 auto tx = d.vault.withdraw(
4106 {.depositor = d.depositor,
4112 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4114 env.balance(d.depositor, d.assets) ==
4117 env.balance(d.vaultAccount, d.assets) ==
4120 env.balance(d.vaultAccount, d.shares) ==
4125 testcase(
"Scale withdraw with rounding shares up");
4133 auto const start = env.balance(d.depositor, d.assets).number();
4134 auto tx = d.vault.withdraw(
4135 {.depositor = d.depositor,
4141 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4143 env.balance(d.depositor, d.assets) ==
4146 env.balance(d.vaultAccount, d.assets) ==
4149 env.balance(d.vaultAccount, d.shares) ==
4154 testcase(
"Scale withdraw with rounding shares down");
4162 auto const start = env.balance(d.depositor, d.assets).number();
4163 auto tx = d.vault.withdraw(
4164 {.depositor = d.depositor,
4170 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4172 env.balance(d.depositor, d.assets) ==
4175 env.balance(d.vaultAccount, d.assets) ==
4178 env.balance(d.vaultAccount, d.shares) ==
4183 testcase(
"Scale withdraw tiny amount");
4185 auto const start = env.balance(d.depositor, d.assets).number();
4186 auto tx = d.vault.withdraw(
4187 {.depositor = d.depositor,
4193 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4195 env.balance(d.depositor, d.assets) ==
4198 env.balance(d.vaultAccount, d.assets) ==
4201 env.balance(d.vaultAccount, d.shares) ==
4208 env.balance(d.vaultAccount, d.assets).number();
4210 tx = d.vault.withdraw(
4211 {.depositor = d.depositor,
4213 .amount =
STAmount(d.asset, rest)});
4216 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4218 env.balance(d.vaultAccount, d.assets).number() == 0);
4220 env.balance(d.vaultAccount, d.shares).number() == 0);
4224 testCase(18, [&,
this](Env& env, Data d) {
4225 testcase(
"Scale clawback overflow");
4228 auto tx = d.vault.deposit(
4229 {.depositor = d.depositor,
4231 .amount = d.asset(5)});
4237 auto tx = d.vault.clawback(
4238 {.issuer = d.issuer,
4240 .holder = d.depositor,
4247 testCase(1, [&,
this](Env& env, Data d) {
4249 auto const start = env.balance(d.depositor, d.assets).number();
4250 auto tx = d.vault.deposit(
4251 {.depositor = d.depositor,
4256 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4258 env.balance(d.depositor, d.assets) ==
4261 env.balance(d.vaultAccount, d.assets) ==
4264 env.balance(d.vaultAccount, d.shares) ==
4275 auto const start = env.balance(d.depositor, d.assets).number();
4276 auto tx = d.vault.clawback(
4277 {.issuer = d.issuer,
4279 .holder = d.depositor,
4284 env.balance(d.depositor, d.shares) == d.share(900));
4286 env.balance(d.depositor, d.assets) ==
4289 env.balance(d.vaultAccount, d.assets) ==
4292 env.balance(d.vaultAccount, d.shares) ==
4297 testcase(
"Scale clawback insignificant amount");
4298 auto tx = d.vault.clawback(
4299 {.issuer = d.issuer,
4301 .holder = d.depositor,
4307 testcase(
"Scale clawback with rounding assets");
4315 auto const start = env.balance(d.depositor, d.assets).number();
4316 auto tx = d.vault.clawback(
4317 {.issuer = d.issuer,
4319 .holder = d.depositor,
4324 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4326 env.balance(d.depositor, d.assets) ==
4329 env.balance(d.vaultAccount, d.assets) ==
4332 env.balance(d.vaultAccount, d.shares) ==
4337 testcase(
"Scale clawback with rounding shares up");
4345 auto const start = env.balance(d.depositor, d.assets).number();
4346 auto tx = d.vault.clawback(
4347 {.issuer = d.issuer,
4349 .holder = d.depositor,
4354 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4356 env.balance(d.depositor, d.assets) ==
4359 env.balance(d.vaultAccount, d.assets) ==
4362 env.balance(d.vaultAccount, d.shares) ==
4367 testcase(
"Scale clawback with rounding shares down");
4375 auto const start = env.balance(d.depositor, d.assets).number();
4376 auto tx = d.vault.clawback(
4377 {.issuer = d.issuer,
4379 .holder = d.depositor,
4384 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4386 env.balance(d.depositor, d.assets) ==
4389 env.balance(d.vaultAccount, d.assets) ==
4392 env.balance(d.vaultAccount, d.shares) ==
4397 testcase(
"Scale clawback tiny amount");
4399 auto const start = env.balance(d.depositor, d.assets).number();
4400 auto tx = d.vault.clawback(
4401 {.issuer = d.issuer,
4403 .holder = d.depositor,
4408 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4410 env.balance(d.depositor, d.assets) ==
4413 env.balance(d.vaultAccount, d.assets) ==
4416 env.balance(d.vaultAccount, d.shares) ==
4423 env.balance(d.vaultAccount, d.assets).number();
4424 d.peek([](
SLE& vault,
auto&) ->
bool {
4425 vault[sfAssetsAvailable] =
Number(5);
4433 tx = d.vault.clawback(
4434 {.issuer = d.issuer,
4436 .holder = d.depositor,
4437 .amount =
STAmount(d.asset, rest)});
4440 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4442 env.balance(d.vaultAccount, d.assets).number() == 0);
4444 env.balance(d.vaultAccount, d.shares).number() == 0);
4452 using namespace test::jtx;
4455 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4456 Account
const owner{
"owner"};
4457 Account
const issuer{
"issuer"};
4459 env.fund(XRP(1000), issuer, owner);
4463 env.trust(asset(1000), owner);
4464 env(pay(issuer, owner, asset(200)));
4467 auto const sequence = env.seq(owner);
4468 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
4474 auto tx1 = vault.deposit(
4475 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
4478 auto tx2 = vault.set({.owner = owner, .id = keylet.
key});
4479 tx2[sfAssetsMaximum] = asset(1000).number();
4484 auto const sleVault = [&env, keylet = keylet,
this]() {
4485 auto const vault = env.le(keylet);
4486 BEAST_EXPECT(vault !=
nullptr);
4490 auto const check = [&, keylet = keylet, sle = sleVault,
this](
4493 BEAST_EXPECT(vault.isObject());
4495 constexpr auto checkString =
4497 return node.isMember(field.fieldName) &&
4498 node[field.fieldName].isString() &&
4499 node[field.fieldName] == v;
4501 constexpr auto checkObject =
4503 return node.isMember(field.fieldName) &&
4504 node[field.fieldName].isObject() &&
4505 node[field.fieldName] == v;
4507 constexpr auto checkInt =
4508 [](
auto& node,
SField const& field,
int v) ->
bool {
4509 return node.isMember(field.fieldName) &&
4510 ((node[field.fieldName].isInt() &&
4511 node[field.fieldName] ==
Json::Int(v)) ||
4512 (node[field.fieldName].isUInt() &&
4516 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
4517 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(keylet.
key));
4518 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
4522 checkString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
4524 checkObject(vault, sfAsset,
to_json(sle->at(sfAsset))));
4525 BEAST_EXPECT(checkString(vault, sfAssetsAvailable,
"50"));
4526 BEAST_EXPECT(checkString(vault, sfAssetsMaximum,
"1000"));
4527 BEAST_EXPECT(checkString(vault, sfAssetsTotal,
"50"));
4528 BEAST_EXPECT(checkString(vault, sfLossUnrealized,
"0"));
4530 auto const strShareID =
strHex(sle->at(sfShareMPTID));
4531 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
4532 BEAST_EXPECT(checkString(vault, sfOwner,
toBase58(owner.id())));
4533 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
4534 BEAST_EXPECT(checkInt(
4537 if (issuance.isObject())
4540 issuance[
"LedgerEntryType"].asString() ==
4543 issuance[jss::mpt_issuance_id].asString() == strShareID);
4544 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
4545 BEAST_EXPECT(checkInt(
4550 checkString(issuance, sfOutstandingAmount,
"50000000"));
4555 testcase(
"RPC ledger_entry selected by key");
4557 jvParams[jss::ledger_index] = jss::validated;
4558 jvParams[jss::vault] =
strHex(keylet.
key);
4559 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4561 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4562 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4563 check(jvVault[jss::result][jss::node]);
4567 testcase(
"RPC ledger_entry selected by owner and seq");
4569 jvParams[jss::ledger_index] = jss::validated;
4570 jvParams[jss::vault][jss::owner] = owner.human();
4571 jvParams[jss::vault][jss::seq] = sequence;
4572 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4574 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4575 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4576 check(jvVault[jss::result][jss::node]);
4580 testcase(
"RPC ledger_entry cannot find vault by key");
4582 jvParams[jss::ledger_index] = jss::validated;
4584 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4586 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4590 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
4592 jvParams[jss::ledger_index] = jss::validated;
4593 jvParams[jss::vault][jss::owner] = issuer.human();
4594 jvParams[jss::vault][jss::seq] = 1'000'000;
4595 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4597 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4601 testcase(
"RPC ledger_entry malformed key");
4603 jvParams[jss::ledger_index] = jss::validated;
4604 jvParams[jss::vault] = 42;
4605 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4607 jvVault[jss::result][jss::error].asString() ==
4608 "malformedRequest");
4612 testcase(
"RPC ledger_entry malformed owner");
4614 jvParams[jss::ledger_index] = jss::validated;
4615 jvParams[jss::vault][jss::owner] = 42;
4616 jvParams[jss::vault][jss::seq] = sequence;
4617 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4619 jvVault[jss::result][jss::error].asString() ==
4624 testcase(
"RPC ledger_entry malformed seq");
4626 jvParams[jss::ledger_index] = jss::validated;
4627 jvParams[jss::vault][jss::owner] = issuer.human();
4628 jvParams[jss::vault][jss::seq] =
"foo";
4629 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4631 jvVault[jss::result][jss::error].asString() ==
4632 "malformedRequest");
4636 testcase(
"RPC ledger_entry negative seq");
4638 jvParams[jss::ledger_index] = jss::validated;
4639 jvParams[jss::vault][jss::owner] = issuer.human();
4640 jvParams[jss::vault][jss::seq] = -1;
4641 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4643 jvVault[jss::result][jss::error].asString() ==
4644 "malformedRequest");
4648 testcase(
"RPC ledger_entry oversized seq");
4650 jvParams[jss::ledger_index] = jss::validated;
4651 jvParams[jss::vault][jss::owner] = issuer.human();
4652 jvParams[jss::vault][jss::seq] = 1e20;
4653 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4655 jvVault[jss::result][jss::error].asString() ==
4656 "malformedRequest");
4660 testcase(
"RPC ledger_entry bool seq");
4662 jvParams[jss::ledger_index] = jss::validated;
4663 jvParams[jss::vault][jss::owner] = issuer.human();
4664 jvParams[jss::vault][jss::seq] =
true;
4665 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4667 jvVault[jss::result][jss::error].asString() ==
4668 "malformedRequest");
4675 jvParams[jss::account] = owner.human();
4676 jvParams[jss::type] = jss::vault;
4678 "json",
"account_objects",
to_string(jvParams))[jss::result];
4680 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
4681 check(jv[jss::account_objects][0u]);
4688 jvParams[jss::ledger_index] = jss::validated;
4689 jvParams[jss::binary] =
false;
4690 jvParams[jss::type] = jss::vault;
4692 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
4693 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
4694 check(jv[jss::result][jss::state][0u]);
4698 testcase(
"RPC vault_info command line");
4700 env.rpc(
"vault_info",
strHex(keylet.
key),
"validated");
4702 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4703 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4705 jv[jss::result][jss::vault],
4706 jv[jss::result][jss::vault][jss::shares]);
4712 jvParams[jss::ledger_index] = jss::validated;
4713 jvParams[jss::vault_id] =
strHex(keylet.
key);
4714 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4716 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4717 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4719 jv[jss::result][jss::vault],
4720 jv[jss::result][jss::vault][jss::shares]);
4724 testcase(
"RPC vault_info invalid vault_id");
4726 jvParams[jss::ledger_index] = jss::validated;
4727 jvParams[jss::vault_id] =
"foobar";
4728 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4730 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4734 testcase(
"RPC vault_info json invalid index");
4736 jvParams[jss::ledger_index] = jss::validated;
4737 jvParams[jss::vault_id] = 0;
4738 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4740 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4744 testcase(
"RPC vault_info json by owner and sequence");
4746 jvParams[jss::ledger_index] = jss::validated;
4747 jvParams[jss::owner] = owner.human();
4748 jvParams[jss::seq] = sequence;
4749 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4751 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4752 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4754 jv[jss::result][jss::vault],
4755 jv[jss::result][jss::vault][jss::shares]);
4759 testcase(
"RPC vault_info json malformed sequence");
4761 jvParams[jss::ledger_index] = jss::validated;
4762 jvParams[jss::owner] = owner.human();
4763 jvParams[jss::seq] =
"foobar";
4764 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4766 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4770 testcase(
"RPC vault_info json invalid sequence");
4772 jvParams[jss::ledger_index] = jss::validated;
4773 jvParams[jss::owner] = owner.human();
4774 jvParams[jss::seq] = 0;
4775 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4777 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4781 testcase(
"RPC vault_info json negative sequence");
4783 jvParams[jss::ledger_index] = jss::validated;
4784 jvParams[jss::owner] = owner.human();
4785 jvParams[jss::seq] = -1;
4786 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4788 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4792 testcase(
"RPC vault_info json oversized sequence");
4794 jvParams[jss::ledger_index] = jss::validated;
4795 jvParams[jss::owner] = owner.human();
4796 jvParams[jss::seq] = 1e20;
4797 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4799 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4803 testcase(
"RPC vault_info json bool sequence");
4805 jvParams[jss::ledger_index] = jss::validated;
4806 jvParams[jss::owner] = owner.human();
4807 jvParams[jss::seq] =
true;
4808 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4810 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4814 testcase(
"RPC vault_info json malformed owner");
4816 jvParams[jss::ledger_index] = jss::validated;
4817 jvParams[jss::owner] =
"foobar";
4818 jvParams[jss::seq] = sequence;
4819 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4821 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4825 testcase(
"RPC vault_info json invalid combination only owner");
4827 jvParams[jss::ledger_index] = jss::validated;
4828 jvParams[jss::owner] = owner.human();
4829 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4831 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4835 testcase(
"RPC vault_info json invalid combination only seq");
4837 jvParams[jss::ledger_index] = jss::validated;
4838 jvParams[jss::seq] = sequence;
4839 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4841 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4845 testcase(
"RPC vault_info json invalid combination seq vault_id");
4847 jvParams[jss::ledger_index] = jss::validated;
4848 jvParams[jss::vault_id] =
strHex(keylet.
key);
4849 jvParams[jss::seq] = sequence;
4850 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4852 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4856 testcase(
"RPC vault_info json invalid combination owner vault_id");
4858 jvParams[jss::ledger_index] = jss::validated;
4859 jvParams[jss::vault_id] =
strHex(keylet.
key);
4860 jvParams[jss::owner] = owner.human();
4861 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4863 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4868 "RPC vault_info json invalid combination owner seq "
4871 jvParams[jss::ledger_index] = jss::validated;
4872 jvParams[jss::vault_id] =
strHex(keylet.
key);
4873 jvParams[jss::seq] = sequence;
4874 jvParams[jss::owner] = owner.human();
4875 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4877 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4881 testcase(
"RPC vault_info json no input");
4883 jvParams[jss::ledger_index] = jss::validated;
4884 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4886 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4890 testcase(
"RPC vault_info command line invalid index");
4891 Json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
4892 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
4896 testcase(
"RPC vault_info command line invalid index");
4897 Json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
4899 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4903 testcase(
"RPC vault_info command line invalid index");
4907 jv[jss::result][jss::error].asString() ==
"entryNotFound");
4911 testcase(
"RPC vault_info command line invalid ledger");
4914 jv[jss::result][jss::error].asString() ==
"lgrNotFound");
4921 using namespace test::jtx;
4923 Env env(*
this, testable_amendments());
4924 Account alice{
"alice"};
4926 Account carol{
"carol"};
4933 auto const xrpBalance =
4937 if (BEAST_EXPECT(sle !=
nullptr))
4938 return sle->getFieldAmount(sfBalance).xrp().drops();
4942 auto testCase = [&,
this](
auto test, CaseArgs args = {}) {
4943 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4948 env.fund(XRP(10000), alice);
4949 env.fund(XRP(20000), bob);
4950 env.fund(XRP(30000), carol);
4964 test(env, vault, args.asset);
4967 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4968 testcase(
"delegated vault creation");
4969 auto startBalance = xrpBalance(env, carol);
4970 if (!BEAST_EXPECT(startBalance.has_value()))
4973 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4974 env(tx, delegate::as(alice));
4976 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
4979 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4980 testcase(
"delegated deposit and withdrawal");
4981 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4985 auto const amount = 1513;
4986 auto const baseFee = env.current()->fees().base;
4988 auto startBalance = xrpBalance(env, carol);
4989 if (!BEAST_EXPECT(startBalance.has_value()))
4993 {.depositor = carol,
4995 .amount = asset(amount)});
4996 env(tx, delegate::as(alice));
4998 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
5000 tx = vault.withdraw(
5001 {.depositor = carol,
5003 .amount = asset(amount - 1)});
5004 env(tx, delegate::as(alice));
5006 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
5008 tx = vault.withdraw(
5009 {.depositor = carol, .id = keylet.
key, .amount = asset(1)});
5012 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
5015 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
5016 testcase(
"delegated withdrawal same as base fee and deletion");
5017 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
5021 auto const amount = 25537;
5022 auto const baseFee = env.current()->fees().base;
5024 auto startBalance = xrpBalance(env, carol);
5025 if (!BEAST_EXPECT(startBalance.has_value()))
5029 {.depositor = carol,
5031 .amount = asset(amount)});
5035 xrpBalance(env, carol) == *startBalance - amount - baseFee);
5037 tx = vault.withdraw(
5038 {.depositor = carol,
5040 .amount = asset(baseFee)});
5041 env(tx, delegate::as(alice));
5043 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
5045 tx = vault.withdraw(
5046 {.depositor = carol,
5048 .amount = asset(amount - baseFee)});
5049 env(tx, delegate::as(alice));
5051 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
5053 tx = vault.del({.owner = carol, .id = keylet.
key});
5054 env(tx, delegate::as(alice));