64 Env env{*
this, testableAmendments()};
67 Account const depositor{
"depositor"};
68 env.fund(XRP(1000), issuer, owner, depositor);
71 Vault const vault{env};
73 env.trust(asset(1000), owner);
74 env(pay(issuer, owner, asset(100)));
75 env.trust(asset(1000), depositor);
76 env(pay(issuer, depositor, asset(100)));
79 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
80 tx[sfFlags] = tfVaultShareNonTransferable;
85 testcase(
"nontransferable deposits");
87 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(40)});
90 auto tx2 = vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(60)});
95 auto const vaultAccount =
97 auto jvVault = env.rpc(
"vault_info",
strHex(key));
99 BEAST_EXPECT(jvVault[jss::result][jss::vault][sfAssetsTotal] ==
"100");
101 jvVault[jss::result][jss::vault][jss::shares][sfOutstandingAmount] ==
"100000000");
108 auto const mptId =
makeMptID(1, vaultAccount);
109 Asset const shares = mptId;
112 testcase(
"nontransferable shares cannot be moved");
113 env(pay(owner, depositor, shares(10)), Ter{
tecNO_AUTH});
114 env(pay(depositor, owner, shares(10)), Ter{
tecNO_AUTH});
118 testcase(
"nontransferable shares can be used to withdraw");
120 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(20)});
123 auto tx2 = vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(30)});
129 testcase(
"nontransferable shares balance check");
130 auto jvVault = env.rpc(
"vault_info",
strHex(
keylet.key));
131 BEAST_EXPECT(jvVault[jss::result][jss::vault][sfAssetsTotal] ==
"50");
133 jvVault[jss::result][jss::vault][jss::shares][sfOutstandingAmount] ==
"50000000");
137 testcase(
"nontransferable shares withdraw rest");
139 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(20)});
142 auto tx2 = vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(30)});
148 testcase(
"nontransferable shares delete empty vault");
149 auto tx = vault.del({.owner = owner, .id =
keylet.key});
151 BEAST_EXPECT(!env.le(
keylet));
185 testcase(
"removeEmptyHolding deletes MPToken with sfLockedAmount");
189 auto const amendments = testableAmendments();
192 auto const baseFee = env.current()->fees().base;
194 Account const issuer{
"issuer"};
196 Account const depositor{
"depositor"};
199 env.fund(XRP(100000), issuer, owner, depositor, bob);
202 Vault const vault{env};
205 MPTTester mptt{env, issuer, kMptInitNoFund};
206 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
208 mptt.authorize({.account = owner});
209 mptt.authorize({.account = depositor});
210 env(pay(issuer, depositor, asset(1000)));
214 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
218 auto const vaultSle = env.le(
keylet);
219 BEAST_EXPECT(vaultSle !=
nullptr);
220 auto const shareMptID = vaultSle->at(sfShareMPTID);
221 MPTIssue const shareIssue{shareMptID};
224 env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)}));
230 BEAST_EXPECT(sleMpt !=
nullptr);
231 BEAST_EXPECT(sleMpt->at(sfMPTAmount) == 1000);
235 env(escrow::create(depositor, bob,
STAmount{shareIssue, 500}),
236 escrow::kCondition(escrow::kCb1),
237 escrow::kFinishTime(env.now() + 1s),
245 BEAST_EXPECT(sleMpt !=
nullptr);
246 BEAST_EXPECT(sleMpt->at(sfLockedAmount) == 500);
247 BEAST_EXPECT(sleMpt->at(sfMPTAmount) == 500);
251 env(vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(500)}),
255 auto const sleMptAfter = env.le(
keylet::mptoken(shareMptID, depositor));
256 if (!f[fixCleanup3_1_3])
261 BEAST_EXPECT(sleMptAfter ==
nullptr);
267 BEAST_EXPECT(sleMptAfter !=
nullptr);
270 BEAST_EXPECT(sleMptAfter->at(sfLockedAmount) == 500);
271 BEAST_EXPECT(sleMptAfter->at(sfMPTAmount) == 0);
276 runTest(amendments - fixCleanup3_1_3);
326 auto readReferenceHolding = [&](Env
const& env,
328 auto const sleVault = env.le(vaultKeylet);
332 if (!sleIssuance || !sleIssuance->isFieldPresent(sfReferenceHolding))
334 return sleIssuance->getFieldH256(sfReferenceHolding);
341 testcase(
"sfReferenceHolding: MPT-backed vault, post-amendment");
342 Env env{*
this, testableAmendments()};
343 Account const issuer{
"issuer"};
345 env.fund(XRP(10'000), issuer, owner);
348 MPTTester mptt{env, issuer, kMptInitNoFund};
349 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
351 mptt.authorize({.account = owner});
353 Vault const vault{env};
354 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
358 auto const sleVault = env.le(
keylet);
359 BEAST_EXPECT(sleVault !=
nullptr);
360 auto const pseudoId = sleVault->at(sfAccount);
363 auto const stored = readReferenceHolding(env,
keylet);
364 BEAST_EXPECT(stored.has_value());
365 BEAST_EXPECT(stored && *stored == expected);
367 BEAST_EXPECT(env.le(
keylet::mptoken(mptt.issuanceID(), pseudoId)) !=
nullptr);
371 testcase(
"sfReferenceHolding: IOU-backed vault, post-amendment");
372 Env env{*
this, testableAmendments()};
373 Account const issuer{
"issuer"};
375 env.fund(XRP(10'000), issuer, owner);
376 env(fset(issuer, asfDefaultRipple));
380 env.trust(asset(1'000'000), owner);
383 Vault const vault{env};
384 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
388 auto const sleVault = env.le(
keylet);
389 BEAST_EXPECT(sleVault !=
nullptr);
390 auto const pseudoId = sleVault->at(sfAccount);
393 auto const stored = readReferenceHolding(env,
keylet);
394 BEAST_EXPECT(stored.has_value());
395 BEAST_EXPECT(stored && *stored == expected);
403 testcase(
"sfReferenceHolding: XRP-backed vault, field absent");
404 Env env{*
this, testableAmendments()};
406 env.fund(XRP(10'000), owner);
410 Vault const vault{env};
411 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
415 BEAST_EXPECT(!readReferenceHolding(env,
keylet).has_value());
421 testcase(
"sfReferenceHolding: vault share, pre-amendment");
422 Env env{*
this, testableAmendments() - fixCleanup3_2_0};
423 Account const issuer{
"issuer"};
425 env.fund(XRP(10'000), issuer, owner);
428 MPTTester mptt{env, issuer, kMptInitNoFund};
429 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
431 mptt.authorize({.account = owner});
433 Vault const vault{env};
434 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
438 BEAST_EXPECT(!readReferenceHolding(env,
keylet).has_value());
445 testcase(
"sfReferenceHolding: plain MPT issuance never set");
446 Env env{*
this, testableAmendments()};
447 Account const issuer{
"issuer"};
448 env.fund(XRP(10'000), issuer);
451 MPTTester mptt{env, issuer, kMptInitNoFund};
452 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
456 if (BEAST_EXPECT(sleIssuance))
457 BEAST_EXPECT(!sleIssuance->isFieldPresent(sfReferenceHolding));
477 auto referencedHoldingExists = [&](Env
const& env,
Keylet const& vaultKeylet) ->
bool {
478 auto const sleVault = env.le(vaultKeylet);
482 if (!sleIssuance || !sleIssuance->isFieldPresent(sfReferenceHolding))
484 auto const holdingKey = sleIssuance->getFieldH256(sfReferenceHolding);
490 testcase(
"vault pseudo MPToken: Clawback blocked by tecPSEUDO_ACCOUNT");
491 Env env{*
this, testableAmendments()};
492 Account const issuer{
"issuer"};
494 Account const depositor{
"depositor"};
495 env.fund(XRP(10'000), issuer, owner, depositor);
498 MPTTester mptt{env, issuer, kMptInitNoFund};
499 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanClawback});
501 mptt.authorize({.account = owner});
502 mptt.authorize({.account = depositor});
503 env(pay(issuer, depositor, asset(1'000)));
506 Vault const vault{env};
507 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
511 env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(500)}));
514 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
516 Account const pseudoAccount{
"vault-pseudo", env.le(
keylet)->at(sfAccount)};
526 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
528 BEAST_EXPECT(env.balance(pseudoAccount, asset).number() == 500);
532 testcase(
"vault pseudo MPToken: Issuer cannot Unauthorize pseudo");
533 Env env{*
this, testableAmendments()};
534 Account const issuer{
"issuer"};
536 env.fund(XRP(10'000), issuer, owner);
539 MPTTester mptt{env, issuer, kMptInitNoFund};
540 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth});
542 mptt.authorize({.account = owner});
543 mptt.authorize({.account = issuer, .holder = owner});
546 Vault const vault{env};
547 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
551 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
553 auto const pseudoId = env.le(
keylet)->at(sfAccount);
561 jv[sfAccount] = issuer.human();
563 jv[sfMPTokenIssuanceID] =
to_string(mptt.issuanceID());
564 jv[sfFlags] = tfMPTUnauthorize;
565 jv[sfTransactionType] = jss::MPTokenAuthorize;
568 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
572 testcase(
"vault pseudo MPToken: MPTokenIssuanceDestroy blocked while vault holds");
573 Env env{*
this, testableAmendments()};
574 Account const issuer{
"issuer"};
576 Account const depositor{
"depositor"};
577 env.fund(XRP(10'000), issuer, owner, depositor);
580 MPTTester mptt{env, issuer, kMptInitNoFund};
581 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
583 mptt.authorize({.account = owner});
584 mptt.authorize({.account = depositor});
585 env(pay(issuer, depositor, asset(1'000)));
588 Vault const vault{env};
589 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
593 env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(500)}));
596 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
605 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
610 testcase(
"vault pseudo trust line: Clawback blocked by tecPSEUDO_ACCOUNT");
611 Env env{*
this, testableAmendments()};
612 Account const issuer{
"issuer"};
614 env.fund(XRP(10'000), issuer, owner);
615 env(fset(issuer, asfAllowTrustLineClawback));
619 env.trust(asset(1'000'000), owner);
620 env(pay(issuer, owner, asset(1'000)));
623 Vault const vault{env};
624 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
628 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(500)}));
631 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
633 Account const pseudoAccount{
"vault-pseudo", env.le(
keylet)->at(sfAccount)};
644 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
646 BEAST_EXPECT(env.balance(pseudoAccount, asset).number() == 500);
650 testcase(
"vault pseudo trust line: TrustSet limit=0 from issuer preserves line");
651 Env env{*
this, testableAmendments()};
652 Account const issuer{
"issuer"};
654 env.fund(XRP(10'000), issuer, owner);
655 env(fset(issuer, asfDefaultRipple));
659 env.trust(asset(1'000'000), owner);
660 env(pay(issuer, owner, asset(1'000)));
663 Vault const vault{env};
664 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
668 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(500)}));
671 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
679 Account const pseudoAccount{
"vault-pseudo", env.le(
keylet)->at(sfAccount)};
680 env(trust(issuer, pseudoAccount[
"IOU"](0)));
682 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
687 testcase(
"vault pseudo holding: VaultDelete is the legitimate cleanup path");
688 Env env{*
this, testableAmendments()};
689 Account const issuer{
"issuer"};
691 env.fund(XRP(10'000), issuer, owner);
694 MPTTester mptt{env, issuer, kMptInitNoFund};
695 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
697 mptt.authorize({.account = owner});
699 Vault const vault{env};
700 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
704 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
705 auto const pseudoId = env.le(
keylet)->at(sfAccount);
706 auto const sharedMptId = env.le(
keylet)->at(sfShareMPTID);
707 auto const holdingKeylet =
keylet::mptoken(mptt.issuanceID(), pseudoId);
712 env(vault.del({.owner = owner, .id = keylet.key}));
715 BEAST_EXPECT(env.le(
keylet) ==
nullptr);
716 BEAST_EXPECT(env.le(holdingKeylet) ==
nullptr);