61 using namespace test::jtx;
63 auto const testSequence = [
this](
66 Account
const& issuer,
68 Account
const& depositor,
69 Account
const& charlie,
72 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
73 tx[sfData] =
"AFEED00E";
74 tx[sfAssetsMaximum] = asset(100).number();
77 BEAST_EXPECT(env.le(keylet));
80 auto const [share, vaultAccount] =
85 auto const vault = env.le(keylet);
86 BEAST_EXPECT(vault !=
nullptr);
87 if (asset.raw().holds<
Issue>() && !asset.raw().
native())
88 BEAST_EXPECT(vault->at(sfScale) == 6);
90 BEAST_EXPECT(vault->at(sfScale) == 0);
93 BEAST_EXPECT(shares !=
nullptr);
94 if (asset.raw().holds<
Issue>() && !asset.raw().
native())
95 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
97 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
100 Account(
"vault", vault->at(sfAccount))};
102 auto const shares = share.raw().get<
MPTIssue>();
103 env.memoize(vaultAccount);
106 Account alice{
"alice"};
107 Account dave{
"dave"};
108 Account erin{
"erin"};
109 env.fund(XRP(1000), alice, dave, erin);
115 testcase(prefix +
" fail to deposit more than assets held");
116 auto tx = vault.deposit(
117 {.depositor = depositor,
119 .amount = asset(10000)});
125 testcase(prefix +
" deposit non-zero amount");
126 auto tx = vault.deposit(
127 {.depositor = depositor,
129 .amount = asset(50)});
133 env.balance(depositor, shares) == share(50 * scale));
137 testcase(prefix +
" deposit non-zero amount again");
138 auto tx = vault.deposit(
139 {.depositor = depositor,
141 .amount = asset(50)});
145 env.balance(depositor, shares) == share(100 * scale));
149 testcase(prefix +
" fail to delete non-empty vault");
150 auto tx = vault.del({.owner = owner, .id = keylet.
key});
156 testcase(prefix +
" fail to update because wrong owner");
157 auto tx = vault.set({.owner = issuer, .id = keylet.
key});
158 tx[sfAssetsMaximum] = asset(50).number();
165 prefix +
" fail to set maximum lower than current amount");
166 auto tx = vault.set({.owner = owner, .id = keylet.
key});
167 tx[sfAssetsMaximum] = asset(50).number();
173 testcase(prefix +
" set maximum higher than current amount");
174 auto tx = vault.set({.owner = owner, .id = keylet.
key});
175 tx[sfAssetsMaximum] = asset(150).number();
181 testcase(prefix +
" set maximum is idempotent, set it again");
182 auto tx = vault.set({.owner = owner, .id = keylet.
key});
183 tx[sfAssetsMaximum] = asset(150).number();
190 auto tx = vault.set({.owner = owner, .id = keylet.
key});
197 testcase(prefix +
" fail to set domain on public vault");
198 auto tx = vault.set({.owner = owner, .id = keylet.
key});
205 testcase(prefix +
" fail to deposit more than maximum");
206 auto tx = vault.deposit(
207 {.depositor = depositor,
209 .amount = asset(100)});
215 testcase(prefix +
" reset maximum to zero i.e. not enforced");
216 auto tx = vault.set({.owner = owner, .id = keylet.
key});
217 tx[sfAssetsMaximum] = asset(0).number();
223 testcase(prefix +
" fail to withdraw more than assets held");
224 auto tx = vault.withdraw(
225 {.depositor = depositor,
227 .amount = asset(1000)});
233 testcase(prefix +
" deposit some more");
234 auto tx = vault.deposit(
235 {.depositor = depositor,
237 .amount = asset(100)});
241 env.balance(depositor, shares) == share(200 * scale));
245 testcase(prefix +
" clawback some");
248 auto tx = vault.clawback(
252 .amount = asset(10)});
255 if (!asset.raw().native())
258 env.balance(depositor, shares) == share(190 * scale));
266 auto tx = vault.clawback(
267 {.issuer = issuer, .id = keylet.
key, .holder = depositor});
270 if (!asset.raw().native())
272 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
275 auto tx = vault.clawback(
279 .amount = asset(10)});
285 auto tx = vault.withdraw(
286 {.depositor = depositor,
288 .amount = asset(10)});
295 if (!asset.raw().native())
297 testcase(prefix +
" deposit again");
298 auto tx = vault.deposit(
299 {.depositor = depositor,
301 .amount = asset(200)});
305 env.balance(depositor, shares) == share(200 * scale));
310 prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
311 auto tx = vault.withdraw(
312 {.depositor = depositor,
314 .amount = asset(100)});
315 tx[sfDestination] = alice.human();
321 testcase(prefix +
" fail to withdraw to zero destination");
322 auto tx = vault.withdraw(
323 {.depositor = depositor,
325 .amount = asset(1000)});
326 tx[sfDestination] =
"0";
334 " fail to withdraw with tag but without destination");
335 auto tx = vault.withdraw(
336 {.depositor = depositor,
338 .amount = asset(1000)});
339 tx[sfDestinationTag] =
"0";
344 if (!asset.raw().native())
347 prefix +
" fail to withdraw to 3rd party no authorization");
348 auto tx = vault.withdraw(
349 {.depositor = depositor,
351 .amount = asset(100)});
352 tx[sfDestination] = erin.human();
361 " fail to withdraw to 3rd party lsfRequireDestTag");
362 auto tx = vault.withdraw(
363 {.depositor = depositor,
365 .amount = asset(100)});
366 tx[sfDestination] = dave.human();
372 testcase(prefix +
" withdraw to authorized 3rd party");
373 auto tx = vault.withdraw(
374 {.depositor = depositor,
376 .amount = asset(100)});
377 tx[sfDestination] = charlie.human();
381 env.balance(depositor, shares) == share(100 * scale));
385 testcase(prefix +
" withdraw to issuer");
386 auto tx = vault.withdraw(
387 {.depositor = depositor,
389 .amount = asset(50)});
390 tx[sfDestination] = issuer.human();
394 env.balance(depositor, shares) == share(50 * scale));
397 if (!asset.raw().native())
399 testcase(prefix +
" issuer deposits");
400 auto tx = vault.deposit(
401 {.depositor = issuer,
403 .amount = asset(10)});
406 BEAST_EXPECT(env.balance(issuer, shares) == share(10 * scale));
408 testcase(prefix +
" issuer withdraws");
410 {.depositor = issuer,
412 .amount = share(10 * scale)});
415 BEAST_EXPECT(env.balance(issuer, shares) == share(0 * scale));
419 testcase(prefix +
" withdraw remaining assets");
420 auto tx = vault.withdraw(
421 {.depositor = depositor,
423 .amount = asset(50)});
426 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
428 if (!asset.raw().native())
430 auto tx = vault.clawback(
434 .amount = asset(0)});
440 auto tx = vault.withdraw(
441 {.depositor = depositor,
443 .amount = share(10)});
449 if (!asset.raw().native() && asset.raw().holds<
Issue>())
451 testcase(prefix +
" temporary authorization for 3rd party");
452 env(trust(erin, asset(1000)));
453 env(trust(issuer, asset(0), erin,
tfSetfAuth));
454 env(pay(issuer, erin, asset(10)));
457 auto tx = vault.deposit(
458 {.depositor = erin, .id = keylet.
key, .amount = asset(10)});
462 auto tx = pay(erin, depositor, share(10 * scale));
470 {.depositor = depositor,
472 .amount = asset(1)}));
479 testcase(prefix +
" withdraw to authorized 3rd party");
482 {.depositor = depositor,
484 .amount = asset(10)});
485 tx[sfDestination] = erin.human();
490 env(pay(erin, issuer, asset(10)));
493 testcase(prefix +
" fail to pay to unauthorized 3rd party");
494 env(trust(erin, asset(0)));
498 env(pay(depositor, erin, share(1)), ter{
tecNO_LINE});
503 {.depositor = depositor,
505 .amount = asset(1)});
511 testcase(prefix +
" fail to delete because wrong owner");
512 auto tx = vault.del({.owner = issuer, .id = keylet.
key});
518 testcase(prefix +
" delete empty vault");
519 auto tx = vault.del({.owner = owner, .id = keylet.
key});
522 BEAST_EXPECT(!env.le(keylet));
526 auto testCases = [
this, &testSequence](
530 Account
const& issuer,
531 Account
const& owner,
532 Account
const& depositor,
533 Account
const& charlie)> setup) {
534 Env env{*
this, testable_amendments() | featureSingleAssetVault};
535 Account issuer{
"issuer"};
536 Account owner{
"owner"};
537 Account depositor{
"depositor"};
538 Account charlie{
"charlie"};
540 env.fund(XRP(1000), issuer, owner, depositor, charlie);
548 PrettyAsset asset = setup(env, issuer, owner, depositor, charlie);
550 prefix, env, issuer, owner, depositor, charlie, vault, asset);
556 Account
const& issuer,
557 Account
const& owner,
558 Account
const& depositor,
566 Account
const& issuer,
567 Account
const& owner,
568 Account
const& depositor,
569 Account
const& charlie) ->
Asset {
571 env(trust(owner, asset(1000)));
572 env(trust(depositor, asset(1000)));
573 env(trust(charlie, asset(1000)));
574 env(trust(issuer, asset(0), owner,
tfSetfAuth));
575 env(trust(issuer, asset(0), depositor,
tfSetfAuth));
576 env(trust(issuer, asset(0), charlie,
tfSetfAuth));
577 env(pay(issuer, depositor, asset(1000)));
585 Account
const& issuer,
586 Account
const& owner,
587 Account
const& depositor,
588 Account
const& charlie) ->
Asset {
589 MPTTester mptt{env, issuer, mptInitNoFund};
594 mptt.authorize({.account = depositor});
595 mptt.authorize({.account = charlie});
596 env(pay(issuer, depositor, asset(1000)));
605 using namespace test::jtx;
610 testable_amendments() | featureSingleAssetVault;
613 auto testCase = [&,
this](
616 Account
const& issuer,
617 Account
const& owner,
620 CaseArgs args = {}) {
621 Env env{*
this, args.features};
622 Account issuer{
"issuer"};
623 Account owner{
"owner"};
625 env.fund(XRP(1000), issuer, owner);
633 env(trust(owner, asset(1000)));
634 env(trust(issuer, asset(0), owner,
tfSetfAuth));
635 env(pay(issuer, owner, asset(1000)));
638 test(env, issuer, owner, asset, vault);
643 Account
const& issuer,
644 Account
const& owner,
647 testcase(
"disabled single asset vault");
650 vault.create({.owner = owner, .asset = asset});
654 auto tx = vault.set({.owner = owner, .id = keylet.
key});
659 auto tx = vault.deposit(
662 .amount = asset(10)});
667 auto tx = vault.withdraw(
670 .amount = asset(10)});
675 auto tx = vault.clawback(
679 .amount = asset(10)});
684 auto tx = vault.del({.owner = owner, .id = keylet.
key});
688 {.features = testable_amendments() - featureSingleAssetVault});
690 testCase([&](Env& env,
691 Account
const& issuer,
692 Account
const& owner,
697 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
702 auto tx = vault.set({.owner = owner, .id = keylet.
key});
708 auto tx = vault.deposit(
711 .amount = asset(10)});
717 auto tx = vault.withdraw(
720 .amount = asset(10)});
726 auto tx = vault.clawback(
730 .amount = asset(10)});
736 auto tx = vault.del({.owner = owner, .id = keylet.
key});
742 testCase([&](Env& env,
743 Account
const& issuer,
744 Account
const& owner,
749 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
754 auto tx = vault.set({.owner = owner, .id = keylet.
key});
760 auto tx = vault.deposit(
763 .amount = asset(10)});
769 auto tx = vault.withdraw(
772 .amount = asset(10)});
778 auto tx = vault.clawback(
782 .amount = asset(10)});
788 auto tx = vault.del({.owner = owner, .id = keylet.
key});
797 Account
const& owner,
800 testcase(
"disabled permissioned domain");
803 vault.create({.owner = owner, .asset =
xrpIssue()});
808 auto tx = vault.set({.owner = owner, .id = keylet.
key});
814 auto tx = vault.set({.owner = owner, .id = keylet.
key});
815 tx[sfDomainID] =
"0";
819 {.features = (testable_amendments() | featureSingleAssetVault) -
820 featurePermissionedDomains});
822 testCase([&](Env& env,
823 Account
const& issuer,
824 Account
const& owner,
830 vault.create({.owner = owner, .asset =
xrpIssue()});
833 auto tx = vault.set({
841 auto tx = vault.deposit(
844 .amount = asset(10)});
849 auto tx = vault.withdraw(
852 .amount = asset(10)});
857 auto tx = vault.clawback(
861 .amount = asset(10)});
866 auto tx = vault.del({
874 testCase([&](Env& env,
875 Account
const& issuer,
876 Account
const& owner,
881 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
884 auto tx = vault.clawback(
888 .amount = asset(10)});
893 testCase([&](Env& env,
895 Account
const& owner,
898 testcase(
"withdraw to bad destination");
900 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
903 auto tx = vault.withdraw(
906 .amount = asset(10)});
907 tx[jss::Destination] =
"0";
912 testCase([&](Env& env,
914 Account
const& owner,
921 vault.create({.owner = owner, .asset = asset});
928 vault.create({.owner = owner, .asset = asset});
936 vault.create({.owner = owner, .asset = asset});
940 auto const sleVault = env.le(keylet);
941 BEAST_EXPECT(sleVault);
942 BEAST_EXPECT((*sleVault)[sfScale] == 18);
947 vault.create({.owner = owner, .asset = asset});
951 auto const sleVault = env.le(keylet);
952 BEAST_EXPECT(sleVault);
953 BEAST_EXPECT((*sleVault)[sfScale] == 0);
958 vault.create({.owner = owner, .asset = asset});
961 auto const sleVault = env.le(keylet);
962 BEAST_EXPECT(sleVault);
963 BEAST_EXPECT((*sleVault)[sfScale] == 6);
967 testCase([&](Env& env,
969 Account
const& owner,
972 testcase(
"create or set invalid data");
974 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
990 auto tx = vault.set({.owner = owner, .id = keylet.
key});
996 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1003 testCase([&](Env& env,
1005 Account
const& owner,
1010 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1013 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1018 testCase([&](Env& env,
1020 Account
const& owner,
1023 testcase(
"create with invalid metadata");
1025 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1029 tx[sfMPTokenMetadata] =
"";
1042 testCase([&](Env& env,
1044 Account
const& owner,
1049 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1052 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1058 testCase([&](Env& env,
1060 Account
const& owner,
1063 testcase(
"invalid deposit amount");
1065 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1068 auto tx = vault.deposit(
1069 {.depositor = owner,
1076 auto tx = vault.deposit(
1077 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1082 testCase([&](Env& env,
1084 Account
const& owner,
1087 testcase(
"invalid set immutable flag");
1089 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1092 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1098 testCase([&](Env& env,
1100 Account
const& owner,
1103 testcase(
"invalid withdraw amount");
1105 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1108 auto tx = vault.withdraw(
1109 {.depositor = owner,
1116 auto tx = vault.withdraw(
1117 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1122 testCase([&](Env& env,
1123 Account
const& issuer,
1124 Account
const& owner,
1129 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1132 auto tx = vault.clawback(
1136 .amount = asset(50)});
1141 auto tx = vault.clawback(
1150 testCase([&](Env& env,
1152 Account
const& owner,
1157 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1161 tx[sfWithdrawalPolicy] = 0;
1180 tx[sfDomainID] =
"0";
1669 using namespace test::jtx;
1673 bool enableClawback =
true;
1677 auto testCase = [
this](
1680 Account
const& issuer,
1681 Account
const& owner,
1682 Account
const& depositor,
1685 MPTTester& mptt)> test,
1686 CaseArgs args = {}) {
1687 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1688 Account issuer{
"issuer"};
1689 Account owner{
"owner"};
1690 Account depositor{
"depositor"};
1691 env.fund(XRP(1000), issuer, owner, depositor);
1695 MPTTester mptt{env, issuer, mptInitNoFund};
1702 mptt.authorize({.account = owner});
1703 mptt.authorize({.account = depositor});
1704 if (args.requireAuth)
1706 mptt.authorize({.account = issuer, .holder = owner});
1707 mptt.authorize({.account = issuer, .holder = depositor});
1710 env(pay(issuer, depositor, asset(1000)));
1713 test(env, issuer, owner, depositor, asset, vault, mptt);
1718 Account
const& issuer,
1719 Account
const& owner,
1720 Account
const& depositor,
1724 testcase(
"MPT nothing to clawback from");
1725 auto tx = vault.clawback(
1728 .holder = depositor,
1729 .amount = asset(10)});
1735 Account
const& issuer,
1736 Account
const& owner,
1737 Account
const& depositor,
1741 testcase(
"MPT global lock blocks create");
1742 mptt.set({.account = issuer, .flags =
tfMPTLock});
1743 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1749 Account
const& issuer,
1750 Account
const& owner,
1751 Account
const& depositor,
1755 testcase(
"MPT global lock blocks deposit");
1756 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1760 mptt.set({.account = issuer, .flags =
tfMPTLock});
1764 {.depositor = depositor,
1766 .amount = asset(100)});
1771 tx = vault.del({.owner = owner, .id = keylet.
key});
1777 Account
const& issuer,
1778 Account
const& owner,
1779 Account
const& depositor,
1783 testcase(
"MPT global lock blocks withdrawal");
1784 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1788 {.depositor = depositor,
1790 .amount = asset(100)});
1796 auto v = env.le(keylet);
1798 MPTID share = (*v)[sfShareMPTID];
1800 BEAST_EXPECT(issuance);
1801 Number outstandingShares = issuance->at(sfOutstandingAmount);
1802 BEAST_EXPECT(outstandingShares == 100);
1804 mptt.set({.account = issuer, .flags =
tfMPTLock});
1807 tx = vault.withdraw(
1808 {.depositor = depositor,
1810 .amount = asset(100)});
1813 tx[sfDestination] = issuer.human();
1817 tx = vault.clawback(
1820 .holder = depositor,
1821 .amount = asset(0)});
1827 BEAST_EXPECT(mptSle ==
nullptr);
1830 tx = vault.del({.owner = owner, .id = keylet.
key});
1836 Account
const& issuer,
1837 Account
const& owner,
1838 Account
const& depositor,
1842 testcase(
"MPT only issuer can clawback");
1844 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1849 {.depositor = depositor,
1851 .amount = asset(100)});
1856 auto tx = vault.clawback(
1857 {.issuer = owner, .id = keylet.
key, .holder = depositor});
1865 Account
const& issuer,
1866 Account
const& owner,
1867 Account
const& depositor,
1872 "MPT 3rd party without MPToken cannot be withdrawal "
1876 vault.create({.owner = owner, .asset = asset});
1881 {.depositor = depositor,
1883 .amount = asset(100)});
1889 Account charlie{
"charlie"};
1890 env.fund(XRP(1000), charlie);
1893 tx = vault.withdraw(
1894 {.depositor = depositor,
1896 .amount = asset(100)});
1897 tx[sfDestination] = charlie.human();
1901 {.requireAuth =
false});
1906 Account
const& issuer,
1907 Account
const& owner,
1908 Account
const& depositor,
1912 testcase(
"MPT depositor without MPToken cannot withdraw");
1915 vault.create({.owner = owner, .asset = asset});
1918 auto v = env.le(keylet);
1920 MPTID share = (*v)[sfShareMPTID];
1923 {.depositor = depositor,
1925 .amount = asset(1000)});
1934 auto const mptoken =
1936 BEAST_EXPECT(mptoken ==
nullptr);
1938 tx = vault.withdraw(
1939 {.depositor = depositor,
1941 .amount = asset(100)});
1947 mptt.authorize({.account = depositor});
1950 tx = vault.withdraw(
1951 {.depositor = depositor,
1953 .amount = asset(1000)});
1960 BEAST_EXPECT(mptSle ==
nullptr);
1963 {.requireAuth =
false});
1967 Account
const& issuer,
1968 Account
const& owner,
1969 Account
const& depositor,
1975 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1980 {.depositor = depositor,
1982 .amount = asset(1000)});
1987 auto tx = vault.clawback(
1990 .holder = depositor,
1991 .amount = asset(0)});
1995 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
2000 vault.create({.owner = depositor, .asset = asset});
2005 auto tx = vault.deposit(
2006 {.depositor = depositor,
2008 .amount = asset(10)});
2013 auto tx = vault.withdraw(
2014 {.depositor = depositor,
2016 .amount = asset(10)});
2021 auto tx = vault.clawback(
2024 .holder = depositor,
2025 .amount = asset(0)});
2029 env(vault.del({.owner = owner, .id = keylet.key}));
2034 Account
const& issuer,
2035 Account
const& owner,
2036 Account
const& depositor,
2040 testcase(
"MPT vault owner can receive shares unless unauthorized");
2042 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2047 {.depositor = depositor,
2049 .amount = asset(1000)});
2054 auto const vault = env.le(keylet);
2055 return vault->at(sfShareMPTID);
2061 env(pay(depositor, owner, shares(1)));
2064 tx = vault.withdraw(
2065 {.depositor = owner,
2067 .amount = shares(1)});
2072 env(pay(depositor, owner, shares(1)));
2075 tx = vault.clawback(
2079 .amount = asset(0)});
2084 env(pay(depositor, owner, shares(1)));
2088 env(pay(owner, depositor, shares(1)));
2094 jv[sfAccount] = owner.human();
2095 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
2097 jv[sfTransactionType] = jss::MPTokenAuthorize;
2103 tx = pay(depositor, owner, shares(1));
2108 tx = vault.clawback(
2111 .holder = depositor,
2112 .amount = asset(0)});
2117 env(vault.del({.owner = owner, .id = keylet.key}));
2125 Account
const& issuer,
2126 Account
const& owner,
2127 Account
const& depositor,
2134 vault.create({.owner = owner, .asset = asset});
2139 {.depositor = depositor,
2141 .amount = asset(1000)});
2146 auto tx = vault.clawback(
2149 .holder = depositor,
2150 .amount = asset(0)});
2154 {.enableClawback =
false});
2158 Account
const& issuer,
2159 Account
const& owner,
2160 Account
const& depositor,
2165 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2169 {.depositor = depositor,
2171 .amount = asset(1000)});
2177 .holder = depositor,
2182 auto tx = vault.withdraw(
2183 {.depositor = depositor,
2185 .amount = asset(100)});
2189 tx[sfDestination] = issuer.human();
2193 tx[sfDestination] = owner.human();
2200 auto tx = vault.deposit(
2201 {.depositor = depositor,
2203 .amount = asset(100)});
2208 tx = vault.clawback(
2211 .holder = depositor,
2212 .amount = asset(800)});
2216 env(vault.del({.owner = owner, .id = keylet.key}));
2221 Account
const& issuer,
2222 Account
const& owner,
2223 Account
const& depositor,
2227 testcase(
"MPT lock of vault pseudo-account");
2228 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2232 auto const vaultAccount =
2233 [&env, keylet = keylet,
this]() ->
AccountID {
2234 auto const vault = env.le(keylet);
2235 BEAST_EXPECT(vault !=
nullptr);
2236 return vault->at(sfAccount);
2240 {.depositor = depositor,
2242 .amount = asset(100)});
2248 jv[jss::Account] = issuer.human();
2249 jv[sfMPTokenIssuanceID] =
2251 jv[jss::Holder] =
toBase58(vaultAccount);
2252 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
2260 {.depositor = depositor,
2262 .amount = asset(100)});
2265 tx = vault.withdraw(
2266 {.depositor = depositor,
2268 .amount = asset(100)});
2272 tx = vault.clawback(
2275 .holder = depositor,
2276 .amount = asset(100)});
2280 tx = vault.del({.owner = owner, .id = keylet.
key});
2287 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2288 Account owner{
"owner"};
2289 Account issuer{
"issuer"};
2290 env.fund(XRP(1000000), owner, issuer);
2294 MPTTester mptt{env, issuer, mptInitNoFund};
2298 mptt.authorize({.account = owner});
2299 mptt.authorize({.account = issuer, .holder = owner});
2301 env(pay(issuer, owner, asset(100)));
2302 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
2306 auto const shares = [&env, keylet = k1,
this]() ->
Asset {
2307 auto const vault = env.le(keylet);
2308 BEAST_EXPECT(vault !=
nullptr);
2309 return MPTIssue(vault->at(sfShareMPTID));
2312 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
2321 using namespace test::jtx;
2327 Account
const& owner,
2328 Account
const& issuer,
2329 Account
const& charlie,
2334 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2335 Account
const owner{
"owner"};
2336 Account
const issuer{
"issuer"};
2337 Account
const charlie{
"charlie"};
2339 env.fund(XRP(1000), issuer, owner, charlie);
2344 env.trust(asset(1000), owner);
2345 env.trust(asset(1000), charlie);
2346 env(pay(issuer, owner, asset(200)));
2347 env(rate(issuer, 1.25));
2350 auto const vaultAccount =
2352 return Account(
"vault", env.le(keylet)->at(sfAccount));
2355 return env.le(keylet)->at(sfShareMPTID);
2371 Account
const& owner,
2372 Account
const& issuer,
2378 testcase(
"IOU cannot use different asset");
2381 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2387 auto tx = [&, account = vaultAccount(keylet)]() {
2389 jv[jss::Account] = issuer.human();
2391 auto& ja = jv[jss::LimitAmount] =
2393 ja[jss::issuer] =
toBase58(account);
2395 jv[jss::TransactionType] = jss::TrustSet;
2404 auto tx = vault.deposit(
2405 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2411 auto tx = vault.withdraw(
2412 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2417 env(vault.del({.owner = owner, .id = keylet.key}));
2423 Account
const& owner,
2424 Account
const& issuer,
2425 Account
const& charlie,
2430 testcase(
"IOU frozen trust line to vault account");
2432 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2437 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2440 Asset const share =
Asset(issuanceId(keylet));
2443 auto trustSet = [&, account = vaultAccount(keylet)]() {
2445 jv[jss::Account] = issuer.human();
2447 auto& ja = jv[jss::LimitAmount] =
2449 ja[jss::issuer] =
toBase58(account);
2451 jv[jss::TransactionType] = jss::TrustSet;
2463 auto tx = vault.deposit(
2464 {.depositor = owner,
2466 .amount = asset(80)});
2471 auto tx = vault.withdraw(
2472 {.depositor = owner,
2474 .amount = asset(100)});
2478 tx[sfDestination] = charlie.human();
2485 auto tx = vault.clawback(
2489 .amount = asset(50)});
2500 {.depositor = owner,
2502 .amount = share(50'000'000)}));
2504 env(vault.del({.owner = owner, .id = keylet.key}));
2510 Account
const& owner,
2511 Account
const& issuer,
2512 Account
const& charlie,
2517 testcase(
"IOU transfer fees not applied");
2519 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2524 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2528 Asset const share =
Asset(issuanceId(keylet));
2531 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2533 env.balance(vaultAccount(keylet), issue) == asset(100));
2536 auto tx = vault.clawback(
2540 .amount = asset(50)});
2546 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2547 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(50));
2550 {.depositor = owner,
2552 .amount = share(20'000'000)}));
2555 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2556 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(30));
2559 auto tx = vault.withdraw(
2560 {.depositor = owner,
2562 .amount = share(30'000'000)});
2563 tx[sfDestination] = charlie.human();
2568 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2569 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2570 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(0));
2572 env(vault.del({.owner = owner, .id = keylet.key}));
2578 Account
const& owner,
2579 Account
const& issuer,
2580 Account
const& charlie,
2585 testcase(
"IOU frozen trust line to depositor");
2587 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2592 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2597 auto tx = vault.withdraw(
2598 {.depositor = owner,
2600 .amount = asset(10)});
2601 tx[sfDestination] = charlie.human();
2604 env(withdrawToCharlie);
2611 auto const withdraw = vault.withdraw(
2612 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2621 auto tx = vault.deposit(
2622 {.depositor = owner,
2624 .amount = asset(10)});
2630 auto tx = vault.clawback(
2634 .amount = asset(0)});
2639 env(vault.del({.owner = owner, .id = keylet.key}));
2645 Account
const& owner,
2646 Account
const& issuer,
2647 Account
const& charlie,
2652 testcase(
"IOU no trust line to 3rd party");
2654 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2659 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2662 Account
const erin{
"erin"};
2663 env.fund(XRP(1000), erin);
2668 auto tx = vault.withdraw(
2669 {.depositor = owner,
2671 .amount = asset(10)});
2672 tx[sfDestination] = erin.human();
2680 Account
const& owner,
2681 Account
const& issuer,
2682 Account
const& charlie,
2687 testcase(
"IOU no trust line to depositor");
2689 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2694 env.trust(asset(0), owner);
2698 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2703 BEAST_EXPECT(trustline ==
nullptr);
2707 auto tx = vault.withdraw(
2708 {.depositor = owner,
2710 .amount = asset(10)});
2718 Account
const& owner,
2719 Account
const& issuer,
2720 Account
const& charlie,
2725 testcase(
"IOU frozen trust line to 3rd party");
2727 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2732 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2737 auto tx = vault.withdraw(
2738 {.depositor = owner,
2740 .amount = asset(10)});
2741 tx[sfDestination] = charlie.human();
2744 env(withdrawToCharlie);
2747 env(trust(issuer, asset(0), charlie,
tfSetFreeze));
2751 auto const withdraw = vault.withdraw(
2752 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2764 .amount = asset(0)}));
2767 env(vault.del({.owner = owner, .id = keylet.key}));
2773 Account
const& owner,
2774 Account
const& issuer,
2775 Account
const& charlie,
2782 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2787 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2795 auto tx = vault.withdraw(
2796 {.depositor = owner,
2798 .amount = asset(10)});
2802 tx[sfDestination] = charlie.human();
2808 {.depositor = owner,
2810 .amount = asset(10)});
2820 .amount = asset(0)}));
2823 env(vault.del({.owner = owner, .id = keylet.key}));
2831 using namespace test::jtx;
2835 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2836 Account issuer{
"issuer"};
2837 Account owner{
"owner"};
2838 Account depositor{
"depositor"};
2839 Account charlie{
"charlie"};
2840 Account pdOwner{
"pdOwner"};
2841 Account credIssuer1{
"credIssuer1"};
2842 Account credIssuer2{
"credIssuer2"};
2860 env.trust(asset(1000), owner);
2861 env(pay(issuer, owner, asset(500)));
2862 env.trust(asset(1000), depositor);
2863 env(pay(issuer, depositor, asset(500)));
2864 env.trust(asset(1000), charlie);
2865 env(pay(issuer, charlie, asset(5)));
2868 auto [tx, keylet] = vault.create(
2872 BEAST_EXPECT(env.le(keylet));
2875 testcase(
"private vault owner can deposit");
2876 auto tx = vault.deposit(
2877 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
2882 testcase(
"private vault depositor not authorized yet");
2883 auto tx = vault.deposit(
2884 {.depositor = depositor,
2886 .amount = asset(50)});
2891 testcase(
"private vault cannot set non-existing domain");
2892 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2898 testcase(
"private vault set domainId");
2901 pdomain::Credentials
const credentials1{
2902 {.issuer = credIssuer1, .credType = credType}};
2904 env(pdomain::setTx(pdOwner, credentials1));
2905 auto const domainId1 = [&]() {
2907 return pdomain::getNewDomain(env.meta());
2910 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2921 pdomain::Credentials
const credentials{
2922 {.issuer = credIssuer1, .credType = credType},
2923 {.issuer = credIssuer2, .credType = credType}};
2925 env(pdomain::setTx(pdOwner, credentials));
2926 auto const domainId = [&]() {
2928 return pdomain::getNewDomain(env.meta());
2931 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2937 tx = vault.set({.owner = owner, .id = keylet.
key});
2945 testcase(
"private vault depositor still not authorized");
2946 auto tx = vault.deposit(
2947 {.depositor = depositor,
2949 .amount = asset(50)});
2954 auto const credKeylet =
2955 credentials::keylet(depositor, credIssuer1, credType);
2957 testcase(
"private vault depositor now authorized");
2958 env(credentials::create(depositor, credIssuer1, credType));
2959 env(credentials::accept(depositor, credIssuer1, credType));
2960 env(credentials::create(charlie, credIssuer1, credType));
2963 auto credSle = env.le(credKeylet);
2964 BEAST_EXPECT(credSle !=
nullptr);
2966 auto tx = vault.deposit(
2967 {.depositor = depositor,
2969 .amount = asset(50)});
2974 {.depositor = charlie, .id = keylet.
key, .amount = asset(50)});
2980 testcase(
"private vault depositor lost authorization");
2981 env(credentials::deleteCred(
2982 credIssuer1, depositor, credIssuer1, credType));
2983 env(credentials::deleteCred(
2984 credIssuer1, charlie, credIssuer1, credType));
2986 auto credSle = env.le(credKeylet);
2987 BEAST_EXPECT(credSle ==
nullptr);
2989 auto tx = vault.deposit(
2990 {.depositor = depositor,
2992 .amount = asset(50)});
2997 auto const shares = [&env, keylet = keylet,
this]() ->
Asset {
2998 auto const vault = env.le(keylet);
2999 BEAST_EXPECT(vault !=
nullptr);
3000 return MPTIssue(vault->at(sfShareMPTID));
3004 testcase(
"private vault expired authorization");
3005 uint32_t
const closeTime = env.current()
3007 .parentCloseTime.time_since_epoch()
3011 credentials::create(depositor, credIssuer2, credType);
3012 tx0[sfExpiration] = closeTime + 20;
3014 tx0 = credentials::create(charlie, credIssuer2, credType);
3015 tx0[sfExpiration] = closeTime + 20;
3019 env(credentials::accept(depositor, credIssuer2, credType));
3020 env(credentials::accept(charlie, credIssuer2, credType));
3025 auto tx1 = vault.deposit(
3026 {.depositor = depositor,
3028 .amount = asset(50)});
3034 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
3043 auto const credsKeylet =
3044 credentials::keylet(depositor, credIssuer2, credType);
3045 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3047 auto tx2 = vault.deposit(
3048 {.depositor = depositor,
3050 .amount = asset(1)});
3054 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3058 auto const credsKeylet =
3059 credentials::keylet(charlie, credIssuer2, credType);
3060 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3063 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3065 auto tx3 = vault.deposit(
3066 {.depositor = charlie,
3068 .amount = asset(2)});
3072 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3073 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3078 testcase(
"private vault reset domainId");
3079 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3080 tx[sfDomainID] =
"0";
3085 {.depositor = depositor,
3087 .amount = asset(50)});
3091 tx = vault.withdraw(
3092 {.depositor = depositor,
3094 .amount = asset(50)});
3098 tx = vault.clawback(
3101 .holder = depositor,
3102 .amount = asset(0)});
3105 tx = vault.clawback(
3109 .amount = asset(0)});
3258 using namespace test::jtx;
3262 Account
const& owner;
3263 Account
const& issuer;
3264 Account
const& depositor;
3265 Account
const& vaultAccount;
3275 auto testCase = [&,
this](
3278 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3279 Account
const owner{
"owner"};
3280 Account
const issuer{
"issuer"};
3281 Account
const depositor{
"depositor"};
3283 env.fund(XRP(1000), issuer, owner, depositor);
3288 env.trust(asset(1000), owner);
3289 env.trust(asset(1000), depositor);
3290 env(pay(issuer, owner, asset(200)));
3291 env(pay(issuer, depositor, asset(200)));
3294 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3295 tx[sfScale] = scale;
3298 auto const [vaultAccount, issuanceId] =
3300 auto const vault = env.le(keylet);
3302 Account(
"vault", vault->at(sfAccount)),
3303 vault->at(sfShareMPTID)};
3306 env.memoize(vaultAccount);
3310 return env.app().openLedger().modify(
3314 if (!BEAST_EXPECT(vault !=
nullptr))
3316 auto shares = sb.
peek(
3318 if (!BEAST_EXPECT(shares !=
nullptr))
3320 if (fn(*vault, *shares))
3335 .depositor = depositor,
3336 .vaultAccount = vaultAccount,
3346 testCase(18, [&,
this](Env& env, Data d) {
3347 testcase(
"Scale deposit overflow on first deposit");
3348 auto tx = d.vault.deposit(
3349 {.depositor = d.depositor,
3351 .amount = d.asset(10)});
3356 testCase(18, [&,
this](Env& env, Data d) {
3357 testcase(
"Scale deposit overflow on second deposit");
3360 auto tx = d.vault.deposit(
3361 {.depositor = d.depositor,
3363 .amount = d.asset(5)});
3369 auto tx = d.vault.deposit(
3370 {.depositor = d.depositor,
3372 .amount = d.asset(10)});
3378 testCase(18, [&,
this](Env& env, Data d) {
3379 testcase(
"Scale deposit overflow on total shares");
3382 auto tx = d.vault.deposit(
3383 {.depositor = d.depositor,
3385 .amount = d.asset(5)});
3391 auto tx = d.vault.deposit(
3392 {.depositor = d.depositor,
3394 .amount = d.asset(5)});
3400 testCase(1, [&,
this](Env& env, Data d) {
3403 auto const start = env.balance(d.depositor, d.assets).number();
3404 auto tx = d.vault.deposit(
3405 {.depositor = d.depositor,
3407 .amount = d.asset(1)});
3410 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3412 env.balance(d.depositor, d.assets) ==
3416 testCase(1, [&,
this](Env& env, Data d) {
3417 testcase(
"Scale deposit insignificant amount");
3419 auto tx = d.vault.deposit(
3420 {.depositor = d.depositor,
3426 testCase(1, [&,
this](Env& env, Data d) {
3427 testcase(
"Scale deposit exact, using full precision");
3429 auto const start = env.balance(d.depositor, d.assets).number();
3430 auto tx = d.vault.deposit(
3431 {.depositor = d.depositor,
3436 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3438 env.balance(d.depositor, d.assets) ==
3442 testCase(1, [&,
this](Env& env, Data d) {
3443 testcase(
"Scale deposit exact, truncating from .5");
3445 auto const start = env.balance(d.depositor, d.assets).number();
3449 auto tx = d.vault.deposit(
3450 {.depositor = d.depositor,
3455 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3457 env.balance(d.depositor, d.assets) ==
3462 auto tx = d.vault.deposit(
3463 {.depositor = d.depositor,
3468 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3470 env.balance(d.depositor, d.assets) ==
3475 auto tx = d.vault.deposit(
3476 {.depositor = d.depositor,
3481 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
3483 env.balance(d.depositor, d.assets) ==
3488 testCase(1, [&,
this](Env& env, Data d) {
3489 testcase(
"Scale deposit exact, truncating from .01");
3491 auto const start = env.balance(d.depositor, d.assets).number();
3493 auto tx = d.vault.deposit(
3494 {.depositor = d.depositor,
3499 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3501 env.balance(d.depositor, d.assets) ==
3506 auto tx = d.vault.deposit(
3507 {.depositor = d.depositor,
3512 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3514 env.balance(d.depositor, d.assets) ==
3519 testCase(1, [&,
this](Env& env, Data d) {
3520 testcase(
"Scale deposit exact, truncating from .99");
3522 auto const start = env.balance(d.depositor, d.assets).number();
3524 auto tx = d.vault.deposit(
3525 {.depositor = d.depositor,
3530 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3532 env.balance(d.depositor, d.assets) ==
3537 auto tx = d.vault.deposit(
3538 {.depositor = d.depositor,
3543 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3545 env.balance(d.depositor, d.assets) ==
3550 testCase(1, [&,
this](Env& env, Data d) {
3552 auto const start = env.balance(d.depositor, d.assets).number();
3553 auto tx = d.vault.deposit(
3554 {.depositor = d.depositor,
3559 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3561 env.balance(d.depositor, d.assets) ==
3564 env.balance(d.vaultAccount, d.assets) ==
3567 env.balance(d.vaultAccount, d.shares) ==
3576 auto const start = env.balance(d.depositor, d.assets).number();
3577 auto tx = d.vault.withdraw(
3578 {.depositor = d.depositor,
3584 env.balance(d.depositor, d.shares) == d.share(900));
3586 env.balance(d.depositor, d.assets) ==
3589 env.balance(d.vaultAccount, d.assets) ==
3592 env.balance(d.vaultAccount, d.shares) ==
3597 testcase(
"Scale redeem with rounding");
3602 auto const start = env.balance(d.depositor, d.assets).number();
3603 d.peek([](
SLE& vault,
auto&) ->
bool {
3604 vault[sfAssetsAvailable] =
Number(1);
3612 auto tx = d.vault.withdraw(
3613 {.depositor = d.depositor,
3619 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3621 env.balance(d.depositor, d.assets) ==
3624 env.balance(d.vaultAccount, d.assets) ==
3627 env.balance(d.vaultAccount, d.shares) ==
3637 auto const start = env.balance(d.depositor, d.assets).number();
3639 tx = d.vault.withdraw(
3640 {.depositor = d.depositor,
3646 env.balance(d.depositor, d.shares) == d.share(875 - 21));
3648 env.balance(d.depositor, d.assets) ==
3651 env.balance(d.vaultAccount, d.assets) ==
3654 env.balance(d.vaultAccount, d.shares) ==
3660 auto const rest = env.balance(d.depositor, d.shares).number();
3662 tx = d.vault.withdraw(
3663 {.depositor = d.depositor,
3665 .amount =
STAmount(d.share, rest)});
3668 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3670 env.balance(d.vaultAccount, d.assets).number() == 0);
3672 env.balance(d.vaultAccount, d.shares).number() == 0);
3676 testCase(18, [&,
this](Env& env, Data d) {
3677 testcase(
"Scale withdraw overflow");
3680 auto tx = d.vault.deposit(
3681 {.depositor = d.depositor,
3683 .amount = d.asset(5)});
3689 auto tx = d.vault.withdraw(
3690 {.depositor = d.depositor,
3698 testCase(1, [&,
this](Env& env, Data d) {
3700 auto const start = env.balance(d.depositor, d.assets).number();
3701 auto tx = d.vault.deposit(
3702 {.depositor = d.depositor,
3707 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3709 env.balance(d.depositor, d.assets) ==
3712 env.balance(d.vaultAccount, d.assets) ==
3715 env.balance(d.vaultAccount, d.shares) ==
3727 auto const start = env.balance(d.depositor, d.assets).number();
3728 auto tx = d.vault.withdraw(
3729 {.depositor = d.depositor,
3735 env.balance(d.depositor, d.shares) == d.share(900));
3737 env.balance(d.depositor, d.assets) ==
3740 env.balance(d.vaultAccount, d.assets) ==
3743 env.balance(d.vaultAccount, d.shares) ==
3748 testcase(
"Scale withdraw insignificant amount");
3749 auto tx = d.vault.withdraw(
3750 {.depositor = d.depositor,
3757 testcase(
"Scale withdraw with rounding assets");
3765 auto const start = env.balance(d.depositor, d.assets).number();
3766 d.peek([](
SLE& vault,
auto&) ->
bool {
3767 vault[sfAssetsAvailable] =
Number(1);
3775 auto tx = d.vault.withdraw(
3776 {.depositor = d.depositor,
3782 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3784 env.balance(d.depositor, d.assets) ==
3787 env.balance(d.vaultAccount, d.assets) ==
3790 env.balance(d.vaultAccount, d.shares) ==
3795 testcase(
"Scale withdraw with rounding shares up");
3803 auto const start = env.balance(d.depositor, d.assets).number();
3804 auto tx = d.vault.withdraw(
3805 {.depositor = d.depositor,
3811 env.balance(d.depositor, d.shares) == d.share(875 - 38));
3813 env.balance(d.depositor, d.assets) ==
3816 env.balance(d.vaultAccount, d.assets) ==
3819 env.balance(d.vaultAccount, d.shares) ==
3824 testcase(
"Scale withdraw with rounding shares down");
3832 auto const start = env.balance(d.depositor, d.assets).number();
3833 auto tx = d.vault.withdraw(
3834 {.depositor = d.depositor,
3840 env.balance(d.depositor, d.shares) == d.share(837 - 37));
3842 env.balance(d.depositor, d.assets) ==
3845 env.balance(d.vaultAccount, d.assets) ==
3848 env.balance(d.vaultAccount, d.shares) ==
3853 testcase(
"Scale withdraw tiny amount");
3855 auto const start = env.balance(d.depositor, d.assets).number();
3856 auto tx = d.vault.withdraw(
3857 {.depositor = d.depositor,
3863 env.balance(d.depositor, d.shares) == d.share(800 - 1));
3865 env.balance(d.depositor, d.assets) ==
3868 env.balance(d.vaultAccount, d.assets) ==
3871 env.balance(d.vaultAccount, d.shares) ==
3878 env.balance(d.vaultAccount, d.assets).number();
3880 tx = d.vault.withdraw(
3881 {.depositor = d.depositor,
3883 .amount =
STAmount(d.asset, rest)});
3886 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3888 env.balance(d.vaultAccount, d.assets).number() == 0);
3890 env.balance(d.vaultAccount, d.shares).number() == 0);
3894 testCase(18, [&,
this](Env& env, Data d) {
3895 testcase(
"Scale clawback overflow");
3898 auto tx = d.vault.deposit(
3899 {.depositor = d.depositor,
3901 .amount = d.asset(5)});
3907 auto tx = d.vault.clawback(
3908 {.issuer = d.issuer,
3910 .holder = d.depositor,
3917 testCase(1, [&,
this](Env& env, Data d) {
3919 auto const start = env.balance(d.depositor, d.assets).number();
3920 auto tx = d.vault.deposit(
3921 {.depositor = d.depositor,
3926 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3928 env.balance(d.depositor, d.assets) ==
3931 env.balance(d.vaultAccount, d.assets) ==
3934 env.balance(d.vaultAccount, d.shares) ==
3945 auto const start = env.balance(d.depositor, d.assets).number();
3946 auto tx = d.vault.clawback(
3947 {.issuer = d.issuer,
3949 .holder = d.depositor,
3954 env.balance(d.depositor, d.shares) == d.share(900));
3956 env.balance(d.depositor, d.assets) ==
3959 env.balance(d.vaultAccount, d.assets) ==
3962 env.balance(d.vaultAccount, d.shares) ==
3967 testcase(
"Scale clawback insignificant amount");
3968 auto tx = d.vault.clawback(
3969 {.issuer = d.issuer,
3971 .holder = d.depositor,
3977 testcase(
"Scale clawback with rounding assets");
3985 auto const start = env.balance(d.depositor, d.assets).number();
3986 auto tx = d.vault.clawback(
3987 {.issuer = d.issuer,
3989 .holder = d.depositor,
3994 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3996 env.balance(d.depositor, d.assets) ==
3999 env.balance(d.vaultAccount, d.assets) ==
4002 env.balance(d.vaultAccount, d.shares) ==
4007 testcase(
"Scale clawback with rounding shares up");
4015 auto const start = env.balance(d.depositor, d.assets).number();
4016 auto tx = d.vault.clawback(
4017 {.issuer = d.issuer,
4019 .holder = d.depositor,
4024 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4026 env.balance(d.depositor, d.assets) ==
4029 env.balance(d.vaultAccount, d.assets) ==
4032 env.balance(d.vaultAccount, d.shares) ==
4037 testcase(
"Scale clawback with rounding shares down");
4045 auto const start = env.balance(d.depositor, d.assets).number();
4046 auto tx = d.vault.clawback(
4047 {.issuer = d.issuer,
4049 .holder = d.depositor,
4054 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4056 env.balance(d.depositor, d.assets) ==
4059 env.balance(d.vaultAccount, d.assets) ==
4062 env.balance(d.vaultAccount, d.shares) ==
4067 testcase(
"Scale clawback tiny amount");
4069 auto const start = env.balance(d.depositor, d.assets).number();
4070 auto tx = d.vault.clawback(
4071 {.issuer = d.issuer,
4073 .holder = d.depositor,
4078 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4080 env.balance(d.depositor, d.assets) ==
4083 env.balance(d.vaultAccount, d.assets) ==
4086 env.balance(d.vaultAccount, d.shares) ==
4093 env.balance(d.vaultAccount, d.assets).number();
4094 d.peek([](
SLE& vault,
auto&) ->
bool {
4095 vault[sfAssetsAvailable] =
Number(5);
4103 tx = d.vault.clawback(
4104 {.issuer = d.issuer,
4106 .holder = d.depositor,
4107 .amount =
STAmount(d.asset, rest)});
4110 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4112 env.balance(d.vaultAccount, d.assets).number() == 0);
4114 env.balance(d.vaultAccount, d.shares).number() == 0);
4122 using namespace test::jtx;
4125 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4126 Account
const owner{
"owner"};
4127 Account
const issuer{
"issuer"};
4129 env.fund(XRP(1000), issuer, owner);
4133 env.trust(asset(1000), owner);
4134 env(pay(issuer, owner, asset(200)));
4137 auto const sequence = env.seq(owner);
4138 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
4144 auto tx1 = vault.deposit(
4145 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
4148 auto tx2 = vault.set({.owner = owner, .id = keylet.
key});
4149 tx2[sfAssetsMaximum] = asset(1000).number();
4154 auto const sleVault = [&env, keylet = keylet,
this]() {
4155 auto const vault = env.le(keylet);
4156 BEAST_EXPECT(vault !=
nullptr);
4160 auto const check = [&, keylet = keylet, sle = sleVault,
this](
4163 BEAST_EXPECT(vault.isObject());
4165 constexpr auto checkString =
4167 return node.isMember(field.fieldName) &&
4168 node[field.fieldName].isString() &&
4169 node[field.fieldName] == v;
4171 constexpr auto checkObject =
4173 return node.isMember(field.fieldName) &&
4174 node[field.fieldName].isObject() &&
4175 node[field.fieldName] == v;
4177 constexpr auto checkInt =
4178 [](
auto& node,
SField const& field,
int v) ->
bool {
4179 return node.isMember(field.fieldName) &&
4180 ((node[field.fieldName].isInt() &&
4181 node[field.fieldName] ==
Json::Int(v)) ||
4182 (node[field.fieldName].isUInt() &&
4186 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
4187 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(keylet.
key));
4188 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
4192 checkString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
4194 checkObject(vault, sfAsset,
to_json(sle->at(sfAsset))));
4195 BEAST_EXPECT(checkString(vault, sfAssetsAvailable,
"50"));
4196 BEAST_EXPECT(checkString(vault, sfAssetsMaximum,
"1000"));
4197 BEAST_EXPECT(checkString(vault, sfAssetsTotal,
"50"));
4198 BEAST_EXPECT(checkString(vault, sfLossUnrealized,
"0"));
4200 auto const strShareID =
strHex(sle->at(sfShareMPTID));
4201 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
4202 BEAST_EXPECT(checkString(vault, sfOwner,
toBase58(owner.id())));
4203 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
4204 BEAST_EXPECT(checkInt(
4207 if (issuance.isObject())
4210 issuance[
"LedgerEntryType"].asString() ==
4213 issuance[jss::mpt_issuance_id].asString() == strShareID);
4214 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
4215 BEAST_EXPECT(checkInt(
4220 checkString(issuance, sfOutstandingAmount,
"50000000"));
4225 testcase(
"RPC ledger_entry selected by key");
4227 jvParams[jss::ledger_index] = jss::validated;
4228 jvParams[jss::vault] =
strHex(keylet.
key);
4229 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4231 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4232 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4233 check(jvVault[jss::result][jss::node]);
4237 testcase(
"RPC ledger_entry selected by owner and seq");
4239 jvParams[jss::ledger_index] = jss::validated;
4240 jvParams[jss::vault][jss::owner] = owner.human();
4241 jvParams[jss::vault][jss::seq] = sequence;
4242 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4244 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4245 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4246 check(jvVault[jss::result][jss::node]);
4250 testcase(
"RPC ledger_entry cannot find vault by key");
4252 jvParams[jss::ledger_index] = jss::validated;
4254 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4256 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4260 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
4262 jvParams[jss::ledger_index] = jss::validated;
4263 jvParams[jss::vault][jss::owner] = issuer.human();
4264 jvParams[jss::vault][jss::seq] = 1'000'000;
4265 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4267 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4271 testcase(
"RPC ledger_entry malformed key");
4273 jvParams[jss::ledger_index] = jss::validated;
4274 jvParams[jss::vault] = 42;
4275 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4277 jvVault[jss::result][jss::error].asString() ==
4278 "malformedRequest");
4282 testcase(
"RPC ledger_entry malformed owner");
4284 jvParams[jss::ledger_index] = jss::validated;
4285 jvParams[jss::vault][jss::owner] = 42;
4286 jvParams[jss::vault][jss::seq] = sequence;
4287 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4289 jvVault[jss::result][jss::error].asString() ==
4294 testcase(
"RPC ledger_entry malformed seq");
4296 jvParams[jss::ledger_index] = jss::validated;
4297 jvParams[jss::vault][jss::owner] = issuer.human();
4298 jvParams[jss::vault][jss::seq] =
"foo";
4299 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4301 jvVault[jss::result][jss::error].asString() ==
4302 "malformedRequest");
4306 testcase(
"RPC ledger_entry negative seq");
4308 jvParams[jss::ledger_index] = jss::validated;
4309 jvParams[jss::vault][jss::owner] = issuer.human();
4310 jvParams[jss::vault][jss::seq] = -1;
4311 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4313 jvVault[jss::result][jss::error].asString() ==
4314 "malformedRequest");
4318 testcase(
"RPC ledger_entry oversized seq");
4320 jvParams[jss::ledger_index] = jss::validated;
4321 jvParams[jss::vault][jss::owner] = issuer.human();
4322 jvParams[jss::vault][jss::seq] = 1e20;
4323 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4325 jvVault[jss::result][jss::error].asString() ==
4326 "malformedRequest");
4330 testcase(
"RPC ledger_entry bool seq");
4332 jvParams[jss::ledger_index] = jss::validated;
4333 jvParams[jss::vault][jss::owner] = issuer.human();
4334 jvParams[jss::vault][jss::seq] =
true;
4335 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4337 jvVault[jss::result][jss::error].asString() ==
4338 "malformedRequest");
4345 jvParams[jss::account] = owner.human();
4346 jvParams[jss::type] = jss::vault;
4348 "json",
"account_objects",
to_string(jvParams))[jss::result];
4350 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
4351 check(jv[jss::account_objects][0u]);
4358 jvParams[jss::ledger_index] = jss::validated;
4359 jvParams[jss::binary] =
false;
4360 jvParams[jss::type] = jss::vault;
4362 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
4363 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
4364 check(jv[jss::result][jss::state][0u]);
4368 testcase(
"RPC vault_info command line");
4370 env.rpc(
"vault_info",
strHex(keylet.
key),
"validated");
4372 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4373 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4375 jv[jss::result][jss::vault],
4376 jv[jss::result][jss::vault][jss::shares]);
4382 jvParams[jss::ledger_index] = jss::validated;
4383 jvParams[jss::vault_id] =
strHex(keylet.
key);
4384 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4386 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4387 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4389 jv[jss::result][jss::vault],
4390 jv[jss::result][jss::vault][jss::shares]);
4394 testcase(
"RPC vault_info invalid vault_id");
4396 jvParams[jss::ledger_index] = jss::validated;
4397 jvParams[jss::vault_id] =
"foobar";
4398 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4400 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4404 testcase(
"RPC vault_info json invalid index");
4406 jvParams[jss::ledger_index] = jss::validated;
4407 jvParams[jss::vault_id] = 0;
4408 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4410 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4414 testcase(
"RPC vault_info json by owner and sequence");
4416 jvParams[jss::ledger_index] = jss::validated;
4417 jvParams[jss::owner] = owner.human();
4418 jvParams[jss::seq] = sequence;
4419 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4421 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4422 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4424 jv[jss::result][jss::vault],
4425 jv[jss::result][jss::vault][jss::shares]);
4429 testcase(
"RPC vault_info json malformed sequence");
4431 jvParams[jss::ledger_index] = jss::validated;
4432 jvParams[jss::owner] = owner.human();
4433 jvParams[jss::seq] =
"foobar";
4434 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4436 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4440 testcase(
"RPC vault_info json invalid sequence");
4442 jvParams[jss::ledger_index] = jss::validated;
4443 jvParams[jss::owner] = owner.human();
4444 jvParams[jss::seq] = 0;
4445 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4447 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4451 testcase(
"RPC vault_info json negative sequence");
4453 jvParams[jss::ledger_index] = jss::validated;
4454 jvParams[jss::owner] = owner.human();
4455 jvParams[jss::seq] = -1;
4456 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4458 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4462 testcase(
"RPC vault_info json oversized sequence");
4464 jvParams[jss::ledger_index] = jss::validated;
4465 jvParams[jss::owner] = owner.human();
4466 jvParams[jss::seq] = 1e20;
4467 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4469 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4473 testcase(
"RPC vault_info json bool sequence");
4475 jvParams[jss::ledger_index] = jss::validated;
4476 jvParams[jss::owner] = owner.human();
4477 jvParams[jss::seq] =
true;
4478 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4480 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4484 testcase(
"RPC vault_info json malformed owner");
4486 jvParams[jss::ledger_index] = jss::validated;
4487 jvParams[jss::owner] =
"foobar";
4488 jvParams[jss::seq] = sequence;
4489 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4491 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4495 testcase(
"RPC vault_info json invalid combination only owner");
4497 jvParams[jss::ledger_index] = jss::validated;
4498 jvParams[jss::owner] = owner.human();
4499 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4501 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4505 testcase(
"RPC vault_info json invalid combination only seq");
4507 jvParams[jss::ledger_index] = jss::validated;
4508 jvParams[jss::seq] = sequence;
4509 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4511 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4515 testcase(
"RPC vault_info json invalid combination seq vault_id");
4517 jvParams[jss::ledger_index] = jss::validated;
4518 jvParams[jss::vault_id] =
strHex(keylet.
key);
4519 jvParams[jss::seq] = sequence;
4520 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4522 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4526 testcase(
"RPC vault_info json invalid combination owner vault_id");
4528 jvParams[jss::ledger_index] = jss::validated;
4529 jvParams[jss::vault_id] =
strHex(keylet.
key);
4530 jvParams[jss::owner] = owner.human();
4531 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4533 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4538 "RPC vault_info json invalid combination owner seq "
4541 jvParams[jss::ledger_index] = jss::validated;
4542 jvParams[jss::vault_id] =
strHex(keylet.
key);
4543 jvParams[jss::seq] = sequence;
4544 jvParams[jss::owner] = owner.human();
4545 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4547 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4551 testcase(
"RPC vault_info json no input");
4553 jvParams[jss::ledger_index] = jss::validated;
4554 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4556 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4560 testcase(
"RPC vault_info command line invalid index");
4561 Json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
4562 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
4566 testcase(
"RPC vault_info command line invalid index");
4567 Json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
4569 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4573 testcase(
"RPC vault_info command line invalid index");
4577 jv[jss::result][jss::error].asString() ==
"entryNotFound");
4581 testcase(
"RPC vault_info command line invalid ledger");
4584 jv[jss::result][jss::error].asString() ==
"lgrNotFound");