45 using namespace test::jtx;
46 Account issuer{
"issuer"};
47 Account owner{
"owner"};
48 Account depositor{
"depositor"};
49 Account charlie{
"charlie"};
52 auto const testSequence = [&,
this](
57 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
58 tx[sfData] =
"AFEED00E";
59 tx[sfAssetsMaximum] = asset(100).number();
62 BEAST_EXPECT(env.le(keylet));
65 auto const [share, vaultAccount] =
67 auto const vault = env.le(keylet);
68 BEAST_EXPECT(vault !=
nullptr);
69 if (!asset.integral())
71 BEAST_EXPECT(vault->at(sfScale) == 6);
75 BEAST_EXPECT(vault->at(sfScale) == 0);
78 BEAST_EXPECT(shares !=
nullptr);
79 if (!asset.integral())
81 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
85 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
87 return {
MPTIssue(vault->at(sfShareMPTID)), Account(
"vault", vault->at(sfAccount))};
89 auto const shares = share.raw().get<
MPTIssue>();
90 env.memoize(vaultAccount);
93 Account
const alice{
"alice"};
94 Account
const erin{
"erin"};
95 env.fund(XRP(1000), alice, erin);
96 env(fset(alice, asfDepositAuth));
100 testcase(prefix +
" fail to deposit more than assets held");
101 auto tx = vault.deposit(
102 {.depositor = depositor, .id = keylet.
key, .amount = asset(10000)});
108 testcase(prefix +
" deposit non-zero amount");
110 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
113 BEAST_EXPECT(env.balance(depositor, shares) == share(50 * scale));
117 testcase(prefix +
" deposit non-zero amount again");
119 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
122 BEAST_EXPECT(env.balance(depositor, shares) == share(100 * scale));
126 testcase(prefix +
" fail to delete non-empty vault");
127 auto tx = vault.del({.owner = owner, .id = keylet.
key});
133 testcase(prefix +
" fail to update because wrong owner");
134 auto tx = vault.set({.owner = issuer, .id = keylet.
key});
135 tx[sfAssetsMaximum] = asset(50).number();
141 testcase(prefix +
" fail to set maximum lower than current amount");
142 auto tx = vault.set({.owner = owner, .id = keylet.
key});
143 tx[sfAssetsMaximum] = asset(50).number();
149 testcase(prefix +
" set maximum higher than current amount");
150 auto tx = vault.set({.owner = owner, .id = keylet.
key});
151 tx[sfAssetsMaximum] = asset(150).number();
157 testcase(prefix +
" set maximum is idempotent, set it again");
158 auto tx = vault.set({.owner = owner, .id = keylet.
key});
159 tx[sfAssetsMaximum] = asset(150).number();
166 auto tx = vault.set({.owner = owner, .id = keylet.
key});
173 testcase(prefix +
" fail to set domain on public vault");
174 auto tx = vault.set({.owner = owner, .id = keylet.
key});
181 testcase(prefix +
" fail to deposit more than maximum");
183 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
189 testcase(prefix +
" reset maximum to zero i.e. not enforced");
190 auto tx = vault.set({.owner = owner, .id = keylet.
key});
191 tx[sfAssetsMaximum] = asset(0).number();
197 testcase(prefix +
" fail to withdraw more than assets held");
198 auto tx = vault.withdraw(
199 {.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
205 testcase(prefix +
" deposit some more");
207 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
210 BEAST_EXPECT(env.balance(depositor, shares) == share(200 * scale));
214 testcase(prefix +
" clawback some");
216 auto tx = vault.clawback(
217 {.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(10)});
220 if (!asset.raw().native())
222 BEAST_EXPECT(env.balance(depositor, shares) == share(190 * scale));
229 auto tx = vault.clawback({.issuer = issuer, .id = keylet.
key, .holder = depositor});
232 if (!asset.raw().native())
234 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
237 auto tx = vault.clawback(
241 .amount = asset(10)});
247 auto tx = vault.withdraw(
248 {.depositor = depositor, .id = keylet.
key, .amount = asset(10)});
255 if (!asset.raw().native())
257 testcase(prefix +
" deposit again");
259 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(200)});
262 BEAST_EXPECT(env.balance(depositor, shares) == share(200 * scale));
266 testcase(prefix +
" deposit/withdrawal same or less than fee");
267 auto const amount = env.current()->fees().base;
270 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = amount});
274 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = amount});
278 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = amount});
283 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = amount});
284 tx[sfDestination] = charlie.human();
289 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = amount - 1});
294 {.depositor = depositor, .id = keylet.
key, .amount = amount - 1});
300 testcase(prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
301 auto tx = vault.withdraw(
302 {.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
303 tx[sfDestination] = alice.human();
309 testcase(prefix +
" fail to withdraw to zero destination");
310 auto tx = vault.withdraw(
311 {.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
312 tx[sfDestination] =
"0";
317 if (!asset.raw().native())
319 testcase(prefix +
" fail to withdraw to 3rd party no authorization");
320 auto tx = vault.withdraw(
321 {.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
322 tx[sfDestination] = erin.human();
328 testcase(prefix +
" fail to withdraw to 3rd party lsfRequireDestTag");
329 auto tx = vault.withdraw(
330 {.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
331 tx[sfDestination] = dave.human();
337 testcase(prefix +
" withdraw to 3rd party lsfRequireDestTag");
339 vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
340 tx[sfDestination] = dave.human();
341 tx[sfDestinationTag] =
"0";
347 testcase(prefix +
" deposit again");
348 auto tx = vault.deposit({.depositor = dave, .id = keylet.
key, .amount = asset(50)});
354 testcase(prefix +
" fail to withdraw lsfRequireDestTag");
356 vault.withdraw({.depositor = dave, .id = keylet.
key, .amount = asset(50)});
362 testcase(prefix +
" withdraw with tag");
364 vault.withdraw({.depositor = dave, .id = keylet.
key, .amount = asset(50)});
365 tx[sfDestinationTag] =
"0";
371 testcase(prefix +
" withdraw to authorized 3rd party");
373 vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
374 tx[sfDestination] = charlie.human();
377 BEAST_EXPECT(env.balance(depositor, shares) == share(100 * scale));
381 testcase(prefix +
" withdraw to issuer");
383 vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
384 tx[sfDestination] = issuer.human();
387 BEAST_EXPECT(env.balance(depositor, shares) == share(50 * scale));
390 if (!asset.raw().native())
392 testcase(prefix +
" issuer deposits");
394 vault.deposit({.depositor = issuer, .id = keylet.
key, .amount = asset(10)});
397 BEAST_EXPECT(env.balance(issuer, shares) == share(10 * scale));
399 testcase(prefix +
" issuer withdraws");
401 {.depositor = issuer, .id = keylet.
key, .amount = share(10 * scale)});
404 BEAST_EXPECT(env.balance(issuer, shares) == share(0 * scale));
408 testcase(prefix +
" withdraw remaining assets");
410 vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
413 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
415 if (!asset.raw().native())
417 auto tx = vault.clawback(
421 .amount = asset(0)});
427 auto tx = vault.withdraw(
428 {.depositor = depositor, .id = keylet.
key, .amount = share(10)});
434 if (!asset.integral())
436 testcase(prefix +
" temporary authorization for 3rd party");
437 env(trust(erin, asset(1000)));
438 env(trust(issuer, asset(0), erin, tfSetfAuth));
439 env(pay(issuer, erin, asset(10)));
442 auto tx = vault.deposit({.depositor = erin, .id = keylet.
key, .amount = asset(10)});
446 auto tx = pay(erin, depositor, share(10 * scale));
454 {.depositor = depositor, .id = keylet.key, .amount = asset(1)}));
461 testcase(prefix +
" withdraw to authorized 3rd party");
464 vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(10)});
465 tx[sfDestination] = erin.human();
470 env(pay(erin, issuer, asset(10)));
473 testcase(prefix +
" fail to pay to unauthorized 3rd party");
474 env(trust(erin, asset(0)));
478 env(pay(depositor, erin, share(1)), ter{
tecNO_LINE});
482 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(1)});
488 testcase(prefix +
" fail to delete because wrong owner");
489 auto tx = vault.del({.owner = issuer, .id = keylet.
key});
495 testcase(prefix +
" delete empty vault");
496 auto tx = vault.del({.owner = owner, .id = keylet.
key});
499 BEAST_EXPECT(!env.le(keylet));
503 auto testCases = [&,
this](
505 Env env{*
this, testable_amendments() | featureSingleAssetVault};
508 env.fund(XRP(1000), issuer, owner, depositor, charlie, dave);
510 env(fset(issuer, asfAllowTrustLineClawback));
511 env(fset(issuer, asfRequireAuth));
512 env(fset(dave, asfRequireDest));
514 env.require(flags(issuer, asfAllowTrustLineClawback));
515 env.require(flags(issuer, asfRequireAuth));
518 testSequence(prefix, env, vault, asset);
523 testCases(
"IOU", [&](Env& env) ->
Asset {
525 env(trust(owner, asset(1000)));
526 env(trust(depositor, asset(1000)));
527 env(trust(charlie, asset(1000)));
528 env(trust(dave, asset(1000)));
529 env(trust(issuer, asset(0), owner, tfSetfAuth));
530 env(trust(issuer, asset(0), depositor, tfSetfAuth));
531 env(trust(issuer, asset(0), charlie, tfSetfAuth));
532 env(trust(issuer, asset(0), dave, tfSetfAuth));
533 env(pay(issuer, depositor, asset(1000)));
538 testCases(
"MPT", [&](Env& env) ->
Asset {
539 MPTTester mptt{env, issuer, mptInitNoFund};
540 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
542 mptt.authorize({.account = depositor});
543 mptt.authorize({.account = charlie});
544 mptt.authorize({.account = dave});
545 env(pay(issuer, depositor, asset(1000)));
554 using namespace test::jtx;
558 FeatureBitset features = testable_amendments() | featureSingleAssetVault;
561 auto testCase = [&,
this](
564 Account
const& issuer,
565 Account
const& owner,
568 CaseArgs args = {}) {
569 Env env{*
this, args.features};
570 Account
const issuer{
"issuer"};
571 Account
const owner{
"owner"};
573 env.fund(XRP(1000), issuer, owner);
576 env(fset(issuer, asfAllowTrustLineClawback));
577 env(fset(issuer, asfRequireAuth));
581 env(trust(owner, asset(1000)));
582 env(trust(issuer, asset(0), owner, tfSetfAuth));
583 env(pay(issuer, owner, asset(1000)));
586 test(env, issuer, owner, asset, vault);
590 return [&, resultAfterCreate](
592 Account
const& issuer,
593 Account
const& owner,
596 testcase(
"disabled single asset vault");
598 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
602 auto tx = vault.set({.owner = owner, .id = keylet.
key});
603 env(tx, data(
"test"), ter{resultAfterCreate});
608 vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
609 env(tx, ter{resultAfterCreate});
614 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
615 env(tx, ter{resultAfterCreate});
619 auto tx = vault.clawback(
620 {.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(10)});
621 env(tx, ter{resultAfterCreate});
625 auto tx = vault.del({.owner = owner, .id = keylet.
key});
626 env(tx, ter{resultAfterCreate});
631 testCase(testDisabled(), {.features = testable_amendments() - featureSingleAssetVault});
634 testDisabled(
tecNO_ENTRY), {.features = testable_amendments() - featureMPTokensV1});
638 Account
const& issuer,
639 Account
const& owner,
642 testcase(
"disabled permissioned domains");
644 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
647 tx[sfFlags] = tx[sfFlags].asUInt() | tfVaultPrivate;
652 auto tx = vault.set({.owner = owner, .id = keylet.
key});
653 env(tx, data(
"Test"));
659 {.features = testable_amendments() - featurePermissionedDomains});
661 testCase([&](Env& env,
662 Account
const& issuer,
663 Account
const& owner,
668 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
669 tx[sfFlags] = tfClearDeepFreeze;
673 auto tx = vault.set({.owner = owner, .id = keylet.
key});
674 tx[sfFlags] = tfClearDeepFreeze;
680 vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
681 tx[sfFlags] = tfClearDeepFreeze;
687 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
688 tx[sfFlags] = tfClearDeepFreeze;
693 auto tx = vault.clawback(
694 {.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(10)});
695 tx[sfFlags] = tfClearDeepFreeze;
700 auto tx = vault.del({.owner = owner, .id = keylet.
key});
701 tx[sfFlags] = tfClearDeepFreeze;
706 testCase([&](Env& env,
707 Account
const& issuer,
708 Account
const& owner,
713 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
718 auto tx = vault.set({.owner = owner, .id = keylet.
key});
725 vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
732 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
738 auto tx = vault.clawback(
739 {.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(10)});
745 auto tx = vault.del({.owner = owner, .id = keylet.
key});
752 [&](Env& env, Account
const&, Account
const& owner,
Asset const&, Vault& vault) {
753 testcase(
"disabled permissioned domain");
755 auto [tx, keylet] = vault.create({.owner = owner, .asset =
xrpIssue()});
760 auto tx = vault.set({.owner = owner, .id = keylet.
key});
766 auto tx = vault.set({.owner = owner, .id = keylet.
key});
767 tx[sfDomainID] =
"0";
772 (testable_amendments() | featureSingleAssetVault) - featurePermissionedDomains});
774 testCase([&](Env& env,
775 Account
const& issuer,
776 Account
const& owner,
781 auto [tx, keylet] = vault.create({.owner = owner, .asset =
xrpIssue()});
784 auto tx = vault.set({
793 vault.deposit({.depositor = owner, .id = beast::zero, .amount = asset(10)});
799 vault.withdraw({.depositor = owner, .id = beast::zero, .amount = asset(10)});
804 auto tx = vault.clawback(
805 {.issuer = issuer, .id = beast::zero, .holder = owner, .amount = asset(10)});
810 auto tx = vault.del({
819 [&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
820 testcase(
"withdraw to bad destination");
822 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
826 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
827 tx[jss::Destination] =
"0";
833 [&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
837 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
843 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
850 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
854 auto const sleVault = env.le(keylet);
855 BEAST_EXPECT(sleVault);
856 BEAST_EXPECT((*sleVault)[sfScale] == 18);
860 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
864 auto const sleVault = env.le(keylet);
865 BEAST_EXPECT(sleVault);
866 BEAST_EXPECT((*sleVault)[sfScale] == 0);
870 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
873 auto const sleVault = env.le(keylet);
874 BEAST_EXPECT(sleVault);
875 BEAST_EXPECT((*sleVault)[sfScale] == 6);
880 [&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
881 testcase(
"create or set invalid data");
883 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
899 auto tx = vault.set({.owner = owner, .id = keylet.
key});
905 auto tx = vault.set({.owner = owner, .id = keylet.
key});
913 [&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
916 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
919 auto tx = vault.set({.owner = owner, .id = keylet.
key});
925 [&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
926 testcase(
"create with invalid metadata");
928 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
932 tx[sfMPTokenMetadata] =
"";
946 [&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
949 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
952 auto tx = vault.set({.owner = owner, .id = keylet.
key});
959 [&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
962 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
965 auto tx = vault.deposit(
972 vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(0)});
978 [&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
979 testcase(
"invalid set immutable flag");
981 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
984 auto tx = vault.set({.owner = owner, .id = keylet.
key});
985 tx[sfFlags] = tfVaultPrivate;
991 [&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
992 testcase(
"invalid withdraw amount");
994 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
997 auto tx = vault.withdraw(
1004 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1009 testCase([&](Env& env,
1010 Account
const& issuer,
1011 Account
const& owner,
1016 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1021 auto tx = vault.clawback(
1022 {.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(50)});
1027 auto tx = vault.clawback(
1037 [&](Env& env, Account
const&, Account
const& owner,
Asset const& asset, Vault& vault) {
1040 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1044 tx[sfWithdrawalPolicy] = 0;
1062 tx[sfFlags] = tfVaultPrivate;
1063 tx[sfDomainID] =
"0";
1243 using namespace test::jtx;
1246 testcase(
"IOU fail because MPT is disabled");
1248 *
this, (testable_amendments() - featureMPTokensV1) | featureSingleAssetVault};
1249 Account
const issuer{
"issuer"};
1250 Account
const owner{
"owner"};
1251 env.fund(XRP(1000), issuer, owner);
1254 Vault
const vault{env};
1255 Asset const asset = issuer[
"IOU"].asset();
1256 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1263 testcase(
"IOU fail create frozen");
1264 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1265 Account
const issuer{
"issuer"};
1266 Account
const owner{
"owner"};
1267 env.fund(XRP(1000), issuer, owner);
1269 env(fset(issuer, asfGlobalFreeze));
1272 Vault
const vault{env};
1273 Asset const asset = issuer[
"IOU"].asset();
1274 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1281 testcase(
"IOU fail create no ripling");
1282 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1283 Account
const issuer{
"issuer"};
1284 Account
const owner{
"owner"};
1285 env.fund(XRP(1000), issuer, owner);
1287 env(fclear(issuer, asfDefaultRipple));
1290 Vault
const vault{env};
1291 Asset const asset = issuer[
"IOU"].asset();
1292 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1299 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1300 Account
const issuer{
"issuer"};
1301 Account
const owner{
"owner"};
1302 env.fund(XRP(1000), owner);
1305 Vault
const vault{env};
1306 Asset const asset = issuer[
"IOU"].asset();
1308 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1316 testcase(
"IOU fail create vault for AMM LPToken");
1317 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1318 Account
const gw(
"gateway");
1319 Account
const alice(
"alice");
1320 Account
const carol(
"carol");
1321 IOU
const USD = gw[
"USD"];
1327 auto const defXRP = XRP(30000);
1330 return a + XRP(1000);
1337 auto const toFund1 = toFund(asset1);
1338 auto const toFund2 = toFund(asset2);
1339 BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2);
1341 if (!asset1.native() && !asset2.native())
1343 fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All);
1345 else if (asset1.native())
1347 fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All);
1349 else if (asset2.native())
1351 fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All);
1354 AMM const ammAlice(env, alice, asset1, asset2, CreateArg{.log =
false, .tfee = 0});
1356 Account
const owner{
"owner"};
1357 env.fund(XRP(1000000), owner);
1359 Vault
const vault{env};
1360 auto [tx, k] = vault.create({.owner = owner, .asset = ammAlice.lptIssue()});
1532 using namespace test::jtx;
1536 bool enableClawback =
true;
1538 int initialXRP = 1000;
1541 auto testCase = [
this](
1544 Account
const& issuer,
1545 Account
const& owner,
1546 Account
const& depositor,
1549 MPTTester& mptt)> test,
1550 CaseArgs args = {}) {
1551 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1552 Account issuer{
"issuer"};
1553 Account owner{
"owner"};
1554 Account depositor{
"depositor"};
1555 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
1559 MPTTester mptt{env, issuer, mptInitNoFund};
1562 {.flags = tfMPTCanTransfer | tfMPTCanLock |
1563 (args.enableClawback ? tfMPTCanClawback : none) |
1564 (args.requireAuth ? tfMPTRequireAuth : none),
1567 mptt.authorize({.account = owner});
1568 mptt.authorize({.account = depositor});
1569 if (args.requireAuth)
1571 mptt.authorize({.account = issuer, .holder = owner});
1572 mptt.authorize({.account = issuer, .holder = depositor});
1575 env(pay(issuer, depositor, asset(1000)));
1578 test(env, issuer, owner, depositor, asset, vault, mptt);
1583 Account
const& issuer,
1584 Account
const& owner,
1585 Account
const& depositor,
1589 testcase(
"MPT nothing to clawback from");
1590 auto tx = vault.clawback(
1593 .holder = depositor,
1594 .amount = asset(10)});
1600 Account
const& issuer,
1601 Account
const& owner,
1602 Account
const& depositor,
1606 testcase(
"MPT global lock blocks create");
1607 mptt.set({.account = issuer, .flags = tfMPTLock});
1608 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1614 Account
const& issuer,
1615 Account
const& owner,
1616 Account
const& depositor,
1620 testcase(
"MPT global lock blocks deposit");
1621 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1625 mptt.set({.account = issuer, .flags = tfMPTLock});
1628 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1633 tx = vault.del({.owner = owner, .id = keylet.
key});
1639 Account
const& issuer,
1640 Account
const& owner,
1641 Account
const& depositor,
1645 testcase(
"MPT global lock blocks withdrawal");
1646 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1649 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1655 auto v = env.le(keylet);
1657 MPTID const share = (*v)[sfShareMPTID];
1659 BEAST_EXPECT(issuance);
1660 Number const outstandingShares = issuance->at(sfOutstandingAmount);
1661 BEAST_EXPECT(outstandingShares == 100);
1663 mptt.set({.account = issuer, .flags = tfMPTLock});
1666 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1669 tx[sfDestination] = issuer.human();
1673 tx = vault.clawback(
1674 {.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
1680 BEAST_EXPECT(mptSle ==
nullptr);
1683 tx = vault.del({.owner = owner, .id = keylet.
key});
1689 Account
const& issuer,
1690 Account
const& owner,
1691 Account
const& depositor,
1695 testcase(
"MPT only issuer can clawback");
1697 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1701 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1706 auto tx = vault.clawback({
1707 .issuer = depositor,
1709 .holder = depositor,
1715 auto tx = vault.clawback({
1718 .holder = depositor,
1727 Account
const& issuer,
1728 Account
const& owner,
1729 Account
const& depositor,
1733 testcase(
"MPT depositor without MPToken, auth required");
1735 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1740 {.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
1746 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
1750 auto const sleMPT1 = env.le(mptoken);
1751 BEAST_EXPECT(sleMPT1 ==
nullptr);
1753 tx = vault.withdraw(
1754 {.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1758 auto const sleMPT2 = env.le(mptoken);
1759 BEAST_EXPECT(sleMPT2 ==
nullptr);
1764 Account
const charlie{
"charlie"};
1765 env.fund(XRP(1000), charlie);
1768 tx = vault.withdraw(
1769 {.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1770 tx[sfDestination] = charlie.human();
1774 {.requireAuth =
true});
1779 Account
const& issuer,
1780 Account
const& owner,
1781 Account
const& depositor,
1785 testcase(
"MPT depositor without MPToken, no auth required");
1787 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1790 auto v = env.le(keylet);
1794 {.depositor = depositor,
1796 .amount = asset(1000)});
1802 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
1806 auto const sleMPT1 = env.le(mptoken);
1807 BEAST_EXPECT(sleMPT1 ==
nullptr);
1809 tx = vault.withdraw(
1810 {.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1814 auto const sleMPT2 = env.le(mptoken);
1815 BEAST_EXPECT(sleMPT2 !=
nullptr);
1816 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
1821 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
1825 auto const sleMPT1 = env.le(mptoken);
1826 BEAST_EXPECT(sleMPT1 ==
nullptr);
1828 tx = vault.withdraw(
1829 {.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
1830 tx[sfDestination] = owner.human();
1834 auto const sleMPT2 = env.le(mptoken);
1835 BEAST_EXPECT(sleMPT2 ==
nullptr);
1838 {.requireAuth =
false});
1841 Env
const env{*
this, testable_amendments()};
1850 Account
const& issuer,
1851 Account
const& owner,
1852 Account
const& depositor,
1856 testcase(
"MPT fail reserve to re-create MPToken");
1858 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1861 auto v = env.le(keylet);
1864 env(pay(depositor, owner, asset(1000)));
1868 {.depositor = owner,
1870 .amount = asset(1000)});
1876 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
1880 auto const sleMPT = env.le(mptoken);
1881 BEAST_EXPECT(sleMPT ==
nullptr);
1884 env(ticket::create(owner, 1));
1888 tx = vault.withdraw(
1889 {.depositor = owner, .id = keylet.
key, .amount = asset(100)});
1893 env(pay(depositor, owner, XRP(incReserve)));
1901 {.requireAuth =
false, .initialXRP = acctReserve + (incReserve * 4) + 1});
1905 Account
const& issuer,
1906 Account
const& owner,
1907 Account
const& depositor,
1913 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1917 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
1922 auto tx = vault.clawback(
1923 {.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
1927 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
1931 auto [tx, keylet] = vault.create({.owner = depositor, .asset = asset});
1937 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(10)});
1943 vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(10)});
1948 auto tx = vault.clawback(
1949 {.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
1953 env(vault.del({.owner = owner, .id = keylet.key}));
1958 Account
const& issuer,
1959 Account
const& owner,
1960 Account
const& depositor,
1964 testcase(
"MPT vault owner can receive shares unless unauthorized");
1966 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1970 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
1975 auto const vault = env.le(keylet);
1976 return vault->at(sfShareMPTID);
1982 env(pay(depositor, owner, shares(1)));
1985 tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = shares(1)});
1990 env(pay(depositor, owner, shares(1)));
1993 tx = vault.clawback(
1994 {.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(0)});
1999 env(pay(depositor, owner, shares(1)));
2003 env(pay(owner, depositor, shares(1)));
2009 jv[sfAccount] = owner.human();
2010 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
2011 jv[sfFlags] = tfMPTUnauthorize;
2012 jv[sfTransactionType] = jss::MPTokenAuthorize;
2018 tx = pay(depositor, owner, shares(1));
2023 tx = vault.clawback(
2024 {.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
2029 env(vault.del({.owner = owner, .id = keylet.key}));
2037 Account
const& issuer,
2038 Account
const& owner,
2039 Account
const& depositor,
2045 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2050 {.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
2055 auto tx = vault.clawback(
2058 .holder = depositor,
2059 .amount = asset(0)});
2063 {.enableClawback =
false});
2067 Account
const& issuer,
2068 Account
const& owner,
2069 Account
const& depositor,
2074 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2077 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(1000)});
2081 mptt.authorize({.account = issuer, .holder = depositor, .flags = tfMPTUnauthorize});
2085 auto tx = vault.withdraw(
2086 {.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
2090 tx[sfDestination] = issuer.human();
2094 tx[sfDestination] = owner.human();
2102 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
2108 tx = vault.clawback(
2109 {.issuer = issuer, .id = keylet.
key, .holder = issuer, .amount = asset(800)});
2113 tx = vault.clawback(
2114 {.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(800)});
2118 env(vault.del({.owner = owner, .id = keylet.key}));
2123 Account
const& issuer,
2124 Account
const& owner,
2125 Account
const& depositor,
2129 testcase(
"MPT lock of vault pseudo-account");
2130 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2134 auto const vaultAccount = [&env, keylet = keylet,
this]() ->
AccountID {
2135 auto const vault = env.le(keylet);
2136 BEAST_EXPECT(vault !=
nullptr);
2137 return vault->at(sfAccount);
2140 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
2146 jv[jss::Account] = issuer.human();
2148 jv[jss::Holder] =
toBase58(vaultAccount);
2149 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
2150 jv[jss::Flags] = tfMPTLock;
2156 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
2159 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
2163 tx = vault.clawback(
2164 {.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(100)});
2168 tx = vault.del({.owner = owner, .id = keylet.
key});
2175 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2176 Account owner{
"owner"};
2177 Account issuer{
"issuer"};
2178 env.fund(XRP(1000000), owner, issuer);
2180 Vault
const vault{env};
2182 MPTTester mptt{env, issuer, mptInitNoFund};
2184 {.flags = tfMPTCanTransfer | tfMPTCanLock | lsfMPTCanClawback | tfMPTRequireAuth});
2185 mptt.authorize({.account = owner});
2186 mptt.authorize({.account = issuer, .holder = owner});
2188 env(pay(issuer, owner, asset(100)));
2189 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
2193 auto const shares = [&env, keylet = k1,
this]() ->
Asset {
2194 auto const vault = env.le(keylet);
2195 BEAST_EXPECT(vault !=
nullptr);
2196 return MPTIssue(vault->at(sfShareMPTID));
2199 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
2207 Account
const& owner,
2208 Account
const& depositor,
2214 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2218 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
2229 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(100)});
2242 env(vault.del({.owner = owner, .id = keylet.key}));
2249 using namespace test::jtx;
2253 int initialXRP = 1000;
2256 bool charlieRipple =
true;
2259 auto testCase = [&,
this](
2262 Account
const& owner,
2263 Account
const& issuer,
2264 Account
const& charlie,
2269 CaseArgs args = {}) {
2270 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2271 Account
const owner{
"owner"};
2272 Account
const issuer{
"issuer"};
2273 Account
const charlie{
"charlie"};
2275 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
2276 env(fset(issuer, asfAllowTrustLineClawback));
2280 env.trust(asset(1000), owner);
2281 env(pay(issuer, owner, asset(args.initialIOU)));
2283 if (!args.charlieRipple)
2285 env(fset(issuer, 0, asfDefaultRipple));
2287 env.trust(asset(1000), charlie);
2289 env(pay(issuer, charlie, asset(args.initialIOU)));
2291 env(fset(issuer, asfDefaultRipple));
2295 env.trust(asset(1000), charlie);
2298 env(rate(issuer, args.transferRate));
2301 auto const vaultAccount = [&env](
xrpl::Keylet keylet) -> Account {
2302 return Account(
"vault", env.le(keylet)->at(sfAccount));
2305 return env.le(keylet)->at(sfShareMPTID);
2308 test(env, owner, issuer, charlie, vaultAccount, vault, asset, issuanceId);
2313 Account
const& owner,
2314 Account
const& issuer,
2320 testcase(
"IOU cannot use different asset");
2323 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2329 auto tx = [&, account = vaultAccount(keylet)]() {
2331 jv[jss::Account] = issuer.human();
2334 ja[jss::issuer] =
toBase58(account);
2336 jv[jss::TransactionType] = jss::TrustSet;
2337 jv[jss::Flags] = tfSetFreeze;
2345 auto tx = vault.deposit({.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2352 vault.withdraw({.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2357 env(vault.del({.owner = owner, .id = keylet.key}));
2363 Account
const& owner,
2364 Account
const& issuer,
2365 Account
const& charlie,
2370 testcase(
"IOU frozen trust line to vault account");
2372 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2376 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2379 Asset const share =
Asset(issuanceId(keylet));
2382 auto trustSet = [&, account = vaultAccount(keylet)]() {
2384 jv[jss::Account] = issuer.human();
2387 ja[jss::issuer] =
toBase58(account);
2389 jv[jss::TransactionType] = jss::TrustSet;
2390 jv[jss::Flags] = tfSetFreeze;
2402 vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(80)});
2408 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(100)});
2412 tx[sfDestination] = charlie.human();
2419 auto tx = vault.clawback(
2420 {.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(50)});
2426 trustSet[jss::Flags] = tfClearFreeze;
2431 {.depositor = owner, .id = keylet.key, .amount = share(50'000'000)}));
2433 env(vault.del({.owner = owner, .id = keylet.key}));
2440 Account
const& owner,
2441 Account
const& issuer,
2442 Account
const& charlie,
2447 testcase(
"IOU transfer fees not applied");
2449 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2453 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2457 Asset const share =
Asset(issuanceId(keylet));
2460 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2461 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(100));
2464 auto tx = vault.clawback(
2465 {.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(50)});
2471 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2472 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(50));
2475 {.depositor = owner, .id = keylet.key, .amount = share(20'000'000)}));
2478 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2479 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(30));
2482 auto tx = vault.withdraw(
2483 {.depositor = owner, .id = keylet.
key, .amount = share(30'000'000)});
2484 tx[sfDestination] = charlie.human();
2489 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2490 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2491 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(0));
2493 env(vault.del({.owner = owner, .id = keylet.key}));
2496 CaseArgs{.transferRate = 1.25});
2500 Account
const& owner,
2501 Account
const& issuer,
2502 Account
const& charlie,
2507 testcase(
"IOU frozen trust line to depositor");
2509 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2513 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2517 auto const withdrawToCharlie = [&](
xrpl::Keylet keylet) {
2519 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2520 tx[sfDestination] = charlie.human();
2523 env(withdrawToCharlie);
2526 env(trust(issuer, asset(0), owner, tfSetFreeze));
2530 auto const withdraw =
2531 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2541 vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2547 auto tx = vault.clawback(
2548 {.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(0)});
2553 env(vault.del({.owner = owner, .id = keylet.key}));
2559 Account
const& owner,
2560 Account
const& issuer,
2561 Account
const& charlie,
2566 testcase(
"IOU no trust line to 3rd party");
2568 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2572 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2575 Account
const erin{
"erin"};
2576 env.fund(XRP(1000), erin);
2582 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2583 tx[sfDestination] = erin.human();
2591 Account
const& owner,
2592 Account
const& issuer,
2593 Account
const& charlie,
2598 testcase(
"IOU no trust line to depositor");
2600 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2605 env.trust(asset(0), owner);
2608 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2612 BEAST_EXPECT(trustline ==
nullptr);
2617 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2626 Account
const& owner,
2627 Account
const& issuer,
2628 Account
const& charlie,
2635 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2642 env(trust(issuer, vaultAccount(keylet)[
"IOU"], tfSetNoRipple));
2646 auto tx = vault.deposit(
2647 {.depositor = charlie, .id = keylet.
key, .amount = asset(100)});
2655 vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(100)});
2660 auto tx2 = vault.withdraw(
2661 {.depositor = owner, .id = keylet.
key, .amount = shares(100)});
2662 tx2[sfDestination] = charlie.human();
2669 tx[sfAccount] = charlie.human();
2670 tx[sfMPTokenIssuanceID] =
2672 tx[sfTransactionType] = jss::MPTokenAuthorize;
2676 env(pay(owner, charlie, shares(100)));
2680 auto tx3 = vault.withdraw(
2681 {.depositor = charlie, .id = keylet.
key, .amount = shares(100)});
2685 env(pay(charlie, owner, shares(100)));
2689 tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(100)});
2694 env(vault.del({.owner = owner, .id = keylet.key}));
2696 {.charlieRipple =
false});
2701 Account
const& owner,
2702 Account
const& issuer,
2703 Account
const& charlie,
2704 auto const& vaultAccount,
2708 testcase(
"IOU calculation rounding");
2710 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2715 auto const startingOwnerBalance = env.balance(owner, asset);
2716 BEAST_EXPECT((startingOwnerBalance.value() ==
STAmount{asset, 11875, -2}));
2722 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2724 auto const tx1 = vault.deposit(
2725 {.depositor = owner, .id = keylet.
key, .amount = asset(
Number(375, -2))});
2726 for (
auto i = 0; i < 5; ++i)
2733 STAmount const xfer{asset, 1185, -1};
2734 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value() - xfer);
2735 BEAST_EXPECT(env.balance(vaultAccount(keylet), asset) == xfer);
2737 auto const vault = env.le(keylet);
2738 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
2739 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
2746 {.depositor = owner,
2748 .amount = asset(Number(1000 + (37 * 5), -1))}));
2751 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value());
2752 BEAST_EXPECT(env.balance(vaultAccount(keylet), asset) == beast::zero);
2753 auto const vault = env.le(keylet);
2754 BEAST_EXPECT(vault->at(sfAssetsAvailable) == beast::zero);
2755 BEAST_EXPECT(vault->at(sfAssetsTotal) == beast::zero);
2758 env(vault.del({.owner = owner, .id = keylet.key}));
2761 {.initialIOU =
Number(11875, -2)});
2764 Env
const env{*
this, testable_amendments()};
2773 Account
const& owner,
2774 Account
const& issuer,
2775 Account
const& charlie,
2780 testcase(
"IOU no trust line to depositor no reserve");
2781 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2787 env.trust(asset(0), owner);
2790 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2794 BEAST_EXPECT(trustline ==
nullptr);
2796 env(ticket::create(owner, 1));
2800 tx = vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2804 env(pay(charlie, owner, XRP(incReserve)));
2811 CaseArgs{.initialXRP = acctReserve + (incReserve * 4) + 1});
2816 Account
const& owner,
2817 Account
const& issuer,
2818 Account
const& charlie,
2823 testcase(
"IOU no reserve for share MPToken");
2824 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2828 env(pay(owner, charlie, asset(100)));
2831 env(ticket::create(charlie, 3));
2835 tx = vault.deposit({.depositor = charlie, .id = keylet.
key, .amount = asset(100)});
2839 env(pay(issuer, charlie, XRP(incReserve)));
2846 CaseArgs{.initialXRP = acctReserve + (incReserve * 4) + 1});
2850 Account
const& owner,
2851 Account
const& issuer,
2852 Account
const& charlie,
2857 testcase(
"IOU frozen trust line to 3rd party");
2859 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2863 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2867 auto const withdrawToCharlie = [&](
xrpl::Keylet keylet) {
2869 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2870 tx[sfDestination] = charlie.human();
2873 env(withdrawToCharlie);
2876 env(trust(issuer, asset(0), charlie, tfSetFreeze));
2880 auto const withdraw =
2881 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2890 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}));
2893 env(vault.del({.owner = owner, .id = keylet.key}));
2899 Account
const& owner,
2900 Account
const& issuer,
2901 Account
const& charlie,
2908 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2912 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2915 env(fset(issuer, asfGlobalFreeze));
2921 vault.withdraw({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2925 tx[sfDestination] = charlie.human();
2930 tx = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2937 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}));
2940 env(vault.del({.owner = owner, .id = keylet.key}));
2948 using namespace test::jtx;
2952 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2953 Account
const issuer{
"issuer"};
2954 Account
const owner{
"owner"};
2955 Account
const depositor{
"depositor"};
2956 Account
const charlie{
"charlie"};
2957 Account
const pdOwner{
"pdOwner"};
2958 Account
const credIssuer1{
"credIssuer1"};
2959 Account
const credIssuer2{
"credIssuer2"};
2961 Vault
const vault{env};
2962 env.fund(XRP(1000), issuer, owner, depositor, charlie, pdOwner, credIssuer1, credIssuer2);
2964 env(fset(issuer, asfAllowTrustLineClawback));
2966 env.require(flags(issuer, asfAllowTrustLineClawback));
2969 env.trust(asset(1000), owner);
2970 env(pay(issuer, owner, asset(500)));
2971 env.trust(asset(1000), depositor);
2972 env(pay(issuer, depositor, asset(500)));
2973 env.trust(asset(1000), charlie);
2974 env(pay(issuer, charlie, asset(5)));
2977 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate});
2980 BEAST_EXPECT(env.le(keylet));
2983 testcase(
"private vault owner can deposit");
2984 auto tx = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(50)});
2989 testcase(
"private vault depositor not authorized yet");
2991 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
2996 testcase(
"private vault cannot set non-existing domain");
2997 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3003 testcase(
"private vault set domainId");
3006 pdomain::Credentials
const credentials1{
3007 {.issuer = credIssuer1, .credType = credType}};
3009 env(pdomain::setTx(pdOwner, credentials1));
3010 auto const domainId1 = [&]() {
3012 return pdomain::getNewDomain(env.meta());
3015 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3026 pdomain::Credentials
const credentials{
3027 {.issuer = credIssuer1, .credType = credType},
3028 {.issuer = credIssuer2, .credType = credType}};
3030 env(pdomain::setTx(pdOwner, credentials));
3031 auto const domainId = [&]() {
3033 return pdomain::getNewDomain(env.meta());
3036 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3042 tx = vault.set({.owner = owner, .id = keylet.
key});
3050 testcase(
"private vault depositor still not authorized");
3052 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
3057 auto const credKeylet = credentials::keylet(depositor, credIssuer1, credType);
3059 testcase(
"private vault depositor now authorized");
3060 env(credentials::create(depositor, credIssuer1, credType));
3061 env(credentials::accept(depositor, credIssuer1, credType));
3062 env(credentials::create(charlie, credIssuer1, credType));
3065 auto credSle = env.le(credKeylet);
3066 BEAST_EXPECT(credSle !=
nullptr);
3069 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
3073 tx = vault.deposit({.depositor = charlie, .id = keylet.
key, .amount = asset(50)});
3079 testcase(
"private vault depositor lost authorization");
3080 env(credentials::deleteCred(credIssuer1, depositor, credIssuer1, credType));
3081 env(credentials::deleteCred(credIssuer1, charlie, credIssuer1, credType));
3083 auto credSle = env.le(credKeylet);
3084 BEAST_EXPECT(credSle ==
nullptr);
3087 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
3092 auto const shares = [&env, keylet = keylet,
this]() ->
Asset {
3093 auto const vault = env.le(keylet);
3094 BEAST_EXPECT(vault !=
nullptr);
3095 return MPTIssue(vault->at(sfShareMPTID));
3099 testcase(
"private vault expired authorization");
3100 uint32_t
const closeTime =
3101 env.current()->header().parentCloseTime.time_since_epoch().count();
3103 auto tx0 = credentials::create(depositor, credIssuer2, credType);
3104 tx0[sfExpiration] = closeTime + 20;
3106 tx0 = credentials::create(charlie, credIssuer2, credType);
3107 tx0[sfExpiration] = closeTime + 20;
3111 env(credentials::accept(depositor, credIssuer2, credType));
3112 env(credentials::accept(charlie, credIssuer2, credType));
3118 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
3122 auto const tokenKeylet =
3124 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
3133 auto const credsKeylet = credentials::keylet(depositor, credIssuer2, credType);
3134 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3137 vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(1)});
3141 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3145 auto const credsKeylet = credentials::keylet(charlie, credIssuer2, credType);
3146 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3147 auto const tokenKeylet =
3149 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3152 vault.deposit({.depositor = charlie, .id = keylet.
key, .amount = asset(2)});
3156 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3157 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3162 testcase(
"private vault reset domainId");
3163 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3164 tx[sfDomainID] =
"0";
3168 tx = vault.deposit({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
3172 tx = vault.withdraw({.depositor = depositor, .id = keylet.
key, .amount = asset(50)});
3176 tx = vault.clawback(
3177 {.issuer = issuer, .id = keylet.
key, .holder = depositor, .amount = asset(0)});
3180 tx = vault.clawback(
3181 {.issuer = issuer, .id = keylet.
key, .holder = owner, .amount = asset(0)});
3321 using namespace test::jtx;
3325 Account
const& owner;
3326 Account
const& issuer;
3327 Account
const& depositor;
3328 Account
const& vaultAccount;
3338 auto testCase = [&,
this](
3340 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3341 Account
const owner{
"owner"};
3342 Account
const issuer{
"issuer"};
3343 Account
const depositor{
"depositor"};
3345 env.fund(XRP(1000), issuer, owner, depositor);
3346 env(fset(issuer, asfAllowTrustLineClawback));
3350 env.trust(asset(1000), owner);
3351 env.trust(asset(1000), depositor);
3352 env(pay(issuer, owner, asset(200)));
3353 env(pay(issuer, depositor, asset(200)));
3356 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3357 tx[sfScale] = scale;
3360 auto const [vaultAccount, issuanceId] =
3362 auto const vault = env.le(keylet);
3363 return {Account(
"vault", vault->at(sfAccount)), vault->at(sfShareMPTID)};
3366 env.memoize(vaultAccount);
3369 return env.app().getOpenLedger().modify(
3373 if (!BEAST_EXPECT(vault !=
nullptr))
3376 if (!BEAST_EXPECT(shares !=
nullptr))
3378 if (fn(*vault, *shares))
3393 .depositor = depositor,
3394 .vaultAccount = vaultAccount,
3404 testCase(18, [&,
this](Env& env, Data d) {
3405 testcase(
"Scale deposit overflow on first deposit");
3406 auto tx = d.vault.deposit(
3407 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)});
3412 testCase(18, [&,
this](Env& env, Data d) {
3413 testcase(
"Scale deposit overflow on second deposit");
3416 auto tx = d.vault.deposit(
3417 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3423 auto tx = d.vault.deposit(
3424 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)});
3430 testCase(18, [&,
this](Env& env, Data d) {
3431 testcase(
"Scale deposit overflow on total shares");
3434 auto tx = d.vault.deposit(
3435 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3441 auto tx = d.vault.deposit(
3442 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3448 testCase(1, [&,
this](Env& env, Data d) {
3451 auto const start = env.balance(d.depositor, d.assets).number();
3452 auto tx = d.vault.deposit(
3453 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(1)});
3456 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3457 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start - 1));
3460 testCase(1, [&,
this](Env& env, Data d) {
3461 testcase(
"Scale deposit insignificant amount");
3463 auto tx = d.vault.deposit(
3464 {.depositor = d.depositor,
3470 testCase(1, [&,
this](Env& env, Data d) {
3471 testcase(
"Scale deposit exact, using full precision");
3473 auto const start = env.balance(d.depositor, d.assets).number();
3474 auto tx = d.vault.deposit(
3475 {.depositor = d.depositor,
3480 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3482 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(15, -1)));
3485 testCase(1, [&,
this](Env& env, Data d) {
3486 testcase(
"Scale deposit exact, truncating from .5");
3488 auto const start = env.balance(d.depositor, d.assets).number();
3492 auto tx = d.vault.deposit(
3493 {.depositor = d.depositor,
3498 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3500 env.balance(d.depositor, d.assets) ==
3505 auto tx = d.vault.deposit(
3506 {.depositor = d.depositor,
3511 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3513 env.balance(d.depositor, d.assets) ==
3518 auto tx = d.vault.deposit(
3519 {.depositor = d.depositor,
3524 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
3526 env.balance(d.depositor, d.assets) ==
3531 testCase(1, [&,
this](Env& env, Data d) {
3532 testcase(
"Scale deposit exact, truncating from .01");
3534 auto const start = env.balance(d.depositor, d.assets).number();
3536 auto tx = d.vault.deposit(
3537 {.depositor = d.depositor,
3542 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3544 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(12, -1)));
3548 auto tx = d.vault.deposit(
3549 {.depositor = d.depositor,
3554 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3556 env.balance(d.depositor, d.assets) ==
3561 testCase(1, [&,
this](Env& env, Data d) {
3562 testcase(
"Scale deposit exact, truncating from .99");
3564 auto const start = env.balance(d.depositor, d.assets).number();
3566 auto tx = d.vault.deposit(
3567 {.depositor = d.depositor,
3572 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3574 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(12, -1)));
3578 auto tx = d.vault.deposit(
3579 {.depositor = d.depositor,
3584 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3586 env.balance(d.depositor, d.assets) ==
3591 testCase(1, [&,
this](Env& env, Data d) {
3593 auto const start = env.balance(d.depositor, d.assets).number();
3594 auto tx = d.vault.deposit(
3595 {.depositor = d.depositor,
3600 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3602 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(100, 0)));
3604 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(100, 0)));
3606 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-1000, 0)));
3614 auto const start = env.balance(d.depositor, d.assets).number();
3615 auto tx = d.vault.withdraw(
3616 {.depositor = d.depositor,
3621 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
3623 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(10, 0)));
3625 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(90, 0)));
3627 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-900, 0)));
3631 testcase(
"Scale redeem with rounding");
3636 auto const start = env.balance(d.depositor, d.assets).number();
3637 d.peek([](
SLE& vault,
auto&) ->
bool {
3638 vault[sfAssetsAvailable] =
Number(1);
3646 auto tx = d.vault.withdraw(
3647 {.depositor = d.depositor,
3652 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
3654 env.balance(d.depositor, d.assets) ==
3657 env.balance(d.vaultAccount, d.assets) ==
3660 env.balance(d.vaultAccount, d.shares) ==
3670 auto const start = env.balance(d.depositor, d.assets).number();
3672 tx = d.vault.withdraw(
3673 {.depositor = d.depositor,
3678 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 21));
3680 env.balance(d.depositor, d.assets) ==
3683 env.balance(d.vaultAccount, d.assets) ==
3686 env.balance(d.vaultAccount, d.shares) ==
3692 auto const rest = env.balance(d.depositor, d.shares).number();
3694 tx = d.vault.withdraw(
3695 {.depositor = d.depositor,
3697 .amount =
STAmount(d.share, rest)});
3700 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3701 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
3702 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
3706 testCase(18, [&,
this](Env& env, Data d) {
3707 testcase(
"Scale withdraw overflow");
3710 auto tx = d.vault.deposit(
3711 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3717 auto tx = d.vault.withdraw(
3718 {.depositor = d.depositor,
3726 testCase(1, [&,
this](Env& env, Data d) {
3728 auto const start = env.balance(d.depositor, d.assets).number();
3729 auto tx = d.vault.deposit(
3730 {.depositor = d.depositor,
3735 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3737 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(100, 0)));
3739 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(100, 0)));
3741 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-1000, 0)));
3752 auto const start = env.balance(d.depositor, d.assets).number();
3753 auto tx = d.vault.withdraw(
3754 {.depositor = d.depositor,
3759 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
3761 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(10, 0)));
3763 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(90, 0)));
3765 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-900, 0)));
3769 testcase(
"Scale withdraw insignificant amount");
3770 auto tx = d.vault.withdraw(
3771 {.depositor = d.depositor,
3778 testcase(
"Scale withdraw with rounding assets");
3786 auto const start = env.balance(d.depositor, d.assets).number();
3787 d.peek([](
SLE& vault,
auto&) ->
bool {
3788 vault[sfAssetsAvailable] =
Number(1);
3796 auto tx = d.vault.withdraw(
3797 {.depositor = d.depositor,
3802 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
3804 env.balance(d.depositor, d.assets) ==
3807 env.balance(d.vaultAccount, d.assets) ==
3810 env.balance(d.vaultAccount, d.shares) ==
3815 testcase(
"Scale withdraw with rounding shares up");
3823 auto const start = env.balance(d.depositor, d.assets).number();
3824 auto tx = d.vault.withdraw(
3825 {.depositor = d.depositor,
3830 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38));
3832 env.balance(d.depositor, d.assets) ==
3835 env.balance(d.vaultAccount, d.assets) ==
3838 env.balance(d.vaultAccount, d.shares) ==
3843 testcase(
"Scale withdraw with rounding shares down");
3851 auto const start = env.balance(d.depositor, d.assets).number();
3852 auto tx = d.vault.withdraw(
3853 {.depositor = d.depositor,
3858 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37));
3860 env.balance(d.depositor, d.assets) ==
3863 env.balance(d.vaultAccount, d.assets) ==
3866 env.balance(d.vaultAccount, d.shares) ==
3871 testcase(
"Scale withdraw tiny amount");
3873 auto const start = env.balance(d.depositor, d.assets).number();
3874 auto tx = d.vault.withdraw(
3875 {.depositor = d.depositor,
3880 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1));
3882 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(1, -1)));
3884 env.balance(d.vaultAccount, d.assets) ==
3887 env.balance(d.vaultAccount, d.shares) ==
3893 auto const rest = env.balance(d.vaultAccount, d.assets).number();
3895 tx = d.vault.withdraw(
3896 {.depositor = d.depositor,
3898 .amount =
STAmount(d.asset, rest)});
3901 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3902 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
3903 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
3907 testCase(18, [&,
this](Env& env, Data d) {
3908 testcase(
"Scale clawback overflow");
3911 auto tx = d.vault.deposit(
3912 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3918 auto tx = d.vault.clawback(
3919 {.issuer = d.issuer,
3921 .holder = d.depositor,
3928 testCase(1, [&,
this](Env& env, Data d) {
3930 auto const start = env.balance(d.depositor, d.assets).number();
3931 auto tx = d.vault.deposit(
3932 {.depositor = d.depositor,
3937 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3939 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(100, 0)));
3941 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(100, 0)));
3943 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(1000, 0)));
3953 auto const start = env.balance(d.depositor, d.assets).number();
3954 auto tx = d.vault.clawback(
3955 {.issuer = d.issuer,
3957 .holder = d.depositor,
3961 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
3962 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3964 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(90, 0)));
3966 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(900, 0)));
3970 testcase(
"Scale clawback insignificant amount");
3971 auto tx = d.vault.clawback(
3972 {.issuer = d.issuer,
3974 .holder = d.depositor,
3980 testcase(
"Scale clawback with rounding assets");
3988 auto const start = env.balance(d.depositor, d.assets).number();
3989 auto tx = d.vault.clawback(
3990 {.issuer = d.issuer,
3992 .holder = d.depositor,
3996 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
3997 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3999 env.balance(d.vaultAccount, d.assets) ==
4002 env.balance(d.vaultAccount, d.shares) ==
4007 testcase(
"Scale clawback with rounding shares up");
4015 auto const start = env.balance(d.depositor, d.assets).number();
4016 auto tx = d.vault.clawback(
4017 {.issuer = d.issuer,
4019 .holder = d.depositor,
4023 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38));
4024 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
4026 env.balance(d.vaultAccount, d.assets) ==
4029 env.balance(d.vaultAccount, d.shares) ==
4034 testcase(
"Scale clawback with rounding shares down");
4042 auto const start = env.balance(d.depositor, d.assets).number();
4043 auto tx = d.vault.clawback(
4044 {.issuer = d.issuer,
4046 .holder = d.depositor,
4050 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37));
4051 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
4053 env.balance(d.vaultAccount, d.assets) ==
4056 env.balance(d.vaultAccount, d.shares) ==
4061 testcase(
"Scale clawback tiny amount");
4063 auto const start = env.balance(d.depositor, d.assets).number();
4064 auto tx = d.vault.clawback(
4065 {.issuer = d.issuer,
4067 .holder = d.depositor,
4071 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1));
4072 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
4074 env.balance(d.vaultAccount, d.assets) ==
4077 env.balance(d.vaultAccount, d.shares) ==
4083 auto const rest = env.balance(d.vaultAccount, d.assets).number();
4084 d.peek([](
SLE& vault,
auto&) ->
bool {
4085 vault[sfAssetsAvailable] =
Number(5);
4093 tx = d.vault.clawback(
4094 {.issuer = d.issuer,
4096 .holder = d.depositor,
4097 .amount =
STAmount(d.asset, rest)});
4100 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4101 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
4102 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
4110 using namespace test::jtx;
4113 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4114 Account
const owner{
"owner"};
4115 Account
const issuer{
"issuer"};
4116 Vault
const vault{env};
4117 env.fund(XRP(1000), issuer, owner);
4121 env.trust(asset(1000), owner);
4122 env(pay(issuer, owner, asset(200)));
4125 auto const sequence = env.seq(owner);
4126 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
4132 auto tx1 = vault.deposit({.depositor = owner, .id = keylet.
key, .amount = asset(50)});
4135 auto tx2 = vault.set({.owner = owner, .id = keylet.
key});
4136 tx2[sfAssetsMaximum] = asset(1000).number();
4141 auto const sleVault = [&env, keylet = keylet,
this]() {
4142 auto const vault = env.le(keylet);
4143 BEAST_EXPECT(vault !=
nullptr);
4147 auto const check = [&, keylet = keylet, sle = sleVault,
this](
4150 BEAST_EXPECT(vault.isObject());
4152 constexpr auto checkString =
4154 return node.isMember(field.fieldName) && node[field.fieldName].isString() &&
4155 node[field.fieldName] == v;
4157 constexpr auto checkObject =
4159 return node.isMember(field.fieldName) && node[field.fieldName].isObject() &&
4160 node[field.fieldName] == v;
4162 constexpr auto checkInt = [](
auto& node,
SField const& field,
int v) ->
bool {
4163 return node.isMember(field.fieldName) &&
4164 ((node[field.fieldName].isInt() && node[field.fieldName] ==
Json::Int(v)) ||
4165 (node[field.fieldName].isUInt() && node[field.fieldName] ==
Json::UInt(v)));
4168 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
4169 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(keylet.
key));
4170 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
4173 BEAST_EXPECT(checkString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
4174 BEAST_EXPECT(checkObject(vault, sfAsset,
to_json(sle->at(sfAsset))));
4175 BEAST_EXPECT(checkString(vault, sfAssetsAvailable,
"50"));
4176 BEAST_EXPECT(checkString(vault, sfAssetsMaximum,
"1000"));
4177 BEAST_EXPECT(checkString(vault, sfAssetsTotal,
"50"));
4178 BEAST_EXPECT(!vault.isMember(sfLossUnrealized.getJsonName()));
4180 auto const strShareID =
strHex(sle->at(sfShareMPTID));
4181 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
4182 BEAST_EXPECT(checkString(vault, sfOwner,
toBase58(owner.id())));
4183 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
4186 if (issuance.isObject())
4188 BEAST_EXPECT(issuance[
"LedgerEntryType"].asString() ==
"MPTokenIssuance");
4189 BEAST_EXPECT(issuance[jss::mpt_issuance_id].asString() == strShareID);
4190 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
4191 BEAST_EXPECT(checkInt(
4192 issuance, sfFlags,
int(lsfMPTCanEscrow | lsfMPTCanTrade | lsfMPTCanTransfer)));
4193 BEAST_EXPECT(checkString(issuance, sfOutstandingAmount,
"50000000"));
4198 testcase(
"RPC ledger_entry selected by key");
4200 jvParams[jss::ledger_index] = jss::validated;
4201 jvParams[jss::vault] =
strHex(keylet.
key);
4202 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4204 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4205 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4206 check(jvVault[jss::result][jss::node]);
4210 testcase(
"RPC ledger_entry selected by owner and seq");
4212 jvParams[jss::ledger_index] = jss::validated;
4213 jvParams[jss::vault][jss::owner] = owner.human();
4214 jvParams[jss::vault][jss::seq] = sequence;
4215 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4217 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4218 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4219 check(jvVault[jss::result][jss::node]);
4223 testcase(
"RPC ledger_entry cannot find vault by key");
4225 jvParams[jss::ledger_index] = jss::validated;
4227 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4228 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4232 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
4234 jvParams[jss::ledger_index] = jss::validated;
4235 jvParams[jss::vault][jss::owner] = issuer.human();
4236 jvParams[jss::vault][jss::seq] = 1'000'000;
4237 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4238 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4242 testcase(
"RPC ledger_entry malformed key");
4244 jvParams[jss::ledger_index] = jss::validated;
4245 jvParams[jss::vault] = 42;
4246 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4247 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
4251 testcase(
"RPC ledger_entry malformed owner");
4253 jvParams[jss::ledger_index] = jss::validated;
4254 jvParams[jss::vault][jss::owner] = 42;
4255 jvParams[jss::vault][jss::seq] = sequence;
4256 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4257 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedOwner");
4261 testcase(
"RPC ledger_entry malformed seq");
4263 jvParams[jss::ledger_index] = jss::validated;
4264 jvParams[jss::vault][jss::owner] = issuer.human();
4265 jvParams[jss::vault][jss::seq] =
"foo";
4266 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4267 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
4271 testcase(
"RPC ledger_entry negative seq");
4273 jvParams[jss::ledger_index] = jss::validated;
4274 jvParams[jss::vault][jss::owner] = issuer.human();
4275 jvParams[jss::vault][jss::seq] = -1;
4276 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4277 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
4281 testcase(
"RPC ledger_entry oversized seq");
4283 jvParams[jss::ledger_index] = jss::validated;
4284 jvParams[jss::vault][jss::owner] = issuer.human();
4285 jvParams[jss::vault][jss::seq] = 1e20;
4286 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4287 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
4291 testcase(
"RPC ledger_entry bool seq");
4293 jvParams[jss::ledger_index] = jss::validated;
4294 jvParams[jss::vault][jss::owner] = issuer.human();
4295 jvParams[jss::vault][jss::seq] =
true;
4296 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4297 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
4304 jvParams[jss::account] = owner.human();
4305 jvParams[jss::type] = jss::vault;
4306 auto jv = env.rpc(
"json",
"account_objects",
to_string(jvParams))[jss::result];
4308 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
4309 check(jv[jss::account_objects][0u]);
4316 jvParams[jss::ledger_index] = jss::validated;
4317 jvParams[jss::binary] =
false;
4318 jvParams[jss::type] = jss::vault;
4320 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
4321 check(jv[jss::result][jss::state][0u]);
4325 testcase(
"RPC vault_info command line");
4328 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4329 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4330 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
4336 jvParams[jss::ledger_index] = jss::validated;
4337 jvParams[jss::vault_id] =
strHex(keylet.
key);
4338 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4340 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4341 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4342 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
4346 testcase(
"RPC vault_info invalid vault_id");
4348 jvParams[jss::ledger_index] = jss::validated;
4349 jvParams[jss::vault_id] =
"foobar";
4350 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4351 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4355 testcase(
"RPC vault_info json invalid index");
4357 jvParams[jss::ledger_index] = jss::validated;
4358 jvParams[jss::vault_id] = 0;
4359 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4360 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4364 testcase(
"RPC vault_info json by owner and sequence");
4366 jvParams[jss::ledger_index] = jss::validated;
4367 jvParams[jss::owner] = owner.human();
4368 jvParams[jss::seq] = sequence;
4369 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4371 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4372 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4373 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
4377 testcase(
"RPC vault_info json malformed sequence");
4379 jvParams[jss::ledger_index] = jss::validated;
4380 jvParams[jss::owner] = owner.human();
4381 jvParams[jss::seq] =
"foobar";
4382 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4383 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4387 testcase(
"RPC vault_info json invalid sequence");
4389 jvParams[jss::ledger_index] = jss::validated;
4390 jvParams[jss::owner] = owner.human();
4391 jvParams[jss::seq] = 0;
4392 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4393 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4397 testcase(
"RPC vault_info json negative sequence");
4399 jvParams[jss::ledger_index] = jss::validated;
4400 jvParams[jss::owner] = owner.human();
4401 jvParams[jss::seq] = -1;
4402 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4403 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4407 testcase(
"RPC vault_info json oversized sequence");
4409 jvParams[jss::ledger_index] = jss::validated;
4410 jvParams[jss::owner] = owner.human();
4411 jvParams[jss::seq] = 1e20;
4412 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4413 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4417 testcase(
"RPC vault_info json bool sequence");
4419 jvParams[jss::ledger_index] = jss::validated;
4420 jvParams[jss::owner] = owner.human();
4421 jvParams[jss::seq] =
true;
4422 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4423 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4427 testcase(
"RPC vault_info json malformed owner");
4429 jvParams[jss::ledger_index] = jss::validated;
4430 jvParams[jss::owner] =
"foobar";
4431 jvParams[jss::seq] = sequence;
4432 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4433 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4437 testcase(
"RPC vault_info json invalid combination only owner");
4439 jvParams[jss::ledger_index] = jss::validated;
4440 jvParams[jss::owner] = owner.human();
4441 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4442 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4446 testcase(
"RPC vault_info json invalid combination only seq");
4448 jvParams[jss::ledger_index] = jss::validated;
4449 jvParams[jss::seq] = sequence;
4450 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4451 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4455 testcase(
"RPC vault_info json invalid combination seq vault_id");
4457 jvParams[jss::ledger_index] = jss::validated;
4458 jvParams[jss::vault_id] =
strHex(keylet.
key);
4459 jvParams[jss::seq] = sequence;
4460 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4461 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4465 testcase(
"RPC vault_info json invalid combination owner vault_id");
4467 jvParams[jss::ledger_index] = jss::validated;
4468 jvParams[jss::vault_id] =
strHex(keylet.
key);
4469 jvParams[jss::owner] = owner.human();
4470 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4471 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4476 "RPC vault_info json invalid combination owner seq "
4479 jvParams[jss::ledger_index] = jss::validated;
4480 jvParams[jss::vault_id] =
strHex(keylet.
key);
4481 jvParams[jss::seq] = sequence;
4482 jvParams[jss::owner] = owner.human();
4483 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4484 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4488 testcase(
"RPC vault_info json no input");
4490 jvParams[jss::ledger_index] = jss::validated;
4491 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4492 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4496 testcase(
"RPC vault_info command line invalid index");
4497 Json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
4498 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
4502 testcase(
"RPC vault_info command line invalid index");
4503 Json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
4504 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4508 testcase(
"RPC vault_info command line invalid index");
4510 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"entryNotFound");
4514 testcase(
"RPC vault_info command line invalid ledger");
4516 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"lgrNotFound");
4523 using namespace test::jtx;
4524 using namespace loanBroker;
4525 using namespace loan;
4528 auto const vaultAssetBalance = [&](
Keylet const& vaultKeylet) {
4529 auto const sleVault = env.le(vaultKeylet);
4530 BEAST_EXPECT(sleVault !=
nullptr);
4532 return std::make_pair(sleVault->at(sfAssetsAvailable), sleVault->at(sfAssetsTotal));
4535 auto const vaultShareBalance = [&](
Keylet const& vaultKeylet) {
4536 auto const sleVault = env.le(vaultKeylet);
4537 BEAST_EXPECT(sleVault !=
nullptr);
4540 BEAST_EXPECT(sleIssuance !=
nullptr);
4542 return sleIssuance->at(sfOutstandingAmount);
4545 auto const setupVault = [&](
PrettyAsset const& asset,
4546 Account
const& owner,
4548 Vault
const vault{env};
4550 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
4554 auto const& vaultSle = env.le(vaultKeylet);
4555 BEAST_EXPECT(vaultSle !=
nullptr);
4557 Asset const share = vaultSle->at(sfShareMPTID);
4560 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
4564 auto const& [availablePreDefault, totalPreDefault] = vaultAssetBalance(vaultKeylet);
4565 BEAST_EXPECT(availablePreDefault == totalPreDefault);
4566 BEAST_EXPECT(availablePreDefault == asset(100).value());
4571 .id = vaultKeylet.key,
4572 .holder = depositor,
4573 .amount = share(0).value()}),
4577 auto const& sharesAvailable = vaultShareBalance(vaultKeylet);
4580 env(
set(owner, vaultKeylet.key));
4583 auto const& loanKeylet =
keylet::loan(brokerKeylet.key, 1);
4586 env(
set(depositor, brokerKeylet.key, asset(100).value()),
4589 paymentInterval(120),
4591 sig(sfCounterpartySignature, owner),
4592 fee(env.current()->fees().base * 2),
4600 .id = vaultKeylet.key,
4601 .holder = depositor,
4602 .amount = share(0).value()}),
4608 env(manage(owner, loanKeylet.key, tfLoanDefault), ter(
tesSUCCESS));
4610 auto const& [availablePostDefault, totalPostDefault] = vaultAssetBalance(vaultKeylet);
4612 BEAST_EXPECT(availablePostDefault == totalPostDefault);
4613 BEAST_EXPECT(availablePostDefault == asset(0).value());
4614 BEAST_EXPECT(vaultShareBalance(vaultKeylet) == sharesAvailable);
4619 auto const testCase = [&](
PrettyAsset const& asset,
4621 Account
const& owner,
4622 Account
const& depositor) {
4624 testcase(
"VaultClawback (share) - " + prefix +
" owner asset clawback fails");
4625 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4629 auto const expectedTer = [&]() {
4636 env(vault.clawback({
4638 .id = vaultKeylet.key,
4639 .holder = depositor,
4640 .amount = asset(100).value(),
4648 "VaultClawback (share) - " + prefix +
" owner incomplete share clawback fails");
4649 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4650 auto const& vaultSle = env.le(vaultKeylet);
4651 BEAST_EXPECT(vaultSle !=
nullptr);
4654 Asset const share = vaultSle->at(sfShareMPTID);
4655 env(vault.clawback({
4657 .id = vaultKeylet.key,
4658 .holder = depositor,
4659 .amount = share(1).value(),
4667 "VaultClawback (share) - " + prefix +
4668 " owner implicit complete share clawback");
4669 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4670 env(vault.clawback({
4672 .id = vaultKeylet.key,
4673 .holder = depositor,
4683 "VaultClawback (share) - " + prefix +
4684 " owner explicit complete share clawback succeeds");
4685 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4686 auto const& vaultSle = env.le(vaultKeylet);
4687 BEAST_EXPECT(vaultSle !=
nullptr);
4690 Asset const share = vaultSle->at(sfShareMPTID);
4691 env(vault.clawback({
4693 .id = vaultKeylet.key,
4694 .holder = depositor,
4695 .amount = share(vaultShareBalance(vaultKeylet)).value(),
4701 testcase(
"VaultClawback (share) - " + prefix +
" owner can clawback own shares");
4702 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
4703 auto const& vaultSle = env.le(vaultKeylet);
4704 BEAST_EXPECT(vaultSle !=
nullptr);
4707 Asset const share = vaultSle->at(sfShareMPTID);
4708 env(vault.clawback({
4710 .id = vaultKeylet.key,
4712 .amount = share(vaultShareBalance(vaultKeylet)).value(),
4719 testcase(
"VaultClawback (share) - " + prefix +
" empty vault share clawback fails");
4720 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
4721 auto const& vaultSle = env.le(vaultKeylet);
4722 if (BEAST_EXPECT(vaultSle !=
nullptr))
4724 Asset const share = vaultSle->at(sfShareMPTID);
4725 env(vault.clawback({
4727 .id = vaultKeylet.key,
4729 .amount = share(vaultShareBalance(vaultKeylet)).value(),
4734 env(vault.clawback({
4736 .id = vaultKeylet.key,
4744 Account owner{
"alice"};
4745 Account depositor{
"bob"};
4746 Account
const issuer{
"issuer"};
4748 env.fund(XRP(10000), issuer, owner, depositor);
4753 testCase(xrp,
"XRP", owner, depositor);
4754 testCase(xrp,
"XRP (depositor is owner)", owner, owner);
4758 env(fset(issuer, asfAllowTrustLineClawback));
4761 env.trust(IOU(1000), owner);
4762 env.trust(IOU(1000), depositor);
4763 env(pay(issuer, owner, IOU(100)));
4764 env(pay(issuer, depositor, IOU(100)));
4766 testCase(IOU,
"IOU", owner, depositor);
4767 testCase(IOU,
"IOU (owner is issuer)", issuer, depositor);
4770 MPTTester mptt{env, issuer, mptInitNoFund};
4771 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
4773 mptt.authorize({.account = owner});
4774 mptt.authorize({.account = depositor});
4775 env(pay(issuer, owner, MPT(1000)));
4776 env(pay(issuer, depositor, MPT(1000)));
4778 testCase(MPT,
"MPT", owner, depositor);
4779 testCase(MPT,
"MPT (owner is issuer)", issuer, depositor);
4785 using namespace test::jtx;
4786 using namespace loanBroker;
4787 using namespace loan;
4790 auto const setupVault = [&](
PrettyAsset const& asset,
4791 Account
const& owner,
4792 Account
const& depositor,
4794 Vault
const vault{env};
4796 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
4800 auto const& vaultSle = env.le(vaultKeylet);
4801 BEAST_EXPECT(vaultSle !=
nullptr);
4803 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
4810 auto const testCase = [&](
PrettyAsset const& asset,
4812 Account
const& owner,
4813 Account
const& depositor,
4814 Account
const& issuer) {
4817 testcase(
"VaultClawback (asset) - " + prefix +
" issuer XRP clawback fails");
4818 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4821 env(vault.clawback({
4823 .id = vaultKeylet.key,
4825 .amount = asset(1).value(),
4829 env(vault.clawback({
4831 .id = vaultKeylet.key,
4840 "VaultClawback (asset) - " + prefix +
" clawback for different asset fails");
4841 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4843 Account
const issuer2{
"issuer2"};
4845 env(vault.clawback({
4847 .id = vaultKeylet.key,
4848 .holder = depositor,
4849 .amount = asset2(1).value(),
4856 "VaultClawback (asset) - " + prefix +
4857 " ambiguous owner/issuer asset clawback fails");
4858 auto [vault, vaultKeylet] = setupVault(asset, issuer, depositor, issuer);
4859 env(vault.clawback({
4861 .id = vaultKeylet.key,
4868 testcase(
"VaultClawback (asset) - " + prefix +
" non-issuer asset clawback fails");
4869 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4871 env(vault.clawback({
4873 .id = vaultKeylet.key,
4874 .holder = depositor,
4878 env(vault.clawback({
4880 .id = vaultKeylet.key,
4881 .holder = depositor,
4882 .amount = asset(1).value(),
4888 testcase(
"VaultClawback (asset) - " + prefix +
" issuer clawback from self fails");
4889 auto [vault, vaultKeylet] = setupVault(asset, owner, issuer, issuer);
4890 env(vault.clawback({
4892 .id = vaultKeylet.key,
4899 testcase(
"VaultClawback (asset) - " + prefix +
" issuer share clawback fails");
4900 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4901 auto const& vaultSle = env.le(vaultKeylet);
4902 BEAST_EXPECT(vaultSle !=
nullptr);
4905 Asset const share = vaultSle->at(sfShareMPTID);
4907 env(vault.clawback({
4909 .id = vaultKeylet.key,
4910 .holder = depositor,
4911 .amount = share(1).value(),
4918 "VaultClawback (asset) - " + prefix +
4919 " partial issuer asset clawback succeeds");
4920 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4922 env(vault.clawback({
4924 .id = vaultKeylet.key,
4925 .holder = depositor,
4926 .amount = asset(1).value(),
4933 "VaultClawback (asset) - " + prefix +
" full issuer asset clawback succeeds");
4934 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4936 env(vault.clawback({
4938 .id = vaultKeylet.key,
4939 .holder = depositor,
4940 .amount = asset(100).value(),
4947 "VaultClawback (asset) - " + prefix +
4948 " implicit full issuer asset clawback succeeds");
4949 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4951 env(vault.clawback({
4953 .id = vaultKeylet.key,
4954 .holder = depositor,
4960 Account owner{
"alice"};
4961 Account depositor{
"bob"};
4962 Account
const issuer{
"issuer"};
4964 env.fund(XRP(10000), issuer, owner, depositor);
4969 testCase(xrp,
"XRP", owner, depositor, issuer);
4973 env(fset(issuer, asfAllowTrustLineClawback));
4975 env.trust(IOU(1000), owner);
4976 env.trust(IOU(1000), depositor);
4977 env(pay(issuer, owner, IOU(1000)));
4978 env(pay(issuer, depositor, IOU(1000)));
4980 testCase(IOU,
"IOU", owner, depositor, issuer);
4983 MPTTester mptt{env, issuer, mptInitNoFund};
4984 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
4986 mptt.authorize({.account = owner});
4987 mptt.authorize({.account = depositor});
4988 env(pay(issuer, depositor, MPT(1000)));
4990 testCase(MPT,
"MPT", owner, depositor, issuer);
4998 using namespace test::jtx;
5000 Env env{*
this, testable_amendments() | featureSingleAssetVault};
5001 Account
const owner{
"owner"};
5002 Account
const issuer{
"issuer"};
5004 Vault
const vault{env};
5005 env.fund(XRP(1'000'000), issuer, owner);
5009 BEAST_EXPECT(maxInt64 ==
"9223372036854775807");
5014 BEAST_EXPECT(maxInt64Plus1 ==
"9223372036854775808");
5017 BEAST_EXPECT(initialXRP ==
"100000000000000000");
5020 BEAST_EXPECT(initialXRPPlus1 ==
"100000000000000001");
5027 auto [tx, keylet] = vault.create({.owner = owner, .asset = xrpAsset});
5028 tx[sfData] =
"4D65746144617461";
5030 tx[sfAssetsMaximum] = maxInt64;
5034 tx[sfAssetsMaximum] = initialXRPPlus1;
5038 tx[sfAssetsMaximum] = initialXRP;
5042 tx[sfAssetsMaximum] = maxInt64Plus1;
5047 auto const insertAt = maxInt64Plus1.size() - 3;
5048 auto const decimalTest = maxInt64Plus1.substr(0, insertAt) +
"." +
5049 maxInt64Plus1.substr(insertAt);
5050 BEAST_EXPECT(decimalTest ==
"9223372036854775.808");
5051 tx[sfAssetsMaximum] = decimalTest;
5052 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5056 auto const vaultSle = env.le(newKeylet);
5057 if (!BEAST_EXPECT(vaultSle))
5060 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 9223372036854776);
5067 MPTTester mptt{env, issuer, mptInitNoFund};
5068 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
5071 mptt.authorize({.account = owner});
5076 env(pay(issuer, owner, mptAsset(100'000)));
5079 auto [tx, keylet] = vault.create({.owner = owner, .asset = mptAsset});
5080 tx[sfData] =
"4D65746144617461";
5082 tx[sfAssetsMaximum] = maxInt64;
5086 tx[sfAssetsMaximum] = initialXRPPlus1;
5090 tx[sfAssetsMaximum] = initialXRP;
5094 tx[sfAssetsMaximum] = maxInt64Plus1;
5099 auto const insertAt = maxInt64Plus1.size() - 1;
5100 auto const decimalTest = maxInt64Plus1.substr(0, insertAt) +
"." +
5101 maxInt64Plus1.substr(insertAt);
5102 BEAST_EXPECT(decimalTest ==
"922337203685477580.8");
5103 tx[sfAssetsMaximum] = decimalTest;
5104 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5108 auto const vaultSle = env.le(newKeylet);
5109 if (!BEAST_EXPECT(vaultSle))
5112 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 922337203685477581);
5120 env.trust(iouAsset(1000), owner);
5121 env(pay(issuer, owner, iouAsset(200)));
5124 auto [tx, keylet] = vault.create({.owner = owner, .asset = iouAsset});
5125 tx[sfData] =
"4D65746144617461";
5127 tx[sfAssetsMaximum] = maxInt64;
5131 tx[sfAssetsMaximum] = initialXRPPlus1;
5135 tx[sfAssetsMaximum] = initialXRP;
5139 tx[sfAssetsMaximum] = maxInt64Plus1;
5143 tx[sfAssetsMaximum] =
"1000000000000000e80";
5146 tx[sfAssetsMaximum] =
"1000000000000000e-96";
5151 auto const insertAt = maxInt64Plus1.size() - 1;
5152 auto const decimalTest = maxInt64Plus1.substr(0, insertAt) +
"." +
5153 maxInt64Plus1.substr(insertAt);
5154 BEAST_EXPECT(decimalTest ==
"922337203685477580.8");
5155 tx[sfAssetsMaximum] = decimalTest;
5156 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5160 auto const vaultSle = env.le(newKeylet);
5161 if (!BEAST_EXPECT(vaultSle))
5165 (vaultSle->at(sfAssetsMaximum) ==
5166 Number{9223372036854776, 2, Number::normalized{}}));
5169 tx[sfAssetsMaximum] =
"9223372036854775807e40";
5170 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5174 auto const vaultSle = env.le(newKeylet);
5175 if (!BEAST_EXPECT(vaultSle))
5179 (vaultSle->at(sfAssetsMaximum) ==
5180 Number{9223372036854776, 43, Number::normalized{}}));
5183 tx[sfAssetsMaximum] =
"9223372036854775807e-40";
5184 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5188 auto const vaultSle = env.le(newKeylet);
5189 if (!BEAST_EXPECT(vaultSle))
5193 (vaultSle->at(sfAssetsMaximum) ==
5194 Number{9223372036854776, -37, Number::normalized{}}));
5197 tx[sfAssetsMaximum] =
"9223372036854775807e-100";
5198 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5203 auto const vaultSle = env.le(newKeylet);
5204 if (!BEAST_EXPECT(vaultSle))
5207 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) ==
numZero);
5212 tx[sfAssetsMaximum] =
"1000000000000000e81";
5217 env.set_parse_failure_expected(
true);
5220 tx[sfAssetsMaximum] =
"18446744073709551617e5";
5222 BEAST_EXPECTS(
false,
"Expected parse_error for mantissa larger than uint64 max");
5224 catch (parse_error
const& e)
5226 using namespace std::string_literals;
5228 e.what() ==
"invalidParamsField 'tx_json.AssetsMaximum' has invalid data."s);
5230 env.set_parse_failure_expected(
false);