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();
182 auto tx = vault.set({.owner = owner, .id = keylet.
key});
189 testcase(prefix +
" fail to set domain on public vault");
190 auto tx = vault.set({.owner = owner, .id = keylet.
key});
197 testcase(prefix +
" fail to deposit more than maximum");
198 auto tx = vault.deposit(
199 {.depositor = depositor,
201 .amount = asset(100)});
207 testcase(prefix +
" reset maximum to zero i.e. not enforced");
208 auto tx = vault.set({.owner = owner, .id = keylet.
key});
209 tx[sfAssetsMaximum] = asset(0).number();
215 testcase(prefix +
" fail to withdraw more than assets held");
216 auto tx = vault.withdraw(
217 {.depositor = depositor,
219 .amount = asset(1000)});
224 testcase(prefix +
" deposit some more");
225 auto tx = vault.deposit(
226 {.depositor = depositor,
228 .amount = asset(100)});
232 env.balance(depositor, shares) == share(200 * scale));
236 testcase(prefix +
" clawback some");
239 auto tx = vault.clawback(
243 .amount = asset(10)});
246 if (!asset.raw().native())
249 env.balance(depositor, shares) == share(190 * scale));
257 auto tx = vault.clawback(
258 {.issuer = issuer, .id = keylet.
key, .holder = depositor});
261 if (!asset.raw().native())
263 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
266 auto tx = vault.clawback(
270 .amount = asset(10)});
276 auto tx = vault.withdraw(
277 {.depositor = depositor,
279 .amount = asset(10)});
286 if (!asset.raw().native())
288 testcase(prefix +
" deposit again");
289 auto tx = vault.deposit(
290 {.depositor = depositor,
292 .amount = asset(200)});
296 env.balance(depositor, shares) == share(200 * scale));
301 prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
302 auto tx = vault.withdraw(
303 {.depositor = depositor,
305 .amount = asset(100)});
306 tx[sfDestination] = alice.human();
312 testcase(prefix +
" fail to withdraw to zero destination");
313 auto tx = vault.withdraw(
314 {.depositor = depositor,
316 .amount = asset(1000)});
317 tx[sfDestination] =
"0";
325 " fail to withdraw with tag but without destination");
326 auto tx = vault.withdraw(
327 {.depositor = depositor,
329 .amount = asset(1000)});
330 tx[sfDestinationTag] =
"0";
335 if (!asset.raw().native())
338 prefix +
" fail to withdraw to 3rd party no authorization");
339 auto tx = vault.withdraw(
340 {.depositor = depositor,
342 .amount = asset(100)});
343 tx[sfDestination] = erin.human();
352 " fail to withdraw to 3rd party lsfRequireDestTag");
353 auto tx = vault.withdraw(
354 {.depositor = depositor,
356 .amount = asset(100)});
357 tx[sfDestination] = dave.human();
363 testcase(prefix +
" withdraw to authorized 3rd party");
364 auto tx = vault.withdraw(
365 {.depositor = depositor,
367 .amount = asset(100)});
368 tx[sfDestination] = charlie.human();
372 env.balance(depositor, shares) == share(100 * scale));
376 testcase(prefix +
" withdraw to issuer");
377 auto tx = vault.withdraw(
378 {.depositor = depositor,
380 .amount = asset(50)});
381 tx[sfDestination] = issuer.human();
385 env.balance(depositor, shares) == share(50 * scale));
389 testcase(prefix +
" withdraw remaining assets");
390 auto tx = vault.withdraw(
391 {.depositor = depositor,
393 .amount = asset(50)});
396 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
398 if (!asset.raw().native())
400 auto tx = vault.clawback(
404 .amount = asset(0)});
410 auto tx = vault.withdraw(
411 {.depositor = depositor,
413 .amount = share(10)});
419 if (!asset.raw().native() && asset.raw().holds<
Issue>())
421 testcase(prefix +
" temporary authorization for 3rd party");
422 env(trust(erin, asset(1000)));
423 env(trust(issuer, asset(0), erin,
tfSetfAuth));
424 env(pay(issuer, erin, asset(10)));
427 auto tx = vault.deposit(
428 {.depositor = erin, .id = keylet.
key, .amount = asset(10)});
432 auto tx = pay(erin, depositor, share(10 * scale));
440 {.depositor = depositor,
442 .amount = asset(1)}));
449 testcase(prefix +
" withdraw to authorized 3rd party");
452 {.depositor = depositor,
454 .amount = asset(10)});
455 tx[sfDestination] = erin.human();
458 env(pay(erin, issuer, asset(10)));
461 testcase(prefix +
" fail to pay to unauthorized 3rd party");
462 env(trust(erin, asset(0)));
466 env(pay(depositor, erin, share(1)), ter{
tecNO_LINE});
471 {.depositor = depositor,
473 .amount = asset(1)});
479 testcase(prefix +
" fail to delete because wrong owner");
480 auto tx = vault.del({.owner = issuer, .id = keylet.
key});
485 testcase(prefix +
" delete empty vault");
486 auto tx = vault.del({.owner = owner, .id = keylet.
key});
488 BEAST_EXPECT(!env.le(keylet));
492 auto testCases = [
this, &testSequence](
496 Account
const& issuer,
497 Account
const& owner,
498 Account
const& depositor,
499 Account
const& charlie)> setup) {
500 Env env{*
this, testable_amendments() | featureSingleAssetVault};
501 Account issuer{
"issuer"};
502 Account owner{
"owner"};
503 Account depositor{
"depositor"};
504 Account charlie{
"charlie"};
506 env.fund(XRP(1000), issuer, owner, depositor, charlie);
514 PrettyAsset asset = setup(env, issuer, owner, depositor, charlie);
516 prefix, env, issuer, owner, depositor, charlie, vault, asset);
522 Account
const& issuer,
523 Account
const& owner,
524 Account
const& depositor,
532 Account
const& issuer,
533 Account
const& owner,
534 Account
const& depositor,
535 Account
const& charlie) ->
Asset {
537 env(trust(owner, asset(1000)));
538 env(trust(depositor, asset(1000)));
539 env(trust(charlie, asset(1000)));
540 env(trust(issuer, asset(0), owner,
tfSetfAuth));
541 env(trust(issuer, asset(0), depositor,
tfSetfAuth));
542 env(trust(issuer, asset(0), charlie,
tfSetfAuth));
543 env(pay(issuer, depositor, asset(1000)));
551 Account
const& issuer,
552 Account
const& owner,
553 Account
const& depositor,
554 Account
const& charlie) ->
Asset {
555 MPTTester mptt{env, issuer, mptInitNoFund};
560 mptt.authorize({.account = depositor});
561 mptt.authorize({.account = charlie});
562 env(pay(issuer, depositor, asset(1000)));
571 using namespace test::jtx;
576 testable_amendments() | featureSingleAssetVault;
579 auto testCase = [&,
this](
582 Account
const& issuer,
583 Account
const& owner,
586 CaseArgs args = {}) {
587 Env env{*
this, args.features};
588 Account issuer{
"issuer"};
589 Account owner{
"owner"};
591 env.fund(XRP(1000), issuer, owner);
599 env(trust(owner, asset(1000)));
600 env(trust(issuer, asset(0), owner,
tfSetfAuth));
601 env(pay(issuer, owner, asset(1000)));
604 test(env, issuer, owner, asset, vault);
609 Account
const& issuer,
610 Account
const& owner,
613 testcase(
"disabled single asset vault");
616 vault.create({.owner = owner, .asset = asset});
620 auto tx = vault.set({.owner = owner, .id = keylet.
key});
625 auto tx = vault.deposit(
628 .amount = asset(10)});
633 auto tx = vault.withdraw(
636 .amount = asset(10)});
641 auto tx = vault.clawback(
645 .amount = asset(10)});
650 auto tx = vault.del({.owner = owner, .id = keylet.
key});
654 {.features = testable_amendments() - featureSingleAssetVault});
656 testCase([&](Env& env,
657 Account
const& issuer,
658 Account
const& owner,
663 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
668 auto tx = vault.set({.owner = owner, .id = keylet.
key});
674 auto tx = vault.deposit(
677 .amount = asset(10)});
683 auto tx = vault.withdraw(
686 .amount = asset(10)});
692 auto tx = vault.clawback(
696 .amount = asset(10)});
702 auto tx = vault.del({.owner = owner, .id = keylet.
key});
708 testCase([&](Env& env,
709 Account
const& issuer,
710 Account
const& owner,
715 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
720 auto tx = vault.set({.owner = owner, .id = keylet.
key});
726 auto tx = vault.deposit(
729 .amount = asset(10)});
735 auto tx = vault.withdraw(
738 .amount = asset(10)});
744 auto tx = vault.clawback(
748 .amount = asset(10)});
754 auto tx = vault.del({.owner = owner, .id = keylet.
key});
763 Account
const& owner,
766 testcase(
"disabled permissioned domain");
769 vault.create({.owner = owner, .asset =
xrpIssue()});
774 auto tx = vault.set({.owner = owner, .id = keylet.
key});
780 auto tx = vault.set({.owner = owner, .id = keylet.
key});
781 tx[sfDomainID] =
"0";
785 {.features = (testable_amendments() | featureSingleAssetVault) -
786 featurePermissionedDomains});
788 testCase([&](Env& env,
789 Account
const& issuer,
790 Account
const& owner,
796 vault.create({.owner = owner, .asset =
xrpIssue()});
799 auto tx = vault.set({
807 auto tx = vault.deposit(
810 .amount = asset(10)});
815 auto tx = vault.withdraw(
818 .amount = asset(10)});
823 auto tx = vault.clawback(
827 .amount = asset(10)});
832 auto tx = vault.del({
840 testCase([&](Env& env,
841 Account
const& issuer,
842 Account
const& owner,
847 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
850 auto tx = vault.clawback(
854 .amount = asset(10)});
859 testCase([&](Env& env,
861 Account
const& owner,
864 testcase(
"withdraw to bad destination");
866 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
869 auto tx = vault.withdraw(
872 .amount = asset(10)});
873 tx[jss::Destination] =
"0";
878 testCase([&](Env& env,
880 Account
const& owner,
887 vault.create({.owner = owner, .asset = asset});
894 vault.create({.owner = owner, .asset = asset});
902 vault.create({.owner = owner, .asset = asset});
906 auto const sleVault = env.le(keylet);
907 BEAST_EXPECT(sleVault);
908 BEAST_EXPECT((*sleVault)[sfScale] == 18);
913 vault.create({.owner = owner, .asset = asset});
917 auto const sleVault = env.le(keylet);
918 BEAST_EXPECT(sleVault);
919 BEAST_EXPECT((*sleVault)[sfScale] == 0);
924 vault.create({.owner = owner, .asset = asset});
927 auto const sleVault = env.le(keylet);
928 BEAST_EXPECT(sleVault);
929 BEAST_EXPECT((*sleVault)[sfScale] == 6);
933 testCase([&](Env& env,
935 Account
const& owner,
938 testcase(
"create or set invalid data");
940 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
956 auto tx = vault.set({.owner = owner, .id = keylet.
key});
962 auto tx = vault.set({.owner = owner, .id = keylet.
key});
969 testCase([&](Env& env,
971 Account
const& owner,
976 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
979 auto tx = vault.set({.owner = owner, .id = keylet.
key});
984 testCase([&](Env& env,
986 Account
const& owner,
989 testcase(
"create with invalid metadata");
991 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
995 tx[sfMPTokenMetadata] =
"";
1008 testCase([&](Env& env,
1010 Account
const& owner,
1015 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1018 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1024 testCase([&](Env& env,
1026 Account
const& owner,
1029 testcase(
"invalid deposit amount");
1031 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1034 auto tx = vault.deposit(
1035 {.depositor = owner,
1042 auto tx = vault.deposit(
1043 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1048 testCase([&](Env& env,
1050 Account
const& owner,
1053 testcase(
"invalid set immutable flag");
1055 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1058 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1064 testCase([&](Env& env,
1066 Account
const& owner,
1069 testcase(
"invalid withdraw amount");
1071 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1074 auto tx = vault.withdraw(
1075 {.depositor = owner,
1082 auto tx = vault.withdraw(
1083 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1088 testCase([&](Env& env,
1089 Account
const& issuer,
1090 Account
const& owner,
1095 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1098 auto tx = vault.clawback(
1102 .amount = asset(50)});
1107 auto tx = vault.clawback(
1116 testCase([&](Env& env,
1118 Account
const& owner,
1123 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1127 tx[sfWithdrawalPolicy] = 0;
1146 tx[sfDomainID] =
"0";
1615 using namespace test::jtx;
1619 bool enableClawback =
true;
1623 auto testCase = [
this](
1626 Account
const& issuer,
1627 Account
const& owner,
1628 Account
const& depositor,
1631 MPTTester& mptt)> test,
1632 CaseArgs args = {}) {
1633 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1634 Account issuer{
"issuer"};
1635 Account owner{
"owner"};
1636 Account depositor{
"depositor"};
1637 env.fund(XRP(1000), issuer, owner, depositor);
1641 MPTTester mptt{env, issuer, mptInitNoFund};
1648 mptt.authorize({.account = owner});
1649 mptt.authorize({.account = depositor});
1650 if (args.requireAuth)
1652 mptt.authorize({.account = issuer, .holder = owner});
1653 mptt.authorize({.account = issuer, .holder = depositor});
1656 env(pay(issuer, depositor, asset(1000)));
1659 test(env, issuer, owner, depositor, asset, vault, mptt);
1664 Account
const& issuer,
1665 Account
const& owner,
1666 Account
const& depositor,
1670 testcase(
"MPT nothing to clawback from");
1671 auto tx = vault.clawback(
1674 .holder = depositor,
1675 .amount = asset(10)});
1681 Account
const& issuer,
1682 Account
const& owner,
1683 Account
const& depositor,
1687 testcase(
"MPT global lock blocks create");
1688 mptt.set({.account = issuer, .flags =
tfMPTLock});
1689 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1695 Account
const& issuer,
1696 Account
const& owner,
1697 Account
const& depositor,
1701 testcase(
"MPT global lock blocks deposit");
1702 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1706 mptt.set({.account = issuer, .flags =
tfMPTLock});
1710 {.depositor = depositor,
1712 .amount = asset(100)});
1717 tx = vault.del({.owner = owner, .id = keylet.
key});
1723 Account
const& issuer,
1724 Account
const& owner,
1725 Account
const& depositor,
1729 testcase(
"MPT global lock blocks withdrawal");
1730 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1734 {.depositor = depositor,
1736 .amount = asset(100)});
1742 auto v = env.le(keylet);
1744 MPTID share = (*v)[sfShareMPTID];
1746 BEAST_EXPECT(issuance);
1747 Number outstandingShares = issuance->at(sfOutstandingAmount);
1748 BEAST_EXPECT(outstandingShares == 100);
1750 mptt.set({.account = issuer, .flags =
tfMPTLock});
1753 tx = vault.withdraw(
1754 {.depositor = depositor,
1756 .amount = asset(100)});
1759 tx[sfDestination] = issuer.human();
1763 tx = vault.clawback(
1766 .holder = depositor,
1767 .amount = asset(0)});
1773 BEAST_EXPECT(mptSle ==
nullptr);
1776 tx = vault.del({.owner = owner, .id = keylet.
key});
1782 Account
const& issuer,
1783 Account
const& owner,
1784 Account
const& depositor,
1788 testcase(
"MPT only issuer can clawback");
1790 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1795 {.depositor = depositor,
1797 .amount = asset(100)});
1802 auto tx = vault.clawback(
1803 {.issuer = owner, .id = keylet.
key, .holder = depositor});
1811 Account
const& issuer,
1812 Account
const& owner,
1813 Account
const& depositor,
1818 "MPT 3rd party without MPToken cannot be withdrawal "
1822 vault.create({.owner = owner, .asset = asset});
1827 {.depositor = depositor,
1829 .amount = asset(100)});
1835 Account charlie{
"charlie"};
1836 env.fund(XRP(1000), charlie);
1839 tx = vault.withdraw(
1840 {.depositor = depositor,
1842 .amount = asset(100)});
1843 tx[sfDestination] = charlie.human();
1847 {.requireAuth =
false});
1852 Account
const& issuer,
1853 Account
const& owner,
1854 Account
const& depositor,
1858 testcase(
"MPT depositor without MPToken cannot withdraw");
1861 vault.create({.owner = owner, .asset = asset});
1864 auto v = env.le(keylet);
1866 MPTID share = (*v)[sfShareMPTID];
1869 {.depositor = depositor,
1871 .amount = asset(1000)});
1880 auto const mptoken =
1882 BEAST_EXPECT(mptoken ==
nullptr);
1884 tx = vault.withdraw(
1885 {.depositor = depositor,
1887 .amount = asset(100)});
1893 mptt.authorize({.account = depositor});
1896 tx = vault.withdraw(
1897 {.depositor = depositor,
1899 .amount = asset(1000)});
1906 BEAST_EXPECT(mptSle ==
nullptr);
1909 {.requireAuth =
false});
1913 Account
const& issuer,
1914 Account
const& owner,
1915 Account
const& depositor,
1921 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1926 {.depositor = depositor,
1928 .amount = asset(1000)});
1933 auto tx = vault.clawback(
1936 .holder = depositor,
1937 .amount = asset(0)});
1941 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
1946 vault.create({.owner = depositor, .asset = asset});
1951 auto tx = vault.deposit(
1952 {.depositor = depositor,
1954 .amount = asset(10)});
1959 auto tx = vault.withdraw(
1960 {.depositor = depositor,
1962 .amount = asset(10)});
1967 auto tx = vault.clawback(
1970 .holder = depositor,
1971 .amount = asset(0)});
1975 env(vault.del({.owner = owner, .id = keylet.key}));
1980 Account
const& issuer,
1981 Account
const& owner,
1982 Account
const& depositor,
1986 testcase(
"MPT vault owner can receive shares unless unauthorized");
1988 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1993 {.depositor = depositor,
1995 .amount = asset(1000)});
2000 auto const vault = env.le(keylet);
2001 return vault->at(sfShareMPTID);
2007 env(pay(depositor, owner, shares(1)));
2010 tx = vault.withdraw(
2011 {.depositor = owner,
2013 .amount = shares(1)});
2018 env(pay(depositor, owner, shares(1)));
2021 tx = vault.clawback(
2025 .amount = asset(0)});
2030 env(pay(depositor, owner, shares(1)));
2034 env(pay(owner, depositor, shares(1)));
2040 jv[sfAccount] = owner.human();
2041 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
2043 jv[sfTransactionType] = jss::MPTokenAuthorize;
2049 tx = pay(depositor, owner, shares(1));
2054 tx = vault.clawback(
2057 .holder = depositor,
2058 .amount = asset(0)});
2063 env(vault.del({.owner = owner, .id = keylet.key}));
2071 Account
const& issuer,
2072 Account
const& owner,
2073 Account
const& depositor,
2080 vault.create({.owner = owner, .asset = asset});
2085 {.depositor = depositor,
2087 .amount = asset(1000)});
2092 auto tx = vault.clawback(
2095 .holder = depositor,
2096 .amount = asset(0)});
2100 {.enableClawback =
false});
2104 Account
const& issuer,
2105 Account
const& owner,
2106 Account
const& depositor,
2111 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2115 {.depositor = depositor,
2117 .amount = asset(1000)});
2123 .holder = depositor,
2128 auto tx = vault.withdraw(
2129 {.depositor = depositor,
2131 .amount = asset(100)});
2135 tx[sfDestination] = issuer.human();
2139 tx[sfDestination] = owner.human();
2146 auto tx = vault.deposit(
2147 {.depositor = depositor,
2149 .amount = asset(100)});
2154 tx = vault.clawback(
2157 .holder = depositor,
2158 .amount = asset(800)});
2162 env(vault.del({.owner = owner, .id = keylet.key}));
2167 Account
const& issuer,
2168 Account
const& owner,
2169 Account
const& depositor,
2173 testcase(
"MPT lock of vault pseudo-account");
2174 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2178 auto const vaultAccount =
2179 [&env, keylet = keylet,
this]() ->
AccountID {
2180 auto const vault = env.le(keylet);
2181 BEAST_EXPECT(vault !=
nullptr);
2182 return vault->at(sfAccount);
2186 {.depositor = depositor,
2188 .amount = asset(100)});
2194 jv[jss::Account] = issuer.human();
2195 jv[sfMPTokenIssuanceID] =
2197 jv[jss::Holder] =
toBase58(vaultAccount);
2198 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
2206 {.depositor = depositor,
2208 .amount = asset(100)});
2211 tx = vault.withdraw(
2212 {.depositor = depositor,
2214 .amount = asset(100)});
2218 tx = vault.clawback(
2221 .holder = depositor,
2222 .amount = asset(100)});
2226 tx = vault.del({.owner = owner, .id = keylet.
key});
2233 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2234 Account owner{
"owner"};
2235 Account issuer{
"issuer"};
2236 env.fund(XRP(1000000), owner, issuer);
2240 MPTTester mptt{env, issuer, mptInitNoFund};
2244 mptt.authorize({.account = owner});
2245 mptt.authorize({.account = issuer, .holder = owner});
2247 env(pay(issuer, owner, asset(100)));
2248 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
2252 auto const shares = [&env, keylet = k1,
this]() ->
Asset {
2253 auto const vault = env.le(keylet);
2254 BEAST_EXPECT(vault !=
nullptr);
2255 return MPTIssue(vault->at(sfShareMPTID));
2258 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
2267 using namespace test::jtx;
2273 Account
const& owner,
2274 Account
const& issuer,
2275 Account
const& charlie,
2280 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2281 Account
const owner{
"owner"};
2282 Account
const issuer{
"issuer"};
2283 Account
const charlie{
"charlie"};
2285 env.fund(XRP(1000), issuer, owner, charlie);
2290 env.trust(asset(1000), owner);
2291 env.trust(asset(1000), charlie);
2292 env(pay(issuer, owner, asset(200)));
2293 env(rate(issuer, 1.25));
2296 auto const vaultAccount =
2298 return Account(
"vault", env.le(keylet)->at(sfAccount));
2301 return env.le(keylet)->at(sfShareMPTID);
2317 Account
const& owner,
2318 Account
const& issuer,
2324 testcase(
"IOU cannot use different asset");
2327 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2333 auto tx = [&, account = vaultAccount(keylet)]() {
2335 jv[jss::Account] = issuer.human();
2337 auto& ja = jv[jss::LimitAmount] =
2339 ja[jss::issuer] =
toBase58(account);
2341 jv[jss::TransactionType] = jss::TrustSet;
2350 auto tx = vault.deposit(
2351 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2357 auto tx = vault.withdraw(
2358 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2363 env(vault.del({.owner = owner, .id = keylet.key}));
2369 Account
const& owner,
2370 Account
const& issuer,
2371 Account
const& charlie,
2376 testcase(
"IOU frozen trust line to vault account");
2378 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2383 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2386 Asset const share =
Asset(issuanceId(keylet));
2389 auto trustSet = [&, account = vaultAccount(keylet)]() {
2391 jv[jss::Account] = issuer.human();
2393 auto& ja = jv[jss::LimitAmount] =
2395 ja[jss::issuer] =
toBase58(account);
2397 jv[jss::TransactionType] = jss::TrustSet;
2409 auto tx = vault.deposit(
2410 {.depositor = owner,
2412 .amount = asset(80)});
2417 auto tx = vault.withdraw(
2418 {.depositor = owner,
2420 .amount = asset(100)});
2424 tx[sfDestination] = charlie.human();
2431 auto tx = vault.clawback(
2435 .amount = asset(50)});
2446 {.depositor = owner,
2448 .amount = share(50'000'000)}));
2450 env(vault.del({.owner = owner, .id = keylet.key}));
2456 Account
const& owner,
2457 Account
const& issuer,
2458 Account
const& charlie,
2463 testcase(
"IOU transfer fees not applied");
2465 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2470 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2474 Asset const share =
Asset(issuanceId(keylet));
2477 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2479 env.balance(vaultAccount(keylet), issue) == asset(100));
2482 auto tx = vault.clawback(
2486 .amount = asset(50)});
2492 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2493 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(50));
2496 {.depositor = owner,
2498 .amount = share(20'000'000)}));
2501 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2502 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(30));
2505 auto tx = vault.withdraw(
2506 {.depositor = owner,
2508 .amount = share(30'000'000)});
2509 tx[sfDestination] = charlie.human();
2514 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2515 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2516 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(0));
2518 env(vault.del({.owner = owner, .id = keylet.key}));
2524 Account
const& owner,
2525 Account
const& issuer,
2526 Account
const& charlie,
2531 testcase(
"IOU frozen trust line to depositor");
2533 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2538 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2543 auto tx = vault.withdraw(
2544 {.depositor = owner,
2546 .amount = asset(10)});
2547 tx[sfDestination] = charlie.human();
2550 env(withdrawToCharlie);
2557 auto const withdraw = vault.withdraw(
2558 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2567 auto tx = vault.deposit(
2568 {.depositor = owner,
2570 .amount = asset(10)});
2576 auto tx = vault.clawback(
2580 .amount = asset(0)});
2585 env(vault.del({.owner = owner, .id = keylet.key}));
2591 Account
const& owner,
2592 Account
const& issuer,
2593 Account
const& charlie,
2598 testcase(
"IOU no trust line to 3rd party");
2600 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2605 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2608 Account
const erin{
"erin"};
2609 env.fund(XRP(1000), erin);
2614 auto tx = vault.withdraw(
2615 {.depositor = owner,
2617 .amount = asset(10)});
2618 tx[sfDestination] = erin.human();
2626 Account
const& owner,
2627 Account
const& issuer,
2628 Account
const& charlie,
2633 testcase(
"IOU no trust line to depositor");
2635 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2640 env.trust(asset(0), owner);
2644 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2649 BEAST_EXPECT(trustline ==
nullptr);
2653 auto tx = vault.withdraw(
2654 {.depositor = owner,
2656 .amount = asset(10)});
2664 Account
const& owner,
2665 Account
const& issuer,
2666 Account
const& charlie,
2671 testcase(
"IOU frozen trust line to 3rd party");
2673 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2678 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2683 auto tx = vault.withdraw(
2684 {.depositor = owner,
2686 .amount = asset(10)});
2687 tx[sfDestination] = charlie.human();
2690 env(withdrawToCharlie);
2693 env(trust(issuer, asset(0), charlie,
tfSetFreeze));
2697 auto const withdraw = vault.withdraw(
2698 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2710 .amount = asset(0)}));
2713 env(vault.del({.owner = owner, .id = keylet.key}));
2719 Account
const& owner,
2720 Account
const& issuer,
2721 Account
const& charlie,
2728 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2733 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2741 auto tx = vault.withdraw(
2742 {.depositor = owner,
2744 .amount = asset(10)});
2748 tx[sfDestination] = charlie.human();
2754 {.depositor = owner,
2756 .amount = asset(10)});
2766 .amount = asset(0)}));
2769 env(vault.del({.owner = owner, .id = keylet.key}));
2777 using namespace test::jtx;
2781 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2782 Account issuer{
"issuer"};
2783 Account owner{
"owner"};
2784 Account depositor{
"depositor"};
2785 Account charlie{
"charlie"};
2786 Account pdOwner{
"pdOwner"};
2787 Account credIssuer1{
"credIssuer1"};
2788 Account credIssuer2{
"credIssuer2"};
2806 env.trust(asset(1000), owner);
2807 env(pay(issuer, owner, asset(500)));
2808 env.trust(asset(1000), depositor);
2809 env(pay(issuer, depositor, asset(500)));
2810 env.trust(asset(1000), charlie);
2811 env(pay(issuer, charlie, asset(5)));
2814 auto [tx, keylet] = vault.create(
2818 BEAST_EXPECT(env.le(keylet));
2821 testcase(
"private vault owner can deposit");
2822 auto tx = vault.deposit(
2823 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
2828 testcase(
"private vault depositor not authorized yet");
2829 auto tx = vault.deposit(
2830 {.depositor = depositor,
2832 .amount = asset(50)});
2837 testcase(
"private vault cannot set non-existing domain");
2838 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2844 testcase(
"private vault set domainId");
2847 pdomain::Credentials
const credentials1{
2848 {.issuer = credIssuer1, .credType = credType}};
2850 env(pdomain::setTx(pdOwner, credentials1));
2851 auto const domainId1 = [&]() {
2853 return pdomain::getNewDomain(env.meta());
2856 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2867 pdomain::Credentials
const credentials{
2868 {.issuer = credIssuer1, .credType = credType},
2869 {.issuer = credIssuer2, .credType = credType}};
2871 env(pdomain::setTx(pdOwner, credentials));
2872 auto const domainId = [&]() {
2874 return pdomain::getNewDomain(env.meta());
2877 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2885 testcase(
"private vault depositor still not authorized");
2886 auto tx = vault.deposit(
2887 {.depositor = depositor,
2889 .amount = asset(50)});
2894 auto const credKeylet =
2895 credentials::keylet(depositor, credIssuer1, credType);
2897 testcase(
"private vault depositor now authorized");
2898 env(credentials::create(depositor, credIssuer1, credType));
2899 env(credentials::accept(depositor, credIssuer1, credType));
2900 env(credentials::create(charlie, credIssuer1, credType));
2903 auto credSle = env.le(credKeylet);
2904 BEAST_EXPECT(credSle !=
nullptr);
2906 auto tx = vault.deposit(
2907 {.depositor = depositor,
2909 .amount = asset(50)});
2914 {.depositor = charlie, .id = keylet.
key, .amount = asset(50)});
2920 testcase(
"private vault depositor lost authorization");
2921 env(credentials::deleteCred(
2922 credIssuer1, depositor, credIssuer1, credType));
2923 env(credentials::deleteCred(
2924 credIssuer1, charlie, credIssuer1, credType));
2926 auto credSle = env.le(credKeylet);
2927 BEAST_EXPECT(credSle ==
nullptr);
2929 auto tx = vault.deposit(
2930 {.depositor = depositor,
2932 .amount = asset(50)});
2937 auto const shares = [&env, keylet = keylet,
this]() ->
Asset {
2938 auto const vault = env.le(keylet);
2939 BEAST_EXPECT(vault !=
nullptr);
2940 return MPTIssue(vault->at(sfShareMPTID));
2944 testcase(
"private vault expired authorization");
2945 uint32_t
const closeTime = env.current()
2947 .parentCloseTime.time_since_epoch()
2951 credentials::create(depositor, credIssuer2, credType);
2952 tx0[sfExpiration] = closeTime + 20;
2954 tx0 = credentials::create(charlie, credIssuer2, credType);
2955 tx0[sfExpiration] = closeTime + 20;
2959 env(credentials::accept(depositor, credIssuer2, credType));
2960 env(credentials::accept(charlie, credIssuer2, credType));
2965 auto tx1 = vault.deposit(
2966 {.depositor = depositor,
2968 .amount = asset(50)});
2974 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
2983 auto const credsKeylet =
2984 credentials::keylet(depositor, credIssuer2, credType);
2985 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
2987 auto tx2 = vault.deposit(
2988 {.depositor = depositor,
2990 .amount = asset(1)});
2994 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
2998 auto const credsKeylet =
2999 credentials::keylet(charlie, credIssuer2, credType);
3000 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3003 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3005 auto tx3 = vault.deposit(
3006 {.depositor = charlie,
3008 .amount = asset(2)});
3012 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3013 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3018 testcase(
"private vault reset domainId");
3019 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3020 tx[sfDomainID] =
"0";
3025 {.depositor = depositor,
3027 .amount = asset(50)});
3031 tx = vault.withdraw(
3032 {.depositor = depositor,
3034 .amount = asset(50)});
3037 tx = vault.clawback(
3040 .holder = depositor,
3041 .amount = asset(0)});
3044 tx = vault.clawback(
3048 .amount = asset(0)});
3195 using namespace test::jtx;
3199 Account
const& owner;
3200 Account
const& issuer;
3201 Account
const& depositor;
3202 Account
const& vaultAccount;
3212 auto testCase = [&,
this](
3215 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3216 Account
const owner{
"owner"};
3217 Account
const issuer{
"issuer"};
3218 Account
const depositor{
"depositor"};
3220 env.fund(XRP(1000), issuer, owner, depositor);
3225 env.trust(asset(1000), owner);
3226 env.trust(asset(1000), depositor);
3227 env(pay(issuer, owner, asset(200)));
3228 env(pay(issuer, depositor, asset(200)));
3231 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3232 tx[sfScale] = scale;
3235 auto const [vaultAccount, issuanceId] =
3237 auto const vault = env.le(keylet);
3239 Account(
"vault", vault->at(sfAccount)),
3240 vault->at(sfShareMPTID)};
3243 env.memoize(vaultAccount);
3247 return env.app().openLedger().modify(
3251 if (!BEAST_EXPECT(vault !=
nullptr))
3253 auto shares = sb.
peek(
3255 if (!BEAST_EXPECT(shares !=
nullptr))
3257 if (fn(*vault, *shares))
3272 .depositor = depositor,
3273 .vaultAccount = vaultAccount,
3283 testCase(18, [&,
this](Env& env, Data d) {
3284 testcase(
"Scale deposit overflow on first deposit");
3285 auto tx = d.vault.deposit(
3286 {.depositor = d.depositor,
3288 .amount = d.asset(10)});
3293 testCase(18, [&,
this](Env& env, Data d) {
3294 testcase(
"Scale deposit overflow on second deposit");
3297 auto tx = d.vault.deposit(
3298 {.depositor = d.depositor,
3300 .amount = d.asset(5)});
3306 auto tx = d.vault.deposit(
3307 {.depositor = d.depositor,
3309 .amount = d.asset(10)});
3315 testCase(18, [&,
this](Env& env, Data d) {
3316 testcase(
"Scale deposit overflow on total shares");
3319 auto tx = d.vault.deposit(
3320 {.depositor = d.depositor,
3322 .amount = d.asset(5)});
3328 auto tx = d.vault.deposit(
3329 {.depositor = d.depositor,
3331 .amount = d.asset(5)});
3337 testCase(1, [&,
this](Env& env, Data d) {
3340 auto const start = env.balance(d.depositor, d.assets).number();
3341 auto tx = d.vault.deposit(
3342 {.depositor = d.depositor,
3344 .amount = d.asset(1)});
3347 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3349 env.balance(d.depositor, d.assets) ==
3353 testCase(1, [&,
this](Env& env, Data d) {
3354 testcase(
"Scale deposit insignificant amount");
3356 auto tx = d.vault.deposit(
3357 {.depositor = d.depositor,
3363 testCase(1, [&,
this](Env& env, Data d) {
3364 testcase(
"Scale deposit exact, using full precision");
3366 auto const start = env.balance(d.depositor, d.assets).number();
3367 auto tx = d.vault.deposit(
3368 {.depositor = d.depositor,
3373 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3375 env.balance(d.depositor, d.assets) ==
3379 testCase(1, [&,
this](Env& env, Data d) {
3380 testcase(
"Scale deposit exact, truncating from .5");
3382 auto const start = env.balance(d.depositor, d.assets).number();
3386 auto tx = d.vault.deposit(
3387 {.depositor = d.depositor,
3392 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3394 env.balance(d.depositor, d.assets) ==
3399 auto tx = d.vault.deposit(
3400 {.depositor = d.depositor,
3405 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3407 env.balance(d.depositor, d.assets) ==
3412 auto tx = d.vault.deposit(
3413 {.depositor = d.depositor,
3418 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
3420 env.balance(d.depositor, d.assets) ==
3425 testCase(1, [&,
this](Env& env, Data d) {
3426 testcase(
"Scale deposit exact, truncating from .01");
3428 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(12));
3438 env.balance(d.depositor, d.assets) ==
3443 auto tx = d.vault.deposit(
3444 {.depositor = d.depositor,
3449 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3451 env.balance(d.depositor, d.assets) ==
3456 testCase(1, [&,
this](Env& env, Data d) {
3457 testcase(
"Scale deposit exact, truncating from .99");
3459 auto const start = env.balance(d.depositor, d.assets).number();
3461 auto tx = d.vault.deposit(
3462 {.depositor = d.depositor,
3467 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3469 env.balance(d.depositor, d.assets) ==
3474 auto tx = d.vault.deposit(
3475 {.depositor = d.depositor,
3480 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3482 env.balance(d.depositor, d.assets) ==
3487 testCase(1, [&,
this](Env& env, Data d) {
3489 auto const start = env.balance(d.depositor, d.assets).number();
3490 auto tx = d.vault.deposit(
3491 {.depositor = d.depositor,
3496 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3498 env.balance(d.depositor, d.assets) ==
3501 env.balance(d.vaultAccount, d.assets) ==
3504 env.balance(d.vaultAccount, d.shares) ==
3513 auto const start = env.balance(d.depositor, d.assets).number();
3514 auto tx = d.vault.withdraw(
3515 {.depositor = d.depositor,
3521 env.balance(d.depositor, d.shares) == d.share(900));
3523 env.balance(d.depositor, d.assets) ==
3526 env.balance(d.vaultAccount, d.assets) ==
3529 env.balance(d.vaultAccount, d.shares) ==
3534 testcase(
"Scale redeem with rounding");
3539 auto const start = env.balance(d.depositor, d.assets).number();
3540 d.peek([](
SLE& vault,
auto&) ->
bool {
3541 vault[sfAssetsAvailable] =
Number(1);
3549 auto tx = d.vault.withdraw(
3550 {.depositor = d.depositor,
3556 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3558 env.balance(d.depositor, d.assets) ==
3561 env.balance(d.vaultAccount, d.assets) ==
3564 env.balance(d.vaultAccount, d.shares) ==
3574 auto const start = env.balance(d.depositor, d.assets).number();
3576 tx = d.vault.withdraw(
3577 {.depositor = d.depositor,
3583 env.balance(d.depositor, d.shares) == d.share(875 - 21));
3585 env.balance(d.depositor, d.assets) ==
3588 env.balance(d.vaultAccount, d.assets) ==
3591 env.balance(d.vaultAccount, d.shares) ==
3597 auto const rest = env.balance(d.depositor, d.shares).number();
3599 tx = d.vault.withdraw(
3600 {.depositor = d.depositor,
3602 .amount =
STAmount(d.share, rest)});
3605 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3607 env.balance(d.vaultAccount, d.assets).number() == 0);
3609 env.balance(d.vaultAccount, d.shares).number() == 0);
3613 testCase(18, [&,
this](Env& env, Data d) {
3614 testcase(
"Scale withdraw overflow");
3617 auto tx = d.vault.deposit(
3618 {.depositor = d.depositor,
3620 .amount = d.asset(5)});
3626 auto tx = d.vault.withdraw(
3627 {.depositor = d.depositor,
3635 testCase(1, [&,
this](Env& env, Data d) {
3637 auto const start = env.balance(d.depositor, d.assets).number();
3638 auto tx = d.vault.deposit(
3639 {.depositor = d.depositor,
3644 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3646 env.balance(d.depositor, d.assets) ==
3649 env.balance(d.vaultAccount, d.assets) ==
3652 env.balance(d.vaultAccount, d.shares) ==
3664 auto const start = env.balance(d.depositor, d.assets).number();
3665 auto tx = d.vault.withdraw(
3666 {.depositor = d.depositor,
3672 env.balance(d.depositor, d.shares) == d.share(900));
3674 env.balance(d.depositor, d.assets) ==
3677 env.balance(d.vaultAccount, d.assets) ==
3680 env.balance(d.vaultAccount, d.shares) ==
3685 testcase(
"Scale withdraw insignificant amount");
3686 auto tx = d.vault.withdraw(
3687 {.depositor = d.depositor,
3694 testcase(
"Scale withdraw with rounding assets");
3702 auto const start = env.balance(d.depositor, d.assets).number();
3703 d.peek([](
SLE& vault,
auto&) ->
bool {
3704 vault[sfAssetsAvailable] =
Number(1);
3712 auto tx = d.vault.withdraw(
3713 {.depositor = d.depositor,
3719 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3721 env.balance(d.depositor, d.assets) ==
3724 env.balance(d.vaultAccount, d.assets) ==
3727 env.balance(d.vaultAccount, d.shares) ==
3732 testcase(
"Scale withdraw with rounding shares up");
3740 auto const start = env.balance(d.depositor, d.assets).number();
3741 auto tx = d.vault.withdraw(
3742 {.depositor = d.depositor,
3748 env.balance(d.depositor, d.shares) == d.share(875 - 38));
3750 env.balance(d.depositor, d.assets) ==
3753 env.balance(d.vaultAccount, d.assets) ==
3756 env.balance(d.vaultAccount, d.shares) ==
3761 testcase(
"Scale withdraw with rounding shares down");
3769 auto const start = env.balance(d.depositor, d.assets).number();
3770 auto tx = d.vault.withdraw(
3771 {.depositor = d.depositor,
3777 env.balance(d.depositor, d.shares) == d.share(837 - 37));
3779 env.balance(d.depositor, d.assets) ==
3782 env.balance(d.vaultAccount, d.assets) ==
3785 env.balance(d.vaultAccount, d.shares) ==
3790 testcase(
"Scale withdraw tiny amount");
3792 auto const start = env.balance(d.depositor, d.assets).number();
3793 auto tx = d.vault.withdraw(
3794 {.depositor = d.depositor,
3800 env.balance(d.depositor, d.shares) == d.share(800 - 1));
3802 env.balance(d.depositor, d.assets) ==
3805 env.balance(d.vaultAccount, d.assets) ==
3808 env.balance(d.vaultAccount, d.shares) ==
3815 env.balance(d.vaultAccount, d.assets).number();
3817 tx = d.vault.withdraw(
3818 {.depositor = d.depositor,
3820 .amount =
STAmount(d.asset, rest)});
3823 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3825 env.balance(d.vaultAccount, d.assets).number() == 0);
3827 env.balance(d.vaultAccount, d.shares).number() == 0);
3831 testCase(18, [&,
this](Env& env, Data d) {
3832 testcase(
"Scale clawback overflow");
3835 auto tx = d.vault.deposit(
3836 {.depositor = d.depositor,
3838 .amount = d.asset(5)});
3844 auto tx = d.vault.clawback(
3845 {.issuer = d.issuer,
3847 .holder = d.depositor,
3854 testCase(1, [&,
this](Env& env, Data d) {
3856 auto const start = env.balance(d.depositor, d.assets).number();
3857 auto tx = d.vault.deposit(
3858 {.depositor = d.depositor,
3863 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3865 env.balance(d.depositor, d.assets) ==
3868 env.balance(d.vaultAccount, d.assets) ==
3871 env.balance(d.vaultAccount, d.shares) ==
3882 auto const start = env.balance(d.depositor, d.assets).number();
3883 auto tx = d.vault.clawback(
3884 {.issuer = d.issuer,
3886 .holder = d.depositor,
3891 env.balance(d.depositor, d.shares) == d.share(900));
3893 env.balance(d.depositor, d.assets) ==
3896 env.balance(d.vaultAccount, d.assets) ==
3899 env.balance(d.vaultAccount, d.shares) ==
3904 testcase(
"Scale clawback insignificant amount");
3905 auto tx = d.vault.clawback(
3906 {.issuer = d.issuer,
3908 .holder = d.depositor,
3914 testcase(
"Scale clawback with rounding assets");
3922 auto const start = env.balance(d.depositor, d.assets).number();
3923 auto tx = d.vault.clawback(
3924 {.issuer = d.issuer,
3926 .holder = d.depositor,
3931 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3933 env.balance(d.depositor, d.assets) ==
3936 env.balance(d.vaultAccount, d.assets) ==
3939 env.balance(d.vaultAccount, d.shares) ==
3944 testcase(
"Scale clawback with rounding shares up");
3952 auto const start = env.balance(d.depositor, d.assets).number();
3953 auto tx = d.vault.clawback(
3954 {.issuer = d.issuer,
3956 .holder = d.depositor,
3961 env.balance(d.depositor, d.shares) == d.share(875 - 38));
3963 env.balance(d.depositor, d.assets) ==
3966 env.balance(d.vaultAccount, d.assets) ==
3969 env.balance(d.vaultAccount, d.shares) ==
3974 testcase(
"Scale clawback with rounding shares down");
3982 auto const start = env.balance(d.depositor, d.assets).number();
3983 auto tx = d.vault.clawback(
3984 {.issuer = d.issuer,
3986 .holder = d.depositor,
3991 env.balance(d.depositor, d.shares) == d.share(837 - 37));
3993 env.balance(d.depositor, d.assets) ==
3996 env.balance(d.vaultAccount, d.assets) ==
3999 env.balance(d.vaultAccount, d.shares) ==
4004 testcase(
"Scale clawback tiny amount");
4006 auto const start = env.balance(d.depositor, d.assets).number();
4007 auto tx = d.vault.clawback(
4008 {.issuer = d.issuer,
4010 .holder = d.depositor,
4015 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4017 env.balance(d.depositor, d.assets) ==
4020 env.balance(d.vaultAccount, d.assets) ==
4023 env.balance(d.vaultAccount, d.shares) ==
4030 env.balance(d.vaultAccount, d.assets).number();
4031 d.peek([](
SLE& vault,
auto&) ->
bool {
4032 vault[sfAssetsAvailable] =
Number(5);
4040 tx = d.vault.clawback(
4041 {.issuer = d.issuer,
4043 .holder = d.depositor,
4044 .amount =
STAmount(d.asset, rest)});
4047 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4049 env.balance(d.vaultAccount, d.assets).number() == 0);
4051 env.balance(d.vaultAccount, d.shares).number() == 0);
4059 using namespace test::jtx;
4062 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4063 Account
const owner{
"owner"};
4064 Account
const issuer{
"issuer"};
4066 env.fund(XRP(1000), issuer, owner);
4070 env.trust(asset(1000), owner);
4071 env(pay(issuer, owner, asset(200)));
4074 auto const sequence = env.seq(owner);
4075 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
4081 auto tx1 = vault.deposit(
4082 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
4085 auto tx2 = vault.set({.owner = owner, .id = keylet.
key});
4086 tx2[sfAssetsMaximum] = asset(1000).number();
4091 auto const sleVault = [&env, keylet = keylet,
this]() {
4092 auto const vault = env.le(keylet);
4093 BEAST_EXPECT(vault !=
nullptr);
4097 auto const check = [&, keylet = keylet, sle = sleVault,
this](
4100 BEAST_EXPECT(vault.isObject());
4102 constexpr auto checkString =
4104 return node.isMember(field.fieldName) &&
4105 node[field.fieldName].isString() &&
4106 node[field.fieldName] == v;
4108 constexpr auto checkObject =
4110 return node.isMember(field.fieldName) &&
4111 node[field.fieldName].isObject() &&
4112 node[field.fieldName] == v;
4114 constexpr auto checkInt =
4115 [](
auto& node,
SField const& field,
int v) ->
bool {
4116 return node.isMember(field.fieldName) &&
4117 ((node[field.fieldName].isInt() &&
4118 node[field.fieldName] ==
Json::Int(v)) ||
4119 (node[field.fieldName].isUInt() &&
4123 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
4124 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(keylet.
key));
4125 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
4129 checkString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
4131 checkObject(vault, sfAsset,
to_json(sle->at(sfAsset))));
4132 BEAST_EXPECT(checkString(vault, sfAssetsAvailable,
"50"));
4133 BEAST_EXPECT(checkString(vault, sfAssetsMaximum,
"1000"));
4134 BEAST_EXPECT(checkString(vault, sfAssetsTotal,
"50"));
4135 BEAST_EXPECT(checkString(vault, sfLossUnrealized,
"0"));
4137 auto const strShareID =
strHex(sle->at(sfShareMPTID));
4138 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
4139 BEAST_EXPECT(checkString(vault, sfOwner,
toBase58(owner.id())));
4140 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
4141 BEAST_EXPECT(checkInt(
4144 if (issuance.isObject())
4147 issuance[
"LedgerEntryType"].asString() ==
4150 issuance[jss::mpt_issuance_id].asString() == strShareID);
4151 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
4152 BEAST_EXPECT(checkInt(
4157 checkString(issuance, sfOutstandingAmount,
"50000000"));
4162 testcase(
"RPC ledger_entry selected by key");
4164 jvParams[jss::ledger_index] = jss::validated;
4165 jvParams[jss::vault] =
strHex(keylet.
key);
4166 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4168 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4169 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4170 check(jvVault[jss::result][jss::node]);
4174 testcase(
"RPC ledger_entry selected by owner and seq");
4176 jvParams[jss::ledger_index] = jss::validated;
4177 jvParams[jss::vault][jss::owner] = owner.human();
4178 jvParams[jss::vault][jss::seq] = sequence;
4179 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4181 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4182 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4183 check(jvVault[jss::result][jss::node]);
4187 testcase(
"RPC ledger_entry cannot find vault by key");
4189 jvParams[jss::ledger_index] = jss::validated;
4191 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4193 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4197 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
4199 jvParams[jss::ledger_index] = jss::validated;
4200 jvParams[jss::vault][jss::owner] = issuer.human();
4201 jvParams[jss::vault][jss::seq] = 1'000'000;
4202 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4204 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4208 testcase(
"RPC ledger_entry malformed key");
4210 jvParams[jss::ledger_index] = jss::validated;
4211 jvParams[jss::vault] = 42;
4212 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4214 jvVault[jss::result][jss::error].asString() ==
4215 "malformedRequest");
4219 testcase(
"RPC ledger_entry malformed owner");
4221 jvParams[jss::ledger_index] = jss::validated;
4222 jvParams[jss::vault][jss::owner] = 42;
4223 jvParams[jss::vault][jss::seq] = sequence;
4224 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4226 jvVault[jss::result][jss::error].asString() ==
4231 testcase(
"RPC ledger_entry malformed seq");
4233 jvParams[jss::ledger_index] = jss::validated;
4234 jvParams[jss::vault][jss::owner] = issuer.human();
4235 jvParams[jss::vault][jss::seq] =
"foo";
4236 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4238 jvVault[jss::result][jss::error].asString() ==
4239 "malformedRequest");
4243 testcase(
"RPC ledger_entry negative seq");
4245 jvParams[jss::ledger_index] = jss::validated;
4246 jvParams[jss::vault][jss::owner] = issuer.human();
4247 jvParams[jss::vault][jss::seq] = -1;
4248 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4250 jvVault[jss::result][jss::error].asString() ==
4251 "malformedRequest");
4255 testcase(
"RPC ledger_entry oversized seq");
4257 jvParams[jss::ledger_index] = jss::validated;
4258 jvParams[jss::vault][jss::owner] = issuer.human();
4259 jvParams[jss::vault][jss::seq] = 1e20;
4260 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4262 jvVault[jss::result][jss::error].asString() ==
4263 "malformedRequest");
4267 testcase(
"RPC ledger_entry bool seq");
4269 jvParams[jss::ledger_index] = jss::validated;
4270 jvParams[jss::vault][jss::owner] = issuer.human();
4271 jvParams[jss::vault][jss::seq] =
true;
4272 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4274 jvVault[jss::result][jss::error].asString() ==
4275 "malformedRequest");
4282 jvParams[jss::account] = owner.human();
4283 jvParams[jss::type] = jss::vault;
4285 "json",
"account_objects",
to_string(jvParams))[jss::result];
4287 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
4288 check(jv[jss::account_objects][0u]);
4295 jvParams[jss::ledger_index] = jss::validated;
4296 jvParams[jss::binary] =
false;
4297 jvParams[jss::type] = jss::vault;
4299 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
4300 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
4301 check(jv[jss::result][jss::state][0u]);
4305 testcase(
"RPC vault_info command line");
4307 env.rpc(
"vault_info",
strHex(keylet.
key),
"validated");
4309 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4310 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4312 jv[jss::result][jss::vault],
4313 jv[jss::result][jss::vault][jss::shares]);
4319 jvParams[jss::ledger_index] = jss::validated;
4320 jvParams[jss::vault_id] =
strHex(keylet.
key);
4321 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4323 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4324 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4326 jv[jss::result][jss::vault],
4327 jv[jss::result][jss::vault][jss::shares]);
4331 testcase(
"RPC vault_info invalid vault_id");
4333 jvParams[jss::ledger_index] = jss::validated;
4334 jvParams[jss::vault_id] =
"foobar";
4335 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4337 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4341 testcase(
"RPC vault_info json invalid index");
4343 jvParams[jss::ledger_index] = jss::validated;
4344 jvParams[jss::vault_id] = 0;
4345 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4347 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4351 testcase(
"RPC vault_info json by owner and sequence");
4353 jvParams[jss::ledger_index] = jss::validated;
4354 jvParams[jss::owner] = owner.human();
4355 jvParams[jss::seq] = sequence;
4356 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4358 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4359 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4361 jv[jss::result][jss::vault],
4362 jv[jss::result][jss::vault][jss::shares]);
4366 testcase(
"RPC vault_info json malformed sequence");
4368 jvParams[jss::ledger_index] = jss::validated;
4369 jvParams[jss::owner] = owner.human();
4370 jvParams[jss::seq] =
"foobar";
4371 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4373 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4377 testcase(
"RPC vault_info json invalid sequence");
4379 jvParams[jss::ledger_index] = jss::validated;
4380 jvParams[jss::owner] = owner.human();
4381 jvParams[jss::seq] = 0;
4382 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4384 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4388 testcase(
"RPC vault_info json negative sequence");
4390 jvParams[jss::ledger_index] = jss::validated;
4391 jvParams[jss::owner] = owner.human();
4392 jvParams[jss::seq] = -1;
4393 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4395 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4399 testcase(
"RPC vault_info json oversized sequence");
4401 jvParams[jss::ledger_index] = jss::validated;
4402 jvParams[jss::owner] = owner.human();
4403 jvParams[jss::seq] = 1e20;
4404 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4406 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4410 testcase(
"RPC vault_info json bool sequence");
4412 jvParams[jss::ledger_index] = jss::validated;
4413 jvParams[jss::owner] = owner.human();
4414 jvParams[jss::seq] =
true;
4415 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4417 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4421 testcase(
"RPC vault_info json malformed owner");
4423 jvParams[jss::ledger_index] = jss::validated;
4424 jvParams[jss::owner] =
"foobar";
4425 jvParams[jss::seq] = sequence;
4426 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4428 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4432 testcase(
"RPC vault_info json invalid combination only owner");
4434 jvParams[jss::ledger_index] = jss::validated;
4435 jvParams[jss::owner] = owner.human();
4436 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4438 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4442 testcase(
"RPC vault_info json invalid combination only seq");
4444 jvParams[jss::ledger_index] = jss::validated;
4445 jvParams[jss::seq] = sequence;
4446 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4448 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4452 testcase(
"RPC vault_info json invalid combination seq vault_id");
4454 jvParams[jss::ledger_index] = jss::validated;
4455 jvParams[jss::vault_id] =
strHex(keylet.
key);
4456 jvParams[jss::seq] = sequence;
4457 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4459 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4463 testcase(
"RPC vault_info json invalid combination owner vault_id");
4465 jvParams[jss::ledger_index] = jss::validated;
4466 jvParams[jss::vault_id] =
strHex(keylet.
key);
4467 jvParams[jss::owner] = owner.human();
4468 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4470 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4475 "RPC vault_info json invalid combination owner seq "
4478 jvParams[jss::ledger_index] = jss::validated;
4479 jvParams[jss::vault_id] =
strHex(keylet.
key);
4480 jvParams[jss::seq] = sequence;
4481 jvParams[jss::owner] = owner.human();
4482 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4484 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4488 testcase(
"RPC vault_info json no input");
4490 jvParams[jss::ledger_index] = jss::validated;
4491 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4493 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4497 testcase(
"RPC vault_info command line invalid index");
4498 Json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
4499 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
4503 testcase(
"RPC vault_info command line invalid index");
4504 Json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
4506 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4510 testcase(
"RPC vault_info command line invalid index");
4514 jv[jss::result][jss::error].asString() ==
"entryNotFound");
4518 testcase(
"RPC vault_info command line invalid ledger");
4521 jv[jss::result][jss::error].asString() ==
"lgrNotFound");