64 using namespace test::jtx;
65 Account issuer{
"issuer"};
66 Account owner{
"owner"};
67 Account depositor{
"depositor"};
68 Account charlie{
"charlie"};
71 auto const testSequence = [&,
this](
76 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
77 tx[sfData] =
"AFEED00E";
78 tx[sfAssetsMaximum] = asset(100).number();
81 BEAST_EXPECT(env.le(keylet));
84 auto const [share, vaultAccount] =
89 auto const vault = env.le(keylet);
90 BEAST_EXPECT(vault !=
nullptr);
91 if (asset.raw().holds<
Issue>() && !asset.raw().
native())
92 BEAST_EXPECT(vault->at(sfScale) == 6);
94 BEAST_EXPECT(vault->at(sfScale) == 0);
97 BEAST_EXPECT(shares !=
nullptr);
98 if (asset.raw().holds<
Issue>() && !asset.raw().
native())
99 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
101 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
104 Account(
"vault", vault->at(sfAccount))};
106 auto const shares = share.raw().get<
MPTIssue>();
107 env.memoize(vaultAccount);
110 Account alice{
"alice"};
111 Account erin{
"erin"};
112 env.fund(XRP(1000), alice, erin);
117 testcase(prefix +
" fail to deposit more than assets held");
118 auto tx = vault.deposit(
119 {.depositor = depositor,
121 .amount = asset(10000)});
127 testcase(prefix +
" deposit non-zero amount");
128 auto tx = vault.deposit(
129 {.depositor = depositor,
131 .amount = asset(50)});
135 env.balance(depositor, shares) == share(50 * scale));
139 testcase(prefix +
" deposit non-zero amount again");
140 auto tx = vault.deposit(
141 {.depositor = depositor,
143 .amount = asset(50)});
147 env.balance(depositor, shares) == share(100 * scale));
151 testcase(prefix +
" fail to delete non-empty vault");
152 auto tx = vault.del({.owner = owner, .id = keylet.
key});
158 testcase(prefix +
" fail to update because wrong owner");
159 auto tx = vault.set({.owner = issuer, .id = keylet.
key});
160 tx[sfAssetsMaximum] = asset(50).number();
167 prefix +
" fail to set maximum lower than current amount");
168 auto tx = vault.set({.owner = owner, .id = keylet.
key});
169 tx[sfAssetsMaximum] = asset(50).number();
175 testcase(prefix +
" set maximum higher than current amount");
176 auto tx = vault.set({.owner = owner, .id = keylet.
key});
177 tx[sfAssetsMaximum] = asset(150).number();
183 testcase(prefix +
" set maximum is idempotent, set it again");
184 auto tx = vault.set({.owner = owner, .id = keylet.
key});
185 tx[sfAssetsMaximum] = asset(150).number();
192 auto tx = vault.set({.owner = owner, .id = keylet.
key});
199 testcase(prefix +
" fail to set domain on public vault");
200 auto tx = vault.set({.owner = owner, .id = keylet.
key});
207 testcase(prefix +
" fail to deposit more than maximum");
208 auto tx = vault.deposit(
209 {.depositor = depositor,
211 .amount = asset(100)});
217 testcase(prefix +
" reset maximum to zero i.e. not enforced");
218 auto tx = vault.set({.owner = owner, .id = keylet.
key});
219 tx[sfAssetsMaximum] = asset(0).number();
225 testcase(prefix +
" fail to withdraw more than assets held");
226 auto tx = vault.withdraw(
227 {.depositor = depositor,
229 .amount = asset(1000)});
235 testcase(prefix +
" deposit some more");
236 auto tx = vault.deposit(
237 {.depositor = depositor,
239 .amount = asset(100)});
243 env.balance(depositor, shares) == share(200 * scale));
247 testcase(prefix +
" clawback some");
250 auto tx = vault.clawback(
254 .amount = asset(10)});
257 if (!asset.raw().native())
260 env.balance(depositor, shares) == share(190 * scale));
268 auto tx = vault.clawback(
269 {.issuer = issuer, .id = keylet.
key, .holder = depositor});
272 if (!asset.raw().native())
274 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
277 auto tx = vault.clawback(
281 .amount = asset(10)});
287 auto tx = vault.withdraw(
288 {.depositor = depositor,
290 .amount = asset(10)});
297 if (!asset.raw().native())
299 testcase(prefix +
" deposit again");
300 auto tx = vault.deposit(
301 {.depositor = depositor,
303 .amount = asset(200)});
307 env.balance(depositor, shares) == share(200 * scale));
311 testcase(prefix +
" deposit/withdrawal same or less than fee");
312 auto const amount = env.current()->fees().base;
314 auto tx = vault.deposit(
315 {.depositor = depositor,
322 {.depositor = depositor,
329 {.depositor = depositor,
337 {.depositor = depositor,
340 tx[sfDestination] = charlie.human();
345 {.depositor = depositor,
347 .amount = amount - 1});
352 {.depositor = depositor,
354 .amount = amount - 1});
361 prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
362 auto tx = vault.withdraw(
363 {.depositor = depositor,
365 .amount = asset(100)});
366 tx[sfDestination] = alice.human();
372 testcase(prefix +
" fail to withdraw to zero destination");
373 auto tx = vault.withdraw(
374 {.depositor = depositor,
376 .amount = asset(1000)});
377 tx[sfDestination] =
"0";
382 if (!asset.raw().native())
385 prefix +
" fail to withdraw to 3rd party no authorization");
386 auto tx = vault.withdraw(
387 {.depositor = depositor,
389 .amount = asset(100)});
390 tx[sfDestination] = erin.human();
399 " fail to withdraw to 3rd party lsfRequireDestTag");
400 auto tx = vault.withdraw(
401 {.depositor = depositor,
403 .amount = asset(100)});
404 tx[sfDestination] = dave.human();
410 testcase(prefix +
" withdraw to 3rd party lsfRequireDestTag");
411 auto tx = vault.withdraw(
412 {.depositor = depositor,
414 .amount = asset(50)});
415 tx[sfDestination] = dave.human();
416 tx[sfDestinationTag] =
"0";
422 testcase(prefix +
" deposit again");
423 auto tx = vault.deposit(
424 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
430 testcase(prefix +
" fail to withdraw lsfRequireDestTag");
431 auto tx = vault.withdraw(
432 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
438 testcase(prefix +
" withdraw with tag");
439 auto tx = vault.withdraw(
440 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
441 tx[sfDestinationTag] =
"0";
447 testcase(prefix +
" withdraw to authorized 3rd party");
448 auto tx = vault.withdraw(
449 {.depositor = depositor,
451 .amount = asset(50)});
452 tx[sfDestination] = charlie.human();
456 env.balance(depositor, shares) == share(100 * scale));
460 testcase(prefix +
" withdraw to issuer");
461 auto tx = vault.withdraw(
462 {.depositor = depositor,
464 .amount = asset(50)});
465 tx[sfDestination] = issuer.human();
469 env.balance(depositor, shares) == share(50 * scale));
472 if (!asset.raw().native())
474 testcase(prefix +
" issuer deposits");
475 auto tx = vault.deposit(
476 {.depositor = issuer,
478 .amount = asset(10)});
481 BEAST_EXPECT(env.balance(issuer, shares) == share(10 * scale));
483 testcase(prefix +
" issuer withdraws");
485 {.depositor = issuer,
487 .amount = share(10 * scale)});
490 BEAST_EXPECT(env.balance(issuer, shares) == share(0 * scale));
494 testcase(prefix +
" withdraw remaining assets");
495 auto tx = vault.withdraw(
496 {.depositor = depositor,
498 .amount = asset(50)});
501 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
503 if (!asset.raw().native())
505 auto tx = vault.clawback(
509 .amount = asset(0)});
515 auto tx = vault.withdraw(
516 {.depositor = depositor,
518 .amount = share(10)});
524 if (!asset.raw().native() && asset.raw().holds<
Issue>())
526 testcase(prefix +
" temporary authorization for 3rd party");
527 env(trust(erin, asset(1000)));
528 env(trust(issuer, asset(0), erin,
tfSetfAuth));
529 env(pay(issuer, erin, asset(10)));
532 auto tx = vault.deposit(
533 {.depositor = erin, .id = keylet.
key, .amount = asset(10)});
537 auto tx = pay(erin, depositor, share(10 * scale));
545 {.depositor = depositor,
547 .amount = asset(1)}));
554 testcase(prefix +
" withdraw to authorized 3rd party");
557 {.depositor = depositor,
559 .amount = asset(10)});
560 tx[sfDestination] = erin.human();
565 env(pay(erin, issuer, asset(10)));
568 testcase(prefix +
" fail to pay to unauthorized 3rd party");
569 env(trust(erin, asset(0)));
573 env(pay(depositor, erin, share(1)), ter{
tecNO_LINE});
578 {.depositor = depositor,
580 .amount = asset(1)});
586 testcase(prefix +
" fail to delete because wrong owner");
587 auto tx = vault.del({.owner = issuer, .id = keylet.
key});
593 testcase(prefix +
" delete empty vault");
594 auto tx = vault.del({.owner = owner, .id = keylet.
key});
597 BEAST_EXPECT(!env.le(keylet));
601 auto testCases = [&,
this](
604 Env env{*
this, testable_amendments() | featureSingleAssetVault};
607 env.fund(XRP(1000), issuer, owner, depositor, charlie, dave);
617 testSequence(prefix, env, vault, asset);
624 testCases(
"IOU", [&](Env& env) ->
Asset {
626 env(trust(owner, asset(1000)));
627 env(trust(depositor, asset(1000)));
628 env(trust(charlie, asset(1000)));
629 env(trust(dave, asset(1000)));
630 env(trust(issuer, asset(0), owner,
tfSetfAuth));
631 env(trust(issuer, asset(0), depositor,
tfSetfAuth));
632 env(trust(issuer, asset(0), charlie,
tfSetfAuth));
633 env(trust(issuer, asset(0), dave,
tfSetfAuth));
634 env(pay(issuer, depositor, asset(1000)));
639 testCases(
"MPT", [&](Env& env) ->
Asset {
640 MPTTester mptt{env, issuer, mptInitNoFund};
644 mptt.authorize({.account = depositor});
645 mptt.authorize({.account = charlie});
646 mptt.authorize({.account = dave});
647 env(pay(issuer, depositor, asset(1000)));
656 using namespace test::jtx;
661 testable_amendments() | featureSingleAssetVault;
664 auto testCase = [&,
this](
667 Account
const& issuer,
668 Account
const& owner,
671 CaseArgs args = {}) {
672 Env env{*
this, args.features};
673 Account issuer{
"issuer"};
674 Account owner{
"owner"};
676 env.fund(XRP(1000), issuer, owner);
684 env(trust(owner, asset(1000)));
685 env(trust(issuer, asset(0), owner,
tfSetfAuth));
686 env(pay(issuer, owner, asset(1000)));
689 test(env, issuer, owner, asset, vault);
694 Account
const& issuer,
695 Account
const& owner,
698 testcase(
"disabled single asset vault");
701 vault.create({.owner = owner, .asset = asset});
705 auto tx = vault.set({.owner = owner, .id = keylet.
key});
710 auto tx = vault.deposit(
713 .amount = asset(10)});
718 auto tx = vault.withdraw(
721 .amount = asset(10)});
726 auto tx = vault.clawback(
730 .amount = asset(10)});
735 auto tx = vault.del({.owner = owner, .id = keylet.
key});
739 {.features = testable_amendments() - featureSingleAssetVault});
741 testCase([&](Env& env,
742 Account
const& issuer,
743 Account
const& owner,
748 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
753 auto tx = vault.set({.owner = owner, .id = keylet.
key});
759 auto tx = vault.deposit(
762 .amount = asset(10)});
768 auto tx = vault.withdraw(
771 .amount = asset(10)});
777 auto tx = vault.clawback(
781 .amount = asset(10)});
787 auto tx = vault.del({.owner = owner, .id = keylet.
key});
793 testCase([&](Env& env,
794 Account
const& issuer,
795 Account
const& owner,
800 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
805 auto tx = vault.set({.owner = owner, .id = keylet.
key});
811 auto tx = vault.deposit(
814 .amount = asset(10)});
820 auto tx = vault.withdraw(
823 .amount = asset(10)});
829 auto tx = vault.clawback(
833 .amount = asset(10)});
839 auto tx = vault.del({.owner = owner, .id = keylet.
key});
848 Account
const& owner,
851 testcase(
"disabled permissioned domain");
854 vault.create({.owner = owner, .asset =
xrpIssue()});
859 auto tx = vault.set({.owner = owner, .id = keylet.
key});
865 auto tx = vault.set({.owner = owner, .id = keylet.
key});
866 tx[sfDomainID] =
"0";
870 {.features = (testable_amendments() | featureSingleAssetVault) -
871 featurePermissionedDomains});
873 testCase([&](Env& env,
874 Account
const& issuer,
875 Account
const& owner,
881 vault.create({.owner = owner, .asset =
xrpIssue()});
884 auto tx = vault.set({
892 auto tx = vault.deposit(
895 .amount = asset(10)});
900 auto tx = vault.withdraw(
903 .amount = asset(10)});
908 auto tx = vault.clawback(
912 .amount = asset(10)});
917 auto tx = vault.del({
925 testCase([&](Env& env,
926 Account
const& issuer,
927 Account
const& owner,
932 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
935 auto tx = vault.clawback(
939 .amount = asset(10)});
944 testCase([&](Env& env,
946 Account
const& owner,
949 testcase(
"withdraw to bad destination");
951 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
954 auto tx = vault.withdraw(
957 .amount = asset(10)});
958 tx[jss::Destination] =
"0";
963 testCase([&](Env& env,
965 Account
const& owner,
972 vault.create({.owner = owner, .asset = asset});
979 vault.create({.owner = owner, .asset = asset});
987 vault.create({.owner = owner, .asset = asset});
991 auto const sleVault = env.le(keylet);
992 BEAST_EXPECT(sleVault);
993 BEAST_EXPECT((*sleVault)[sfScale] == 18);
998 vault.create({.owner = owner, .asset = asset});
1002 auto const sleVault = env.le(keylet);
1003 BEAST_EXPECT(sleVault);
1004 BEAST_EXPECT((*sleVault)[sfScale] == 0);
1009 vault.create({.owner = owner, .asset = asset});
1012 auto const sleVault = env.le(keylet);
1013 BEAST_EXPECT(sleVault);
1014 BEAST_EXPECT((*sleVault)[sfScale] == 6);
1018 testCase([&](Env& env,
1020 Account
const& owner,
1023 testcase(
"create or set invalid data");
1025 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1041 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1047 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1054 testCase([&](Env& env,
1056 Account
const& owner,
1061 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1064 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1069 testCase([&](Env& env,
1071 Account
const& owner,
1074 testcase(
"create with invalid metadata");
1076 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1080 tx[sfMPTokenMetadata] =
"";
1093 testCase([&](Env& env,
1095 Account
const& owner,
1100 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1103 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1109 testCase([&](Env& env,
1111 Account
const& owner,
1114 testcase(
"invalid deposit amount");
1116 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1119 auto tx = vault.deposit(
1120 {.depositor = owner,
1127 auto tx = vault.deposit(
1128 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1133 testCase([&](Env& env,
1135 Account
const& owner,
1138 testcase(
"invalid set immutable flag");
1140 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1143 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1149 testCase([&](Env& env,
1151 Account
const& owner,
1154 testcase(
"invalid withdraw amount");
1156 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1159 auto tx = vault.withdraw(
1160 {.depositor = owner,
1167 auto tx = vault.withdraw(
1168 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1173 testCase([&](Env& env,
1174 Account
const& issuer,
1175 Account
const& owner,
1180 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};
1754 mptt.authorize({.account = owner});
1755 mptt.authorize({.account = depositor});
1756 if (args.requireAuth)
1758 mptt.authorize({.account = issuer, .holder = owner});
1759 mptt.authorize({.account = issuer, .holder = depositor});
1762 env(pay(issuer, depositor, asset(1000)));
1765 test(env, issuer, owner, depositor, asset, vault, mptt);
1770 Account
const& issuer,
1771 Account
const& owner,
1772 Account
const& depositor,
1776 testcase(
"MPT nothing to clawback from");
1777 auto tx = vault.clawback(
1780 .holder = depositor,
1781 .amount = asset(10)});
1787 Account
const& issuer,
1788 Account
const& owner,
1789 Account
const& depositor,
1793 testcase(
"MPT global lock blocks create");
1794 mptt.set({.account = issuer, .flags =
tfMPTLock});
1795 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1801 Account
const& issuer,
1802 Account
const& owner,
1803 Account
const& depositor,
1807 testcase(
"MPT global lock blocks deposit");
1808 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1812 mptt.set({.account = issuer, .flags =
tfMPTLock});
1816 {.depositor = depositor,
1818 .amount = asset(100)});
1823 tx = vault.del({.owner = owner, .id = keylet.
key});
1829 Account
const& issuer,
1830 Account
const& owner,
1831 Account
const& depositor,
1835 testcase(
"MPT global lock blocks withdrawal");
1836 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1840 {.depositor = depositor,
1842 .amount = asset(100)});
1848 auto v = env.le(keylet);
1850 MPTID share = (*v)[sfShareMPTID];
1852 BEAST_EXPECT(issuance);
1853 Number outstandingShares = issuance->at(sfOutstandingAmount);
1854 BEAST_EXPECT(outstandingShares == 100);
1856 mptt.set({.account = issuer, .flags =
tfMPTLock});
1859 tx = vault.withdraw(
1860 {.depositor = depositor,
1862 .amount = asset(100)});
1865 tx[sfDestination] = issuer.human();
1869 tx = vault.clawback(
1872 .holder = depositor,
1873 .amount = asset(0)});
1879 BEAST_EXPECT(mptSle ==
nullptr);
1882 tx = vault.del({.owner = owner, .id = keylet.
key});
1888 Account
const& issuer,
1889 Account
const& owner,
1890 Account
const& depositor,
1894 testcase(
"MPT only issuer can clawback");
1896 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1901 {.depositor = depositor,
1903 .amount = asset(100)});
1908 auto tx = vault.clawback(
1909 {.issuer = owner, .id = keylet.
key, .holder = depositor});
1917 Account
const& issuer,
1918 Account
const& owner,
1919 Account
const& depositor,
1923 testcase(
"MPT depositor without MPToken, auth required");
1926 vault.create({.owner = owner, .asset = asset});
1931 {.depositor = depositor,
1933 .amount = asset(1000)});
1943 auto const mptoken =
1945 auto const sleMPT1 = env.le(mptoken);
1946 BEAST_EXPECT(sleMPT1 ==
nullptr);
1948 tx = vault.withdraw(
1949 {.depositor = depositor,
1951 .amount = asset(100)});
1955 auto const sleMPT2 = env.le(mptoken);
1956 BEAST_EXPECT(sleMPT2 ==
nullptr);
1961 Account charlie{
"charlie"};
1962 env.fund(XRP(1000), charlie);
1965 tx = vault.withdraw(
1966 {.depositor = depositor,
1968 .amount = asset(100)});
1969 tx[sfDestination] = charlie.human();
1973 {.requireAuth =
true});
1978 Account
const& issuer,
1979 Account
const& owner,
1980 Account
const& depositor,
1984 testcase(
"MPT depositor without MPToken, no auth required");
1987 vault.create({.owner = owner, .asset = asset});
1990 auto v = env.le(keylet);
1994 {.depositor = depositor,
1996 .amount = asset(1000)});
2006 auto const mptoken =
2008 auto const sleMPT1 = env.le(mptoken);
2009 BEAST_EXPECT(sleMPT1 ==
nullptr);
2011 tx = vault.withdraw(
2012 {.depositor = depositor,
2014 .amount = asset(100)});
2018 auto const sleMPT2 = env.le(mptoken);
2019 BEAST_EXPECT(sleMPT2 !=
nullptr);
2020 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
2029 auto const mptoken =
2031 auto const sleMPT1 = env.le(mptoken);
2032 BEAST_EXPECT(sleMPT1 ==
nullptr);
2034 tx = vault.withdraw(
2035 {.depositor = depositor,
2037 .amount = asset(100)});
2038 tx[sfDestination] = owner.human();
2042 auto const sleMPT2 = env.le(mptoken);
2043 BEAST_EXPECT(sleMPT2 ==
nullptr);
2046 {.requireAuth =
false});
2049 Env env{*
this, testable_amendments()};
2051 env.current()->fees().accountReserve(0).drops() /
2053 env.current()->fees().increment.drops() /
2060 Account
const& issuer,
2061 Account
const& owner,
2062 Account
const& depositor,
2066 testcase(
"MPT failed reserve to re-create MPToken");
2069 vault.create({.owner = owner, .asset = asset});
2072 auto v = env.le(keylet);
2075 env(pay(depositor, owner, asset(1000)));
2079 {.depositor = owner,
2081 .amount = asset(1000)});
2091 auto const mptoken =
2093 auto const sleMPT = env.le(mptoken);
2094 BEAST_EXPECT(sleMPT ==
nullptr);
2097 tx = vault.withdraw(
2098 {.depositor = owner,
2100 .amount = asset(100)});
2104 env(pay(depositor, owner, XRP(incReserve)));
2112 {.requireAuth =
false,
2113 .initialXRP = acctReserve + incReserve * 4 - 1});
2117 Account
const& issuer,
2118 Account
const& owner,
2119 Account
const& depositor,
2125 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2130 {.depositor = depositor,
2132 .amount = asset(1000)});
2137 auto tx = vault.clawback(
2140 .holder = depositor,
2141 .amount = asset(0)});
2145 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
2150 vault.create({.owner = depositor, .asset = asset});
2155 auto tx = vault.deposit(
2156 {.depositor = depositor,
2158 .amount = asset(10)});
2163 auto tx = vault.withdraw(
2164 {.depositor = depositor,
2166 .amount = asset(10)});
2171 auto tx = vault.clawback(
2174 .holder = depositor,
2175 .amount = asset(0)});
2179 env(vault.del({.owner = owner, .id = keylet.key}));
2184 Account
const& issuer,
2185 Account
const& owner,
2186 Account
const& depositor,
2190 testcase(
"MPT vault owner can receive shares unless unauthorized");
2192 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2197 {.depositor = depositor,
2199 .amount = asset(1000)});
2204 auto const vault = env.le(keylet);
2205 return vault->at(sfShareMPTID);
2211 env(pay(depositor, owner, shares(1)));
2214 tx = vault.withdraw(
2215 {.depositor = owner,
2217 .amount = shares(1)});
2222 env(pay(depositor, owner, shares(1)));
2225 tx = vault.clawback(
2229 .amount = asset(0)});
2234 env(pay(depositor, owner, shares(1)));
2238 env(pay(owner, depositor, shares(1)));
2244 jv[sfAccount] = owner.human();
2245 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
2247 jv[sfTransactionType] = jss::MPTokenAuthorize;
2253 tx = pay(depositor, owner, shares(1));
2258 tx = vault.clawback(
2261 .holder = depositor,
2262 .amount = asset(0)});
2267 env(vault.del({.owner = owner, .id = keylet.key}));
2275 Account
const& issuer,
2276 Account
const& owner,
2277 Account
const& depositor,
2284 vault.create({.owner = owner, .asset = asset});
2289 {.depositor = depositor,
2291 .amount = asset(1000)});
2296 auto tx = vault.clawback(
2299 .holder = depositor,
2300 .amount = asset(0)});
2304 {.enableClawback =
false});
2308 Account
const& issuer,
2309 Account
const& owner,
2310 Account
const& depositor,
2315 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2319 {.depositor = depositor,
2321 .amount = asset(1000)});
2327 .holder = depositor,
2332 auto tx = vault.withdraw(
2333 {.depositor = depositor,
2335 .amount = asset(100)});
2339 tx[sfDestination] = issuer.human();
2343 tx[sfDestination] = owner.human();
2350 auto tx = vault.deposit(
2351 {.depositor = depositor,
2353 .amount = asset(100)});
2358 tx = vault.clawback(
2361 .holder = depositor,
2362 .amount = asset(800)});
2366 env(vault.del({.owner = owner, .id = keylet.key}));
2371 Account
const& issuer,
2372 Account
const& owner,
2373 Account
const& depositor,
2377 testcase(
"MPT lock of vault pseudo-account");
2378 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2382 auto const vaultAccount =
2383 [&env, keylet = keylet,
this]() ->
AccountID {
2384 auto const vault = env.le(keylet);
2385 BEAST_EXPECT(vault !=
nullptr);
2386 return vault->at(sfAccount);
2390 {.depositor = depositor,
2392 .amount = asset(100)});
2398 jv[jss::Account] = issuer.human();
2399 jv[sfMPTokenIssuanceID] =
2401 jv[jss::Holder] =
toBase58(vaultAccount);
2402 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
2410 {.depositor = depositor,
2412 .amount = asset(100)});
2415 tx = vault.withdraw(
2416 {.depositor = depositor,
2418 .amount = asset(100)});
2422 tx = vault.clawback(
2425 .holder = depositor,
2426 .amount = asset(100)});
2430 tx = vault.del({.owner = owner, .id = keylet.
key});
2437 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2438 Account owner{
"owner"};
2439 Account issuer{
"issuer"};
2440 env.fund(XRP(1000000), owner, issuer);
2444 MPTTester mptt{env, issuer, mptInitNoFund};
2448 mptt.authorize({.account = owner});
2449 mptt.authorize({.account = issuer, .holder = owner});
2451 env(pay(issuer, owner, asset(100)));
2452 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
2456 auto const shares = [&env, keylet = k1,
this]() ->
Asset {
2457 auto const vault = env.le(keylet);
2458 BEAST_EXPECT(vault !=
nullptr);
2459 return MPTIssue(vault->at(sfShareMPTID));
2462 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
2471 using namespace test::jtx;
2475 int initialXRP = 1000;
2483 Account
const& owner,
2484 Account
const& issuer,
2485 Account
const& charlie,
2490 CaseArgs args = {}) {
2491 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2492 Account
const owner{
"owner"};
2493 Account
const issuer{
"issuer"};
2494 Account
const charlie{
"charlie"};
2496 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
2501 env.trust(asset(1000), owner);
2502 env.trust(asset(1000), charlie);
2503 env(pay(issuer, owner, asset(200)));
2504 env(rate(issuer, args.transferRate));
2507 auto const vaultAccount =
2509 return Account(
"vault", env.le(keylet)->at(sfAccount));
2512 return env.le(keylet)->at(sfShareMPTID);
2528 Account
const& owner,
2529 Account
const& issuer,
2535 testcase(
"IOU cannot use different asset");
2538 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2544 auto tx = [&, account = vaultAccount(keylet)]() {
2546 jv[jss::Account] = issuer.human();
2548 auto& ja = jv[jss::LimitAmount] =
2550 ja[jss::issuer] =
toBase58(account);
2552 jv[jss::TransactionType] = jss::TrustSet;
2561 auto tx = vault.deposit(
2562 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2568 auto tx = vault.withdraw(
2569 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2574 env(vault.del({.owner = owner, .id = keylet.key}));
2580 Account
const& owner,
2581 Account
const& issuer,
2582 Account
const& charlie,
2587 testcase(
"IOU frozen trust line to vault account");
2589 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2594 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2597 Asset const share =
Asset(issuanceId(keylet));
2600 auto trustSet = [&, account = vaultAccount(keylet)]() {
2602 jv[jss::Account] = issuer.human();
2604 auto& ja = jv[jss::LimitAmount] =
2606 ja[jss::issuer] =
toBase58(account);
2608 jv[jss::TransactionType] = jss::TrustSet;
2620 auto tx = vault.deposit(
2621 {.depositor = owner,
2623 .amount = asset(80)});
2628 auto tx = vault.withdraw(
2629 {.depositor = owner,
2631 .amount = asset(100)});
2635 tx[sfDestination] = charlie.human();
2642 auto tx = vault.clawback(
2646 .amount = asset(50)});
2657 {.depositor = owner,
2659 .amount = share(50'000'000)}));
2661 env(vault.del({.owner = owner, .id = keylet.key}));
2668 Account
const& owner,
2669 Account
const& issuer,
2670 Account
const& charlie,
2675 testcase(
"IOU transfer fees not applied");
2678 vault.create({.owner = owner, .asset = asset});
2683 {.depositor = owner,
2685 .amount = asset(100)}));
2689 Asset const share =
Asset(issuanceId(keylet));
2692 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2694 env.balance(vaultAccount(keylet), issue) == asset(100));
2697 auto tx = vault.clawback(
2701 .amount = asset(50)});
2707 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2709 env.balance(vaultAccount(keylet), issue) == asset(50));
2712 {.depositor = owner,
2714 .amount = share(20'000'000)}));
2717 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2719 env.balance(vaultAccount(keylet), issue) == asset(30));
2722 auto tx = vault.withdraw(
2723 {.depositor = owner,
2725 .amount = share(30'000'000)});
2726 tx[sfDestination] = charlie.human();
2731 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2732 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2734 env.balance(vaultAccount(keylet), issue) == asset(0));
2736 env(vault.del({.owner = owner, .id = keylet.key}));
2739 CaseArgs{.transferRate = 1.25});
2743 Account
const& owner,
2744 Account
const& issuer,
2745 Account
const& charlie,
2750 testcase(
"IOU frozen trust line to depositor");
2752 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2757 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2762 auto tx = vault.withdraw(
2763 {.depositor = owner,
2765 .amount = asset(10)});
2766 tx[sfDestination] = charlie.human();
2769 env(withdrawToCharlie);
2776 auto const withdraw = vault.withdraw(
2777 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2786 auto tx = vault.deposit(
2787 {.depositor = owner,
2789 .amount = asset(10)});
2795 auto tx = vault.clawback(
2799 .amount = asset(0)});
2804 env(vault.del({.owner = owner, .id = keylet.key}));
2810 Account
const& owner,
2811 Account
const& issuer,
2812 Account
const& charlie,
2817 testcase(
"IOU no trust line to 3rd party");
2819 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2824 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2827 Account
const erin{
"erin"};
2828 env.fund(XRP(1000), erin);
2833 auto tx = vault.withdraw(
2834 {.depositor = owner,
2836 .amount = asset(10)});
2837 tx[sfDestination] = erin.human();
2845 Account
const& owner,
2846 Account
const& issuer,
2847 Account
const& charlie,
2852 testcase(
"IOU no trust line to depositor");
2854 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2859 env.trust(asset(0), owner);
2863 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2868 BEAST_EXPECT(trustline ==
nullptr);
2872 auto tx = vault.withdraw(
2873 {.depositor = owner,
2875 .amount = asset(10)});
2882 Env env{*
this, testable_amendments()};
2884 env.current()->fees().accountReserve(0).drops() /
2886 env.current()->fees().increment.drops() /
2893 Account
const& owner,
2894 Account
const& issuer,
2895 Account
const& charlie,
2900 testcase(
"IOU no trust line to depositor no reserve");
2902 vault.create({.owner = owner, .asset = asset});
2908 env.trust(asset(0), owner);
2912 {.depositor = owner,
2914 .amount = asset(200)}));
2919 BEAST_EXPECT(trustline ==
nullptr);
2922 tx = vault.withdraw(
2923 {.depositor = owner,
2925 .amount = asset(10)});
2929 env(pay(charlie, owner, XRP(incReserve)));
2936 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
2941 Account
const& owner,
2942 Account
const& issuer,
2943 Account
const& charlie,
2948 testcase(
"IOU no reserve for share MPToken");
2950 vault.create({.owner = owner, .asset = asset});
2954 env(pay(owner, charlie, asset(100)));
2958 env(ticket::create(charlie, 2));
2963 {.depositor = charlie,
2965 .amount = asset(100)});
2969 env(pay(issuer, charlie, XRP(incReserve)));
2976 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
2980 Account
const& owner,
2981 Account
const& issuer,
2982 Account
const& charlie,
2987 testcase(
"IOU frozen trust line to 3rd party");
2989 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2994 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2999 auto tx = vault.withdraw(
3000 {.depositor = owner,
3002 .amount = asset(10)});
3003 tx[sfDestination] = charlie.human();
3006 env(withdrawToCharlie);
3009 env(trust(issuer, asset(0), charlie,
tfSetFreeze));
3013 auto const withdraw = vault.withdraw(
3014 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
3026 .amount = asset(0)}));
3029 env(vault.del({.owner = owner, .id = keylet.key}));
3035 Account
const& owner,
3036 Account
const& issuer,
3037 Account
const& charlie,
3044 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3049 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3057 auto tx = vault.withdraw(
3058 {.depositor = owner,
3060 .amount = asset(10)});
3064 tx[sfDestination] = charlie.human();
3070 {.depositor = owner,
3072 .amount = asset(10)});
3082 .amount = asset(0)}));
3085 env(vault.del({.owner = owner, .id = keylet.key}));
3093 using namespace test::jtx;
3097 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3098 Account issuer{
"issuer"};
3099 Account owner{
"owner"};
3100 Account depositor{
"depositor"};
3101 Account charlie{
"charlie"};
3102 Account pdOwner{
"pdOwner"};
3103 Account credIssuer1{
"credIssuer1"};
3104 Account credIssuer2{
"credIssuer2"};
3122 env.trust(asset(1000), owner);
3123 env(pay(issuer, owner, asset(500)));
3124 env.trust(asset(1000), depositor);
3125 env(pay(issuer, depositor, asset(500)));
3126 env.trust(asset(1000), charlie);
3127 env(pay(issuer, charlie, asset(5)));
3130 auto [tx, keylet] = vault.create(
3134 BEAST_EXPECT(env.le(keylet));
3137 testcase(
"private vault owner can deposit");
3138 auto tx = vault.deposit(
3139 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
3144 testcase(
"private vault depositor not authorized yet");
3145 auto tx = vault.deposit(
3146 {.depositor = depositor,
3148 .amount = asset(50)});
3153 testcase(
"private vault cannot set non-existing domain");
3154 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3160 testcase(
"private vault set domainId");
3163 pdomain::Credentials
const credentials1{
3164 {.issuer = credIssuer1, .credType = credType}};
3166 env(pdomain::setTx(pdOwner, credentials1));
3167 auto const domainId1 = [&]() {
3169 return pdomain::getNewDomain(env.meta());
3172 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3183 pdomain::Credentials
const credentials{
3184 {.issuer = credIssuer1, .credType = credType},
3185 {.issuer = credIssuer2, .credType = credType}};
3187 env(pdomain::setTx(pdOwner, credentials));
3188 auto const domainId = [&]() {
3190 return pdomain::getNewDomain(env.meta());
3193 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3199 tx = vault.set({.owner = owner, .id = keylet.
key});
3207 testcase(
"private vault depositor still not authorized");
3208 auto tx = vault.deposit(
3209 {.depositor = depositor,
3211 .amount = asset(50)});
3216 auto const credKeylet =
3217 credentials::keylet(depositor, credIssuer1, credType);
3219 testcase(
"private vault depositor now authorized");
3220 env(credentials::create(depositor, credIssuer1, credType));
3221 env(credentials::accept(depositor, credIssuer1, credType));
3222 env(credentials::create(charlie, credIssuer1, credType));
3225 auto credSle = env.le(credKeylet);
3226 BEAST_EXPECT(credSle !=
nullptr);
3228 auto tx = vault.deposit(
3229 {.depositor = depositor,
3231 .amount = asset(50)});
3236 {.depositor = charlie, .id = keylet.
key, .amount = asset(50)});
3242 testcase(
"private vault depositor lost authorization");
3243 env(credentials::deleteCred(
3244 credIssuer1, depositor, credIssuer1, credType));
3245 env(credentials::deleteCred(
3246 credIssuer1, charlie, credIssuer1, credType));
3248 auto credSle = env.le(credKeylet);
3249 BEAST_EXPECT(credSle ==
nullptr);
3251 auto tx = vault.deposit(
3252 {.depositor = depositor,
3254 .amount = asset(50)});
3259 auto const shares = [&env, keylet = keylet,
this]() ->
Asset {
3260 auto const vault = env.le(keylet);
3261 BEAST_EXPECT(vault !=
nullptr);
3262 return MPTIssue(vault->at(sfShareMPTID));
3266 testcase(
"private vault expired authorization");
3267 uint32_t
const closeTime = env.current()
3269 .parentCloseTime.time_since_epoch()
3273 credentials::create(depositor, credIssuer2, credType);
3274 tx0[sfExpiration] = closeTime + 20;
3276 tx0 = credentials::create(charlie, credIssuer2, credType);
3277 tx0[sfExpiration] = closeTime + 20;
3281 env(credentials::accept(depositor, credIssuer2, credType));
3282 env(credentials::accept(charlie, credIssuer2, credType));
3287 auto tx1 = vault.deposit(
3288 {.depositor = depositor,
3290 .amount = asset(50)});
3296 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
3305 auto const credsKeylet =
3306 credentials::keylet(depositor, credIssuer2, credType);
3307 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3309 auto tx2 = vault.deposit(
3310 {.depositor = depositor,
3312 .amount = asset(1)});
3316 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3320 auto const credsKeylet =
3321 credentials::keylet(charlie, credIssuer2, credType);
3322 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3325 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3327 auto tx3 = vault.deposit(
3328 {.depositor = charlie,
3330 .amount = asset(2)});
3334 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3335 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3340 testcase(
"private vault reset domainId");
3341 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3342 tx[sfDomainID] =
"0";
3347 {.depositor = depositor,
3349 .amount = asset(50)});
3353 tx = vault.withdraw(
3354 {.depositor = depositor,
3356 .amount = asset(50)});
3360 tx = vault.clawback(
3363 .holder = depositor,
3364 .amount = asset(0)});
3367 tx = vault.clawback(
3371 .amount = asset(0)});
3520 using namespace test::jtx;
3524 Account
const& owner;
3525 Account
const& issuer;
3526 Account
const& depositor;
3527 Account
const& vaultAccount;
3537 auto testCase = [&,
this](
3540 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3541 Account
const owner{
"owner"};
3542 Account
const issuer{
"issuer"};
3543 Account
const depositor{
"depositor"};
3545 env.fund(XRP(1000), issuer, owner, depositor);
3550 env.trust(asset(1000), owner);
3551 env.trust(asset(1000), depositor);
3552 env(pay(issuer, owner, asset(200)));
3553 env(pay(issuer, depositor, asset(200)));
3556 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3557 tx[sfScale] = scale;
3560 auto const [vaultAccount, issuanceId] =
3562 auto const vault = env.le(keylet);
3564 Account(
"vault", vault->at(sfAccount)),
3565 vault->at(sfShareMPTID)};
3568 env.memoize(vaultAccount);
3572 return env.app().openLedger().modify(
3576 if (!BEAST_EXPECT(vault !=
nullptr))
3578 auto shares = sb.
peek(
3580 if (!BEAST_EXPECT(shares !=
nullptr))
3582 if (fn(*vault, *shares))
3597 .depositor = depositor,
3598 .vaultAccount = vaultAccount,
3608 testCase(18, [&,
this](Env& env, Data d) {
3609 testcase(
"Scale deposit overflow on first deposit");
3610 auto tx = d.vault.deposit(
3611 {.depositor = d.depositor,
3613 .amount = d.asset(10)});
3618 testCase(18, [&,
this](Env& env, Data d) {
3619 testcase(
"Scale deposit overflow on second deposit");
3622 auto tx = d.vault.deposit(
3623 {.depositor = d.depositor,
3625 .amount = d.asset(5)});
3631 auto tx = d.vault.deposit(
3632 {.depositor = d.depositor,
3634 .amount = d.asset(10)});
3640 testCase(18, [&,
this](Env& env, Data d) {
3641 testcase(
"Scale deposit overflow on total shares");
3644 auto tx = d.vault.deposit(
3645 {.depositor = d.depositor,
3647 .amount = d.asset(5)});
3653 auto tx = d.vault.deposit(
3654 {.depositor = d.depositor,
3656 .amount = d.asset(5)});
3662 testCase(1, [&,
this](Env& env, Data d) {
3665 auto const start = env.balance(d.depositor, d.assets).number();
3666 auto tx = d.vault.deposit(
3667 {.depositor = d.depositor,
3669 .amount = d.asset(1)});
3672 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3674 env.balance(d.depositor, d.assets) ==
3678 testCase(1, [&,
this](Env& env, Data d) {
3679 testcase(
"Scale deposit insignificant amount");
3681 auto tx = d.vault.deposit(
3682 {.depositor = d.depositor,
3688 testCase(1, [&,
this](Env& env, Data d) {
3689 testcase(
"Scale deposit exact, using full precision");
3691 auto const start = env.balance(d.depositor, d.assets).number();
3692 auto tx = d.vault.deposit(
3693 {.depositor = d.depositor,
3698 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3700 env.balance(d.depositor, d.assets) ==
3704 testCase(1, [&,
this](Env& env, Data d) {
3705 testcase(
"Scale deposit exact, truncating from .5");
3707 auto const start = env.balance(d.depositor, d.assets).number();
3711 auto tx = d.vault.deposit(
3712 {.depositor = d.depositor,
3717 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3719 env.balance(d.depositor, d.assets) ==
3724 auto tx = d.vault.deposit(
3725 {.depositor = d.depositor,
3730 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3732 env.balance(d.depositor, d.assets) ==
3737 auto tx = d.vault.deposit(
3738 {.depositor = d.depositor,
3743 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
3745 env.balance(d.depositor, d.assets) ==
3750 testCase(1, [&,
this](Env& env, Data d) {
3751 testcase(
"Scale deposit exact, truncating from .01");
3753 auto const start = env.balance(d.depositor, d.assets).number();
3755 auto tx = d.vault.deposit(
3756 {.depositor = d.depositor,
3761 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3763 env.balance(d.depositor, d.assets) ==
3768 auto tx = d.vault.deposit(
3769 {.depositor = d.depositor,
3774 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3776 env.balance(d.depositor, d.assets) ==
3781 testCase(1, [&,
this](Env& env, Data d) {
3782 testcase(
"Scale deposit exact, truncating from .99");
3784 auto const start = env.balance(d.depositor, d.assets).number();
3786 auto tx = d.vault.deposit(
3787 {.depositor = d.depositor,
3792 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3794 env.balance(d.depositor, d.assets) ==
3799 auto tx = d.vault.deposit(
3800 {.depositor = d.depositor,
3805 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3807 env.balance(d.depositor, d.assets) ==
3812 testCase(1, [&,
this](Env& env, Data d) {
3814 auto const start = env.balance(d.depositor, d.assets).number();
3815 auto tx = d.vault.deposit(
3816 {.depositor = d.depositor,
3821 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3823 env.balance(d.depositor, d.assets) ==
3826 env.balance(d.vaultAccount, d.assets) ==
3829 env.balance(d.vaultAccount, d.shares) ==
3838 auto const start = env.balance(d.depositor, d.assets).number();
3839 auto tx = d.vault.withdraw(
3840 {.depositor = d.depositor,
3846 env.balance(d.depositor, d.shares) == d.share(900));
3848 env.balance(d.depositor, d.assets) ==
3851 env.balance(d.vaultAccount, d.assets) ==
3854 env.balance(d.vaultAccount, d.shares) ==
3859 testcase(
"Scale redeem with rounding");
3864 auto const start = env.balance(d.depositor, d.assets).number();
3865 d.peek([](
SLE& vault,
auto&) ->
bool {
3866 vault[sfAssetsAvailable] =
Number(1);
3874 auto tx = d.vault.withdraw(
3875 {.depositor = d.depositor,
3881 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3883 env.balance(d.depositor, d.assets) ==
3886 env.balance(d.vaultAccount, d.assets) ==
3889 env.balance(d.vaultAccount, d.shares) ==
3899 auto const start = env.balance(d.depositor, d.assets).number();
3901 tx = d.vault.withdraw(
3902 {.depositor = d.depositor,
3908 env.balance(d.depositor, d.shares) == d.share(875 - 21));
3910 env.balance(d.depositor, d.assets) ==
3913 env.balance(d.vaultAccount, d.assets) ==
3916 env.balance(d.vaultAccount, d.shares) ==
3922 auto const rest = env.balance(d.depositor, d.shares).number();
3924 tx = d.vault.withdraw(
3925 {.depositor = d.depositor,
3927 .amount =
STAmount(d.share, rest)});
3930 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3932 env.balance(d.vaultAccount, d.assets).number() == 0);
3934 env.balance(d.vaultAccount, d.shares).number() == 0);
3938 testCase(18, [&,
this](Env& env, Data d) {
3939 testcase(
"Scale withdraw overflow");
3942 auto tx = d.vault.deposit(
3943 {.depositor = d.depositor,
3945 .amount = d.asset(5)});
3951 auto tx = d.vault.withdraw(
3952 {.depositor = d.depositor,
3960 testCase(1, [&,
this](Env& env, Data d) {
3962 auto const start = env.balance(d.depositor, d.assets).number();
3963 auto tx = d.vault.deposit(
3964 {.depositor = d.depositor,
3969 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3971 env.balance(d.depositor, d.assets) ==
3974 env.balance(d.vaultAccount, d.assets) ==
3977 env.balance(d.vaultAccount, d.shares) ==
3989 auto const start = env.balance(d.depositor, d.assets).number();
3990 auto tx = d.vault.withdraw(
3991 {.depositor = d.depositor,
3997 env.balance(d.depositor, d.shares) == d.share(900));
3999 env.balance(d.depositor, d.assets) ==
4002 env.balance(d.vaultAccount, d.assets) ==
4005 env.balance(d.vaultAccount, d.shares) ==
4010 testcase(
"Scale withdraw insignificant amount");
4011 auto tx = d.vault.withdraw(
4012 {.depositor = d.depositor,
4019 testcase(
"Scale withdraw with rounding assets");
4027 auto const start = env.balance(d.depositor, d.assets).number();
4028 d.peek([](
SLE& vault,
auto&) ->
bool {
4029 vault[sfAssetsAvailable] =
Number(1);
4037 auto tx = d.vault.withdraw(
4038 {.depositor = d.depositor,
4044 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4046 env.balance(d.depositor, d.assets) ==
4049 env.balance(d.vaultAccount, d.assets) ==
4052 env.balance(d.vaultAccount, d.shares) ==
4057 testcase(
"Scale withdraw with rounding shares up");
4065 auto const start = env.balance(d.depositor, d.assets).number();
4066 auto tx = d.vault.withdraw(
4067 {.depositor = d.depositor,
4073 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4075 env.balance(d.depositor, d.assets) ==
4078 env.balance(d.vaultAccount, d.assets) ==
4081 env.balance(d.vaultAccount, d.shares) ==
4086 testcase(
"Scale withdraw with rounding shares down");
4094 auto const start = env.balance(d.depositor, d.assets).number();
4095 auto tx = d.vault.withdraw(
4096 {.depositor = d.depositor,
4102 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4104 env.balance(d.depositor, d.assets) ==
4107 env.balance(d.vaultAccount, d.assets) ==
4110 env.balance(d.vaultAccount, d.shares) ==
4115 testcase(
"Scale withdraw tiny amount");
4117 auto const start = env.balance(d.depositor, d.assets).number();
4118 auto tx = d.vault.withdraw(
4119 {.depositor = d.depositor,
4125 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4127 env.balance(d.depositor, d.assets) ==
4130 env.balance(d.vaultAccount, d.assets) ==
4133 env.balance(d.vaultAccount, d.shares) ==
4140 env.balance(d.vaultAccount, d.assets).number();
4142 tx = d.vault.withdraw(
4143 {.depositor = d.depositor,
4145 .amount =
STAmount(d.asset, rest)});
4148 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4150 env.balance(d.vaultAccount, d.assets).number() == 0);
4152 env.balance(d.vaultAccount, d.shares).number() == 0);
4156 testCase(18, [&,
this](Env& env, Data d) {
4157 testcase(
"Scale clawback overflow");
4160 auto tx = d.vault.deposit(
4161 {.depositor = d.depositor,
4163 .amount = d.asset(5)});
4169 auto tx = d.vault.clawback(
4170 {.issuer = d.issuer,
4172 .holder = d.depositor,
4179 testCase(1, [&,
this](Env& env, Data d) {
4181 auto const start = env.balance(d.depositor, d.assets).number();
4182 auto tx = d.vault.deposit(
4183 {.depositor = d.depositor,
4188 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4190 env.balance(d.depositor, d.assets) ==
4193 env.balance(d.vaultAccount, d.assets) ==
4196 env.balance(d.vaultAccount, d.shares) ==
4207 auto const start = env.balance(d.depositor, d.assets).number();
4208 auto tx = d.vault.clawback(
4209 {.issuer = d.issuer,
4211 .holder = d.depositor,
4216 env.balance(d.depositor, d.shares) == d.share(900));
4218 env.balance(d.depositor, d.assets) ==
4221 env.balance(d.vaultAccount, d.assets) ==
4224 env.balance(d.vaultAccount, d.shares) ==
4229 testcase(
"Scale clawback insignificant amount");
4230 auto tx = d.vault.clawback(
4231 {.issuer = d.issuer,
4233 .holder = d.depositor,
4239 testcase(
"Scale clawback with rounding assets");
4247 auto const start = env.balance(d.depositor, d.assets).number();
4248 auto tx = d.vault.clawback(
4249 {.issuer = d.issuer,
4251 .holder = d.depositor,
4256 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4258 env.balance(d.depositor, d.assets) ==
4261 env.balance(d.vaultAccount, d.assets) ==
4264 env.balance(d.vaultAccount, d.shares) ==
4269 testcase(
"Scale clawback with rounding shares up");
4277 auto const start = env.balance(d.depositor, d.assets).number();
4278 auto tx = d.vault.clawback(
4279 {.issuer = d.issuer,
4281 .holder = d.depositor,
4286 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4288 env.balance(d.depositor, d.assets) ==
4291 env.balance(d.vaultAccount, d.assets) ==
4294 env.balance(d.vaultAccount, d.shares) ==
4299 testcase(
"Scale clawback with rounding shares down");
4307 auto const start = env.balance(d.depositor, d.assets).number();
4308 auto tx = d.vault.clawback(
4309 {.issuer = d.issuer,
4311 .holder = d.depositor,
4316 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4318 env.balance(d.depositor, d.assets) ==
4321 env.balance(d.vaultAccount, d.assets) ==
4324 env.balance(d.vaultAccount, d.shares) ==
4329 testcase(
"Scale clawback tiny amount");
4331 auto const start = env.balance(d.depositor, d.assets).number();
4332 auto tx = d.vault.clawback(
4333 {.issuer = d.issuer,
4335 .holder = d.depositor,
4340 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4342 env.balance(d.depositor, d.assets) ==
4345 env.balance(d.vaultAccount, d.assets) ==
4348 env.balance(d.vaultAccount, d.shares) ==
4355 env.balance(d.vaultAccount, d.assets).number();
4356 d.peek([](
SLE& vault,
auto&) ->
bool {
4357 vault[sfAssetsAvailable] =
Number(5);
4365 tx = d.vault.clawback(
4366 {.issuer = d.issuer,
4368 .holder = d.depositor,
4369 .amount =
STAmount(d.asset, rest)});
4372 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4374 env.balance(d.vaultAccount, d.assets).number() == 0);
4376 env.balance(d.vaultAccount, d.shares).number() == 0);
4384 using namespace test::jtx;
4387 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4388 Account
const owner{
"owner"};
4389 Account
const issuer{
"issuer"};
4391 env.fund(XRP(1000), issuer, owner);
4395 env.trust(asset(1000), owner);
4396 env(pay(issuer, owner, asset(200)));
4399 auto const sequence = env.seq(owner);
4400 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
4406 auto tx1 = vault.deposit(
4407 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
4410 auto tx2 = vault.set({.owner = owner, .id = keylet.
key});
4411 tx2[sfAssetsMaximum] = asset(1000).number();
4416 auto const sleVault = [&env, keylet = keylet,
this]() {
4417 auto const vault = env.le(keylet);
4418 BEAST_EXPECT(vault !=
nullptr);
4422 auto const check = [&, keylet = keylet, sle = sleVault,
this](
4425 BEAST_EXPECT(vault.isObject());
4427 constexpr auto checkString =
4429 return node.isMember(field.fieldName) &&
4430 node[field.fieldName].isString() &&
4431 node[field.fieldName] == v;
4433 constexpr auto checkObject =
4435 return node.isMember(field.fieldName) &&
4436 node[field.fieldName].isObject() &&
4437 node[field.fieldName] == v;
4439 constexpr auto checkInt =
4440 [](
auto& node,
SField const& field,
int v) ->
bool {
4441 return node.isMember(field.fieldName) &&
4442 ((node[field.fieldName].isInt() &&
4443 node[field.fieldName] ==
Json::Int(v)) ||
4444 (node[field.fieldName].isUInt() &&
4448 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
4449 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(keylet.
key));
4450 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
4454 checkString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
4456 checkObject(vault, sfAsset,
to_json(sle->at(sfAsset))));
4457 BEAST_EXPECT(checkString(vault, sfAssetsAvailable,
"50"));
4458 BEAST_EXPECT(checkString(vault, sfAssetsMaximum,
"1000"));
4459 BEAST_EXPECT(checkString(vault, sfAssetsTotal,
"50"));
4460 BEAST_EXPECT(checkString(vault, sfLossUnrealized,
"0"));
4462 auto const strShareID =
strHex(sle->at(sfShareMPTID));
4463 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
4464 BEAST_EXPECT(checkString(vault, sfOwner,
toBase58(owner.id())));
4465 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
4466 BEAST_EXPECT(checkInt(
4469 if (issuance.isObject())
4472 issuance[
"LedgerEntryType"].asString() ==
4475 issuance[jss::mpt_issuance_id].asString() == strShareID);
4476 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
4477 BEAST_EXPECT(checkInt(
4482 checkString(issuance, sfOutstandingAmount,
"50000000"));
4487 testcase(
"RPC ledger_entry selected by key");
4489 jvParams[jss::ledger_index] = jss::validated;
4490 jvParams[jss::vault] =
strHex(keylet.
key);
4491 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4493 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4494 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4495 check(jvVault[jss::result][jss::node]);
4499 testcase(
"RPC ledger_entry selected by owner and seq");
4501 jvParams[jss::ledger_index] = jss::validated;
4502 jvParams[jss::vault][jss::owner] = owner.human();
4503 jvParams[jss::vault][jss::seq] = sequence;
4504 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4506 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4507 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4508 check(jvVault[jss::result][jss::node]);
4512 testcase(
"RPC ledger_entry cannot find vault by key");
4514 jvParams[jss::ledger_index] = jss::validated;
4516 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4518 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4522 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
4524 jvParams[jss::ledger_index] = jss::validated;
4525 jvParams[jss::vault][jss::owner] = issuer.human();
4526 jvParams[jss::vault][jss::seq] = 1'000'000;
4527 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4529 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4533 testcase(
"RPC ledger_entry malformed key");
4535 jvParams[jss::ledger_index] = jss::validated;
4536 jvParams[jss::vault] = 42;
4537 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4539 jvVault[jss::result][jss::error].asString() ==
4540 "malformedRequest");
4544 testcase(
"RPC ledger_entry malformed owner");
4546 jvParams[jss::ledger_index] = jss::validated;
4547 jvParams[jss::vault][jss::owner] = 42;
4548 jvParams[jss::vault][jss::seq] = sequence;
4549 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4551 jvVault[jss::result][jss::error].asString() ==
4556 testcase(
"RPC ledger_entry malformed seq");
4558 jvParams[jss::ledger_index] = jss::validated;
4559 jvParams[jss::vault][jss::owner] = issuer.human();
4560 jvParams[jss::vault][jss::seq] =
"foo";
4561 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4563 jvVault[jss::result][jss::error].asString() ==
4564 "malformedRequest");
4568 testcase(
"RPC ledger_entry negative seq");
4570 jvParams[jss::ledger_index] = jss::validated;
4571 jvParams[jss::vault][jss::owner] = issuer.human();
4572 jvParams[jss::vault][jss::seq] = -1;
4573 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4575 jvVault[jss::result][jss::error].asString() ==
4576 "malformedRequest");
4580 testcase(
"RPC ledger_entry oversized seq");
4582 jvParams[jss::ledger_index] = jss::validated;
4583 jvParams[jss::vault][jss::owner] = issuer.human();
4584 jvParams[jss::vault][jss::seq] = 1e20;
4585 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4587 jvVault[jss::result][jss::error].asString() ==
4588 "malformedRequest");
4592 testcase(
"RPC ledger_entry bool seq");
4594 jvParams[jss::ledger_index] = jss::validated;
4595 jvParams[jss::vault][jss::owner] = issuer.human();
4596 jvParams[jss::vault][jss::seq] =
true;
4597 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4599 jvVault[jss::result][jss::error].asString() ==
4600 "malformedRequest");
4607 jvParams[jss::account] = owner.human();
4608 jvParams[jss::type] = jss::vault;
4610 "json",
"account_objects",
to_string(jvParams))[jss::result];
4612 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
4613 check(jv[jss::account_objects][0u]);
4620 jvParams[jss::ledger_index] = jss::validated;
4621 jvParams[jss::binary] =
false;
4622 jvParams[jss::type] = jss::vault;
4624 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
4625 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
4626 check(jv[jss::result][jss::state][0u]);
4630 testcase(
"RPC vault_info command line");
4632 env.rpc(
"vault_info",
strHex(keylet.
key),
"validated");
4634 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4635 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4637 jv[jss::result][jss::vault],
4638 jv[jss::result][jss::vault][jss::shares]);
4644 jvParams[jss::ledger_index] = jss::validated;
4645 jvParams[jss::vault_id] =
strHex(keylet.
key);
4646 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4648 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4649 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4651 jv[jss::result][jss::vault],
4652 jv[jss::result][jss::vault][jss::shares]);
4656 testcase(
"RPC vault_info invalid vault_id");
4658 jvParams[jss::ledger_index] = jss::validated;
4659 jvParams[jss::vault_id] =
"foobar";
4660 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4662 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4666 testcase(
"RPC vault_info json invalid index");
4668 jvParams[jss::ledger_index] = jss::validated;
4669 jvParams[jss::vault_id] = 0;
4670 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4672 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4676 testcase(
"RPC vault_info json by owner and sequence");
4678 jvParams[jss::ledger_index] = jss::validated;
4679 jvParams[jss::owner] = owner.human();
4680 jvParams[jss::seq] = sequence;
4681 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4683 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4684 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4686 jv[jss::result][jss::vault],
4687 jv[jss::result][jss::vault][jss::shares]);
4691 testcase(
"RPC vault_info json malformed sequence");
4693 jvParams[jss::ledger_index] = jss::validated;
4694 jvParams[jss::owner] = owner.human();
4695 jvParams[jss::seq] =
"foobar";
4696 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4698 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4702 testcase(
"RPC vault_info json invalid sequence");
4704 jvParams[jss::ledger_index] = jss::validated;
4705 jvParams[jss::owner] = owner.human();
4706 jvParams[jss::seq] = 0;
4707 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4709 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4713 testcase(
"RPC vault_info json negative sequence");
4715 jvParams[jss::ledger_index] = jss::validated;
4716 jvParams[jss::owner] = owner.human();
4717 jvParams[jss::seq] = -1;
4718 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4720 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4724 testcase(
"RPC vault_info json oversized sequence");
4726 jvParams[jss::ledger_index] = jss::validated;
4727 jvParams[jss::owner] = owner.human();
4728 jvParams[jss::seq] = 1e20;
4729 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4731 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4735 testcase(
"RPC vault_info json bool sequence");
4737 jvParams[jss::ledger_index] = jss::validated;
4738 jvParams[jss::owner] = owner.human();
4739 jvParams[jss::seq] =
true;
4740 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4742 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4746 testcase(
"RPC vault_info json malformed owner");
4748 jvParams[jss::ledger_index] = jss::validated;
4749 jvParams[jss::owner] =
"foobar";
4750 jvParams[jss::seq] = sequence;
4751 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4753 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4757 testcase(
"RPC vault_info json invalid combination only owner");
4759 jvParams[jss::ledger_index] = jss::validated;
4760 jvParams[jss::owner] = owner.human();
4761 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4763 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4767 testcase(
"RPC vault_info json invalid combination only seq");
4769 jvParams[jss::ledger_index] = jss::validated;
4770 jvParams[jss::seq] = sequence;
4771 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4773 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4777 testcase(
"RPC vault_info json invalid combination seq vault_id");
4779 jvParams[jss::ledger_index] = jss::validated;
4780 jvParams[jss::vault_id] =
strHex(keylet.
key);
4781 jvParams[jss::seq] = sequence;
4782 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4784 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4788 testcase(
"RPC vault_info json invalid combination owner vault_id");
4790 jvParams[jss::ledger_index] = jss::validated;
4791 jvParams[jss::vault_id] =
strHex(keylet.
key);
4792 jvParams[jss::owner] = owner.human();
4793 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4795 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4800 "RPC vault_info json invalid combination owner seq "
4803 jvParams[jss::ledger_index] = jss::validated;
4804 jvParams[jss::vault_id] =
strHex(keylet.
key);
4805 jvParams[jss::seq] = sequence;
4806 jvParams[jss::owner] = owner.human();
4807 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4809 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4813 testcase(
"RPC vault_info json no input");
4815 jvParams[jss::ledger_index] = jss::validated;
4816 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4818 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4822 testcase(
"RPC vault_info command line invalid index");
4823 Json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
4824 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
4828 testcase(
"RPC vault_info command line invalid index");
4829 Json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
4831 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4835 testcase(
"RPC vault_info command line invalid index");
4839 jv[jss::result][jss::error].asString() ==
"entryNotFound");
4843 testcase(
"RPC vault_info command line invalid ledger");
4846 jv[jss::result][jss::error].asString() ==
"lgrNotFound");
4853 using namespace test::jtx;
4855 Env env(*
this, testable_amendments());
4856 Account alice{
"alice"};
4858 Account carol{
"carol"};
4865 auto const xrpBalance =
4869 if (BEAST_EXPECT(sle !=
nullptr))
4870 return sle->getFieldAmount(sfBalance).xrp().drops();
4874 auto testCase = [&,
this](
auto test, CaseArgs args = {}) {
4875 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4880 env.fund(XRP(10000), alice);
4881 env.fund(XRP(20000), bob);
4882 env.fund(XRP(30000), carol);
4896 test(env, vault, args.asset);
4899 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4900 testcase(
"delegated vault creation");
4901 auto startBalance = xrpBalance(env, carol);
4902 if (!BEAST_EXPECT(startBalance.has_value()))
4905 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4906 env(tx, delegate::as(alice));
4908 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
4911 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4912 testcase(
"delegated deposit and withdrawal");
4913 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4917 auto const amount = 1513;
4918 auto const baseFee = env.current()->fees().base;
4920 auto startBalance = xrpBalance(env, carol);
4921 if (!BEAST_EXPECT(startBalance.has_value()))
4925 {.depositor = carol,
4927 .amount = asset(amount)});
4928 env(tx, delegate::as(alice));
4930 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
4932 tx = vault.withdraw(
4933 {.depositor = carol,
4935 .amount = asset(amount - 1)});
4936 env(tx, delegate::as(alice));
4938 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
4940 tx = vault.withdraw(
4941 {.depositor = carol, .id = keylet.
key, .amount = asset(1)});
4944 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
4947 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4948 testcase(
"delegated withdrawal same as base fee and deletion");
4949 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4953 auto const amount = 25537;
4954 auto const baseFee = env.current()->fees().base;
4956 auto startBalance = xrpBalance(env, carol);
4957 if (!BEAST_EXPECT(startBalance.has_value()))
4961 {.depositor = carol,
4963 .amount = asset(amount)});
4967 xrpBalance(env, carol) == *startBalance - amount - baseFee);
4969 tx = vault.withdraw(
4970 {.depositor = carol,
4972 .amount = asset(baseFee)});
4973 env(tx, delegate::as(alice));
4975 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
4977 tx = vault.withdraw(
4978 {.depositor = carol,
4980 .amount = asset(amount - baseFee)});
4981 env(tx, delegate::as(alice));
4983 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
4985 tx = vault.del({.owner = carol, .id = keylet.
key});
4986 env(tx, delegate::as(alice));