62 using namespace test::jtx;
64 auto const testSequence = [
this](
67 Account
const& issuer,
69 Account
const& depositor,
70 Account
const& charlie,
73 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
74 tx[sfData] =
"AFEED00E";
75 tx[sfAssetsMaximum] = asset(100).number();
78 BEAST_EXPECT(env.le(keylet));
81 auto const [share, vaultAccount] =
86 auto const vault = env.le(keylet);
87 BEAST_EXPECT(vault !=
nullptr);
88 if (asset.raw().holds<
Issue>() && !asset.raw().
native())
89 BEAST_EXPECT(vault->at(sfScale) == 6);
91 BEAST_EXPECT(vault->at(sfScale) == 0);
94 BEAST_EXPECT(shares !=
nullptr);
95 if (asset.raw().holds<
Issue>() && !asset.raw().
native())
96 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
98 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
101 Account(
"vault", vault->at(sfAccount))};
103 auto const shares = share.raw().get<
MPTIssue>();
104 env.memoize(vaultAccount);
107 Account alice{
"alice"};
108 Account dave{
"dave"};
109 Account erin{
"erin"};
110 env.fund(XRP(1000), alice, dave, erin);
116 testcase(prefix +
" fail to deposit more than assets held");
117 auto tx = vault.deposit(
118 {.depositor = depositor,
120 .amount = asset(10000)});
126 testcase(prefix +
" deposit non-zero amount");
127 auto tx = vault.deposit(
128 {.depositor = depositor,
130 .amount = asset(50)});
134 env.balance(depositor, shares) == share(50 * scale));
138 testcase(prefix +
" deposit non-zero amount again");
139 auto tx = vault.deposit(
140 {.depositor = depositor,
142 .amount = asset(50)});
146 env.balance(depositor, shares) == share(100 * scale));
150 testcase(prefix +
" fail to delete non-empty vault");
151 auto tx = vault.del({.owner = owner, .id = keylet.
key});
157 testcase(prefix +
" fail to update because wrong owner");
158 auto tx = vault.set({.owner = issuer, .id = keylet.
key});
159 tx[sfAssetsMaximum] = asset(50).number();
166 prefix +
" fail to set maximum lower than current amount");
167 auto tx = vault.set({.owner = owner, .id = keylet.
key});
168 tx[sfAssetsMaximum] = asset(50).number();
174 testcase(prefix +
" set maximum higher than current amount");
175 auto tx = vault.set({.owner = owner, .id = keylet.
key});
176 tx[sfAssetsMaximum] = asset(150).number();
183 auto tx = vault.set({.owner = owner, .id = keylet.
key});
190 testcase(prefix +
" fail to set domain on public vault");
191 auto tx = vault.set({.owner = owner, .id = keylet.
key});
198 testcase(prefix +
" fail to deposit more than maximum");
199 auto tx = vault.deposit(
200 {.depositor = depositor,
202 .amount = asset(100)});
208 testcase(prefix +
" reset maximum to zero i.e. not enforced");
209 auto tx = vault.set({.owner = owner, .id = keylet.
key});
210 tx[sfAssetsMaximum] = asset(0).number();
216 testcase(prefix +
" fail to withdraw more than assets held");
217 auto tx = vault.withdraw(
218 {.depositor = depositor,
220 .amount = asset(1000)});
225 testcase(prefix +
" deposit some more");
226 auto tx = vault.deposit(
227 {.depositor = depositor,
229 .amount = asset(100)});
233 env.balance(depositor, shares) == share(200 * scale));
237 testcase(prefix +
" clawback some");
240 auto tx = vault.clawback(
244 .amount = asset(10)});
247 if (!asset.raw().native())
250 env.balance(depositor, shares) == share(190 * scale));
258 auto tx = vault.clawback(
259 {.issuer = issuer, .id = keylet.
key, .holder = depositor});
262 if (!asset.raw().native())
264 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
267 auto tx = vault.clawback(
271 .amount = asset(10)});
277 auto tx = vault.withdraw(
278 {.depositor = depositor,
280 .amount = asset(10)});
287 if (!asset.raw().native())
289 testcase(prefix +
" deposit again");
290 auto tx = vault.deposit(
291 {.depositor = depositor,
293 .amount = asset(200)});
297 env.balance(depositor, shares) == share(200 * scale));
302 prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
303 auto tx = vault.withdraw(
304 {.depositor = depositor,
306 .amount = asset(100)});
307 tx[sfDestination] = alice.human();
313 testcase(prefix +
" fail to withdraw to zero destination");
314 auto tx = vault.withdraw(
315 {.depositor = depositor,
317 .amount = asset(1000)});
318 tx[sfDestination] =
"0";
326 " fail to withdraw with tag but without destination");
327 auto tx = vault.withdraw(
328 {.depositor = depositor,
330 .amount = asset(1000)});
331 tx[sfDestinationTag] =
"0";
336 if (!asset.raw().native())
339 prefix +
" fail to withdraw to 3rd party no authorization");
340 auto tx = vault.withdraw(
341 {.depositor = depositor,
343 .amount = asset(100)});
344 tx[sfDestination] = erin.human();
353 " fail to withdraw to 3rd party lsfRequireDestTag");
354 auto tx = vault.withdraw(
355 {.depositor = depositor,
357 .amount = asset(100)});
358 tx[sfDestination] = dave.human();
364 testcase(prefix +
" withdraw to authorized 3rd party");
365 auto tx = vault.withdraw(
366 {.depositor = depositor,
368 .amount = asset(100)});
369 tx[sfDestination] = charlie.human();
373 env.balance(depositor, shares) == share(100 * scale));
377 testcase(prefix +
" withdraw to issuer");
378 auto tx = vault.withdraw(
379 {.depositor = depositor,
381 .amount = asset(50)});
382 tx[sfDestination] = issuer.human();
386 env.balance(depositor, shares) == share(50 * scale));
390 testcase(prefix +
" withdraw remaining assets");
391 auto tx = vault.withdraw(
392 {.depositor = depositor,
394 .amount = asset(50)});
397 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
399 if (!asset.raw().native())
401 auto tx = vault.clawback(
405 .amount = asset(0)});
411 auto tx = vault.withdraw(
412 {.depositor = depositor,
414 .amount = share(10)});
420 if (!asset.raw().native() && asset.raw().holds<
Issue>())
422 testcase(prefix +
" temporary authorization for 3rd party");
423 env(trust(erin, asset(1000)));
424 env(trust(issuer, asset(0), erin,
tfSetfAuth));
425 env(pay(issuer, erin, asset(10)));
428 auto tx = vault.deposit(
429 {.depositor = erin, .id = keylet.
key, .amount = asset(10)});
433 auto tx = pay(erin, depositor, share(10 * scale));
441 {.depositor = depositor,
443 .amount = asset(1)}));
450 testcase(prefix +
" withdraw to authorized 3rd party");
453 {.depositor = depositor,
455 .amount = asset(10)});
456 tx[sfDestination] = erin.human();
459 env(pay(erin, issuer, asset(10)));
462 testcase(prefix +
" fail to pay to unauthorized 3rd party");
463 env(trust(erin, asset(0)));
467 env(pay(depositor, erin, share(1)), ter{
tecNO_LINE});
472 {.depositor = depositor,
474 .amount = asset(1)});
480 testcase(prefix +
" fail to delete because wrong owner");
481 auto tx = vault.del({.owner = issuer, .id = keylet.
key});
486 testcase(prefix +
" delete empty vault");
487 auto tx = vault.del({.owner = owner, .id = keylet.
key});
489 BEAST_EXPECT(!env.le(keylet));
493 auto testCases = [
this, &testSequence](
497 Account
const& issuer,
498 Account
const& owner,
499 Account
const& depositor,
500 Account
const& charlie)> setup) {
501 Env env{*
this, testable_amendments() | featureSingleAssetVault};
502 Account issuer{
"issuer"};
503 Account owner{
"owner"};
504 Account depositor{
"depositor"};
505 Account charlie{
"charlie"};
507 env.fund(XRP(1000), issuer, owner, depositor, charlie);
515 PrettyAsset asset = setup(env, issuer, owner, depositor, charlie);
517 prefix, env, issuer, owner, depositor, charlie, vault, asset);
523 Account
const& issuer,
524 Account
const& owner,
525 Account
const& depositor,
533 Account
const& issuer,
534 Account
const& owner,
535 Account
const& depositor,
536 Account
const& charlie) ->
Asset {
538 env(trust(owner, asset(1000)));
539 env(trust(depositor, asset(1000)));
540 env(trust(charlie, asset(1000)));
541 env(trust(issuer, asset(0), owner,
tfSetfAuth));
542 env(trust(issuer, asset(0), depositor,
tfSetfAuth));
543 env(trust(issuer, asset(0), charlie,
tfSetfAuth));
544 env(pay(issuer, depositor, asset(1000)));
552 Account
const& issuer,
553 Account
const& owner,
554 Account
const& depositor,
555 Account
const& charlie) ->
Asset {
556 MPTTester mptt{env, issuer, mptInitNoFund};
561 mptt.authorize({.account = depositor});
562 mptt.authorize({.account = charlie});
563 env(pay(issuer, depositor, asset(1000)));
572 using namespace test::jtx;
577 testable_amendments() | featureSingleAssetVault;
580 auto testCase = [&,
this](
583 Account
const& issuer,
584 Account
const& owner,
587 CaseArgs args = {}) {
588 Env env{*
this, args.features};
589 Account issuer{
"issuer"};
590 Account owner{
"owner"};
592 env.fund(XRP(1000), issuer, owner);
600 env(trust(owner, asset(1000)));
601 env(trust(issuer, asset(0), owner,
tfSetfAuth));
602 env(pay(issuer, owner, asset(1000)));
605 test(env, issuer, owner, asset, vault);
610 Account
const& issuer,
611 Account
const& owner,
614 testcase(
"disabled single asset vault");
617 vault.create({.owner = owner, .asset = asset});
621 auto tx = vault.set({.owner = owner, .id = keylet.
key});
626 auto tx = vault.deposit(
629 .amount = asset(10)});
634 auto tx = vault.withdraw(
637 .amount = asset(10)});
642 auto tx = vault.clawback(
646 .amount = asset(10)});
651 auto tx = vault.del({.owner = owner, .id = keylet.
key});
655 {.features = testable_amendments() - featureSingleAssetVault});
657 testCase([&](Env& env,
658 Account
const& issuer,
659 Account
const& owner,
664 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
669 auto tx = vault.set({.owner = owner, .id = keylet.
key});
675 auto tx = vault.deposit(
678 .amount = asset(10)});
684 auto tx = vault.withdraw(
687 .amount = asset(10)});
693 auto tx = vault.clawback(
697 .amount = asset(10)});
703 auto tx = vault.del({.owner = owner, .id = keylet.
key});
709 testCase([&](Env& env,
710 Account
const& issuer,
711 Account
const& owner,
716 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
721 auto tx = vault.set({.owner = owner, .id = keylet.
key});
727 auto tx = vault.deposit(
730 .amount = asset(10)});
736 auto tx = vault.withdraw(
739 .amount = asset(10)});
745 auto tx = vault.clawback(
749 .amount = asset(10)});
755 auto tx = vault.del({.owner = owner, .id = keylet.
key});
764 Account
const& owner,
767 testcase(
"disabled permissioned domain");
770 vault.create({.owner = owner, .asset =
xrpIssue()});
775 auto tx = vault.set({.owner = owner, .id = keylet.
key});
781 auto tx = vault.set({.owner = owner, .id = keylet.
key});
782 tx[sfDomainID] =
"0";
786 {.features = (testable_amendments() | featureSingleAssetVault) -
787 featurePermissionedDomains});
789 testCase([&](Env& env,
790 Account
const& issuer,
791 Account
const& owner,
797 vault.create({.owner = owner, .asset =
xrpIssue()});
800 auto tx = vault.set({
808 auto tx = vault.deposit(
811 .amount = asset(10)});
816 auto tx = vault.withdraw(
819 .amount = asset(10)});
824 auto tx = vault.clawback(
828 .amount = asset(10)});
833 auto tx = vault.del({
841 testCase([&](Env& env,
842 Account
const& issuer,
843 Account
const& owner,
848 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
851 auto tx = vault.clawback(
855 .amount = asset(10)});
860 testCase([&](Env& env,
862 Account
const& owner,
865 testcase(
"withdraw to bad destination");
867 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
870 auto tx = vault.withdraw(
873 .amount = asset(10)});
874 tx[jss::Destination] =
"0";
879 testCase([&](Env& env,
881 Account
const& owner,
888 vault.create({.owner = owner, .asset = asset});
895 vault.create({.owner = owner, .asset = asset});
903 vault.create({.owner = owner, .asset = asset});
907 auto const sleVault = env.le(keylet);
908 BEAST_EXPECT(sleVault);
909 BEAST_EXPECT((*sleVault)[sfScale] == 18);
914 vault.create({.owner = owner, .asset = asset});
918 auto const sleVault = env.le(keylet);
919 BEAST_EXPECT(sleVault);
920 BEAST_EXPECT((*sleVault)[sfScale] == 0);
925 vault.create({.owner = owner, .asset = asset});
928 auto const sleVault = env.le(keylet);
929 BEAST_EXPECT(sleVault);
930 BEAST_EXPECT((*sleVault)[sfScale] == 6);
934 testCase([&](Env& env,
936 Account
const& owner,
939 testcase(
"create or set invalid data");
941 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
957 auto tx = vault.set({.owner = owner, .id = keylet.
key});
963 auto tx = vault.set({.owner = owner, .id = keylet.
key});
970 testCase([&](Env& env,
972 Account
const& owner,
977 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
980 auto tx = vault.set({.owner = owner, .id = keylet.
key});
985 testCase([&](Env& env,
987 Account
const& owner,
990 testcase(
"create with invalid metadata");
992 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
996 tx[sfMPTokenMetadata] =
"";
1009 testCase([&](Env& env,
1011 Account
const& owner,
1016 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1019 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1025 testCase([&](Env& env,
1027 Account
const& owner,
1030 testcase(
"invalid deposit amount");
1032 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1035 auto tx = vault.deposit(
1036 {.depositor = owner,
1043 auto tx = vault.deposit(
1044 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1049 testCase([&](Env& env,
1051 Account
const& owner,
1054 testcase(
"invalid set immutable flag");
1056 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1059 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1065 testCase([&](Env& env,
1067 Account
const& owner,
1070 testcase(
"invalid withdraw amount");
1072 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1075 auto tx = vault.withdraw(
1076 {.depositor = owner,
1083 auto tx = vault.withdraw(
1084 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1089 testCase([&](Env& env,
1090 Account
const& issuer,
1091 Account
const& owner,
1096 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1099 auto tx = vault.clawback(
1103 .amount = asset(50)});
1108 auto tx = vault.clawback(
1117 testCase([&](Env& env,
1119 Account
const& owner,
1124 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1128 tx[sfWithdrawalPolicy] = 0;
1147 tx[sfDomainID] =
"0";
1616 using namespace test::jtx;
1620 bool enableClawback =
true;
1624 auto testCase = [
this](
1627 Account
const& issuer,
1628 Account
const& owner,
1629 Account
const& depositor,
1632 MPTTester& mptt)> test,
1633 CaseArgs args = {}) {
1634 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1635 Account issuer{
"issuer"};
1636 Account owner{
"owner"};
1637 Account depositor{
"depositor"};
1638 env.fund(XRP(1000), issuer, owner, depositor);
1642 MPTTester mptt{env, issuer, mptInitNoFund};
1649 mptt.authorize({.account = owner});
1650 mptt.authorize({.account = depositor});
1651 if (args.requireAuth)
1653 mptt.authorize({.account = issuer, .holder = owner});
1654 mptt.authorize({.account = issuer, .holder = depositor});
1657 env(pay(issuer, depositor, asset(1000)));
1660 test(env, issuer, owner, depositor, asset, vault, mptt);
1665 Account
const& issuer,
1666 Account
const& owner,
1667 Account
const& depositor,
1671 testcase(
"MPT nothing to clawback from");
1672 auto tx = vault.clawback(
1675 .holder = depositor,
1676 .amount = asset(10)});
1682 Account
const& issuer,
1683 Account
const& owner,
1684 Account
const& depositor,
1688 testcase(
"MPT global lock blocks create");
1689 mptt.set({.account = issuer, .flags =
tfMPTLock});
1690 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1696 Account
const& issuer,
1697 Account
const& owner,
1698 Account
const& depositor,
1702 testcase(
"MPT global lock blocks deposit");
1703 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1707 mptt.set({.account = issuer, .flags =
tfMPTLock});
1711 {.depositor = depositor,
1713 .amount = asset(100)});
1718 tx = vault.del({.owner = owner, .id = keylet.
key});
1724 Account
const& issuer,
1725 Account
const& owner,
1726 Account
const& depositor,
1730 testcase(
"MPT global lock blocks withdrawal");
1731 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1735 {.depositor = depositor,
1737 .amount = asset(100)});
1743 auto v = env.le(keylet);
1745 MPTID share = (*v)[sfShareMPTID];
1747 BEAST_EXPECT(issuance);
1748 Number outstandingShares = issuance->at(sfOutstandingAmount);
1749 BEAST_EXPECT(outstandingShares == 100);
1751 mptt.set({.account = issuer, .flags =
tfMPTLock});
1754 tx = vault.withdraw(
1755 {.depositor = depositor,
1757 .amount = asset(100)});
1760 tx[sfDestination] = issuer.human();
1764 tx = vault.clawback(
1767 .holder = depositor,
1768 .amount = asset(0)});
1774 BEAST_EXPECT(mptSle ==
nullptr);
1777 tx = vault.del({.owner = owner, .id = keylet.
key});
1783 Account
const& issuer,
1784 Account
const& owner,
1785 Account
const& depositor,
1789 testcase(
"MPT only issuer can clawback");
1791 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1796 {.depositor = depositor,
1798 .amount = asset(100)});
1803 auto tx = vault.clawback(
1804 {.issuer = owner, .id = keylet.
key, .holder = depositor});
1812 Account
const& issuer,
1813 Account
const& owner,
1814 Account
const& depositor,
1819 "MPT 3rd party without MPToken cannot be withdrawal "
1823 vault.create({.owner = owner, .asset = asset});
1828 {.depositor = depositor,
1830 .amount = asset(100)});
1836 Account charlie{
"charlie"};
1837 env.fund(XRP(1000), charlie);
1840 tx = vault.withdraw(
1841 {.depositor = depositor,
1843 .amount = asset(100)});
1844 tx[sfDestination] = charlie.human();
1848 {.requireAuth =
false});
1853 Account
const& issuer,
1854 Account
const& owner,
1855 Account
const& depositor,
1859 testcase(
"MPT depositor without MPToken cannot withdraw");
1862 vault.create({.owner = owner, .asset = asset});
1865 auto v = env.le(keylet);
1867 MPTID share = (*v)[sfShareMPTID];
1870 {.depositor = depositor,
1872 .amount = asset(1000)});
1881 auto const mptoken =
1883 BEAST_EXPECT(mptoken ==
nullptr);
1885 tx = vault.withdraw(
1886 {.depositor = depositor,
1888 .amount = asset(100)});
1894 mptt.authorize({.account = depositor});
1897 tx = vault.withdraw(
1898 {.depositor = depositor,
1900 .amount = asset(1000)});
1907 BEAST_EXPECT(mptSle ==
nullptr);
1910 {.requireAuth =
false});
1914 Account
const& issuer,
1915 Account
const& owner,
1916 Account
const& depositor,
1922 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1927 {.depositor = depositor,
1929 .amount = asset(1000)});
1934 auto tx = vault.clawback(
1937 .holder = depositor,
1938 .amount = asset(0)});
1942 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
1947 vault.create({.owner = depositor, .asset = asset});
1952 auto tx = vault.deposit(
1953 {.depositor = depositor,
1955 .amount = asset(10)});
1960 auto tx = vault.withdraw(
1961 {.depositor = depositor,
1963 .amount = asset(10)});
1968 auto tx = vault.clawback(
1971 .holder = depositor,
1972 .amount = asset(0)});
1976 env(vault.del({.owner = owner, .id = keylet.key}));
1981 Account
const& issuer,
1982 Account
const& owner,
1983 Account
const& depositor,
1987 testcase(
"MPT vault owner can receive shares unless unauthorized");
1989 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1994 {.depositor = depositor,
1996 .amount = asset(1000)});
2001 auto const vault = env.le(keylet);
2002 return vault->at(sfShareMPTID);
2008 env(pay(depositor, owner, shares(1)));
2011 tx = vault.withdraw(
2012 {.depositor = owner,
2014 .amount = shares(1)});
2019 env(pay(depositor, owner, shares(1)));
2022 tx = vault.clawback(
2026 .amount = asset(0)});
2031 env(pay(depositor, owner, shares(1)));
2035 env(pay(owner, depositor, shares(1)));
2041 jv[sfAccount] = owner.human();
2042 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
2044 jv[sfTransactionType] = jss::MPTokenAuthorize;
2050 tx = pay(depositor, owner, shares(1));
2055 tx = vault.clawback(
2058 .holder = depositor,
2059 .amount = asset(0)});
2064 env(vault.del({.owner = owner, .id = keylet.key}));
2072 Account
const& issuer,
2073 Account
const& owner,
2074 Account
const& depositor,
2081 vault.create({.owner = owner, .asset = asset});
2086 {.depositor = depositor,
2088 .amount = asset(1000)});
2093 auto tx = vault.clawback(
2096 .holder = depositor,
2097 .amount = asset(0)});
2101 {.enableClawback =
false});
2105 Account
const& issuer,
2106 Account
const& owner,
2107 Account
const& depositor,
2112 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2116 {.depositor = depositor,
2118 .amount = asset(1000)});
2124 .holder = depositor,
2129 auto tx = vault.withdraw(
2130 {.depositor = depositor,
2132 .amount = asset(100)});
2136 tx[sfDestination] = issuer.human();
2140 tx[sfDestination] = owner.human();
2147 auto tx = vault.deposit(
2148 {.depositor = depositor,
2150 .amount = asset(100)});
2155 tx = vault.clawback(
2158 .holder = depositor,
2159 .amount = asset(800)});
2163 env(vault.del({.owner = owner, .id = keylet.key}));
2168 Account
const& issuer,
2169 Account
const& owner,
2170 Account
const& depositor,
2174 testcase(
"MPT lock of vault pseudo-account");
2175 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2179 auto const vaultAccount =
2180 [&env, keylet = keylet,
this]() ->
AccountID {
2181 auto const vault = env.le(keylet);
2182 BEAST_EXPECT(vault !=
nullptr);
2183 return vault->at(sfAccount);
2187 {.depositor = depositor,
2189 .amount = asset(100)});
2195 jv[jss::Account] = issuer.human();
2196 jv[sfMPTokenIssuanceID] =
2198 jv[jss::Holder] =
toBase58(vaultAccount);
2199 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
2207 {.depositor = depositor,
2209 .amount = asset(100)});
2212 tx = vault.withdraw(
2213 {.depositor = depositor,
2215 .amount = asset(100)});
2219 tx = vault.clawback(
2222 .holder = depositor,
2223 .amount = asset(100)});
2227 tx = vault.del({.owner = owner, .id = keylet.
key});
2234 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2235 Account owner{
"owner"};
2236 Account issuer{
"issuer"};
2237 env.fund(XRP(1000000), owner, issuer);
2241 MPTTester mptt{env, issuer, mptInitNoFund};
2245 mptt.authorize({.account = owner});
2246 mptt.authorize({.account = issuer, .holder = owner});
2248 env(pay(issuer, owner, asset(100)));
2249 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
2253 auto const shares = [&env, keylet = k1,
this]() ->
Asset {
2254 auto const vault = env.le(keylet);
2255 BEAST_EXPECT(vault !=
nullptr);
2256 return MPTIssue(vault->at(sfShareMPTID));
2259 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
2268 using namespace test::jtx;
2274 Account
const& owner,
2275 Account
const& issuer,
2276 Account
const& charlie,
2281 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2282 Account
const owner{
"owner"};
2283 Account
const issuer{
"issuer"};
2284 Account
const charlie{
"charlie"};
2286 env.fund(XRP(1000), issuer, owner, charlie);
2291 env.trust(asset(1000), owner);
2292 env.trust(asset(1000), charlie);
2293 env(pay(issuer, owner, asset(200)));
2294 env(rate(issuer, 1.25));
2297 auto const vaultAccount =
2299 return Account(
"vault", env.le(keylet)->at(sfAccount));
2302 return env.le(keylet)->at(sfShareMPTID);
2318 Account
const& owner,
2319 Account
const& issuer,
2325 testcase(
"IOU cannot use different asset");
2328 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2334 auto tx = [&, account = vaultAccount(keylet)]() {
2336 jv[jss::Account] = issuer.human();
2338 auto& ja = jv[jss::LimitAmount] =
2340 ja[jss::issuer] =
toBase58(account);
2342 jv[jss::TransactionType] = jss::TrustSet;
2351 auto tx = vault.deposit(
2352 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2358 auto tx = vault.withdraw(
2359 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2364 env(vault.del({.owner = owner, .id = keylet.key}));
2370 Account
const& owner,
2371 Account
const& issuer,
2372 Account
const& charlie,
2377 testcase(
"IOU frozen trust line to vault account");
2379 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2384 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2387 Asset const share =
Asset(issuanceId(keylet));
2390 auto trustSet = [&, account = vaultAccount(keylet)]() {
2392 jv[jss::Account] = issuer.human();
2394 auto& ja = jv[jss::LimitAmount] =
2396 ja[jss::issuer] =
toBase58(account);
2398 jv[jss::TransactionType] = jss::TrustSet;
2410 auto tx = vault.deposit(
2411 {.depositor = owner,
2413 .amount = asset(80)});
2418 auto tx = vault.withdraw(
2419 {.depositor = owner,
2421 .amount = asset(100)});
2425 tx[sfDestination] = charlie.human();
2432 auto tx = vault.clawback(
2436 .amount = asset(50)});
2447 {.depositor = owner,
2449 .amount = share(50'000'000)}));
2451 env(vault.del({.owner = owner, .id = keylet.key}));
2457 Account
const& owner,
2458 Account
const& issuer,
2459 Account
const& charlie,
2464 testcase(
"IOU transfer fees not applied");
2466 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2471 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2475 Asset const share =
Asset(issuanceId(keylet));
2478 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2480 env.balance(vaultAccount(keylet), issue) == asset(100));
2483 auto tx = vault.clawback(
2487 .amount = asset(50)});
2493 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2494 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(50));
2497 {.depositor = owner,
2499 .amount = share(20'000'000)}));
2502 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2503 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(30));
2506 auto tx = vault.withdraw(
2507 {.depositor = owner,
2509 .amount = share(30'000'000)});
2510 tx[sfDestination] = charlie.human();
2515 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2516 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2517 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(0));
2519 env(vault.del({.owner = owner, .id = keylet.key}));
2525 Account
const& owner,
2526 Account
const& issuer,
2527 Account
const& charlie,
2532 testcase(
"IOU frozen trust line to depositor");
2534 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2539 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2544 auto tx = vault.withdraw(
2545 {.depositor = owner,
2547 .amount = asset(10)});
2548 tx[sfDestination] = charlie.human();
2551 env(withdrawToCharlie);
2558 auto const withdraw = vault.withdraw(
2559 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2568 auto tx = vault.deposit(
2569 {.depositor = owner,
2571 .amount = asset(10)});
2577 auto tx = vault.clawback(
2581 .amount = asset(0)});
2586 env(vault.del({.owner = owner, .id = keylet.key}));
2592 Account
const& owner,
2593 Account
const& issuer,
2594 Account
const& charlie,
2599 testcase(
"IOU no trust line to 3rd party");
2601 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2606 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2609 Account
const erin{
"erin"};
2610 env.fund(XRP(1000), erin);
2615 auto tx = vault.withdraw(
2616 {.depositor = owner,
2618 .amount = asset(10)});
2619 tx[sfDestination] = erin.human();
2627 Account
const& owner,
2628 Account
const& issuer,
2629 Account
const& charlie,
2634 testcase(
"IOU no trust line to depositor");
2636 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2641 env.trust(asset(0), owner);
2645 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2650 BEAST_EXPECT(trustline ==
nullptr);
2654 auto tx = vault.withdraw(
2655 {.depositor = owner,
2657 .amount = asset(10)});
2665 Account
const& owner,
2666 Account
const& issuer,
2667 Account
const& charlie,
2672 testcase(
"IOU frozen trust line to 3rd party");
2674 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2679 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2684 auto tx = vault.withdraw(
2685 {.depositor = owner,
2687 .amount = asset(10)});
2688 tx[sfDestination] = charlie.human();
2691 env(withdrawToCharlie);
2694 env(trust(issuer, asset(0), charlie,
tfSetFreeze));
2698 auto const withdraw = vault.withdraw(
2699 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2711 .amount = asset(0)}));
2714 env(vault.del({.owner = owner, .id = keylet.key}));
2720 Account
const& owner,
2721 Account
const& issuer,
2722 Account
const& charlie,
2729 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2734 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2742 auto tx = vault.withdraw(
2743 {.depositor = owner,
2745 .amount = asset(10)});
2749 tx[sfDestination] = charlie.human();
2755 {.depositor = owner,
2757 .amount = asset(10)});
2767 .amount = asset(0)}));
2770 env(vault.del({.owner = owner, .id = keylet.key}));
2778 using namespace test::jtx;
2782 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2783 Account issuer{
"issuer"};
2784 Account owner{
"owner"};
2785 Account depositor{
"depositor"};
2786 Account charlie{
"charlie"};
2787 Account pdOwner{
"pdOwner"};
2788 Account credIssuer1{
"credIssuer1"};
2789 Account credIssuer2{
"credIssuer2"};
2807 env.trust(asset(1000), owner);
2808 env(pay(issuer, owner, asset(500)));
2809 env.trust(asset(1000), depositor);
2810 env(pay(issuer, depositor, asset(500)));
2811 env.trust(asset(1000), charlie);
2812 env(pay(issuer, charlie, asset(5)));
2815 auto [tx, keylet] = vault.create(
2819 BEAST_EXPECT(env.le(keylet));
2822 testcase(
"private vault owner can deposit");
2823 auto tx = vault.deposit(
2824 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
2829 testcase(
"private vault depositor not authorized yet");
2830 auto tx = vault.deposit(
2831 {.depositor = depositor,
2833 .amount = asset(50)});
2838 testcase(
"private vault cannot set non-existing domain");
2839 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2845 testcase(
"private vault set domainId");
2848 pdomain::Credentials
const credentials1{
2849 {.issuer = credIssuer1, .credType = credType}};
2851 env(pdomain::setTx(pdOwner, credentials1));
2852 auto const domainId1 = [&]() {
2854 return pdomain::getNewDomain(env.meta());
2857 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2868 pdomain::Credentials
const credentials{
2869 {.issuer = credIssuer1, .credType = credType},
2870 {.issuer = credIssuer2, .credType = credType}};
2872 env(pdomain::setTx(pdOwner, credentials));
2873 auto const domainId = [&]() {
2875 return pdomain::getNewDomain(env.meta());
2878 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2886 testcase(
"private vault depositor still not authorized");
2887 auto tx = vault.deposit(
2888 {.depositor = depositor,
2890 .amount = asset(50)});
2895 auto const credKeylet =
2896 credentials::keylet(depositor, credIssuer1, credType);
2898 testcase(
"private vault depositor now authorized");
2899 env(credentials::create(depositor, credIssuer1, credType));
2900 env(credentials::accept(depositor, credIssuer1, credType));
2901 env(credentials::create(charlie, credIssuer1, credType));
2904 auto credSle = env.le(credKeylet);
2905 BEAST_EXPECT(credSle !=
nullptr);
2907 auto tx = vault.deposit(
2908 {.depositor = depositor,
2910 .amount = asset(50)});
2915 {.depositor = charlie, .id = keylet.
key, .amount = asset(50)});
2921 testcase(
"private vault depositor lost authorization");
2922 env(credentials::deleteCred(
2923 credIssuer1, depositor, credIssuer1, credType));
2924 env(credentials::deleteCred(
2925 credIssuer1, charlie, credIssuer1, credType));
2927 auto credSle = env.le(credKeylet);
2928 BEAST_EXPECT(credSle ==
nullptr);
2930 auto tx = vault.deposit(
2931 {.depositor = depositor,
2933 .amount = asset(50)});
2938 auto const shares = [&env, keylet = keylet,
this]() ->
Asset {
2939 auto const vault = env.le(keylet);
2940 BEAST_EXPECT(vault !=
nullptr);
2941 return MPTIssue(vault->at(sfShareMPTID));
2945 testcase(
"private vault expired authorization");
2946 uint32_t
const closeTime = env.current()
2948 .parentCloseTime.time_since_epoch()
2952 credentials::create(depositor, credIssuer2, credType);
2953 tx0[sfExpiration] = closeTime + 20;
2955 tx0 = credentials::create(charlie, credIssuer2, credType);
2956 tx0[sfExpiration] = closeTime + 20;
2960 env(credentials::accept(depositor, credIssuer2, credType));
2961 env(credentials::accept(charlie, credIssuer2, credType));
2966 auto tx1 = vault.deposit(
2967 {.depositor = depositor,
2969 .amount = asset(50)});
2975 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
2984 auto const credsKeylet =
2985 credentials::keylet(depositor, credIssuer2, credType);
2986 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
2988 auto tx2 = vault.deposit(
2989 {.depositor = depositor,
2991 .amount = asset(1)});
2995 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
2999 auto const credsKeylet =
3000 credentials::keylet(charlie, credIssuer2, credType);
3001 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3004 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3006 auto tx3 = vault.deposit(
3007 {.depositor = charlie,
3009 .amount = asset(2)});
3013 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3014 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3019 testcase(
"private vault reset domainId");
3020 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3021 tx[sfDomainID] =
"0";
3026 {.depositor = depositor,
3028 .amount = asset(50)});
3032 tx = vault.withdraw(
3033 {.depositor = depositor,
3035 .amount = asset(50)});
3038 tx = vault.clawback(
3041 .holder = depositor,
3042 .amount = asset(0)});
3045 tx = vault.clawback(
3049 .amount = asset(0)});
3196 using namespace test::jtx;
3200 Account
const& owner;
3201 Account
const& issuer;
3202 Account
const& depositor;
3203 Account
const& vaultAccount;
3213 auto testCase = [&,
this](
3216 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3217 Account
const owner{
"owner"};
3218 Account
const issuer{
"issuer"};
3219 Account
const depositor{
"depositor"};
3221 env.fund(XRP(1000), issuer, owner, depositor);
3226 env.trust(asset(1000), owner);
3227 env.trust(asset(1000), depositor);
3228 env(pay(issuer, owner, asset(200)));
3229 env(pay(issuer, depositor, asset(200)));
3232 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3233 tx[sfScale] = scale;
3236 auto const [vaultAccount, issuanceId] =
3238 auto const vault = env.le(keylet);
3240 Account(
"vault", vault->at(sfAccount)),
3241 vault->at(sfShareMPTID)};
3244 env.memoize(vaultAccount);
3248 return env.app().openLedger().modify(
3252 if (!BEAST_EXPECT(vault !=
nullptr))
3254 auto shares = sb.
peek(
3256 if (!BEAST_EXPECT(shares !=
nullptr))
3258 if (fn(*vault, *shares))
3273 .depositor = depositor,
3274 .vaultAccount = vaultAccount,
3284 testCase(18, [&,
this](Env& env, Data d) {
3285 testcase(
"Scale deposit overflow on first deposit");
3286 auto tx = d.vault.deposit(
3287 {.depositor = d.depositor,
3289 .amount = d.asset(10)});
3294 testCase(18, [&,
this](Env& env, Data d) {
3295 testcase(
"Scale deposit overflow on second deposit");
3298 auto tx = d.vault.deposit(
3299 {.depositor = d.depositor,
3301 .amount = d.asset(5)});
3307 auto tx = d.vault.deposit(
3308 {.depositor = d.depositor,
3310 .amount = d.asset(10)});
3316 testCase(18, [&,
this](Env& env, Data d) {
3317 testcase(
"Scale deposit overflow on total shares");
3320 auto tx = d.vault.deposit(
3321 {.depositor = d.depositor,
3323 .amount = d.asset(5)});
3329 auto tx = d.vault.deposit(
3330 {.depositor = d.depositor,
3332 .amount = d.asset(5)});
3338 testCase(1, [&,
this](Env& env, Data d) {
3341 auto const start = env.balance(d.depositor, d.assets).number();
3342 auto tx = d.vault.deposit(
3343 {.depositor = d.depositor,
3345 .amount = d.asset(1)});
3348 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3350 env.balance(d.depositor, d.assets) ==
3354 testCase(1, [&,
this](Env& env, Data d) {
3355 testcase(
"Scale deposit insignificant amount");
3357 auto tx = d.vault.deposit(
3358 {.depositor = d.depositor,
3364 testCase(1, [&,
this](Env& env, Data d) {
3365 testcase(
"Scale deposit exact, using full precision");
3367 auto const start = env.balance(d.depositor, d.assets).number();
3368 auto tx = d.vault.deposit(
3369 {.depositor = d.depositor,
3374 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3376 env.balance(d.depositor, d.assets) ==
3380 testCase(1, [&,
this](Env& env, Data d) {
3381 testcase(
"Scale deposit exact, truncating from .5");
3383 auto const start = env.balance(d.depositor, d.assets).number();
3387 auto tx = d.vault.deposit(
3388 {.depositor = d.depositor,
3393 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3395 env.balance(d.depositor, d.assets) ==
3400 auto tx = d.vault.deposit(
3401 {.depositor = d.depositor,
3406 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3408 env.balance(d.depositor, d.assets) ==
3413 auto tx = d.vault.deposit(
3414 {.depositor = d.depositor,
3419 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
3421 env.balance(d.depositor, d.assets) ==
3426 testCase(1, [&,
this](Env& env, Data d) {
3427 testcase(
"Scale deposit exact, truncating from .01");
3429 auto const start = env.balance(d.depositor, d.assets).number();
3431 auto tx = d.vault.deposit(
3432 {.depositor = d.depositor,
3437 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3439 env.balance(d.depositor, d.assets) ==
3444 auto tx = d.vault.deposit(
3445 {.depositor = d.depositor,
3450 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3452 env.balance(d.depositor, d.assets) ==
3457 testCase(1, [&,
this](Env& env, Data d) {
3458 testcase(
"Scale deposit exact, truncating from .99");
3460 auto const start = env.balance(d.depositor, d.assets).number();
3462 auto tx = d.vault.deposit(
3463 {.depositor = d.depositor,
3468 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
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(18));
3483 env.balance(d.depositor, d.assets) ==
3488 testCase(1, [&,
this](Env& env, Data d) {
3490 auto const start = env.balance(d.depositor, d.assets).number();
3491 auto tx = d.vault.deposit(
3492 {.depositor = d.depositor,
3497 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3499 env.balance(d.depositor, d.assets) ==
3502 env.balance(d.vaultAccount, d.assets) ==
3505 env.balance(d.vaultAccount, d.shares) ==
3514 auto const start = env.balance(d.depositor, d.assets).number();
3515 auto tx = d.vault.withdraw(
3516 {.depositor = d.depositor,
3522 env.balance(d.depositor, d.shares) == d.share(900));
3524 env.balance(d.depositor, d.assets) ==
3527 env.balance(d.vaultAccount, d.assets) ==
3530 env.balance(d.vaultAccount, d.shares) ==
3535 testcase(
"Scale redeem with rounding");
3540 auto const start = env.balance(d.depositor, d.assets).number();
3541 d.peek([](
SLE& vault,
auto&) ->
bool {
3542 vault[sfAssetsAvailable] =
Number(1);
3550 auto tx = d.vault.withdraw(
3551 {.depositor = d.depositor,
3557 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3559 env.balance(d.depositor, d.assets) ==
3562 env.balance(d.vaultAccount, d.assets) ==
3565 env.balance(d.vaultAccount, d.shares) ==
3575 auto const start = env.balance(d.depositor, d.assets).number();
3577 tx = d.vault.withdraw(
3578 {.depositor = d.depositor,
3584 env.balance(d.depositor, d.shares) == d.share(875 - 21));
3586 env.balance(d.depositor, d.assets) ==
3589 env.balance(d.vaultAccount, d.assets) ==
3592 env.balance(d.vaultAccount, d.shares) ==
3598 auto const rest = env.balance(d.depositor, d.shares).number();
3600 tx = d.vault.withdraw(
3601 {.depositor = d.depositor,
3603 .amount =
STAmount(d.share, rest)});
3606 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3608 env.balance(d.vaultAccount, d.assets).number() == 0);
3610 env.balance(d.vaultAccount, d.shares).number() == 0);
3614 testCase(18, [&,
this](Env& env, Data d) {
3615 testcase(
"Scale withdraw overflow");
3618 auto tx = d.vault.deposit(
3619 {.depositor = d.depositor,
3621 .amount = d.asset(5)});
3627 auto tx = d.vault.withdraw(
3628 {.depositor = d.depositor,
3636 testCase(1, [&,
this](Env& env, Data d) {
3638 auto const start = env.balance(d.depositor, d.assets).number();
3639 auto tx = d.vault.deposit(
3640 {.depositor = d.depositor,
3645 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3647 env.balance(d.depositor, d.assets) ==
3650 env.balance(d.vaultAccount, d.assets) ==
3653 env.balance(d.vaultAccount, d.shares) ==
3665 auto const start = env.balance(d.depositor, d.assets).number();
3666 auto tx = d.vault.withdraw(
3667 {.depositor = d.depositor,
3673 env.balance(d.depositor, d.shares) == d.share(900));
3675 env.balance(d.depositor, d.assets) ==
3678 env.balance(d.vaultAccount, d.assets) ==
3681 env.balance(d.vaultAccount, d.shares) ==
3686 testcase(
"Scale withdraw insignificant amount");
3687 auto tx = d.vault.withdraw(
3688 {.depositor = d.depositor,
3695 testcase(
"Scale withdraw with rounding assets");
3703 auto const start = env.balance(d.depositor, d.assets).number();
3704 d.peek([](
SLE& vault,
auto&) ->
bool {
3705 vault[sfAssetsAvailable] =
Number(1);
3713 auto tx = d.vault.withdraw(
3714 {.depositor = d.depositor,
3720 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3722 env.balance(d.depositor, d.assets) ==
3725 env.balance(d.vaultAccount, d.assets) ==
3728 env.balance(d.vaultAccount, d.shares) ==
3733 testcase(
"Scale withdraw with rounding shares up");
3741 auto const start = env.balance(d.depositor, d.assets).number();
3742 auto tx = d.vault.withdraw(
3743 {.depositor = d.depositor,
3749 env.balance(d.depositor, d.shares) == d.share(875 - 38));
3751 env.balance(d.depositor, d.assets) ==
3754 env.balance(d.vaultAccount, d.assets) ==
3757 env.balance(d.vaultAccount, d.shares) ==
3762 testcase(
"Scale withdraw with rounding shares down");
3770 auto const start = env.balance(d.depositor, d.assets).number();
3771 auto tx = d.vault.withdraw(
3772 {.depositor = d.depositor,
3778 env.balance(d.depositor, d.shares) == d.share(837 - 37));
3780 env.balance(d.depositor, d.assets) ==
3783 env.balance(d.vaultAccount, d.assets) ==
3786 env.balance(d.vaultAccount, d.shares) ==
3791 testcase(
"Scale withdraw tiny amount");
3793 auto const start = env.balance(d.depositor, d.assets).number();
3794 auto tx = d.vault.withdraw(
3795 {.depositor = d.depositor,
3801 env.balance(d.depositor, d.shares) == d.share(800 - 1));
3803 env.balance(d.depositor, d.assets) ==
3806 env.balance(d.vaultAccount, d.assets) ==
3809 env.balance(d.vaultAccount, d.shares) ==
3816 env.balance(d.vaultAccount, d.assets).number();
3818 tx = d.vault.withdraw(
3819 {.depositor = d.depositor,
3821 .amount =
STAmount(d.asset, rest)});
3824 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3826 env.balance(d.vaultAccount, d.assets).number() == 0);
3828 env.balance(d.vaultAccount, d.shares).number() == 0);
3832 testCase(18, [&,
this](Env& env, Data d) {
3833 testcase(
"Scale clawback overflow");
3836 auto tx = d.vault.deposit(
3837 {.depositor = d.depositor,
3839 .amount = d.asset(5)});
3845 auto tx = d.vault.clawback(
3846 {.issuer = d.issuer,
3848 .holder = d.depositor,
3855 testCase(1, [&,
this](Env& env, Data d) {
3857 auto const start = env.balance(d.depositor, d.assets).number();
3858 auto tx = d.vault.deposit(
3859 {.depositor = d.depositor,
3864 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3866 env.balance(d.depositor, d.assets) ==
3869 env.balance(d.vaultAccount, d.assets) ==
3872 env.balance(d.vaultAccount, d.shares) ==
3883 auto const start = env.balance(d.depositor, d.assets).number();
3884 auto tx = d.vault.clawback(
3885 {.issuer = d.issuer,
3887 .holder = d.depositor,
3892 env.balance(d.depositor, d.shares) == d.share(900));
3894 env.balance(d.depositor, d.assets) ==
3897 env.balance(d.vaultAccount, d.assets) ==
3900 env.balance(d.vaultAccount, d.shares) ==
3905 testcase(
"Scale clawback insignificant amount");
3906 auto tx = d.vault.clawback(
3907 {.issuer = d.issuer,
3909 .holder = d.depositor,
3915 testcase(
"Scale clawback with rounding assets");
3923 auto const start = env.balance(d.depositor, d.assets).number();
3924 auto tx = d.vault.clawback(
3925 {.issuer = d.issuer,
3927 .holder = d.depositor,
3932 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3934 env.balance(d.depositor, d.assets) ==
3937 env.balance(d.vaultAccount, d.assets) ==
3940 env.balance(d.vaultAccount, d.shares) ==
3945 testcase(
"Scale clawback with rounding shares up");
3953 auto const start = env.balance(d.depositor, d.assets).number();
3954 auto tx = d.vault.clawback(
3955 {.issuer = d.issuer,
3957 .holder = d.depositor,
3962 env.balance(d.depositor, d.shares) == d.share(875 - 38));
3964 env.balance(d.depositor, d.assets) ==
3967 env.balance(d.vaultAccount, d.assets) ==
3970 env.balance(d.vaultAccount, d.shares) ==
3975 testcase(
"Scale clawback with rounding shares down");
3983 auto const start = env.balance(d.depositor, d.assets).number();
3984 auto tx = d.vault.clawback(
3985 {.issuer = d.issuer,
3987 .holder = d.depositor,
3992 env.balance(d.depositor, d.shares) == d.share(837 - 37));
3994 env.balance(d.depositor, d.assets) ==
3997 env.balance(d.vaultAccount, d.assets) ==
4000 env.balance(d.vaultAccount, d.shares) ==
4005 testcase(
"Scale clawback tiny amount");
4007 auto const start = env.balance(d.depositor, d.assets).number();
4008 auto tx = d.vault.clawback(
4009 {.issuer = d.issuer,
4011 .holder = d.depositor,
4016 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4018 env.balance(d.depositor, d.assets) ==
4021 env.balance(d.vaultAccount, d.assets) ==
4024 env.balance(d.vaultAccount, d.shares) ==
4031 env.balance(d.vaultAccount, d.assets).number();
4032 d.peek([](
SLE& vault,
auto&) ->
bool {
4033 vault[sfAssetsAvailable] =
Number(5);
4041 tx = d.vault.clawback(
4042 {.issuer = d.issuer,
4044 .holder = d.depositor,
4045 .amount =
STAmount(d.asset, rest)});
4048 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4050 env.balance(d.vaultAccount, d.assets).number() == 0);
4052 env.balance(d.vaultAccount, d.shares).number() == 0);
4060 using namespace test::jtx;
4063 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4064 Account
const owner{
"owner"};
4065 Account
const issuer{
"issuer"};
4067 env.fund(XRP(1000), issuer, owner);
4071 env.trust(asset(1000), owner);
4072 env(pay(issuer, owner, asset(200)));
4075 auto const sequence = env.seq(owner);
4076 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
4082 auto tx1 = vault.deposit(
4083 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
4086 auto tx2 = vault.set({.owner = owner, .id = keylet.
key});
4087 tx2[sfAssetsMaximum] = asset(1000).number();
4092 auto const sleVault = [&env, keylet = keylet,
this]() {
4093 auto const vault = env.le(keylet);
4094 BEAST_EXPECT(vault !=
nullptr);
4098 auto const check = [&, keylet = keylet, sle = sleVault,
this](
4101 BEAST_EXPECT(vault.isObject());
4103 constexpr auto checkString =
4105 return node.isMember(field.fieldName) &&
4106 node[field.fieldName].isString() &&
4107 node[field.fieldName] == v;
4109 constexpr auto checkObject =
4111 return node.isMember(field.fieldName) &&
4112 node[field.fieldName].isObject() &&
4113 node[field.fieldName] == v;
4115 constexpr auto checkInt =
4116 [](
auto& node,
SField const& field,
int v) ->
bool {
4117 return node.isMember(field.fieldName) &&
4118 ((node[field.fieldName].isInt() &&
4119 node[field.fieldName] ==
Json::Int(v)) ||
4120 (node[field.fieldName].isUInt() &&
4124 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
4125 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(keylet.
key));
4126 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
4130 checkString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
4132 checkObject(vault, sfAsset,
to_json(sle->at(sfAsset))));
4133 BEAST_EXPECT(checkString(vault, sfAssetsAvailable,
"50"));
4134 BEAST_EXPECT(checkString(vault, sfAssetsMaximum,
"1000"));
4135 BEAST_EXPECT(checkString(vault, sfAssetsTotal,
"50"));
4136 BEAST_EXPECT(checkString(vault, sfLossUnrealized,
"0"));
4138 auto const strShareID =
strHex(sle->at(sfShareMPTID));
4139 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
4140 BEAST_EXPECT(checkString(vault, sfOwner,
toBase58(owner.id())));
4141 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
4142 BEAST_EXPECT(checkInt(
4145 if (issuance.isObject())
4148 issuance[
"LedgerEntryType"].asString() ==
4151 issuance[jss::mpt_issuance_id].asString() == strShareID);
4152 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
4153 BEAST_EXPECT(checkInt(
4158 checkString(issuance, sfOutstandingAmount,
"50000000"));
4163 testcase(
"RPC ledger_entry selected by key");
4165 jvParams[jss::ledger_index] = jss::validated;
4166 jvParams[jss::vault] =
strHex(keylet.
key);
4167 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4169 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4170 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4171 check(jvVault[jss::result][jss::node]);
4175 testcase(
"RPC ledger_entry selected by owner and seq");
4177 jvParams[jss::ledger_index] = jss::validated;
4178 jvParams[jss::vault][jss::owner] = owner.human();
4179 jvParams[jss::vault][jss::seq] = sequence;
4180 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4182 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4183 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4184 check(jvVault[jss::result][jss::node]);
4188 testcase(
"RPC ledger_entry cannot find vault by key");
4190 jvParams[jss::ledger_index] = jss::validated;
4192 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4194 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4198 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
4200 jvParams[jss::ledger_index] = jss::validated;
4201 jvParams[jss::vault][jss::owner] = issuer.human();
4202 jvParams[jss::vault][jss::seq] = 1'000'000;
4203 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4205 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4209 testcase(
"RPC ledger_entry malformed key");
4211 jvParams[jss::ledger_index] = jss::validated;
4212 jvParams[jss::vault] = 42;
4213 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4215 jvVault[jss::result][jss::error].asString() ==
4216 "malformedRequest");
4220 testcase(
"RPC ledger_entry malformed owner");
4222 jvParams[jss::ledger_index] = jss::validated;
4223 jvParams[jss::vault][jss::owner] = 42;
4224 jvParams[jss::vault][jss::seq] = sequence;
4225 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4227 jvVault[jss::result][jss::error].asString() ==
4232 testcase(
"RPC ledger_entry malformed seq");
4234 jvParams[jss::ledger_index] = jss::validated;
4235 jvParams[jss::vault][jss::owner] = issuer.human();
4236 jvParams[jss::vault][jss::seq] =
"foo";
4237 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4239 jvVault[jss::result][jss::error].asString() ==
4240 "malformedRequest");
4244 testcase(
"RPC ledger_entry negative seq");
4246 jvParams[jss::ledger_index] = jss::validated;
4247 jvParams[jss::vault][jss::owner] = issuer.human();
4248 jvParams[jss::vault][jss::seq] = -1;
4249 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4251 jvVault[jss::result][jss::error].asString() ==
4252 "malformedRequest");
4256 testcase(
"RPC ledger_entry oversized seq");
4258 jvParams[jss::ledger_index] = jss::validated;
4259 jvParams[jss::vault][jss::owner] = issuer.human();
4260 jvParams[jss::vault][jss::seq] = 1e20;
4261 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4263 jvVault[jss::result][jss::error].asString() ==
4264 "malformedRequest");
4268 testcase(
"RPC ledger_entry bool seq");
4270 jvParams[jss::ledger_index] = jss::validated;
4271 jvParams[jss::vault][jss::owner] = issuer.human();
4272 jvParams[jss::vault][jss::seq] =
true;
4273 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4275 jvVault[jss::result][jss::error].asString() ==
4276 "malformedRequest");
4283 jvParams[jss::account] = owner.human();
4284 jvParams[jss::type] = jss::vault;
4286 "json",
"account_objects",
to_string(jvParams))[jss::result];
4288 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
4289 check(jv[jss::account_objects][0u]);
4296 jvParams[jss::ledger_index] = jss::validated;
4297 jvParams[jss::binary] =
false;
4298 jvParams[jss::type] = jss::vault;
4300 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
4301 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
4302 check(jv[jss::result][jss::state][0u]);
4306 testcase(
"RPC vault_info command line");
4308 env.rpc(
"vault_info",
strHex(keylet.
key),
"validated");
4310 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4311 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4313 jv[jss::result][jss::vault],
4314 jv[jss::result][jss::vault][jss::shares]);
4320 jvParams[jss::ledger_index] = jss::validated;
4321 jvParams[jss::vault_id] =
strHex(keylet.
key);
4322 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4324 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4325 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4327 jv[jss::result][jss::vault],
4328 jv[jss::result][jss::vault][jss::shares]);
4332 testcase(
"RPC vault_info invalid vault_id");
4334 jvParams[jss::ledger_index] = jss::validated;
4335 jvParams[jss::vault_id] =
"foobar";
4336 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4338 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4342 testcase(
"RPC vault_info json invalid index");
4344 jvParams[jss::ledger_index] = jss::validated;
4345 jvParams[jss::vault_id] = 0;
4346 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4348 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4352 testcase(
"RPC vault_info json by owner and sequence");
4354 jvParams[jss::ledger_index] = jss::validated;
4355 jvParams[jss::owner] = owner.human();
4356 jvParams[jss::seq] = sequence;
4357 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4359 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4360 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4362 jv[jss::result][jss::vault],
4363 jv[jss::result][jss::vault][jss::shares]);
4367 testcase(
"RPC vault_info json malformed sequence");
4369 jvParams[jss::ledger_index] = jss::validated;
4370 jvParams[jss::owner] = owner.human();
4371 jvParams[jss::seq] =
"foobar";
4372 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4374 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4378 testcase(
"RPC vault_info json invalid sequence");
4380 jvParams[jss::ledger_index] = jss::validated;
4381 jvParams[jss::owner] = owner.human();
4382 jvParams[jss::seq] = 0;
4383 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4385 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4389 testcase(
"RPC vault_info json negative sequence");
4391 jvParams[jss::ledger_index] = jss::validated;
4392 jvParams[jss::owner] = owner.human();
4393 jvParams[jss::seq] = -1;
4394 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4396 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4400 testcase(
"RPC vault_info json oversized sequence");
4402 jvParams[jss::ledger_index] = jss::validated;
4403 jvParams[jss::owner] = owner.human();
4404 jvParams[jss::seq] = 1e20;
4405 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4407 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4411 testcase(
"RPC vault_info json bool sequence");
4413 jvParams[jss::ledger_index] = jss::validated;
4414 jvParams[jss::owner] = owner.human();
4415 jvParams[jss::seq] =
true;
4416 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4418 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4422 testcase(
"RPC vault_info json malformed owner");
4424 jvParams[jss::ledger_index] = jss::validated;
4425 jvParams[jss::owner] =
"foobar";
4426 jvParams[jss::seq] = sequence;
4427 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4429 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4433 testcase(
"RPC vault_info json invalid combination only owner");
4435 jvParams[jss::ledger_index] = jss::validated;
4436 jvParams[jss::owner] = owner.human();
4437 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4439 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4443 testcase(
"RPC vault_info json invalid combination only seq");
4445 jvParams[jss::ledger_index] = jss::validated;
4446 jvParams[jss::seq] = sequence;
4447 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4449 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4453 testcase(
"RPC vault_info json invalid combination seq vault_id");
4455 jvParams[jss::ledger_index] = jss::validated;
4456 jvParams[jss::vault_id] =
strHex(keylet.
key);
4457 jvParams[jss::seq] = sequence;
4458 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4460 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4464 testcase(
"RPC vault_info json invalid combination owner vault_id");
4466 jvParams[jss::ledger_index] = jss::validated;
4467 jvParams[jss::vault_id] =
strHex(keylet.
key);
4468 jvParams[jss::owner] = owner.human();
4469 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4471 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4476 "RPC vault_info json invalid combination owner seq "
4479 jvParams[jss::ledger_index] = jss::validated;
4480 jvParams[jss::vault_id] =
strHex(keylet.
key);
4481 jvParams[jss::seq] = sequence;
4482 jvParams[jss::owner] = owner.human();
4483 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4485 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4489 testcase(
"RPC vault_info json no input");
4491 jvParams[jss::ledger_index] = jss::validated;
4492 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4494 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4498 testcase(
"RPC vault_info command line invalid index");
4499 Json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
4500 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
4504 testcase(
"RPC vault_info command line invalid index");
4505 Json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
4507 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4511 testcase(
"RPC vault_info command line invalid index");
4515 jv[jss::result][jss::error].asString() ==
"entryNotFound");
4519 testcase(
"RPC vault_info command line invalid ledger");
4522 jv[jss::result][jss::error].asString() ==
"lgrNotFound");