Skip to content

Commit

Permalink
Add MPT support to Payment.
Browse files Browse the repository at this point in the history
  • Loading branch information
gregtatcam committed Sep 27, 2024
1 parent a5171cb commit af93589
Show file tree
Hide file tree
Showing 14 changed files with 213 additions and 330 deletions.
3 changes: 1 addition & 2 deletions include/xrpl/protocol/AmountConversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,7 @@ template <>
inline MPTAmount
toAmount<MPTAmount>(STAmount const& amt)
{
assert(amt.mantissa() < std::numeric_limits<std::int64_t>::max());
assert(amt.holds<MPTIssue>());
assert(amt.holds<MPTIssue>() && amt.mantissa() <= maxMPTokenAmount);
bool const isNeg = amt.negative();
std::int64_t const sMant =
isNeg ? -std::int64_t(amt.mantissa()) : amt.mantissa();
Expand Down
3 changes: 3 additions & 0 deletions include/xrpl/protocol/MPTIssue.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ to_json(MPTIssue const& issue);
std::string
to_string(MPTIssue const& mpt);

std::ostream&
operator<<(std::ostream& os, MPTIssue const& x);

} // namespace ripple

namespace std {
Expand Down
7 changes: 7 additions & 0 deletions src/libxrpl/protocol/MPTIssue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,11 @@ to_string(MPTIssue const& mptIssue)
return to_string(mptIssue.getMptID());
}

std::ostream&
operator<<(std::ostream& os, MPTIssue const& x)
{
os << to_string(x);
return os;
}

} // namespace ripple
57 changes: 27 additions & 30 deletions src/test/app/MPToken_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -742,10 +742,10 @@ class MPToken_test : public beast::unit_test::suite
mptAlice.pay(alice, bob, 100);

// Pay to another holder
mptAlice.pay(bob, carol, 101, tecINSUFFICIENT_FUNDS);
mptAlice.pay(bob, carol, 101, tecPATH_PARTIAL);

// Pay to the issuer
mptAlice.pay(bob, alice, 101, tecINSUFFICIENT_FUNDS);
mptAlice.pay(bob, alice, 101, tecPATH_PARTIAL);
}

// MPT is locked
Expand Down Expand Up @@ -805,7 +805,11 @@ class MPToken_test : public beast::unit_test::suite
mptAlice.pay(alice, bob, 100);

// issuer tries to exceed max amount
mptAlice.pay(alice, bob, 1, tecMPT_MAX_AMOUNT_EXCEEDED);
auto const MPTA = mptAlice["MPTA"];
mptAlice.pay(alice, bob, 1, tecPATH_PARTIAL);
env(pay(alice, bob, MPTA(1)),
txflags(tfPartialPayment),
ter(tecPATH_DRY));
}

// Issuer fails trying to send more than the default maximum
Expand All @@ -823,7 +827,7 @@ class MPToken_test : public beast::unit_test::suite
mptAlice.pay(alice, bob, maxMPTokenAmount);

// issuer tries to exceed max amount
mptAlice.pay(alice, bob, 1, tecMPT_MAX_AMOUNT_EXCEEDED);
mptAlice.pay(alice, bob, 1, tecPATH_PARTIAL);
}

