From 35dde1521b8f26bc5816ca5ff517c210b440d387 Mon Sep 17 00:00:00 2001 From: bthomee Date: Tue, 11 Nov 2025 11:42:09 -0800 Subject: [PATCH] deploy: 2ebc2ca885ffa90fa6c7e8cf421dc0a12a34c3a8 --- Vault__test_8cpp_source.html | 5055 ++++++++++++++++--------------- View_8cpp_source.html | 8 +- classripple_1_1Vault__test.html | 14 +- 3 files changed, 2579 insertions(+), 2498 deletions(-) diff --git a/Vault__test_8cpp_source.html b/Vault__test_8cpp_source.html index d7e57fd134..9660610ca2 100644 --- a/Vault__test_8cpp_source.html +++ b/Vault__test_8cpp_source.html @@ -2555,2561 +2555,2642 @@ $(document).ready(function() { init_codefold(0); });
2454 struct CaseArgs
2455 {
2456 int initialXRP = 1000;
-
2457 double transferRate = 1.0;
-
2458 };
-
2459
-
2460 auto testCase =
-
2461 [&, this](
-
2462 std::function<void(
-
2463 Env & env,
-
2464 Account const& owner,
-
2465 Account const& issuer,
-
2466 Account const& charlie,
-
2467 std::function<Account(ripple::Keylet)> vaultAccount,
-
2468 Vault& vault,
-
2469 PrettyAsset const& asset,
-
2470 std::function<MPTID(ripple::Keylet)> issuanceId)> test,
-
2471 CaseArgs args = {}) {
-
2472 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
2473 Account const owner{"owner"};
-
2474 Account const issuer{"issuer"};
-
2475 Account const charlie{"charlie"};
-
2476 Vault vault{env};
-
2477 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
-
2478 env(fset(issuer, asfAllowTrustLineClawback));
-
2479 env.close();
-
2480
-
2481 PrettyAsset const asset = issuer["IOU"];
-
2482 env.trust(asset(1000), owner);
-
2483 env.trust(asset(1000), charlie);
-
2484 env(pay(issuer, owner, asset(200)));
-
2485 env(rate(issuer, args.transferRate));
-
2486 env.close();
-
2487
-
2488 auto const vaultAccount =
-
2489 [&env](ripple::Keylet keylet) -> Account {
-
2490 return Account("vault", env.le(keylet)->at(sfAccount));
-
2491 };
-
2492 auto const issuanceId = [&env](ripple::Keylet keylet) -> MPTID {
-
2493 return env.le(keylet)->at(sfShareMPTID);
-
2494 };
-
2495
-
2496 test(
-
2497 env,
-
2498 owner,
-
2499 issuer,
-
2500 charlie,
-
2501 vaultAccount,
-
2502 vault,
-
2503 asset,
-
2504 issuanceId);
-
2505 };
-
2506
-
2507 testCase([&, this](
-
2508 Env& env,
-
2509 Account const& owner,
-
2510 Account const& issuer,
-
2511 Account const&,
-
2512 auto vaultAccount,
-
2513 Vault& vault,
-
2514 PrettyAsset const& asset,
-
2515 auto&&...) {
-
2516 testcase("IOU cannot use different asset");
-
2517 PrettyAsset const foo = issuer["FOO"];
-
2518
-
2519 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2520 env(tx);
-
2521 env.close();
-
2522
-
2523 {
-
2524 // Cannot create new trustline to a vault
-
2525 auto tx = [&, account = vaultAccount(keylet)]() {
-
2526 Json::Value jv;
-
2527 jv[jss::Account] = issuer.human();
-
2528 {
-
2529 auto& ja = jv[jss::LimitAmount] =
-
2530 foo(0).value().getJson(JsonOptions::none);
-
2531 ja[jss::issuer] = toBase58(account);
-
2532 }
-
2533 jv[jss::TransactionType] = jss::TrustSet;
-
2534 jv[jss::Flags] = tfSetFreeze;
-
2535 return jv;
-
2536 }();
-
2537 env(tx, ter{tecNO_PERMISSION});
-
2538 env.close();
-
2539 }
-
2540
-
2541 {
-
2542 auto tx = vault.deposit(
-
2543 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
-
2544 env(tx, ter{tecWRONG_ASSET});
-
2545 env.close();
-
2546 }
-
2547
-
2548 {
-
2549 auto tx = vault.withdraw(
-
2550 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
-
2551 env(tx, ter{tecWRONG_ASSET});
-
2552 env.close();
-
2553 }
-
2554
-
2555 env(vault.del({.owner = owner, .id = keylet.key}));
-
2556 env.close();
-
2557 });
-
2558
-
2559 testCase([&, this](
-
2560 Env& env,
-
2561 Account const& owner,
-
2562 Account const& issuer,
-
2563 Account const& charlie,
-
2564 auto vaultAccount,
-
2565 Vault& vault,
-
2566 PrettyAsset const& asset,
-
2567 auto issuanceId) {
-
2568 testcase("IOU frozen trust line to vault account");
-
2569
-
2570 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2571 env(tx);
-
2572 env.close();
-
2573
-
2574 env(vault.deposit(
-
2575 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2576 env.close();
-
2577
-
2578 Asset const share = Asset(issuanceId(keylet));
-
2579
-
2580 // Freeze the trustline to the vault
-
2581 auto trustSet = [&, account = vaultAccount(keylet)]() {
-
2582 Json::Value jv;
-
2583 jv[jss::Account] = issuer.human();
-
2584 {
-
2585 auto& ja = jv[jss::LimitAmount] =
-
2586 asset(0).value().getJson(JsonOptions::none);
-
2587 ja[jss::issuer] = toBase58(account);
-
2588 }
-
2589 jv[jss::TransactionType] = jss::TrustSet;
-
2590 jv[jss::Flags] = tfSetFreeze;
-
2591 return jv;
-
2592 }();
-
2593 env(trustSet);
-
2594 env.close();
-
2595
-
2596 {
-
2597 // Note, the "frozen" state of the trust line to vault account
-
2598 // is reported as "locked" state of the vault shares, because
-
2599 // this state is attached to shares by means of the transitive
-
2600 // isFrozen.
-
2601 auto tx = vault.deposit(
-
2602 {.depositor = owner,
-
2603 .id = keylet.key,
-
2604 .amount = asset(80)});
-
2605 env(tx, ter{tecLOCKED});
-
2606 }
-
2607
-
2608 {
-
2609 auto tx = vault.withdraw(
-
2610 {.depositor = owner,
-
2611 .id = keylet.key,
-
2612 .amount = asset(100)});
-
2613 env(tx, ter{tecLOCKED});
-
2614
-
2615 // also when trying to withdraw to a 3rd party
-
2616 tx[sfDestination] = charlie.human();
-
2617 env(tx, ter{tecLOCKED});
-
2618 env.close();
-
2619 }
-
2620
-
2621 {
-
2622 // Clawback works, even when locked
-
2623 auto tx = vault.clawback(
-
2624 {.issuer = issuer,
-
2625 .id = keylet.key,
-
2626 .holder = owner,
-
2627 .amount = asset(50)});
-
2628 env(tx);
-
2629 env.close();
-
2630 }
-
2631
-
2632 // Clear the frozen state
-
2633 trustSet[jss::Flags] = tfClearFreeze;
-
2634 env(trustSet);
-
2635 env.close();
-
2636
-
2637 env(vault.withdraw(
-
2638 {.depositor = owner,
-
2639 .id = keylet.key,
-
2640 .amount = share(50'000'000)}));
-
2641
-
2642 env(vault.del({.owner = owner, .id = keylet.key}));
-
2643 env.close();
-
2644 });
-
2645
-
2646 testCase(
-
2647 [&, this](
-
2648 Env& env,
-
2649 Account const& owner,
-
2650 Account const& issuer,
-
2651 Account const& charlie,
-
2652 auto vaultAccount,
-
2653 Vault& vault,
-
2654 PrettyAsset const& asset,
-
2655 auto issuanceId) {
-
2656 testcase("IOU transfer fees not applied");
-
2657
-
2658 auto [tx, keylet] =
-
2659 vault.create({.owner = owner, .asset = asset});
-
2660 env(tx);
-
2661 env.close();
-
2662
-
2663 env(vault.deposit(
-
2664 {.depositor = owner,
-
2665 .id = keylet.key,
-
2666 .amount = asset(100)}));
-
2667 env.close();
-
2668
-
2669 auto const issue = asset.raw().get<Issue>();
-
2670 Asset const share = Asset(issuanceId(keylet));
-
2671
-
2672 // transfer fees ignored on deposit
-
2673 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
-
2674 BEAST_EXPECT(
-
2675 env.balance(vaultAccount(keylet), issue) == asset(100));
-
2676
-
2677 {
-
2678 auto tx = vault.clawback(
-
2679 {.issuer = issuer,
-
2680 .id = keylet.key,
-
2681 .holder = owner,
-
2682 .amount = asset(50)});
-
2683 env(tx);
-
2684 env.close();
-
2685 }
-
2686
-
2687 // transfer fees ignored on clawback
-
2688 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
-
2689 BEAST_EXPECT(
-
2690 env.balance(vaultAccount(keylet), issue) == asset(50));
-
2691
-
2692 env(vault.withdraw(
-
2693 {.depositor = owner,
-
2694 .id = keylet.key,
-
2695 .amount = share(20'000'000)}));
-
2696
-
2697 // transfer fees ignored on withdraw
-
2698 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
-
2699 BEAST_EXPECT(
-
2700 env.balance(vaultAccount(keylet), issue) == asset(30));
-
2701
-
2702 {
-
2703 auto tx = vault.withdraw(
-
2704 {.depositor = owner,
-
2705 .id = keylet.key,
-
2706 .amount = share(30'000'000)});
-
2707 tx[sfDestination] = charlie.human();
-
2708 env(tx);
-
2709 }
-
2710
-
2711 // transfer fees ignored on withdraw to 3rd party
-
2712 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
-
2713 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
-
2714 BEAST_EXPECT(
-
2715 env.balance(vaultAccount(keylet), issue) == asset(0));
-
2716
-
2717 env(vault.del({.owner = owner, .id = keylet.key}));
-
2718 env.close();
-
2719 },
-
2720 CaseArgs{.transferRate = 1.25});
-
2721
-
2722 testCase([&, this](
-
2723 Env& env,
-
2724 Account const& owner,
-
2725 Account const& issuer,
-
2726 Account const& charlie,
-
2727 auto,
-
2728 Vault& vault,
-
2729 PrettyAsset const& asset,
-
2730 auto&&...) {
-
2731 testcase("IOU frozen trust line to depositor");
-
2732
-
2733 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2734 env(tx);
-
2735 env.close();
-
2736
-
2737 env(vault.deposit(
-
2738 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2739 env.close();
-
2740
-
2741 // Withdraw to 3rd party works
-
2742 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
-
2743 auto tx = vault.withdraw(
-
2744 {.depositor = owner,
-
2745 .id = keylet.key,
-
2746 .amount = asset(10)});
-
2747 tx[sfDestination] = charlie.human();
-
2748 return tx;
-
2749 }(keylet);
-
2750 env(withdrawToCharlie);
-
2751
-
2752 // Freeze the owner
-
2753 env(trust(issuer, asset(0), owner, tfSetFreeze));
-
2754 env.close();
-
2755
-
2756 // Cannot withdraw
-
2757 auto const withdraw = vault.withdraw(
-
2758 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2759 env(withdraw, ter{tecFROZEN});
-
2760
-
2761 // Cannot withdraw to 3rd party
-
2762 env(withdrawToCharlie, ter{tecLOCKED});
-
2763 env.close();
-
2764
-
2765 {
-
2766 // Cannot deposit some more
-
2767 auto tx = vault.deposit(
-
2768 {.depositor = owner,
-
2769 .id = keylet.key,
-
2770 .amount = asset(10)});
-
2771 env(tx, ter{tecFROZEN});
-
2772 }
-
2773
-
2774 {
-
2775 // Clawback still works
-
2776 auto tx = vault.clawback(
-
2777 {.issuer = issuer,
-
2778 .id = keylet.key,
-
2779 .holder = owner,
-
2780 .amount = asset(0)});
-
2781 env(tx);
-
2782 env.close();
-
2783 }
-
2784
-
2785 env(vault.del({.owner = owner, .id = keylet.key}));
-
2786 env.close();
-
2787 });
-
2788
-
2789 testCase([&, this](
-
2790 Env& env,
-
2791 Account const& owner,
-
2792 Account const& issuer,
-
2793 Account const& charlie,
-
2794 auto,
-
2795 Vault& vault,
-
2796 PrettyAsset const& asset,
-
2797 auto&&...) {
-
2798 testcase("IOU no trust line to 3rd party");
-
2799
-
2800 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2801 env(tx);
-
2802 env.close();
-
2803
-
2804 env(vault.deposit(
-
2805 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2806 env.close();
-
2807
-
2808 Account const erin{"erin"};
-
2809 env.fund(XRP(1000), erin);
-
2810 env.close();
-
2811
-
2812 // Withdraw to 3rd party without trust line
-
2813 auto const tx1 = [&](ripple::Keylet keylet) {
-
2814 auto tx = vault.withdraw(
-
2815 {.depositor = owner,
-
2816 .id = keylet.key,
-
2817 .amount = asset(10)});
-
2818 tx[sfDestination] = erin.human();
-
2819 return tx;
-
2820 }(keylet);
-
2821 env(tx1, ter{tecNO_LINE});
-
2822 });
-
2823
-
2824 testCase([&, this](
-
2825 Env& env,
-
2826 Account const& owner,
-
2827 Account const& issuer,
-
2828 Account const& charlie,
-
2829 auto,
-
2830 Vault& vault,
-
2831 PrettyAsset const& asset,
-
2832 auto&&...) {
-
2833 testcase("IOU no trust line to depositor");
-
2834
-
2835 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2836 env(tx);
-
2837 env.close();
-
2838
-
2839 // reset limit, so deposit of all funds will delete the trust line
-
2840 env.trust(asset(0), owner);
-
2841 env.close();
-
2842
-
2843 env(vault.deposit(
-
2844 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
-
2845 env.close();
-
2846
-
2847 auto trustline =
-
2848 env.le(keylet::line(owner, asset.raw().get<Issue>()));
-
2849 BEAST_EXPECT(trustline == nullptr);
-
2850
-
2851 // Withdraw without trust line, will succeed
-
2852 auto const tx1 = [&](ripple::Keylet keylet) {
-
2853 auto tx = vault.withdraw(
-
2854 {.depositor = owner,
-
2855 .id = keylet.key,
-
2856 .amount = asset(10)});
-
2857 return tx;
-
2858 }(keylet);
-
2859 env(tx1);
-
2860 });
-
2861
-
2862 auto const [acctReserve, incReserve] = [this]() -> std::pair<int, int> {
-
2863 Env env{*this, testable_amendments()};
-
2864 return {
-
2865 env.current()->fees().accountReserve(0).drops() /
-
2866 DROPS_PER_XRP.drops(),
-
2867 env.current()->fees().increment.drops() /
-
2868 DROPS_PER_XRP.drops()};
-
2869 }();
-
2870
-
2871 testCase(
-
2872 [&, this](
-
2873 Env& env,
-
2874 Account const& owner,
-
2875 Account const& issuer,
-
2876 Account const& charlie,
-
2877 auto,
-
2878 Vault& vault,
-
2879 PrettyAsset const& asset,
-
2880 auto&&...) {
-
2881 testcase("IOU no trust line to depositor no reserve");
-
2882 auto [tx, keylet] =
-
2883 vault.create({.owner = owner, .asset = asset});
-
2884 env(tx);
-
2885 env.close();
-
2886
-
2887 // reset limit, so deposit of all funds will delete the trust
-
2888 // line
-
2889 env.trust(asset(0), owner);
-
2890 env.close();
-
2891
-
2892 env(vault.deposit(
-
2893 {.depositor = owner,
-
2894 .id = keylet.key,
-
2895 .amount = asset(200)}));
-
2896 env.close();
-
2897
-
2898 auto trustline =
-
2899 env.le(keylet::line(owner, asset.raw().get<Issue>()));
-
2900 BEAST_EXPECT(trustline == nullptr);
-
2901
-
2902 // Fail because not enough reserve to create trust line
-
2903 tx = vault.withdraw(
-
2904 {.depositor = owner,
-
2905 .id = keylet.key,
-
2906 .amount = asset(10)});
-
2907 env(tx, ter{tecNO_LINE_INSUF_RESERVE});
-
2908 env.close();
-
2909
-
2910 env(pay(charlie, owner, XRP(incReserve)));
-
2911 env.close();
+
2457 Number initialIOU = 200;
+
2458 double transferRate = 1.0;
+
2459 };
+
2460
+
2461 auto testCase =
+
2462 [&, this](
+
2463 std::function<void(
+
2464 Env & env,
+
2465 Account const& owner,
+
2466 Account const& issuer,
+
2467 Account const& charlie,
+
2468 std::function<Account(ripple::Keylet)> vaultAccount,
+
2469 Vault& vault,
+
2470 PrettyAsset const& asset,
+
2471 std::function<MPTID(ripple::Keylet)> issuanceId)> test,
+
2472 CaseArgs args = {}) {
+
2473 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
2474 Account const owner{"owner"};
+
2475 Account const issuer{"issuer"};
+
2476 Account const charlie{"charlie"};
+
2477 Vault vault{env};
+
2478 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
+
2479 env(fset(issuer, asfAllowTrustLineClawback));
+
2480 env.close();
+
2481
+
2482 PrettyAsset const asset = issuer["IOU"];
+
2483 env.trust(asset(1000), owner);
+
2484 env.trust(asset(1000), charlie);
+
2485 env(pay(issuer, owner, asset(args.initialIOU)));
+
2486 env(rate(issuer, args.transferRate));
+
2487 env.close();
+
2488
+
2489 auto const vaultAccount =
+
2490 [&env](ripple::Keylet keylet) -> Account {
+
2491 return Account("vault", env.le(keylet)->at(sfAccount));
+
2492 };
+
2493 auto const issuanceId = [&env](ripple::Keylet keylet) -> MPTID {
+
2494 return env.le(keylet)->at(sfShareMPTID);
+
2495 };
+
2496
+
2497 test(
+
2498 env,
+
2499 owner,
+
2500 issuer,
+
2501 charlie,
+
2502 vaultAccount,
+
2503 vault,
+
2504 asset,
+
2505 issuanceId);
+
2506 };
+
2507
+
2508 testCase([&, this](
+
2509 Env& env,
+
2510 Account const& owner,
+
2511 Account const& issuer,
+
2512 Account const&,
+
2513 auto vaultAccount,
+
2514 Vault& vault,
+
2515 PrettyAsset const& asset,
+
2516 auto&&...) {
+
2517 testcase("IOU cannot use different asset");
+
2518 PrettyAsset const foo = issuer["FOO"];
+
2519
+
2520 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2521 env(tx);
+
2522 env.close();
+
2523
+
2524 {
+
2525 // Cannot create new trustline to a vault
+
2526 auto tx = [&, account = vaultAccount(keylet)]() {
+
2527 Json::Value jv;
+
2528 jv[jss::Account] = issuer.human();
+
2529 {
+
2530 auto& ja = jv[jss::LimitAmount] =
+
2531 foo(0).value().getJson(JsonOptions::none);
+
2532 ja[jss::issuer] = toBase58(account);
+
2533 }
+
2534 jv[jss::TransactionType] = jss::TrustSet;
+
2535 jv[jss::Flags] = tfSetFreeze;
+
2536 return jv;
+
2537 }();
+
2538 env(tx, ter{tecNO_PERMISSION});
+
2539 env.close();
+
2540 }
+
2541
+
2542 {
+
2543 auto tx = vault.deposit(
+
2544 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
+
2545 env(tx, ter{tecWRONG_ASSET});
+
2546 env.close();
+
2547 }
+
2548
+
2549 {
+
2550 auto tx = vault.withdraw(
+
2551 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
+
2552 env(tx, ter{tecWRONG_ASSET});
+
2553 env.close();
+
2554 }
+
2555
+
2556 env(vault.del({.owner = owner, .id = keylet.key}));
+
2557 env.close();
+
2558 });
+
2559
+
2560 testCase([&, this](
+
2561 Env& env,
+
2562 Account const& owner,
+
2563 Account const& issuer,
+
2564 Account const& charlie,
+
2565 auto vaultAccount,
+
2566 Vault& vault,
+
2567 PrettyAsset const& asset,
+
2568 auto issuanceId) {
+
2569 testcase("IOU frozen trust line to vault account");
+
2570
+
2571 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2572 env(tx);
+
2573 env.close();
+
2574
+
2575 env(vault.deposit(
+
2576 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2577 env.close();
+
2578
+
2579 Asset const share = Asset(issuanceId(keylet));
+
2580
+
2581 // Freeze the trustline to the vault
+
2582 auto trustSet = [&, account = vaultAccount(keylet)]() {
+
2583 Json::Value jv;
+
2584 jv[jss::Account] = issuer.human();
+
2585 {
+
2586 auto& ja = jv[jss::LimitAmount] =
+
2587 asset(0).value().getJson(JsonOptions::none);
+
2588 ja[jss::issuer] = toBase58(account);
+
2589 }
+
2590 jv[jss::TransactionType] = jss::TrustSet;
+
2591 jv[jss::Flags] = tfSetFreeze;
+
2592 return jv;
+
2593 }();
+
2594 env(trustSet);
+
2595 env.close();
+
2596
+
2597 {
+
2598 // Note, the "frozen" state of the trust line to vault account
+
2599 // is reported as "locked" state of the vault shares, because
+
2600 // this state is attached to shares by means of the transitive
+
2601 // isFrozen.
+
2602 auto tx = vault.deposit(
+
2603 {.depositor = owner,
+
2604 .id = keylet.key,
+
2605 .amount = asset(80)});
+
2606 env(tx, ter{tecLOCKED});
+
2607 }
+
2608
+
2609 {
+
2610 auto tx = vault.withdraw(
+
2611 {.depositor = owner,
+
2612 .id = keylet.key,
+
2613 .amount = asset(100)});
+
2614 env(tx, ter{tecLOCKED});
+
2615
+
2616 // also when trying to withdraw to a 3rd party
+
2617 tx[sfDestination] = charlie.human();
+
2618 env(tx, ter{tecLOCKED});
+
2619 env.close();
+
2620 }
+
2621
+
2622 {
+
2623 // Clawback works, even when locked
+
2624 auto tx = vault.clawback(
+
2625 {.issuer = issuer,
+
2626 .id = keylet.key,
+
2627 .holder = owner,
+
2628 .amount = asset(50)});
+
2629 env(tx);
+
2630 env.close();
+
2631 }
+
2632
+
2633 // Clear the frozen state
+
2634 trustSet[jss::Flags] = tfClearFreeze;
+
2635 env(trustSet);
+
2636 env.close();
+
2637
+
2638 env(vault.withdraw(
+
2639 {.depositor = owner,
+
2640 .id = keylet.key,
+
2641 .amount = share(50'000'000)}));
+
2642
+
2643 env(vault.del({.owner = owner, .id = keylet.key}));
+
2644 env.close();
+
2645 });
+
2646
+
2647 testCase(
+
2648 [&, this](
+
2649 Env& env,
+
2650 Account const& owner,
+
2651 Account const& issuer,
+
2652 Account const& charlie,
+
2653 auto vaultAccount,
+
2654 Vault& vault,
+
2655 PrettyAsset const& asset,
+
2656 auto issuanceId) {
+
2657 testcase("IOU transfer fees not applied");
+
2658
+
2659 auto [tx, keylet] =
+
2660 vault.create({.owner = owner, .asset = asset});
+
2661 env(tx);
+
2662 env.close();
+
2663
+
2664 env(vault.deposit(
+
2665 {.depositor = owner,
+
2666 .id = keylet.key,
+
2667 .amount = asset(100)}));
+
2668 env.close();
+
2669
+
2670 auto const issue = asset.raw().get<Issue>();
+
2671 Asset const share = Asset(issuanceId(keylet));
+
2672
+
2673 // transfer fees ignored on deposit
+
2674 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
+
2675 BEAST_EXPECT(
+
2676 env.balance(vaultAccount(keylet), issue) == asset(100));
+
2677
+
2678 {
+
2679 auto tx = vault.clawback(
+
2680 {.issuer = issuer,
+
2681 .id = keylet.key,
+
2682 .holder = owner,
+
2683 .amount = asset(50)});
+
2684 env(tx);
+
2685 env.close();
+
2686 }
+
2687
+
2688 // transfer fees ignored on clawback
+
2689 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
+
2690 BEAST_EXPECT(
+
2691 env.balance(vaultAccount(keylet), issue) == asset(50));
+
2692
+
2693 env(vault.withdraw(
+
2694 {.depositor = owner,
+
2695 .id = keylet.key,
+
2696 .amount = share(20'000'000)}));
+
2697
+
2698 // transfer fees ignored on withdraw
+
2699 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
+
2700 BEAST_EXPECT(
+
2701 env.balance(vaultAccount(keylet), issue) == asset(30));
+
2702
+
2703 {
+
2704 auto tx = vault.withdraw(
+
2705 {.depositor = owner,
+
2706 .id = keylet.key,
+
2707 .amount = share(30'000'000)});
+
2708 tx[sfDestination] = charlie.human();
+
2709 env(tx);
+
2710 }
+
2711
+
2712 // transfer fees ignored on withdraw to 3rd party
+
2713 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
+
2714 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
+
2715 BEAST_EXPECT(
+
2716 env.balance(vaultAccount(keylet), issue) == asset(0));
+
2717
+
2718 env(vault.del({.owner = owner, .id = keylet.key}));
+
2719 env.close();
+
2720 },
+
2721 CaseArgs{.transferRate = 1.25});
+
2722
+
2723 testCase([&, this](
+
2724 Env& env,
+
2725 Account const& owner,
+
2726 Account const& issuer,
+
2727 Account const& charlie,
+
2728 auto,
+
2729 Vault& vault,
+
2730 PrettyAsset const& asset,
+
2731 auto&&...) {
+
2732 testcase("IOU frozen trust line to depositor");
+
2733
+
2734 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2735 env(tx);
+
2736 env.close();
+
2737
+
2738 env(vault.deposit(
+
2739 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2740 env.close();
+
2741
+
2742 // Withdraw to 3rd party works
+
2743 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
+
2744 auto tx = vault.withdraw(
+
2745 {.depositor = owner,
+
2746 .id = keylet.key,
+
2747 .amount = asset(10)});
+
2748 tx[sfDestination] = charlie.human();
+
2749 return tx;
+
2750 }(keylet);
+
2751 env(withdrawToCharlie);
+
2752
+
2753 // Freeze the owner
+
2754 env(trust(issuer, asset(0), owner, tfSetFreeze));
+
2755 env.close();
+
2756
+
2757 // Cannot withdraw
+
2758 auto const withdraw = vault.withdraw(
+
2759 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2760 env(withdraw, ter{tecFROZEN});
+
2761
+
2762 // Cannot withdraw to 3rd party
+
2763 env(withdrawToCharlie, ter{tecLOCKED});
+
2764 env.close();
+
2765
+
2766 {
+
2767 // Cannot deposit some more
+
2768 auto tx = vault.deposit(
+
2769 {.depositor = owner,
+
2770 .id = keylet.key,
+
2771 .amount = asset(10)});
+
2772 env(tx, ter{tecFROZEN});
+
2773 }
+
2774
+
2775 {
+
2776 // Clawback still works
+
2777 auto tx = vault.clawback(
+
2778 {.issuer = issuer,
+
2779 .id = keylet.key,
+
2780 .holder = owner,
+
2781 .amount = asset(0)});
+
2782 env(tx);
+
2783 env.close();
+
2784 }
+
2785
+
2786 env(vault.del({.owner = owner, .id = keylet.key}));
+
2787 env.close();
+
2788 });
+
2789
+
2790 testCase([&, this](
+
2791 Env& env,
+
2792 Account const& owner,
+
2793 Account const& issuer,
+
2794 Account const& charlie,
+
2795 auto,
+
2796 Vault& vault,
+
2797 PrettyAsset const& asset,
+
2798 auto&&...) {
+
2799 testcase("IOU no trust line to 3rd party");
+
2800
+
2801 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2802 env(tx);
+
2803 env.close();
+
2804
+
2805 env(vault.deposit(
+
2806 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2807 env.close();
+
2808
+
2809 Account const erin{"erin"};
+
2810 env.fund(XRP(1000), erin);
+
2811 env.close();
+
2812
+
2813 // Withdraw to 3rd party without trust line
+
2814 auto const tx1 = [&](ripple::Keylet keylet) {
+
2815 auto tx = vault.withdraw(
+
2816 {.depositor = owner,
+
2817 .id = keylet.key,
+
2818 .amount = asset(10)});
+
2819 tx[sfDestination] = erin.human();
+
2820 return tx;
+
2821 }(keylet);
+
2822 env(tx1, ter{tecNO_LINE});
+
2823 });
+
2824
+
2825 testCase([&, this](
+
2826 Env& env,
+
2827 Account const& owner,
+
2828 Account const& issuer,
+
2829 Account const& charlie,
+
2830 auto,
+
2831 Vault& vault,
+
2832 PrettyAsset const& asset,
+
2833 auto&&...) {
+
2834 testcase("IOU no trust line to depositor");
+
2835
+
2836 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2837 env(tx);
+
2838 env.close();
+
2839
+
2840 // reset limit, so deposit of all funds will delete the trust line
+
2841 env.trust(asset(0), owner);
+
2842 env.close();
+
2843
+
2844 env(vault.deposit(
+
2845 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
+
2846 env.close();
+
2847
+
2848 auto trustline =
+
2849 env.le(keylet::line(owner, asset.raw().get<Issue>()));
+
2850 BEAST_EXPECT(trustline == nullptr);
+
2851
+
2852 // Withdraw without trust line, will succeed
+
2853 auto const tx1 = [&](ripple::Keylet keylet) {
+
2854 auto tx = vault.withdraw(
+
2855 {.depositor = owner,
+
2856 .id = keylet.key,
+
2857 .amount = asset(10)});
+
2858 return tx;
+
2859 }(keylet);
+
2860 env(tx1);
+
2861 });
+
2862
+
2863 testCase(
+
2864 [&, this](
+
2865 Env& env,
+
2866 Account const& owner,
+
2867 Account const& issuer,
+
2868 Account const& charlie,
+
2869 auto const& vaultAccount,
+
2870 Vault& vault,
+
2871 PrettyAsset const& asset,
+
2872 auto&&...) {
+
2873 testcase("IOU calculation rounding");
+
2874
+
2875 auto [tx, keylet] =
+
2876 vault.create({.owner = owner, .asset = asset});
+
2877 tx[sfScale] = 1;
+
2878 env(tx);
+
2879 env.close();
+
2880
+
2881 auto const startingOwnerBalance = env.balance(owner, asset);
+
2882 BEAST_EXPECT(
+
2883 (startingOwnerBalance.value() ==
+
2884 STAmount{asset, 11875, -2}));
+
2885
+
2886 // This operation (first deposit 100, then 3.75 x 5) is known to
+
2887 // have triggered calculation rounding errors in Number
+
2888 // (addition and division), causing the last deposit to be
+
2889 // blocked by Vault invariants.
+
2890 env(vault.deposit(
+
2891 {.depositor = owner,
+
2892 .id = keylet.key,
+
2893 .amount = asset(100)}));
+
2894
+
2895 auto const tx1 = vault.deposit(
+
2896 {.depositor = owner,
+
2897 .id = keylet.key,
+
2898 .amount = asset(Number(375, -2))});
+
2899 for (auto i = 0; i < 5; ++i)
+
2900 {
+
2901 env(tx1);
+
2902 }
+
2903 env.close();
+
2904
+
2905 {
+
2906 STAmount const xfer{asset, 1185, -1};
+
2907 BEAST_EXPECT(
+
2908 env.balance(owner, asset) ==
+
2909 startingOwnerBalance.value() - xfer);
+
2910 BEAST_EXPECT(
+
2911 env.balance(vaultAccount(keylet), asset) == xfer);
2912
-
2913 // Withdraw can now create trust line, will succeed
-
2914 env(tx);
-
2915 env.close();
-
2916 },
-
2917 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
-
2918
-
2919 testCase(
-
2920 [&, this](
-
2921 Env& env,
-
2922 Account const& owner,
-
2923 Account const& issuer,
-
2924 Account const& charlie,
-
2925 auto,
-
2926 Vault& vault,
-
2927 PrettyAsset const& asset,
-
2928 auto&&...) {
-
2929 testcase("IOU no reserve for share MPToken");
-
2930 auto [tx, keylet] =
-
2931 vault.create({.owner = owner, .asset = asset});
-
2932 env(tx);
-
2933 env.close();
-
2934
-
2935 env(pay(owner, charlie, asset(100)));
-
2936 env.close();
+
2913 auto const vault = env.le(keylet);
+
2914 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
+
2915 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
+
2916 }
+
2917
+
2918 // Total vault balance should be 118.5 IOU. Withdraw and delete
+
2919 // the vault to verify this exact amount was deposited and the
+
2920 // owner has matching shares
+
2921 env(vault.withdraw(
+
2922 {.depositor = owner,
+
2923 .id = keylet.key,
+
2924 .amount = asset(Number(1000 + 37 * 5, -1))}));
+
2925
+
2926 {
+
2927 BEAST_EXPECT(
+
2928 env.balance(owner, asset) ==
+
2929 startingOwnerBalance.value());
+
2930 BEAST_EXPECT(
+
2931 env.balance(vaultAccount(keylet), asset) ==
+
2932 beast::zero);
+
2933 auto const vault = env.le(keylet);
+
2934 BEAST_EXPECT(vault->at(sfAssetsAvailable) == beast::zero);
+
2935 BEAST_EXPECT(vault->at(sfAssetsTotal) == beast::zero);
+
2936 }
2937
-
2938 // Use up some reserve on tickets
-
2939 env(ticket::create(charlie, 2));
-
2940 env.close();
-
2941
-
2942 // Fail because not enough reserve to create MPToken for shares
-
2943 tx = vault.deposit(
-
2944 {.depositor = charlie,
-
2945 .id = keylet.key,
-
2946 .amount = asset(100)});
-
2947 env(tx, ter{tecINSUFFICIENT_RESERVE});
-
2948 env.close();
-
2949
-
2950 env(pay(issuer, charlie, XRP(incReserve)));
-
2951 env.close();
-
2952
-
2953 // Deposit can now create MPToken, will succeed
-
2954 env(tx);
-
2955 env.close();
-
2956 },
-
2957 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
-
2958
-
2959 testCase([&, this](
-
2960 Env& env,
-
2961 Account const& owner,
-
2962 Account const& issuer,
-
2963 Account const& charlie,
-
2964 auto,
-
2965 Vault& vault,
-
2966 PrettyAsset const& asset,
-
2967 auto&&...) {
-
2968 testcase("IOU frozen trust line to 3rd party");
-
2969
-
2970 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2971 env(tx);
-
2972 env.close();
-
2973
-
2974 env(vault.deposit(
-
2975 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2976 env.close();
-
2977
-
2978 // Withdraw to 3rd party works
-
2979 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
-
2980 auto tx = vault.withdraw(
-
2981 {.depositor = owner,
-
2982 .id = keylet.key,
-
2983 .amount = asset(10)});
-
2984 tx[sfDestination] = charlie.human();
-
2985 return tx;
-
2986 }(keylet);
-
2987 env(withdrawToCharlie);
-
2988
-
2989 // Freeze the 3rd party
-
2990 env(trust(issuer, asset(0), charlie, tfSetFreeze));
-
2991 env.close();
-
2992
-
2993 // Can withdraw
-
2994 auto const withdraw = vault.withdraw(
-
2995 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2996 env(withdraw);
-
2997 env.close();
-
2998
-
2999 // Cannot withdraw to 3rd party
-
3000 env(withdrawToCharlie, ter{tecFROZEN});
-
3001 env.close();
-
3002
-
3003 env(vault.clawback(
-
3004 {.issuer = issuer,
-
3005 .id = keylet.key,
-
3006 .holder = owner,
-
3007 .amount = asset(0)}));
-
3008 env.close();
-
3009
-
3010 env(vault.del({.owner = owner, .id = keylet.key}));
-
3011 env.close();
-
3012 });
-
3013
-
3014 testCase([&, this](
-
3015 Env& env,
-
3016 Account const& owner,
-
3017 Account const& issuer,
-
3018 Account const& charlie,
-
3019 auto,
-
3020 Vault& vault,
-
3021 PrettyAsset const& asset,
-
3022 auto&&...) {
-
3023 testcase("IOU global freeze");
-
3024
-
3025 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
3026 env(tx);
-
3027 env.close();
-
3028
-
3029 env(vault.deposit(
-
3030 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
3031 env.close();
-
3032
-
3033 env(fset(issuer, asfGlobalFreeze));
-
3034 env.close();
-
3035
-
3036 {
-
3037 // Cannot withdraw
-
3038 auto tx = vault.withdraw(
-
3039 {.depositor = owner,
-
3040 .id = keylet.key,
-
3041 .amount = asset(10)});
-
3042 env(tx, ter{tecFROZEN});
-
3043
-
3044 // Cannot withdraw to 3rd party
-
3045 tx[sfDestination] = charlie.human();
-
3046 env(tx, ter{tecFROZEN});
-
3047 env.close();
-
3048
-
3049 // Cannot deposit some more
-
3050 tx = vault.deposit(
-
3051 {.depositor = owner,
-
3052 .id = keylet.key,
-
3053 .amount = asset(10)});
+
2938 env(vault.del({.owner = owner, .id = keylet.key}));
+
2939 env.close();
+
2940 },
+
2941 {.initialIOU = Number(11875, -2)});
+
2942
+
2943 auto const [acctReserve, incReserve] = [this]() -> std::pair<int, int> {
+
2944 Env env{*this, testable_amendments()};
+
2945 return {
+
2946 env.current()->fees().accountReserve(0).drops() /
+
2947 DROPS_PER_XRP.drops(),
+
2948 env.current()->fees().increment.drops() /
+
2949 DROPS_PER_XRP.drops()};
+
2950 }();
+
2951
+
2952 testCase(
+
2953 [&, this](
+
2954 Env& env,
+
2955 Account const& owner,
+
2956 Account const& issuer,
+
2957 Account const& charlie,
+
2958 auto,
+
2959 Vault& vault,
+
2960 PrettyAsset const& asset,
+
2961 auto&&...) {
+
2962 testcase("IOU no trust line to depositor no reserve");
+
2963 auto [tx, keylet] =
+
2964 vault.create({.owner = owner, .asset = asset});
+
2965 env(tx);
+
2966 env.close();
+
2967
+
2968 // reset limit, so deposit of all funds will delete the trust
+
2969 // line
+
2970 env.trust(asset(0), owner);
+
2971 env.close();
+
2972
+
2973 env(vault.deposit(
+
2974 {.depositor = owner,
+
2975 .id = keylet.key,
+
2976 .amount = asset(200)}));
+
2977 env.close();
+
2978
+
2979 auto trustline =
+
2980 env.le(keylet::line(owner, asset.raw().get<Issue>()));
+
2981 BEAST_EXPECT(trustline == nullptr);
+
2982
+
2983 // Fail because not enough reserve to create trust line
+
2984 tx = vault.withdraw(
+
2985 {.depositor = owner,
+
2986 .id = keylet.key,
+
2987 .amount = asset(10)});
+
2988 env(tx, ter{tecNO_LINE_INSUF_RESERVE});
+
2989 env.close();
+
2990
+
2991 env(pay(charlie, owner, XRP(incReserve)));
+
2992 env.close();
+
2993
+
2994 // Withdraw can now create trust line, will succeed
+
2995 env(tx);
+
2996 env.close();
+
2997 },
+
2998 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
+
2999
+
3000 testCase(
+
3001 [&, this](
+
3002 Env& env,
+
3003 Account const& owner,
+
3004 Account const& issuer,
+
3005 Account const& charlie,
+
3006 auto,
+
3007 Vault& vault,
+
3008 PrettyAsset const& asset,
+
3009 auto&&...) {
+
3010 testcase("IOU no reserve for share MPToken");
+
3011 auto [tx, keylet] =
+
3012 vault.create({.owner = owner, .asset = asset});
+
3013 env(tx);
+
3014 env.close();
+
3015
+
3016 env(pay(owner, charlie, asset(100)));
+
3017 env.close();
+
3018
+
3019 // Use up some reserve on tickets
+
3020 env(ticket::create(charlie, 2));
+
3021 env.close();
+
3022
+
3023 // Fail because not enough reserve to create MPToken for shares
+
3024 tx = vault.deposit(
+
3025 {.depositor = charlie,
+
3026 .id = keylet.key,
+
3027 .amount = asset(100)});
+
3028 env(tx, ter{tecINSUFFICIENT_RESERVE});
+
3029 env.close();
+
3030
+
3031 env(pay(issuer, charlie, XRP(incReserve)));
+
3032 env.close();
+
3033
+
3034 // Deposit can now create MPToken, will succeed
+
3035 env(tx);
+
3036 env.close();
+
3037 },
+
3038 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
+
3039
+
3040 testCase([&, this](
+
3041 Env& env,
+
3042 Account const& owner,
+
3043 Account const& issuer,
+
3044 Account const& charlie,
+
3045 auto,
+
3046 Vault& vault,
+
3047 PrettyAsset const& asset,
+
3048 auto&&...) {
+
3049 testcase("IOU frozen trust line to 3rd party");
+
3050
+
3051 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
3052 env(tx);
+
3053 env.close();
3054
-
3055 env(tx, ter{tecFROZEN});
-
3056 }
-
3057
-
3058 // Clawback is permitted
-
3059 env(vault.clawback(
-
3060 {.issuer = issuer,
-
3061 .id = keylet.key,
-
3062 .holder = owner,
-
3063 .amount = asset(0)}));
-
3064 env.close();
-
3065
-
3066 env(vault.del({.owner = owner, .id = keylet.key}));
-
3067 env.close();
-
3068 });
-
3069 }
- -
3070
-
3071 void
-
- -
3073 {
-
3074 using namespace test::jtx;
-
3075
-
3076 testcase("private vault");
-
3077
-
3078 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
3079 Account issuer{"issuer"};
-
3080 Account owner{"owner"};
-
3081 Account depositor{"depositor"};
-
3082 Account charlie{"charlie"};
-
3083 Account pdOwner{"pdOwner"};
-
3084 Account credIssuer1{"credIssuer1"};
-
3085 Account credIssuer2{"credIssuer2"};
-
3086 std::string const credType = "credential";
-
3087 Vault vault{env};
-
3088 env.fund(
-
3089 XRP(1000),
-
3090 issuer,
-
3091 owner,
-
3092 depositor,
-
3093 charlie,
-
3094 pdOwner,
-
3095 credIssuer1,
-
3096 credIssuer2);
-
3097 env.close();
-
3098 env(fset(issuer, asfAllowTrustLineClawback));
-
3099 env.close();
-
3100 env.require(flags(issuer, asfAllowTrustLineClawback));
-
3101
-
3102 PrettyAsset asset = issuer["IOU"];
-
3103 env.trust(asset(1000), owner);
-
3104 env(pay(issuer, owner, asset(500)));
-
3105 env.trust(asset(1000), depositor);
-
3106 env(pay(issuer, depositor, asset(500)));
-
3107 env.trust(asset(1000), charlie);
-
3108 env(pay(issuer, charlie, asset(5)));
-
3109 env.close();
-
3110
-
3111 auto [tx, keylet] = vault.create(
-
3112 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
-
3113 env(tx);
-
3114 env.close();
-
3115 BEAST_EXPECT(env.le(keylet));
+
3055 env(vault.deposit(
+
3056 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
3057 env.close();
+
3058
+
3059 // Withdraw to 3rd party works
+
3060 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
+
3061 auto tx = vault.withdraw(
+
3062 {.depositor = owner,
+
3063 .id = keylet.key,
+
3064 .amount = asset(10)});
+
3065 tx[sfDestination] = charlie.human();
+
3066 return tx;
+
3067 }(keylet);
+
3068 env(withdrawToCharlie);
+
3069
+
3070 // Freeze the 3rd party
+
3071 env(trust(issuer, asset(0), charlie, tfSetFreeze));
+
3072 env.close();
+
3073
+
3074 // Can withdraw
+
3075 auto const withdraw = vault.withdraw(
+
3076 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
3077 env(withdraw);
+
3078 env.close();
+
3079
+
3080 // Cannot withdraw to 3rd party
+
3081 env(withdrawToCharlie, ter{tecFROZEN});
+
3082 env.close();
+
3083
+
3084 env(vault.clawback(
+
3085 {.issuer = issuer,
+
3086 .id = keylet.key,
+
3087 .holder = owner,
+
3088 .amount = asset(0)}));
+
3089 env.close();
+
3090
+
3091 env(vault.del({.owner = owner, .id = keylet.key}));
+
3092 env.close();
+
3093 });
+
3094
+
3095 testCase([&, this](
+
3096 Env& env,
+
3097 Account const& owner,
+
3098 Account const& issuer,
+
3099 Account const& charlie,
+
3100 auto,
+
3101 Vault& vault,
+
3102 PrettyAsset const& asset,
+
3103 auto&&...) {
+
3104 testcase("IOU global freeze");
+
3105
+
3106 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
3107 env(tx);
+
3108 env.close();
+
3109
+
3110 env(vault.deposit(
+
3111 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
3112 env.close();
+
3113
+
3114 env(fset(issuer, asfGlobalFreeze));
+
3115 env.close();
3116
-
3117 {
-
3118 testcase("private vault owner can deposit");
-
3119 auto tx = vault.deposit(
-
3120 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
-
3121 env(tx);
-
3122 }
-
3123
-
3124 {
-
3125 testcase("private vault depositor not authorized yet");
-
3126 auto tx = vault.deposit(
-
3127 {.depositor = depositor,
-
3128 .id = keylet.key,
-
3129 .amount = asset(50)});
-
3130 env(tx, ter{tecNO_AUTH});
-
3131 }
-
3132
-
3133 {
-
3134 testcase("private vault cannot set non-existing domain");
-
3135 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
3136 tx[sfDomainID] = to_string(base_uint<256>(42ul));
-
3137 env(tx, ter{tecOBJECT_NOT_FOUND});
-
3138 }
-
3139
-
3140 {
-
3141 testcase("private vault set domainId");
-
3142
-
3143 {
-
3144 pdomain::Credentials const credentials1{
-
3145 {.issuer = credIssuer1, .credType = credType}};
+
3117 {
+
3118 // Cannot withdraw
+
3119 auto tx = vault.withdraw(
+
3120 {.depositor = owner,
+
3121 .id = keylet.key,
+
3122 .amount = asset(10)});
+
3123 env(tx, ter{tecFROZEN});
+
3124
+
3125 // Cannot withdraw to 3rd party
+
3126 tx[sfDestination] = charlie.human();
+
3127 env(tx, ter{tecFROZEN});
+
3128 env.close();
+
3129
+
3130 // Cannot deposit some more
+
3131 tx = vault.deposit(
+
3132 {.depositor = owner,
+
3133 .id = keylet.key,
+
3134 .amount = asset(10)});
+
3135
+
3136 env(tx, ter{tecFROZEN});
+
3137 }
+
3138
+
3139 // Clawback is permitted
+
3140 env(vault.clawback(
+
3141 {.issuer = issuer,
+
3142 .id = keylet.key,
+
3143 .holder = owner,
+
3144 .amount = asset(0)}));
+
3145 env.close();
3146
-
3147 env(pdomain::setTx(pdOwner, credentials1));
-
3148 auto const domainId1 = [&]() {
-
3149 auto tx = env.tx()->getJson(JsonOptions::none);
-
3150 return pdomain::getNewDomain(env.meta());
-
3151 }();
-
3152
-
3153 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
3154 tx[sfDomainID] = to_string(domainId1);
-
3155 env(tx);
-
3156 env.close();
-
3157
-
3158 // Update domain second time, should be harmless
-
3159 env(tx);
-
3160 env.close();
-
3161 }
-
3162
-
3163 {
-
3164 pdomain::Credentials const credentials{
-
3165 {.issuer = credIssuer1, .credType = credType},
-
3166 {.issuer = credIssuer2, .credType = credType}};
-
3167
-
3168 env(pdomain::setTx(pdOwner, credentials));
-
3169 auto const domainId = [&]() {
-
3170 auto tx = env.tx()->getJson(JsonOptions::none);
-
3171 return pdomain::getNewDomain(env.meta());
-
3172 }();
-
3173
-
3174 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
3175 tx[sfDomainID] = to_string(domainId);
-
3176 env(tx);
-
3177 env.close();
-
3178
-
3179 // Should be idempotent
-
3180 tx = vault.set({.owner = owner, .id = keylet.key});
-
3181 tx[sfDomainID] = to_string(domainId);
-
3182 env(tx);
-
3183 env.close();
-
3184 }
-
3185 }
-
3186
-
3187 {
-
3188 testcase("private vault depositor still not authorized");
-
3189 auto tx = vault.deposit(
-
3190 {.depositor = depositor,
-
3191 .id = keylet.key,
-
3192 .amount = asset(50)});
-
3193 env(tx, ter{tecNO_AUTH});
-
3194 env.close();
-
3195 }
-
3196
-
3197 auto const credKeylet =
-
3198 credentials::keylet(depositor, credIssuer1, credType);
-
3199 {
-
3200 testcase("private vault depositor now authorized");
-
3201 env(credentials::create(depositor, credIssuer1, credType));
-
3202 env(credentials::accept(depositor, credIssuer1, credType));
-
3203 env(credentials::create(charlie, credIssuer1, credType));
-
3204 // charlie's credential not accepted
-
3205 env.close();
-
3206 auto credSle = env.le(credKeylet);
-
3207 BEAST_EXPECT(credSle != nullptr);
-
3208
-
3209 auto tx = vault.deposit(
-
3210 {.depositor = depositor,
-
3211 .id = keylet.key,
-
3212 .amount = asset(50)});
-
3213 env(tx);
-
3214 env.close();
-
3215
-
3216 tx = vault.deposit(
-
3217 {.depositor = charlie, .id = keylet.key, .amount = asset(50)});
-
3218 env(tx, ter{tecNO_AUTH});
-
3219 env.close();
-
3220 }
-
3221
-
3222 {
-
3223 testcase("private vault depositor lost authorization");
-
3224 env(credentials::deleteCred(
-
3225 credIssuer1, depositor, credIssuer1, credType));
-
3226 env(credentials::deleteCred(
-
3227 credIssuer1, charlie, credIssuer1, credType));
-
3228 env.close();
-
3229 auto credSle = env.le(credKeylet);
-
3230 BEAST_EXPECT(credSle == nullptr);
-
3231
-
3232 auto tx = vault.deposit(
-
3233 {.depositor = depositor,
-
3234 .id = keylet.key,
-
3235 .amount = asset(50)});
-
3236 env(tx, ter{tecNO_AUTH});
-
3237 env.close();
-
3238 }
-
3239
-
3240 auto const shares = [&env, keylet = keylet, this]() -> Asset {
-
3241 auto const vault = env.le(keylet);
-
3242 BEAST_EXPECT(vault != nullptr);
-
3243 return MPTIssue(vault->at(sfShareMPTID));
-
3244 }();
-
3245
-
3246 {
-
3247 testcase("private vault expired authorization");
-
3248 uint32_t const closeTime = env.current()
-
3249 ->info()
-
3250 .parentCloseTime.time_since_epoch()
-
3251 .count();
-
3252 {
-
3253 auto tx0 =
-
3254 credentials::create(depositor, credIssuer2, credType);
-
3255 tx0[sfExpiration] = closeTime + 20;
-
3256 env(tx0);
-
3257 tx0 = credentials::create(charlie, credIssuer2, credType);
-
3258 tx0[sfExpiration] = closeTime + 20;
-
3259 env(tx0);
-
3260 env.close();
-
3261
-
3262 env(credentials::accept(depositor, credIssuer2, credType));
-
3263 env(credentials::accept(charlie, credIssuer2, credType));
+
3147 env(vault.del({.owner = owner, .id = keylet.key}));
+
3148 env.close();
+
3149 });
+
3150 }
+
+
3151
+
3152 void
+
+ +
3154 {
+
3155 using namespace test::jtx;
+
3156
+
3157 testcase("private vault");
+
3158
+
3159 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3160 Account issuer{"issuer"};
+
3161 Account owner{"owner"};
+
3162 Account depositor{"depositor"};
+
3163 Account charlie{"charlie"};
+
3164 Account pdOwner{"pdOwner"};
+
3165 Account credIssuer1{"credIssuer1"};
+
3166 Account credIssuer2{"credIssuer2"};
+
3167 std::string const credType = "credential";
+
3168 Vault vault{env};
+
3169 env.fund(
+
3170 XRP(1000),
+
3171 issuer,
+
3172 owner,
+
3173 depositor,
+
3174 charlie,
+
3175 pdOwner,
+
3176 credIssuer1,
+
3177 credIssuer2);
+
3178 env.close();
+
3179 env(fset(issuer, asfAllowTrustLineClawback));
+
3180 env.close();
+
3181 env.require(flags(issuer, asfAllowTrustLineClawback));
+
3182
+
3183 PrettyAsset asset = issuer["IOU"];
+
3184 env.trust(asset(1000), owner);
+
3185 env(pay(issuer, owner, asset(500)));
+
3186 env.trust(asset(1000), depositor);
+
3187 env(pay(issuer, depositor, asset(500)));
+
3188 env.trust(asset(1000), charlie);
+
3189 env(pay(issuer, charlie, asset(5)));
+
3190 env.close();
+
3191
+
3192 auto [tx, keylet] = vault.create(
+
3193 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
+
3194 env(tx);
+
3195 env.close();
+
3196 BEAST_EXPECT(env.le(keylet));
+
3197
+
3198 {
+
3199 testcase("private vault owner can deposit");
+
3200 auto tx = vault.deposit(
+
3201 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
+
3202 env(tx);
+
3203 }
+
3204
+
3205 {
+
3206 testcase("private vault depositor not authorized yet");
+
3207 auto tx = vault.deposit(
+
3208 {.depositor = depositor,
+
3209 .id = keylet.key,
+
3210 .amount = asset(50)});
+
3211 env(tx, ter{tecNO_AUTH});
+
3212 }
+
3213
+
3214 {
+
3215 testcase("private vault cannot set non-existing domain");
+
3216 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
3217 tx[sfDomainID] = to_string(base_uint<256>(42ul));
+
3218 env(tx, ter{tecOBJECT_NOT_FOUND});
+
3219 }
+
3220
+
3221 {
+
3222 testcase("private vault set domainId");
+
3223
+
3224 {
+
3225 pdomain::Credentials const credentials1{
+
3226 {.issuer = credIssuer1, .credType = credType}};
+
3227
+
3228 env(pdomain::setTx(pdOwner, credentials1));
+
3229 auto const domainId1 = [&]() {
+
3230 auto tx = env.tx()->getJson(JsonOptions::none);
+
3231 return pdomain::getNewDomain(env.meta());
+
3232 }();
+
3233
+
3234 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
3235 tx[sfDomainID] = to_string(domainId1);
+
3236 env(tx);
+
3237 env.close();
+
3238
+
3239 // Update domain second time, should be harmless
+
3240 env(tx);
+
3241 env.close();
+
3242 }
+
3243
+
3244 {
+
3245 pdomain::Credentials const credentials{
+
3246 {.issuer = credIssuer1, .credType = credType},
+
3247 {.issuer = credIssuer2, .credType = credType}};
+
3248
+
3249 env(pdomain::setTx(pdOwner, credentials));
+
3250 auto const domainId = [&]() {
+
3251 auto tx = env.tx()->getJson(JsonOptions::none);
+
3252 return pdomain::getNewDomain(env.meta());
+
3253 }();
+
3254
+
3255 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
3256 tx[sfDomainID] = to_string(domainId);
+
3257 env(tx);
+
3258 env.close();
+
3259
+
3260 // Should be idempotent
+
3261 tx = vault.set({.owner = owner, .id = keylet.key});
+
3262 tx[sfDomainID] = to_string(domainId);
+
3263 env(tx);
3264 env.close();
3265 }
-
3266
-
3267 {
-
3268 auto tx1 = vault.deposit(
-
3269 {.depositor = depositor,
-
3270 .id = keylet.key,
-
3271 .amount = asset(50)});
-
3272 env(tx1);
-
3273 env.close();
-
3274
-
3275 auto const tokenKeylet = keylet::mptoken(
-
3276 shares.get<MPTIssue>().getMptID(), depositor.id());
-
3277 BEAST_EXPECT(env.le(tokenKeylet) != nullptr);
-
3278 }
-
3279
-
3280 {
-
3281 // time advance
-
3282 env.close();
-
3283 env.close();
-
3284 env.close();
-
3285
-
3286 auto const credsKeylet =
-
3287 credentials::keylet(depositor, credIssuer2, credType);
-
3288 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
+
3266 }
+
3267
+
3268 {
+
3269 testcase("private vault depositor still not authorized");
+
3270 auto tx = vault.deposit(
+
3271 {.depositor = depositor,
+
3272 .id = keylet.key,
+
3273 .amount = asset(50)});
+
3274 env(tx, ter{tecNO_AUTH});
+
3275 env.close();
+
3276 }
+
3277
+
3278 auto const credKeylet =
+
3279 credentials::keylet(depositor, credIssuer1, credType);
+
3280 {
+
3281 testcase("private vault depositor now authorized");
+
3282 env(credentials::create(depositor, credIssuer1, credType));
+
3283 env(credentials::accept(depositor, credIssuer1, credType));
+
3284 env(credentials::create(charlie, credIssuer1, credType));
+
3285 // charlie's credential not accepted
+
3286 env.close();
+
3287 auto credSle = env.le(credKeylet);
+
3288 BEAST_EXPECT(credSle != nullptr);
3289
-
3290 auto tx2 = vault.deposit(
-
3291 {.depositor = depositor,
-
3292 .id = keylet.key,
-
3293 .amount = asset(1)});
-
3294 env(tx2, ter{tecEXPIRED});
-
3295 env.close();
+
3290 auto tx = vault.deposit(
+
3291 {.depositor = depositor,
+
3292 .id = keylet.key,
+
3293 .amount = asset(50)});
+
3294 env(tx);
+
3295 env.close();
3296
-
3297 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
-
3298 }
-
3299
-
3300 {
-
3301 auto const credsKeylet =
-
3302 credentials::keylet(charlie, credIssuer2, credType);
-
3303 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
-
3304 auto const tokenKeylet = keylet::mptoken(
-
3305 shares.get<MPTIssue>().getMptID(), charlie.id());
-
3306 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
-
3307
-
3308 auto tx3 = vault.deposit(
-
3309 {.depositor = charlie,
-
3310 .id = keylet.key,
-
3311 .amount = asset(2)});
-
3312 env(tx3, ter{tecEXPIRED});
-
3313
-
3314 env.close();
-
3315 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
-
3316 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
-
3317 }
-
3318 }
-
3319
-
3320 {
-
3321 testcase("private vault reset domainId");
-
3322 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
3323 tx[sfDomainID] = "0";
-
3324 env(tx);
-
3325 env.close();
+
3297 tx = vault.deposit(
+
3298 {.depositor = charlie, .id = keylet.key, .amount = asset(50)});
+
3299 env(tx, ter{tecNO_AUTH});
+
3300 env.close();
+
3301 }
+
3302
+
3303 {
+
3304 testcase("private vault depositor lost authorization");
+
3305 env(credentials::deleteCred(
+
3306 credIssuer1, depositor, credIssuer1, credType));
+
3307 env(credentials::deleteCred(
+
3308 credIssuer1, charlie, credIssuer1, credType));
+
3309 env.close();
+
3310 auto credSle = env.le(credKeylet);
+
3311 BEAST_EXPECT(credSle == nullptr);
+
3312
+
3313 auto tx = vault.deposit(
+
3314 {.depositor = depositor,
+
3315 .id = keylet.key,
+
3316 .amount = asset(50)});
+
3317 env(tx, ter{tecNO_AUTH});
+
3318 env.close();
+
3319 }
+
3320
+
3321 auto const shares = [&env, keylet = keylet, this]() -> Asset {
+
3322 auto const vault = env.le(keylet);
+
3323 BEAST_EXPECT(vault != nullptr);
+
3324 return MPTIssue(vault->at(sfShareMPTID));
+
3325 }();
3326
-
3327 tx = vault.deposit(
-
3328 {.depositor = depositor,
-
3329 .id = keylet.key,
-
3330 .amount = asset(50)});
-
3331 env(tx, ter{tecNO_AUTH});
-
3332 env.close();
-
3333
-
3334 tx = vault.withdraw(
-
3335 {.depositor = depositor,
-
3336 .id = keylet.key,
-
3337 .amount = asset(50)});
-
3338 env(tx);
-
3339 env.close();
-
3340
-
3341 tx = vault.clawback(
-
3342 {.issuer = issuer,
-
3343 .id = keylet.key,
-
3344 .holder = depositor,
-
3345 .amount = asset(0)});
-
3346 env(tx);
+
3327 {
+
3328 testcase("private vault expired authorization");
+
3329 uint32_t const closeTime = env.current()
+
3330 ->info()
+
3331 .parentCloseTime.time_since_epoch()
+
3332 .count();
+
3333 {
+
3334 auto tx0 =
+
3335 credentials::create(depositor, credIssuer2, credType);
+
3336 tx0[sfExpiration] = closeTime + 20;
+
3337 env(tx0);
+
3338 tx0 = credentials::create(charlie, credIssuer2, credType);
+
3339 tx0[sfExpiration] = closeTime + 20;
+
3340 env(tx0);
+
3341 env.close();
+
3342
+
3343 env(credentials::accept(depositor, credIssuer2, credType));
+
3344 env(credentials::accept(charlie, credIssuer2, credType));
+
3345 env.close();
+
3346 }
3347
-
3348 tx = vault.clawback(
-
3349 {.issuer = issuer,
-
3350 .id = keylet.key,
-
3351 .holder = owner,
-
3352 .amount = asset(0)});
-
3353 env(tx);
-
3354 env.close();
+
3348 {
+
3349 auto tx1 = vault.deposit(
+
3350 {.depositor = depositor,
+
3351 .id = keylet.key,
+
3352 .amount = asset(50)});
+
3353 env(tx1);
+
3354 env.close();
3355
-
3356 tx = vault.del({
-
3357 .owner = owner,
-
3358 .id = keylet.key,
-
3359 });
-
3360 env(tx);
-
3361 }
-
3362 }
-
-
3363
-
3364 void
-
- -
3366 {
-
3367 using namespace test::jtx;
-
3368
-
3369 testcase("private XRP vault");
+
3356 auto const tokenKeylet = keylet::mptoken(
+
3357 shares.get<MPTIssue>().getMptID(), depositor.id());
+
3358 BEAST_EXPECT(env.le(tokenKeylet) != nullptr);
+
3359 }
+
3360
+
3361 {
+
3362 // time advance
+
3363 env.close();
+
3364 env.close();
+
3365 env.close();
+
3366
+
3367 auto const credsKeylet =
+
3368 credentials::keylet(depositor, credIssuer2, credType);
+
3369 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
3370
-
3371 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
3372 Account owner{"owner"};
-
3373 Account depositor{"depositor"};
-
3374 Account alice{"charlie"};
-
3375 std::string const credType = "credential";
-
3376 Vault vault{env};
-
3377 env.fund(XRP(100000), owner, depositor, alice);
-
3378 env.close();
-
3379
-
3380 PrettyAsset asset = xrpIssue();
-
3381 auto [tx, keylet] = vault.create(
-
3382 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
-
3383 env(tx);
-
3384 env.close();
-
3385
-
3386 auto const [vaultAccount, issuanceId] =
-
3387 [&env, keylet = keylet, this]() -> std::tuple<AccountID, uint192> {
-
3388 auto const vault = env.le(keylet);
-
3389 BEAST_EXPECT(vault != nullptr);
-
3390 return {vault->at(sfAccount), vault->at(sfShareMPTID)};
-
3391 }();
-
3392 BEAST_EXPECT(env.le(keylet::account(vaultAccount)));
-
3393 BEAST_EXPECT(env.le(keylet::mptIssuance(issuanceId)));
-
3394 PrettyAsset shares{issuanceId};
-
3395
-
3396 {
-
3397 testcase("private XRP vault owner can deposit");
-
3398 auto tx = vault.deposit(
-
3399 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
-
3400 env(tx);
-
3401 env.close();
-
3402 }
-
3403
-
3404 {
-
3405 testcase("private XRP vault cannot pay shares to depositor yet");
-
3406 env(pay(owner, depositor, shares(1)), ter{tecNO_AUTH});
-
3407 }
-
3408
-
3409 {
-
3410 testcase("private XRP vault depositor not authorized yet");
-
3411 auto tx = vault.deposit(
-
3412 {.depositor = depositor,
-
3413 .id = keylet.key,
-
3414 .amount = asset(50)});
-
3415 env(tx, ter{tecNO_AUTH});
-
3416 }
-
3417
-
3418 {
-
3419 testcase("private XRP vault set DomainID");
-
3420 pdomain::Credentials const credentials{
-
3421 {.issuer = owner, .credType = credType}};
-
3422
-
3423 env(pdomain::setTx(owner, credentials));
-
3424 auto const domainId = [&]() {
-
3425 auto tx = env.tx()->getJson(JsonOptions::none);
-
3426 return pdomain::getNewDomain(env.meta());
-
3427 }();
+
3371 auto tx2 = vault.deposit(
+
3372 {.depositor = depositor,
+
3373 .id = keylet.key,
+
3374 .amount = asset(1)});
+
3375 env(tx2, ter{tecEXPIRED});
+
3376 env.close();
+
3377
+
3378 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
+
3379 }
+
3380
+
3381 {
+
3382 auto const credsKeylet =
+
3383 credentials::keylet(charlie, credIssuer2, credType);
+
3384 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
+
3385 auto const tokenKeylet = keylet::mptoken(
+
3386 shares.get<MPTIssue>().getMptID(), charlie.id());
+
3387 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
+
3388
+
3389 auto tx3 = vault.deposit(
+
3390 {.depositor = charlie,
+
3391 .id = keylet.key,
+
3392 .amount = asset(2)});
+
3393 env(tx3, ter{tecEXPIRED});
+
3394
+
3395 env.close();
+
3396 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
+
3397 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
+
3398 }
+
3399 }
+
3400
+
3401 {
+
3402 testcase("private vault reset domainId");
+
3403 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
3404 tx[sfDomainID] = "0";
+
3405 env(tx);
+
3406 env.close();
+
3407
+
3408 tx = vault.deposit(
+
3409 {.depositor = depositor,
+
3410 .id = keylet.key,
+
3411 .amount = asset(50)});
+
3412 env(tx, ter{tecNO_AUTH});
+
3413 env.close();
+
3414
+
3415 tx = vault.withdraw(
+
3416 {.depositor = depositor,
+
3417 .id = keylet.key,
+
3418 .amount = asset(50)});
+
3419 env(tx);
+
3420 env.close();
+
3421
+
3422 tx = vault.clawback(
+
3423 {.issuer = issuer,
+
3424 .id = keylet.key,
+
3425 .holder = depositor,
+
3426 .amount = asset(0)});
+
3427 env(tx);
3428
-
3429 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
3430 tx[sfDomainID] = to_string(domainId);
-
3431 env(tx);
-
3432 env.close();
-
3433 }
-
3434
-
3435 auto const credKeylet = credentials::keylet(depositor, owner, credType);
-
3436 {
-
3437 testcase("private XRP vault depositor now authorized");
-
3438 env(credentials::create(depositor, owner, credType));
-
3439 env(credentials::accept(depositor, owner, credType));
-
3440 env.close();
-
3441
-
3442 BEAST_EXPECT(env.le(credKeylet));
-
3443 auto tx = vault.deposit(
-
3444 {.depositor = depositor,
-
3445 .id = keylet.key,
-
3446 .amount = asset(50)});
-
3447 env(tx);
-
3448 env.close();
-
3449 }
-
3450
-
3451 {
-
3452 testcase("private XRP vault can pay shares to depositor");
-
3453 env(pay(owner, depositor, shares(1)));
-
3454 }
-
3455
-
3456 {
-
3457 testcase("private XRP vault cannot pay shares to 3rd party");
-
3458 Json::Value jv;
-
3459 jv[sfAccount] = alice.human();
-
3460 jv[sfTransactionType] = jss::MPTokenAuthorize;
-
3461 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
-
3462 env(jv);
-
3463 env.close();
-
3464
-
3465 env(pay(owner, alice, shares(1)), ter{tecNO_AUTH});
-
3466 }
-
3467 }
+
3429 tx = vault.clawback(
+
3430 {.issuer = issuer,
+
3431 .id = keylet.key,
+
3432 .holder = owner,
+
3433 .amount = asset(0)});
+
3434 env(tx);
+
3435 env.close();
+
3436
+
3437 tx = vault.del({
+
3438 .owner = owner,
+
3439 .id = keylet.key,
+
3440 });
+
3441 env(tx);
+
3442 }
+
3443 }
-
3468
-
3469 void
-
- -
3471 {
-
3472 using namespace test::jtx;
-
3473
-
3474 testcase("fail pseudo-account allocation");
-
3475 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
3476 Account const owner{"owner"};
-
3477 Vault vault{env};
-
3478 env.fund(XRP(1000), owner);
-
3479
-
3480 auto const keylet = keylet::vault(owner.id(), env.seq(owner));
-
3481 for (int i = 0; i < 256; ++i)
-
3482 {
-
3483 AccountID const accountId =
-
3484 ripple::pseudoAccountAddress(*env.current(), keylet.key);
-
3485
-
3486 env(pay(env.master.id(), accountId, XRP(1000)),
-
3487 seq(autofill),
-
3488 fee(autofill),
-
3489 sig(autofill));
-
3490 }
-
3491
-
3492 auto [tx, keylet1] =
-
3493 vault.create({.owner = owner, .asset = xrpIssue()});
-
3494 BEAST_EXPECT(keylet.key == keylet1.key);
-
3495 env(tx, ter{terADDRESS_COLLISION});
-
3496 }
-
-
3497
-
3498 void
-
- -
3500 {
-
3501 using namespace test::jtx;
-
3502
-
3503 struct Data
-
3504 {
-
3505 Account const& owner;
-
3506 Account const& issuer;
-
3507 Account const& depositor;
-
3508 Account const& vaultAccount;
-
3509 MPTIssue shares;
-
3510 PrettyAsset const& share;
-
3511 Vault& vault;
-
3512 ripple::Keylet keylet;
-
3513 Issue assets;
-
3514 PrettyAsset const& asset;
-
3515 std::function<bool(std::function<bool(SLE&, SLE&)>)> peek;
-
3516 };
-
3517
-
3518 auto testCase = [&, this](
-
3519 std::uint8_t scale,
-
3520 std::function<void(Env & env, Data data)> test) {
-
3521 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
3522 Account const owner{"owner"};
-
3523 Account const issuer{"issuer"};
-
3524 Account const depositor{"depositor"};
-
3525 Vault vault{env};
-
3526 env.fund(XRP(1000), issuer, owner, depositor);
-
3527 env(fset(issuer, asfAllowTrustLineClawback));
-
3528 env.close();
-
3529
-
3530 PrettyAsset const asset = issuer["IOU"];
-
3531 env.trust(asset(1000), owner);
-
3532 env.trust(asset(1000), depositor);
-
3533 env(pay(issuer, owner, asset(200)));
-
3534 env(pay(issuer, depositor, asset(200)));
-
3535 env.close();
+
3444
+
3445 void
+
+ +
3447 {
+
3448 using namespace test::jtx;
+
3449
+
3450 testcase("private XRP vault");
+
3451
+
3452 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3453 Account owner{"owner"};
+
3454 Account depositor{"depositor"};
+
3455 Account alice{"charlie"};
+
3456 std::string const credType = "credential";
+
3457 Vault vault{env};
+
3458 env.fund(XRP(100000), owner, depositor, alice);
+
3459 env.close();
+
3460
+
3461 PrettyAsset asset = xrpIssue();
+
3462 auto [tx, keylet] = vault.create(
+
3463 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
+
3464 env(tx);
+
3465 env.close();
+
3466
+
3467 auto const [vaultAccount, issuanceId] =
+
3468 [&env, keylet = keylet, this]() -> std::tuple<AccountID, uint192> {
+
3469 auto const vault = env.le(keylet);
+
3470 BEAST_EXPECT(vault != nullptr);
+
3471 return {vault->at(sfAccount), vault->at(sfShareMPTID)};
+
3472 }();
+
3473 BEAST_EXPECT(env.le(keylet::account(vaultAccount)));
+
3474 BEAST_EXPECT(env.le(keylet::mptIssuance(issuanceId)));
+
3475 PrettyAsset shares{issuanceId};
+
3476
+
3477 {
+
3478 testcase("private XRP vault owner can deposit");
+
3479 auto tx = vault.deposit(
+
3480 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
+
3481 env(tx);
+
3482 env.close();
+
3483 }
+
3484
+
3485 {
+
3486 testcase("private XRP vault cannot pay shares to depositor yet");
+
3487 env(pay(owner, depositor, shares(1)), ter{tecNO_AUTH});
+
3488 }
+
3489
+
3490 {
+
3491 testcase("private XRP vault depositor not authorized yet");
+
3492 auto tx = vault.deposit(
+
3493 {.depositor = depositor,
+
3494 .id = keylet.key,
+
3495 .amount = asset(50)});
+
3496 env(tx, ter{tecNO_AUTH});
+
3497 }
+
3498
+
3499 {
+
3500 testcase("private XRP vault set DomainID");
+
3501 pdomain::Credentials const credentials{
+
3502 {.issuer = owner, .credType = credType}};
+
3503
+
3504 env(pdomain::setTx(owner, credentials));
+
3505 auto const domainId = [&]() {
+
3506 auto tx = env.tx()->getJson(JsonOptions::none);
+
3507 return pdomain::getNewDomain(env.meta());
+
3508 }();
+
3509
+
3510 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
3511 tx[sfDomainID] = to_string(domainId);
+
3512 env(tx);
+
3513 env.close();
+
3514 }
+
3515
+
3516 auto const credKeylet = credentials::keylet(depositor, owner, credType);
+
3517 {
+
3518 testcase("private XRP vault depositor now authorized");
+
3519 env(credentials::create(depositor, owner, credType));
+
3520 env(credentials::accept(depositor, owner, credType));
+
3521 env.close();
+
3522
+
3523 BEAST_EXPECT(env.le(credKeylet));
+
3524 auto tx = vault.deposit(
+
3525 {.depositor = depositor,
+
3526 .id = keylet.key,
+
3527 .amount = asset(50)});
+
3528 env(tx);
+
3529 env.close();
+
3530 }
+
3531
+
3532 {
+
3533 testcase("private XRP vault can pay shares to depositor");
+
3534 env(pay(owner, depositor, shares(1)));
+
3535 }
3536
-
3537 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
3538 tx[sfScale] = scale;
-
3539 env(tx);
-
3540
-
3541 auto const [vaultAccount, issuanceId] =
-
3542 [&env](ripple::Keylet keylet) -> std::tuple<Account, MPTID> {
-
3543 auto const vault = env.le(keylet);
-
3544 return {
-
3545 Account("vault", vault->at(sfAccount)),
-
3546 vault->at(sfShareMPTID)};
-
3547 }(keylet);
-
3548 MPTIssue shares(issuanceId);
-
3549 env.memoize(vaultAccount);
-
3550
-
3551 auto const peek =
-
3552 [=, &env, this](std::function<bool(SLE&, SLE&)> fn) -> bool {
-
3553 return env.app().openLedger().modify(
-
3554 [&](OpenView& view, beast::Journal j) -> bool {
-
3555 Sandbox sb(&view, tapNONE);
-
3556 auto vault = sb.peek(keylet::vault(keylet.key));
-
3557 if (!BEAST_EXPECT(vault != nullptr))
-
3558 return false;
-
3559 auto shares = sb.peek(
-
3560 keylet::mptIssuance(vault->at(sfShareMPTID)));
-
3561 if (!BEAST_EXPECT(shares != nullptr))
-
3562 return false;
-
3563 if (fn(*vault, *shares))
-
3564 {
-
3565 sb.update(vault);
-
3566 sb.update(shares);
-
3567 sb.apply(view);
-
3568 return true;
-
3569 }
-
3570 return false;
-
3571 });
-
3572 };
-
3573
-
3574 test(
-
3575 env,
-
3576 {.owner = owner,
-
3577 .issuer = issuer,
-
3578 .depositor = depositor,
-
3579 .vaultAccount = vaultAccount,
-
3580 .shares = shares,
-
3581 .share = PrettyAsset(shares),
-
3582 .vault = vault,
-
3583 .keylet = keylet,
-
3584 .assets = asset.raw().get<Issue>(),
-
3585 .asset = asset,
-
3586 .peek = peek});
-
3587 };
-
3588
-
3589 testCase(18, [&, this](Env& env, Data d) {
-
3590 testcase("Scale deposit overflow on first deposit");
-
3591 auto tx = d.vault.deposit(
-
3592 {.depositor = d.depositor,
-
3593 .id = d.keylet.key,
-
3594 .amount = d.asset(10)});
-
3595 env(tx, ter{tecPATH_DRY});
-
3596 env.close();
-
3597 });
+
3537 {
+
3538 testcase("private XRP vault cannot pay shares to 3rd party");
+
3539 Json::Value jv;
+
3540 jv[sfAccount] = alice.human();
+
3541 jv[sfTransactionType] = jss::MPTokenAuthorize;
+
3542 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
+
3543 env(jv);
+
3544 env.close();
+
3545
+
3546 env(pay(owner, alice, shares(1)), ter{tecNO_AUTH});
+
3547 }
+
3548 }
+
+
3549
+
3550 void
+
+ +
3552 {
+
3553 using namespace test::jtx;
+
3554
+
3555 testcase("fail pseudo-account allocation");
+
3556 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3557 Account const owner{"owner"};
+
3558 Vault vault{env};
+
3559 env.fund(XRP(1000), owner);
+
3560
+
3561 auto const keylet = keylet::vault(owner.id(), env.seq(owner));
+
3562 for (int i = 0; i < 256; ++i)
+
3563 {
+
3564 AccountID const accountId =
+
3565 ripple::pseudoAccountAddress(*env.current(), keylet.key);
+
3566
+
3567 env(pay(env.master.id(), accountId, XRP(1000)),
+
3568 seq(autofill),
+
3569 fee(autofill),
+
3570 sig(autofill));
+
3571 }
+
3572
+
3573 auto [tx, keylet1] =
+
3574 vault.create({.owner = owner, .asset = xrpIssue()});
+
3575 BEAST_EXPECT(keylet.key == keylet1.key);
+
3576 env(tx, ter{terADDRESS_COLLISION});
+
3577 }
+
+
3578
+
3579 void
+
+ +
3581 {
+
3582 using namespace test::jtx;
+
3583
+
3584 struct Data
+
3585 {
+
3586 Account const& owner;
+
3587 Account const& issuer;
+
3588 Account const& depositor;
+
3589 Account const& vaultAccount;
+
3590 MPTIssue shares;
+
3591 PrettyAsset const& share;
+
3592 Vault& vault;
+
3593 ripple::Keylet keylet;
+
3594 Issue assets;
+
3595 PrettyAsset const& asset;
+
3596 std::function<bool(std::function<bool(SLE&, SLE&)>)> peek;
+
3597 };
3598
-
3599 testCase(18, [&, this](Env& env, Data d) {
-
3600 testcase("Scale deposit overflow on second deposit");
-
3601
-
3602 {
-
3603 auto tx = d.vault.deposit(
-
3604 {.depositor = d.depositor,
-
3605 .id = d.keylet.key,
-
3606 .amount = d.asset(5)});
-
3607 env(tx);
-
3608 env.close();
-
3609 }
+
3599 auto testCase = [&, this](
+
3600 std::uint8_t scale,
+
3601 std::function<void(Env & env, Data data)> test) {
+
3602 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3603 Account const owner{"owner"};
+
3604 Account const issuer{"issuer"};
+
3605 Account const depositor{"depositor"};
+
3606 Vault vault{env};
+
3607 env.fund(XRP(1000), issuer, owner, depositor);
+
3608 env(fset(issuer, asfAllowTrustLineClawback));
+
3609 env.close();
3610
-
3611 {
-
3612 auto tx = d.vault.deposit(
-
3613 {.depositor = d.depositor,
-
3614 .id = d.keylet.key,
-
3615 .amount = d.asset(10)});
-
3616 env(tx, ter{tecPATH_DRY});
-
3617 env.close();
-
3618 }
-
3619 });
-
3620
-
3621 testCase(18, [&, this](Env& env, Data d) {
-
3622 testcase("Scale deposit overflow on total shares");
-
3623
-
3624 {
-
3625 auto tx = d.vault.deposit(
-
3626 {.depositor = d.depositor,
-
3627 .id = d.keylet.key,
-
3628 .amount = d.asset(5)});
-
3629 env(tx);
-
3630 env.close();
-
3631 }
-
3632
-
3633 {
-
3634 auto tx = d.vault.deposit(
-
3635 {.depositor = d.depositor,
-
3636 .id = d.keylet.key,
-
3637 .amount = d.asset(5)});
-
3638 env(tx, ter{tecPATH_DRY});
-
3639 env.close();
-
3640 }
-
3641 });
-
3642
-
3643 testCase(1, [&, this](Env& env, Data d) {
-
3644 testcase("Scale deposit exact");
-
3645
-
3646 auto const start = env.balance(d.depositor, d.assets).number();
-
3647 auto tx = d.vault.deposit(
-
3648 {.depositor = d.depositor,
-
3649 .id = d.keylet.key,
-
3650 .amount = d.asset(1)});
-
3651 env(tx);
-
3652 env.close();
-
3653 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
-
3654 BEAST_EXPECT(
-
3655 env.balance(d.depositor, d.assets) ==
-
3656 STAmount(d.asset, start - 1));
-
3657 });
-
3658
-
3659 testCase(1, [&, this](Env& env, Data d) {
-
3660 testcase("Scale deposit insignificant amount");
-
3661
-
3662 auto tx = d.vault.deposit(
-
3663 {.depositor = d.depositor,
-
3664 .id = d.keylet.key,
-
3665 .amount = STAmount(d.asset, Number(9, -2))});
-
3666 env(tx, ter{tecPRECISION_LOSS});
-
3667 });
-
3668
-
3669 testCase(1, [&, this](Env& env, Data d) {
-
3670 testcase("Scale deposit exact, using full precision");
-
3671
-
3672 auto const start = env.balance(d.depositor, d.assets).number();
-
3673 auto tx = d.vault.deposit(
-
3674 {.depositor = d.depositor,
-
3675 .id = d.keylet.key,
-
3676 .amount = STAmount(d.asset, Number(15, -1))});
-
3677 env(tx);
-
3678 env.close();
-
3679 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
-
3680 BEAST_EXPECT(
-
3681 env.balance(d.depositor, d.assets) ==
-
3682 STAmount(d.asset, start - Number(15, -1)));
-
3683 });
-
3684
-
3685 testCase(1, [&, this](Env& env, Data d) {
-
3686 testcase("Scale deposit exact, truncating from .5");
-
3687
-
3688 auto const start = env.balance(d.depositor, d.assets).number();
-
3689 // Each of the cases below will transfer exactly 1.2 IOU to the
-
3690 // vault and receive 12 shares in exchange
-
3691 {
-
3692 auto tx = d.vault.deposit(
-
3693 {.depositor = d.depositor,
-
3694 .id = d.keylet.key,
-
3695 .amount = STAmount(d.asset, Number(125, -2))});
-
3696 env(tx);
-
3697 env.close();
-
3698 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
-
3699 BEAST_EXPECT(
-
3700 env.balance(d.depositor, d.assets) ==
-
3701 STAmount(d.asset, start - Number(12, -1)));
-
3702 }
-
3703
-
3704 {
-
3705 auto tx = d.vault.deposit(
-
3706 {.depositor = d.depositor,
-
3707 .id = d.keylet.key,
-
3708 .amount = STAmount(d.asset, Number(1201, -3))});
-
3709 env(tx);
-
3710 env.close();
-
3711 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
-
3712 BEAST_EXPECT(
-
3713 env.balance(d.depositor, d.assets) ==
-
3714 STAmount(d.asset, start - Number(24, -1)));
-
3715 }
-
3716
-
3717 {
-
3718 auto tx = d.vault.deposit(
-
3719 {.depositor = d.depositor,
-
3720 .id = d.keylet.key,
-
3721 .amount = STAmount(d.asset, Number(1299, -3))});
-
3722 env(tx);
-
3723 env.close();
-
3724 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
-
3725 BEAST_EXPECT(
-
3726 env.balance(d.depositor, d.assets) ==
-
3727 STAmount(d.asset, start - Number(36, -1)));
-
3728 }
-
3729 });
-
3730
-
3731 testCase(1, [&, this](Env& env, Data d) {
-
3732 testcase("Scale deposit exact, truncating from .01");
-
3733
-
3734 auto const start = env.balance(d.depositor, d.assets).number();
-
3735 // round to 12
-
3736 auto tx = d.vault.deposit(
-
3737 {.depositor = d.depositor,
-
3738 .id = d.keylet.key,
-
3739 .amount = STAmount(d.asset, Number(1201, -3))});
-
3740 env(tx);
-
3741 env.close();
-
3742 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
-
3743 BEAST_EXPECT(
-
3744 env.balance(d.depositor, d.assets) ==
-
3745 STAmount(d.asset, start - Number(12, -1)));
-
3746
-
3747 {
-
3748 // round to 6
-
3749 auto tx = d.vault.deposit(
-
3750 {.depositor = d.depositor,
-
3751 .id = d.keylet.key,
-
3752 .amount = STAmount(d.asset, Number(69, -2))});
-
3753 env(tx);
-
3754 env.close();
-
3755 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
-
3756 BEAST_EXPECT(
-
3757 env.balance(d.depositor, d.assets) ==
-
3758 STAmount(d.asset, start - Number(18, -1)));
-
3759 }
-
3760 });
-
3761
-
3762 testCase(1, [&, this](Env& env, Data d) {
-
3763 testcase("Scale deposit exact, truncating from .99");
-
3764
-
3765 auto const start = env.balance(d.depositor, d.assets).number();
-
3766 // round to 12
-
3767 auto tx = d.vault.deposit(
-
3768 {.depositor = d.depositor,
-
3769 .id = d.keylet.key,
-
3770 .amount = STAmount(d.asset, Number(1299, -3))});
-
3771 env(tx);
-
3772 env.close();
-
3773 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
-
3774 BEAST_EXPECT(
-
3775 env.balance(d.depositor, d.assets) ==
-
3776 STAmount(d.asset, start - Number(12, -1)));
-
3777
-
3778 {
-
3779 // round to 6
-
3780 auto tx = d.vault.deposit(
-
3781 {.depositor = d.depositor,
-
3782 .id = d.keylet.key,
-
3783 .amount = STAmount(d.asset, Number(62, -2))});
-
3784 env(tx);
-
3785 env.close();
-
3786 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
-
3787 BEAST_EXPECT(
-
3788 env.balance(d.depositor, d.assets) ==
-
3789 STAmount(d.asset, start - Number(18, -1)));
-
3790 }
-
3791 });
-
3792
-
3793 testCase(1, [&, this](Env& env, Data d) {
-
3794 // initial setup: deposit 100 IOU, receive 1000 shares
-
3795 auto const start = env.balance(d.depositor, d.assets).number();
-
3796 auto tx = d.vault.deposit(
-
3797 {.depositor = d.depositor,
-
3798 .id = d.keylet.key,
-
3799 .amount = STAmount(d.asset, Number(100, 0))});
-
3800 env(tx);
-
3801 env.close();
-
3802 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
-
3803 BEAST_EXPECT(
-
3804 env.balance(d.depositor, d.assets) ==
-
3805 STAmount(d.asset, start - Number(100, 0)));
-
3806 BEAST_EXPECT(
-
3807 env.balance(d.vaultAccount, d.assets) ==
-
3808 STAmount(d.asset, Number(100, 0)));
-
3809 BEAST_EXPECT(
-
3810 env.balance(d.vaultAccount, d.shares) ==
-
3811 STAmount(d.share, Number(-1000, 0)));
-
3812
-
3813 {
-
3814 testcase("Scale redeem exact");
-
3815 // sharesToAssetsWithdraw:
-
3816 // assets = assetsTotal * (shares / sharesTotal)
-
3817 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
-
3818
-
3819 auto const start = env.balance(d.depositor, d.assets).number();
-
3820 auto tx = d.vault.withdraw(
-
3821 {.depositor = d.depositor,
-
3822 .id = d.keylet.key,
-
3823 .amount = STAmount(d.share, Number(100, 0))});
-
3824 env(tx);
-
3825 env.close();
-
3826 BEAST_EXPECT(
-
3827 env.balance(d.depositor, d.shares) == d.share(900));
-
3828 BEAST_EXPECT(
-
3829 env.balance(d.depositor, d.assets) ==
-
3830 STAmount(d.asset, start + Number(10, 0)));
-
3831 BEAST_EXPECT(
-
3832 env.balance(d.vaultAccount, d.assets) ==
-
3833 STAmount(d.asset, Number(90, 0)));
-
3834 BEAST_EXPECT(
-
3835 env.balance(d.vaultAccount, d.shares) ==
-
3836 STAmount(d.share, Number(-900, 0)));
-
3837 }
-
3838
-
3839 {
-
3840 testcase("Scale redeem with rounding");
-
3841 // sharesToAssetsWithdraw:
-
3842 // assets = assetsTotal * (shares / sharesTotal)
-
3843 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
-
3844
-
3845 auto const start = env.balance(d.depositor, d.assets).number();
-
3846 d.peek([](SLE& vault, auto&) -> bool {
-
3847 vault[sfAssetsAvailable] = Number(1);
-
3848 return true;
-
3849 });
-
3850
-
3851 // Note, this transaction fails first (because of above change
-
3852 // in the open ledger) but then succeeds when the ledger is
-
3853 // closed (because a modification like above is not persistent),
-
3854 // which is why the checks below are expected to pass.
-
3855 auto tx = d.vault.withdraw(
-
3856 {.depositor = d.depositor,
-
3857 .id = d.keylet.key,
-
3858 .amount = STAmount(d.share, Number(25, 0))});
-
3859 env(tx, ter{tecINSUFFICIENT_FUNDS});
-
3860 env.close();
-
3861 BEAST_EXPECT(
-
3862 env.balance(d.depositor, d.shares) == d.share(900 - 25));
-
3863 BEAST_EXPECT(
-
3864 env.balance(d.depositor, d.assets) ==
-
3865 STAmount(d.asset, start + Number(25, -1)));
-
3866 BEAST_EXPECT(
-
3867 env.balance(d.vaultAccount, d.assets) ==
-
3868 STAmount(d.asset, Number(900 - 25, -1)));
-
3869 BEAST_EXPECT(
-
3870 env.balance(d.vaultAccount, d.shares) ==
-
3871 STAmount(d.share, -Number(900 - 25, 0)));
-
3872 }
+
3611 PrettyAsset const asset = issuer["IOU"];
+
3612 env.trust(asset(1000), owner);
+
3613 env.trust(asset(1000), depositor);
+
3614 env(pay(issuer, owner, asset(200)));
+
3615 env(pay(issuer, depositor, asset(200)));
+
3616 env.close();
+
3617
+
3618 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
3619 tx[sfScale] = scale;
+
3620 env(tx);
+
3621
+
3622 auto const [vaultAccount, issuanceId] =
+
3623 [&env](ripple::Keylet keylet) -> std::tuple<Account, MPTID> {
+
3624 auto const vault = env.le(keylet);
+
3625 return {
+
3626 Account("vault", vault->at(sfAccount)),
+
3627 vault->at(sfShareMPTID)};
+
3628 }(keylet);
+
3629 MPTIssue shares(issuanceId);
+
3630 env.memoize(vaultAccount);
+
3631
+
3632 auto const peek =
+
3633 [=, &env, this](std::function<bool(SLE&, SLE&)> fn) -> bool {
+
3634 return env.app().openLedger().modify(
+
3635 [&](OpenView& view, beast::Journal j) -> bool {
+
3636 Sandbox sb(&view, tapNONE);
+
3637 auto vault = sb.peek(keylet::vault(keylet.key));
+
3638 if (!BEAST_EXPECT(vault != nullptr))
+
3639 return false;
+
3640 auto shares = sb.peek(
+
3641 keylet::mptIssuance(vault->at(sfShareMPTID)));
+
3642 if (!BEAST_EXPECT(shares != nullptr))
+
3643 return false;
+
3644 if (fn(*vault, *shares))
+
3645 {
+
3646 sb.update(vault);
+
3647 sb.update(shares);
+
3648 sb.apply(view);
+
3649 return true;
+
3650 }
+
3651 return false;
+
3652 });
+
3653 };
+
3654
+
3655 test(
+
3656 env,
+
3657 {.owner = owner,
+
3658 .issuer = issuer,
+
3659 .depositor = depositor,
+
3660 .vaultAccount = vaultAccount,
+
3661 .shares = shares,
+
3662 .share = PrettyAsset(shares),
+
3663 .vault = vault,
+
3664 .keylet = keylet,
+
3665 .assets = asset.raw().get<Issue>(),
+
3666 .asset = asset,
+
3667 .peek = peek});
+
3668 };
+
3669
+
3670 testCase(18, [&, this](Env& env, Data d) {
+
3671 testcase("Scale deposit overflow on first deposit");
+
3672 auto tx = d.vault.deposit(
+
3673 {.depositor = d.depositor,
+
3674 .id = d.keylet.key,
+
3675 .amount = d.asset(10)});
+
3676 env(tx, ter{tecPATH_DRY});
+
3677 env.close();
+
3678 });
+
3679
+
3680 testCase(18, [&, this](Env& env, Data d) {
+
3681 testcase("Scale deposit overflow on second deposit");
+
3682
+
3683 {
+
3684 auto tx = d.vault.deposit(
+
3685 {.depositor = d.depositor,
+
3686 .id = d.keylet.key,
+
3687 .amount = d.asset(5)});
+
3688 env(tx);
+
3689 env.close();
+
3690 }
+
3691
+
3692 {
+
3693 auto tx = d.vault.deposit(
+
3694 {.depositor = d.depositor,
+
3695 .id = d.keylet.key,
+
3696 .amount = d.asset(10)});
+
3697 env(tx, ter{tecPATH_DRY});
+
3698 env.close();
+
3699 }
+
3700 });
+
3701
+
3702 testCase(18, [&, this](Env& env, Data d) {
+
3703 testcase("Scale deposit overflow on total shares");
+
3704
+
3705 {
+
3706 auto tx = d.vault.deposit(
+
3707 {.depositor = d.depositor,
+
3708 .id = d.keylet.key,
+
3709 .amount = d.asset(5)});
+
3710 env(tx);
+
3711 env.close();
+
3712 }
+
3713
+
3714 {
+
3715 auto tx = d.vault.deposit(
+
3716 {.depositor = d.depositor,
+
3717 .id = d.keylet.key,
+
3718 .amount = d.asset(5)});
+
3719 env(tx, ter{tecPATH_DRY});
+
3720 env.close();
+
3721 }
+
3722 });
+
3723
+
3724 testCase(1, [&, this](Env& env, Data d) {
+
3725 testcase("Scale deposit exact");
+
3726
+
3727 auto const start = env.balance(d.depositor, d.assets).number();
+
3728 auto tx = d.vault.deposit(
+
3729 {.depositor = d.depositor,
+
3730 .id = d.keylet.key,
+
3731 .amount = d.asset(1)});
+
3732 env(tx);
+
3733 env.close();
+
3734 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
+
3735 BEAST_EXPECT(
+
3736 env.balance(d.depositor, d.assets) ==
+
3737 STAmount(d.asset, start - 1));
+
3738 });
+
3739
+
3740 testCase(1, [&, this](Env& env, Data d) {
+
3741 testcase("Scale deposit insignificant amount");
+
3742
+
3743 auto tx = d.vault.deposit(
+
3744 {.depositor = d.depositor,
+
3745 .id = d.keylet.key,
+
3746 .amount = STAmount(d.asset, Number(9, -2))});
+
3747 env(tx, ter{tecPRECISION_LOSS});
+
3748 });
+
3749
+
3750 testCase(1, [&, this](Env& env, Data d) {
+
3751 testcase("Scale deposit exact, using full precision");
+
3752
+
3753 auto const start = env.balance(d.depositor, d.assets).number();
+
3754 auto tx = d.vault.deposit(
+
3755 {.depositor = d.depositor,
+
3756 .id = d.keylet.key,
+
3757 .amount = STAmount(d.asset, Number(15, -1))});
+
3758 env(tx);
+
3759 env.close();
+
3760 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
+
3761 BEAST_EXPECT(
+
3762 env.balance(d.depositor, d.assets) ==
+
3763 STAmount(d.asset, start - Number(15, -1)));
+
3764 });
+
3765
+
3766 testCase(1, [&, this](Env& env, Data d) {
+
3767 testcase("Scale deposit exact, truncating from .5");
+
3768
+
3769 auto const start = env.balance(d.depositor, d.assets).number();
+
3770 // Each of the cases below will transfer exactly 1.2 IOU to the
+
3771 // vault and receive 12 shares in exchange
+
3772 {
+
3773 auto tx = d.vault.deposit(
+
3774 {.depositor = d.depositor,
+
3775 .id = d.keylet.key,
+
3776 .amount = STAmount(d.asset, Number(125, -2))});
+
3777 env(tx);
+
3778 env.close();
+
3779 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
+
3780 BEAST_EXPECT(
+
3781 env.balance(d.depositor, d.assets) ==
+
3782 STAmount(d.asset, start - Number(12, -1)));
+
3783 }
+
3784
+
3785 {
+
3786 auto tx = d.vault.deposit(
+
3787 {.depositor = d.depositor,
+
3788 .id = d.keylet.key,
+
3789 .amount = STAmount(d.asset, Number(1201, -3))});
+
3790 env(tx);
+
3791 env.close();
+
3792 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
+
3793 BEAST_EXPECT(
+
3794 env.balance(d.depositor, d.assets) ==
+
3795 STAmount(d.asset, start - Number(24, -1)));
+
3796 }
+
3797
+
3798 {
+
3799 auto tx = d.vault.deposit(
+
3800 {.depositor = d.depositor,
+
3801 .id = d.keylet.key,
+
3802 .amount = STAmount(d.asset, Number(1299, -3))});
+
3803 env(tx);
+
3804 env.close();
+
3805 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
+
3806 BEAST_EXPECT(
+
3807 env.balance(d.depositor, d.assets) ==
+
3808 STAmount(d.asset, start - Number(36, -1)));
+
3809 }
+
3810 });
+
3811
+
3812 testCase(1, [&, this](Env& env, Data d) {
+
3813 testcase("Scale deposit exact, truncating from .01");
+
3814
+
3815 auto const start = env.balance(d.depositor, d.assets).number();
+
3816 // round to 12
+
3817 auto tx = d.vault.deposit(
+
3818 {.depositor = d.depositor,
+
3819 .id = d.keylet.key,
+
3820 .amount = STAmount(d.asset, Number(1201, -3))});
+
3821 env(tx);
+
3822 env.close();
+
3823 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
+
3824 BEAST_EXPECT(
+
3825 env.balance(d.depositor, d.assets) ==
+
3826 STAmount(d.asset, start - Number(12, -1)));
+
3827
+
3828 {
+
3829 // round to 6
+
3830 auto tx = d.vault.deposit(
+
3831 {.depositor = d.depositor,
+
3832 .id = d.keylet.key,
+
3833 .amount = STAmount(d.asset, Number(69, -2))});
+
3834 env(tx);
+
3835 env.close();
+
3836 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
+
3837 BEAST_EXPECT(
+
3838 env.balance(d.depositor, d.assets) ==
+
3839 STAmount(d.asset, start - Number(18, -1)));
+
3840 }
+
3841 });
+
3842
+
3843 testCase(1, [&, this](Env& env, Data d) {
+
3844 testcase("Scale deposit exact, truncating from .99");
+
3845
+
3846 auto const start = env.balance(d.depositor, d.assets).number();
+
3847 // round to 12
+
3848 auto tx = d.vault.deposit(
+
3849 {.depositor = d.depositor,
+
3850 .id = d.keylet.key,
+
3851 .amount = STAmount(d.asset, Number(1299, -3))});
+
3852 env(tx);
+
3853 env.close();
+
3854 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
+
3855 BEAST_EXPECT(
+
3856 env.balance(d.depositor, d.assets) ==
+
3857 STAmount(d.asset, start - Number(12, -1)));
+
3858
+
3859 {
+
3860 // round to 6
+
3861 auto tx = d.vault.deposit(
+
3862 {.depositor = d.depositor,
+
3863 .id = d.keylet.key,
+
3864 .amount = STAmount(d.asset, Number(62, -2))});
+
3865 env(tx);
+
3866 env.close();
+
3867 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
+
3868 BEAST_EXPECT(
+
3869 env.balance(d.depositor, d.assets) ==
+
3870 STAmount(d.asset, start - Number(18, -1)));
+
3871 }
+
3872 });
3873
-
3874 {
-
3875 testcase("Scale redeem exact");
-
3876 // sharesToAssetsWithdraw:
-
3877 // assets = assetsTotal * (shares / sharesTotal)
-
3878 // assets = 87.5 * 21 / 875 = 87.5 * 0.024 = 2.1
-
3879
-
3880 auto const start = env.balance(d.depositor, d.assets).number();
-
3881
-
3882 tx = d.vault.withdraw(
-
3883 {.depositor = d.depositor,
-
3884 .id = d.keylet.key,
-
3885 .amount = STAmount(d.share, Number(21, 0))});
-
3886 env(tx);
-
3887 env.close();
-
3888 BEAST_EXPECT(
-
3889 env.balance(d.depositor, d.shares) == d.share(875 - 21));
-
3890 BEAST_EXPECT(
-
3891 env.balance(d.depositor, d.assets) ==
-
3892 STAmount(d.asset, start + Number(21, -1)));
-
3893 BEAST_EXPECT(
-
3894 env.balance(d.vaultAccount, d.assets) ==
-
3895 STAmount(d.asset, Number(875 - 21, -1)));
-
3896 BEAST_EXPECT(
-
3897 env.balance(d.vaultAccount, d.shares) ==
-
3898 STAmount(d.share, -Number(875 - 21, 0)));
-
3899 }
-
3900
-
3901 {
-
3902 testcase("Scale redeem rest");
-
3903 auto const rest = env.balance(d.depositor, d.shares).number();
-
3904
-
3905 tx = d.vault.withdraw(
-
3906 {.depositor = d.depositor,
-
3907 .id = d.keylet.key,
-
3908 .amount = STAmount(d.share, rest)});
-
3909 env(tx);
-
3910 env.close();
-
3911 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
+
3874 testCase(1, [&, this](Env& env, Data d) {
+
3875 // initial setup: deposit 100 IOU, receive 1000 shares
+
3876 auto const start = env.balance(d.depositor, d.assets).number();
+
3877 auto tx = d.vault.deposit(
+
3878 {.depositor = d.depositor,
+
3879 .id = d.keylet.key,
+
3880 .amount = STAmount(d.asset, Number(100, 0))});
+
3881 env(tx);
+
3882 env.close();
+
3883 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
+
3884 BEAST_EXPECT(
+
3885 env.balance(d.depositor, d.assets) ==
+
3886 STAmount(d.asset, start - Number(100, 0)));
+
3887 BEAST_EXPECT(
+
3888 env.balance(d.vaultAccount, d.assets) ==
+
3889 STAmount(d.asset, Number(100, 0)));
+
3890 BEAST_EXPECT(
+
3891 env.balance(d.vaultAccount, d.shares) ==
+
3892 STAmount(d.share, Number(-1000, 0)));
+
3893
+
3894 {
+
3895 testcase("Scale redeem exact");
+
3896 // sharesToAssetsWithdraw:
+
3897 // assets = assetsTotal * (shares / sharesTotal)
+
3898 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
+
3899
+
3900 auto const start = env.balance(d.depositor, d.assets).number();
+
3901 auto tx = d.vault.withdraw(
+
3902 {.depositor = d.depositor,
+
3903 .id = d.keylet.key,
+
3904 .amount = STAmount(d.share, Number(100, 0))});
+
3905 env(tx);
+
3906 env.close();
+
3907 BEAST_EXPECT(
+
3908 env.balance(d.depositor, d.shares) == d.share(900));
+
3909 BEAST_EXPECT(
+
3910 env.balance(d.depositor, d.assets) ==
+
3911 STAmount(d.asset, start + Number(10, 0)));
3912 BEAST_EXPECT(
-
3913 env.balance(d.vaultAccount, d.assets).number() == 0);
-
3914 BEAST_EXPECT(
-
3915 env.balance(d.vaultAccount, d.shares).number() == 0);
-
3916 }
-
3917 });
-
3918
-
3919 testCase(18, [&, this](Env& env, Data d) {
-
3920 testcase("Scale withdraw overflow");
-
3921
-
3922 {
-
3923 auto tx = d.vault.deposit(
-
3924 {.depositor = d.depositor,
-
3925 .id = d.keylet.key,
-
3926 .amount = d.asset(5)});
-
3927 env(tx);
-
3928 env.close();
-
3929 }
-
3930
-
3931 {
-
3932 auto tx = d.vault.withdraw(
-
3933 {.depositor = d.depositor,
-
3934 .id = d.keylet.key,
-
3935 .amount = STAmount(d.asset, Number(10, 0))});
-
3936 env(tx, ter{tecPATH_DRY});
-
3937 env.close();
-
3938 }
-
3939 });
-
3940
-
3941 testCase(1, [&, this](Env& env, Data d) {
-
3942 // initial setup: deposit 100 IOU, receive 1000 shares
-
3943 auto const start = env.balance(d.depositor, d.assets).number();
-
3944 auto tx = d.vault.deposit(
-
3945 {.depositor = d.depositor,
-
3946 .id = d.keylet.key,
-
3947 .amount = STAmount(d.asset, Number(100, 0))});
-
3948 env(tx);
-
3949 env.close();
-
3950 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
-
3951 BEAST_EXPECT(
-
3952 env.balance(d.depositor, d.assets) ==
-
3953 STAmount(d.asset, start - Number(100, 0)));
-
3954 BEAST_EXPECT(
-
3955 env.balance(d.vaultAccount, d.assets) ==
-
3956 STAmount(d.asset, Number(100, 0)));
-
3957 BEAST_EXPECT(
-
3958 env.balance(d.vaultAccount, d.shares) ==
-
3959 STAmount(d.share, Number(-1000, 0)));
+
3913 env.balance(d.vaultAccount, d.assets) ==
+
3914 STAmount(d.asset, Number(90, 0)));
+
3915 BEAST_EXPECT(
+
3916 env.balance(d.vaultAccount, d.shares) ==
+
3917 STAmount(d.share, Number(-900, 0)));
+
3918 }
+
3919
+
3920 {
+
3921 testcase("Scale redeem with rounding");
+
3922 // sharesToAssetsWithdraw:
+
3923 // assets = assetsTotal * (shares / sharesTotal)
+
3924 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
+
3925
+
3926 auto const start = env.balance(d.depositor, d.assets).number();
+
3927 d.peek([](SLE& vault, auto&) -> bool {
+
3928 vault[sfAssetsAvailable] = Number(1);
+
3929 return true;
+
3930 });
+
3931
+
3932 // Note, this transaction fails first (because of above change
+
3933 // in the open ledger) but then succeeds when the ledger is
+
3934 // closed (because a modification like above is not persistent),
+
3935 // which is why the checks below are expected to pass.
+
3936 auto tx = d.vault.withdraw(
+
3937 {.depositor = d.depositor,
+
3938 .id = d.keylet.key,
+
3939 .amount = STAmount(d.share, Number(25, 0))});
+
3940 env(tx, ter{tecINSUFFICIENT_FUNDS});
+
3941 env.close();
+
3942 BEAST_EXPECT(
+
3943 env.balance(d.depositor, d.shares) == d.share(900 - 25));
+
3944 BEAST_EXPECT(
+
3945 env.balance(d.depositor, d.assets) ==
+
3946 STAmount(d.asset, start + Number(25, -1)));
+
3947 BEAST_EXPECT(
+
3948 env.balance(d.vaultAccount, d.assets) ==
+
3949 STAmount(d.asset, Number(900 - 25, -1)));
+
3950 BEAST_EXPECT(
+
3951 env.balance(d.vaultAccount, d.shares) ==
+
3952 STAmount(d.share, -Number(900 - 25, 0)));
+
3953 }
+
3954
+
3955 {
+
3956 testcase("Scale redeem exact");
+
3957 // sharesToAssetsWithdraw:
+
3958 // assets = assetsTotal * (shares / sharesTotal)
+
3959 // assets = 87.5 * 21 / 875 = 87.5 * 0.024 = 2.1
3960
-
3961 {
-
3962 testcase("Scale withdraw exact");
-
3963 // assetsToSharesWithdraw:
-
3964 // shares = sharesTotal * (assets / assetsTotal)
-
3965 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
-
3966 // sharesToAssetsWithdraw:
-
3967 // assets = assetsTotal * (shares / sharesTotal)
-
3968 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
-
3969
-
3970 auto const start = env.balance(d.depositor, d.assets).number();
-
3971 auto tx = d.vault.withdraw(
-
3972 {.depositor = d.depositor,
-
3973 .id = d.keylet.key,
-
3974 .amount = STAmount(d.asset, Number(10, 0))});
-
3975 env(tx);
-
3976 env.close();
+
3961 auto const start = env.balance(d.depositor, d.assets).number();
+
3962
+
3963 tx = d.vault.withdraw(
+
3964 {.depositor = d.depositor,
+
3965 .id = d.keylet.key,
+
3966 .amount = STAmount(d.share, Number(21, 0))});
+
3967 env(tx);
+
3968 env.close();
+
3969 BEAST_EXPECT(
+
3970 env.balance(d.depositor, d.shares) == d.share(875 - 21));
+
3971 BEAST_EXPECT(
+
3972 env.balance(d.depositor, d.assets) ==
+
3973 STAmount(d.asset, start + Number(21, -1)));
+
3974 BEAST_EXPECT(
+
3975 env.balance(d.vaultAccount, d.assets) ==
+
3976 STAmount(d.asset, Number(875 - 21, -1)));
3977 BEAST_EXPECT(
-
3978 env.balance(d.depositor, d.shares) == d.share(900));
-
3979 BEAST_EXPECT(
-
3980 env.balance(d.depositor, d.assets) ==
-
3981 STAmount(d.asset, start + Number(10, 0)));
-
3982 BEAST_EXPECT(
-
3983 env.balance(d.vaultAccount, d.assets) ==
-
3984 STAmount(d.asset, Number(90, 0)));
-
3985 BEAST_EXPECT(
-
3986 env.balance(d.vaultAccount, d.shares) ==
-
3987 STAmount(d.share, Number(-900, 0)));
-
3988 }
-
3989
-
3990 {
-
3991 testcase("Scale withdraw insignificant amount");
-
3992 auto tx = d.vault.withdraw(
-
3993 {.depositor = d.depositor,
-
3994 .id = d.keylet.key,
-
3995 .amount = STAmount(d.asset, Number(4, -2))});
-
3996 env(tx, ter{tecPRECISION_LOSS});
+
3978 env.balance(d.vaultAccount, d.shares) ==
+
3979 STAmount(d.share, -Number(875 - 21, 0)));
+
3980 }
+
3981
+
3982 {
+
3983 testcase("Scale redeem rest");
+
3984 auto const rest = env.balance(d.depositor, d.shares).number();
+
3985
+
3986 tx = d.vault.withdraw(
+
3987 {.depositor = d.depositor,
+
3988 .id = d.keylet.key,
+
3989 .amount = STAmount(d.share, rest)});
+
3990 env(tx);
+
3991 env.close();
+
3992 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
+
3993 BEAST_EXPECT(
+
3994 env.balance(d.vaultAccount, d.assets).number() == 0);
+
3995 BEAST_EXPECT(
+
3996 env.balance(d.vaultAccount, d.shares).number() == 0);
3997 }
-
3998
-
3999 {
-
4000 testcase("Scale withdraw with rounding assets");
-
4001 // assetsToSharesWithdraw:
-
4002 // shares = sharesTotal * (assets / assetsTotal)
-
4003 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
-
4004 // sharesToAssetsWithdraw:
-
4005 // assets = assetsTotal * (shares / sharesTotal)
-
4006 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
-
4007
-
4008 auto const start = env.balance(d.depositor, d.assets).number();
-
4009 d.peek([](SLE& vault, auto&) -> bool {
-
4010 vault[sfAssetsAvailable] = Number(1);
-
4011 return true;
-
4012 });
-
4013
-
4014 // Note, this transaction fails first (because of above change
-
4015 // in the open ledger) but then succeeds when the ledger is
-
4016 // closed (because a modification like above is not persistent),
-
4017 // which is why the checks below are expected to pass.
-
4018 auto tx = d.vault.withdraw(
-
4019 {.depositor = d.depositor,
-
4020 .id = d.keylet.key,
-
4021 .amount = STAmount(d.asset, Number(25, -1))});
-
4022 env(tx, ter{tecINSUFFICIENT_FUNDS});
-
4023 env.close();
-
4024 BEAST_EXPECT(
-
4025 env.balance(d.depositor, d.shares) == d.share(900 - 25));
-
4026 BEAST_EXPECT(
-
4027 env.balance(d.depositor, d.assets) ==
-
4028 STAmount(d.asset, start + Number(25, -1)));
-
4029 BEAST_EXPECT(
-
4030 env.balance(d.vaultAccount, d.assets) ==
-
4031 STAmount(d.asset, Number(900 - 25, -1)));
-
4032 BEAST_EXPECT(
-
4033 env.balance(d.vaultAccount, d.shares) ==
-
4034 STAmount(d.share, -Number(900 - 25, 0)));
-
4035 }
-
4036
-
4037 {
-
4038 testcase("Scale withdraw with rounding shares up");
-
4039 // assetsToSharesWithdraw:
-
4040 // shares = sharesTotal * (assets / assetsTotal)
-
4041 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
-
4042 // sharesToAssetsWithdraw:
-
4043 // assets = assetsTotal * (shares / sharesTotal)
-
4044 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
-
4045
-
4046 auto const start = env.balance(d.depositor, d.assets).number();
-
4047 auto tx = d.vault.withdraw(
-
4048 {.depositor = d.depositor,
-
4049 .id = d.keylet.key,
-
4050 .amount = STAmount(d.asset, Number(375, -2))});
-
4051 env(tx);
-
4052 env.close();
-
4053 BEAST_EXPECT(
-
4054 env.balance(d.depositor, d.shares) == d.share(875 - 38));
-
4055 BEAST_EXPECT(
-
4056 env.balance(d.depositor, d.assets) ==
-
4057 STAmount(d.asset, start + Number(38, -1)));
+
3998 });
+
3999
+
4000 testCase(18, [&, this](Env& env, Data d) {
+
4001 testcase("Scale withdraw overflow");
+
4002
+
4003 {
+
4004 auto tx = d.vault.deposit(
+
4005 {.depositor = d.depositor,
+
4006 .id = d.keylet.key,
+
4007 .amount = d.asset(5)});
+
4008 env(tx);
+
4009 env.close();
+
4010 }
+
4011
+
4012 {
+
4013 auto tx = d.vault.withdraw(
+
4014 {.depositor = d.depositor,
+
4015 .id = d.keylet.key,
+
4016 .amount = STAmount(d.asset, Number(10, 0))});
+
4017 env(tx, ter{tecPATH_DRY});
+
4018 env.close();
+
4019 }
+
4020 });
+
4021
+
4022 testCase(1, [&, this](Env& env, Data d) {
+
4023 // initial setup: deposit 100 IOU, receive 1000 shares
+
4024 auto const start = env.balance(d.depositor, d.assets).number();
+
4025 auto tx = d.vault.deposit(
+
4026 {.depositor = d.depositor,
+
4027 .id = d.keylet.key,
+
4028 .amount = STAmount(d.asset, Number(100, 0))});
+
4029 env(tx);
+
4030 env.close();
+
4031 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
+
4032 BEAST_EXPECT(
+
4033 env.balance(d.depositor, d.assets) ==
+
4034 STAmount(d.asset, start - Number(100, 0)));
+
4035 BEAST_EXPECT(
+
4036 env.balance(d.vaultAccount, d.assets) ==
+
4037 STAmount(d.asset, Number(100, 0)));
+
4038 BEAST_EXPECT(
+
4039 env.balance(d.vaultAccount, d.shares) ==
+
4040 STAmount(d.share, Number(-1000, 0)));
+
4041
+
4042 {
+
4043 testcase("Scale withdraw exact");
+
4044 // assetsToSharesWithdraw:
+
4045 // shares = sharesTotal * (assets / assetsTotal)
+
4046 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
+
4047 // sharesToAssetsWithdraw:
+
4048 // assets = assetsTotal * (shares / sharesTotal)
+
4049 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
+
4050
+
4051 auto const start = env.balance(d.depositor, d.assets).number();
+
4052 auto tx = d.vault.withdraw(
+
4053 {.depositor = d.depositor,
+
4054 .id = d.keylet.key,
+
4055 .amount = STAmount(d.asset, Number(10, 0))});
+
4056 env(tx);
+
4057 env.close();
4058 BEAST_EXPECT(
-
4059 env.balance(d.vaultAccount, d.assets) ==
-
4060 STAmount(d.asset, Number(875 - 38, -1)));
-
4061 BEAST_EXPECT(
-
4062 env.balance(d.vaultAccount, d.shares) ==
-
4063 STAmount(d.share, -Number(875 - 38, 0)));
-
4064 }
-
4065
-
4066 {
-
4067 testcase("Scale withdraw with rounding shares down");
-
4068 // assetsToSharesWithdraw:
-
4069 // shares = sharesTotal * (assets / assetsTotal)
-
4070 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
-
4071 // sharesToAssetsWithdraw:
-
4072 // assets = assetsTotal * (shares / sharesTotal)
-
4073 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
-
4074
-
4075 auto const start = env.balance(d.depositor, d.assets).number();
-
4076 auto tx = d.vault.withdraw(
-
4077 {.depositor = d.depositor,
-
4078 .id = d.keylet.key,
-
4079 .amount = STAmount(d.asset, Number(372, -2))});
-
4080 env(tx);
-
4081 env.close();
-
4082 BEAST_EXPECT(
-
4083 env.balance(d.depositor, d.shares) == d.share(837 - 37));
-
4084 BEAST_EXPECT(
-
4085 env.balance(d.depositor, d.assets) ==
-
4086 STAmount(d.asset, start + Number(37, -1)));
-
4087 BEAST_EXPECT(
-
4088 env.balance(d.vaultAccount, d.assets) ==
-
4089 STAmount(d.asset, Number(837 - 37, -1)));
-
4090 BEAST_EXPECT(
-
4091 env.balance(d.vaultAccount, d.shares) ==
-
4092 STAmount(d.share, -Number(837 - 37, 0)));
-
4093 }
+
4059 env.balance(d.depositor, d.shares) == d.share(900));
+
4060 BEAST_EXPECT(
+
4061 env.balance(d.depositor, d.assets) ==
+
4062 STAmount(d.asset, start + Number(10, 0)));
+
4063 BEAST_EXPECT(
+
4064 env.balance(d.vaultAccount, d.assets) ==
+
4065 STAmount(d.asset, Number(90, 0)));
+
4066 BEAST_EXPECT(
+
4067 env.balance(d.vaultAccount, d.shares) ==
+
4068 STAmount(d.share, Number(-900, 0)));
+
4069 }
+
4070
+
4071 {
+
4072 testcase("Scale withdraw insignificant amount");
+
4073 auto tx = d.vault.withdraw(
+
4074 {.depositor = d.depositor,
+
4075 .id = d.keylet.key,
+
4076 .amount = STAmount(d.asset, Number(4, -2))});
+
4077 env(tx, ter{tecPRECISION_LOSS});
+
4078 }
+
4079
+
4080 {
+
4081 testcase("Scale withdraw with rounding assets");
+
4082 // assetsToSharesWithdraw:
+
4083 // shares = sharesTotal * (assets / assetsTotal)
+
4084 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
+
4085 // sharesToAssetsWithdraw:
+
4086 // assets = assetsTotal * (shares / sharesTotal)
+
4087 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
+
4088
+
4089 auto const start = env.balance(d.depositor, d.assets).number();
+
4090 d.peek([](SLE& vault, auto&) -> bool {
+
4091 vault[sfAssetsAvailable] = Number(1);
+
4092 return true;
+
4093 });
4094
-
4095 {
-
4096 testcase("Scale withdraw tiny amount");
-
4097
-
4098 auto const start = env.balance(d.depositor, d.assets).number();
+
4095 // Note, this transaction fails first (because of above change
+
4096 // in the open ledger) but then succeeds when the ledger is
+
4097 // closed (because a modification like above is not persistent),
+
4098 // which is why the checks below are expected to pass.
4099 auto tx = d.vault.withdraw(
4100 {.depositor = d.depositor,
4101 .id = d.keylet.key,
-
4102 .amount = STAmount(d.asset, Number(9, -2))});
-
4103 env(tx);
+
4102 .amount = STAmount(d.asset, Number(25, -1))});
+
4103 env(tx, ter{tecINSUFFICIENT_FUNDS});
4104 env.close();
4105 BEAST_EXPECT(
-
4106 env.balance(d.depositor, d.shares) == d.share(800 - 1));
+
4106 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4107 BEAST_EXPECT(
4108 env.balance(d.depositor, d.assets) ==
-
4109 STAmount(d.asset, start + Number(1, -1)));
+
4109 STAmount(d.asset, start + Number(25, -1)));
4110 BEAST_EXPECT(
4111 env.balance(d.vaultAccount, d.assets) ==
-
4112 STAmount(d.asset, Number(800 - 1, -1)));
+
4112 STAmount(d.asset, Number(900 - 25, -1)));
4113 BEAST_EXPECT(
4114 env.balance(d.vaultAccount, d.shares) ==
-
4115 STAmount(d.share, -Number(800 - 1, 0)));
+
4115 STAmount(d.share, -Number(900 - 25, 0)));
4116 }
4117
4118 {
-
4119 testcase("Scale withdraw rest");
-
4120 auto const rest =
-
4121 env.balance(d.vaultAccount, d.assets).number();
-
4122
-
4123 tx = d.vault.withdraw(
-
4124 {.depositor = d.depositor,
-
4125 .id = d.keylet.key,
-
4126 .amount = STAmount(d.asset, rest)});
-
4127 env(tx);
-
4128 env.close();
-
4129 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
-
4130 BEAST_EXPECT(
-
4131 env.balance(d.vaultAccount, d.assets).number() == 0);
-
4132 BEAST_EXPECT(
-
4133 env.balance(d.vaultAccount, d.shares).number() == 0);
-
4134 }
-
4135 });
-
4136
-
4137 testCase(18, [&, this](Env& env, Data d) {
-
4138 testcase("Scale clawback overflow");
-
4139
-
4140 {
-
4141 auto tx = d.vault.deposit(
-
4142 {.depositor = d.depositor,
-
4143 .id = d.keylet.key,
-
4144 .amount = d.asset(5)});
-
4145 env(tx);
-
4146 env.close();
-
4147 }
-
4148
-
4149 {
-
4150 auto tx = d.vault.clawback(
-
4151 {.issuer = d.issuer,
-
4152 .id = d.keylet.key,
-
4153 .holder = d.depositor,
-
4154 .amount = STAmount(d.asset, Number(10, 0))});
-
4155 env(tx, ter{tecPATH_DRY});
-
4156 env.close();
-
4157 }
-
4158 });
-
4159
-
4160 testCase(1, [&, this](Env& env, Data d) {
-
4161 // initial setup: deposit 100 IOU, receive 1000 shares
-
4162 auto const start = env.balance(d.depositor, d.assets).number();
-
4163 auto tx = d.vault.deposit(
-
4164 {.depositor = d.depositor,
-
4165 .id = d.keylet.key,
-
4166 .amount = STAmount(d.asset, Number(100, 0))});
-
4167 env(tx);
-
4168 env.close();
-
4169 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
-
4170 BEAST_EXPECT(
-
4171 env.balance(d.depositor, d.assets) ==
-
4172 STAmount(d.asset, start - Number(100, 0)));
-
4173 BEAST_EXPECT(
-
4174 env.balance(d.vaultAccount, d.assets) ==
-
4175 STAmount(d.asset, Number(100, 0)));
-
4176 BEAST_EXPECT(
-
4177 env.balance(d.vaultAccount, d.shares) ==
-
4178 STAmount(d.share, -Number(1000, 0)));
-
4179 {
-
4180 testcase("Scale clawback exact");
-
4181 // assetsToSharesWithdraw:
-
4182 // shares = sharesTotal * (assets / assetsTotal)
-
4183 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
-
4184 // sharesToAssetsWithdraw:
-
4185 // assets = assetsTotal * (shares / sharesTotal)
-
4186 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
-
4187
-
4188 auto const start = env.balance(d.depositor, d.assets).number();
-
4189 auto tx = d.vault.clawback(
-
4190 {.issuer = d.issuer,
-
4191 .id = d.keylet.key,
-
4192 .holder = d.depositor,
-
4193 .amount = STAmount(d.asset, Number(10, 0))});
-
4194 env(tx);
-
4195 env.close();
-
4196 BEAST_EXPECT(
-
4197 env.balance(d.depositor, d.shares) == d.share(900));
-
4198 BEAST_EXPECT(
-
4199 env.balance(d.depositor, d.assets) ==
-
4200 STAmount(d.asset, start));
-
4201 BEAST_EXPECT(
-
4202 env.balance(d.vaultAccount, d.assets) ==
-
4203 STAmount(d.asset, Number(90, 0)));
-
4204 BEAST_EXPECT(
-
4205 env.balance(d.vaultAccount, d.shares) ==
-
4206 STAmount(d.share, -Number(900, 0)));
-
4207 }
-
4208
-
4209 {
-
4210 testcase("Scale clawback insignificant amount");
-
4211 auto tx = d.vault.clawback(
-
4212 {.issuer = d.issuer,
-
4213 .id = d.keylet.key,
-
4214 .holder = d.depositor,
-
4215 .amount = STAmount(d.asset, Number(4, -2))});
-
4216 env(tx, ter{tecPRECISION_LOSS});
-
4217 }
-
4218
-
4219 {
-
4220 testcase("Scale clawback with rounding assets");
-
4221 // assetsToSharesWithdraw:
-
4222 // shares = sharesTotal * (assets / assetsTotal)
-
4223 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
-
4224 // sharesToAssetsWithdraw:
-
4225 // assets = assetsTotal * (shares / sharesTotal)
-
4226 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
-
4227
-
4228 auto const start = env.balance(d.depositor, d.assets).number();
-
4229 auto tx = d.vault.clawback(
-
4230 {.issuer = d.issuer,
-
4231 .id = d.keylet.key,
-
4232 .holder = d.depositor,
-
4233 .amount = STAmount(d.asset, Number(25, -1))});
-
4234 env(tx);
-
4235 env.close();
-
4236 BEAST_EXPECT(
-
4237 env.balance(d.depositor, d.shares) == d.share(900 - 25));
-
4238 BEAST_EXPECT(
-
4239 env.balance(d.depositor, d.assets) ==
-
4240 STAmount(d.asset, start));
-
4241 BEAST_EXPECT(
-
4242 env.balance(d.vaultAccount, d.assets) ==
-
4243 STAmount(d.asset, Number(900 - 25, -1)));
-
4244 BEAST_EXPECT(
-
4245 env.balance(d.vaultAccount, d.shares) ==
-
4246 STAmount(d.share, -Number(900 - 25, 0)));
-
4247 }
-
4248
-
4249 {
-
4250 testcase("Scale clawback with rounding shares up");
-
4251 // assetsToSharesWithdraw:
-
4252 // shares = sharesTotal * (assets / assetsTotal)
-
4253 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
-
4254 // sharesToAssetsWithdraw:
-
4255 // assets = assetsTotal * (shares / sharesTotal)
-
4256 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
-
4257
-
4258 auto const start = env.balance(d.depositor, d.assets).number();
-
4259 auto tx = d.vault.clawback(
-
4260 {.issuer = d.issuer,
-
4261 .id = d.keylet.key,
-
4262 .holder = d.depositor,
-
4263 .amount = STAmount(d.asset, Number(375, -2))});
-
4264 env(tx);
-
4265 env.close();
-
4266 BEAST_EXPECT(
-
4267 env.balance(d.depositor, d.shares) == d.share(875 - 38));
-
4268 BEAST_EXPECT(
-
4269 env.balance(d.depositor, d.assets) ==
-
4270 STAmount(d.asset, start));
-
4271 BEAST_EXPECT(
-
4272 env.balance(d.vaultAccount, d.assets) ==
-
4273 STAmount(d.asset, Number(875 - 38, -1)));
-
4274 BEAST_EXPECT(
-
4275 env.balance(d.vaultAccount, d.shares) ==
-
4276 STAmount(d.share, -Number(875 - 38, 0)));
-
4277 }
-
4278
-
4279 {
-
4280 testcase("Scale clawback with rounding shares down");
-
4281 // assetsToSharesWithdraw:
-
4282 // shares = sharesTotal * (assets / assetsTotal)
-
4283 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
-
4284 // sharesToAssetsWithdraw:
-
4285 // assets = assetsTotal * (shares / sharesTotal)
-
4286 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
-
4287
-
4288 auto const start = env.balance(d.depositor, d.assets).number();
-
4289 auto tx = d.vault.clawback(
-
4290 {.issuer = d.issuer,
-
4291 .id = d.keylet.key,
-
4292 .holder = d.depositor,
-
4293 .amount = STAmount(d.asset, Number(372, -2))});
-
4294 env(tx);
-
4295 env.close();
-
4296 BEAST_EXPECT(
-
4297 env.balance(d.depositor, d.shares) == d.share(837 - 37));
-
4298 BEAST_EXPECT(
-
4299 env.balance(d.depositor, d.assets) ==
-
4300 STAmount(d.asset, start));
-
4301 BEAST_EXPECT(
-
4302 env.balance(d.vaultAccount, d.assets) ==
-
4303 STAmount(d.asset, Number(837 - 37, -1)));
-
4304 BEAST_EXPECT(
-
4305 env.balance(d.vaultAccount, d.shares) ==
-
4306 STAmount(d.share, -Number(837 - 37, 0)));
-
4307 }
+
4119 testcase("Scale withdraw with rounding shares up");
+
4120 // assetsToSharesWithdraw:
+
4121 // shares = sharesTotal * (assets / assetsTotal)
+
4122 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
+
4123 // sharesToAssetsWithdraw:
+
4124 // assets = assetsTotal * (shares / sharesTotal)
+
4125 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
+
4126
+
4127 auto const start = env.balance(d.depositor, d.assets).number();
+
4128 auto tx = d.vault.withdraw(
+
4129 {.depositor = d.depositor,
+
4130 .id = d.keylet.key,
+
4131 .amount = STAmount(d.asset, Number(375, -2))});
+
4132 env(tx);
+
4133 env.close();
+
4134 BEAST_EXPECT(
+
4135 env.balance(d.depositor, d.shares) == d.share(875 - 38));
+
4136 BEAST_EXPECT(
+
4137 env.balance(d.depositor, d.assets) ==
+
4138 STAmount(d.asset, start + Number(38, -1)));
+
4139 BEAST_EXPECT(
+
4140 env.balance(d.vaultAccount, d.assets) ==
+
4141 STAmount(d.asset, Number(875 - 38, -1)));
+
4142 BEAST_EXPECT(
+
4143 env.balance(d.vaultAccount, d.shares) ==
+
4144 STAmount(d.share, -Number(875 - 38, 0)));
+
4145 }
+
4146
+
4147 {
+
4148 testcase("Scale withdraw with rounding shares down");
+
4149 // assetsToSharesWithdraw:
+
4150 // shares = sharesTotal * (assets / assetsTotal)
+
4151 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
+
4152 // sharesToAssetsWithdraw:
+
4153 // assets = assetsTotal * (shares / sharesTotal)
+
4154 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
+
4155
+
4156 auto const start = env.balance(d.depositor, d.assets).number();
+
4157 auto tx = d.vault.withdraw(
+
4158 {.depositor = d.depositor,
+
4159 .id = d.keylet.key,
+
4160 .amount = STAmount(d.asset, Number(372, -2))});
+
4161 env(tx);
+
4162 env.close();
+
4163 BEAST_EXPECT(
+
4164 env.balance(d.depositor, d.shares) == d.share(837 - 37));
+
4165 BEAST_EXPECT(
+
4166 env.balance(d.depositor, d.assets) ==
+
4167 STAmount(d.asset, start + Number(37, -1)));
+
4168 BEAST_EXPECT(
+
4169 env.balance(d.vaultAccount, d.assets) ==
+
4170 STAmount(d.asset, Number(837 - 37, -1)));
+
4171 BEAST_EXPECT(
+
4172 env.balance(d.vaultAccount, d.shares) ==
+
4173 STAmount(d.share, -Number(837 - 37, 0)));
+
4174 }
+
4175
+
4176 {
+
4177 testcase("Scale withdraw tiny amount");
+
4178
+
4179 auto const start = env.balance(d.depositor, d.assets).number();
+
4180 auto tx = d.vault.withdraw(
+
4181 {.depositor = d.depositor,
+
4182 .id = d.keylet.key,
+
4183 .amount = STAmount(d.asset, Number(9, -2))});
+
4184 env(tx);
+
4185 env.close();
+
4186 BEAST_EXPECT(
+
4187 env.balance(d.depositor, d.shares) == d.share(800 - 1));
+
4188 BEAST_EXPECT(
+
4189 env.balance(d.depositor, d.assets) ==
+
4190 STAmount(d.asset, start + Number(1, -1)));
+
4191 BEAST_EXPECT(
+
4192 env.balance(d.vaultAccount, d.assets) ==
+
4193 STAmount(d.asset, Number(800 - 1, -1)));
+
4194 BEAST_EXPECT(
+
4195 env.balance(d.vaultAccount, d.shares) ==
+
4196 STAmount(d.share, -Number(800 - 1, 0)));
+
4197 }
+
4198
+
4199 {
+
4200 testcase("Scale withdraw rest");
+
4201 auto const rest =
+
4202 env.balance(d.vaultAccount, d.assets).number();
+
4203
+
4204 tx = d.vault.withdraw(
+
4205 {.depositor = d.depositor,
+
4206 .id = d.keylet.key,
+
4207 .amount = STAmount(d.asset, rest)});
+
4208 env(tx);
+
4209 env.close();
+
4210 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
+
4211 BEAST_EXPECT(
+
4212 env.balance(d.vaultAccount, d.assets).number() == 0);
+
4213 BEAST_EXPECT(
+
4214 env.balance(d.vaultAccount, d.shares).number() == 0);
+
4215 }
+
4216 });
+
4217
+
4218 testCase(18, [&, this](Env& env, Data d) {
+
4219 testcase("Scale clawback overflow");
+
4220
+
4221 {
+
4222 auto tx = d.vault.deposit(
+
4223 {.depositor = d.depositor,
+
4224 .id = d.keylet.key,
+
4225 .amount = d.asset(5)});
+
4226 env(tx);
+
4227 env.close();
+
4228 }
+
4229
+
4230 {
+
4231 auto tx = d.vault.clawback(
+
4232 {.issuer = d.issuer,
+
4233 .id = d.keylet.key,
+
4234 .holder = d.depositor,
+
4235 .amount = STAmount(d.asset, Number(10, 0))});
+
4236 env(tx, ter{tecPATH_DRY});
+
4237 env.close();
+
4238 }
+
4239 });
+
4240
+
4241 testCase(1, [&, this](Env& env, Data d) {
+
4242 // initial setup: deposit 100 IOU, receive 1000 shares
+
4243 auto const start = env.balance(d.depositor, d.assets).number();
+
4244 auto tx = d.vault.deposit(
+
4245 {.depositor = d.depositor,
+
4246 .id = d.keylet.key,
+
4247 .amount = STAmount(d.asset, Number(100, 0))});
+
4248 env(tx);
+
4249 env.close();
+
4250 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
+
4251 BEAST_EXPECT(
+
4252 env.balance(d.depositor, d.assets) ==
+
4253 STAmount(d.asset, start - Number(100, 0)));
+
4254 BEAST_EXPECT(
+
4255 env.balance(d.vaultAccount, d.assets) ==
+
4256 STAmount(d.asset, Number(100, 0)));
+
4257 BEAST_EXPECT(
+
4258 env.balance(d.vaultAccount, d.shares) ==
+
4259 STAmount(d.share, -Number(1000, 0)));
+
4260 {
+
4261 testcase("Scale clawback exact");
+
4262 // assetsToSharesWithdraw:
+
4263 // shares = sharesTotal * (assets / assetsTotal)
+
4264 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
+
4265 // sharesToAssetsWithdraw:
+
4266 // assets = assetsTotal * (shares / sharesTotal)
+
4267 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
+
4268
+
4269 auto const start = env.balance(d.depositor, d.assets).number();
+
4270 auto tx = d.vault.clawback(
+
4271 {.issuer = d.issuer,
+
4272 .id = d.keylet.key,
+
4273 .holder = d.depositor,
+
4274 .amount = STAmount(d.asset, Number(10, 0))});
+
4275 env(tx);
+
4276 env.close();
+
4277 BEAST_EXPECT(
+
4278 env.balance(d.depositor, d.shares) == d.share(900));
+
4279 BEAST_EXPECT(
+
4280 env.balance(d.depositor, d.assets) ==
+
4281 STAmount(d.asset, start));
+
4282 BEAST_EXPECT(
+
4283 env.balance(d.vaultAccount, d.assets) ==
+
4284 STAmount(d.asset, Number(90, 0)));
+
4285 BEAST_EXPECT(
+
4286 env.balance(d.vaultAccount, d.shares) ==
+
4287 STAmount(d.share, -Number(900, 0)));
+
4288 }
+
4289
+
4290 {
+
4291 testcase("Scale clawback insignificant amount");
+
4292 auto tx = d.vault.clawback(
+
4293 {.issuer = d.issuer,
+
4294 .id = d.keylet.key,
+
4295 .holder = d.depositor,
+
4296 .amount = STAmount(d.asset, Number(4, -2))});
+
4297 env(tx, ter{tecPRECISION_LOSS});
+
4298 }
+
4299
+
4300 {
+
4301 testcase("Scale clawback with rounding assets");
+
4302 // assetsToSharesWithdraw:
+
4303 // shares = sharesTotal * (assets / assetsTotal)
+
4304 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
+
4305 // sharesToAssetsWithdraw:
+
4306 // assets = assetsTotal * (shares / sharesTotal)
+
4307 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
4308
-
4309 {
-
4310 testcase("Scale clawback tiny amount");
-
4311
-
4312 auto const start = env.balance(d.depositor, d.assets).number();
-
4313 auto tx = d.vault.clawback(
-
4314 {.issuer = d.issuer,
-
4315 .id = d.keylet.key,
-
4316 .holder = d.depositor,
-
4317 .amount = STAmount(d.asset, Number(9, -2))});
-
4318 env(tx);
-
4319 env.close();
-
4320 BEAST_EXPECT(
-
4321 env.balance(d.depositor, d.shares) == d.share(800 - 1));
+
4309 auto const start = env.balance(d.depositor, d.assets).number();
+
4310 auto tx = d.vault.clawback(
+
4311 {.issuer = d.issuer,
+
4312 .id = d.keylet.key,
+
4313 .holder = d.depositor,
+
4314 .amount = STAmount(d.asset, Number(25, -1))});
+
4315 env(tx);
+
4316 env.close();
+
4317 BEAST_EXPECT(
+
4318 env.balance(d.depositor, d.shares) == d.share(900 - 25));
+
4319 BEAST_EXPECT(
+
4320 env.balance(d.depositor, d.assets) ==
+
4321 STAmount(d.asset, start));
4322 BEAST_EXPECT(
-
4323 env.balance(d.depositor, d.assets) ==
-
4324 STAmount(d.asset, start));
+
4323 env.balance(d.vaultAccount, d.assets) ==
+
4324 STAmount(d.asset, Number(900 - 25, -1)));
4325 BEAST_EXPECT(
-
4326 env.balance(d.vaultAccount, d.assets) ==
-
4327 STAmount(d.asset, Number(800 - 1, -1)));
-
4328 BEAST_EXPECT(
-
4329 env.balance(d.vaultAccount, d.shares) ==
-
4330 STAmount(d.share, -Number(800 - 1, 0)));
-
4331 }
-
4332
-
4333 {
-
4334 testcase("Scale clawback rest");
-
4335 auto const rest =
-
4336 env.balance(d.vaultAccount, d.assets).number();
-
4337 d.peek([](SLE& vault, auto&) -> bool {
-
4338 vault[sfAssetsAvailable] = Number(5);
-
4339 return true;
-
4340 });
-
4341
-
4342 // Note, this transaction yields two different results:
-
4343 // * in the open ledger, with AssetsAvailable = 5
-
4344 // * when the ledger is closed with unmodified AssetsAvailable
-
4345 // because a modification like above is not persistent.
-
4346 tx = d.vault.clawback(
-
4347 {.issuer = d.issuer,
-
4348 .id = d.keylet.key,
-
4349 .holder = d.depositor,
-
4350 .amount = STAmount(d.asset, rest)});
-
4351 env(tx);
-
4352 env.close();
-
4353 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
-
4354 BEAST_EXPECT(
-
4355 env.balance(d.vaultAccount, d.assets).number() == 0);
-
4356 BEAST_EXPECT(
-
4357 env.balance(d.vaultAccount, d.shares).number() == 0);
+
4326 env.balance(d.vaultAccount, d.shares) ==
+
4327 STAmount(d.share, -Number(900 - 25, 0)));
+
4328 }
+
4329
+
4330 {
+
4331 testcase("Scale clawback with rounding shares up");
+
4332 // assetsToSharesWithdraw:
+
4333 // shares = sharesTotal * (assets / assetsTotal)
+
4334 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
+
4335 // sharesToAssetsWithdraw:
+
4336 // assets = assetsTotal * (shares / sharesTotal)
+
4337 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
+
4338
+
4339 auto const start = env.balance(d.depositor, d.assets).number();
+
4340 auto tx = d.vault.clawback(
+
4341 {.issuer = d.issuer,
+
4342 .id = d.keylet.key,
+
4343 .holder = d.depositor,
+
4344 .amount = STAmount(d.asset, Number(375, -2))});
+
4345 env(tx);
+
4346 env.close();
+
4347 BEAST_EXPECT(
+
4348 env.balance(d.depositor, d.shares) == d.share(875 - 38));
+
4349 BEAST_EXPECT(
+
4350 env.balance(d.depositor, d.assets) ==
+
4351 STAmount(d.asset, start));
+
4352 BEAST_EXPECT(
+
4353 env.balance(d.vaultAccount, d.assets) ==
+
4354 STAmount(d.asset, Number(875 - 38, -1)));
+
4355 BEAST_EXPECT(
+
4356 env.balance(d.vaultAccount, d.shares) ==
+
4357 STAmount(d.share, -Number(875 - 38, 0)));
4358 }
-
4359 });
-
4360 }
+
4359
+
4360 {
+
4361 testcase("Scale clawback with rounding shares down");
+
4362 // assetsToSharesWithdraw:
+
4363 // shares = sharesTotal * (assets / assetsTotal)
+
4364 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
+
4365 // sharesToAssetsWithdraw:
+
4366 // assets = assetsTotal * (shares / sharesTotal)
+
4367 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
+
4368
+
4369 auto const start = env.balance(d.depositor, d.assets).number();
+
4370 auto tx = d.vault.clawback(
+
4371 {.issuer = d.issuer,
+
4372 .id = d.keylet.key,
+
4373 .holder = d.depositor,
+
4374 .amount = STAmount(d.asset, Number(372, -2))});
+
4375 env(tx);
+
4376 env.close();
+
4377 BEAST_EXPECT(
+
4378 env.balance(d.depositor, d.shares) == d.share(837 - 37));
+
4379 BEAST_EXPECT(
+
4380 env.balance(d.depositor, d.assets) ==
+
4381 STAmount(d.asset, start));
+
4382 BEAST_EXPECT(
+
4383 env.balance(d.vaultAccount, d.assets) ==
+
4384 STAmount(d.asset, Number(837 - 37, -1)));
+
4385 BEAST_EXPECT(
+
4386 env.balance(d.vaultAccount, d.shares) ==
+
4387 STAmount(d.share, -Number(837 - 37, 0)));
+
4388 }
+
4389
+
4390 {
+
4391 testcase("Scale clawback tiny amount");
+
4392
+
4393 auto const start = env.balance(d.depositor, d.assets).number();
+
4394 auto tx = d.vault.clawback(
+
4395 {.issuer = d.issuer,
+
4396 .id = d.keylet.key,
+
4397 .holder = d.depositor,
+
4398 .amount = STAmount(d.asset, Number(9, -2))});
+
4399 env(tx);
+
4400 env.close();
+
4401 BEAST_EXPECT(
+
4402 env.balance(d.depositor, d.shares) == d.share(800 - 1));
+
4403 BEAST_EXPECT(
+
4404 env.balance(d.depositor, d.assets) ==
+
4405 STAmount(d.asset, start));
+
4406 BEAST_EXPECT(
+
4407 env.balance(d.vaultAccount, d.assets) ==
+
4408 STAmount(d.asset, Number(800 - 1, -1)));
+
4409 BEAST_EXPECT(
+
4410 env.balance(d.vaultAccount, d.shares) ==
+
4411 STAmount(d.share, -Number(800 - 1, 0)));
+
4412 }
+
4413
+
4414 {
+
4415 testcase("Scale clawback rest");
+
4416 auto const rest =
+
4417 env.balance(d.vaultAccount, d.assets).number();
+
4418 d.peek([](SLE& vault, auto&) -> bool {
+
4419 vault[sfAssetsAvailable] = Number(5);
+
4420 return true;
+
4421 });
+
4422
+
4423 // Note, this transaction yields two different results:
+
4424 // * in the open ledger, with AssetsAvailable = 5
+
4425 // * when the ledger is closed with unmodified AssetsAvailable
+
4426 // because a modification like above is not persistent.
+
4427 tx = d.vault.clawback(
+
4428 {.issuer = d.issuer,
+
4429 .id = d.keylet.key,
+
4430 .holder = d.depositor,
+
4431 .amount = STAmount(d.asset, rest)});
+
4432 env(tx);
+
4433 env.close();
+
4434 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
+
4435 BEAST_EXPECT(
+
4436 env.balance(d.vaultAccount, d.assets).number() == 0);
+
4437 BEAST_EXPECT(
+
4438 env.balance(d.vaultAccount, d.shares).number() == 0);
+
4439 }
+
4440 });
+
4441 }
-
4361
-
4362 void
-
- -
4364 {
-
4365 using namespace test::jtx;
-
4366
-
4367 testcase("RPC");
-
4368 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
4369 Account const owner{"owner"};
-
4370 Account const issuer{"issuer"};
-
4371 Vault vault{env};
-
4372 env.fund(XRP(1000), issuer, owner);
-
4373 env.close();
-
4374
-
4375 PrettyAsset asset = issuer["IOU"];
-
4376 env.trust(asset(1000), owner);
-
4377 env(pay(issuer, owner, asset(200)));
-
4378 env.close();
-
4379
-
4380 auto const sequence = env.seq(owner);
-
4381 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
4382 env(tx);
-
4383 env.close();
-
4384
-
4385 // Set some fields
-
4386 {
-
4387 auto tx1 = vault.deposit(
-
4388 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
-
4389 env(tx1);
-
4390
-
4391 auto tx2 = vault.set({.owner = owner, .id = keylet.key});
-
4392 tx2[sfAssetsMaximum] = asset(1000).number();
-
4393 env(tx2);
-
4394 env.close();
-
4395 }
-
4396
-
4397 auto const sleVault = [&env, keylet = keylet, this]() {
-
4398 auto const vault = env.le(keylet);
-
4399 BEAST_EXPECT(vault != nullptr);
-
4400 return vault;
-
4401 }();
-
4402
-
4403 auto const check = [&, keylet = keylet, sle = sleVault, this](
-
4404 Json::Value const& vault,
-
4405 Json::Value const& issuance = Json::nullValue) {
-
4406 BEAST_EXPECT(vault.isObject());
-
4407
-
4408 constexpr auto checkString =
-
4409 [](auto& node, SField const& field, std::string v) -> bool {
-
4410 return node.isMember(field.fieldName) &&
-
4411 node[field.fieldName].isString() &&
-
4412 node[field.fieldName] == v;
-
4413 };
-
4414 constexpr auto checkObject =
-
4415 [](auto& node, SField const& field, Json::Value v) -> bool {
-
4416 return node.isMember(field.fieldName) &&
-
4417 node[field.fieldName].isObject() &&
-
4418 node[field.fieldName] == v;
-
4419 };
-
4420 constexpr auto checkInt =
-
4421 [](auto& node, SField const& field, int v) -> bool {
-
4422 return node.isMember(field.fieldName) &&
-
4423 ((node[field.fieldName].isInt() &&
-
4424 node[field.fieldName] == Json::Int(v)) ||
-
4425 (node[field.fieldName].isUInt() &&
-
4426 node[field.fieldName] == Json::UInt(v)));
-
4427 };
-
4428
-
4429 BEAST_EXPECT(vault["LedgerEntryType"].asString() == "Vault");
-
4430 BEAST_EXPECT(vault[jss::index].asString() == strHex(keylet.key));
-
4431 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
-
4432 // Ignore all other standard fields, this test doesn't care
-
4433
-
4434 BEAST_EXPECT(
-
4435 checkString(vault, sfAccount, toBase58(sle->at(sfAccount))));
-
4436 BEAST_EXPECT(
-
4437 checkObject(vault, sfAsset, to_json(sle->at(sfAsset))));
-
4438 BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
-
4439 BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
-
4440 BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
-
4441 BEAST_EXPECT(checkString(vault, sfLossUnrealized, "0"));
4442
-
4443 auto const strShareID = strHex(sle->at(sfShareMPTID));
-
4444 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
-
4445 BEAST_EXPECT(checkString(vault, sfOwner, toBase58(owner.id())));
-
4446 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
-
4447 BEAST_EXPECT(checkInt(
-
4448 vault, sfWithdrawalPolicy, vaultStrategyFirstComeFirstServe));
-
4449
-
4450 if (issuance.isObject())
-
4451 {
-
4452 BEAST_EXPECT(
-
4453 issuance["LedgerEntryType"].asString() ==
-
4454 "MPTokenIssuance");
-
4455 BEAST_EXPECT(
-
4456 issuance[jss::mpt_issuance_id].asString() == strShareID);
-
4457 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
-
4458 BEAST_EXPECT(checkInt(
-
4459 issuance,
-
4460 sfFlags,
- -
4462 BEAST_EXPECT(
-
4463 checkString(issuance, sfOutstandingAmount, "50000000"));
-
4464 }
-
4465 };
-
4466
+
4443 void
+
+ +
4445 {
+
4446 using namespace test::jtx;
+
4447
+
4448 testcase("RPC");
+
4449 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
4450 Account const owner{"owner"};
+
4451 Account const issuer{"issuer"};
+
4452 Vault vault{env};
+
4453 env.fund(XRP(1000), issuer, owner);
+
4454 env.close();
+
4455
+
4456 PrettyAsset asset = issuer["IOU"];
+
4457 env.trust(asset(1000), owner);
+
4458 env(pay(issuer, owner, asset(200)));
+
4459 env.close();
+
4460
+
4461 auto const sequence = env.seq(owner);
+
4462 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
4463 env(tx);
+
4464 env.close();
+
4465
+
4466 // Set some fields
4467 {
-
4468 testcase("RPC ledger_entry selected by key");
-
4469 Json::Value jvParams;
-
4470 jvParams[jss::ledger_index] = jss::validated;
-
4471 jvParams[jss::vault] = strHex(keylet.key);
-
4472 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4473
-
4474 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
-
4475 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
-
4476 check(jvVault[jss::result][jss::node]);
-
4477 }
-
4478
-
4479 {
-
4480 testcase("RPC ledger_entry selected by owner and seq");
-
4481 Json::Value jvParams;
-
4482 jvParams[jss::ledger_index] = jss::validated;
-
4483 jvParams[jss::vault][jss::owner] = owner.human();
-
4484 jvParams[jss::vault][jss::seq] = sequence;
-
4485 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4486
-
4487 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
-
4488 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
-
4489 check(jvVault[jss::result][jss::node]);
-
4490 }
-
4491
-
4492 {
-
4493 testcase("RPC ledger_entry cannot find vault by key");
-
4494 Json::Value jvParams;
-
4495 jvParams[jss::ledger_index] = jss::validated;
-
4496 jvParams[jss::vault] = to_string(uint256(42));
-
4497 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4498 BEAST_EXPECT(
-
4499 jvVault[jss::result][jss::error].asString() == "entryNotFound");
-
4500 }
-
4501
-
4502 {
-
4503 testcase("RPC ledger_entry cannot find vault by owner and seq");
-
4504 Json::Value jvParams;
-
4505 jvParams[jss::ledger_index] = jss::validated;
-
4506 jvParams[jss::vault][jss::owner] = issuer.human();
-
4507 jvParams[jss::vault][jss::seq] = 1'000'000;
-
4508 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4509 BEAST_EXPECT(
-
4510 jvVault[jss::result][jss::error].asString() == "entryNotFound");
-
4511 }
-
4512
-
4513 {
-
4514 testcase("RPC ledger_entry malformed key");
-
4515 Json::Value jvParams;
-
4516 jvParams[jss::ledger_index] = jss::validated;
-
4517 jvParams[jss::vault] = 42;
-
4518 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4519 BEAST_EXPECT(
-
4520 jvVault[jss::result][jss::error].asString() ==
-
4521 "malformedRequest");
-
4522 }
+
4468 auto tx1 = vault.deposit(
+
4469 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
+
4470 env(tx1);
+
4471
+
4472 auto tx2 = vault.set({.owner = owner, .id = keylet.key});
+
4473 tx2[sfAssetsMaximum] = asset(1000).number();
+
4474 env(tx2);
+
4475 env.close();
+
4476 }
+
4477
+
4478 auto const sleVault = [&env, keylet = keylet, this]() {
+
4479 auto const vault = env.le(keylet);
+
4480 BEAST_EXPECT(vault != nullptr);
+
4481 return vault;
+
4482 }();
+
4483
+
4484 auto const check = [&, keylet = keylet, sle = sleVault, this](
+
4485 Json::Value const& vault,
+
4486 Json::Value const& issuance = Json::nullValue) {
+
4487 BEAST_EXPECT(vault.isObject());
+
4488
+
4489 constexpr auto checkString =
+
4490 [](auto& node, SField const& field, std::string v) -> bool {
+
4491 return node.isMember(field.fieldName) &&
+
4492 node[field.fieldName].isString() &&
+
4493 node[field.fieldName] == v;
+
4494 };
+
4495 constexpr auto checkObject =
+
4496 [](auto& node, SField const& field, Json::Value v) -> bool {
+
4497 return node.isMember(field.fieldName) &&
+
4498 node[field.fieldName].isObject() &&
+
4499 node[field.fieldName] == v;
+
4500 };
+
4501 constexpr auto checkInt =
+
4502 [](auto& node, SField const& field, int v) -> bool {
+
4503 return node.isMember(field.fieldName) &&
+
4504 ((node[field.fieldName].isInt() &&
+
4505 node[field.fieldName] == Json::Int(v)) ||
+
4506 (node[field.fieldName].isUInt() &&
+
4507 node[field.fieldName] == Json::UInt(v)));
+
4508 };
+
4509
+
4510 BEAST_EXPECT(vault["LedgerEntryType"].asString() == "Vault");
+
4511 BEAST_EXPECT(vault[jss::index].asString() == strHex(keylet.key));
+
4512 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
+
4513 // Ignore all other standard fields, this test doesn't care
+
4514
+
4515 BEAST_EXPECT(
+
4516 checkString(vault, sfAccount, toBase58(sle->at(sfAccount))));
+
4517 BEAST_EXPECT(
+
4518 checkObject(vault, sfAsset, to_json(sle->at(sfAsset))));
+
4519 BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
+
4520 BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
+
4521 BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
+
4522 BEAST_EXPECT(checkString(vault, sfLossUnrealized, "0"));
4523
-
4524 {
-
4525 testcase("RPC ledger_entry malformed owner");
-
4526 Json::Value jvParams;
-
4527 jvParams[jss::ledger_index] = jss::validated;
-
4528 jvParams[jss::vault][jss::owner] = 42;
-
4529 jvParams[jss::vault][jss::seq] = sequence;
-
4530 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4531 BEAST_EXPECT(
-
4532 jvVault[jss::result][jss::error].asString() ==
-
4533 "malformedOwner");
-
4534 }
-
4535
-
4536 {
-
4537 testcase("RPC ledger_entry malformed seq");
-
4538 Json::Value jvParams;
-
4539 jvParams[jss::ledger_index] = jss::validated;
-
4540 jvParams[jss::vault][jss::owner] = issuer.human();
-
4541 jvParams[jss::vault][jss::seq] = "foo";
-
4542 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4543 BEAST_EXPECT(
-
4544 jvVault[jss::result][jss::error].asString() ==
-
4545 "malformedRequest");
-
4546 }
+
4524 auto const strShareID = strHex(sle->at(sfShareMPTID));
+
4525 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
+
4526 BEAST_EXPECT(checkString(vault, sfOwner, toBase58(owner.id())));
+
4527 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
+
4528 BEAST_EXPECT(checkInt(
+
4529 vault, sfWithdrawalPolicy, vaultStrategyFirstComeFirstServe));
+
4530
+
4531 if (issuance.isObject())
+
4532 {
+
4533 BEAST_EXPECT(
+
4534 issuance["LedgerEntryType"].asString() ==
+
4535 "MPTokenIssuance");
+
4536 BEAST_EXPECT(
+
4537 issuance[jss::mpt_issuance_id].asString() == strShareID);
+
4538 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
+
4539 BEAST_EXPECT(checkInt(
+
4540 issuance,
+
4541 sfFlags,
+ +
4543 BEAST_EXPECT(
+
4544 checkString(issuance, sfOutstandingAmount, "50000000"));
+
4545 }
+
4546 };
4547
4548 {
-
4549 testcase("RPC ledger_entry negative seq");
+
4549 testcase("RPC ledger_entry selected by key");
4550 Json::Value jvParams;
4551 jvParams[jss::ledger_index] = jss::validated;
-
4552 jvParams[jss::vault][jss::owner] = issuer.human();
-
4553 jvParams[jss::vault][jss::seq] = -1;
-
4554 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4555 BEAST_EXPECT(
-
4556 jvVault[jss::result][jss::error].asString() ==
-
4557 "malformedRequest");
+
4552 jvParams[jss::vault] = strHex(keylet.key);
+
4553 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4554
+
4555 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
+
4556 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
+
4557 check(jvVault[jss::result][jss::node]);
4558 }
4559
4560 {
-
4561 testcase("RPC ledger_entry oversized seq");
+
4561 testcase("RPC ledger_entry selected by owner and seq");
4562 Json::Value jvParams;
4563 jvParams[jss::ledger_index] = jss::validated;
-
4564 jvParams[jss::vault][jss::owner] = issuer.human();
-
4565 jvParams[jss::vault][jss::seq] = 1e20;
+
4564 jvParams[jss::vault][jss::owner] = owner.human();
+
4565 jvParams[jss::vault][jss::seq] = sequence;
4566 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
4567 BEAST_EXPECT(
-
4568 jvVault[jss::result][jss::error].asString() ==
-
4569 "malformedRequest");
-
4570 }
-
4571
-
4572 {
-
4573 testcase("RPC ledger_entry bool seq");
-
4574 Json::Value jvParams;
-
4575 jvParams[jss::ledger_index] = jss::validated;
-
4576 jvParams[jss::vault][jss::owner] = issuer.human();
-
4577 jvParams[jss::vault][jss::seq] = true;
+
4567
+
4568 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
+
4569 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
+
4570 check(jvVault[jss::result][jss::node]);
+
4571 }
+
4572
+
4573 {
+
4574 testcase("RPC ledger_entry cannot find vault by key");
+
4575 Json::Value jvParams;
+
4576 jvParams[jss::ledger_index] = jss::validated;
+
4577 jvParams[jss::vault] = to_string(uint256(42));
4578 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
4579 BEAST_EXPECT(
-
4580 jvVault[jss::result][jss::error].asString() ==
-
4581 "malformedRequest");
-
4582 }
-
4583
-
4584 {
-
4585 testcase("RPC account_objects");
-
4586
-
4587 Json::Value jvParams;
-
4588 jvParams[jss::account] = owner.human();
-
4589 jvParams[jss::type] = jss::vault;
-
4590 auto jv = env.rpc(
-
4591 "json", "account_objects", to_string(jvParams))[jss::result];
-
4592
-
4593 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
-
4594 check(jv[jss::account_objects][0u]);
-
4595 }
-
4596
-
4597 {
-
4598 testcase("RPC ledger_data");
-
4599
-
4600 Json::Value jvParams;
-
4601 jvParams[jss::ledger_index] = jss::validated;
-
4602 jvParams[jss::binary] = false;
-
4603 jvParams[jss::type] = jss::vault;
-
4604 Json::Value jv =
-
4605 env.rpc("json", "ledger_data", to_string(jvParams));
-
4606 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
-
4607 check(jv[jss::result][jss::state][0u]);
-
4608 }
-
4609
-
4610 {
-
4611 testcase("RPC vault_info command line");
-
4612 Json::Value jv =
-
4613 env.rpc("vault_info", strHex(keylet.key), "validated");
-
4614
-
4615 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
-
4616 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
-
4617 check(
-
4618 jv[jss::result][jss::vault],
-
4619 jv[jss::result][jss::vault][jss::shares]);
-
4620 }
-
4621
-
4622 {
-
4623 testcase("RPC vault_info json");
-
4624 Json::Value jvParams;
-
4625 jvParams[jss::ledger_index] = jss::validated;
-
4626 jvParams[jss::vault_id] = strHex(keylet.key);
-
4627 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4580 jvVault[jss::result][jss::error].asString() == "entryNotFound");
+
4581 }
+
4582
+
4583 {
+
4584 testcase("RPC ledger_entry cannot find vault by owner and seq");
+
4585 Json::Value jvParams;
+
4586 jvParams[jss::ledger_index] = jss::validated;
+
4587 jvParams[jss::vault][jss::owner] = issuer.human();
+
4588 jvParams[jss::vault][jss::seq] = 1'000'000;
+
4589 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4590 BEAST_EXPECT(
+
4591 jvVault[jss::result][jss::error].asString() == "entryNotFound");
+
4592 }
+
4593
+
4594 {
+
4595 testcase("RPC ledger_entry malformed key");
+
4596 Json::Value jvParams;
+
4597 jvParams[jss::ledger_index] = jss::validated;
+
4598 jvParams[jss::vault] = 42;
+
4599 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4600 BEAST_EXPECT(
+
4601 jvVault[jss::result][jss::error].asString() ==
+
4602 "malformedRequest");
+
4603 }
+
4604
+
4605 {
+
4606 testcase("RPC ledger_entry malformed owner");
+
4607 Json::Value jvParams;
+
4608 jvParams[jss::ledger_index] = jss::validated;
+
4609 jvParams[jss::vault][jss::owner] = 42;
+
4610 jvParams[jss::vault][jss::seq] = sequence;
+
4611 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4612 BEAST_EXPECT(
+
4613 jvVault[jss::result][jss::error].asString() ==
+
4614 "malformedOwner");
+
4615 }
+
4616
+
4617 {
+
4618 testcase("RPC ledger_entry malformed seq");
+
4619 Json::Value jvParams;
+
4620 jvParams[jss::ledger_index] = jss::validated;
+
4621 jvParams[jss::vault][jss::owner] = issuer.human();
+
4622 jvParams[jss::vault][jss::seq] = "foo";
+
4623 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4624 BEAST_EXPECT(
+
4625 jvVault[jss::result][jss::error].asString() ==
+
4626 "malformedRequest");
+
4627 }
4628
-
4629 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
-
4630 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
-
4631 check(
-
4632 jv[jss::result][jss::vault],
-
4633 jv[jss::result][jss::vault][jss::shares]);
-
4634 }
-
4635
-
4636 {
-
4637 testcase("RPC vault_info invalid vault_id");
-
4638 Json::Value jvParams;
-
4639 jvParams[jss::ledger_index] = jss::validated;
-
4640 jvParams[jss::vault_id] = "foobar";
-
4641 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4642 BEAST_EXPECT(
-
4643 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4644 }
-
4645
-
4646 {
-
4647 testcase("RPC vault_info json invalid index");
-
4648 Json::Value jvParams;
-
4649 jvParams[jss::ledger_index] = jss::validated;
-
4650 jvParams[jss::vault_id] = 0;
-
4651 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4652 BEAST_EXPECT(
-
4653 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4654 }
-
4655
-
4656 {
-
4657 testcase("RPC vault_info json by owner and sequence");
-
4658 Json::Value jvParams;
-
4659 jvParams[jss::ledger_index] = jss::validated;
-
4660 jvParams[jss::owner] = owner.human();
-
4661 jvParams[jss::seq] = sequence;
-
4662 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4663
-
4664 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
-
4665 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
-
4666 check(
-
4667 jv[jss::result][jss::vault],
-
4668 jv[jss::result][jss::vault][jss::shares]);
-
4669 }
-
4670
-
4671 {
-
4672 testcase("RPC vault_info json malformed sequence");
-
4673 Json::Value jvParams;
-
4674 jvParams[jss::ledger_index] = jss::validated;
-
4675 jvParams[jss::owner] = owner.human();
-
4676 jvParams[jss::seq] = "foobar";
-
4677 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4678 BEAST_EXPECT(
-
4679 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4680 }
-
4681
-
4682 {
-
4683 testcase("RPC vault_info json invalid sequence");
-
4684 Json::Value jvParams;
-
4685 jvParams[jss::ledger_index] = jss::validated;
-
4686 jvParams[jss::owner] = owner.human();
-
4687 jvParams[jss::seq] = 0;
-
4688 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4689 BEAST_EXPECT(
-
4690 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4691 }
-
4692
-
4693 {
-
4694 testcase("RPC vault_info json negative sequence");
-
4695 Json::Value jvParams;
-
4696 jvParams[jss::ledger_index] = jss::validated;
-
4697 jvParams[jss::owner] = owner.human();
-
4698 jvParams[jss::seq] = -1;
-
4699 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4700 BEAST_EXPECT(
-
4701 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4702 }
-
4703
-
4704 {
-
4705 testcase("RPC vault_info json oversized sequence");
-
4706 Json::Value jvParams;
-
4707 jvParams[jss::ledger_index] = jss::validated;
-
4708 jvParams[jss::owner] = owner.human();
-
4709 jvParams[jss::seq] = 1e20;
-
4710 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4711 BEAST_EXPECT(
-
4712 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4713 }
-
4714
-
4715 {
-
4716 testcase("RPC vault_info json bool sequence");
-
4717 Json::Value jvParams;
-
4718 jvParams[jss::ledger_index] = jss::validated;
-
4719 jvParams[jss::owner] = owner.human();
-
4720 jvParams[jss::seq] = true;
-
4721 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4722 BEAST_EXPECT(
-
4723 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4724 }
-
4725
-
4726 {
-
4727 testcase("RPC vault_info json malformed owner");
-
4728 Json::Value jvParams;
-
4729 jvParams[jss::ledger_index] = jss::validated;
-
4730 jvParams[jss::owner] = "foobar";
-
4731 jvParams[jss::seq] = sequence;
+
4629 {
+
4630 testcase("RPC ledger_entry negative seq");
+
4631 Json::Value jvParams;
+
4632 jvParams[jss::ledger_index] = jss::validated;
+
4633 jvParams[jss::vault][jss::owner] = issuer.human();
+
4634 jvParams[jss::vault][jss::seq] = -1;
+
4635 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4636 BEAST_EXPECT(
+
4637 jvVault[jss::result][jss::error].asString() ==
+
4638 "malformedRequest");
+
4639 }
+
4640
+
4641 {
+
4642 testcase("RPC ledger_entry oversized seq");
+
4643 Json::Value jvParams;
+
4644 jvParams[jss::ledger_index] = jss::validated;
+
4645 jvParams[jss::vault][jss::owner] = issuer.human();
+
4646 jvParams[jss::vault][jss::seq] = 1e20;
+
4647 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4648 BEAST_EXPECT(
+
4649 jvVault[jss::result][jss::error].asString() ==
+
4650 "malformedRequest");
+
4651 }
+
4652
+
4653 {
+
4654 testcase("RPC ledger_entry bool seq");
+
4655 Json::Value jvParams;
+
4656 jvParams[jss::ledger_index] = jss::validated;
+
4657 jvParams[jss::vault][jss::owner] = issuer.human();
+
4658 jvParams[jss::vault][jss::seq] = true;
+
4659 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
4660 BEAST_EXPECT(
+
4661 jvVault[jss::result][jss::error].asString() ==
+
4662 "malformedRequest");
+
4663 }
+
4664
+
4665 {
+
4666 testcase("RPC account_objects");
+
4667
+
4668 Json::Value jvParams;
+
4669 jvParams[jss::account] = owner.human();
+
4670 jvParams[jss::type] = jss::vault;
+
4671 auto jv = env.rpc(
+
4672 "json", "account_objects", to_string(jvParams))[jss::result];
+
4673
+
4674 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
+
4675 check(jv[jss::account_objects][0u]);
+
4676 }
+
4677
+
4678 {
+
4679 testcase("RPC ledger_data");
+
4680
+
4681 Json::Value jvParams;
+
4682 jvParams[jss::ledger_index] = jss::validated;
+
4683 jvParams[jss::binary] = false;
+
4684 jvParams[jss::type] = jss::vault;
+
4685 Json::Value jv =
+
4686 env.rpc("json", "ledger_data", to_string(jvParams));
+
4687 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
+
4688 check(jv[jss::result][jss::state][0u]);
+
4689 }
+
4690
+
4691 {
+
4692 testcase("RPC vault_info command line");
+
4693 Json::Value jv =
+
4694 env.rpc("vault_info", strHex(keylet.key), "validated");
+
4695
+
4696 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
+
4697 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
+
4698 check(
+
4699 jv[jss::result][jss::vault],
+
4700 jv[jss::result][jss::vault][jss::shares]);
+
4701 }
+
4702
+
4703 {
+
4704 testcase("RPC vault_info json");
+
4705 Json::Value jvParams;
+
4706 jvParams[jss::ledger_index] = jss::validated;
+
4707 jvParams[jss::vault_id] = strHex(keylet.key);
+
4708 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4709
+
4710 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
+
4711 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
+
4712 check(
+
4713 jv[jss::result][jss::vault],
+
4714 jv[jss::result][jss::vault][jss::shares]);
+
4715 }
+
4716
+
4717 {
+
4718 testcase("RPC vault_info invalid vault_id");
+
4719 Json::Value jvParams;
+
4720 jvParams[jss::ledger_index] = jss::validated;
+
4721 jvParams[jss::vault_id] = "foobar";
+
4722 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4723 BEAST_EXPECT(
+
4724 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4725 }
+
4726
+
4727 {
+
4728 testcase("RPC vault_info json invalid index");
+
4729 Json::Value jvParams;
+
4730 jvParams[jss::ledger_index] = jss::validated;
+
4731 jvParams[jss::vault_id] = 0;
4732 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
4733 BEAST_EXPECT(
4734 jv[jss::result][jss::error].asString() == "malformedRequest");
4735 }
4736
4737 {
-
4738 testcase("RPC vault_info json invalid combination only owner");
+
4738 testcase("RPC vault_info json by owner and sequence");
4739 Json::Value jvParams;
4740 jvParams[jss::ledger_index] = jss::validated;
4741 jvParams[jss::owner] = owner.human();
-
4742 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4743 BEAST_EXPECT(
-
4744 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4745 }
-
4746
-
4747 {
-
4748 testcase("RPC vault_info json invalid combination only seq");
-
4749 Json::Value jvParams;
-
4750 jvParams[jss::ledger_index] = jss::validated;
-
4751 jvParams[jss::seq] = sequence;
-
4752 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4753 BEAST_EXPECT(
-
4754 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4755 }
-
4756
-
4757 {
-
4758 testcase("RPC vault_info json invalid combination seq vault_id");
-
4759 Json::Value jvParams;
-
4760 jvParams[jss::ledger_index] = jss::validated;
-
4761 jvParams[jss::vault_id] = strHex(keylet.key);
-
4762 jvParams[jss::seq] = sequence;
-
4763 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4764 BEAST_EXPECT(
-
4765 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4766 }
-
4767
-
4768 {
-
4769 testcase("RPC vault_info json invalid combination owner vault_id");
-
4770 Json::Value jvParams;
-
4771 jvParams[jss::ledger_index] = jss::validated;
-
4772 jvParams[jss::vault_id] = strHex(keylet.key);
-
4773 jvParams[jss::owner] = owner.human();
-
4774 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4775 BEAST_EXPECT(
-
4776 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4777 }
-
4778
-
4779 {
-
4780 testcase(
-
4781 "RPC vault_info json invalid combination owner seq "
-
4782 "vault_id");
-
4783 Json::Value jvParams;
-
4784 jvParams[jss::ledger_index] = jss::validated;
-
4785 jvParams[jss::vault_id] = strHex(keylet.key);
-
4786 jvParams[jss::seq] = sequence;
-
4787 jvParams[jss::owner] = owner.human();
-
4788 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4789 BEAST_EXPECT(
-
4790 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4791 }
-
4792
-
4793 {
-
4794 testcase("RPC vault_info json no input");
-
4795 Json::Value jvParams;
-
4796 jvParams[jss::ledger_index] = jss::validated;
-
4797 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4798 BEAST_EXPECT(
-
4799 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4800 }
-
4801
-
4802 {
-
4803 testcase("RPC vault_info command line invalid index");
-
4804 Json::Value jv = env.rpc("vault_info", "foobar", "validated");
-
4805 BEAST_EXPECT(jv[jss::error].asString() == "invalidParams");
-
4806 }
-
4807
-
4808 {
-
4809 testcase("RPC vault_info command line invalid index");
-
4810 Json::Value jv = env.rpc("vault_info", "0", "validated");
-
4811 BEAST_EXPECT(
-
4812 jv[jss::result][jss::error].asString() == "malformedRequest");
-
4813 }
-
4814
-
4815 {
-
4816 testcase("RPC vault_info command line invalid index");
-
4817 Json::Value jv =
-
4818 env.rpc("vault_info", strHex(uint256(42)), "validated");
-
4819 BEAST_EXPECT(
-
4820 jv[jss::result][jss::error].asString() == "entryNotFound");
-
4821 }
-
4822
-
4823 {
-
4824 testcase("RPC vault_info command line invalid ledger");
-
4825 Json::Value jv = env.rpc("vault_info", strHex(keylet.key), "0");
-
4826 BEAST_EXPECT(
-
4827 jv[jss::result][jss::error].asString() == "lgrNotFound");
-
4828 }
-
4829 }
-
-
4830
-
4831 void
-
- -
4833 {
-
4834 using namespace test::jtx;
-
4835
-
4836 Env env(*this, testable_amendments());
-
4837 Account alice{"alice"};
-
4838 Account bob{"bob"};
-
4839 Account carol{"carol"};
-
4840
-
4841 struct CaseArgs
-
4842 {
-
4843 PrettyAsset asset = xrpIssue();
-
4844 };
-
4845
-
4846 auto const xrpBalance =
-
4847 [this](
-
4848 Env const& env, Account const& account) -> std::optional<long> {
-
4849 auto sle = env.le(keylet::account(account.id()));
-
4850 if (BEAST_EXPECT(sle != nullptr))
-
4851 return sle->getFieldAmount(sfBalance).xrp().drops();
-
4852 return std::nullopt;
-
4853 };
-
4854
-
4855 auto testCase = [&, this](auto test, CaseArgs args = {}) {
-
4856 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
4857
-
4858 Vault vault{env};
+
4742 jvParams[jss::seq] = sequence;
+
4743 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4744
+
4745 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
+
4746 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
+
4747 check(
+
4748 jv[jss::result][jss::vault],
+
4749 jv[jss::result][jss::vault][jss::shares]);
+
4750 }
+
4751
+
4752 {
+
4753 testcase("RPC vault_info json malformed sequence");
+
4754 Json::Value jvParams;
+
4755 jvParams[jss::ledger_index] = jss::validated;
+
4756 jvParams[jss::owner] = owner.human();
+
4757 jvParams[jss::seq] = "foobar";
+
4758 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4759 BEAST_EXPECT(
+
4760 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4761 }
+
4762
+
4763 {
+
4764 testcase("RPC vault_info json invalid sequence");
+
4765 Json::Value jvParams;
+
4766 jvParams[jss::ledger_index] = jss::validated;
+
4767 jvParams[jss::owner] = owner.human();
+
4768 jvParams[jss::seq] = 0;
+
4769 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4770 BEAST_EXPECT(
+
4771 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4772 }
+
4773
+
4774 {
+
4775 testcase("RPC vault_info json negative sequence");
+
4776 Json::Value jvParams;
+
4777 jvParams[jss::ledger_index] = jss::validated;
+
4778 jvParams[jss::owner] = owner.human();
+
4779 jvParams[jss::seq] = -1;
+
4780 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4781 BEAST_EXPECT(
+
4782 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4783 }
+
4784
+
4785 {
+
4786 testcase("RPC vault_info json oversized sequence");
+
4787 Json::Value jvParams;
+
4788 jvParams[jss::ledger_index] = jss::validated;
+
4789 jvParams[jss::owner] = owner.human();
+
4790 jvParams[jss::seq] = 1e20;
+
4791 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4792 BEAST_EXPECT(
+
4793 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4794 }
+
4795
+
4796 {
+
4797 testcase("RPC vault_info json bool sequence");
+
4798 Json::Value jvParams;
+
4799 jvParams[jss::ledger_index] = jss::validated;
+
4800 jvParams[jss::owner] = owner.human();
+
4801 jvParams[jss::seq] = true;
+
4802 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4803 BEAST_EXPECT(
+
4804 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4805 }
+
4806
+
4807 {
+
4808 testcase("RPC vault_info json malformed owner");
+
4809 Json::Value jvParams;
+
4810 jvParams[jss::ledger_index] = jss::validated;
+
4811 jvParams[jss::owner] = "foobar";
+
4812 jvParams[jss::seq] = sequence;
+
4813 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4814 BEAST_EXPECT(
+
4815 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4816 }
+
4817
+
4818 {
+
4819 testcase("RPC vault_info json invalid combination only owner");
+
4820 Json::Value jvParams;
+
4821 jvParams[jss::ledger_index] = jss::validated;
+
4822 jvParams[jss::owner] = owner.human();
+
4823 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4824 BEAST_EXPECT(
+
4825 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4826 }
+
4827
+
4828 {
+
4829 testcase("RPC vault_info json invalid combination only seq");
+
4830 Json::Value jvParams;
+
4831 jvParams[jss::ledger_index] = jss::validated;
+
4832 jvParams[jss::seq] = sequence;
+
4833 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4834 BEAST_EXPECT(
+
4835 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4836 }
+
4837
+
4838 {
+
4839 testcase("RPC vault_info json invalid combination seq vault_id");
+
4840 Json::Value jvParams;
+
4841 jvParams[jss::ledger_index] = jss::validated;
+
4842 jvParams[jss::vault_id] = strHex(keylet.key);
+
4843 jvParams[jss::seq] = sequence;
+
4844 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4845 BEAST_EXPECT(
+
4846 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4847 }
+
4848
+
4849 {
+
4850 testcase("RPC vault_info json invalid combination owner vault_id");
+
4851 Json::Value jvParams;
+
4852 jvParams[jss::ledger_index] = jss::validated;
+
4853 jvParams[jss::vault_id] = strHex(keylet.key);
+
4854 jvParams[jss::owner] = owner.human();
+
4855 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4856 BEAST_EXPECT(
+
4857 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4858 }
4859
-
4860 // use different initial amount to distinguish the source balance
-
4861 env.fund(XRP(10000), alice);
-
4862 env.fund(XRP(20000), bob);
-
4863 env.fund(XRP(30000), carol);
-
4864 env.close();
-
4865
-
4866 env(delegate::set(
-
4867 carol,
-
4868 alice,
-
4869 {"Payment",
-
4870 "VaultCreate",
-
4871 "VaultSet",
-
4872 "VaultDelete",
-
4873 "VaultDeposit",
-
4874 "VaultWithdraw",
-
4875 "VaultClawback"}));
-
4876
-
4877 test(env, vault, args.asset);
-
4878 };
-
4879
-
4880 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
-
4881 testcase("delegated vault creation");
-
4882 auto startBalance = xrpBalance(env, carol);
-
4883 if (!BEAST_EXPECT(startBalance.has_value()))
-
4884 return;
-
4885
-
4886 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
-
4887 env(tx, delegate::as(alice));
-
4888 env.close();
-
4889 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
-
4890 });
-
4891
-
4892 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
-
4893 testcase("delegated deposit and withdrawal");
-
4894 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
-
4895 env(tx);
-
4896 env.close();
-
4897
-
4898 auto const amount = 1513;
-
4899 auto const baseFee = env.current()->fees().base;
-
4900
-
4901 auto startBalance = xrpBalance(env, carol);
-
4902 if (!BEAST_EXPECT(startBalance.has_value()))
-
4903 return;
-
4904
-
4905 tx = vault.deposit(
-
4906 {.depositor = carol,
-
4907 .id = keylet.key,
-
4908 .amount = asset(amount)});
-
4909 env(tx, delegate::as(alice));
-
4910 env.close();
-
4911 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
-
4912
-
4913 tx = vault.withdraw(
-
4914 {.depositor = carol,
-
4915 .id = keylet.key,
-
4916 .amount = asset(amount - 1)});
-
4917 env(tx, delegate::as(alice));
-
4918 env.close();
-
4919 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
-
4920
-
4921 tx = vault.withdraw(
-
4922 {.depositor = carol, .id = keylet.key, .amount = asset(1)});
-
4923 env(tx);
-
4924 env.close();
-
4925 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
-
4926 });
-
4927
-
4928 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
-
4929 testcase("delegated withdrawal same as base fee and deletion");
-
4930 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
-
4931 env(tx);
-
4932 env.close();
-
4933
-
4934 auto const amount = 25537;
-
4935 auto const baseFee = env.current()->fees().base;
-
4936
-
4937 auto startBalance = xrpBalance(env, carol);
-
4938 if (!BEAST_EXPECT(startBalance.has_value()))
-
4939 return;
+
4860 {
+
4861 testcase(
+
4862 "RPC vault_info json invalid combination owner seq "
+
4863 "vault_id");
+
4864 Json::Value jvParams;
+
4865 jvParams[jss::ledger_index] = jss::validated;
+
4866 jvParams[jss::vault_id] = strHex(keylet.key);
+
4867 jvParams[jss::seq] = sequence;
+
4868 jvParams[jss::owner] = owner.human();
+
4869 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4870 BEAST_EXPECT(
+
4871 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4872 }
+
4873
+
4874 {
+
4875 testcase("RPC vault_info json no input");
+
4876 Json::Value jvParams;
+
4877 jvParams[jss::ledger_index] = jss::validated;
+
4878 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4879 BEAST_EXPECT(
+
4880 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4881 }
+
4882
+
4883 {
+
4884 testcase("RPC vault_info command line invalid index");
+
4885 Json::Value jv = env.rpc("vault_info", "foobar", "validated");
+
4886 BEAST_EXPECT(jv[jss::error].asString() == "invalidParams");
+
4887 }
+
4888
+
4889 {
+
4890 testcase("RPC vault_info command line invalid index");
+
4891 Json::Value jv = env.rpc("vault_info", "0", "validated");
+
4892 BEAST_EXPECT(
+
4893 jv[jss::result][jss::error].asString() == "malformedRequest");
+
4894 }
+
4895
+
4896 {
+
4897 testcase("RPC vault_info command line invalid index");
+
4898 Json::Value jv =
+
4899 env.rpc("vault_info", strHex(uint256(42)), "validated");
+
4900 BEAST_EXPECT(
+
4901 jv[jss::result][jss::error].asString() == "entryNotFound");
+
4902 }
+
4903
+
4904 {
+
4905 testcase("RPC vault_info command line invalid ledger");
+
4906 Json::Value jv = env.rpc("vault_info", strHex(keylet.key), "0");
+
4907 BEAST_EXPECT(
+
4908 jv[jss::result][jss::error].asString() == "lgrNotFound");
+
4909 }
+
4910 }
+
+
4911
+
4912 void
+
+ +
4914 {
+
4915 using namespace test::jtx;
+
4916
+
4917 Env env(*this, testable_amendments());
+
4918 Account alice{"alice"};
+
4919 Account bob{"bob"};
+
4920 Account carol{"carol"};
+
4921
+
4922 struct CaseArgs
+
4923 {
+
4924 PrettyAsset asset = xrpIssue();
+
4925 };
+
4926
+
4927 auto const xrpBalance =
+
4928 [this](
+
4929 Env const& env, Account const& account) -> std::optional<long> {
+
4930 auto sle = env.le(keylet::account(account.id()));
+
4931 if (BEAST_EXPECT(sle != nullptr))
+
4932 return sle->getFieldAmount(sfBalance).xrp().drops();
+
4933 return std::nullopt;
+
4934 };
+
4935
+
4936 auto testCase = [&, this](auto test, CaseArgs args = {}) {
+
4937 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
4938
+
4939 Vault vault{env};
4940
-
4941 tx = vault.deposit(
-
4942 {.depositor = carol,
-
4943 .id = keylet.key,
-
4944 .amount = asset(amount)});
-
4945 env(tx);
-
4946 env.close();
-
4947 BEAST_EXPECT(
-
4948 xrpBalance(env, carol) == *startBalance - amount - baseFee);
-
4949
-
4950 tx = vault.withdraw(
-
4951 {.depositor = carol,
-
4952 .id = keylet.key,
-
4953 .amount = asset(baseFee)});
-
4954 env(tx, delegate::as(alice));
-
4955 env.close();
-
4956 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
+
4941 // use different initial amount to distinguish the source balance
+
4942 env.fund(XRP(10000), alice);
+
4943 env.fund(XRP(20000), bob);
+
4944 env.fund(XRP(30000), carol);
+
4945 env.close();
+
4946
+
4947 env(delegate::set(
+
4948 carol,
+
4949 alice,
+
4950 {"Payment",
+
4951 "VaultCreate",
+
4952 "VaultSet",
+
4953 "VaultDelete",
+
4954 "VaultDeposit",
+
4955 "VaultWithdraw",
+
4956 "VaultClawback"}));
4957
-
4958 tx = vault.withdraw(
-
4959 {.depositor = carol,
-
4960 .id = keylet.key,
-
4961 .amount = asset(amount - baseFee)});
-
4962 env(tx, delegate::as(alice));
-
4963 env.close();
-
4964 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
-
4965
-
4966 tx = vault.del({.owner = carol, .id = keylet.key});
-
4967 env(tx, delegate::as(alice));
-
4968 env.close();
-
4969 });
-
4970 }
+
4958 test(env, vault, args.asset);
+
4959 };
+
4960
+
4961 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
+
4962 testcase("delegated vault creation");
+
4963 auto startBalance = xrpBalance(env, carol);
+
4964 if (!BEAST_EXPECT(startBalance.has_value()))
+
4965 return;
+
4966
+
4967 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
+
4968 env(tx, delegate::as(alice));
+
4969 env.close();
+
4970 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
+
4971 });
+
4972
+
4973 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
+
4974 testcase("delegated deposit and withdrawal");
+
4975 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
+
4976 env(tx);
+
4977 env.close();
+
4978
+
4979 auto const amount = 1513;
+
4980 auto const baseFee = env.current()->fees().base;
+
4981
+
4982 auto startBalance = xrpBalance(env, carol);
+
4983 if (!BEAST_EXPECT(startBalance.has_value()))
+
4984 return;
+
4985
+
4986 tx = vault.deposit(
+
4987 {.depositor = carol,
+
4988 .id = keylet.key,
+
4989 .amount = asset(amount)});
+
4990 env(tx, delegate::as(alice));
+
4991 env.close();
+
4992 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
+
4993
+
4994 tx = vault.withdraw(
+
4995 {.depositor = carol,
+
4996 .id = keylet.key,
+
4997 .amount = asset(amount - 1)});
+
4998 env(tx, delegate::as(alice));
+
4999 env.close();
+
5000 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
+
5001
+
5002 tx = vault.withdraw(
+
5003 {.depositor = carol, .id = keylet.key, .amount = asset(1)});
+
5004 env(tx);
+
5005 env.close();
+
5006 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
+
5007 });
+
5008
+
5009 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
+
5010 testcase("delegated withdrawal same as base fee and deletion");
+
5011 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
+
5012 env(tx);
+
5013 env.close();
+
5014
+
5015 auto const amount = 25537;
+
5016 auto const baseFee = env.current()->fees().base;
+
5017
+
5018 auto startBalance = xrpBalance(env, carol);
+
5019 if (!BEAST_EXPECT(startBalance.has_value()))
+
5020 return;
+
5021
+
5022 tx = vault.deposit(
+
5023 {.depositor = carol,
+
5024 .id = keylet.key,
+
5025 .amount = asset(amount)});
+
5026 env(tx);
+
5027 env.close();
+
5028 BEAST_EXPECT(
+
5029 xrpBalance(env, carol) == *startBalance - amount - baseFee);
+
5030
+
5031 tx = vault.withdraw(
+
5032 {.depositor = carol,
+
5033 .id = keylet.key,
+
5034 .amount = asset(baseFee)});
+
5035 env(tx, delegate::as(alice));
+
5036 env.close();
+
5037 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
+
5038
+
5039 tx = vault.withdraw(
+
5040 {.depositor = carol,
+
5041 .id = keylet.key,
+
5042 .amount = asset(amount - baseFee)});
+
5043 env(tx, delegate::as(alice));
+
5044 env.close();
+
5045 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
+
5046
+
5047 tx = vault.del({.owner = carol, .id = keylet.key});
+
5048 env(tx, delegate::as(alice));
+
5049 env.close();
+
5050 });
+
5051 }
-
4971
-
4972public:
-
4973 void
-
-
4974 run() override
-
4975 {
-
4976 testSequences();
-
4977 testPreflight();
- - - -
4981 testWithMPT();
-
4982 testWithIOU();
- - - - -
4987 testScaleIOU();
-
4988 testRPC();
-
4989 testDelegate();
-
4990 }
+
5052
+
5053public:
+
5054 void
+
+
5055 run() override
+
5056 {
+
5057 testSequences();
+
5058 testPreflight();
+ + + +
5062 testWithMPT();
+
5063 testWithIOU();
+ + + + +
5068 testScaleIOU();
+
5069 testRPC();
+
5070 testDelegate();
+
5071 }
-
4991};
+
5072};
-
4992
-
4993BEAST_DEFINE_TESTSUITE_PRIO(Vault, app, ripple, 1);
-
4994
-
4995} // namespace ripple
+
5073
+
5074BEAST_DEFINE_TESTSUITE_PRIO(Vault, app, ripple, 1);
+
5075
+
5076} // namespace ripple
Represents a JSON value.
Definition json_value.h:131
A generic endpoint for log messages.
Definition Journal.h:41
@@ -5133,20 +5214,20 @@ $(document).ready(function() { init_codefold(0); }); - +
ripple::test::jtx::PrettyAsset PrettyAsset
- + - +
void testNonTransferableShares()
- +
static auto constexpr negativeAmount
-
void run() override
Runs the suite.
- +
void run() override
Runs the suite.
+ - +
constexpr value_type drops() const
Returns the number of drops.
Definition XRPAmount.h:158
Integers of any length that is a multiple of 32-bits.
Definition base_uint.h:67
diff --git a/View_8cpp_source.html b/View_8cpp_source.html index 66c6a91f0b..521921c3c9 100644 --- a/View_8cpp_source.html +++ b/View_8cpp_source.html @@ -3113,7 +3113,7 @@ $(document).ready(function() { init_codefold(0); });
2885 .truncate()};
2886
2887 Number const shareTotal = issuance->at(sfOutstandingAmount);
-
2888 shares = (shareTotal * (assets / assetTotal)).truncate();
+
2888 shares = ((shareTotal * assets) / assetTotal).truncate();
2889 return shares;
2890}
@@ -3144,7 +3144,7 @@ $(document).ready(function() { init_codefold(0); });
2914 false};
2915
2916 Number const shareTotal = issuance->at(sfOutstandingAmount);
-
2917 assets = assetTotal * (shares / shareTotal);
+
2917 assets = (assetTotal * shares) / shareTotal;
2918 return assets;
2919}
@@ -3172,7 +3172,7 @@ $(document).ready(function() { init_codefold(0); });
2940 if (assetTotal == 0)
2941 return shares;
2942 Number const shareTotal = issuance->at(sfOutstandingAmount);
-
2943 Number result = shareTotal * (assets / assetTotal);
+
2943 Number result = (shareTotal * assets) / assetTotal;
2944 if (truncate == TruncateShares::yes)
2945 result = result.truncate();
2946 shares = result;
@@ -3202,7 +3202,7 @@ $(document).ready(function() { init_codefold(0); });
2968 if (assetTotal == 0)
2969 return assets;
2970 Number const shareTotal = issuance->at(sfOutstandingAmount);
-
2971 assets = assetTotal * (shares / shareTotal);
+
2971 assets = (assetTotal * shares) / shareTotal;
2972 return assets;
2973}
diff --git a/classripple_1_1Vault__test.html b/classripple_1_1Vault__test.html index 0498eb9560..9091c87e27 100644 --- a/classripple_1_1Vault__test.html +++ b/classripple_1_1Vault__test.html @@ -572,7 +572,7 @@ Static Private Attributes
-

Definition at line 3072 of file Vault_test.cpp.

+

Definition at line 3153 of file Vault_test.cpp.

@@ -599,7 +599,7 @@ Static Private Attributes
-

Definition at line 3365 of file Vault_test.cpp.

+

Definition at line 3446 of file Vault_test.cpp.

@@ -626,7 +626,7 @@ Static Private Attributes
-

Definition at line 3470 of file Vault_test.cpp.

+

Definition at line 3551 of file Vault_test.cpp.

@@ -653,7 +653,7 @@ Static Private Attributes
-

Definition at line 3499 of file Vault_test.cpp.

+

Definition at line 3580 of file Vault_test.cpp.

@@ -680,7 +680,7 @@ Static Private Attributes
-

Definition at line 4363 of file Vault_test.cpp.

+

Definition at line 4444 of file Vault_test.cpp.

@@ -707,7 +707,7 @@ Static Private Attributes
-

Definition at line 4832 of file Vault_test.cpp.

+

Definition at line 4913 of file Vault_test.cpp.

@@ -738,7 +738,7 @@ Static Private Attributes

Implements beast::unit_test::suite.

-

Definition at line 4974 of file Vault_test.cpp.

+

Definition at line 5055 of file Vault_test.cpp.