mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Implement Lending Protocol (unsupported) (#5270)
- Spec: XLS-66 - Introduces amendment "LendingProtocol", but leaves it UNSUPPORTED to allow for standalone testing, future development work, and potential bug fixes. - AccountInfo RPC will indicate the type of pseudo-account when appropriate. - Refactors and improves several existing classes and functional areas, including Number, STAmount, STObject, json_value, Asset, directory handling, View helper functions, and unit test helpers.
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
#include <test/jtx/AMMTest.h>
|
||||
#include <test/jtx/Env.h>
|
||||
#include <test/jtx/amount.h>
|
||||
#include <test/jtx/mpt.h>
|
||||
#include <test/jtx/testline.h>
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
@@ -69,14 +71,14 @@ class Vault_test : public beast::unit_test::suite
|
||||
this]() -> std::tuple<PrettyAsset, Account> {
|
||||
auto const vault = env.le(keylet);
|
||||
BEAST_EXPECT(vault != nullptr);
|
||||
if (asset.raw().holds<Issue>() && !asset.raw().native())
|
||||
if (!asset.integral())
|
||||
BEAST_EXPECT(vault->at(sfScale) == 6);
|
||||
else
|
||||
BEAST_EXPECT(vault->at(sfScale) == 0);
|
||||
auto const shares =
|
||||
env.le(keylet::mptIssuance(vault->at(sfShareMPTID)));
|
||||
BEAST_EXPECT(shares != nullptr);
|
||||
if (asset.raw().holds<Issue>() && !asset.raw().native())
|
||||
if (!asset.integral())
|
||||
BEAST_EXPECT(shares->at(sfAssetScale) == 6);
|
||||
else
|
||||
BEAST_EXPECT(shares->at(sfAssetScale) == 0);
|
||||
@@ -502,7 +504,7 @@ class Vault_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
if (!asset.raw().native() && asset.raw().holds<Issue>())
|
||||
if (!asset.integral())
|
||||
{
|
||||
testcase(prefix + " temporary authorization for 3rd party");
|
||||
env(trust(erin, asset(1000)));
|
||||
@@ -670,12 +672,13 @@ class Vault_test : public beast::unit_test::suite
|
||||
test(env, issuer, owner, asset, vault);
|
||||
};
|
||||
|
||||
testCase(
|
||||
[&](Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
auto testDisabled = [&](TER resultAfterCreate = temDISABLED) {
|
||||
return [&, resultAfterCreate](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
testcase("disabled single asset vault");
|
||||
|
||||
auto [tx, keylet] =
|
||||
@@ -684,7 +687,7 @@ class Vault_test : public beast::unit_test::suite
|
||||
|
||||
{
|
||||
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||
env(tx, ter{temDISABLED});
|
||||
env(tx, data("test"), ter{resultAfterCreate});
|
||||
}
|
||||
|
||||
{
|
||||
@@ -692,7 +695,7 @@ class Vault_test : public beast::unit_test::suite
|
||||
{.depositor = owner,
|
||||
.id = keylet.key,
|
||||
.amount = asset(10)});
|
||||
env(tx, ter{temDISABLED});
|
||||
env(tx, ter{resultAfterCreate});
|
||||
}
|
||||
|
||||
{
|
||||
@@ -700,7 +703,7 @@ class Vault_test : public beast::unit_test::suite
|
||||
{.depositor = owner,
|
||||
.id = keylet.key,
|
||||
.amount = asset(10)});
|
||||
env(tx, ter{temDISABLED});
|
||||
env(tx, ter{resultAfterCreate});
|
||||
}
|
||||
|
||||
{
|
||||
@@ -709,15 +712,49 @@ class Vault_test : public beast::unit_test::suite
|
||||
.id = keylet.key,
|
||||
.holder = owner,
|
||||
.amount = asset(10)});
|
||||
env(tx, ter{temDISABLED});
|
||||
env(tx, ter{resultAfterCreate});
|
||||
}
|
||||
|
||||
{
|
||||
auto tx = vault.del({.owner = owner, .id = keylet.key});
|
||||
env(tx, ter{resultAfterCreate});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
testCase(
|
||||
testDisabled(),
|
||||
{.features = testable_amendments() - featureSingleAssetVault});
|
||||
|
||||
testCase(
|
||||
testDisabled(tecNO_ENTRY),
|
||||
{.features = testable_amendments() - featureMPTokensV1});
|
||||
|
||||
testCase(
|
||||
[&](Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
testcase("disabled permissioned domains");
|
||||
|
||||
auto [tx, keylet] =
|
||||
vault.create({.owner = owner, .asset = asset});
|
||||
env(tx);
|
||||
|
||||
tx[sfFlags] = tx[sfFlags].asUInt() | tfVaultPrivate;
|
||||
tx[sfDomainID] = to_string(base_uint<256>(42ul));
|
||||
env(tx, ter{temDISABLED});
|
||||
|
||||
{
|
||||
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||
env(tx, data("Test"));
|
||||
|
||||
tx[sfDomainID] = to_string(base_uint<256>(13ul));
|
||||
env(tx, ter{temDISABLED});
|
||||
}
|
||||
},
|
||||
{.features = testable_amendments() - featureSingleAssetVault});
|
||||
{.features = testable_amendments() - featurePermissionedDomains});
|
||||
|
||||
testCase([&](Env& env,
|
||||
Account const& issuer,
|
||||
@@ -1730,7 +1767,8 @@ class Vault_test : public beast::unit_test::suite
|
||||
mptt.create(
|
||||
{.flags = tfMPTCanTransfer | tfMPTCanLock |
|
||||
(args.enableClawback ? tfMPTCanClawback : none) |
|
||||
(args.requireAuth ? tfMPTRequireAuth : none)});
|
||||
(args.requireAuth ? tfMPTRequireAuth : none),
|
||||
.mutableFlags = tmfMPTCanMutateCanTransfer});
|
||||
PrettyAsset asset = mptt.issuanceID();
|
||||
mptt.authorize({.account = owner});
|
||||
mptt.authorize({.account = depositor});
|
||||
@@ -2448,6 +2486,53 @@ class Vault_test : public beast::unit_test::suite
|
||||
env(tx2, ter{tecWRONG_ASSET});
|
||||
env.close();
|
||||
}
|
||||
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const&,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
PrettyAsset const& asset,
|
||||
Vault& vault,
|
||||
MPTTester& mptt) {
|
||||
testcase("MPT non-transferable");
|
||||
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(100)});
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
// Remove CanTransfer
|
||||
mptt.set({.mutableFlags = tmfMPTClearCanTransfer});
|
||||
env.close();
|
||||
|
||||
env(tx, ter{tecNO_AUTH});
|
||||
env.close();
|
||||
|
||||
tx = vault.withdraw(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(100)});
|
||||
|
||||
env(tx, ter{tecNO_AUTH});
|
||||
env.close();
|
||||
|
||||
// Restore CanTransfer
|
||||
mptt.set({.mutableFlags = tmfMPTSetCanTransfer});
|
||||
env.close();
|
||||
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
// Delete vault with zero balance
|
||||
env(vault.del({.owner = owner, .id = keylet.key}));
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2460,6 +2545,7 @@ class Vault_test : public beast::unit_test::suite
|
||||
int initialXRP = 1000;
|
||||
Number initialIOU = 200;
|
||||
double transferRate = 1.0;
|
||||
bool charlieRipple = true;
|
||||
};
|
||||
|
||||
auto testCase =
|
||||
@@ -2485,8 +2571,21 @@ class Vault_test : public beast::unit_test::suite
|
||||
|
||||
PrettyAsset const asset = issuer["IOU"];
|
||||
env.trust(asset(1000), owner);
|
||||
env.trust(asset(1000), charlie);
|
||||
env(pay(issuer, owner, asset(args.initialIOU)));
|
||||
env.close();
|
||||
if (!args.charlieRipple)
|
||||
{
|
||||
env(fset(issuer, 0, asfDefaultRipple));
|
||||
env.close();
|
||||
env.trust(asset(1000), charlie);
|
||||
env.close();
|
||||
env(pay(issuer, charlie, asset(args.initialIOU)));
|
||||
env.close();
|
||||
env(fset(issuer, asfDefaultRipple));
|
||||
}
|
||||
else
|
||||
env.trust(asset(1000), charlie);
|
||||
env.close();
|
||||
env(rate(issuer, args.transferRate));
|
||||
env.close();
|
||||
|
||||
@@ -2864,6 +2963,94 @@ class Vault_test : public beast::unit_test::suite
|
||||
env(tx1);
|
||||
});
|
||||
|
||||
testCase(
|
||||
[&, this](
|
||||
Env& env,
|
||||
Account const& owner,
|
||||
Account const& issuer,
|
||||
Account const& charlie,
|
||||
auto vaultAccount,
|
||||
Vault& vault,
|
||||
PrettyAsset const& asset,
|
||||
std::function<MPTID(ripple::Keylet)> issuanceId) {
|
||||
testcase("IOU non-transferable");
|
||||
|
||||
auto [tx, keylet] =
|
||||
vault.create({.owner = owner, .asset = asset});
|
||||
tx[sfScale] = 0;
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
// Turn on noripple on the pseudo account's trust line.
|
||||
// Charlie's is already set.
|
||||
env(trust(issuer, vaultAccount(keylet)["IOU"], tfSetNoRipple),
|
||||
THISLINE);
|
||||
|
||||
{
|
||||
// Charlie cannot deposit
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = charlie,
|
||||
.id = keylet.key,
|
||||
.amount = asset(100)});
|
||||
env(tx, ter{terNO_RIPPLE}, THISLINE);
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
PrettyAsset shares = issuanceId(keylet);
|
||||
auto tx1 = vault.deposit(
|
||||
{.depositor = owner,
|
||||
.id = keylet.key,
|
||||
.amount = asset(100)});
|
||||
env(tx1, THISLINE);
|
||||
env.close();
|
||||
|
||||
// Charlie cannot receive funds
|
||||
auto tx2 = vault.withdraw(
|
||||
{.depositor = owner,
|
||||
.id = keylet.key,
|
||||
.amount = shares(100)});
|
||||
tx2[sfDestination] = charlie.human();
|
||||
env(tx2, ter{terNO_RIPPLE}, THISLINE);
|
||||
env.close();
|
||||
|
||||
{
|
||||
// Create MPToken for shares held by Charlie
|
||||
Json::Value tx{Json::objectValue};
|
||||
tx[sfAccount] = charlie.human();
|
||||
tx[sfMPTokenIssuanceID] =
|
||||
to_string(shares.raw().get<MPTIssue>().getMptID());
|
||||
tx[sfTransactionType] = jss::MPTokenAuthorize;
|
||||
env(tx);
|
||||
env.close();
|
||||
}
|
||||
env(pay(owner, charlie, shares(100)), THISLINE);
|
||||
env.close();
|
||||
|
||||
// Charlie cannot withdraw
|
||||
auto tx3 = vault.withdraw(
|
||||
{.depositor = charlie,
|
||||
.id = keylet.key,
|
||||
.amount = shares(100)});
|
||||
env(tx3, ter{terNO_RIPPLE});
|
||||
env.close();
|
||||
|
||||
env(pay(charlie, owner, shares(100)), THISLINE);
|
||||
env.close();
|
||||
}
|
||||
|
||||
tx = vault.withdraw(
|
||||
{.depositor = owner,
|
||||
.id = keylet.key,
|
||||
.amount = asset(100)});
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
// Delete vault with zero balance
|
||||
env(vault.del({.owner = owner, .id = keylet.key}), THISLINE);
|
||||
},
|
||||
{.charlieRipple = false});
|
||||
|
||||
testCase(
|
||||
[&, this](
|
||||
Env& env,
|
||||
@@ -4525,7 +4712,7 @@ class Vault_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
|
||||
BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
|
||||
BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
|
||||
BEAST_EXPECT(checkString(vault, sfLossUnrealized, "0"));
|
||||
BEAST_EXPECT(!vault.isMember(sfLossUnrealized.getJsonName()));
|
||||
|
||||
auto const strShareID = strHex(sle->at(sfShareMPTID));
|
||||
BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
|
||||
|
||||
Reference in New Issue
Block a user