rippled
Loading...
Searching...
No Matches
Vault_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2024 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx.h>
21#include <test/jtx/AMMTest.h>
22
23#include <xrpld/ledger/View.h>
24
25#include <xrpl/basics/base_uint.h>
26#include <xrpl/beast/unit_test/suite.h>
27#include <xrpl/json/json_forwards.h>
28#include <xrpl/json/json_value.h>
29#include <xrpl/protocol/AccountID.h>
30#include <xrpl/protocol/Asset.h>
31#include <xrpl/protocol/Feature.h>
32#include <xrpl/protocol/Indexes.h>
33#include <xrpl/protocol/Issue.h>
34#include <xrpl/protocol/MPTIssue.h>
35#include <xrpl/protocol/Protocol.h>
36#include <xrpl/protocol/SField.h>
37#include <xrpl/protocol/STAmount.h>
38#include <xrpl/protocol/STNumber.h>
39#include <xrpl/protocol/TER.h>
40#include <xrpl/protocol/TxFlags.h>
41#include <xrpl/protocol/XRPAmount.h>
42#include <xrpl/protocol/jss.h>
43
44namespace ripple {
45
47{
50
51 static auto constexpr negativeAmount =
52 [](PrettyAsset const& asset) -> PrettyAmount {
53 return {STAmount{asset.raw(), 1ul, 0, true, STAmount::unchecked{}}, ""};
54 };
55
56 void
58 {
59 using namespace test::jtx;
60
61 auto const testSequence = [this](
62 std::string const& prefix,
63 Env& env,
64 Account const& issuer,
65 Account const& owner,
66 Account const& depositor,
67 Account const& charlie,
68 Vault& vault,
69 PrettyAsset const& asset) {
70 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
71 tx[sfData] = "AFEED00E";
72 tx[sfAssetsMaximum] = asset(100).number();
73 env(tx);
74 env.close();
75 BEAST_EXPECT(env.le(keylet));
76
77 auto const share = [&env, keylet = keylet, this]() -> PrettyAsset {
78 auto const vault = env.le(keylet);
79 BEAST_EXPECT(vault != nullptr);
80 return MPTIssue(vault->at(sfShareMPTID));
81 }();
82
83 // Several 3rd party accounts which cannot receive funds
84 Account alice{"alice"};
85 Account dave{"dave"};
86 Account erin{"erin"}; // not authorized by issuer
87 env.fund(XRP(1000), alice, dave, erin);
88 env(fset(alice, asfDepositAuth));
89 env(fset(dave, asfRequireDest));
90 env.close();
91
92 {
93 testcase(prefix + " fail to deposit more than assets held");
94 auto tx = vault.deposit(
95 {.depositor = depositor,
96 .id = keylet.key,
97 .amount = asset(10000)});
98 env(tx, ter(tecINSUFFICIENT_FUNDS));
99 }
100
101 {
102 testcase(prefix + " deposit non-zero amount");
103 auto tx = vault.deposit(
104 {.depositor = depositor,
105 .id = keylet.key,
106 .amount = asset(50)});
107 env(tx);
108 }
109
110 {
111 testcase(prefix + " deposit non-zero amount again");
112 auto tx = vault.deposit(
113 {.depositor = depositor,
114 .id = keylet.key,
115 .amount = asset(50)});
116 env(tx);
117 }
118
119 {
120 testcase(prefix + " fail to delete non-empty vault");
121 auto tx = vault.del({.owner = owner, .id = keylet.key});
122 env(tx, ter(tecHAS_OBLIGATIONS));
123 }
124
125 {
126 testcase(prefix + " fail to update because wrong owner");
127 auto tx = vault.set({.owner = issuer, .id = keylet.key});
128 tx[sfAssetsMaximum] = asset(50).number();
129 env(tx, ter(tecNO_PERMISSION));
130 }
131
132 {
133 testcase(
134 prefix + " fail to set maximum lower than current amount");
135 auto tx = vault.set({.owner = owner, .id = keylet.key});
136 tx[sfAssetsMaximum] = asset(50).number();
137 env(tx, ter(tecLIMIT_EXCEEDED));
138 }
139
140 {
141 testcase(prefix + " set maximum higher than current amount");
142 auto tx = vault.set({.owner = owner, .id = keylet.key});
143 tx[sfAssetsMaximum] = asset(150).number();
144 env(tx);
145 }
146
147 {
148 testcase(prefix + " set data");
149 auto tx = vault.set({.owner = owner, .id = keylet.key});
150 tx[sfData] = "0";
151 env(tx);
152 }
153
154 {
155 testcase(prefix + " fail to set domain on public vault");
156 auto tx = vault.set({.owner = owner, .id = keylet.key});
157 tx[sfDomainID] = to_string(base_uint<256>(42ul));
158 env(tx, ter{tecNO_PERMISSION});
159 }
160
161 {
162 testcase(prefix + " fail to deposit more than maximum");
163 auto tx = vault.deposit(
164 {.depositor = depositor,
165 .id = keylet.key,
166 .amount = asset(100)});
167 env(tx, ter(tecLIMIT_EXCEEDED));
168 }
169
170 {
171 testcase(prefix + " reset maximum to zero i.e. not enforced");
172 auto tx = vault.set({.owner = owner, .id = keylet.key});
173 tx[sfAssetsMaximum] = asset(0).number();
174 env(tx);
175 }
176
177 {
178 testcase(prefix + " fail to withdraw more than assets held");
179 auto tx = vault.withdraw(
180 {.depositor = depositor,
181 .id = keylet.key,
182 .amount = asset(1000)});
183 env(tx, ter(tecINSUFFICIENT_FUNDS));
184 }
185
186 {
187 testcase(prefix + " deposit some more");
188 auto tx = vault.deposit(
189 {.depositor = depositor,
190 .id = keylet.key,
191 .amount = asset(100)});
192 env(tx);
193 }
194
195 {
196 testcase(prefix + " clawback some");
197 auto code =
198 asset.raw().native() ? ter(temMALFORMED) : ter(tesSUCCESS);
199 auto tx = vault.clawback(
200 {.issuer = issuer,
201 .id = keylet.key,
202 .holder = depositor,
203 .amount = asset(10)});
204 env(tx, code);
205 }
206
207 {
208 testcase(prefix + " clawback all");
209 auto code = asset.raw().native() ? ter(tecNO_PERMISSION)
210 : ter(tesSUCCESS);
211 auto tx = vault.clawback(
212 {.issuer = issuer, .id = keylet.key, .holder = depositor});
213 env(tx, code);
214 }
215
216 if (!asset.raw().native())
217 {
218 testcase(prefix + " deposit again");
219 auto tx = vault.deposit(
220 {.depositor = depositor,
221 .id = keylet.key,
222 .amount = asset(200)});
223 env(tx);
224 }
225
226 {
227 testcase(
228 prefix + " fail to withdraw to 3rd party lsfDepositAuth");
229 auto tx = vault.withdraw(
230 {.depositor = depositor,
231 .id = keylet.key,
232 .amount = asset(100)});
233 tx[sfDestination] = alice.human();
234 env(tx, ter{tecNO_PERMISSION});
235 }
236
237 {
238 testcase(prefix + " fail to withdraw to zero destination");
239 auto tx = vault.withdraw(
240 {.depositor = depositor,
241 .id = keylet.key,
242 .amount = asset(1000)});
243 tx[sfDestination] = "0";
244 env(tx, ter(temMALFORMED));
245 }
246
247 {
248 testcase(
249 prefix +
250 " fail to withdraw with tag but without destination");
251 auto tx = vault.withdraw(
252 {.depositor = depositor,
253 .id = keylet.key,
254 .amount = asset(1000)});
255 tx[sfDestinationTag] = "0";
256 env(tx, ter(temMALFORMED));
257 }
258
259 if (!asset.raw().native())
260 {
261 testcase(
262 prefix + " fail to withdraw to 3rd party no authorization");
263 auto tx = vault.withdraw(
264 {.depositor = depositor,
265 .id = keylet.key,
266 .amount = asset(100)});
267 tx[sfDestination] = erin.human();
268 env(tx,
269 ter{asset.raw().holds<Issue>() ? tecNO_LINE : tecNO_AUTH});
270 }
271
272 if (!asset.raw().native() && asset.raw().holds<Issue>())
273 {
274 testcase(prefix + " temporary authorization for 3rd party");
275 env(trust(erin, asset(1000)));
276 env(trust(issuer, asset(0), erin, tfSetfAuth));
277 env(pay(issuer, erin, asset(10)));
278
279 // Erin deposits all in vault, then sends shares to depositor
280 auto tx = vault.deposit(
281 {.depositor = erin, .id = keylet.key, .amount = asset(10)});
282 env(tx);
283 env(pay(erin, depositor, share(10)));
284
285 testcase(prefix + " withdraw to authorized 3rd party");
286 // Depositor withdraws shares, destined to Erin
287 tx = vault.withdraw(
288 {.depositor = depositor,
289 .id = keylet.key,
290 .amount = asset(10)});
291 tx[sfDestination] = erin.human();
292 env(tx);
293 // Erin returns assets to issuer
294 env(pay(erin, issuer, asset(10)));
295
296 testcase(prefix + " fail to pay to unauthorized 3rd party");
297 env(trust(erin, asset(0)));
298 // Erin has MPToken but is no longer authorized to hold assets
299 env(pay(depositor, erin, share(1)), ter{tecNO_LINE});
300 }
301
302 {
303 testcase(
304 prefix +
305 " fail to withdraw to 3rd party lsfRequireDestTag");
306 auto tx = vault.withdraw(
307 {.depositor = depositor,
308 .id = keylet.key,
309 .amount = asset(100)});
310 tx[sfDestination] = dave.human();
311 env(tx, ter{tecDST_TAG_NEEDED});
312 }
313
314 {
315 testcase(prefix + " withdraw to authorized 3rd party");
316 auto tx = vault.withdraw(
317 {.depositor = depositor,
318 .id = keylet.key,
319 .amount = asset(100)});
320 tx[sfDestination] = charlie.human();
321 env(tx);
322 }
323
324 {
325 testcase(prefix + " withdraw to issuer");
326 auto tx = vault.withdraw(
327 {.depositor = depositor,
328 .id = keylet.key,
329 .amount = asset(50)});
330 tx[sfDestination] = issuer.human();
331 env(tx);
332 }
333
334 {
335 testcase(prefix + " withdraw remaining assets");
336 auto tx = vault.withdraw(
337 {.depositor = depositor,
338 .id = keylet.key,
339 .amount = asset(50)});
340 env(tx);
341 }
342
343 {
344 testcase(prefix + " fail to delete because wrong owner");
345 auto tx = vault.del({.owner = issuer, .id = keylet.key});
346 env(tx, ter(tecNO_PERMISSION));
347 }
348
349 {
350 testcase(prefix + " delete empty vault");
351 auto tx = vault.del({.owner = owner, .id = keylet.key});
352 env(tx);
353 BEAST_EXPECT(!env.le(keylet));
354 }
355 };
356
357 auto testCases = [this, &testSequence](
358 std::string prefix,
360 Env & env,
361 Account const& issuer,
362 Account const& owner,
363 Account const& depositor,
364 Account const& charlie)> setup) {
365 Env env{*this, testable_amendments() | featureSingleAssetVault};
366 Account issuer{"issuer"};
367 Account owner{"owner"};
368 Account depositor{"depositor"};
369 Account charlie{"charlie"}; // authorized 3rd party
370 Vault vault{env};
371 env.fund(XRP(1000), issuer, owner, depositor, charlie);
372 env.close();
373 env(fset(issuer, asfAllowTrustLineClawback));
374 env(fset(issuer, asfRequireAuth));
375 env.close();
376 env.require(flags(issuer, asfAllowTrustLineClawback));
377 env.require(flags(issuer, asfRequireAuth));
378
379 PrettyAsset asset = setup(env, issuer, owner, depositor, charlie);
380 testSequence(
381 prefix, env, issuer, owner, depositor, charlie, vault, asset);
382 };
383
384 testCases(
385 "XRP",
386 [](Env& env,
387 Account const& issuer,
388 Account const& owner,
389 Account const& depositor,
390 Account const& charlie) -> PrettyAsset {
391 return {xrpIssue(), 1'000'000};
392 });
393
394 testCases(
395 "IOU",
396 [](Env& env,
397 Account const& issuer,
398 Account const& owner,
399 Account const& depositor,
400 Account const& charlie) -> Asset {
401 PrettyAsset asset = issuer["IOU"];
402 env(trust(owner, asset(1000)));
403 env(trust(depositor, asset(1000)));
404 env(trust(charlie, asset(1000)));
405 env(trust(issuer, asset(0), owner, tfSetfAuth));
406 env(trust(issuer, asset(0), depositor, tfSetfAuth));
407 env(trust(issuer, asset(0), charlie, tfSetfAuth));
408 env(pay(issuer, depositor, asset(1000)));
409 env.close();
410 return asset;
411 });
412
413 testCases(
414 "MPT",
415 [](Env& env,
416 Account const& issuer,
417 Account const& owner,
418 Account const& depositor,
419 Account const& charlie) -> Asset {
420 MPTTester mptt{env, issuer, mptInitNoFund};
421 mptt.create(
422 {.flags =
424 PrettyAsset asset = mptt.issuanceID();
425 mptt.authorize({.account = depositor});
426 mptt.authorize({.account = charlie});
427 env(pay(issuer, depositor, asset(1000)));
428 env.close();
429 return asset;
430 });
431 }
432
433 void
435 {
436 using namespace test::jtx;
437
438 struct CaseArgs
439 {
440 FeatureBitset features =
441 testable_amendments() | featureSingleAssetVault;
442 };
443
444 auto testCase = [&, this](
445 std::function<void(
446 Env & env,
447 Account const& issuer,
448 Account const& owner,
449 Asset const& asset,
450 Vault& vault)> test,
451 CaseArgs args = {}) {
452 Env env{*this, args.features};
453 Account issuer{"issuer"};
454 Account owner{"owner"};
455 Vault vault{env};
456 env.fund(XRP(1000), issuer, owner);
457 env.close();
458
459 env(fset(issuer, asfAllowTrustLineClawback));
460 env(fset(issuer, asfRequireAuth));
461 env.close();
462
463 PrettyAsset asset = issuer["IOU"];
464 env(trust(owner, asset(1000)));
465 env(trust(issuer, asset(0), owner, tfSetfAuth));
466 env(pay(issuer, owner, asset(1000)));
467 env.close();
468
469 test(env, issuer, owner, asset, vault);
470 };
471
472 testCase(
473 [&](Env& env,
474 Account const& issuer,
475 Account const& owner,
476 Asset const& asset,
477 Vault& vault) {
478 testcase("disabled single asset vault");
479
480 auto [tx, keylet] =
481 vault.create({.owner = owner, .asset = asset});
482 env(tx, ter{temDISABLED});
483
484 {
485 auto tx = vault.set({.owner = owner, .id = keylet.key});
486 env(tx, ter{temDISABLED});
487 }
488
489 {
490 auto tx = vault.deposit(
491 {.depositor = owner,
492 .id = keylet.key,
493 .amount = asset(10)});
494 env(tx, ter{temDISABLED});
495 }
496
497 {
498 auto tx = vault.withdraw(
499 {.depositor = owner,
500 .id = keylet.key,
501 .amount = asset(10)});
502 env(tx, ter{temDISABLED});
503 }
504
505 {
506 auto tx = vault.clawback(
507 {.issuer = issuer,
508 .id = keylet.key,
509 .holder = owner,
510 .amount = asset(10)});
511 env(tx, ter{temDISABLED});
512 }
513
514 {
515 auto tx = vault.del({.owner = owner, .id = keylet.key});
516 env(tx, ter{temDISABLED});
517 }
518 },
519 {.features = testable_amendments() - featureSingleAssetVault});
520
521 testCase([&](Env& env,
522 Account const& issuer,
523 Account const& owner,
524 Asset const& asset,
525 Vault& vault) {
526 testcase("invalid flags");
527
528 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
529 tx[sfFlags] = tfClearDeepFreeze;
530 env(tx, ter{temINVALID_FLAG});
531
532 {
533 auto tx = vault.set({.owner = owner, .id = keylet.key});
534 tx[sfFlags] = tfClearDeepFreeze;
535 env(tx, ter{temINVALID_FLAG});
536 }
537
538 {
539 auto tx = vault.deposit(
540 {.depositor = owner,
541 .id = keylet.key,
542 .amount = asset(10)});
543 tx[sfFlags] = tfClearDeepFreeze;
544 env(tx, ter{temINVALID_FLAG});
545 }
546
547 {
548 auto tx = vault.withdraw(
549 {.depositor = owner,
550 .id = keylet.key,
551 .amount = asset(10)});
552 tx[sfFlags] = tfClearDeepFreeze;
553 env(tx, ter{temINVALID_FLAG});
554 }
555
556 {
557 auto tx = vault.clawback(
558 {.issuer = issuer,
559 .id = keylet.key,
560 .holder = owner,
561 .amount = asset(10)});
562 tx[sfFlags] = tfClearDeepFreeze;
563 env(tx, ter{temINVALID_FLAG});
564 }
565
566 {
567 auto tx = vault.del({.owner = owner, .id = keylet.key});
568 tx[sfFlags] = tfClearDeepFreeze;
569 env(tx, ter{temINVALID_FLAG});
570 }
571 });
572
573 testCase([&](Env& env,
574 Account const& issuer,
575 Account const& owner,
576 Asset const& asset,
577 Vault& vault) {
578 testcase("invalid fee");
579
580 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
581 tx[jss::Fee] = "-1";
582 env(tx, ter{temBAD_FEE});
583
584 {
585 auto tx = vault.set({.owner = owner, .id = keylet.key});
586 tx[jss::Fee] = "-1";
587 env(tx, ter{temBAD_FEE});
588 }
589
590 {
591 auto tx = vault.deposit(
592 {.depositor = owner,
593 .id = keylet.key,
594 .amount = asset(10)});
595 tx[jss::Fee] = "-1";
596 env(tx, ter{temBAD_FEE});
597 }
598
599 {
600 auto tx = vault.withdraw(
601 {.depositor = owner,
602 .id = keylet.key,
603 .amount = asset(10)});
604 tx[jss::Fee] = "-1";
605 env(tx, ter{temBAD_FEE});
606 }
607
608 {
609 auto tx = vault.clawback(
610 {.issuer = issuer,
611 .id = keylet.key,
612 .holder = owner,
613 .amount = asset(10)});
614 tx[jss::Fee] = "-1";
615 env(tx, ter{temBAD_FEE});
616 }
617
618 {
619 auto tx = vault.del({.owner = owner, .id = keylet.key});
620 tx[jss::Fee] = "-1";
621 env(tx, ter{temBAD_FEE});
622 }
623 });
624
625 testCase(
626 [&](Env& env,
627 Account const&,
628 Account const& owner,
629 Asset const&,
630 Vault& vault) {
631 testcase("disabled permissioned domain");
632
633 auto [tx, keylet] =
634 vault.create({.owner = owner, .asset = xrpIssue()});
635 tx[sfDomainID] = to_string(base_uint<256>(42ul));
636 env(tx, ter{temDISABLED});
637
638 {
639 auto tx = vault.set({.owner = owner, .id = keylet.key});
640 tx[sfDomainID] = to_string(base_uint<256>(42ul));
641 env(tx, ter{temDISABLED});
642 }
643
644 {
645 auto tx = vault.set({.owner = owner, .id = keylet.key});
646 tx[sfDomainID] = "0";
647 env(tx, ter{temDISABLED});
648 }
649 },
650 {.features = (testable_amendments() | featureSingleAssetVault) -
651 featurePermissionedDomains});
652
653 testCase([&](Env& env,
654 Account const& issuer,
655 Account const& owner,
656 Asset const& asset,
657 Vault& vault) {
658 testcase("use zero vault");
659
660 auto [tx, keylet] =
661 vault.create({.owner = owner, .asset = xrpIssue()});
662
663 {
664 auto tx = vault.set({
665 .owner = owner,
666 .id = beast::zero,
667 });
668 env(tx, ter{temMALFORMED});
669 }
670
671 {
672 auto tx = vault.deposit(
673 {.depositor = owner,
674 .id = beast::zero,
675 .amount = asset(10)});
676 env(tx, ter(temMALFORMED));
677 }
678
679 {
680 auto tx = vault.withdraw(
681 {.depositor = owner,
682 .id = beast::zero,
683 .amount = asset(10)});
684 env(tx, ter{temMALFORMED});
685 }
686
687 {
688 auto tx = vault.clawback(
689 {.issuer = issuer,
690 .id = beast::zero,
691 .holder = owner,
692 .amount = asset(10)});
693 env(tx, ter{temMALFORMED});
694 }
695
696 {
697 auto tx = vault.del({
698 .owner = owner,
699 .id = beast::zero,
700 });
701 env(tx, ter{temMALFORMED});
702 }
703 });
704
705 testCase([&](Env& env,
706 Account const& issuer,
707 Account const& owner,
708 Asset const& asset,
709 Vault& vault) {
710 testcase("clawback from self");
711
712 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
713
714 {
715 auto tx = vault.clawback(
716 {.issuer = issuer,
717 .id = keylet.key,
718 .holder = issuer,
719 .amount = asset(10)});
720 env(tx, ter{temMALFORMED});
721 }
722 });
723
724 testCase([&](Env& env,
725 Account const&,
726 Account const& owner,
727 Asset const& asset,
728 Vault& vault) {
729 testcase("withdraw to bad destination");
730
731 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
732
733 {
734 auto tx = vault.withdraw(
735 {.depositor = owner,
736 .id = keylet.key,
737 .amount = asset(10)});
738 tx[jss::Destination] = "0";
739 env(tx, ter{temMALFORMED});
740 }
741 });
742
743 testCase([&](Env& env,
744 Account const&,
745 Account const& owner,
746 Asset const& asset,
747 Vault& vault) {
748 testcase("create or set invalid data");
749
750 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
751
752 {
753 auto tx = tx1;
754 tx[sfData] = "";
755 env(tx, ter(temMALFORMED));
756 }
757
758 {
759 auto tx = tx1;
760 // A hexadecimal string of 257 bytes.
761 tx[sfData] = std::string(514, 'A');
762 env(tx, ter(temMALFORMED));
763 }
764
765 {
766 auto tx = vault.set({.owner = owner, .id = keylet.key});
767 tx[sfData] = "";
768 env(tx, ter{temMALFORMED});
769 }
770
771 {
772 auto tx = vault.set({.owner = owner, .id = keylet.key});
773 // A hexadecimal string of 257 bytes.
774 tx[sfData] = std::string(514, 'A');
775 env(tx, ter{temMALFORMED});
776 }
777 });
778
779 testCase([&](Env& env,
780 Account const&,
781 Account const& owner,
782 Asset const& asset,
783 Vault& vault) {
784 testcase("set nothing updated");
785
786 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
787
788 {
789 auto tx = vault.set({.owner = owner, .id = keylet.key});
790 env(tx, ter{temMALFORMED});
791 }
792 });
793
794 testCase([&](Env& env,
795 Account const&,
796 Account const& owner,
797 Asset const& asset,
798 Vault& vault) {
799 testcase("create with invalid metadata");
800
801 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
802
803 {
804 auto tx = tx1;
805 tx[sfMPTokenMetadata] = "";
806 env(tx, ter(temMALFORMED));
807 }
808
809 {
810 auto tx = tx1;
811 // This metadata is for the share token.
812 // A hexadecimal string of 1025 bytes.
813 tx[sfMPTokenMetadata] = std::string(2050, 'B');
814 env(tx, ter(temMALFORMED));
815 }
816 });
817
818 testCase([&](Env& env,
819 Account const&,
820 Account const& owner,
821 Asset const& asset,
822 Vault& vault) {
823 testcase("set negative maximum");
824
825 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
826
827 {
828 auto tx = vault.set({.owner = owner, .id = keylet.key});
829 tx[sfAssetsMaximum] = negativeAmount(asset).number();
830 env(tx, ter{temMALFORMED});
831 }
832 });
833
834 testCase([&](Env& env,
835 Account const&,
836 Account const& owner,
837 Asset const& asset,
838 Vault& vault) {
839 testcase("invalid deposit amount");
840
841 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
842
843 {
844 auto tx = vault.deposit(
845 {.depositor = owner,
846 .id = keylet.key,
847 .amount = negativeAmount(asset)});
848 env(tx, ter(temBAD_AMOUNT));
849 }
850
851 {
852 auto tx = vault.deposit(
853 {.depositor = owner, .id = keylet.key, .amount = asset(0)});
854 env(tx, ter(temBAD_AMOUNT));
855 }
856 });
857
858 testCase([&](Env& env,
859 Account const&,
860 Account const& owner,
861 Asset const& asset,
862 Vault& vault) {
863 testcase("invalid set immutable flag");
864
865 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
866
867 {
868 auto tx = vault.set({.owner = owner, .id = keylet.key});
869 tx[sfFlags] = tfVaultPrivate;
870 env(tx, ter(temINVALID_FLAG));
871 }
872 });
873
874 testCase([&](Env& env,
875 Account const&,
876 Account const& owner,
877 Asset const& asset,
878 Vault& vault) {
879 testcase("invalid withdraw amount");
880
881 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
882
883 {
884 auto tx = vault.withdraw(
885 {.depositor = owner,
886 .id = keylet.key,
887 .amount = negativeAmount(asset)});
888 env(tx, ter(temBAD_AMOUNT));
889 }
890
891 {
892 auto tx = vault.withdraw(
893 {.depositor = owner, .id = keylet.key, .amount = asset(0)});
894 env(tx, ter(temBAD_AMOUNT));
895 }
896 });
897
898 testCase([&](Env& env,
899 Account const& issuer,
900 Account const& owner,
901 Asset const& asset,
902 Vault& vault) {
903 testcase("invalid clawback");
904
905 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
906
907 {
908 auto tx = vault.clawback(
909 {.issuer = owner,
910 .id = keylet.key,
911 .holder = issuer,
912 .amount = asset(50)});
913 env(tx, ter(temMALFORMED));
914 }
915
916 {
917 auto tx = vault.clawback(
918 {.issuer = issuer,
919 .id = keylet.key,
920 .holder = owner,
921 .amount = negativeAmount(asset)});
922 env(tx, ter(temBAD_AMOUNT));
923 }
924 });
925
926 testCase([&](Env& env,
927 Account const&,
928 Account const& owner,
929 Asset const& asset,
930 Vault& vault) {
931 testcase("invalid create");
932
933 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
934
935 {
936 auto tx = tx1;
937 tx[sfWithdrawalPolicy] = 0;
938 env(tx, ter(temMALFORMED));
939 }
940
941 {
942 auto tx = tx1;
943 tx[sfDomainID] = to_string(base_uint<256>(42ul));
944 env(tx, ter{temMALFORMED});
945 }
946
947 {
948 auto tx = tx1;
949 tx[sfAssetsMaximum] = negativeAmount(asset).number();
950 env(tx, ter{temMALFORMED});
951 }
952
953 {
954 auto tx = tx1;
955 tx[sfFlags] = tfVaultPrivate;
956 tx[sfDomainID] = "0";
957 env(tx, ter{temMALFORMED});
958 }
959 });
960 }
961
962 // Test for non-asset specific behaviors.
963 void
965 {
966 using namespace test::jtx;
967
968 auto testCase = [this](std::function<void(
969 Env & env,
970 Account const& issuer,
971 Account const& owner,
972 Account const& depositor,
973 Asset const& asset,
974 Vault& vault)> test) {
975 Env env{*this, testable_amendments() | featureSingleAssetVault};
976 Account issuer{"issuer"};
977 Account owner{"owner"};
978 Account depositor{"depositor"};
979 env.fund(XRP(1000), issuer, owner, depositor);
980 env.close();
981 Vault vault{env};
982 Asset asset = xrpIssue();
983
984 test(env, issuer, owner, depositor, asset, vault);
985 };
986
987 testCase([this](
988 Env& env,
989 Account const& issuer,
990 Account const& owner,
991 Account const& depositor,
992 PrettyAsset const& asset,
993 Vault& vault) {
994 testcase("nothing to set");
995 auto tx = vault.set({.owner = owner, .id = keylet::skip().key});
996 tx[sfAssetsMaximum] = asset(0).number();
997 env(tx, ter(tecNO_ENTRY));
998 });
999
1000 testCase([this](
1001 Env& env,
1002 Account const& issuer,
1003 Account const& owner,
1004 Account const& depositor,
1005 PrettyAsset const& asset,
1006 Vault& vault) {
1007 testcase("nothing to deposit to");
1008 auto tx = vault.deposit(
1009 {.depositor = depositor,
1010 .id = keylet::skip().key,
1011 .amount = asset(10)});
1012 env(tx, ter(tecNO_ENTRY));
1013 });
1014
1015 testCase([this](
1016 Env& env,
1017 Account const& issuer,
1018 Account const& owner,
1019 Account const& depositor,
1020 PrettyAsset const& asset,
1021 Vault& vault) {
1022 testcase("nothing to withdraw from");
1023 auto tx = vault.withdraw(
1024 {.depositor = depositor,
1025 .id = keylet::skip().key,
1026 .amount = asset(10)});
1027 env(tx, ter(tecNO_ENTRY));
1028 });
1029
1030 testCase([this](
1031 Env& env,
1032 Account const& issuer,
1033 Account const& owner,
1034 Account const& depositor,
1035 Asset const& asset,
1036 Vault& vault) {
1037 testcase("nothing to delete");
1038 auto tx = vault.del({.owner = owner, .id = keylet::skip().key});
1039 env(tx, ter(tecNO_ENTRY));
1040 });
1041
1042 testCase([this](
1043 Env& env,
1044 Account const& issuer,
1045 Account const& owner,
1046 Account const& depositor,
1047 Asset const& asset,
1048 Vault& vault) {
1049 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1050 testcase("transaction is good");
1051 env(tx);
1052 });
1053
1054 testCase([this](
1055 Env& env,
1056 Account const& issuer,
1057 Account const& owner,
1058 Account const& depositor,
1059 Asset const& asset,
1060 Vault& vault) {
1061 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1062 tx[sfWithdrawalPolicy] = 1;
1063 testcase("explicitly select withdrawal policy");
1064 env(tx);
1065 });
1066
1067 testCase([this](
1068 Env& env,
1069 Account const& issuer,
1070 Account const& owner,
1071 Account const& depositor,
1072 Asset const& asset,
1073 Vault& vault) {
1074 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1075 testcase("insufficient fee");
1076 env(tx, fee(env.current()->fees().base), ter(telINSUF_FEE_P));
1077 });
1078
1079 testCase([this](
1080 Env& env,
1081 Account const& issuer,
1082 Account const& owner,
1083 Account const& depositor,
1084 Asset const& asset,
1085 Vault& vault) {
1086 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1087 testcase("insufficient reserve");
1088 // It is possible to construct a complicated mathematical
1089 // expression for this amount, but it is sadly not easy.
1090 env(pay(owner, issuer, XRP(775)));
1091 env.close();
1092 env(tx, ter(tecINSUFFICIENT_RESERVE));
1093 });
1094
1095 testCase([this](
1096 Env& env,
1097 Account const& issuer,
1098 Account const& owner,
1099 Account const& depositor,
1100 Asset const& asset,
1101 Vault& vault) {
1102 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1103 tx[sfFlags] = tfVaultPrivate;
1104 tx[sfDomainID] = to_string(base_uint<256>(42ul));
1105 testcase("non-existing domain");
1106 env(tx, ter{tecOBJECT_NOT_FOUND});
1107 });
1108 }
1109
1110 void
1112 {
1113 using namespace test::jtx;
1114 {
1115 {
1116 testcase("IOU fail create frozen");
1117 Env env{*this, testable_amendments() | featureSingleAssetVault};
1118 Account issuer{"issuer"};
1119 Account owner{"owner"};
1120 env.fund(XRP(1000), issuer, owner);
1121 env.close();
1122 env(fset(issuer, asfGlobalFreeze));
1123 env.close();
1124
1125 Vault vault{env};
1126 Asset asset = issuer["IOU"];
1127 auto [tx, keylet] =
1128 vault.create({.owner = owner, .asset = asset});
1129
1130 env(tx, ter(tecFROZEN));
1131 env.close();
1132 }
1133
1134 {
1135 testcase("IOU fail create no ripling");
1136 Env env{*this, testable_amendments() | featureSingleAssetVault};
1137 Account issuer{"issuer"};
1138 Account owner{"owner"};
1139 env.fund(XRP(1000), issuer, owner);
1140 env.close();
1141 env(fclear(issuer, asfDefaultRipple));
1142 env.close();
1143
1144 Vault vault{env};
1145 Asset asset = issuer["IOU"];
1146 auto [tx, keylet] =
1147 vault.create({.owner = owner, .asset = asset});
1148 env(tx, ter(terNO_RIPPLE));
1149 env.close();
1150 }
1151
1152 {
1153 testcase("IOU no issuer");
1154 Env env{*this, testable_amendments() | featureSingleAssetVault};
1155 Account issuer{"issuer"};
1156 Account owner{"owner"};
1157 env.fund(XRP(1000), owner);
1158 env.close();
1159
1160 Vault vault{env};
1161 Asset asset = issuer["IOU"];
1162 {
1163 auto [tx, keylet] =
1164 vault.create({.owner = owner, .asset = asset});
1165 env(tx, ter(terNO_ACCOUNT));
1166 env.close();
1167 }
1168 }
1169 }
1170
1171 {
1172 testcase("IOU fail create vault for AMM LPToken");
1173 Env env{*this, testable_amendments() | featureSingleAssetVault};
1174 Account const gw("gateway");
1175 Account const alice("alice");
1176 Account const carol("carol");
1177 IOU const USD = gw["USD"];
1178
1179 auto const [asset1, asset2] =
1180 std::pair<STAmount, STAmount>(XRP(10000), USD(10000));
1181 auto tofund = [&](STAmount const& a) -> STAmount {
1182 if (a.native())
1183 {
1184 auto const defXRP = XRP(30000);
1185 if (a <= defXRP)
1186 return defXRP;
1187 return a + XRP(1000);
1188 }
1189 auto const defIOU = STAmount{a.issue(), 30000};
1190 if (a <= defIOU)
1191 return defIOU;
1192 return a + STAmount{a.issue(), 1000};
1193 };
1194 auto const toFund1 = tofund(asset1);
1195 auto const toFund2 = tofund(asset2);
1196 BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2);
1197
1198 if (!asset1.native() && !asset2.native())
1199 fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All);
1200 else if (asset1.native())
1201 fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All);
1202 else if (asset2.native())
1203 fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All);
1204
1205 AMM ammAlice(
1206 env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0});
1207
1208 Account const owner{"owner"};
1209 env.fund(XRP(1000000), owner);
1210
1211 Vault vault{env};
1212 auto [tx, k] =
1213 vault.create({.owner = owner, .asset = ammAlice.lptIssue()});
1214 env(tx, ter{tecWRONG_ASSET});
1215 env.close();
1216 }
1217 }
1218
1219 void
1221 {
1222 using namespace test::jtx;
1223
1224 Env env{*this, testable_amendments() | featureSingleAssetVault};
1225 Account issuer{"issuer"};
1226 Account owner{"owner"};
1227 Account depositor{"depositor"};
1228 env.fund(XRP(1000), issuer, owner, depositor);
1229 env.close();
1230 Vault vault{env};
1231
1232 MPTTester mptt{env, issuer, mptInitNoFund};
1233
1234 // Locked because that is the default flag.
1235 mptt.create();
1236 Asset asset = mptt.issuanceID();
1237 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1238 env(tx, ter(tecNO_AUTH));
1239 }
1240
1241 void
1243 {
1244 using namespace test::jtx;
1245
1246 Env env{*this, testable_amendments() | featureSingleAssetVault};
1247 Account issuer{"issuer"};
1248 Account owner{"owner"};
1249 Account depositor{"depositor"};
1250 env.fund(XRP(1000), issuer, owner, depositor);
1251 env.close();
1252
1253 Vault vault{env};
1254 PrettyAsset asset = issuer["IOU"];
1255 env.trust(asset(1000), owner);
1256 env(pay(issuer, owner, asset(100)));
1257 env.trust(asset(1000), depositor);
1258 env(pay(issuer, depositor, asset(100)));
1259 env.close();
1260
1261 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1262 tx[sfFlags] = tfVaultShareNonTransferable;
1263 env(tx);
1264 env.close();
1265
1266 {
1267 testcase("nontransferable deposits");
1268 auto tx1 = vault.deposit(
1269 {.depositor = depositor,
1270 .id = keylet.key,
1271 .amount = asset(40)});
1272 env(tx1);
1273
1274 auto tx2 = vault.deposit(
1275 {.depositor = owner, .id = keylet.key, .amount = asset(60)});
1276 env(tx2);
1277 env.close();
1278 }
1279
1280 auto const vaultAccount = //
1281 [&env, key = keylet.key, this]() -> AccountID {
1282 auto jvVault = env.rpc("vault_info", strHex(key));
1283
1284 BEAST_EXPECT(
1285 jvVault[jss::result][jss::vault][sfAssetsTotal] == "100");
1286 BEAST_EXPECT(
1287 jvVault[jss::result][jss::vault][jss::shares]
1288 [sfOutstandingAmount] == "100");
1289
1290 // Vault pseudo-account
1291 return parseBase58<AccountID>(
1292 jvVault[jss::result][jss::vault][jss::Account]
1293 .asString())
1294 .value();
1295 }();
1296
1297 auto const MptID = makeMptID(1, vaultAccount);
1298 Asset shares = MptID;
1299
1300 {
1301 testcase("nontransferable shares cannot be moved");
1302 env(pay(owner, depositor, shares(10)), ter{tecNO_AUTH});
1303 env(pay(depositor, owner, shares(10)), ter{tecNO_AUTH});
1304 }
1305
1306 {
1307 testcase("nontransferable shares can be used to withdraw");
1308 auto tx1 = vault.withdraw(
1309 {.depositor = depositor,
1310 .id = keylet.key,
1311 .amount = asset(20)});
1312 env(tx1);
1313
1314 auto tx2 = vault.withdraw(
1315 {.depositor = owner, .id = keylet.key, .amount = asset(30)});
1316 env(tx2);
1317 env.close();
1318 }
1319
1320 {
1321 testcase("nontransferable shares balance check");
1322 auto jvVault = env.rpc("vault_info", strHex(keylet.key));
1323 BEAST_EXPECT(
1324 jvVault[jss::result][jss::vault][sfAssetsTotal] == "50");
1325 BEAST_EXPECT(
1326 jvVault[jss::result][jss::vault][jss::shares]
1327 [sfOutstandingAmount] == "50");
1328 }
1329
1330 {
1331 testcase("nontransferable shares withdraw rest");
1332 auto tx1 = vault.withdraw(
1333 {.depositor = depositor,
1334 .id = keylet.key,
1335 .amount = asset(20)});
1336 env(tx1);
1337
1338 auto tx2 = vault.withdraw(
1339 {.depositor = owner, .id = keylet.key, .amount = asset(30)});
1340 env(tx2);
1341 env.close();
1342 }
1343
1344 {
1345 testcase("nontransferable shares delete empty vault");
1346 auto tx = vault.del({.owner = owner, .id = keylet.key});
1347 env(tx);
1348 BEAST_EXPECT(!env.le(keylet));
1349 }
1350 }
1351
1352 void
1354 {
1355 using namespace test::jtx;
1356
1357 struct CaseArgs
1358 {
1359 bool enableClawback = true;
1360 bool requireAuth = true;
1361 };
1362
1363 auto testCase = [this](
1364 std::function<void(
1365 Env & env,
1366 Account const& issuer,
1367 Account const& owner,
1368 Account const& depositor,
1369 Asset const& asset,
1370 Vault& vault,
1371 MPTTester& mptt)> test,
1372 CaseArgs args = {}) {
1373 Env env{*this, testable_amendments() | featureSingleAssetVault};
1374 Account issuer{"issuer"};
1375 Account owner{"owner"};
1376 Account depositor{"depositor"};
1377 env.fund(XRP(1000), issuer, owner, depositor);
1378 env.close();
1379 Vault vault{env};
1380
1381 MPTTester mptt{env, issuer, mptInitNoFund};
1382 auto const none = LedgerSpecificFlags(0);
1383 mptt.create(
1384 {.flags = tfMPTCanTransfer | tfMPTCanLock |
1385 (args.enableClawback ? tfMPTCanClawback : none) |
1386 (args.requireAuth ? tfMPTRequireAuth : none)});
1387 PrettyAsset asset = mptt.issuanceID();
1388 mptt.authorize({.account = owner});
1389 mptt.authorize({.account = depositor});
1390 if (args.requireAuth)
1391 {
1392 mptt.authorize({.account = issuer, .holder = owner});
1393 mptt.authorize({.account = issuer, .holder = depositor});
1394 }
1395
1396 env(pay(issuer, depositor, asset(1000)));
1397 env.close();
1398
1399 test(env, issuer, owner, depositor, asset, vault, mptt);
1400 };
1401
1402 testCase([this](
1403 Env& env,
1404 Account const& issuer,
1405 Account const& owner,
1406 Account const& depositor,
1407 PrettyAsset const& asset,
1408 Vault& vault,
1409 MPTTester& mptt) {
1410 testcase("MPT nothing to clawback from");
1411 auto tx = vault.clawback(
1412 {.issuer = issuer,
1413 .id = keylet::skip().key,
1414 .holder = depositor,
1415 .amount = asset(10)});
1416 env(tx, ter(tecNO_ENTRY));
1417 });
1418
1419 testCase([this](
1420 Env& env,
1421 Account const& issuer,
1422 Account const& owner,
1423 Account const& depositor,
1424 Asset const& asset,
1425 Vault& vault,
1426 MPTTester& mptt) {
1427 testcase("MPT global lock blocks create");
1428 mptt.set({.account = issuer, .flags = tfMPTLock});
1429 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1430 env(tx, ter(tecLOCKED));
1431 });
1432
1433 testCase([this](
1434 Env& env,
1435 Account const& issuer,
1436 Account const& owner,
1437 Account const& depositor,
1438 Asset const& asset,
1439 Vault& vault,
1440 MPTTester& mptt) {
1441 testcase("MPT global lock blocks deposit");
1442 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1443 env(tx);
1444 env.close();
1445
1446 mptt.set({.account = issuer, .flags = tfMPTLock});
1447 env.close();
1448
1449 tx = vault.deposit(
1450 {.depositor = depositor,
1451 .id = keylet.key,
1452 .amount = asset(100)});
1453 env(tx, ter{tecLOCKED});
1454 env.close();
1455
1456 // Can delete empty vault, even if global lock
1457 tx = vault.del({.owner = owner, .id = keylet.key});
1458 env(tx);
1459 });
1460
1461 testCase([this](
1462 Env& env,
1463 Account const& issuer,
1464 Account const& owner,
1465 Account const& depositor,
1466 Asset const& asset,
1467 Vault& vault,
1468 MPTTester& mptt) {
1469 testcase("MPT global lock blocks withdrawal");
1470 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1471 env(tx);
1472 env.close();
1473 tx = vault.deposit(
1474 {.depositor = depositor,
1475 .id = keylet.key,
1476 .amount = asset(100)});
1477 env(tx);
1478 env.close();
1479
1480 // Check that the OutstandingAmount field of MPTIssuance
1481 // accounts for the issued shares.
1482 auto v = env.le(keylet);
1483 BEAST_EXPECT(v);
1484 MPTID share = (*v)[sfShareMPTID];
1485 auto issuance = env.le(keylet::mptIssuance(share));
1486 BEAST_EXPECT(issuance);
1487 Number outstandingShares = issuance->at(sfOutstandingAmount);
1488 BEAST_EXPECT(outstandingShares == 100);
1489
1490 mptt.set({.account = issuer, .flags = tfMPTLock});
1491 env.close();
1492
1493 tx = vault.withdraw(
1494 {.depositor = depositor,
1495 .id = keylet.key,
1496 .amount = asset(100)});
1497 env(tx, ter(tecLOCKED));
1498
1499 tx[sfDestination] = issuer.human();
1500 env(tx, ter(tecLOCKED));
1501
1502 // Clawback is still permitted, even with global lock
1503 tx = vault.clawback(
1504 {.issuer = issuer,
1505 .id = keylet.key,
1506 .holder = depositor,
1507 .amount = asset(0)});
1508 env(tx);
1509 env.close();
1510
1511 // Can delete empty vault, even if global lock
1512 tx = vault.del({.owner = owner, .id = keylet.key});
1513 env(tx);
1514 });
1515
1516 testCase([this](
1517 Env& env,
1518 Account const& issuer,
1519 Account const& owner,
1520 Account const& depositor,
1521 PrettyAsset const& asset,
1522 Vault& vault,
1523 MPTTester& mptt) {
1524 testcase("MPT only issuer can clawback");
1525
1526 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1527 env(tx);
1528 env.close();
1529
1530 tx = vault.deposit(
1531 {.depositor = depositor,
1532 .id = keylet.key,
1533 .amount = asset(100)});
1534 env(tx);
1535 env.close();
1536
1537 {
1538 auto tx = vault.clawback(
1539 {.issuer = owner, .id = keylet.key, .holder = depositor});
1540 env(tx, ter(tecNO_PERMISSION));
1541 }
1542 });
1543
1544 testCase(
1545 [this](
1546 Env& env,
1547 Account const& issuer,
1548 Account const& owner,
1549 Account const& depositor,
1550 PrettyAsset const& asset,
1551 Vault& vault,
1552 MPTTester& mptt) {
1553 testcase(
1554 "MPT 3rd party without MPToken cannot be withdrawal "
1555 "destination");
1556
1557 auto [tx, keylet] =
1558 vault.create({.owner = owner, .asset = asset});
1559 env(tx);
1560 env.close();
1561
1562 tx = vault.deposit(
1563 {.depositor = depositor,
1564 .id = keylet.key,
1565 .amount = asset(100)});
1566 env(tx);
1567 env.close();
1568
1569 {
1570 // Set destination to 3rd party without MPToken
1571 Account charlie{"charlie"};
1572 env.fund(XRP(1000), charlie);
1573 env.close();
1574
1575 tx = vault.withdraw(
1576 {.depositor = depositor,
1577 .id = keylet.key,
1578 .amount = asset(100)});
1579 tx[sfDestination] = charlie.human();
1580 env(tx, ter(tecNO_AUTH));
1581 }
1582 },
1583 {.requireAuth = false});
1584
1585 testCase(
1586 [this](
1587 Env& env,
1588 Account const& issuer,
1589 Account const& owner,
1590 Account const& depositor,
1591 PrettyAsset const& asset,
1592 Vault& vault,
1593 MPTTester& mptt) {
1594 testcase("MPT depositor without MPToken cannot withdraw");
1595
1596 auto [tx, keylet] =
1597 vault.create({.owner = owner, .asset = asset});
1598 env(tx);
1599 env.close();
1600
1601 tx = vault.deposit(
1602 {.depositor = depositor,
1603 .id = keylet.key,
1604 .amount = asset(1000)});
1605 env(tx);
1606 env.close();
1607
1608 {
1609 // Remove depositor's MPToken and withdraw will fail
1610 mptt.authorize(
1611 {.account = depositor, .flags = tfMPTUnauthorize});
1612 env.close();
1613 auto const mptoken =
1614 env.le(keylet::mptoken(mptt.issuanceID(), depositor));
1615 BEAST_EXPECT(mptoken == nullptr);
1616
1617 tx = vault.withdraw(
1618 {.depositor = depositor,
1619 .id = keylet.key,
1620 .amount = asset(100)});
1621 env(tx, ter(tecNO_AUTH));
1622 }
1623
1624 {
1625 // Restore depositor's MPToken and withdraw will succeed
1626 mptt.authorize({.account = depositor});
1627 env.close();
1628
1629 tx = vault.withdraw(
1630 {.depositor = depositor,
1631 .id = keylet.key,
1632 .amount = asset(100)});
1633 env(tx);
1634 }
1635 },
1636 {.requireAuth = false});
1637
1638 testCase([this](
1639 Env& env,
1640 Account const& issuer,
1641 Account const& owner,
1642 Account const& depositor,
1643 PrettyAsset const& asset,
1644 Vault& vault,
1645 MPTTester& mptt) {
1646 testcase("MPT issuance deleted");
1647
1648 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1649 env(tx);
1650 env.close();
1651
1652 tx = vault.deposit(
1653 {.depositor = depositor,
1654 .id = keylet.key,
1655 .amount = asset(1000)});
1656 env(tx);
1657 env.close();
1658
1659 {
1660 auto tx = vault.clawback(
1661 {.issuer = issuer,
1662 .id = keylet.key,
1663 .holder = depositor,
1664 .amount = asset(0)});
1665 env(tx);
1666 }
1667
1668 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
1669 env.close();
1670
1671 {
1672 auto [tx, keylet] =
1673 vault.create({.owner = depositor, .asset = asset});
1674 env(tx, ter{tecOBJECT_NOT_FOUND});
1675 }
1676
1677 {
1678 auto tx = vault.deposit(
1679 {.depositor = depositor,
1680 .id = keylet.key,
1681 .amount = asset(10)});
1682 env(tx, ter{tecOBJECT_NOT_FOUND});
1683 }
1684
1685 {
1686 auto tx = vault.withdraw(
1687 {.depositor = depositor,
1688 .id = keylet.key,
1689 .amount = asset(10)});
1690 env(tx, ter{tecOBJECT_NOT_FOUND});
1691 }
1692
1693 {
1694 auto tx = vault.clawback(
1695 {.issuer = issuer,
1696 .id = keylet.key,
1697 .holder = depositor,
1698 .amount = asset(0)});
1699 env(tx, ter{tecOBJECT_NOT_FOUND});
1700 }
1701
1702 env(vault.del({.owner = owner, .id = keylet.key}));
1703 });
1704
1705 testCase(
1706 [this](
1707 Env& env,
1708 Account const& issuer,
1709 Account const& owner,
1710 Account const& depositor,
1711 PrettyAsset const& asset,
1712 Vault& vault,
1713 MPTTester& mptt) {
1714 testcase("MPT clawback disabled");
1715
1716 auto [tx, keylet] =
1717 vault.create({.owner = owner, .asset = asset});
1718 env(tx);
1719 env.close();
1720
1721 tx = vault.deposit(
1722 {.depositor = depositor,
1723 .id = keylet.key,
1724 .amount = asset(1000)});
1725 env(tx);
1726 env.close();
1727
1728 {
1729 auto tx = vault.clawback(
1730 {.issuer = issuer,
1731 .id = keylet.key,
1732 .holder = depositor,
1733 .amount = asset(0)});
1734 env(tx, ter{tecNO_PERMISSION});
1735 }
1736 },
1737 {.enableClawback = false});
1738
1739 testCase([this](
1740 Env& env,
1741 Account const& issuer,
1742 Account const& owner,
1743 Account const& depositor,
1744 Asset const& asset,
1745 Vault& vault,
1746 MPTTester& mptt) {
1747 testcase("MPT un-authorization");
1748 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1749 env(tx);
1750 env.close();
1751 tx = vault.deposit(
1752 {.depositor = depositor,
1753 .id = keylet.key,
1754 .amount = asset(1000)});
1755 env(tx);
1756 env.close();
1757
1758 mptt.authorize(
1759 {.account = issuer,
1760 .holder = depositor,
1761 .flags = tfMPTUnauthorize});
1762 env.close();
1763
1764 {
1765 auto tx = vault.withdraw(
1766 {.depositor = depositor,
1767 .id = keylet.key,
1768 .amount = asset(100)});
1769 env(tx, ter(tecNO_AUTH));
1770
1771 // Withdrawal to other (authorized) accounts works
1772 tx[sfDestination] = issuer.human();
1773 env(tx);
1774 tx[sfDestination] = owner.human();
1775 env(tx);
1776 env.close();
1777 }
1778
1779 {
1780 // Cannot deposit some more
1781 auto tx = vault.deposit(
1782 {.depositor = depositor,
1783 .id = keylet.key,
1784 .amount = asset(100)});
1785 env(tx, ter(tecNO_AUTH));
1786 }
1787
1788 // Clawback works
1789 tx = vault.clawback(
1790 {.issuer = issuer,
1791 .id = keylet.key,
1792 .holder = depositor,
1793 .amount = asset(800)});
1794 env(tx);
1795
1796 env(vault.del({.owner = owner, .id = keylet.key}));
1797 });
1798
1799 testCase([this](
1800 Env& env,
1801 Account const& issuer,
1802 Account const& owner,
1803 Account const& depositor,
1804 Asset const& asset,
1805 Vault& vault,
1806 MPTTester& mptt) {
1807 testcase("MPT lock of vault pseudo-account");
1808 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1809 env(tx);
1810 env.close();
1811
1812 auto const vaultAccount =
1813 [&env, keylet = keylet, this]() -> AccountID {
1814 auto const vault = env.le(keylet);
1815 BEAST_EXPECT(vault != nullptr);
1816 return vault->at(sfAccount);
1817 }();
1818
1819 tx = vault.deposit(
1820 {.depositor = depositor,
1821 .id = keylet.key,
1822 .amount = asset(100)});
1823 env(tx);
1824 env.close();
1825
1826 tx = [&]() {
1827 Json::Value jv;
1828 jv[jss::Account] = issuer.human();
1829 jv[sfMPTokenIssuanceID] =
1830 to_string(asset.get<MPTIssue>().getMptID());
1831 jv[jss::Holder] = toBase58(vaultAccount);
1832 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
1833 jv[jss::Flags] = tfMPTLock;
1834 return jv;
1835 }();
1836 env(tx);
1837 env.close();
1838
1839 tx = vault.deposit(
1840 {.depositor = depositor,
1841 .id = keylet.key,
1842 .amount = asset(100)});
1843 env(tx, ter(tecLOCKED));
1844
1845 tx = vault.withdraw(
1846 {.depositor = depositor,
1847 .id = keylet.key,
1848 .amount = asset(100)});
1849 env(tx, ter(tecLOCKED));
1850
1851 // Clawback works, even when locked
1852 tx = vault.clawback(
1853 {.issuer = issuer,
1854 .id = keylet.key,
1855 .holder = depositor,
1856 .amount = asset(100)});
1857 env(tx);
1858
1859 // Can delete an empty vault even when asset is locked.
1860 tx = vault.del({.owner = owner, .id = keylet.key});
1861 env(tx);
1862 });
1863
1864 {
1865 testcase("MPT shares to a vault");
1866
1867 Env env{*this, testable_amendments() | featureSingleAssetVault};
1868 Account owner{"owner"};
1869 Account issuer{"issuer"};
1870 env.fund(XRP(1000000), owner, issuer);
1871 env.close();
1872 Vault vault{env};
1873
1874 MPTTester mptt{env, issuer, mptInitNoFund};
1875 mptt.create(
1878 mptt.authorize({.account = owner});
1879 mptt.authorize({.account = issuer, .holder = owner});
1880 PrettyAsset asset = mptt.issuanceID();
1881 env(pay(issuer, owner, asset(100)));
1882 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
1883 env(tx1);
1884 env.close();
1885
1886 auto const shares = [&env, keylet = k1, this]() -> Asset {
1887 auto const vault = env.le(keylet);
1888 BEAST_EXPECT(vault != nullptr);
1889 return MPTIssue(vault->at(sfShareMPTID));
1890 }();
1891
1892 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
1893 env(tx2, ter{tecWRONG_ASSET});
1894 env.close();
1895 }
1896 }
1897
1898 void
1900 {
1901 using namespace test::jtx;
1902
1903 auto testCase =
1904 [&, this](
1905 std::function<void(
1906 Env & env,
1907 Account const& owner,
1908 Account const& issuer,
1909 Account const& charlie,
1910 std::function<AccountID(ripple::Keylet)> vaultAccount,
1911 Vault& vault,
1912 PrettyAsset const& asset,
1913 std::function<MPTID(ripple::Keylet)> issuanceId,
1915 test) {
1916 Env env{*this, testable_amendments() | featureSingleAssetVault};
1917 Account const owner{"owner"};
1918 Account const issuer{"issuer"};
1919 Account const charlie{"charlie"};
1920 Vault vault{env};
1921 env.fund(XRP(1000), issuer, owner, charlie);
1922 env(fset(issuer, asfAllowTrustLineClawback));
1923 env.close();
1924
1925 PrettyAsset const asset = issuer["IOU"];
1926 env.trust(asset(1000), owner);
1927 env.trust(asset(1000), charlie);
1928 env(pay(issuer, owner, asset(200)));
1929 env(rate(issuer, 1.25));
1930 env.close();
1931
1932 auto const [tx, keylet] =
1933 vault.create({.owner = owner, .asset = asset});
1934 env(tx);
1935 env.close();
1936
1937 auto const vaultAccount =
1938 [&env](ripple::Keylet keylet) -> AccountID {
1939 return env.le(keylet)->at(sfAccount);
1940 };
1941 auto const issuanceId = [&env](ripple::Keylet keylet) -> MPTID {
1942 return env.le(keylet)->at(sfShareMPTID);
1943 };
1944 auto const vaultBalance = //
1945 [&env, &vaultAccount, issue = asset.raw().get<Issue>()](
1946 ripple::Keylet keylet) -> PrettyAmount {
1947 auto const account = vaultAccount(keylet);
1948 auto const sle = env.le(keylet::line(account, issue));
1949 if (sle == nullptr)
1950 return {
1951 STAmount(issue, 0),
1952 env.lookup(issue.account).name()};
1953 auto amount = sle->getFieldAmount(sfBalance);
1954 amount.setIssuer(issue.account);
1955 if (account > issue.account)
1956 amount.negate();
1957 return {amount, env.lookup(issue.account).name()};
1958 };
1959
1960 test(
1961 env,
1962 owner,
1963 issuer,
1964 charlie,
1965 vaultAccount,
1966 vault,
1967 asset,
1968 issuanceId,
1969 vaultBalance);
1970 };
1971
1972 testCase([&, this](
1973 Env& env,
1974 Account const& owner,
1975 Account const& issuer,
1976 Account const&,
1977 auto vaultAccount,
1978 Vault& vault,
1979 PrettyAsset const& asset,
1980 auto&&...) {
1981 testcase("IOU cannot use different asset");
1982 PrettyAsset const foo = issuer["FOO"];
1983
1984 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1985 env(tx);
1986 env.close();
1987
1988 {
1989 // Cannot create new trustline to a vault
1990 auto tx = [&, account = vaultAccount(keylet)]() {
1991 Json::Value jv;
1992 jv[jss::Account] = issuer.human();
1993 {
1994 auto& ja = jv[jss::LimitAmount] =
1995 foo(0).value().getJson(JsonOptions::none);
1996 ja[jss::issuer] = toBase58(account);
1997 }
1998 jv[jss::TransactionType] = jss::TrustSet;
1999 jv[jss::Flags] = tfSetFreeze;
2000 return jv;
2001 }();
2002 env(tx, ter{tecNO_PERMISSION});
2003 env.close();
2004 }
2005
2006 {
2007 auto tx = vault.deposit(
2008 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
2009 env(tx, ter{tecWRONG_ASSET});
2010 env.close();
2011 }
2012
2013 {
2014 auto tx = vault.withdraw(
2015 {.depositor = issuer, .id = keylet.key, .amount = foo(20)});
2016 env(tx, ter{tecWRONG_ASSET});
2017 env.close();
2018 }
2019
2020 env(vault.del({.owner = owner, .id = keylet.key}));
2021 env.close();
2022 });
2023
2024 testCase([&, this](
2025 Env& env,
2026 Account const& owner,
2027 Account const& issuer,
2028 Account const& charlie,
2029 auto vaultAccount,
2030 Vault& vault,
2031 PrettyAsset const& asset,
2032 auto issuanceId,
2033 auto) {
2034 testcase("IOU frozen trust line to vault account");
2035
2036 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2037 env(tx);
2038 env.close();
2039
2040 env(vault.deposit(
2041 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2042 env.close();
2043
2044 Asset const share = Asset(issuanceId(keylet));
2045
2046 // Freeze the trustline to the vault
2047 auto trustSet = [&, account = vaultAccount(keylet)]() {
2048 Json::Value jv;
2049 jv[jss::Account] = issuer.human();
2050 {
2051 auto& ja = jv[jss::LimitAmount] =
2052 asset(0).value().getJson(JsonOptions::none);
2053 ja[jss::issuer] = toBase58(account);
2054 }
2055 jv[jss::TransactionType] = jss::TrustSet;
2056 jv[jss::Flags] = tfSetFreeze;
2057 return jv;
2058 }();
2059 env(trustSet);
2060 env.close();
2061
2062 {
2063 // Note, the "frozen" state of the trust line to vault account
2064 // is reported as "locked" state of the vault shares, because
2065 // this state is attached to shares by means of the transitive
2066 // isFrozen.
2067 auto tx = vault.deposit(
2068 {.depositor = owner,
2069 .id = keylet.key,
2070 .amount = asset(80)});
2071 env(tx, ter{tecLOCKED});
2072 }
2073
2074 {
2075 auto tx = vault.withdraw(
2076 {.depositor = owner,
2077 .id = keylet.key,
2078 .amount = asset(100)});
2079 env(tx, ter{tecLOCKED});
2080
2081 // also when trying to withdraw to a 3rd party
2082 tx[sfDestination] = charlie.human();
2083 env(tx, ter{tecLOCKED});
2084 env.close();
2085 }
2086
2087 {
2088 // Clawback works, even when locked
2089 auto tx = vault.clawback(
2090 {.issuer = issuer,
2091 .id = keylet.key,
2092 .holder = owner,
2093 .amount = asset(50)});
2094 env(tx);
2095 env.close();
2096 }
2097
2098 // Clear the frozen state
2099 trustSet[jss::Flags] = tfClearFreeze;
2100 env(trustSet);
2101 env.close();
2102
2103 env(vault.withdraw(
2104 {.depositor = owner, .id = keylet.key, .amount = share(50)}));
2105
2106 env(vault.del({.owner = owner, .id = keylet.key}));
2107 env.close();
2108 });
2109
2110 testCase([&, this](
2111 Env& env,
2112 Account const& owner,
2113 Account const& issuer,
2114 Account const& charlie,
2115 auto,
2116 Vault& vault,
2117 PrettyAsset const& asset,
2118 auto issuanceId,
2119 auto vaultBalance) {
2120 testcase("IOU transfer fees not applied");
2121
2122 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2123 env(tx);
2124 env.close();
2125
2126 env(vault.deposit(
2127 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2128 env.close();
2129
2130 auto const issue = asset.raw().get<Issue>();
2131 Asset const share = Asset(issuanceId(keylet));
2132
2133 // transfer fees ignored on deposit
2134 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2135 BEAST_EXPECT(vaultBalance(keylet) == asset(100));
2136
2137 {
2138 auto tx = vault.clawback(
2139 {.issuer = issuer,
2140 .id = keylet.key,
2141 .holder = owner,
2142 .amount = asset(50)});
2143 env(tx);
2144 env.close();
2145 }
2146
2147 // transfer fees ignored on clawback
2148 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2149 BEAST_EXPECT(vaultBalance(keylet) == asset(50));
2150
2151 env(vault.withdraw(
2152 {.depositor = owner, .id = keylet.key, .amount = share(20)}));
2153
2154 // transfer fees ignored on withdraw
2155 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2156 BEAST_EXPECT(vaultBalance(keylet) == asset(30));
2157
2158 {
2159 auto tx = vault.withdraw(
2160 {.depositor = owner,
2161 .id = keylet.key,
2162 .amount = share(30)});
2163 tx[sfDestination] = charlie.human();
2164 env(tx);
2165 }
2166
2167 // transfer fees ignored on withdraw to 3rd party
2168 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2169 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2170 BEAST_EXPECT(vaultBalance(keylet) == asset(0));
2171
2172 env(vault.del({.owner = owner, .id = keylet.key}));
2173 env.close();
2174 });
2175
2176 testCase([&, this](
2177 Env& env,
2178 Account const& owner,
2179 Account const& issuer,
2180 Account const& charlie,
2181 auto,
2182 Vault& vault,
2183 PrettyAsset const& asset,
2184 auto&&...) {
2185 testcase("IOU frozen trust line to depositor");
2186
2187 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2188 env(tx);
2189 env.close();
2190
2191 env(vault.deposit(
2192 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2193 env.close();
2194
2195 // Withdraw to 3rd party works
2196 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
2197 auto tx = vault.withdraw(
2198 {.depositor = owner,
2199 .id = keylet.key,
2200 .amount = asset(10)});
2201 tx[sfDestination] = charlie.human();
2202 return tx;
2203 }(keylet);
2204 env(withdrawToCharlie);
2205
2206 // Freeze the owner
2207 env(trust(issuer, asset(0), owner, tfSetFreeze));
2208 env.close();
2209
2210 // Cannot withdraw
2211 auto const withdraw = vault.withdraw(
2212 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
2213 env(withdraw, ter{tecFROZEN});
2214
2215 // Cannot withdraw to 3rd party
2216 env(withdrawToCharlie, ter{tecLOCKED});
2217 env.close();
2218
2219 {
2220 // Cannot deposit some more
2221 auto tx = vault.deposit(
2222 {.depositor = owner,
2223 .id = keylet.key,
2224 .amount = asset(10)});
2225 env(tx, ter{tecFROZEN});
2226 }
2227
2228 {
2229 // Clawback still works
2230 auto tx = vault.clawback(
2231 {.issuer = issuer,
2232 .id = keylet.key,
2233 .holder = owner,
2234 .amount = asset(0)});
2235 env(tx);
2236 env.close();
2237 }
2238
2239 env(vault.del({.owner = owner, .id = keylet.key}));
2240 env.close();
2241 });
2242
2243 testCase([&, this](
2244 Env& env,
2245 Account const& owner,
2246 Account const& issuer,
2247 Account const& charlie,
2248 auto,
2249 Vault& vault,
2250 PrettyAsset const& asset,
2251 auto&&...) {
2252 testcase("IOU no trust line to 3rd party");
2253
2254 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2255 env(tx);
2256 env.close();
2257
2258 env(vault.deposit(
2259 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2260 env.close();
2261
2262 Account const erin{"erin"};
2263 env.fund(XRP(1000), erin);
2264 env.close();
2265
2266 // Withdraw to 3rd party without trust line
2267 auto const tx1 = [&](ripple::Keylet keylet) {
2268 auto tx = vault.withdraw(
2269 {.depositor = owner,
2270 .id = keylet.key,
2271 .amount = asset(10)});
2272 tx[sfDestination] = erin.human();
2273 return tx;
2274 }(keylet);
2275 env(tx1, ter{tecNO_LINE});
2276 });
2277
2278 testCase([&, this](
2279 Env& env,
2280 Account const& owner,
2281 Account const& issuer,
2282 Account const& charlie,
2283 auto,
2284 Vault& vault,
2285 PrettyAsset const& asset,
2286 auto&&...) {
2287 testcase("IOU no trust line to depositor");
2288
2289 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2290 env(tx);
2291 env.close();
2292
2293 // reset limit, so deposit of all funds will delete the trust line
2294 env.trust(asset(0), owner);
2295 env.close();
2296
2297 env(vault.deposit(
2298 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2299 env.close();
2300
2301 auto trustline =
2302 env.le(keylet::line(owner, asset.raw().get<Issue>()));
2303 BEAST_EXPECT(trustline == nullptr);
2304
2305 // Withdraw without trust line, will succeed
2306 auto const tx1 = [&](ripple::Keylet keylet) {
2307 auto tx = vault.withdraw(
2308 {.depositor = owner,
2309 .id = keylet.key,
2310 .amount = asset(10)});
2311 return tx;
2312 }(keylet);
2313 env(tx1);
2314 });
2315
2316 testCase([&, this](
2317 Env& env,
2318 Account const& owner,
2319 Account const& issuer,
2320 Account const& charlie,
2321 auto,
2322 Vault& vault,
2323 PrettyAsset const& asset,
2324 auto&&...) {
2325 testcase("IOU frozen trust line to 3rd party");
2326
2327 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2328 env(tx);
2329 env.close();
2330
2331 env(vault.deposit(
2332 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2333 env.close();
2334
2335 // Withdraw to 3rd party works
2336 auto const withdrawToCharlie = [&](ripple::Keylet keylet) {
2337 auto tx = vault.withdraw(
2338 {.depositor = owner,
2339 .id = keylet.key,
2340 .amount = asset(10)});
2341 tx[sfDestination] = charlie.human();
2342 return tx;
2343 }(keylet);
2344 env(withdrawToCharlie);
2345
2346 // Freeze the 3rd party
2347 env(trust(issuer, asset(0), charlie, tfSetFreeze));
2348 env.close();
2349
2350 // Can withdraw
2351 auto const withdraw = vault.withdraw(
2352 {.depositor = owner, .id = keylet.key, .amount = asset(10)});
2353 env(withdraw);
2354 env.close();
2355
2356 // Cannot withdraw to 3rd party
2357 env(withdrawToCharlie, ter{tecFROZEN});
2358 env.close();
2359
2360 env(vault.clawback(
2361 {.issuer = issuer,
2362 .id = keylet.key,
2363 .holder = owner,
2364 .amount = asset(0)}));
2365 env.close();
2366
2367 env(vault.del({.owner = owner, .id = keylet.key}));
2368 env.close();
2369 });
2370
2371 testCase([&, this](
2372 Env& env,
2373 Account const& owner,
2374 Account const& issuer,
2375 Account const& charlie,
2376 auto,
2377 Vault& vault,
2378 PrettyAsset const& asset,
2379 auto&&...) {
2380 testcase("IOU global freeze");
2381
2382 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2383 env(tx);
2384 env.close();
2385
2386 env(vault.deposit(
2387 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2388 env.close();
2389
2390 env(fset(issuer, asfGlobalFreeze));
2391 env.close();
2392
2393 {
2394 // Cannot withdraw
2395 auto tx = vault.withdraw(
2396 {.depositor = owner,
2397 .id = keylet.key,
2398 .amount = asset(10)});
2399 env(tx, ter{tecFROZEN});
2400
2401 // Cannot withdraw to 3rd party
2402 tx[sfDestination] = charlie.human();
2403 env(tx, ter{tecFROZEN});
2404 env.close();
2405
2406 // Cannot deposit some more
2407 tx = vault.deposit(
2408 {.depositor = owner,
2409 .id = keylet.key,
2410 .amount = asset(10)});
2411
2412 env(tx, ter{tecFROZEN});
2413 }
2414
2415 // Clawback is permitted
2416 env(vault.clawback(
2417 {.issuer = issuer,
2418 .id = keylet.key,
2419 .holder = owner,
2420 .amount = asset(0)}));
2421 env.close();
2422
2423 env(vault.del({.owner = owner, .id = keylet.key}));
2424 env.close();
2425 });
2426 }
2427
2428 void
2430 {
2431 using namespace test::jtx;
2432
2433 testcase("private vault");
2434
2435 Env env{*this, testable_amendments() | featureSingleAssetVault};
2436 Account issuer{"issuer"};
2437 Account owner{"owner"};
2438 Account depositor{"depositor"};
2439 Account charlie{"charlie"};
2440 Account pdOwner{"pdOwner"};
2441 Account credIssuer1{"credIssuer1"};
2442 Account credIssuer2{"credIssuer2"};
2443 std::string const credType = "credential";
2444 Vault vault{env};
2445 env.fund(
2446 XRP(1000),
2447 issuer,
2448 owner,
2449 depositor,
2450 charlie,
2451 pdOwner,
2452 credIssuer1,
2453 credIssuer2);
2454 env.close();
2455 env(fset(issuer, asfAllowTrustLineClawback));
2456 env.close();
2457 env.require(flags(issuer, asfAllowTrustLineClawback));
2458
2459 PrettyAsset asset = issuer["IOU"];
2460 env.trust(asset(1000), owner);
2461 env(pay(issuer, owner, asset(500)));
2462 env.trust(asset(1000), depositor);
2463 env(pay(issuer, depositor, asset(500)));
2464 env.trust(asset(1000), charlie);
2465 env(pay(issuer, charlie, asset(5)));
2466 env.close();
2467
2468 auto [tx, keylet] = vault.create(
2469 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
2470 env(tx);
2471 env.close();
2472 BEAST_EXPECT(env.le(keylet));
2473
2474 {
2475 testcase("private vault owner can deposit");
2476 auto tx = vault.deposit(
2477 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
2478 env(tx);
2479 }
2480
2481 {
2482 testcase("private vault depositor not authorized yet");
2483 auto tx = vault.deposit(
2484 {.depositor = depositor,
2485 .id = keylet.key,
2486 .amount = asset(50)});
2487 env(tx, ter{tecNO_AUTH});
2488 }
2489
2490 {
2491 testcase("private vault cannot set non-existing domain");
2492 auto tx = vault.set({.owner = owner, .id = keylet.key});
2493 tx[sfDomainID] = to_string(base_uint<256>(42ul));
2494 env(tx, ter{tecOBJECT_NOT_FOUND});
2495 }
2496
2497 {
2498 testcase("private vault set domainId");
2499
2500 {
2501 pdomain::Credentials const credentials1{
2502 {.issuer = credIssuer1, .credType = credType}};
2503
2504 env(pdomain::setTx(pdOwner, credentials1));
2505 auto const domainId1 = [&]() {
2506 auto tx = env.tx()->getJson(JsonOptions::none);
2507 return pdomain::getNewDomain(env.meta());
2508 }();
2509
2510 auto tx = vault.set({.owner = owner, .id = keylet.key});
2511 tx[sfDomainID] = to_string(domainId1);
2512 env(tx);
2513 env.close();
2514
2515 // Update domain second time, should be harmless
2516 env(tx);
2517 env.close();
2518 }
2519
2520 {
2521 pdomain::Credentials const credentials{
2522 {.issuer = credIssuer1, .credType = credType},
2523 {.issuer = credIssuer2, .credType = credType}};
2524
2525 env(pdomain::setTx(pdOwner, credentials));
2526 auto const domainId = [&]() {
2527 auto tx = env.tx()->getJson(JsonOptions::none);
2528 return pdomain::getNewDomain(env.meta());
2529 }();
2530
2531 auto tx = vault.set({.owner = owner, .id = keylet.key});
2532 tx[sfDomainID] = to_string(domainId);
2533 env(tx);
2534 env.close();
2535 }
2536 }
2537
2538 {
2539 testcase("private vault depositor still not authorized");
2540 auto tx = vault.deposit(
2541 {.depositor = depositor,
2542 .id = keylet.key,
2543 .amount = asset(50)});
2544 env(tx, ter{tecNO_AUTH});
2545 env.close();
2546 }
2547
2548 auto const credKeylet =
2549 credentials::keylet(depositor, credIssuer1, credType);
2550 {
2551 testcase("private vault depositor now authorized");
2552 env(credentials::create(depositor, credIssuer1, credType));
2553 env(credentials::accept(depositor, credIssuer1, credType));
2554 env(credentials::create(charlie, credIssuer1, credType));
2555 // charlie's credential not accepted
2556 env.close();
2557 auto credSle = env.le(credKeylet);
2558 BEAST_EXPECT(credSle != nullptr);
2559
2560 auto tx = vault.deposit(
2561 {.depositor = depositor,
2562 .id = keylet.key,
2563 .amount = asset(50)});
2564 env(tx);
2565 env.close();
2566
2567 tx = vault.deposit(
2568 {.depositor = charlie, .id = keylet.key, .amount = asset(50)});
2569 env(tx, ter{tecNO_AUTH});
2570 env.close();
2571 }
2572
2573 {
2574 testcase("private vault depositor lost authorization");
2575 env(credentials::deleteCred(
2576 credIssuer1, depositor, credIssuer1, credType));
2577 env(credentials::deleteCred(
2578 credIssuer1, charlie, credIssuer1, credType));
2579 env.close();
2580 auto credSle = env.le(credKeylet);
2581 BEAST_EXPECT(credSle == nullptr);
2582
2583 auto tx = vault.deposit(
2584 {.depositor = depositor,
2585 .id = keylet.key,
2586 .amount = asset(50)});
2587 env(tx, ter{tecNO_AUTH});
2588 env.close();
2589 }
2590
2591 auto const shares = [&env, keylet = keylet, this]() -> Asset {
2592 auto const vault = env.le(keylet);
2593 BEAST_EXPECT(vault != nullptr);
2594 return MPTIssue(vault->at(sfShareMPTID));
2595 }();
2596
2597 {
2598 testcase("private vault expired authorization");
2599 uint32_t const closeTime = env.current()
2600 ->info()
2601 .parentCloseTime.time_since_epoch()
2602 .count();
2603 {
2604 auto tx0 =
2605 credentials::create(depositor, credIssuer2, credType);
2606 tx0[sfExpiration] = closeTime + 20;
2607 env(tx0);
2608 tx0 = credentials::create(charlie, credIssuer2, credType);
2609 tx0[sfExpiration] = closeTime + 20;
2610 env(tx0);
2611 env.close();
2612
2613 env(credentials::accept(depositor, credIssuer2, credType));
2614 env(credentials::accept(charlie, credIssuer2, credType));
2615 env.close();
2616 }
2617
2618 {
2619 auto tx1 = vault.deposit(
2620 {.depositor = depositor,
2621 .id = keylet.key,
2622 .amount = asset(50)});
2623 env(tx1);
2624 env.close();
2625
2626 auto const tokenKeylet = keylet::mptoken(
2627 shares.get<MPTIssue>().getMptID(), depositor.id());
2628 BEAST_EXPECT(env.le(tokenKeylet) != nullptr);
2629 }
2630
2631 {
2632 // time advance
2633 env.close();
2634 env.close();
2635 env.close();
2636
2637 auto const credsKeylet =
2638 credentials::keylet(depositor, credIssuer2, credType);
2639 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
2640
2641 auto tx2 = vault.deposit(
2642 {.depositor = depositor,
2643 .id = keylet.key,
2644 .amount = asset(1)});
2645 env(tx2, ter{tecEXPIRED});
2646 env.close();
2647
2648 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
2649 }
2650
2651 {
2652 auto const credsKeylet =
2653 credentials::keylet(charlie, credIssuer2, credType);
2654 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
2655 auto const tokenKeylet = keylet::mptoken(
2656 shares.get<MPTIssue>().getMptID(), charlie.id());
2657 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
2658
2659 auto tx3 = vault.deposit(
2660 {.depositor = charlie,
2661 .id = keylet.key,
2662 .amount = asset(2)});
2663 env(tx3, ter{tecEXPIRED});
2664
2665 env.close();
2666 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
2667 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
2668 }
2669 }
2670
2671 {
2672 testcase("private vault reset domainId");
2673 auto tx = vault.set({.owner = owner, .id = keylet.key});
2674 tx[sfDomainID] = "0";
2675 env(tx);
2676 env.close();
2677
2678 tx = vault.deposit(
2679 {.depositor = depositor,
2680 .id = keylet.key,
2681 .amount = asset(50)});
2682 env(tx, ter{tecNO_AUTH});
2683 env.close();
2684
2685 tx = vault.withdraw(
2686 {.depositor = depositor,
2687 .id = keylet.key,
2688 .amount = asset(50)});
2689 env(tx);
2690
2691 tx = vault.clawback(
2692 {.issuer = issuer,
2693 .id = keylet.key,
2694 .holder = depositor,
2695 .amount = asset(0)});
2696 env(tx);
2697
2698 tx = vault.clawback(
2699 {.issuer = issuer,
2700 .id = keylet.key,
2701 .holder = owner,
2702 .amount = asset(0)});
2703 env(tx);
2704
2705 tx = vault.del({
2706 .owner = owner,
2707 .id = keylet.key,
2708 });
2709 env(tx);
2710 }
2711 }
2712
2713 void
2715 {
2716 using namespace test::jtx;
2717
2718 testcase("private XRP vault");
2719
2720 Env env{*this, testable_amendments() | featureSingleAssetVault};
2721 Account owner{"owner"};
2722 Account depositor{"depositor"};
2723 Account alice{"charlie"};
2724 std::string const credType = "credential";
2725 Vault vault{env};
2726 env.fund(XRP(100000), owner, depositor, alice);
2727 env.close();
2728
2729 PrettyAsset asset = xrpIssue();
2730 auto [tx, keylet] = vault.create(
2731 {.owner = owner, .asset = asset, .flags = tfVaultPrivate});
2732 env(tx);
2733 env.close();
2734
2735 auto const [vaultAccount, issuanceId] =
2736 [&env, keylet = keylet, this]() -> std::tuple<AccountID, uint192> {
2737 auto const vault = env.le(keylet);
2738 BEAST_EXPECT(vault != nullptr);
2739 return {vault->at(sfAccount), vault->at(sfShareMPTID)};
2740 }();
2741 BEAST_EXPECT(env.le(keylet::account(vaultAccount)));
2742 BEAST_EXPECT(env.le(keylet::mptIssuance(issuanceId)));
2743 PrettyAsset shares{issuanceId};
2744
2745 {
2746 testcase("private XRP vault owner can deposit");
2747 auto tx = vault.deposit(
2748 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
2749 env(tx);
2750 }
2751
2752 {
2753 testcase("private XRP vault cannot pay shares to depositor yet");
2754 env(pay(owner, depositor, shares(1)), ter{tecNO_AUTH});
2755 }
2756
2757 {
2758 testcase("private XRP vault depositor not authorized yet");
2759 auto tx = vault.deposit(
2760 {.depositor = depositor,
2761 .id = keylet.key,
2762 .amount = asset(50)});
2763 env(tx, ter{tecNO_AUTH});
2764 }
2765
2766 {
2767 testcase("private XRP vault set DomainID");
2768 pdomain::Credentials const credentials{
2769 {.issuer = owner, .credType = credType}};
2770
2771 env(pdomain::setTx(owner, credentials));
2772 auto const domainId = [&]() {
2773 auto tx = env.tx()->getJson(JsonOptions::none);
2774 return pdomain::getNewDomain(env.meta());
2775 }();
2776
2777 auto tx = vault.set({.owner = owner, .id = keylet.key});
2778 tx[sfDomainID] = to_string(domainId);
2779 env(tx);
2780 env.close();
2781 }
2782
2783 auto const credKeylet = credentials::keylet(depositor, owner, credType);
2784 {
2785 testcase("private XRP vault depositor now authorized");
2786 env(credentials::create(depositor, owner, credType));
2787 env(credentials::accept(depositor, owner, credType));
2788 env.close();
2789
2790 BEAST_EXPECT(env.le(credKeylet));
2791 auto tx = vault.deposit(
2792 {.depositor = depositor,
2793 .id = keylet.key,
2794 .amount = asset(50)});
2795 env(tx);
2796 env.close();
2797 }
2798
2799 {
2800 testcase("private XRP vault can pay shares to depositor");
2801 env(pay(owner, depositor, shares(1)));
2802 }
2803
2804 {
2805 testcase("private XRP vault cannot pay shares to 3rd party");
2806 Json::Value jv;
2807 jv[sfAccount] = alice.human();
2808 jv[sfTransactionType] = jss::MPTokenAuthorize;
2809 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
2810 env(jv);
2811 env.close();
2812
2813 env(pay(owner, alice, shares(1)), ter{tecNO_AUTH});
2814 }
2815 }
2816
2817 void
2819 {
2820 using namespace test::jtx;
2821
2822 testcase("failed pseudo-account allocation");
2823 Env env{*this, testable_amendments() | featureSingleAssetVault};
2824 Account const owner{"owner"};
2825 Vault vault{env};
2826 env.fund(XRP(1000), owner);
2827
2828 auto const keylet = keylet::vault(owner.id(), env.seq(owner));
2829 for (int i = 0; i < 256; ++i)
2830 {
2831 AccountID const accountId =
2832 ripple::pseudoAccountAddress(*env.current(), keylet.key);
2833
2834 env(pay(env.master.id(), accountId, XRP(1000)),
2835 seq(autofill),
2836 fee(autofill),
2837 sig(autofill));
2838 }
2839
2840 auto [tx, keylet1] =
2841 vault.create({.owner = owner, .asset = xrpIssue()});
2842 BEAST_EXPECT(keylet.key == keylet1.key);
2843 env(tx, ter{terADDRESS_COLLISION});
2844 }
2845
2846 void
2848 {
2849 using namespace test::jtx;
2850
2851 testcase("RPC");
2852 Env env{*this, testable_amendments() | featureSingleAssetVault};
2853 Account const owner{"owner"};
2854 Account const issuer{"issuer"};
2855 Vault vault{env};
2856 env.fund(XRP(1000), issuer, owner);
2857 env.close();
2858
2859 PrettyAsset asset = issuer["IOU"];
2860 env.trust(asset(1000), owner);
2861 env(pay(issuer, owner, asset(200)));
2862 env.close();
2863
2864 auto const sequence = env.seq(owner);
2865 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2866 env(tx);
2867 env.close();
2868
2869 // Set some fields
2870 {
2871 auto tx1 = vault.deposit(
2872 {.depositor = owner, .id = keylet.key, .amount = asset(50)});
2873 env(tx1);
2874
2875 auto tx2 = vault.set({.owner = owner, .id = keylet.key});
2876 tx2[sfAssetsMaximum] = asset(1000).number();
2877 env(tx2);
2878 env.close();
2879 }
2880
2881 auto const sleVault = [&env, keylet = keylet, this]() {
2882 auto const vault = env.le(keylet);
2883 BEAST_EXPECT(vault != nullptr);
2884 return vault;
2885 }();
2886
2887 auto const check = [&, keylet = keylet, sle = sleVault, this](
2888 Json::Value const& vault,
2889 Json::Value const& issuance = Json::nullValue) {
2890 BEAST_EXPECT(vault.isObject());
2891
2892 constexpr auto checkString =
2893 [](auto& node, SField const& field, std::string v) -> bool {
2894 return node.isMember(field.fieldName) &&
2895 node[field.fieldName].isString() &&
2896 node[field.fieldName] == v;
2897 };
2898 constexpr auto checkObject =
2899 [](auto& node, SField const& field, Json::Value v) -> bool {
2900 return node.isMember(field.fieldName) &&
2901 node[field.fieldName].isObject() &&
2902 node[field.fieldName] == v;
2903 };
2904 constexpr auto checkInt =
2905 [](auto& node, SField const& field, int v) -> bool {
2906 return node.isMember(field.fieldName) &&
2907 ((node[field.fieldName].isInt() &&
2908 node[field.fieldName] == Json::Int(v)) ||
2909 (node[field.fieldName].isUInt() &&
2910 node[field.fieldName] == Json::UInt(v)));
2911 };
2912
2913 BEAST_EXPECT(vault["LedgerEntryType"].asString() == "Vault");
2914 BEAST_EXPECT(vault[jss::index].asString() == strHex(keylet.key));
2915 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
2916 // Ignore all other standard fields, this test doesn't care
2917
2918 BEAST_EXPECT(
2919 checkString(vault, sfAccount, toBase58(sle->at(sfAccount))));
2920 BEAST_EXPECT(
2921 checkObject(vault, sfAsset, to_json(sle->at(sfAsset))));
2922 BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
2923 BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
2924 BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
2925 BEAST_EXPECT(checkString(vault, sfLossUnrealized, "0"));
2926
2927 auto const strShareID = strHex(sle->at(sfShareMPTID));
2928 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
2929 BEAST_EXPECT(checkString(vault, sfOwner, toBase58(owner.id())));
2930 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
2931 BEAST_EXPECT(checkInt(
2932 vault, sfWithdrawalPolicy, vaultStrategyFirstComeFirstServe));
2933
2934 if (issuance.isObject())
2935 {
2936 BEAST_EXPECT(
2937 issuance["LedgerEntryType"].asString() ==
2938 "MPTokenIssuance");
2939 BEAST_EXPECT(
2940 issuance[jss::mpt_issuance_id].asString() == strShareID);
2941 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
2942 BEAST_EXPECT(checkInt(
2943 issuance,
2944 sfFlags,
2946 BEAST_EXPECT(checkString(issuance, sfOutstandingAmount, "50"));
2947 }
2948 };
2949
2950 {
2951 testcase("RPC ledger_entry selected by key");
2952 Json::Value jvParams;
2953 jvParams[jss::ledger_index] = jss::validated;
2954 jvParams[jss::vault] = strHex(keylet.key);
2955 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2956
2957 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
2958 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
2959 check(jvVault[jss::result][jss::node]);
2960 }
2961
2962 {
2963 testcase("RPC ledger_entry selected by owner and seq");
2964 Json::Value jvParams;
2965 jvParams[jss::ledger_index] = jss::validated;
2966 jvParams[jss::vault][jss::owner] = owner.human();
2967 jvParams[jss::vault][jss::seq] = sequence;
2968 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2969
2970 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
2971 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
2972 check(jvVault[jss::result][jss::node]);
2973 }
2974
2975 {
2976 testcase("RPC ledger_entry cannot find vault by key");
2977 Json::Value jvParams;
2978 jvParams[jss::ledger_index] = jss::validated;
2979 jvParams[jss::vault] = to_string(uint256(42));
2980 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2981 BEAST_EXPECT(
2982 jvVault[jss::result][jss::error].asString() == "entryNotFound");
2983 }
2984
2985 {
2986 testcase("RPC ledger_entry cannot find vault by owner and seq");
2987 Json::Value jvParams;
2988 jvParams[jss::ledger_index] = jss::validated;
2989 jvParams[jss::vault][jss::owner] = issuer.human();
2990 jvParams[jss::vault][jss::seq] = 1'000'000;
2991 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
2992 BEAST_EXPECT(
2993 jvVault[jss::result][jss::error].asString() == "entryNotFound");
2994 }
2995
2996 {
2997 testcase("RPC ledger_entry malformed key");
2998 Json::Value jvParams;
2999 jvParams[jss::ledger_index] = jss::validated;
3000 jvParams[jss::vault] = 42;
3001 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
3002 BEAST_EXPECT(
3003 jvVault[jss::result][jss::error].asString() ==
3004 "malformedRequest");
3005 }
3006
3007 {
3008 testcase("RPC ledger_entry malformed owner");
3009 Json::Value jvParams;
3010 jvParams[jss::ledger_index] = jss::validated;
3011 jvParams[jss::vault][jss::owner] = 42;
3012 jvParams[jss::vault][jss::seq] = sequence;
3013 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
3014 BEAST_EXPECT(
3015 jvVault[jss::result][jss::error].asString() ==
3016 "malformedOwner");
3017 }
3018
3019 {
3020 testcase("RPC ledger_entry malformed seq");
3021 Json::Value jvParams;
3022 jvParams[jss::ledger_index] = jss::validated;
3023 jvParams[jss::vault][jss::owner] = issuer.human();
3024 jvParams[jss::vault][jss::seq] = "foo";
3025 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
3026 BEAST_EXPECT(
3027 jvVault[jss::result][jss::error].asString() ==
3028 "malformedRequest");
3029 }
3030
3031 {
3032 testcase("RPC ledger_entry zero seq");
3033 Json::Value jvParams;
3034 jvParams[jss::ledger_index] = jss::validated;
3035 jvParams[jss::vault][jss::owner] = issuer.human();
3036 jvParams[jss::vault][jss::seq] = 0;
3037 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
3038 BEAST_EXPECT(
3039 jvVault[jss::result][jss::error].asString() ==
3040 "malformedRequest");
3041 }
3042
3043 {
3044 testcase("RPC ledger_entry negative seq");
3045 Json::Value jvParams;
3046 jvParams[jss::ledger_index] = jss::validated;
3047 jvParams[jss::vault][jss::owner] = issuer.human();
3048 jvParams[jss::vault][jss::seq] = -1;
3049 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
3050 BEAST_EXPECT(
3051 jvVault[jss::result][jss::error].asString() ==
3052 "malformedRequest");
3053 }
3054
3055 {
3056 testcase("RPC ledger_entry oversized seq");
3057 Json::Value jvParams;
3058 jvParams[jss::ledger_index] = jss::validated;
3059 jvParams[jss::vault][jss::owner] = issuer.human();
3060 jvParams[jss::vault][jss::seq] = 1e20;
3061 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
3062 BEAST_EXPECT(
3063 jvVault[jss::result][jss::error].asString() ==
3064 "malformedRequest");
3065 }
3066
3067 {
3068 testcase("RPC ledger_entry bool seq");
3069 Json::Value jvParams;
3070 jvParams[jss::ledger_index] = jss::validated;
3071 jvParams[jss::vault][jss::owner] = issuer.human();
3072 jvParams[jss::vault][jss::seq] = true;
3073 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
3074 BEAST_EXPECT(
3075 jvVault[jss::result][jss::error].asString() ==
3076 "malformedRequest");
3077 }
3078
3079 {
3080 testcase("RPC account_objects");
3081
3082 Json::Value jvParams;
3083 jvParams[jss::account] = owner.human();
3084 jvParams[jss::type] = jss::vault;
3085 auto jv = env.rpc(
3086 "json", "account_objects", to_string(jvParams))[jss::result];
3087
3088 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
3089 check(jv[jss::account_objects][0u]);
3090 }
3091
3092 {
3093 testcase("RPC ledger_data");
3094
3095 Json::Value jvParams;
3096 jvParams[jss::ledger_index] = jss::validated;
3097 jvParams[jss::binary] = false;
3098 jvParams[jss::type] = jss::vault;
3099 Json::Value jv =
3100 env.rpc("json", "ledger_data", to_string(jvParams));
3101 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
3102 check(jv[jss::result][jss::state][0u]);
3103 }
3104
3105 {
3106 testcase("RPC vault_info command line");
3107 Json::Value jv =
3108 env.rpc("vault_info", strHex(keylet.key), "validated");
3109
3110 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
3111 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
3112 check(
3113 jv[jss::result][jss::vault],
3114 jv[jss::result][jss::vault][jss::shares]);
3115 }
3116
3117 {
3118 testcase("RPC vault_info json");
3119 Json::Value jvParams;
3120 jvParams[jss::ledger_index] = jss::validated;
3121 jvParams[jss::vault_id] = strHex(keylet.key);
3122 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3123
3124 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
3125 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
3126 check(
3127 jv[jss::result][jss::vault],
3128 jv[jss::result][jss::vault][jss::shares]);
3129 }
3130
3131 {
3132 testcase("RPC vault_info invalid vault_id");
3133 Json::Value jvParams;
3134 jvParams[jss::ledger_index] = jss::validated;
3135 jvParams[jss::vault_id] = "foobar";
3136 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3137 BEAST_EXPECT(
3138 jv[jss::result][jss::error].asString() == "malformedRequest");
3139 }
3140
3141 {
3142 testcase("RPC vault_info json invalid index");
3143 Json::Value jvParams;
3144 jvParams[jss::ledger_index] = jss::validated;
3145 jvParams[jss::vault_id] = 0;
3146 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3147 BEAST_EXPECT(
3148 jv[jss::result][jss::error].asString() == "malformedRequest");
3149 }
3150
3151 {
3152 testcase("RPC vault_info json by owner and sequence");
3153 Json::Value jvParams;
3154 jvParams[jss::ledger_index] = jss::validated;
3155 jvParams[jss::owner] = owner.human();
3156 jvParams[jss::seq] = sequence;
3157 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3158
3159 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
3160 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
3161 check(
3162 jv[jss::result][jss::vault],
3163 jv[jss::result][jss::vault][jss::shares]);
3164 }
3165
3166 {
3167 testcase("RPC vault_info json malformed sequence");
3168 Json::Value jvParams;
3169 jvParams[jss::ledger_index] = jss::validated;
3170 jvParams[jss::owner] = owner.human();
3171 jvParams[jss::seq] = "foobar";
3172 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3173 BEAST_EXPECT(
3174 jv[jss::result][jss::error].asString() == "malformedRequest");
3175 }
3176
3177 {
3178 testcase("RPC vault_info json invalid sequence");
3179 Json::Value jvParams;
3180 jvParams[jss::ledger_index] = jss::validated;
3181 jvParams[jss::owner] = owner.human();
3182 jvParams[jss::seq] = 0;
3183 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3184 BEAST_EXPECT(
3185 jv[jss::result][jss::error].asString() == "malformedRequest");
3186 }
3187
3188 {
3189 testcase("RPC vault_info json negative sequence");
3190 Json::Value jvParams;
3191 jvParams[jss::ledger_index] = jss::validated;
3192 jvParams[jss::owner] = owner.human();
3193 jvParams[jss::seq] = -1;
3194 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3195 BEAST_EXPECT(
3196 jv[jss::result][jss::error].asString() == "malformedRequest");
3197 }
3198
3199 {
3200 testcase("RPC vault_info json oversized sequence");
3201 Json::Value jvParams;
3202 jvParams[jss::ledger_index] = jss::validated;
3203 jvParams[jss::owner] = owner.human();
3204 jvParams[jss::seq] = 1e20;
3205 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3206 BEAST_EXPECT(
3207 jv[jss::result][jss::error].asString() == "malformedRequest");
3208 }
3209
3210 {
3211 testcase("RPC vault_info json bool sequence");
3212 Json::Value jvParams;
3213 jvParams[jss::ledger_index] = jss::validated;
3214 jvParams[jss::owner] = owner.human();
3215 jvParams[jss::seq] = true;
3216 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3217 BEAST_EXPECT(
3218 jv[jss::result][jss::error].asString() == "malformedRequest");
3219 }
3220
3221 {
3222 testcase("RPC vault_info json malformed owner");
3223 Json::Value jvParams;
3224 jvParams[jss::ledger_index] = jss::validated;
3225 jvParams[jss::owner] = "foobar";
3226 jvParams[jss::seq] = sequence;
3227 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3228 BEAST_EXPECT(
3229 jv[jss::result][jss::error].asString() == "malformedRequest");
3230 }
3231
3232 {
3233 testcase("RPC vault_info json invalid combination only owner");
3234 Json::Value jvParams;
3235 jvParams[jss::ledger_index] = jss::validated;
3236 jvParams[jss::owner] = owner.human();
3237 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3238 BEAST_EXPECT(
3239 jv[jss::result][jss::error].asString() == "malformedRequest");
3240 }
3241
3242 {
3243 testcase("RPC vault_info json invalid combination only seq");
3244 Json::Value jvParams;
3245 jvParams[jss::ledger_index] = jss::validated;
3246 jvParams[jss::seq] = sequence;
3247 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3248 BEAST_EXPECT(
3249 jv[jss::result][jss::error].asString() == "malformedRequest");
3250 }
3251
3252 {
3253 testcase("RPC vault_info json invalid combination seq vault_id");
3254 Json::Value jvParams;
3255 jvParams[jss::ledger_index] = jss::validated;
3256 jvParams[jss::vault_id] = strHex(keylet.key);
3257 jvParams[jss::seq] = sequence;
3258 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3259 BEAST_EXPECT(
3260 jv[jss::result][jss::error].asString() == "malformedRequest");
3261 }
3262
3263 {
3264 testcase("RPC vault_info json invalid combination owner vault_id");
3265 Json::Value jvParams;
3266 jvParams[jss::ledger_index] = jss::validated;
3267 jvParams[jss::vault_id] = strHex(keylet.key);
3268 jvParams[jss::owner] = owner.human();
3269 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3270 BEAST_EXPECT(
3271 jv[jss::result][jss::error].asString() == "malformedRequest");
3272 }
3273
3274 {
3275 testcase(
3276 "RPC vault_info json invalid combination owner seq "
3277 "vault_id");
3278 Json::Value jvParams;
3279 jvParams[jss::ledger_index] = jss::validated;
3280 jvParams[jss::vault_id] = strHex(keylet.key);
3281 jvParams[jss::seq] = sequence;
3282 jvParams[jss::owner] = owner.human();
3283 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3284 BEAST_EXPECT(
3285 jv[jss::result][jss::error].asString() == "malformedRequest");
3286 }
3287
3288 {
3289 testcase("RPC vault_info json no input");
3290 Json::Value jvParams;
3291 jvParams[jss::ledger_index] = jss::validated;
3292 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
3293 BEAST_EXPECT(
3294 jv[jss::result][jss::error].asString() == "malformedRequest");
3295 }
3296
3297 {
3298 testcase("RPC vault_info command line invalid index");
3299 Json::Value jv = env.rpc("vault_info", "foobar", "validated");
3300 BEAST_EXPECT(jv[jss::error].asString() == "invalidParams");
3301 }
3302
3303 {
3304 testcase("RPC vault_info command line invalid index");
3305 Json::Value jv = env.rpc("vault_info", "0", "validated");
3306 BEAST_EXPECT(
3307 jv[jss::result][jss::error].asString() == "malformedRequest");
3308 }
3309
3310 {
3311 testcase("RPC vault_info command line invalid index");
3312 Json::Value jv =
3313 env.rpc("vault_info", strHex(uint256(42)), "validated");
3314 BEAST_EXPECT(
3315 jv[jss::result][jss::error].asString() == "entryNotFound");
3316 }
3317
3318 {
3319 testcase("RPC vault_info command line invalid ledger");
3320 Json::Value jv = env.rpc("vault_info", strHex(keylet.key), "0");
3321 BEAST_EXPECT(
3322 jv[jss::result][jss::error].asString() == "lgrNotFound");
3323 }
3324 }
3325
3326public:
3327 void
3343};
3344
3345BEAST_DEFINE_TESTSUITE_PRIO(Vault, app, ripple, 1);
3346
3347} // namespace ripple
Represents a JSON value.
Definition json_value.h:149
A testsuite class.
Definition suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:155
constexpr TIss const & get() const
A currency issued by an account.
Definition Issue.h:33
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:46
Identifies fields.
Definition SField.h:143
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:588
Issue const & issue() const
Definition STAmount.h:496
ripple::test::jtx::PrettyAsset PrettyAsset
void testNonTransferableShares()
static auto constexpr negativeAmount
void run() override
Runs the suite.
ripple::test::jtx::PrettyAmount PrettyAmount
Integers of any length that is a multiple of 32-bits.
Definition base_uint.h:86
T is_same_v
@ nullValue
'null' value
Definition json_value.h:38
int Int
unsigned int UInt
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:540
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:244
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:526
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:564
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition Indexes.cpp:196
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:48
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:115
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
constexpr std::uint32_t asfGlobalFreeze
Definition TxFlags.h:83
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType)
Check if the account lacks required authorization.
Definition View.cpp:2404
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:85
@ telINSUF_FEE_P
Definition TER.h:57
constexpr std::uint32_t const tfMPTCanTransfer
Definition TxFlags.h:149
constexpr std::uint32_t asfRequireDest
Definition TxFlags.h:77
base_uint< 256 > uint256
Definition base_uint.h:558
base_uint< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:64
@ lsfMPTCanTransfer
@ lsfMPTCanEscrow
@ lsfMPTCanClawback
constexpr std::uint32_t const tfVaultPrivate
Definition TxFlags.h:234
Json::Value to_json(Asset const &asset)
Definition Asset.h:123
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:30
constexpr std::uint32_t const tfMPTUnauthorize
Definition TxFlags.h:155
constexpr std::uint32_t tfSetfAuth
Definition TxFlags.h:115
constexpr std::uint32_t asfDefaultRipple
Definition TxFlags.h:84
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:119
@ tecNO_ENTRY
Definition TER.h:306
@ tecLIMIT_EXCEEDED
Definition TER.h:361
@ tecOBJECT_NOT_FOUND
Definition TER.h:326
@ tecFROZEN
Definition TER.h:303
@ tecINSUFFICIENT_FUNDS
Definition TER.h:325
@ tecNO_PERMISSION
Definition TER.h:305
@ tecDST_TAG_NEEDED
Definition TER.h:309
@ tecHAS_OBLIGATIONS
Definition TER.h:317
@ tecWRONG_ASSET
Definition TER.h:360
@ tecNO_LINE
Definition TER.h:301
@ tecINSUFFICIENT_RESERVE
Definition TER.h:307
@ tecEXPIRED
Definition TER.h:314
@ tecNO_AUTH
Definition TER.h:300
@ tecLOCKED
Definition TER.h:358
constexpr std::uint32_t const tfMPTLock
Definition TxFlags.h:159
@ tesSUCCESS
Definition TER.h:244
constexpr std::uint32_t const tfVaultShareNonTransferable
Definition TxFlags.h:236
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition View.cpp:1067
constexpr std::uint32_t tfClearDeepFreeze
Definition TxFlags.h:121
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
std::uint8_t constexpr vaultStrategyFirstComeFirstServe
Vault withdrawal policies.
Definition Protocol.h:123
constexpr std::uint32_t asfAllowTrustLineClawback
Definition TxFlags.h:94
constexpr std::uint32_t asfRequireAuth
Definition TxFlags.h:78
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
Definition Indexes.cpp:170
@ terADDRESS_COLLISION
Definition TER.h:228
@ terNO_ACCOUNT
Definition TER.h:217
@ terNO_RIPPLE
Definition TER.h:224
constexpr std::uint32_t const tfMPTRequireAuth
Definition TxFlags.h:146
constexpr std::uint32_t tfSetFreeze
Definition TxFlags.h:118
constexpr std::uint32_t const tfMPTCanLock
Definition TxFlags.h:145
constexpr std::uint32_t const tfMPTCanClawback
Definition TxFlags.h:150
@ temBAD_AMOUNT
Definition TER.h:89
@ temBAD_FEE
Definition TER.h:92
@ temMALFORMED
Definition TER.h:87
@ temINVALID_FLAG
Definition TER.h:111
@ temDISABLED
Definition TER.h:114
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:39
uint256 key
Definition Keylet.h:40
Represents an XRP or IOU quantity This customizes the string conversion and supports XRP conversions ...