45 using namespace test::jtx;
46 Account issuer{
"issuer"};
47 Account owner{
"owner"};
48 Account depositor{
"depositor"};
49 Account charlie{
"charlie"};
52 auto const testSequence = [&,
this](
54 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
55 tx[sfData] =
"AFEED00E";
56 tx[sfAssetsMaximum] = asset(100).number();
59 BEAST_EXPECT(env.le(keylet));
62 auto const [share, vaultAccount] =
64 auto const vault = env.le(keylet);
65 BEAST_EXPECT(vault !=
nullptr);
66 if (!asset.integral())
67 BEAST_EXPECT(vault->at(sfScale) == 6);
69 BEAST_EXPECT(vault->at(sfScale) == 0);
71 BEAST_EXPECT(shares !=
nullptr);
72 if (!asset.integral())
73 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
75 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
76 return {
MPTIssue(vault->at(sfShareMPTID)), Account(
"vault", vault->at(sfAccount))};
78 auto const shares = share.raw().get<
MPTIssue>();
79 env.memoize(vaultAccount);
82 Account alice{
"alice"};
84 env.fund(XRP(1000), alice, erin);
89 testcase(prefix +
" fail to deposit more than assets held");
90 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(10000)});
96 testcase(prefix +
" deposit non-zero amount");
97 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
100 BEAST_EXPECT(env.balance(depositor, shares) == share(50 * scale));
104 testcase(prefix +
" deposit non-zero amount again");
105 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
108 BEAST_EXPECT(env.balance(depositor, shares) == share(100 * scale));
112 testcase(prefix +
" fail to delete non-empty vault");
113 auto tx = vault.del({.owner = owner, .id = keylet.
key});
119 testcase(prefix +
" fail to update because wrong owner");
120 auto tx = vault.set({.owner = issuer, .id = keylet.
key});
121 tx[sfAssetsMaximum] = asset(50).number();
127 testcase(prefix +
" fail to set maximum lower than current amount");
128 auto tx = vault.set({.owner = owner, .id = keylet.
key});
129 tx[sfAssetsMaximum] = asset(50).number();
135 testcase(prefix +
" set maximum higher than current amount");
136 auto tx = vault.set({.owner = owner, .id = keylet.
key});
137 tx[sfAssetsMaximum] = asset(150).number();
143 testcase(prefix +
" set maximum is idempotent, set it again");
144 auto tx = vault.set({.owner = owner, .id = keylet.
key});
145 tx[sfAssetsMaximum] = asset(150).number();
152 auto tx = vault.set({.owner = owner, .id = keylet.
key});
159 testcase(prefix +
" fail to set domain on public vault");
160 auto tx = vault.set({.owner = owner, .id = keylet.
key});
167 testcase(prefix +
" fail to deposit more than maximum");
168 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
174 testcase(prefix +
" reset maximum to zero i.e. not enforced");
175 auto tx = vault.set({.owner = owner, .id = keylet.
key});
176 tx[sfAssetsMaximum] = asset(0).number();
182 testcase(prefix +
" fail to withdraw more than assets held");
183 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
189 testcase(prefix +
" deposit some more");
190 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
193 BEAST_EXPECT(env.balance(depositor, shares) == share(200 * scale));
197 testcase(prefix +
" clawback some");
200 vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(10)});
203 if (!asset.raw().native())
205 BEAST_EXPECT(env.balance(depositor, shares) == share(190 * scale));
212 auto tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor});
215 if (!asset.raw().native())
217 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
220 auto tx = vault.clawback(
221 {.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(10)});
227 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(10)});
234 if (!asset.raw().native())
236 testcase(prefix +
" deposit again");
237 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(200)});
240 BEAST_EXPECT(env.balance(depositor, shares) == share(200 * scale));
244 testcase(prefix +
" deposit/withdrawal same or less than fee");
245 auto const amount = env.current()->fees().base;
247 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = amount});
251 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = amount});
255 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = amount});
260 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = amount});
261 tx[sfDestination] = charlie.human();
265 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = amount - 1});
269 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = amount - 1});
275 testcase(prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
276 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
277 tx[sfDestination] = alice.human();
283 testcase(prefix +
" fail to withdraw to zero destination");
284 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
285 tx[sfDestination] =
"0";
290 if (!asset.raw().native())
292 testcase(prefix +
" fail to withdraw to 3rd party no authorization");
293 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
294 tx[sfDestination] = erin.human();
300 testcase(prefix +
" fail to withdraw to 3rd party lsfRequireDestTag");
301 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
302 tx[sfDestination] = dave.human();
308 testcase(prefix +
" withdraw to 3rd party lsfRequireDestTag");
309 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
310 tx[sfDestination] = dave.human();
311 tx[sfDestinationTag] =
"0";
317 testcase(prefix +
" deposit again");
318 auto tx = vault.deposit({.depositor = dave, .id = keylet.
key, .amount = asset(50)});
324 testcase(prefix +
" fail to withdraw lsfRequireDestTag");
325 auto tx = vault.withdraw({.depositor = dave, .id = keylet.
key, .amount = asset(50)});
331 testcase(prefix +
" withdraw with tag");
332 auto tx = vault.withdraw({.depositor = dave, .id = keylet.
key, .amount = asset(50)});
333 tx[sfDestinationTag] =
"0";
339 testcase(prefix +
" withdraw to authorized 3rd party");
340 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
341 tx[sfDestination] = charlie.human();
344 BEAST_EXPECT(env.balance(depositor, shares) == share(100 * scale));
348 testcase(prefix +
" withdraw to issuer");
349 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
350 tx[sfDestination] = issuer.human();
353 BEAST_EXPECT(env.balance(depositor, shares) == share(50 * scale));
356 if (!asset.raw().native())
358 testcase(prefix +
" issuer deposits");
359 auto tx = vault.deposit({.depositor = issuer, .id = keylet.
key, .amount = asset(10)});
362 BEAST_EXPECT(env.balance(issuer, shares) == share(10 * scale));
364 testcase(prefix +
" issuer withdraws");
365 tx = vault.withdraw({.depositor = issuer, .id = keylet.
key, .amount = share(10 * scale)});
368 BEAST_EXPECT(env.balance(issuer, shares) == share(0 * scale));
372 testcase(prefix +
" withdraw remaining assets");
373 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
376 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
378 if (!asset.raw().native())
381 vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
387 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = share(10)});
393 if (!asset.integral())
395 testcase(prefix +
" temporary authorization for 3rd party");
396 env(trust(erin, asset(1000)));
397 env(trust(issuer, asset(0), erin,
tfSetfAuth));
398 env(pay(issuer, erin, asset(10)));
401 auto tx = vault.deposit({.depositor = erin, .id = keylet.
key, .amount = asset(10)});
405 auto tx = pay(erin, depositor, share(10 * scale));
412 env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1)}));
419 testcase(prefix +
" withdraw to authorized 3rd party");
421 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(10)});
422 tx[sfDestination] = erin.human();
427 env(pay(erin, issuer, asset(10)));
430 testcase(prefix +
" fail to pay to unauthorized 3rd party");
431 env(trust(erin, asset(0)));
435 env(pay(depositor, erin, share(1)), ter{
tecNO_LINE});
439 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(1)});
445 testcase(prefix +
" fail to delete because wrong owner");
446 auto tx = vault.del({.owner = issuer, .id = keylet.
key});
452 testcase(prefix +
" delete empty vault");
453 auto tx = vault.del({.owner = owner, .id = keylet.
key});
456 BEAST_EXPECT(!env.le(keylet));
461 Env env{*
this, testable_amendments() | featureSingleAssetVault};
464 env.fund(XRP(1000), issuer, owner, depositor, charlie, dave);
474 testSequence(prefix, env, vault, asset);
479 testCases(
"IOU", [&](Env& env) ->
Asset {
481 env(trust(owner, asset(1000)));
482 env(trust(depositor, asset(1000)));
483 env(trust(charlie, asset(1000)));
484 env(trust(dave, asset(1000)));
485 env(trust(issuer, asset(0), owner,
tfSetfAuth));
486 env(trust(issuer, asset(0), depositor,
tfSetfAuth));
487 env(trust(issuer, asset(0), charlie,
tfSetfAuth));
488 env(trust(issuer, asset(0), dave,
tfSetfAuth));
489 env(pay(issuer, depositor, asset(1000)));
494 testCases(
"MPT", [&](Env& env) ->
Asset {
495 MPTTester mptt{env, issuer, mptInitNoFund};
498 mptt.authorize({.account = depositor});
499 mptt.authorize({.account = charlie});
500 mptt.authorize({.account = dave});
501 env(pay(issuer, depositor, asset(1000)));
510 using namespace test::jtx;
514 FeatureBitset features = testable_amendments() | featureSingleAssetVault;
520 Env & env, Account
const& issuer, Account
const& owner,
Asset const& asset, Vault& vault)> test,
521 CaseArgs args = {}) {
522 Env env{*
this, args.features};
523 Account issuer{
"issuer"};
524 Account owner{
"owner"};
526 env.fund(XRP(1000), issuer, owner);
534 env(trust(owner, asset(1000)));
535 env(trust(issuer, asset(0), owner,
tfSetfAuth));
536 env(pay(issuer, owner, asset(1000)));
539 test(env, issuer, owner, asset, vault);
543 return [&, resultAfterCreate](
544 Env& env, Account
const& issuer, Account
const& owner,
Asset const& asset, Vault& vault) {
545 testcase(
"disabled single asset vault");
547 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
551 auto tx = vault.set({.owner = owner, .id = keylet.
key});
552 env(tx, data(
"test"), ter{resultAfterCreate});
556 auto tx = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
557 env(tx, ter{resultAfterCreate});
561 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
562 env(tx, ter{resultAfterCreate});
567 vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(10)});
568 env(tx, ter{resultAfterCreate});
572 auto tx = vault.del({.owner = owner, .id = keylet.
key});
573 env(tx, ter{resultAfterCreate});
578 testCase(testDisabled(), {.features = testable_amendments() - featureSingleAssetVault});
580 testCase(testDisabled(
tecNO_ENTRY), {.features = testable_amendments() - featureMPTokensV1});
583 [&](Env& env, Account
const& issuer, Account
const& owner,
Asset const& asset, Vault& vault) {
584 testcase(
"disabled permissioned domains");
586 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
594 auto tx = vault.set({.owner = owner, .id = keylet.
key});
595 env(tx, data(
"Test"));
601 {.features = testable_amendments() - featurePermissionedDomains});
603 testCase([&](Env& env, Account
const& issuer, Account
const& owner,
Asset const& asset, Vault& vault) {
606 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
611 auto tx = vault.set({.owner = owner, .id = keylet.
key});
617 auto tx = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
623 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
629 auto tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(10)});
635 auto tx = vault.del({.owner = owner, .id = keylet.
key});
641 testCase([&](Env& env, Account
const& issuer, Account
const& owner,
Asset const& asset, Vault& vault) {
644 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
649 auto tx = vault.set({.owner = owner, .id = keylet.
key});
655 auto tx = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
661 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
667 auto tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(10)});
673 auto tx = vault.del({.owner = owner, .id = keylet.
key});
680 [&](Env& env, Account
const&, Account
const& owner,
Asset const&, Vault& vault) {
681 testcase(
"disabled permissioned domain");
683 auto [tx, keylet] = vault.create({.owner = owner, .asset =
xrpIssue()});
688 auto tx = vault.set({.owner = owner, .id = keylet.
key});
694 auto tx = vault.set({.owner = owner, .id = keylet.
key});
695 tx[sfDomainID] =
"0";
699 {.features = (testable_amendments() | featureSingleAssetVault) - featurePermissionedDomains});
701 testCase([&](Env& env, Account
const& issuer, Account
const& owner,
Asset const& asset, Vault& vault) {
704 auto [tx, keylet] = vault.create({.owner = owner, .asset =
xrpIssue()});
707 auto tx = vault.set({
715 auto tx = vault.deposit({.depositor = owner, .id = beast::zero, .amount = asset(10)});
720 auto tx = vault.withdraw({.depositor = owner, .id = beast::zero, .amount = asset(10)});
725 auto tx = vault.clawback({.issuer = issuer, .id = beast::zero, .holder = owner, .amount = asset(10)});
730 auto tx = vault.del({
738 testCase([&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
739 testcase(
"withdraw to bad destination");
741 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
744 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
745 tx[jss::Destination] =
"0";
750 testCase([&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
754 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
760 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
767 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
771 auto const sleVault = env.le(keylet);
772 BEAST_EXPECT(sleVault);
773 BEAST_EXPECT((*sleVault)[sfScale] == 18);
777 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
781 auto const sleVault = env.le(keylet);
782 BEAST_EXPECT(sleVault);
783 BEAST_EXPECT((*sleVault)[sfScale] == 0);
787 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
790 auto const sleVault = env.le(keylet);
791 BEAST_EXPECT(sleVault);
792 BEAST_EXPECT((*sleVault)[sfScale] == 6);
796 testCase([&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
797 testcase(
"create or set invalid data");
799 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
815 auto tx = vault.set({.owner = owner, .id = keylet.
key});
821 auto tx = vault.set({.owner = owner, .id = keylet.
key});
828 testCase([&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
831 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
834 auto tx = vault.set({.owner = owner, .id = keylet.
key});
839 testCase([&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
840 testcase(
"create with invalid metadata");
842 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
846 tx[sfMPTokenMetadata] =
"";
859 testCase([&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
862 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
865 auto tx = vault.set({.owner = owner, .id = keylet.
key});
871 testCase([&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
874 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
877 auto tx = vault.deposit({.depositor = owner, .id = keylet.
key, .amount =
negativeAmount(asset)});
882 auto tx = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(0)});
887 testCase([&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
888 testcase(
"invalid set immutable flag");
890 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
893 auto tx = vault.set({.owner = owner, .id = keylet.
key});
899 testCase([&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
900 testcase(
"invalid withdraw amount");
902 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
905 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount =
negativeAmount(asset)});
910 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(0)});
915 testCase([&](Env& env, Account
const& issuer, Account
const& owner,
Asset const& asset, Vault& vault) {
918 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
923 auto tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(50)});
928 auto tx = vault.clawback(
929 {.issuer = issuer, .id = keylet.
key, .holder = owner, .amount =
negativeAmount(asset)});
934 testCase([&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
937 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
941 tx[sfWithdrawalPolicy] = 0;
960 tx[sfDomainID] =
"0";
1412 using namespace test::jtx;
1416 bool enableClawback =
true;
1418 int initialXRP = 1000;
1421 auto testCase = [
this](
1424 Account
const& issuer,
1425 Account
const& owner,
1426 Account
const& depositor,
1429 MPTTester& mptt)> test,
1430 CaseArgs args = {}) {
1431 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1432 Account issuer{
"issuer"};
1433 Account owner{
"owner"};
1434 Account depositor{
"depositor"};
1435 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
1439 MPTTester mptt{env, issuer, mptInitNoFund};
1446 mptt.authorize({.account = owner});
1447 mptt.authorize({.account = depositor});
1448 if (args.requireAuth)
1450 mptt.authorize({.account = issuer, .holder = owner});
1451 mptt.authorize({.account = issuer, .holder = depositor});
1454 env(pay(issuer, depositor, asset(1000)));
1457 test(env, issuer, owner, depositor, asset, vault, mptt);
1462 Account
const& issuer,
1463 Account
const& owner,
1464 Account
const& depositor,
1468 testcase(
"MPT nothing to clawback from");
1470 vault.clawback({.issuer = issuer, .id =
keylet::skip().
key, .holder = depositor, .amount = asset(10)});
1476 Account
const& issuer,
1477 Account
const& owner,
1478 Account
const& depositor,
1482 testcase(
"MPT global lock blocks create");
1483 mptt.set({.account = issuer, .flags =
tfMPTLock});
1484 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1490 Account
const& issuer,
1491 Account
const& owner,
1492 Account
const& depositor,
1496 testcase(
"MPT global lock blocks deposit");
1497 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1501 mptt.set({.account = issuer, .flags =
tfMPTLock});
1504 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1509 tx = vault.del({.owner = owner, .id = keylet.
key});
1515 Account
const& issuer,
1516 Account
const& owner,
1517 Account
const& depositor,
1521 testcase(
"MPT global lock blocks withdrawal");
1522 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1525 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1531 auto v = env.le(keylet);
1533 MPTID share = (*v)[sfShareMPTID];
1535 BEAST_EXPECT(issuance);
1536 Number outstandingShares = issuance->at(sfOutstandingAmount);
1537 BEAST_EXPECT(outstandingShares == 100);
1539 mptt.set({.account = issuer, .flags =
tfMPTLock});
1542 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1545 tx[sfDestination] = issuer.human();
1549 tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
1555 BEAST_EXPECT(mptSle ==
nullptr);
1558 tx = vault.del({.owner = owner, .id = keylet.
key});
1564 Account
const& issuer,
1565 Account
const& owner,
1566 Account
const& depositor,
1570 testcase(
"MPT only issuer can clawback");
1572 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1576 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1581 auto tx = vault.clawback({
1582 .issuer = depositor,
1584 .holder = depositor,
1590 auto tx = vault.clawback({
1593 .holder = depositor,
1602 Account
const& issuer,
1603 Account
const& owner,
1604 Account
const& depositor,
1608 testcase(
"MPT depositor without MPToken, auth required");
1610 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1614 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
1624 auto const sleMPT1 = env.le(mptoken);
1625 BEAST_EXPECT(sleMPT1 ==
nullptr);
1627 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1631 auto const sleMPT2 = env.le(mptoken);
1632 BEAST_EXPECT(sleMPT2 ==
nullptr);
1637 Account charlie{
"charlie"};
1638 env.fund(XRP(1000), charlie);
1641 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1642 tx[sfDestination] = charlie.human();
1646 {.requireAuth =
true});
1651 Account
const& issuer,
1652 Account
const& owner,
1653 Account
const& depositor,
1657 testcase(
"MPT depositor without MPToken, no auth required");
1659 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1662 auto v = env.le(keylet);
1666 {.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
1676 auto const sleMPT1 = env.le(mptoken);
1677 BEAST_EXPECT(sleMPT1 ==
nullptr);
1679 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1683 auto const sleMPT2 = env.le(mptoken);
1684 BEAST_EXPECT(sleMPT2 !=
nullptr);
1685 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
1694 auto const sleMPT1 = env.le(mptoken);
1695 BEAST_EXPECT(sleMPT1 ==
nullptr);
1697 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1698 tx[sfDestination] = owner.human();
1702 auto const sleMPT2 = env.le(mptoken);
1703 BEAST_EXPECT(sleMPT2 ==
nullptr);
1706 {.requireAuth =
false});
1709 Env env{*
this, testable_amendments()};
1718 Account
const& issuer,
1719 Account
const& owner,
1720 Account
const& depositor,
1724 testcase(
"MPT fail reserve to re-create MPToken");
1726 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1729 auto v = env.le(keylet);
1732 env(pay(depositor, owner, asset(1000)));
1736 {.depositor = owner, .id = keylet.
key, .amount = asset(1000)});
1746 auto const sleMPT = env.le(mptoken);
1747 BEAST_EXPECT(sleMPT ==
nullptr);
1750 env(ticket::create(owner, 1));
1754 tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(100)});
1758 env(pay(depositor, owner, XRP(incReserve)));
1766 {.requireAuth =
false, .initialXRP = acctReserve + incReserve * 4 + 1});
1770 Account
const& issuer,
1771 Account
const& owner,
1772 Account
const& depositor,
1778 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1782 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
1787 auto tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
1791 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
1795 auto [tx, keylet] = vault.create({.owner = depositor, .asset = asset});
1800 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(10)});
1805 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(10)});
1810 auto tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
1814 env(vault.del({.owner = owner, .id = keylet.key}));
1819 Account
const& issuer,
1820 Account
const& owner,
1821 Account
const& depositor,
1825 testcase(
"MPT vault owner can receive shares unless unauthorized");
1827 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1831 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
1836 auto const vault = env.le(keylet);
1837 return vault->at(sfShareMPTID);
1843 env(pay(depositor, owner, shares(1)));
1846 tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = shares(1)});
1851 env(pay(depositor, owner, shares(1)));
1854 tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(0)});
1859 env(pay(depositor, owner, shares(1)));
1863 env(pay(owner, depositor, shares(1)));
1869 jv[sfAccount] = owner.human();
1870 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
1872 jv[sfTransactionType] = jss::MPTokenAuthorize;
1878 tx = pay(depositor, owner, shares(1));
1883 tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
1888 env(vault.del({.owner = owner, .id = keylet.key}));
1896 Account
const& issuer,
1897 Account
const& owner,
1898 Account
const& depositor,
1904 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1908 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
1914 vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
1918 {.enableClawback =
false});
1922 Account
const& issuer,
1923 Account
const& owner,
1924 Account
const& depositor,
1929 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1932 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
1936 mptt.authorize({.account = issuer, .holder = depositor, .flags =
tfMPTUnauthorize});
1940 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1944 tx[sfDestination] = issuer.human();
1948 tx[sfDestination] = owner.human();
1955 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1961 tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = issuer, .amount = asset(800)});
1965 tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(800)});
1969 env(vault.del({.owner = owner, .id = keylet.key}));
1974 Account
const& issuer,
1975 Account
const& owner,
1976 Account
const& depositor,
1980 testcase(
"MPT lock of vault pseudo-account");
1981 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1985 auto const vaultAccount = [&env, keylet = keylet,
this]() ->
AccountID {
1986 auto const vault = env.le(keylet);
1987 BEAST_EXPECT(vault !=
nullptr);
1988 return vault->at(sfAccount);
1991 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1997 jv[jss::Account] = issuer.human();
1999 jv[jss::Holder] =
toBase58(vaultAccount);
2000 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
2007 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
2010 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
2014 tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(100)});
2018 tx = vault.del({.owner = owner, .id = keylet.
key});
2025 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2026 Account owner{
"owner"};
2027 Account issuer{
"issuer"};
2028 env.fund(XRP(1000000), owner, issuer);
2032 MPTTester mptt{env, issuer, mptInitNoFund};
2034 mptt.authorize({.account = owner});
2035 mptt.authorize({.account = issuer, .holder = owner});
2037 env(pay(issuer, owner, asset(100)));
2038 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
2042 auto const shares = [&env, keylet = k1,
this]() ->
Asset {
2043 auto const vault = env.le(keylet);
2044 BEAST_EXPECT(vault !=
nullptr);
2045 return MPTIssue(vault->at(sfShareMPTID));
2048 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
2056 Account
const& owner,
2057 Account
const& depositor,
2063 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2067 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
2078 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
2091 env(vault.del({.owner = owner, .id = keylet.key}));
2098 using namespace test::jtx;
2102 int initialXRP = 1000;
2105 bool charlieRipple =
true;
2108 auto testCase = [&,
this](
2111 Account
const& owner,
2112 Account
const& issuer,
2113 Account
const& charlie,
2118 CaseArgs args = {}) {
2119 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2120 Account
const owner{
"owner"};
2121 Account
const issuer{
"issuer"};
2122 Account
const charlie{
"charlie"};
2124 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
2129 env.trust(asset(1000), owner);
2130 env(pay(issuer, owner, asset(args.initialIOU)));
2132 if (!args.charlieRipple)
2136 env.trust(asset(1000), charlie);
2138 env(pay(issuer, charlie, asset(args.initialIOU)));
2143 env.trust(asset(1000), charlie);
2145 env(rate(issuer, args.transferRate));
2148 auto const vaultAccount = [&env](
xrpl::Keylet keylet) -> Account {
2149 return Account(
"vault", env.le(keylet)->at(sfAccount));
2151 auto const issuanceId = [&env](
xrpl::Keylet keylet) ->
MPTID {
return env.le(keylet)->at(sfShareMPTID); };
2153 test(env, owner, issuer, charlie, vaultAccount, vault, asset, issuanceId);
2158 Account
const& owner,
2159 Account
const& issuer,
2165 testcase(
"IOU cannot use different asset");
2168 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2174 auto tx = [&, account = vaultAccount(keylet)]() {
2176 jv[jss::Account] = issuer.human();
2179 ja[jss::issuer] =
toBase58(account);
2181 jv[jss::TransactionType] = jss::TrustSet;
2190 auto tx = vault.deposit({.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2196 auto tx = vault.withdraw({.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2201 env(vault.del({.owner = owner, .id = keylet.key}));
2207 Account
const& owner,
2208 Account
const& issuer,
2209 Account
const& charlie,
2214 testcase(
"IOU frozen trust line to vault account");
2216 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2220 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2223 Asset const share =
Asset(issuanceId(keylet));
2226 auto trustSet = [&, account = vaultAccount(keylet)]() {
2228 jv[jss::Account] = issuer.human();
2231 ja[jss::issuer] =
toBase58(account);
2233 jv[jss::TransactionType] = jss::TrustSet;
2245 auto tx = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(80)});
2250 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(100)});
2254 tx[sfDestination] = charlie.human();
2261 auto tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(50)});
2271 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = share(50'000'000)}));
2273 env(vault.del({.owner = owner, .id = keylet.key}));
2280 Account
const& owner,
2281 Account
const& issuer,
2282 Account
const& charlie,
2287 testcase(
"IOU transfer fees not applied");
2289 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2293 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2297 Asset const share =
Asset(issuanceId(keylet));
2300 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2301 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(100));
2305 vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(50)});
2311 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2312 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(50));
2314 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = share(20'000'000)}));
2317 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2318 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(30));
2321 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = share(30'000'000)});
2322 tx[sfDestination] = charlie.human();
2327 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2328 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2329 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(0));
2331 env(vault.del({.owner = owner, .id = keylet.key}));
2334 CaseArgs{.transferRate = 1.25});
2338 Account
const& owner,
2339 Account
const& issuer,
2340 Account
const& charlie,
2345 testcase(
"IOU frozen trust line to depositor");
2347 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2351 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2355 auto const withdrawToCharlie = [&](
xrpl::Keylet keylet) {
2356 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2357 tx[sfDestination] = charlie.human();
2360 env(withdrawToCharlie);
2367 auto const withdraw = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2376 auto tx = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2382 auto tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(0)});
2387 env(vault.del({.owner = owner, .id = keylet.key}));
2393 Account
const& owner,
2394 Account
const& issuer,
2395 Account
const& charlie,
2400 testcase(
"IOU no trust line to 3rd party");
2402 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2406 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2409 Account
const erin{
"erin"};
2410 env.fund(XRP(1000), erin);
2415 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2416 tx[sfDestination] = erin.human();
2424 Account
const& owner,
2425 Account
const& issuer,
2426 Account
const& charlie,
2431 testcase(
"IOU no trust line to depositor");
2433 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2438 env.trust(asset(0), owner);
2441 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2445 BEAST_EXPECT(trustline ==
nullptr);
2449 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2458 Account
const& owner,
2459 Account
const& issuer,
2460 Account
const& charlie,
2467 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2474 env(trust(issuer, vaultAccount(keylet)[
"IOU"],
tfSetNoRipple), THISLINE);
2478 auto tx = vault.deposit({.depositor = charlie, .id = keylet.
key, .amount = asset(100)});
2485 auto tx1 = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(100)});
2490 auto tx2 = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = shares(100)});
2491 tx2[sfDestination] = charlie.human();
2498 tx[sfAccount] = charlie.human();
2500 tx[sfTransactionType] = jss::MPTokenAuthorize;
2504 env(pay(owner, charlie, shares(100)), THISLINE);
2508 auto tx3 = vault.withdraw({.depositor = charlie, .id = keylet.
key, .amount = shares(100)});
2512 env(pay(charlie, owner, shares(100)), THISLINE);
2516 tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(100)});
2521 env(vault.del({.owner = owner, .id = keylet.key}), THISLINE);
2523 {.charlieRipple =
false});
2528 Account
const& owner,
2529 Account
const& issuer,
2530 Account
const& charlie,
2531 auto const& vaultAccount,
2535 testcase(
"IOU calculation rounding");
2537 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2542 auto const startingOwnerBalance = env.balance(owner, asset);
2543 BEAST_EXPECT((startingOwnerBalance.value() ==
STAmount{asset, 11875, -2}));
2549 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2552 vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(
Number(375, -2))});
2553 for (
auto i = 0; i < 5; ++i)
2560 STAmount const xfer{asset, 1185, -1};
2561 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value() - xfer);
2562 BEAST_EXPECT(env.balance(vaultAccount(keylet), asset) == xfer);
2564 auto const vault = env.le(keylet);
2565 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
2566 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
2572 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(Number(1000 + 37 * 5, -1))}));
2575 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value());
2576 BEAST_EXPECT(env.balance(vaultAccount(keylet), asset) == beast::zero);
2577 auto const vault = env.le(keylet);
2578 BEAST_EXPECT(vault->at(sfAssetsAvailable) == beast::zero);
2579 BEAST_EXPECT(vault->at(sfAssetsTotal) == beast::zero);
2582 env(vault.del({.owner = owner, .id = keylet.key}));
2585 {.initialIOU =
Number(11875, -2)});
2588 Env env{*
this, testable_amendments()};
2597 Account
const& owner,
2598 Account
const& issuer,
2599 Account
const& charlie,
2604 testcase(
"IOU no trust line to depositor no reserve");
2605 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2611 env.trust(asset(0), owner);
2614 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2618 BEAST_EXPECT(trustline ==
nullptr);
2620 env(ticket::create(owner, 1));
2624 tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2628 env(pay(charlie, owner, XRP(incReserve)));
2635 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
2640 Account
const& owner,
2641 Account
const& issuer,
2642 Account
const& charlie,
2647 testcase(
"IOU no reserve for share MPToken");
2648 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2652 env(pay(owner, charlie, asset(100)));
2655 env(ticket::create(charlie, 3));
2659 tx = vault.deposit({.depositor = charlie, .id = keylet.
key, .amount = asset(100)});
2663 env(pay(issuer, charlie, XRP(incReserve)));
2670 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
2674 Account
const& owner,
2675 Account
const& issuer,
2676 Account
const& charlie,
2681 testcase(
"IOU frozen trust line to 3rd party");
2683 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2687 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2691 auto const withdrawToCharlie = [&](
xrpl::Keylet keylet) {
2692 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2693 tx[sfDestination] = charlie.human();
2696 env(withdrawToCharlie);
2699 env(trust(issuer, asset(0), charlie,
tfSetFreeze));
2703 auto const withdraw = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2711 env(vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .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});
2733 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2741 auto tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2745 tx[sfDestination] = charlie.human();
2750 tx = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2756 env(vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}));
2759 env(vault.del({.owner = owner, .id = keylet.key}));
2767 using namespace test::jtx;
2771 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2772 Account issuer{
"issuer"};
2773 Account owner{
"owner"};
2774 Account depositor{
"depositor"};
2775 Account charlie{
"charlie"};
2776 Account pdOwner{
"pdOwner"};
2777 Account credIssuer1{
"credIssuer1"};
2778 Account credIssuer2{
"credIssuer2"};
2781 env.fund(XRP(1000), issuer, owner, depositor, charlie, pdOwner, credIssuer1, credIssuer2);
2788 env.trust(asset(1000), owner);
2789 env(pay(issuer, owner, asset(500)));
2790 env.trust(asset(1000), depositor);
2791 env(pay(issuer, depositor, asset(500)));
2792 env.trust(asset(1000), charlie);
2793 env(pay(issuer, charlie, asset(5)));
2796 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset, .flags =
tfVaultPrivate});
2799 BEAST_EXPECT(env.le(keylet));
2802 testcase(
"private vault owner can deposit");
2803 auto tx = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(50)});
2808 testcase(
"private vault depositor not authorized yet");
2809 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
2814 testcase(
"private vault cannot set non-existing domain");
2815 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2821 testcase(
"private vault set domainId");
2824 pdomain::Credentials
const credentials1{{.issuer = credIssuer1, .credType = credType}};
2826 env(pdomain::setTx(pdOwner, credentials1));
2827 auto const domainId1 = [&]() {
2829 return pdomain::getNewDomain(env.meta());
2832 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2843 pdomain::Credentials
const credentials{
2844 {.issuer = credIssuer1, .credType = credType}, {.issuer = credIssuer2, .credType = credType}};
2846 env(pdomain::setTx(pdOwner, credentials));
2847 auto const domainId = [&]() {
2849 return pdomain::getNewDomain(env.meta());
2852 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2858 tx = vault.set({.owner = owner, .id = keylet.
key});
2866 testcase(
"private vault depositor still not authorized");
2867 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
2872 auto const credKeylet = credentials::keylet(depositor, credIssuer1, credType);
2874 testcase(
"private vault depositor now authorized");
2875 env(credentials::create(depositor, credIssuer1, credType));
2876 env(credentials::accept(depositor, credIssuer1, credType));
2877 env(credentials::create(charlie, credIssuer1, credType));
2880 auto credSle = env.le(credKeylet);
2881 BEAST_EXPECT(credSle !=
nullptr);
2883 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
2887 tx = vault.deposit({.depositor = charlie, .id = keylet.
key, .amount = asset(50)});
2893 testcase(
"private vault depositor lost authorization");
2894 env(credentials::deleteCred(credIssuer1, depositor, credIssuer1, credType));
2895 env(credentials::deleteCred(credIssuer1, charlie, credIssuer1, credType));
2897 auto credSle = env.le(credKeylet);
2898 BEAST_EXPECT(credSle ==
nullptr);
2900 auto tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
2905 auto const shares = [&env, keylet = keylet,
this]() ->
Asset {
2906 auto const vault = env.le(keylet);
2907 BEAST_EXPECT(vault !=
nullptr);
2908 return MPTIssue(vault->at(sfShareMPTID));
2912 testcase(
"private vault expired authorization");
2913 uint32_t
const closeTime = env.current()->header().parentCloseTime.time_since_epoch().count();
2915 auto tx0 = credentials::create(depositor, credIssuer2, credType);
2916 tx0[sfExpiration] = closeTime + 20;
2918 tx0 = credentials::create(charlie, credIssuer2, credType);
2919 tx0[sfExpiration] = closeTime + 20;
2923 env(credentials::accept(depositor, credIssuer2, credType));
2924 env(credentials::accept(charlie, credIssuer2, credType));
2929 auto tx1 = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
2934 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
2943 auto const credsKeylet = credentials::keylet(depositor, credIssuer2, credType);
2944 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
2946 auto tx2 = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(1)});
2950 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
2954 auto const credsKeylet = credentials::keylet(charlie, credIssuer2, credType);
2955 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
2957 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
2959 auto tx3 = vault.deposit({.depositor = charlie, .id = keylet.
key, .amount = asset(2)});
2963 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
2964 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
2969 testcase(
"private vault reset domainId");
2970 auto tx = vault.set({.owner = owner, .id = keylet.
key});
2971 tx[sfDomainID] =
"0";
2975 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
2979 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
2983 tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
2986 tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(0)});
3120 using namespace test::jtx;
3124 Account
const& owner;
3125 Account
const& issuer;
3126 Account
const& depositor;
3127 Account
const& vaultAccount;
3138 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3139 Account
const owner{
"owner"};
3140 Account
const issuer{
"issuer"};
3141 Account
const depositor{
"depositor"};
3143 env.fund(XRP(1000), issuer, owner, depositor);
3148 env.trust(asset(1000), owner);
3149 env.trust(asset(1000), depositor);
3150 env(pay(issuer, owner, asset(200)));
3151 env(pay(issuer, depositor, asset(200)));
3154 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3155 tx[sfScale] = scale;
3159 auto const vault = env.le(keylet);
3160 return {Account(
"vault", vault->at(sfAccount)), vault->at(sfShareMPTID)};
3163 env.memoize(vaultAccount);
3169 if (!BEAST_EXPECT(vault !=
nullptr))
3172 if (!BEAST_EXPECT(shares !=
nullptr))
3174 if (fn(*vault, *shares))
3189 .depositor = depositor,
3190 .vaultAccount = vaultAccount,
3200 testCase(18, [&,
this](Env& env, Data d) {
3201 testcase(
"Scale deposit overflow on first deposit");
3202 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)});
3207 testCase(18, [&,
this](Env& env, Data d) {
3208 testcase(
"Scale deposit overflow on second deposit");
3211 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3217 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)});
3223 testCase(18, [&,
this](Env& env, Data d) {
3224 testcase(
"Scale deposit overflow on total shares");
3227 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3233 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3239 testCase(1, [&,
this](Env& env, Data d) {
3242 auto const start = env.balance(d.depositor, d.assets).number();
3243 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(1)});
3246 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3247 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start - 1));
3250 testCase(1, [&,
this](Env& env, Data d) {
3251 testcase(
"Scale deposit insignificant amount");
3253 auto tx = d.vault.deposit(
3254 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(9, -2))});
3258 testCase(1, [&,
this](Env& env, Data d) {
3259 testcase(
"Scale deposit exact, using full precision");
3261 auto const start = env.balance(d.depositor, d.assets).number();
3262 auto tx = d.vault.deposit(
3263 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(15, -1))});
3266 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3267 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(15, -1)));
3270 testCase(1, [&,
this](Env& env, Data d) {
3271 testcase(
"Scale deposit exact, truncating from .5");
3273 auto const start = env.balance(d.depositor, d.assets).number();
3277 auto tx = d.vault.deposit(
3278 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(125, -2))});
3281 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3282 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(12, -1)));
3286 auto tx = d.vault.deposit(
3287 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(1201, -3))});
3290 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3291 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(24, -1)));
3295 auto tx = d.vault.deposit(
3296 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(1299, -3))});
3299 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
3300 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(36, -1)));
3304 testCase(1, [&,
this](Env& env, Data d) {
3305 testcase(
"Scale deposit exact, truncating from .01");
3307 auto const start = env.balance(d.depositor, d.assets).number();
3309 auto tx = d.vault.deposit(
3310 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(1201, -3))});
3313 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3314 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(12, -1)));
3318 auto tx = d.vault.deposit(
3319 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(69, -2))});
3322 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3323 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(18, -1)));
3327 testCase(1, [&,
this](Env& env, Data d) {
3328 testcase(
"Scale deposit exact, truncating from .99");
3330 auto const start = env.balance(d.depositor, d.assets).number();
3332 auto tx = d.vault.deposit(
3333 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(1299, -3))});
3336 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3337 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(12, -1)));
3341 auto tx = d.vault.deposit(
3342 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(62, -2))});
3345 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3346 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(18, -1)));
3350 testCase(1, [&,
this](Env& env, Data d) {
3352 auto const start = env.balance(d.depositor, d.assets).number();
3353 auto tx = d.vault.deposit(
3354 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(100, 0))});
3357 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3358 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(100, 0)));
3359 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(100, 0)));
3360 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-1000, 0)));
3368 auto const start = env.balance(d.depositor, d.assets).number();
3369 auto tx = d.vault.withdraw(
3370 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.share,
Number(100, 0))});
3373 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
3374 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(10, 0)));
3375 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(90, 0)));
3376 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-900, 0)));
3380 testcase(
"Scale redeem with rounding");
3385 auto const start = env.balance(d.depositor, d.assets).number();
3386 d.peek([](
SLE& vault,
auto&) ->
bool {
3387 vault[sfAssetsAvailable] =
Number(1);
3395 auto tx = d.vault.withdraw(
3396 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.share,
Number(25, 0))});
3399 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
3400 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(25, -1)));
3401 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(900 - 25, -1)));
3402 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(900 - 25, 0)));
3411 auto const start = env.balance(d.depositor, d.assets).number();
3413 tx = d.vault.withdraw(
3414 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.share,
Number(21, 0))});
3417 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 21));
3418 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(21, -1)));
3419 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(875 - 21, -1)));
3420 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(875 - 21, 0)));
3425 auto const rest = env.balance(d.depositor, d.shares).number();
3428 d.vault.withdraw({.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.share, rest)});
3431 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3432 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
3433 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
3437 testCase(18, [&,
this](Env& env, Data d) {
3438 testcase(
"Scale withdraw overflow");
3441 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3447 auto tx = d.vault.withdraw(
3448 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(10, 0))});
3454 testCase(1, [&,
this](Env& env, Data d) {
3456 auto const start = env.balance(d.depositor, d.assets).number();
3457 auto tx = d.vault.deposit(
3458 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(100, 0))});
3461 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3462 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(100, 0)));
3463 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(100, 0)));
3464 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-1000, 0)));
3475 auto const start = env.balance(d.depositor, d.assets).number();
3476 auto tx = d.vault.withdraw(
3477 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(10, 0))});
3480 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
3481 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(10, 0)));
3482 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(90, 0)));
3483 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-900, 0)));
3487 testcase(
"Scale withdraw insignificant amount");
3488 auto tx = d.vault.withdraw(
3489 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(4, -2))});
3494 testcase(
"Scale withdraw with rounding assets");
3502 auto const start = env.balance(d.depositor, d.assets).number();
3503 d.peek([](
SLE& vault,
auto&) ->
bool {
3504 vault[sfAssetsAvailable] =
Number(1);
3512 auto tx = d.vault.withdraw(
3513 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(25, -1))});
3516 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
3517 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(25, -1)));
3518 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(900 - 25, -1)));
3519 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(900 - 25, 0)));
3523 testcase(
"Scale withdraw with rounding shares up");
3531 auto const start = env.balance(d.depositor, d.assets).number();
3532 auto tx = d.vault.withdraw(
3533 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(375, -2))});
3536 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38));
3537 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(38, -1)));
3538 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(875 - 38, -1)));
3539 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(875 - 38, 0)));
3543 testcase(
"Scale withdraw with rounding shares down");
3551 auto const start = env.balance(d.depositor, d.assets).number();
3552 auto tx = d.vault.withdraw(
3553 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(372, -2))});
3556 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37));
3557 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(37, -1)));
3558 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(837 - 37, -1)));
3559 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(837 - 37, 0)));
3563 testcase(
"Scale withdraw tiny amount");
3565 auto const start = env.balance(d.depositor, d.assets).number();
3566 auto tx = d.vault.withdraw(
3567 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(9, -2))});
3570 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1));
3571 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(1, -1)));
3572 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(800 - 1, -1)));
3573 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(800 - 1, 0)));
3578 auto const rest = env.balance(d.vaultAccount, d.assets).number();
3581 d.vault.withdraw({.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset, rest)});
3584 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3585 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
3586 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
3590 testCase(18, [&,
this](Env& env, Data d) {
3591 testcase(
"Scale clawback overflow");
3594 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3600 auto tx = d.vault.clawback(
3601 {.issuer = d.issuer,
3603 .holder = d.depositor,
3610 testCase(1, [&,
this](Env& env, Data d) {
3612 auto const start = env.balance(d.depositor, d.assets).number();
3613 auto tx = d.vault.deposit(
3614 {.depositor = d.depositor, .id = d.keylet.key, .amount =
STAmount(d.asset,
Number(100, 0))});
3617 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3618 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(100, 0)));
3619 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(100, 0)));
3620 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(1000, 0)));
3630 auto const start = env.balance(d.depositor, d.assets).number();
3631 auto tx = d.vault.clawback(
3632 {.issuer = d.issuer,
3634 .holder = d.depositor,
3638 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
3639 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3640 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(90, 0)));
3641 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(900, 0)));
3645 testcase(
"Scale clawback insignificant amount");
3646 auto tx = d.vault.clawback(
3647 {.issuer = d.issuer,
3649 .holder = d.depositor,
3655 testcase(
"Scale clawback with rounding assets");
3663 auto const start = env.balance(d.depositor, d.assets).number();
3664 auto tx = d.vault.clawback(
3665 {.issuer = d.issuer,
3667 .holder = d.depositor,
3671 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
3672 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3673 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(900 - 25, -1)));
3674 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(900 - 25, 0)));
3678 testcase(
"Scale clawback with rounding shares up");
3686 auto const start = env.balance(d.depositor, d.assets).number();
3687 auto tx = d.vault.clawback(
3688 {.issuer = d.issuer,
3690 .holder = d.depositor,
3694 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38));
3695 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3696 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(875 - 38, -1)));
3697 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(875 - 38, 0)));
3701 testcase(
"Scale clawback with rounding shares down");
3709 auto const start = env.balance(d.depositor, d.assets).number();
3710 auto tx = d.vault.clawback(
3711 {.issuer = d.issuer,
3713 .holder = d.depositor,
3717 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37));
3718 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3719 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(837 - 37, -1)));
3720 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(837 - 37, 0)));
3724 testcase(
"Scale clawback tiny amount");
3726 auto const start = env.balance(d.depositor, d.assets).number();
3727 auto tx = d.vault.clawback(
3728 {.issuer = d.issuer,
3730 .holder = d.depositor,
3734 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1));
3735 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3736 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(800 - 1, -1)));
3737 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(800 - 1, 0)));
3742 auto const rest = env.balance(d.vaultAccount, d.assets).number();
3743 d.peek([](
SLE& vault,
auto&) ->
bool {
3744 vault[sfAssetsAvailable] =
Number(5);
3752 tx = d.vault.clawback(
3753 {.issuer = d.issuer, .id = d.keylet.key, .holder = d.depositor, .amount =
STAmount(d.asset, rest)});
3756 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3757 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
3758 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
3766 using namespace test::jtx;
3769 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3770 Account
const owner{
"owner"};
3771 Account
const issuer{
"issuer"};
3773 env.fund(XRP(1000), issuer, owner);
3777 env.trust(asset(1000), owner);
3778 env(pay(issuer, owner, asset(200)));
3781 auto const sequence = env.seq(owner);
3782 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3788 auto tx1 = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(50)});
3791 auto tx2 = vault.set({.owner = owner, .id = keylet.
key});
3792 tx2[sfAssetsMaximum] = asset(1000).number();
3797 auto const sleVault = [&env, keylet = keylet,
this]() {
3798 auto const vault = env.le(keylet);
3799 BEAST_EXPECT(vault !=
nullptr);
3803 auto const check = [&, keylet = keylet, sle = sleVault,
this](
3805 BEAST_EXPECT(vault.isObject());
3807 constexpr auto checkString = [](
auto& node,
SField const& field,
std::string v) ->
bool {
3808 return node.isMember(field.fieldName) && node[field.fieldName].isString() && node[field.fieldName] == v;
3810 constexpr auto checkObject = [](
auto& node,
SField const& field,
Json::Value v) ->
bool {
3811 return node.isMember(field.fieldName) && node[field.fieldName].isObject() && node[field.fieldName] == v;
3813 constexpr auto checkInt = [](
auto& node,
SField const& field,
int v) ->
bool {
3814 return node.isMember(field.fieldName) &&
3815 ((node[field.fieldName].isInt() && node[field.fieldName] ==
Json::Int(v)) ||
3816 (node[field.fieldName].isUInt() && node[field.fieldName] ==
Json::UInt(v)));
3819 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
3820 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(keylet.
key));
3821 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
3824 BEAST_EXPECT(checkString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
3825 BEAST_EXPECT(checkObject(vault, sfAsset,
to_json(sle->at(sfAsset))));
3826 BEAST_EXPECT(checkString(vault, sfAssetsAvailable,
"50"));
3827 BEAST_EXPECT(checkString(vault, sfAssetsMaximum,
"1000"));
3828 BEAST_EXPECT(checkString(vault, sfAssetsTotal,
"50"));
3829 BEAST_EXPECT(!vault.isMember(sfLossUnrealized.getJsonName()));
3831 auto const strShareID =
strHex(sle->at(sfShareMPTID));
3832 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
3833 BEAST_EXPECT(checkString(vault, sfOwner,
toBase58(owner.id())));
3834 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
3837 if (issuance.isObject())
3839 BEAST_EXPECT(issuance[
"LedgerEntryType"].asString() ==
"MPTokenIssuance");
3840 BEAST_EXPECT(issuance[jss::mpt_issuance_id].asString() == strShareID);
3841 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
3843 BEAST_EXPECT(checkString(issuance, sfOutstandingAmount,
"50000000"));
3848 testcase(
"RPC ledger_entry selected by key");
3850 jvParams[jss::ledger_index] = jss::validated;
3851 jvParams[jss::vault] =
strHex(keylet.
key);
3852 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
3854 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
3855 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
3856 check(jvVault[jss::result][jss::node]);
3860 testcase(
"RPC ledger_entry selected by owner and seq");
3862 jvParams[jss::ledger_index] = jss::validated;
3863 jvParams[jss::vault][jss::owner] = owner.human();
3864 jvParams[jss::vault][jss::seq] = sequence;
3865 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
3867 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
3868 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
3869 check(jvVault[jss::result][jss::node]);
3873 testcase(
"RPC ledger_entry cannot find vault by key");
3875 jvParams[jss::ledger_index] = jss::validated;
3877 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
3878 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
3882 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
3884 jvParams[jss::ledger_index] = jss::validated;
3885 jvParams[jss::vault][jss::owner] = issuer.human();
3886 jvParams[jss::vault][jss::seq] = 1'000'000;
3887 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
3888 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
3892 testcase(
"RPC ledger_entry malformed key");
3894 jvParams[jss::ledger_index] = jss::validated;
3895 jvParams[jss::vault] = 42;
3896 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
3897 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
3901 testcase(
"RPC ledger_entry malformed owner");
3903 jvParams[jss::ledger_index] = jss::validated;
3904 jvParams[jss::vault][jss::owner] = 42;
3905 jvParams[jss::vault][jss::seq] = sequence;
3906 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
3907 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedOwner");
3911 testcase(
"RPC ledger_entry malformed seq");
3913 jvParams[jss::ledger_index] = jss::validated;
3914 jvParams[jss::vault][jss::owner] = issuer.human();
3915 jvParams[jss::vault][jss::seq] =
"foo";
3916 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
3917 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
3921 testcase(
"RPC ledger_entry negative seq");
3923 jvParams[jss::ledger_index] = jss::validated;
3924 jvParams[jss::vault][jss::owner] = issuer.human();
3925 jvParams[jss::vault][jss::seq] = -1;
3926 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
3927 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
3931 testcase(
"RPC ledger_entry oversized seq");
3933 jvParams[jss::ledger_index] = jss::validated;
3934 jvParams[jss::vault][jss::owner] = issuer.human();
3935 jvParams[jss::vault][jss::seq] = 1e20;
3936 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
3937 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
3941 testcase(
"RPC ledger_entry bool seq");
3943 jvParams[jss::ledger_index] = jss::validated;
3944 jvParams[jss::vault][jss::owner] = issuer.human();
3945 jvParams[jss::vault][jss::seq] =
true;
3946 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
3947 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
3954 jvParams[jss::account] = owner.human();
3955 jvParams[jss::type] = jss::vault;
3956 auto jv = env.rpc(
"json",
"account_objects",
to_string(jvParams))[jss::result];
3958 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
3959 check(jv[jss::account_objects][0u]);
3966 jvParams[jss::ledger_index] = jss::validated;
3967 jvParams[jss::binary] =
false;
3968 jvParams[jss::type] = jss::vault;
3970 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
3971 check(jv[jss::result][jss::state][0u]);
3975 testcase(
"RPC vault_info command line");
3978 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
3979 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
3980 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
3986 jvParams[jss::ledger_index] = jss::validated;
3987 jvParams[jss::vault_id] =
strHex(keylet.
key);
3988 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
3990 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
3991 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
3992 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
3996 testcase(
"RPC vault_info invalid vault_id");
3998 jvParams[jss::ledger_index] = jss::validated;
3999 jvParams[jss::vault_id] =
"foobar";
4000 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4001 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4005 testcase(
"RPC vault_info json invalid index");
4007 jvParams[jss::ledger_index] = jss::validated;
4008 jvParams[jss::vault_id] = 0;
4009 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4010 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4014 testcase(
"RPC vault_info json by owner and sequence");
4016 jvParams[jss::ledger_index] = jss::validated;
4017 jvParams[jss::owner] = owner.human();
4018 jvParams[jss::seq] = sequence;
4019 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4021 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4022 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4023 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
4027 testcase(
"RPC vault_info json malformed sequence");
4029 jvParams[jss::ledger_index] = jss::validated;
4030 jvParams[jss::owner] = owner.human();
4031 jvParams[jss::seq] =
"foobar";
4032 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4033 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4037 testcase(
"RPC vault_info json invalid sequence");
4039 jvParams[jss::ledger_index] = jss::validated;
4040 jvParams[jss::owner] = owner.human();
4041 jvParams[jss::seq] = 0;
4042 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4043 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4047 testcase(
"RPC vault_info json negative sequence");
4049 jvParams[jss::ledger_index] = jss::validated;
4050 jvParams[jss::owner] = owner.human();
4051 jvParams[jss::seq] = -1;
4052 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4053 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4057 testcase(
"RPC vault_info json oversized sequence");
4059 jvParams[jss::ledger_index] = jss::validated;
4060 jvParams[jss::owner] = owner.human();
4061 jvParams[jss::seq] = 1e20;
4062 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4063 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4067 testcase(
"RPC vault_info json bool sequence");
4069 jvParams[jss::ledger_index] = jss::validated;
4070 jvParams[jss::owner] = owner.human();
4071 jvParams[jss::seq] =
true;
4072 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4073 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4077 testcase(
"RPC vault_info json malformed owner");
4079 jvParams[jss::ledger_index] = jss::validated;
4080 jvParams[jss::owner] =
"foobar";
4081 jvParams[jss::seq] = sequence;
4082 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4083 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4087 testcase(
"RPC vault_info json invalid combination only owner");
4089 jvParams[jss::ledger_index] = jss::validated;
4090 jvParams[jss::owner] = owner.human();
4091 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4092 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4096 testcase(
"RPC vault_info json invalid combination only seq");
4098 jvParams[jss::ledger_index] = jss::validated;
4099 jvParams[jss::seq] = sequence;
4100 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4101 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4105 testcase(
"RPC vault_info json invalid combination seq vault_id");
4107 jvParams[jss::ledger_index] = jss::validated;
4108 jvParams[jss::vault_id] =
strHex(keylet.
key);
4109 jvParams[jss::seq] = sequence;
4110 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4111 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4115 testcase(
"RPC vault_info json invalid combination owner vault_id");
4117 jvParams[jss::ledger_index] = jss::validated;
4118 jvParams[jss::vault_id] =
strHex(keylet.
key);
4119 jvParams[jss::owner] = owner.human();
4120 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4121 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4126 "RPC vault_info json invalid combination owner seq "
4129 jvParams[jss::ledger_index] = jss::validated;
4130 jvParams[jss::vault_id] =
strHex(keylet.
key);
4131 jvParams[jss::seq] = sequence;
4132 jvParams[jss::owner] = owner.human();
4133 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4134 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4138 testcase(
"RPC vault_info json no input");
4140 jvParams[jss::ledger_index] = jss::validated;
4141 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4142 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4146 testcase(
"RPC vault_info command line invalid index");
4147 Json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
4148 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
4152 testcase(
"RPC vault_info command line invalid index");
4153 Json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
4154 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4158 testcase(
"RPC vault_info command line invalid index");
4160 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"entryNotFound");
4164 testcase(
"RPC vault_info command line invalid ledger");
4166 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"lgrNotFound");
4173 using namespace test::jtx;
4175 Env env(*
this, testable_amendments());
4176 Account alice{
"alice"};
4178 Account carol{
"carol"};
4185 auto const xrpBalance = [
this](Env
const& env, Account
const& account) ->
std::optional<long> {
4187 if (BEAST_EXPECT(sle !=
nullptr))
4188 return sle->getFieldAmount(sfBalance).xrp().drops();
4192 auto testCase = [&,
this](
auto test, CaseArgs args = {}) {
4193 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4198 env.fund(XRP(10000), alice);
4199 env.fund(XRP(20000), bob);
4200 env.fund(XRP(30000), carol);
4214 test(env, vault, args.asset);
4217 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4218 testcase(
"delegated vault creation");
4219 auto startBalance = xrpBalance(env, carol);
4220 if (!BEAST_EXPECT(startBalance.has_value()))
4223 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4224 env(tx, delegate::as(alice));
4226 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
4229 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4230 testcase(
"delegated deposit and withdrawal");
4231 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4235 auto const amount = 1513;
4236 auto const baseFee = env.current()->fees().base;
4238 auto startBalance = xrpBalance(env, carol);
4239 if (!BEAST_EXPECT(startBalance.has_value()))
4242 tx = vault.deposit({.depositor = carol, .id = keylet.
key, .amount = asset(amount)});
4243 env(tx, delegate::as(alice));
4245 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
4247 tx = vault.withdraw({.depositor = carol, .id = keylet.
key, .amount = asset(amount - 1)});
4248 env(tx, delegate::as(alice));
4250 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
4252 tx = vault.withdraw({.depositor = carol, .id = keylet.
key, .amount = asset(1)});
4255 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
4258 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4259 testcase(
"delegated withdrawal same as base fee and deletion");
4260 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4264 auto const amount = 25537;
4265 auto const baseFee = env.current()->fees().base;
4267 auto startBalance = xrpBalance(env, carol);
4268 if (!BEAST_EXPECT(startBalance.has_value()))
4271 tx = vault.deposit({.depositor = carol, .id = keylet.
key, .amount = asset(amount)});
4274 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount - baseFee);
4276 tx = vault.withdraw({.depositor = carol, .id = keylet.
key, .amount = asset(baseFee)});
4277 env(tx, delegate::as(alice));
4279 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
4281 tx = vault.withdraw({.depositor = carol, .id = keylet.
key, .amount = asset(amount - baseFee)});
4282 env(tx, delegate::as(alice));
4284 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
4286 tx = vault.del({.owner = carol, .id = keylet.
key});
4287 env(tx, delegate::as(alice));
4295 using namespace test::jtx;
4296 using namespace loanBroker;
4297 using namespace loan;
4300 auto const vaultAssetBalance = [&](
Keylet const& vaultKeylet) {
4301 auto const sleVault = env.le(vaultKeylet);
4302 BEAST_EXPECT(sleVault !=
nullptr);
4304 return std::make_pair(sleVault->at(sfAssetsAvailable), sleVault->at(sfAssetsTotal));
4307 auto const vaultShareBalance = [&](
Keylet const& vaultKeylet) {
4308 auto const sleVault = env.le(vaultKeylet);
4309 BEAST_EXPECT(sleVault !=
nullptr);
4312 BEAST_EXPECT(sleIssuance !=
nullptr);
4314 return sleIssuance->at(sfOutstandingAmount);
4317 auto const setupVault =
4321 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
4325 auto const& vaultSle = env.le(vaultKeylet);
4326 BEAST_EXPECT(vaultSle !=
nullptr);
4328 Asset share = vaultSle->at(sfShareMPTID);
4330 env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
4335 auto const& [availablePreDefault, totalPreDefault] = vaultAssetBalance(vaultKeylet);
4336 BEAST_EXPECT(availablePreDefault == totalPreDefault);
4337 BEAST_EXPECT(availablePreDefault == asset(100).value());
4341 {.issuer = owner, .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}),
4346 auto const& sharesAvailable = vaultShareBalance(vaultKeylet);
4349 env(
set(owner, vaultKeylet.key), THISLINE);
4352 auto const& loanKeylet =
keylet::loan(brokerKeylet.key, 1);
4355 env(
set(depositor, brokerKeylet.key, asset(100).value()),
4358 paymentInterval(120),
4360 sig(sfCounterpartySignature, owner),
4361 fee(env.current()->fees().base * 2),
4369 {.issuer = owner, .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}),
4378 auto const& [availablePostDefault, totalPostDefault] = vaultAssetBalance(vaultKeylet);
4380 BEAST_EXPECT(availablePostDefault == totalPostDefault);
4381 BEAST_EXPECT(availablePostDefault == asset(0).value());
4382 BEAST_EXPECT(vaultShareBalance(vaultKeylet) == sharesAvailable);
4387 auto const testCase =
4388 [&](
PrettyAsset const& asset,
std::string const& prefix, Account
const& owner, Account
const& depositor) {
4390 testcase(
"VaultClawback (share) - " + prefix +
" owner asset clawback fails");
4391 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4392 env(vault.clawback({
4394 .id = vaultKeylet.key,
4395 .holder = depositor,
4396 .amount = asset(100).value(),
4409 testcase(
"VaultClawback (share) - " + prefix +
" owner incomplete share clawback fails");
4410 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4411 auto const& vaultSle = env.le(vaultKeylet);
4412 BEAST_EXPECT(vaultSle !=
nullptr);
4415 Asset share = vaultSle->at(sfShareMPTID);
4416 env(vault.clawback({
4418 .id = vaultKeylet.key,
4419 .holder = depositor,
4420 .amount = share(1).value(),
4428 testcase(
"VaultClawback (share) - " + prefix +
" owner implicit complete share clawback");
4429 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4430 env(vault.clawback({
4432 .id = vaultKeylet.key,
4433 .holder = depositor,
4442 testcase(
"VaultClawback (share) - " + prefix +
" owner explicit complete share clawback succeeds");
4443 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4444 auto const& vaultSle = env.le(vaultKeylet);
4445 BEAST_EXPECT(vaultSle !=
nullptr);
4448 Asset share = vaultSle->at(sfShareMPTID);
4449 env(vault.clawback({
4451 .id = vaultKeylet.key,
4452 .holder = depositor,
4453 .amount = share(vaultShareBalance(vaultKeylet)).value(),
4460 testcase(
"VaultClawback (share) - " + prefix +
" owner can clawback own shares");
4461 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
4462 auto const& vaultSle = env.le(vaultKeylet);
4463 BEAST_EXPECT(vaultSle !=
nullptr);
4466 Asset share = vaultSle->at(sfShareMPTID);
4467 env(vault.clawback({
4469 .id = vaultKeylet.key,
4471 .amount = share(vaultShareBalance(vaultKeylet)).value(),
4479 testcase(
"VaultClawback (share) - " + prefix +
" empty vault share clawback fails");
4480 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
4481 auto const& vaultSle = env.le(vaultKeylet);
4482 if (BEAST_EXPECT(vaultSle !=
nullptr))
4484 Asset share = vaultSle->at(sfShareMPTID);
4485 env(vault.clawback({
4487 .id = vaultKeylet.key,
4489 .amount = share(vaultShareBalance(vaultKeylet)).value(),
4495 env(vault.clawback({
4497 .id = vaultKeylet.key,
4506 Account owner{
"alice"};
4507 Account depositor{
"bob"};
4508 Account issuer{
"issuer"};
4510 env.fund(XRP(10000), issuer, owner, depositor);
4515 testCase(xrp,
"XRP", owner, depositor);
4516 testCase(xrp,
"XRP (depositor is owner)", owner, owner);
4523 env.trust(IOU(1000), owner);
4524 env.trust(IOU(1000), depositor);
4525 env(pay(issuer, owner, IOU(100)));
4526 env(pay(issuer, depositor, IOU(100)));
4528 testCase(IOU,
"IOU", owner, depositor);
4529 testCase(IOU,
"IOU (owner is issuer)", issuer, depositor);
4532 MPTTester mptt{env, issuer, mptInitNoFund};
4535 mptt.authorize({.account = owner});
4536 mptt.authorize({.account = depositor});
4537 env(pay(issuer, owner, MPT(1000)));
4538 env(pay(issuer, depositor, MPT(1000)));
4540 testCase(MPT,
"MPT", owner, depositor);
4541 testCase(MPT,
"MPT (owner is issuer)", issuer, depositor);
4547 using namespace test::jtx;
4548 using namespace loanBroker;
4549 using namespace loan;
4552 auto const setupVault = [&](
PrettyAsset const& asset,
4553 Account
const& owner,
4554 Account
const& depositor,
4558 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
4562 auto const& vaultSle = env.le(vaultKeylet);
4563 BEAST_EXPECT(vaultSle !=
nullptr);
4564 env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
4572 auto const testCase = [&](
PrettyAsset const& asset,
4574 Account
const& owner,
4575 Account
const& depositor,
4576 Account
const& issuer) {
4579 testcase(
"VaultClawback (asset) - " + prefix +
" issuer XRP clawback fails");
4580 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4583 env(vault.clawback({
4585 .id = vaultKeylet.key,
4587 .amount = asset(1).value(),
4592 env(vault.clawback({
4594 .id = vaultKeylet.key,
4603 testcase(
"VaultClawback (asset) - " + prefix +
" clawback for different asset fails");
4604 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4606 Account issuer2{
"issuer2"};
4608 env(vault.clawback({
4610 .id = vaultKeylet.key,
4611 .holder = depositor,
4612 .amount = asset2(1).value(),
4619 testcase(
"VaultClawback (asset) - " + prefix +
" ambiguous owner/issuer asset clawback fails");
4620 auto [vault, vaultKeylet] = setupVault(asset, issuer, depositor, issuer);
4621 env(vault.clawback({
4623 .id = vaultKeylet.key,
4631 testcase(
"VaultClawback (asset) - " + prefix +
" non-issuer asset clawback fails");
4632 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4634 env(vault.clawback({
4636 .id = vaultKeylet.key,
4637 .holder = depositor,
4642 env(vault.clawback({
4644 .id = vaultKeylet.key,
4645 .holder = depositor,
4646 .amount = asset(1).value(),
4653 testcase(
"VaultClawback (asset) - " + prefix +
" issuer clawback from self fails");
4654 auto [vault, vaultKeylet] = setupVault(asset, owner, issuer, issuer);
4655 env(vault.clawback({
4657 .id = vaultKeylet.key,
4665 testcase(
"VaultClawback (asset) - " + prefix +
" issuer share clawback fails");
4666 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4667 auto const& vaultSle = env.le(vaultKeylet);
4668 BEAST_EXPECT(vaultSle !=
nullptr);
4671 Asset share = vaultSle->at(sfShareMPTID);
4673 env(vault.clawback({
4675 .id = vaultKeylet.key,
4676 .holder = depositor,
4677 .amount = share(1).value(),
4684 testcase(
"VaultClawback (asset) - " + prefix +
" partial issuer asset clawback succeeds");
4685 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4687 env(vault.clawback({
4689 .id = vaultKeylet.key,
4690 .holder = depositor,
4691 .amount = asset(1).value(),
4698 testcase(
"VaultClawback (asset) - " + prefix +
" full issuer asset clawback succeeds");
4699 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4701 env(vault.clawback({
4703 .id = vaultKeylet.key,
4704 .holder = depositor,
4705 .amount = asset(100).value(),
4712 testcase(
"VaultClawback (asset) - " + prefix +
" implicit full issuer asset clawback succeeds");
4713 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4715 env(vault.clawback({
4717 .id = vaultKeylet.key,
4718 .holder = depositor,
4725 Account owner{
"alice"};
4726 Account depositor{
"bob"};
4727 Account issuer{
"issuer"};
4729 env.fund(XRP(10000), issuer, owner, depositor);
4734 testCase(xrp,
"XRP", owner, depositor, issuer);
4740 env.trust(IOU(1000), owner);
4741 env.trust(IOU(1000), depositor);
4742 env(pay(issuer, owner, IOU(1000)));
4743 env(pay(issuer, depositor, IOU(1000)));
4745 testCase(IOU,
"IOU", owner, depositor, issuer);
4748 MPTTester mptt{env, issuer, mptInitNoFund};
4751 mptt.authorize({.account = owner});
4752 mptt.authorize({.account = depositor});
4753 env(pay(issuer, depositor, MPT(1000)));
4755 testCase(MPT,
"MPT", owner, depositor, issuer);
4763 using namespace test::jtx;
4765 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4766 Account
const owner{
"owner"};
4767 Account
const issuer{
"issuer"};
4770 env.fund(XRP(1'000'000), issuer, owner);
4774 BEAST_EXPECT(maxInt64 ==
"9223372036854775807");
4777 auto const maxInt64Plus1 =
4779 BEAST_EXPECT(maxInt64Plus1 ==
"9223372036854775808");
4782 BEAST_EXPECT(initialXRP ==
"100000000000000000");
4785 BEAST_EXPECT(initialXRPPlus1 ==
"100000000000000001");
4792 auto [tx, keylet] = vault.create({.owner = owner, .asset = xrpAsset});
4793 tx[sfData] =
"4D65746144617461";
4795 tx[sfAssetsMaximum] = maxInt64;
4799 tx[sfAssetsMaximum] = initialXRPPlus1;
4803 tx[sfAssetsMaximum] = initialXRP;
4807 tx[sfAssetsMaximum] = maxInt64Plus1;
4812 auto const insertAt = maxInt64Plus1.size() - 3;
4813 auto const decimalTest =
4814 maxInt64Plus1.substr(0, insertAt) +
"." + maxInt64Plus1.substr(insertAt);
4815 BEAST_EXPECT(decimalTest ==
"9223372036854775.808");
4816 tx[sfAssetsMaximum] = decimalTest;
4817 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
4821 auto const vaultSle = env.le(newKeylet);
4822 if (!BEAST_EXPECT(vaultSle))
4825 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 9223372036854776);
4832 MPTTester mptt{env, issuer, mptInitNoFund};
4836 mptt.authorize({.account = owner});
4841 env(pay(issuer, owner, mptAsset(100'000)), THISLINE);
4844 auto [tx, keylet] = vault.create({.owner = owner, .asset = mptAsset});
4845 tx[sfData] =
"4D65746144617461";
4847 tx[sfAssetsMaximum] = maxInt64;
4851 tx[sfAssetsMaximum] = initialXRPPlus1;
4855 tx[sfAssetsMaximum] = initialXRP;
4859 tx[sfAssetsMaximum] = maxInt64Plus1;
4864 auto const insertAt = maxInt64Plus1.size() - 1;
4865 auto const decimalTest =
4866 maxInt64Plus1.substr(0, insertAt) +
"." + maxInt64Plus1.substr(insertAt);
4867 BEAST_EXPECT(decimalTest ==
"922337203685477580.8");
4868 tx[sfAssetsMaximum] = decimalTest;
4869 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
4873 auto const vaultSle = env.le(newKeylet);
4874 if (!BEAST_EXPECT(vaultSle))
4877 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 922337203685477581);
4885 env.trust(iouAsset(1000), owner);
4886 env(pay(issuer, owner, iouAsset(200)));
4889 auto [tx, keylet] = vault.create({.owner = owner, .asset = iouAsset});
4890 tx[sfData] =
"4D65746144617461";
4892 tx[sfAssetsMaximum] = maxInt64;
4896 tx[sfAssetsMaximum] = initialXRPPlus1;
4900 tx[sfAssetsMaximum] = initialXRP;
4904 tx[sfAssetsMaximum] = maxInt64Plus1;
4908 tx[sfAssetsMaximum] =
"1000000000000000e80";
4911 tx[sfAssetsMaximum] =
"1000000000000000e-96";
4916 auto const insertAt = maxInt64Plus1.size() - 1;
4917 auto const decimalTest =
4918 maxInt64Plus1.substr(0, insertAt) +
"." + maxInt64Plus1.substr(insertAt);
4919 BEAST_EXPECT(decimalTest ==
"922337203685477580.8");
4920 tx[sfAssetsMaximum] = decimalTest;
4921 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
4925 auto const vaultSle = env.le(newKeylet);
4926 if (!BEAST_EXPECT(vaultSle))
4929 BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) ==
Number{9223372036854776, 2, Number::normalized{}}));
4932 tx[sfAssetsMaximum] =
"9223372036854775807e40";
4933 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
4937 auto const vaultSle = env.le(newKeylet);
4938 if (!BEAST_EXPECT(vaultSle))
4941 BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) ==
Number{9223372036854776, 43, Number::normalized{}}));
4944 tx[sfAssetsMaximum] =
"9223372036854775807e-40";
4945 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
4949 auto const vaultSle = env.le(newKeylet);
4950 if (!BEAST_EXPECT(vaultSle))
4953 BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) ==
Number{9223372036854776, -37, Number::normalized{}}));
4956 tx[sfAssetsMaximum] =
"9223372036854775807e-100";
4957 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
4962 auto const vaultSle = env.le(newKeylet);
4963 if (!BEAST_EXPECT(vaultSle))
4966 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) ==
numZero);
4971 tx[sfAssetsMaximum] =
"1000000000000000e81";
4978 tx[sfAssetsMaximum] =
"18446744073709551617e5";
4980 BEAST_EXPECT(
false);
4982 catch (parse_error
const& e)
4984 using namespace std::string_literals;
4987 "invalidParamsField 'tx_json.AssetsMaximum' has invalid "