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