// Can't pay negative amount
Expand Down Expand Up @@ -878,23 +882,24 @@ class MPToken_test : public beast::unit_test::suite
// Payment between the holder and the issuer, no transfer fee.
mptAlice.pay(alice, bob, 1'000);

// Payment between the holders. The sender doesn't have
// enough funds to cover the transfer fee.
mptAlice.pay(bob, carol, 1'000);
// Payment between the holders. The sender has to include sendmax
// to cover the transfer fee.
auto const MPTA = mptAlice["MPTA"];
env(pay(bob, carol, MPTA(1'000)), ter(tecPATH_PARTIAL));
env(pay(bob, carol, MPTA(1'000)), sendmax(MPTA(1'100)));

// Payment between the holders. The sender pays 10% transfer fee.
mptAlice.pay(bob, carol, 100);
// Payment between the holders. The sender has to include sendmax
// to cover the transfer fee.
env(pay(bob, carol, MPTA(100)), ter(tecPATH_PARTIAL));
env(pay(bob, carol, MPTA(100)), sendmax(MPTA(110)));
}

// Test that non-issuer cannot send to each other if MPTCanTransfer
// isn't set
{
Env env(*this, features);
Account const alice{"alice"};
Account const bob{"bob"};
Account const cindy{"cindy"};

MPTTester mptAlice(env, alice, {.holders = {&bob, &cindy}});
MPTTester mptAlice(env, alice, {.holders = {&bob, &carol}});

// alice creates issuance without MPTCanTransfer
mptAlice.create({.ownerCount = 1, .holderCount = 0});
Expand All @@ -903,14 +908,14 @@ class MPToken_test : public beast::unit_test::suite
mptAlice.authorize({.account = &bob});

// cindy creates a MPToken
mptAlice.authorize({.account = &cindy});
mptAlice.authorize({.account = &carol});

// alice pays bob 100 tokens
mptAlice.pay(alice, bob, 100);

// bob tries to send cindy 10 tokens, but fails because canTransfer
// is off
mptAlice.pay(bob, cindy, 10, tecNO_AUTH);
mptAlice.pay(bob, carol, 10, tecNO_AUTH);

// bob can send back to alice(issuer) just fine
mptAlice.pay(bob, alice, 10);
Expand All @@ -919,8 +924,6 @@ class MPToken_test : public beast::unit_test::suite
// MPT is disabled
{
Env env{*this, features - featureMPTokensV1};
Account const alice("alice");
Account const bob("bob");

env.fund(XRP(1'000), alice);
env.fund(XRP(1'000), bob);
Expand All @@ -932,8 +935,6 @@ class MPToken_test : public beast::unit_test::suite
// MPT is disabled, unsigned request
{
Env env{*this, features - featureMPTokensV1};
Account const alice("alice"); // issuer
Account const carol("carol");
auto const USD = alice["USD"];

env.fund(XRP(1'000), alice);
Expand All @@ -951,30 +952,29 @@ class MPToken_test : public beast::unit_test::suite
// Invalid combination of send, sendMax, deliverMin
{
Env env{*this, features};
Account const alice("alice");
Account const carol("carol");

MPTTester mptAlice(env, alice, {.holders = {&carol}});

mptAlice.create({.ownerCount = 1, .holderCount = 0});

mptAlice.authorize({.account = &carol});

env.disableFeature(featureMPTokensV2);

// sendMax and DeliverMin are valid XRP amount,
// but is invalid combination with MPT amount
env(pay(alice, carol, mptAlice.mpt(100)),
auto const MPTA = mptAlice["MPTA"];
env(pay(alice, carol, MPTA(100)),
sendmax(XRP(100)),
ter(temMALFORMED));
env(pay(alice, carol, mptAlice.mpt(100)),
env(pay(alice, carol, MPTA(100)),
delivermin(XRP(100)),
ter(temMALFORMED));
}

// build_path is invalid if MPT
{
Env env{*this, features};
Account const alice("alice");
Account const carol("carol");
Env env{*this, features - featureMPTokensV2};

MPTTester mptAlice(env, alice, {.holders = {&bob, &carol}});

Expand Down Expand Up @@ -1007,7 +1007,7 @@ class MPToken_test : public beast::unit_test::suite
// alice destroys issuance
mptAlice.destroy({.ownerCount = 0});

// alice tries to send bob fund after issuance is destroy, should
// alice tries to send bob fund after issuance is destroyed, should
// fail.
mptAlice.pay(alice, bob, 100, tecMPT_ISSUANCE_NOT_FOUND);
}
Expand All @@ -1033,9 +1033,6 @@ class MPToken_test : public beast::unit_test::suite
// be able to transfer the max amount to someone else
{
Env env{*this, features};
Account const alice("alice");
Account const carol("bob");
Account const bob("carol");

MPTTester mptAlice(env, alice, {.holders = {&bob, &carol}});

Expand Down
11 changes: 11 additions & 0 deletions src/test/jtx/impl/mpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ MPTTester::MPTTester(Env& env, Account const& issuer, MPTConstr const& arg)
}
}

MPTTester::operator MPTIssue() const
{
return issuanceID();
}

MPT
MPTTester::operator[](std::string const& name) const
{
return MPT(name, issuanceID());
}

void
MPTTester::create(const MPTCreate& arg)
{
Expand Down
5 changes: 5 additions & 0 deletions src/test/jtx/mpt.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ class MPTTester
public:
MPTTester(Env& env, Account const& issuer, MPTConstr const& constr = {});

operator MPTIssue() const;

MPT
operator[](std::string const& name) const;

void
create(MPTCreate const& arg = MPTCreate{});

Expand Down
12 changes: 6 additions & 6 deletions src/xrpld/app/paths/Flow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ flow(
Asset const srcAsset = [&]() -> Asset {
if (sendMax)
return sendMax->asset();
if (isXRP(deliver))
return xrpIssue();
if (deliver.holds<Issue>())
return Issue(deliver.issue().currency, src);
if (deliver.holds<MPTIssue>())
return deliver.asset();
return xrpIssue();
return Issue(deliver.get<Issue>().currency, src);
return deliver.asset();
}();

Asset const dstAsset = deliver.asset();
Expand Down Expand Up @@ -134,12 +134,12 @@ flow(
std::variant<XRPAmount const*, MPTAmount const*, IOUAmount const*>;
auto getTypedAmt = [&](Asset const& iss) -> Var {
static auto xrp = XRPAmount{};
static auto cft = MPTAmount{};
static auto mpt = MPTAmount{};
static auto iou = IOUAmount{};
if (isXRP(iss))
return &xrp;
if (iss.holds<MPTIssue>())
return &cft;
return &mpt;
return &iou;
};

Expand Down
Loading

0 comments on commit af93589

Please sign in to comment.