mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Combine AMM Amendments (#521)
* fixAMMv1_2
* fixAMMv1_1
* fixAMMOverflowOffer
* fixLPTokenTransfer
* suppress AMM test logs
* exclude `ltAMM` from `fixPreviousTxnID` Amendment
- make `sfPreviousTxnID` and `sfPreviousTxnLgrSeq` required for ltAMM
This commit is contained in:
@@ -80,7 +80,7 @@ namespace detail {
|
|||||||
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
||||||
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
|
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
|
||||||
// the actual number of amendments. A LogicError on startup will verify this.
|
// the actual number of amendments. A LogicError on startup will verify this.
|
||||||
static constexpr std::size_t numFeatures = 107;
|
static constexpr std::size_t numFeatures = 103;
|
||||||
|
|
||||||
/** Amendments that this server supports and the default voting behavior.
|
/** Amendments that this server supports and the default voting behavior.
|
||||||
Whether they are enabled depends on the Rules defined in the validated
|
Whether they are enabled depends on the Rules defined in the validated
|
||||||
|
|||||||
@@ -29,13 +29,11 @@
|
|||||||
// If you add an amendment here, then do not forget to increment `numFeatures`
|
// If you add an amendment here, then do not forget to increment `numFeatures`
|
||||||
// in include/xrpl/protocol/Feature.h.
|
// in include/xrpl/protocol/Feature.h.
|
||||||
|
|
||||||
XRPL_FIX (FrozenLPTokenTransfer, Supported::yes, VoteBehavior::DefaultNo)
|
|
||||||
XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(PermissionedDomains, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(PermissionedDomains, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(DynamicNFT, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(DynamicNFT, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(Credentials, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(Credentials, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(AMMClawback, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(AMMClawback, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (AMMv1_2, Supported::yes, VoteBehavior::DefaultNo)
|
|
||||||
XRPL_FEATURE(MPTokensV1, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(MPTokensV1, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
// InvariantsV1_1 will be changes to Supported::yes when all the
|
// InvariantsV1_1 will be changes to Supported::yes when all the
|
||||||
// invariants expected to be included under it are complete.
|
// invariants expected to be included under it are complete.
|
||||||
@@ -44,10 +42,8 @@ XRPL_FIX (NFTokenPageLinks, Supported::yes, VoteBehavior::DefaultNo
|
|||||||
XRPL_FIX (EnforceNFTokenTrustline, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (EnforceNFTokenTrustline, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (ReducedOffersV2, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (ReducedOffersV2, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(NFTokenMintOffer, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(NFTokenMintOffer, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (AMMv1_1, Supported::yes, VoteBehavior::DefaultNo)
|
|
||||||
XRPL_FIX (PreviousTxnID, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (PreviousTxnID, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(PriceOracle, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(PriceOracle, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (AMMOverflowOffer, Supported::yes, VoteBehavior::DefaultYes)
|
|
||||||
XRPL_FIX (InnerObjTemplate, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (InnerObjTemplate, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (NFTokenReserve, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (NFTokenReserve, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (FillOrKill, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (FillOrKill, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
|
|||||||
@@ -482,8 +482,8 @@ LEDGER_ENTRY(ltAMM, 0x0079, AMM, amm, ({
|
|||||||
{sfAsset, soeREQUIRED},
|
{sfAsset, soeREQUIRED},
|
||||||
{sfAsset2, soeREQUIRED},
|
{sfAsset2, soeREQUIRED},
|
||||||
{sfOwnerNode, soeREQUIRED},
|
{sfOwnerNode, soeREQUIRED},
|
||||||
{sfPreviousTxnID, soeOPTIONAL},
|
{sfPreviousTxnID, soeREQUIRED},
|
||||||
{sfPreviousTxnLgrSeq, soeOPTIONAL},
|
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** A ledger object which tracks Oracle
|
/** A ledger object which tracks Oracle
|
||||||
|
|||||||
@@ -135,12 +135,11 @@ STLedgerEntry::getJson(JsonOptions options) const
|
|||||||
bool
|
bool
|
||||||
STLedgerEntry::isThreadedType(Rules const& rules) const
|
STLedgerEntry::isThreadedType(Rules const& rules) const
|
||||||
{
|
{
|
||||||
static constexpr std::array<LedgerEntryType, 8> newPreviousTxnIDTypes = {
|
static constexpr std::array<LedgerEntryType, 7> newPreviousTxnIDTypes = {
|
||||||
ltDIR_NODE,
|
ltDIR_NODE,
|
||||||
ltAMENDMENTS,
|
ltAMENDMENTS,
|
||||||
ltFEE_SETTINGS,
|
ltFEE_SETTINGS,
|
||||||
ltNEGATIVE_UNL,
|
ltNEGATIVE_UNL,
|
||||||
ltAMM,
|
|
||||||
ltHOOK_STATE,
|
ltHOOK_STATE,
|
||||||
ltHOOK_DEFINITION,
|
ltHOOK_DEFINITION,
|
||||||
ltIMPORT_VLSEQ,
|
ltIMPORT_VLSEQ,
|
||||||
|
|||||||
@@ -94,20 +94,10 @@ private:
|
|||||||
sendmax(BTC(1'000)),
|
sendmax(BTC(1'000)),
|
||||||
txflags(tfPartialPayment));
|
txflags(tfPartialPayment));
|
||||||
|
|
||||||
if (!features[fixAMMv1_1])
|
BEAST_EXPECT(ammCarol.expectBalances(
|
||||||
{
|
STAmount{BTC, UINT64_C(1'001'000000374815), -12},
|
||||||
BEAST_EXPECT(ammCarol.expectBalances(
|
USD(100'000),
|
||||||
STAmount{BTC, UINT64_C(1'001'000000374812), -12},
|
ammCarol.tokens()));
|
||||||
USD(100'000),
|
|
||||||
ammCarol.tokens()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BEAST_EXPECT(ammCarol.expectBalances(
|
|
||||||
STAmount{BTC, UINT64_C(1'001'000000374815), -12},
|
|
||||||
USD(100'000),
|
|
||||||
ammCarol.tokens()));
|
|
||||||
}
|
|
||||||
|
|
||||||
env.require(balance(bob, USD(200'100)));
|
env.require(balance(bob, USD(200'100)));
|
||||||
BEAST_EXPECT(isOffer(env, carol, BTC(49), XRP(49)));
|
BEAST_EXPECT(isOffer(env, carol, BTC(49), XRP(49)));
|
||||||
@@ -720,24 +710,12 @@ private:
|
|||||||
auto const jrr = env.rpc("json", "submit", to_string(payment));
|
auto const jrr = env.rpc("json", "submit", to_string(payment));
|
||||||
BEAST_EXPECT(jrr[jss::result][jss::status] == "success");
|
BEAST_EXPECT(jrr[jss::result][jss::status] == "success");
|
||||||
BEAST_EXPECT(jrr[jss::result][jss::engine_result] == "tesSUCCESS");
|
BEAST_EXPECT(jrr[jss::result][jss::engine_result] == "tesSUCCESS");
|
||||||
if (!features[fixAMMv1_1])
|
BEAST_EXPECT(ammAlice.expectBalances(
|
||||||
{
|
STAmount(XTS, UINT64_C(101'0101010101011), -13),
|
||||||
BEAST_EXPECT(ammAlice.expectBalances(
|
XXX(99),
|
||||||
STAmount(XTS, UINT64_C(101'010101010101), -12),
|
ammAlice.tokens()));
|
||||||
XXX(99),
|
BEAST_EXPECT(expectLine(
|
||||||
ammAlice.tokens()));
|
env, bob, STAmount{XTS, UINT64_C(98'9898989898989), -13}));
|
||||||
BEAST_EXPECT(expectLine(
|
|
||||||
env, bob, STAmount{XTS, UINT64_C(98'989898989899), -12}));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BEAST_EXPECT(ammAlice.expectBalances(
|
|
||||||
STAmount(XTS, UINT64_C(101'0101010101011), -13),
|
|
||||||
XXX(99),
|
|
||||||
ammAlice.tokens()));
|
|
||||||
BEAST_EXPECT(expectLine(
|
|
||||||
env, bob, STAmount{XTS, UINT64_C(98'9898989898989), -13}));
|
|
||||||
}
|
|
||||||
BEAST_EXPECT(expectLine(env, bob, XXX(101)));
|
BEAST_EXPECT(expectLine(env, bob, XXX(101)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1291,34 +1269,17 @@ private:
|
|||||||
env(offer(cam, B_BUX(30), A_BUX(30)));
|
env(offer(cam, B_BUX(30), A_BUX(30)));
|
||||||
|
|
||||||
// AMM is consumed up to the first cam Offer quality
|
// AMM is consumed up to the first cam Offer quality
|
||||||
if (!features[fixAMMv1_1])
|
BEAST_EXPECT(ammCarol.expectBalances(
|
||||||
{
|
STAmount{A_BUX, UINT64_C(309'3541659651604), -13},
|
||||||
BEAST_EXPECT(ammCarol.expectBalances(
|
STAmount{B_BUX, UINT64_C(320'0215509984419), -13},
|
||||||
STAmount{A_BUX, UINT64_C(309'3541659651605), -13},
|
ammCarol.tokens()));
|
||||||
STAmount{B_BUX, UINT64_C(320'0215509984417), -13},
|
BEAST_EXPECT(expectOffers(
|
||||||
ammCarol.tokens()));
|
env,
|
||||||
BEAST_EXPECT(expectOffers(
|
cam,
|
||||||
env,
|
1,
|
||||||
cam,
|
{{Amounts{
|
||||||
1,
|
STAmount{B_BUX, UINT64_C(20'0215509984419), -13},
|
||||||
{{Amounts{
|
STAmount{A_BUX, UINT64_C(20'0215509984419), -13}}}}));
|
||||||
STAmount{B_BUX, UINT64_C(20'0215509984417), -13},
|
|
||||||
STAmount{A_BUX, UINT64_C(20'0215509984417), -13}}}}));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BEAST_EXPECT(ammCarol.expectBalances(
|
|
||||||
STAmount{A_BUX, UINT64_C(309'3541659651604), -13},
|
|
||||||
STAmount{B_BUX, UINT64_C(320'0215509984419), -13},
|
|
||||||
ammCarol.tokens()));
|
|
||||||
BEAST_EXPECT(expectOffers(
|
|
||||||
env,
|
|
||||||
cam,
|
|
||||||
1,
|
|
||||||
{{Amounts{
|
|
||||||
STAmount{B_BUX, UINT64_C(20'0215509984419), -13},
|
|
||||||
STAmount{A_BUX, UINT64_C(20'0215509984419), -13}}}}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1444,7 +1405,6 @@ private:
|
|||||||
using namespace jtx;
|
using namespace jtx;
|
||||||
FeatureBitset const all{supported_amendments()};
|
FeatureBitset const all{supported_amendments()};
|
||||||
testRmFundedOffer(all);
|
testRmFundedOffer(all);
|
||||||
testRmFundedOffer(all - fixAMMv1_1);
|
|
||||||
testEnforceNoRipple(all);
|
testEnforceNoRipple(all);
|
||||||
testFillModes(all);
|
testFillModes(all);
|
||||||
testOfferCrossWithXRP(all);
|
testOfferCrossWithXRP(all);
|
||||||
@@ -1458,7 +1418,6 @@ private:
|
|||||||
testOfferCreateThenCross(all);
|
testOfferCreateThenCross(all);
|
||||||
testSellFlagExceedLimit(all);
|
testSellFlagExceedLimit(all);
|
||||||
testGatewayCrossCurrency(all);
|
testGatewayCrossCurrency(all);
|
||||||
testGatewayCrossCurrency(all - fixAMMv1_1);
|
|
||||||
testBridgedCross(all);
|
testBridgedCross(all);
|
||||||
testSellWithFillOrKill(all);
|
testSellWithFillOrKill(all);
|
||||||
testTransferRateOffer(all);
|
testTransferRateOffer(all);
|
||||||
@@ -1466,7 +1425,6 @@ private:
|
|||||||
testBadPathAssert(all);
|
testBadPathAssert(all);
|
||||||
testSellFlagBasic(all);
|
testSellFlagBasic(all);
|
||||||
testDirectToDirectPath(all);
|
testDirectToDirectPath(all);
|
||||||
testDirectToDirectPath(all - fixAMMv1_1);
|
|
||||||
testRequireAuth(all);
|
testRequireAuth(all);
|
||||||
testMissingAuth(all);
|
testMissingAuth(all);
|
||||||
}
|
}
|
||||||
@@ -2326,36 +2284,16 @@ private:
|
|||||||
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
if (!features[fixAMMv1_1])
|
// alice buys 77.2727USD with 75.5555GBP and pays 25% tr fee
|
||||||
{
|
// on 75.5555GBP
|
||||||
// alice buys 77.2727USD with 75.5555GBP and pays 25% tr fee
|
// 1,200 - 75.55555*1.25 = 1200 - 94.4444 = 1105.55555GBP
|
||||||
// on 75.5555GBP
|
BEAST_EXPECT(expectLine(
|
||||||
// 1,200 - 75.55555*1.25 = 1200 - 94.4444 = 1105.55555GBP
|
env, alice, STAmount{GBP, UINT64_C(1'105'555555555554), -12}));
|
||||||
BEAST_EXPECT(expectLine(
|
// 75.5555GBP is swapped in for 77.7272USD
|
||||||
env,
|
BEAST_EXPECT(amm.expectBalances(
|
||||||
alice,
|
STAmount{GBP, UINT64_C(1'075'555555555557), -12},
|
||||||
STAmount{GBP, UINT64_C(1'105'555555555555), -12}));
|
STAmount{USD, UINT64_C(1'022'727272727272), -12},
|
||||||
// 75.5555GBP is swapped in for 77.7272USD
|
amm.tokens()));
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
|
||||||
STAmount{GBP, UINT64_C(1'075'555555555556), -12},
|
|
||||||
STAmount{USD, UINT64_C(1'022'727272727272), -12},
|
|
||||||
amm.tokens()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// alice buys 77.2727USD with 75.5555GBP and pays 25% tr fee
|
|
||||||
// on 75.5555GBP
|
|
||||||
// 1,200 - 75.55555*1.25 = 1200 - 94.4444 = 1105.55555GBP
|
|
||||||
BEAST_EXPECT(expectLine(
|
|
||||||
env,
|
|
||||||
alice,
|
|
||||||
STAmount{GBP, UINT64_C(1'105'555555555554), -12}));
|
|
||||||
// 75.5555GBP is swapped in for 77.7272USD
|
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
|
||||||
STAmount{GBP, UINT64_C(1'075'555555555557), -12},
|
|
||||||
STAmount{USD, UINT64_C(1'022'727272727272), -12},
|
|
||||||
amm.tokens()));
|
|
||||||
}
|
|
||||||
BEAST_EXPECT(expectLine(
|
BEAST_EXPECT(expectLine(
|
||||||
env, carol, STAmount{USD, UINT64_C(1'277'272727272728), -12}));
|
env, carol, STAmount{USD, UINT64_C(1'277'272727272728), -12}));
|
||||||
}
|
}
|
||||||
@@ -2373,36 +2311,18 @@ private:
|
|||||||
env(offer(alice, EUR(100), USD(100)));
|
env(offer(alice, EUR(100), USD(100)));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
if (!features[fixAMMv1_1])
|
// 95.2380USD is swapped in for 100EUR
|
||||||
{
|
BEAST_EXPECT(amm.expectBalances(
|
||||||
// 95.2380USD is swapped in for 100EUR
|
STAmount{USD, UINT64_C(1'095'238095238096), -12},
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
EUR(1'050),
|
||||||
STAmount{USD, UINT64_C(1'095'238095238095), -12},
|
amm.tokens()));
|
||||||
EUR(1'050),
|
// alice pays 25% tr fee on 95.2380USD
|
||||||
amm.tokens()));
|
// 1200-95.2380*1.25 = 1200 - 119.0477 = 1080.9523USD
|
||||||
// alice pays 25% tr fee on 95.2380USD
|
BEAST_EXPECT(expectLine(
|
||||||
// 1200-95.2380*1.25 = 1200 - 119.0477 = 1080.9523USD
|
env,
|
||||||
BEAST_EXPECT(expectLine(
|
alice,
|
||||||
env,
|
STAmount{USD, UINT64_C(1'080'95238095238), -11},
|
||||||
alice,
|
EUR(1'300)));
|
||||||
STAmount{USD, UINT64_C(1'080'952380952381), -12},
|
|
||||||
EUR(1'300)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 95.2380USD is swapped in for 100EUR
|
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
|
||||||
STAmount{USD, UINT64_C(1'095'238095238096), -12},
|
|
||||||
EUR(1'050),
|
|
||||||
amm.tokens()));
|
|
||||||
// alice pays 25% tr fee on 95.2380USD
|
|
||||||
// 1200-95.2380*1.25 = 1200 - 119.0477 = 1080.9523USD
|
|
||||||
BEAST_EXPECT(expectLine(
|
|
||||||
env,
|
|
||||||
alice,
|
|
||||||
STAmount{USD, UINT64_C(1'080'95238095238), -11},
|
|
||||||
EUR(1'300)));
|
|
||||||
}
|
|
||||||
BEAST_EXPECT(expectOffers(env, alice, 0));
|
BEAST_EXPECT(expectOffers(env, alice, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2426,42 +2346,23 @@ private:
|
|||||||
env(pay(gw, dan, USD(1'000)));
|
env(pay(gw, dan, USD(1'000)));
|
||||||
AMM ammDan(env, dan, USD(1'000), EUR(1'050));
|
AMM ammDan(env, dan, USD(1'000), EUR(1'050));
|
||||||
|
|
||||||
if (!features[fixAMMv1_1])
|
// alice -> bob -> gw -> carol. $50 should have transfer fee;
|
||||||
{
|
// $10, no fee
|
||||||
// alice -> bob -> gw -> carol. $50 should have transfer fee;
|
env(pay(alice, carol, EUR(50)),
|
||||||
// $10, no fee
|
path(bob, gw, ~EUR),
|
||||||
env(pay(alice, carol, EUR(50)),
|
sendmax(USDA(60.1)),
|
||||||
path(bob, gw, ~EUR),
|
txflags(tfNoRippleDirect));
|
||||||
sendmax(USDA(60)),
|
BEAST_EXPECT(ammDan.expectBalances(
|
||||||
txflags(tfNoRippleDirect));
|
STAmount{USD, UINT64_C(1'050'000000000001), -12},
|
||||||
BEAST_EXPECT(ammDan.expectBalances(
|
EUR(1'000),
|
||||||
USD(1'050), EUR(1'000), ammDan.tokens()));
|
ammDan.tokens()));
|
||||||
BEAST_EXPECT(expectLine(env, dan, USD(0)));
|
BEAST_EXPECT(expectLine(env, dan, USD(0)));
|
||||||
BEAST_EXPECT(expectLine(env, dan, EUR(0)));
|
BEAST_EXPECT(expectLine(env, dan, EUR(0)));
|
||||||
BEAST_EXPECT(expectLine(env, bob, USD(-10)));
|
BEAST_EXPECT(expectLine(
|
||||||
BEAST_EXPECT(expectLine(env, bob, USDA(60)));
|
env, bob, STAmount{USD, INT64_C(-10'000000000001), -12}));
|
||||||
BEAST_EXPECT(expectLine(env, carol, EUR(50)));
|
BEAST_EXPECT(expectLine(
|
||||||
}
|
env, bob, STAmount{USDA, UINT64_C(60'000000000001), -12}));
|
||||||
else
|
BEAST_EXPECT(expectLine(env, carol, EUR(50)));
|
||||||
{
|
|
||||||
// alice -> bob -> gw -> carol. $50 should have transfer fee;
|
|
||||||
// $10, no fee
|
|
||||||
env(pay(alice, carol, EUR(50)),
|
|
||||||
path(bob, gw, ~EUR),
|
|
||||||
sendmax(USDA(60.1)),
|
|
||||||
txflags(tfNoRippleDirect));
|
|
||||||
BEAST_EXPECT(ammDan.expectBalances(
|
|
||||||
STAmount{USD, UINT64_C(1'050'000000000001), -12},
|
|
||||||
EUR(1'000),
|
|
||||||
ammDan.tokens()));
|
|
||||||
BEAST_EXPECT(expectLine(env, dan, USD(0)));
|
|
||||||
BEAST_EXPECT(expectLine(env, dan, EUR(0)));
|
|
||||||
BEAST_EXPECT(expectLine(
|
|
||||||
env, bob, STAmount{USD, INT64_C(-10'000000000001), -12}));
|
|
||||||
BEAST_EXPECT(expectLine(
|
|
||||||
env, bob, STAmount{USDA, UINT64_C(60'000000000001), -12}));
|
|
||||||
BEAST_EXPECT(expectLine(env, carol, EUR(50)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2495,21 +2396,11 @@ private:
|
|||||||
// alice buys 107.1428USD with 120GBP and pays 25% tr fee on 120GBP
|
// alice buys 107.1428USD with 120GBP and pays 25% tr fee on 120GBP
|
||||||
// 1,000 - 120*1.25 = 850GBP
|
// 1,000 - 120*1.25 = 850GBP
|
||||||
BEAST_EXPECT(expectLine(env, alice, GBP(850)));
|
BEAST_EXPECT(expectLine(env, alice, GBP(850)));
|
||||||
if (!features[fixAMMv1_1])
|
BEAST_EXPECT(amm.expectBalances(
|
||||||
{
|
GBP(1'120),
|
||||||
// 120GBP is swapped in for 107.1428USD
|
STAmount{USD, UINT64_C(892'8571428571429), -13},
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
amm.tokens()));
|
||||||
GBP(1'120),
|
|
||||||
STAmount{USD, UINT64_C(892'8571428571428), -13},
|
|
||||||
amm.tokens()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
|
||||||
GBP(1'120),
|
|
||||||
STAmount{USD, UINT64_C(892'8571428571429), -13},
|
|
||||||
amm.tokens()));
|
|
||||||
}
|
|
||||||
// 25% of 85.7142USD is paid in tr fee
|
// 25% of 85.7142USD is paid in tr fee
|
||||||
// 85.7142*1.25 = 107.1428USD
|
// 85.7142*1.25 = 107.1428USD
|
||||||
BEAST_EXPECT(expectLine(
|
BEAST_EXPECT(expectLine(
|
||||||
@@ -2584,38 +2475,20 @@ private:
|
|||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
BEAST_EXPECT(expectLine(env, alice, GBP(850)));
|
BEAST_EXPECT(expectLine(env, alice, GBP(850)));
|
||||||
if (!features[fixAMMv1_1])
|
// alice buys 107.1428EUR with 120GBP and pays 25% tr fee on
|
||||||
{
|
// 120GBP 1,000 - 120*1.25 = 850GBP 120GBP is swapped in for
|
||||||
// alice buys 107.1428EUR with 120GBP and pays 25% tr fee on
|
// 107.1428EUR
|
||||||
// 120GBP 1,000 - 120*1.25 = 850GBP 120GBP is swapped in for
|
BEAST_EXPECT(amm1.expectBalances(
|
||||||
// 107.1428EUR
|
GBP(1'120),
|
||||||
BEAST_EXPECT(amm1.expectBalances(
|
STAmount{EUR, UINT64_C(892'8571428571429), -13},
|
||||||
GBP(1'120),
|
amm1.tokens()));
|
||||||
STAmount{EUR, UINT64_C(892'8571428571428), -13},
|
// 25% on 85.7142EUR is paid in tr fee 85.7142*1.25 =
|
||||||
amm1.tokens()));
|
// 107.1428EUR 85.7142EUR is swapped in for 78.9473USD
|
||||||
// 25% on 85.7142EUR is paid in tr fee 85.7142*1.25 =
|
BEAST_EXPECT(amm2.expectBalances(
|
||||||
// 107.1428EUR 85.7142EUR is swapped in for 78.9473USD
|
STAmount(EUR, UINT64_C(1'085'714285714286), -12),
|
||||||
BEAST_EXPECT(amm2.expectBalances(
|
STAmount{USD, UINT64_C(921'052631578948), -12},
|
||||||
STAmount(EUR, UINT64_C(1'085'714285714286), -12),
|
amm2.tokens()));
|
||||||
STAmount{USD, UINT64_C(921'0526315789471), -13},
|
|
||||||
amm2.tokens()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// alice buys 107.1428EUR with 120GBP and pays 25% tr fee on
|
|
||||||
// 120GBP 1,000 - 120*1.25 = 850GBP 120GBP is swapped in for
|
|
||||||
// 107.1428EUR
|
|
||||||
BEAST_EXPECT(amm1.expectBalances(
|
|
||||||
GBP(1'120),
|
|
||||||
STAmount{EUR, UINT64_C(892'8571428571429), -13},
|
|
||||||
amm1.tokens()));
|
|
||||||
// 25% on 85.7142EUR is paid in tr fee 85.7142*1.25 =
|
|
||||||
// 107.1428EUR 85.7142EUR is swapped in for 78.9473USD
|
|
||||||
BEAST_EXPECT(amm2.expectBalances(
|
|
||||||
STAmount(EUR, UINT64_C(1'085'714285714286), -12),
|
|
||||||
STAmount{USD, UINT64_C(921'052631578948), -12},
|
|
||||||
amm2.tokens()));
|
|
||||||
}
|
|
||||||
// 25% on 63.1578USD is paid in tr fee 63.1578*1.25 = 78.9473USD
|
// 25% on 63.1578USD is paid in tr fee 63.1578*1.25 = 78.9473USD
|
||||||
BEAST_EXPECT(expectLine(
|
BEAST_EXPECT(expectLine(
|
||||||
env, carol, STAmount(USD, UINT64_C(1'063'157894736842), -12)));
|
env, carol, STAmount(USD, UINT64_C(1'063'157894736842), -12)));
|
||||||
@@ -2701,31 +2574,17 @@ private:
|
|||||||
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
if (!features[fixAMMv1_1])
|
// alice buys 28.125USD with 24GBP and pays 25% tr fee
|
||||||
{
|
// on 24GBP
|
||||||
// alice buys 28.125USD with 24GBP and pays 25% tr fee
|
// 1,200 - 24*1.25 =~ 1,170GBP
|
||||||
// on 24GBP
|
BEAST_EXPECT(expectLine(
|
||||||
// 1,200 - 24*1.25 = 1,170GBP
|
env, alice, STAmount{GBP, UINT64_C(1'169'999999999999), -12}));
|
||||||
BEAST_EXPECT(expectLine(env, alice, GBP(1'170)));
|
// 24GBP is swapped in for 28.125USD
|
||||||
// 24GBP is swapped in for 28.125USD
|
BEAST_EXPECT(amm.expectBalances(
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
STAmount{GBP, UINT64_C(1'024'000000000001), -12},
|
||||||
GBP(1'024), USD(1'171.875), amm.tokens()));
|
USD(1'171.875),
|
||||||
}
|
amm.tokens()));
|
||||||
else
|
|
||||||
{
|
|
||||||
// alice buys 28.125USD with 24GBP and pays 25% tr fee
|
|
||||||
// on 24GBP
|
|
||||||
// 1,200 - 24*1.25 =~ 1,170GBP
|
|
||||||
BEAST_EXPECT(expectLine(
|
|
||||||
env,
|
|
||||||
alice,
|
|
||||||
STAmount{GBP, UINT64_C(1'169'999999999999), -12}));
|
|
||||||
// 24GBP is swapped in for 28.125USD
|
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
|
||||||
STAmount{GBP, UINT64_C(1'024'000000000001), -12},
|
|
||||||
USD(1'171.875),
|
|
||||||
amm.tokens()));
|
|
||||||
}
|
|
||||||
// 25% on 22.5USD is paid in tr fee
|
// 25% on 22.5USD is paid in tr fee
|
||||||
// 22.5*1.25 = 28.125USD
|
// 22.5*1.25 = 28.125USD
|
||||||
BEAST_EXPECT(expectLine(env, carol, USD(1'222.5)));
|
BEAST_EXPECT(expectLine(env, carol, USD(1'222.5)));
|
||||||
@@ -2758,66 +2617,32 @@ private:
|
|||||||
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
if (!features[fixAMMv1_1])
|
// alice buys 70.4210EUR with 70.4210GBP via the offer
|
||||||
{
|
// and pays 25% tr fee on 70.4210GBP
|
||||||
// alice buys 70.4210EUR with 70.4210GBP via the offer
|
// 1,400 - 70.4210*1.25 = 1400 - 88.0262 = 1311.9736GBP
|
||||||
// and pays 25% tr fee on 70.4210GBP
|
BEAST_EXPECT(expectLine(
|
||||||
// 1,400 - 70.4210*1.25 = 1400 - 88.0262 = 1311.9736GBP
|
env, alice, STAmount{GBP, UINT64_C(1'311'973684210525), -12}));
|
||||||
BEAST_EXPECT(expectLine(
|
// ed doesn't pay tr fee, the balances reflect consumed offer
|
||||||
env,
|
// 70.4210GBP/70.4210EUR
|
||||||
alice,
|
BEAST_EXPECT(expectLine(
|
||||||
STAmount{GBP, UINT64_C(1'311'973684210527), -12}));
|
env,
|
||||||
// ed doesn't pay tr fee, the balances reflect consumed offer
|
ed,
|
||||||
// 70.4210GBP/70.4210EUR
|
STAmount{EUR, UINT64_C(1'329'57894736842), -11},
|
||||||
BEAST_EXPECT(expectLine(
|
STAmount{GBP, UINT64_C(1'470'42105263158), -11}));
|
||||||
env,
|
BEAST_EXPECT(expectOffers(
|
||||||
ed,
|
env,
|
||||||
STAmount{EUR, UINT64_C(1'329'578947368421), -12},
|
ed,
|
||||||
STAmount{GBP, UINT64_C(1'470'421052631579), -12}));
|
1,
|
||||||
BEAST_EXPECT(expectOffers(
|
{Amounts{
|
||||||
env,
|
STAmount{GBP, UINT64_C(929'57894736842), -11},
|
||||||
ed,
|
STAmount{EUR, UINT64_C(929'57894736842), -11}}}));
|
||||||
1,
|
// 25% on 56.3368EUR is paid in tr fee 56.3368*1.25 = 70.4210EUR
|
||||||
{Amounts{
|
// 56.3368EUR is swapped in for 74.6651USD
|
||||||
STAmount{GBP, UINT64_C(929'5789473684212), -13},
|
BEAST_EXPECT(amm.expectBalances(
|
||||||
STAmount{EUR, UINT64_C(929'5789473684212), -13}}}));
|
STAmount{EUR, UINT64_C(1'056'336842105264), -12},
|
||||||
// 25% on 56.3368EUR is paid in tr fee 56.3368*1.25 = 70.4210EUR
|
STAmount{USD, UINT64_C(1'325'334821428571), -12},
|
||||||
// 56.3368EUR is swapped in for 74.6651USD
|
amm.tokens()));
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
|
||||||
STAmount{EUR, UINT64_C(1'056'336842105263), -12},
|
|
||||||
STAmount{USD, UINT64_C(1'325'334821428571), -12},
|
|
||||||
amm.tokens()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// alice buys 70.4210EUR with 70.4210GBP via the offer
|
|
||||||
// and pays 25% tr fee on 70.4210GBP
|
|
||||||
// 1,400 - 70.4210*1.25 = 1400 - 88.0262 = 1311.9736GBP
|
|
||||||
BEAST_EXPECT(expectLine(
|
|
||||||
env,
|
|
||||||
alice,
|
|
||||||
STAmount{GBP, UINT64_C(1'311'973684210525), -12}));
|
|
||||||
// ed doesn't pay tr fee, the balances reflect consumed offer
|
|
||||||
// 70.4210GBP/70.4210EUR
|
|
||||||
BEAST_EXPECT(expectLine(
|
|
||||||
env,
|
|
||||||
ed,
|
|
||||||
STAmount{EUR, UINT64_C(1'329'57894736842), -11},
|
|
||||||
STAmount{GBP, UINT64_C(1'470'42105263158), -11}));
|
|
||||||
BEAST_EXPECT(expectOffers(
|
|
||||||
env,
|
|
||||||
ed,
|
|
||||||
1,
|
|
||||||
{Amounts{
|
|
||||||
STAmount{GBP, UINT64_C(929'57894736842), -11},
|
|
||||||
STAmount{EUR, UINT64_C(929'57894736842), -11}}}));
|
|
||||||
// 25% on 56.3368EUR is paid in tr fee 56.3368*1.25 = 70.4210EUR
|
|
||||||
// 56.3368EUR is swapped in for 74.6651USD
|
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
|
||||||
STAmount{EUR, UINT64_C(1'056'336842105264), -12},
|
|
||||||
STAmount{USD, UINT64_C(1'325'334821428571), -12},
|
|
||||||
amm.tokens()));
|
|
||||||
}
|
|
||||||
// 25% on 59.7321USD is paid in tr fee 59.7321*1.25 = 74.6651USD
|
// 25% on 59.7321USD is paid in tr fee 59.7321*1.25 = 74.6651USD
|
||||||
BEAST_EXPECT(expectLine(
|
BEAST_EXPECT(expectLine(
|
||||||
env, carol, STAmount(USD, UINT64_C(1'459'732142857143), -12)));
|
env, carol, STAmount(USD, UINT64_C(1'459'732142857143), -12)));
|
||||||
@@ -2850,40 +2675,19 @@ private:
|
|||||||
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
if (!features[fixAMMv1_1])
|
// alice buys 53.3322EUR with 56.3368GBP via the amm
|
||||||
{
|
// and pays 25% tr fee on 56.3368GBP
|
||||||
// alice buys 53.3322EUR with 56.3368GBP via the amm
|
// 1,400 - 56.3368*1.25 = 1400 - 70.4210 = 1329.5789GBP
|
||||||
// and pays 25% tr fee on 56.3368GBP
|
BEAST_EXPECT(expectLine(
|
||||||
// 1,400 - 56.3368*1.25 = 1400 - 70.4210 = 1329.5789GBP
|
env, alice, STAmount{GBP, UINT64_C(1'329'57894736842), -11}));
|
||||||
BEAST_EXPECT(expectLine(
|
//// 25% on 56.3368EUR is paid in tr fee 56.3368*1.25
|
||||||
env,
|
///= 70.4210EUR
|
||||||
alice,
|
// 56.3368GBP is swapped in for 53.3322EUR
|
||||||
STAmount{GBP, UINT64_C(1'329'578947368421), -12}));
|
BEAST_EXPECT(amm.expectBalances(
|
||||||
//// 25% on 56.3368EUR is paid in tr fee 56.3368*1.25
|
STAmount{GBP, UINT64_C(1'056'336842105264), -12},
|
||||||
///= 70.4210EUR
|
STAmount{EUR, UINT64_C(946'6677295918366), -13},
|
||||||
// 56.3368GBP is swapped in for 53.3322EUR
|
amm.tokens()));
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
|
||||||
STAmount{GBP, UINT64_C(1'056'336842105263), -12},
|
|
||||||
STAmount{EUR, UINT64_C(946'6677295918366), -13},
|
|
||||||
amm.tokens()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// alice buys 53.3322EUR with 56.3368GBP via the amm
|
|
||||||
// and pays 25% tr fee on 56.3368GBP
|
|
||||||
// 1,400 - 56.3368*1.25 = 1400 - 70.4210 = 1329.5789GBP
|
|
||||||
BEAST_EXPECT(expectLine(
|
|
||||||
env,
|
|
||||||
alice,
|
|
||||||
STAmount{GBP, UINT64_C(1'329'57894736842), -11}));
|
|
||||||
//// 25% on 56.3368EUR is paid in tr fee 56.3368*1.25
|
|
||||||
///= 70.4210EUR
|
|
||||||
// 56.3368GBP is swapped in for 53.3322EUR
|
|
||||||
BEAST_EXPECT(amm.expectBalances(
|
|
||||||
STAmount{GBP, UINT64_C(1'056'336842105264), -12},
|
|
||||||
STAmount{EUR, UINT64_C(946'6677295918366), -13},
|
|
||||||
amm.tokens()));
|
|
||||||
}
|
|
||||||
// 25% on 42.6658EUR is paid in tr fee 42.6658*1.25 = 53.3322EUR
|
// 25% on 42.6658EUR is paid in tr fee 42.6658*1.25 = 53.3322EUR
|
||||||
// 42.6658EUR/59.7321USD
|
// 42.6658EUR/59.7321USD
|
||||||
BEAST_EXPECT(expectLine(
|
BEAST_EXPECT(expectLine(
|
||||||
@@ -2928,48 +2732,23 @@ private:
|
|||||||
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
if (!features[fixAMMv1_1])
|
// alice buys 53.3322EUR with 107.5308GBP
|
||||||
{
|
// 25% on 86.0246GBP is paid in tr fee
|
||||||
// alice buys 53.3322EUR with 107.5308GBP
|
// 1,400 - 86.0246*1.25 = 1400 - 107.5308 = 1229.4691GBP
|
||||||
// 25% on 86.0246GBP is paid in tr fee
|
BEAST_EXPECT(expectLine(
|
||||||
// 1,400 - 86.0246*1.25 = 1400 - 107.5308 = 1229.4691GBP
|
env, alice, STAmount{GBP, UINT64_C(1'292'469135802466), -12}));
|
||||||
BEAST_EXPECT(expectLine(
|
// 86.0246GBP is swapped in for 79.2106EUR
|
||||||
env,
|
BEAST_EXPECT(amm1.expectBalances(
|
||||||
alice,
|
STAmount{GBP, UINT64_C(1'086'024691358027), -12},
|
||||||
STAmount{GBP, UINT64_C(1'292'469135802469), -12}));
|
STAmount{EUR, UINT64_C(920'7893779556188), -13},
|
||||||
// 86.0246GBP is swapped in for 79.2106EUR
|
amm1.tokens()));
|
||||||
BEAST_EXPECT(amm1.expectBalances(
|
// 25% on 63.3684EUR is paid in tr fee 63.3684*1.25 = 79.2106EUR
|
||||||
STAmount{GBP, UINT64_C(1'086'024691358025), -12},
|
// 63.3684EUR is swapped in for 83.4291USD
|
||||||
STAmount{EUR, UINT64_C(920'78937795562), -11},
|
BEAST_EXPECT(amm2.expectBalances(
|
||||||
amm1.tokens()));
|
STAmount{EUR, UINT64_C(1'063'368497635505), -12},
|
||||||
// 25% on 63.3684EUR is paid in tr fee 63.3684*1.25 = 79.2106EUR
|
STAmount{USD, UINT64_C(1'316'570881226053), -12},
|
||||||
// 63.3684EUR is swapped in for 83.4291USD
|
amm2.tokens()));
|
||||||
BEAST_EXPECT(amm2.expectBalances(
|
|
||||||
STAmount{EUR, UINT64_C(1'063'368497635504), -12},
|
|
||||||
STAmount{USD, UINT64_C(1'316'570881226053), -12},
|
|
||||||
amm2.tokens()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// alice buys 53.3322EUR with 107.5308GBP
|
|
||||||
// 25% on 86.0246GBP is paid in tr fee
|
|
||||||
// 1,400 - 86.0246*1.25 = 1400 - 107.5308 = 1229.4691GBP
|
|
||||||
BEAST_EXPECT(expectLine(
|
|
||||||
env,
|
|
||||||
alice,
|
|
||||||
STAmount{GBP, UINT64_C(1'292'469135802466), -12}));
|
|
||||||
// 86.0246GBP is swapped in for 79.2106EUR
|
|
||||||
BEAST_EXPECT(amm1.expectBalances(
|
|
||||||
STAmount{GBP, UINT64_C(1'086'024691358027), -12},
|
|
||||||
STAmount{EUR, UINT64_C(920'7893779556188), -13},
|
|
||||||
amm1.tokens()));
|
|
||||||
// 25% on 63.3684EUR is paid in tr fee 63.3684*1.25 = 79.2106EUR
|
|
||||||
// 63.3684EUR is swapped in for 83.4291USD
|
|
||||||
BEAST_EXPECT(amm2.expectBalances(
|
|
||||||
STAmount{EUR, UINT64_C(1'063'368497635505), -12},
|
|
||||||
STAmount{USD, UINT64_C(1'316'570881226053), -12},
|
|
||||||
amm2.tokens()));
|
|
||||||
}
|
|
||||||
// 25% on 66.7432USD is paid in tr fee 66.7432*1.25 = 83.4291USD
|
// 25% on 66.7432USD is paid in tr fee 66.7432*1.25 = 83.4291USD
|
||||||
BEAST_EXPECT(expectLine(
|
BEAST_EXPECT(expectLine(
|
||||||
env, carol, STAmount(USD, UINT64_C(1'466'743295019157), -12)));
|
env, carol, STAmount(USD, UINT64_C(1'466'743295019157), -12)));
|
||||||
@@ -2999,34 +2778,18 @@ private:
|
|||||||
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
if (!features[fixAMMv1_1])
|
// 108.1481GBP is swapped in for 97.5935EUR
|
||||||
{
|
BEAST_EXPECT(amm1.expectBalances(
|
||||||
// 108.1481GBP is swapped in for 97.5935EUR
|
STAmount{GBP, UINT64_C(1'108'148148148151), -12},
|
||||||
BEAST_EXPECT(amm1.expectBalances(
|
STAmount{EUR, UINT64_C(902'4064171122975), -13},
|
||||||
STAmount{GBP, UINT64_C(1'108'148148148149), -12},
|
amm1.tokens()));
|
||||||
STAmount{EUR, UINT64_C(902'4064171122988), -13},
|
// 25% on 78.0748EUR is paid in tr fee 78.0748*1.25 = 97.5935EUR
|
||||||
amm1.tokens()));
|
// 78.0748EUR is swapped in for 101.3888USD
|
||||||
// 25% on 78.0748EUR is paid in tr fee 78.0748*1.25 = 97.5935EUR
|
BEAST_EXPECT(amm2.expectBalances(
|
||||||
// 78.0748EUR is swapped in for 101.3888USD
|
STAmount{EUR, UINT64_C(1'078'074866310162), -12},
|
||||||
BEAST_EXPECT(amm2.expectBalances(
|
STAmount{USD, UINT64_C(1'298'611111111111), -12},
|
||||||
STAmount{EUR, UINT64_C(1'078'074866310161), -12},
|
amm2.tokens()));
|
||||||
STAmount{USD, UINT64_C(1'298'611111111111), -12},
|
|
||||||
amm2.tokens()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 108.1481GBP is swapped in for 97.5935EUR
|
|
||||||
BEAST_EXPECT(amm1.expectBalances(
|
|
||||||
STAmount{GBP, UINT64_C(1'108'148148148151), -12},
|
|
||||||
STAmount{EUR, UINT64_C(902'4064171122975), -13},
|
|
||||||
amm1.tokens()));
|
|
||||||
// 25% on 78.0748EUR is paid in tr fee 78.0748*1.25 = 97.5935EUR
|
|
||||||
// 78.0748EUR is swapped in for 101.3888USD
|
|
||||||
BEAST_EXPECT(amm2.expectBalances(
|
|
||||||
STAmount{EUR, UINT64_C(1'078'074866310162), -12},
|
|
||||||
STAmount{USD, UINT64_C(1'298'611111111111), -12},
|
|
||||||
amm2.tokens()));
|
|
||||||
}
|
|
||||||
// 25% on 81.1111USD is paid in tr fee 81.1111*1.25 = 101.3888USD
|
// 25% on 81.1111USD is paid in tr fee 81.1111*1.25 = 101.3888USD
|
||||||
BEAST_EXPECT(expectLine(
|
BEAST_EXPECT(expectLine(
|
||||||
env, carol, STAmount{USD, UINT64_C(1'481'111111111111), -12}));
|
env, carol, STAmount{USD, UINT64_C(1'481'111111111111), -12}));
|
||||||
@@ -3172,12 +2935,8 @@ private:
|
|||||||
// Alice offers to buy 1000 XRP for 1000 USD. She takes Bob's first
|
// Alice offers to buy 1000 XRP for 1000 USD. She takes Bob's first
|
||||||
// offer, removes 999 more as unfunded, then hits the step limit.
|
// offer, removes 999 more as unfunded, then hits the step limit.
|
||||||
env(offer(alice, USD(1'000), XRP(1'000)));
|
env(offer(alice, USD(1'000), XRP(1'000)));
|
||||||
if (!features[fixAMMv1_1])
|
env.require(
|
||||||
env.require(balance(
|
balance(alice, STAmount{USD, UINT64_C(2'050125257867587), -15}));
|
||||||
alice, STAmount{USD, UINT64_C(2'050126257867561), -15}));
|
|
||||||
else
|
|
||||||
env.require(balance(
|
|
||||||
alice, STAmount{USD, UINT64_C(2'050125257867587), -15}));
|
|
||||||
env.require(owners(alice, 2));
|
env.require(owners(alice, 2));
|
||||||
env.require(balance(bob, USD(0)));
|
env.require(balance(bob, USD(0)));
|
||||||
env.require(owners(bob, 1'001));
|
env.require(owners(bob, 1'001));
|
||||||
@@ -3283,33 +3042,19 @@ private:
|
|||||||
env(offer(bob, XRP(100), USD(100)));
|
env(offer(bob, XRP(100), USD(100)));
|
||||||
env(offer(bob, XRP(1'000), USD(100)));
|
env(offer(bob, XRP(1'000), USD(100)));
|
||||||
AMM ammDan(env, dan, XRP(1'000), USD(1'100));
|
AMM ammDan(env, dan, XRP(1'000), USD(1'100));
|
||||||
if (!features[fixAMMv1_1])
|
|
||||||
{
|
env(pay(alice, carol, USD(10'000)),
|
||||||
env(pay(alice, carol, USD(10'000)),
|
paths(XRP),
|
||||||
paths(XRP),
|
delivermin(USD(200)),
|
||||||
delivermin(USD(200)),
|
txflags(tfPartialPayment),
|
||||||
txflags(tfPartialPayment),
|
sendmax(XRPAmount(200'000'001)));
|
||||||
sendmax(XRP(200)));
|
env.require(balance(bob, USD(0)));
|
||||||
env.require(balance(bob, USD(0)));
|
env.require(
|
||||||
env.require(balance(carol, USD(200)));
|
balance(carol, STAmount{USD, UINT64_C(200'00000090909), -11}));
|
||||||
BEAST_EXPECT(ammDan.expectBalances(
|
BEAST_EXPECT(ammDan.expectBalances(
|
||||||
XRP(1'100), USD(1'000), ammDan.tokens()));
|
XRPAmount{1'100'000'001},
|
||||||
}
|
STAmount{USD, UINT64_C(999'99999909091), -11},
|
||||||
else
|
ammDan.tokens()));
|
||||||
{
|
|
||||||
env(pay(alice, carol, USD(10'000)),
|
|
||||||
paths(XRP),
|
|
||||||
delivermin(USD(200)),
|
|
||||||
txflags(tfPartialPayment),
|
|
||||||
sendmax(XRPAmount(200'000'001)));
|
|
||||||
env.require(balance(bob, USD(0)));
|
|
||||||
env.require(balance(
|
|
||||||
carol, STAmount{USD, UINT64_C(200'00000090909), -11}));
|
|
||||||
BEAST_EXPECT(ammDan.expectBalances(
|
|
||||||
XRPAmount{1'100'000'001},
|
|
||||||
STAmount{USD, UINT64_C(999'99999909091), -11},
|
|
||||||
ammDan.tokens()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4100,9 +3845,7 @@ private:
|
|||||||
testBookStep(all);
|
testBookStep(all);
|
||||||
testBookStep(all | ownerPaysFee);
|
testBookStep(all | ownerPaysFee);
|
||||||
testTransferRate(all | ownerPaysFee);
|
testTransferRate(all | ownerPaysFee);
|
||||||
testTransferRate((all - fixAMMv1_1) | ownerPaysFee);
|
|
||||||
testTransferRateNoOwnerFee(all);
|
testTransferRateNoOwnerFee(all);
|
||||||
testTransferRateNoOwnerFee(all - fixAMMv1_1);
|
|
||||||
testLimitQuality();
|
testLimitQuality();
|
||||||
testXRPPathLoop();
|
testXRPPathLoop();
|
||||||
}
|
}
|
||||||
@@ -4113,7 +3856,6 @@ private:
|
|||||||
using namespace jtx;
|
using namespace jtx;
|
||||||
FeatureBitset const all{supported_amendments()};
|
FeatureBitset const all{supported_amendments()};
|
||||||
testStepLimit(all);
|
testStepLimit(all);
|
||||||
testStepLimit(all - fixAMMv1_1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -4122,7 +3864,6 @@ private:
|
|||||||
using namespace jtx;
|
using namespace jtx;
|
||||||
FeatureBitset const all{supported_amendments()};
|
FeatureBitset const all{supported_amendments()};
|
||||||
test_convert_all_of_an_asset(all);
|
test_convert_all_of_an_asset(all);
|
||||||
test_convert_all_of_an_asset(all - fixAMMv1_1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -61,8 +61,7 @@ class LPTokenTransfer_test : public jtx::AMMTest
|
|||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
// bob can still send lptoken to carol even tho carol's USD is
|
// bob can still send lptoken to carol even tho carol's USD is
|
||||||
// frozen, regardless of whether fixFrozenLPTokenTransfer is enabled or
|
// frozen
|
||||||
// not
|
|
||||||
// Note: Deep freeze is not considered for LPToken transfer
|
// Note: Deep freeze is not considered for LPToken transfer
|
||||||
env(pay(bob, carol, STAmount{lpIssue, 5}));
|
env(pay(bob, carol, STAmount{lpIssue, 5}));
|
||||||
env.close();
|
env.close();
|
||||||
@@ -72,16 +71,8 @@ class LPTokenTransfer_test : public jtx::AMMTest
|
|||||||
ter(tecNO_PERMISSION));
|
ter(tecNO_PERMISSION));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
if (features[fixFrozenLPTokenTransfer])
|
// carol is frozen on USD and therefore can't send lptoken to bob
|
||||||
{
|
env(pay(carol, bob, STAmount{lpIssue, 5}), ter(tecPATH_DRY));
|
||||||
// carol is frozen on USD and therefore can't send lptoken to bob
|
|
||||||
env(pay(carol, bob, STAmount{lpIssue, 5}), ter(tecPATH_DRY));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// carol can still send lptoken with frozen USD
|
|
||||||
env(pay(carol, bob, STAmount{lpIssue, 5}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -119,40 +110,26 @@ class LPTokenTransfer_test : public jtx::AMMTest
|
|||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
// exercises alice's ability to consume carol's offer to sell lptoken
|
// exercises alice's ability to consume carol's offer to sell lptoken
|
||||||
// when carol's USD is frozen pre/post fixFrozenLPTokenTransfer
|
// when carol's USD is frozen
|
||||||
// amendment
|
|
||||||
if (features[fixFrozenLPTokenTransfer])
|
|
||||||
{
|
|
||||||
// with fixFrozenLPTokenTransfer, alice fails to consume carol's
|
|
||||||
// offer since carol's USD is frozen
|
|
||||||
env(pay(alice, bob, STAmount{lpIssue, 10}),
|
|
||||||
txflags(tfPartialPayment),
|
|
||||||
sendmax(XRP(10)),
|
|
||||||
ter(tecPATH_DRY));
|
|
||||||
env.close();
|
|
||||||
BEAST_EXPECT(expectOffers(env, carol, 1));
|
|
||||||
|
|
||||||
// gateway unfreezes carol's USD
|
// alice fails to consume carol's offer since carol's USD is frozen
|
||||||
env(trust(gw, carol["USD"](1'000'000'000), tfClearFreeze));
|
env(pay(alice, bob, STAmount{lpIssue, 10}),
|
||||||
env.close();
|
txflags(tfPartialPayment),
|
||||||
|
sendmax(XRP(10)),
|
||||||
|
ter(tecPATH_DRY));
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(expectOffers(env, carol, 1));
|
||||||
|
|
||||||
// alice successfully consumes carol's offer
|
// gateway unfreezes carol's USD
|
||||||
env(pay(alice, bob, STAmount{lpIssue, 10}),
|
env(trust(gw, carol["USD"](1'000'000'000), tfClearFreeze));
|
||||||
txflags(tfPartialPayment),
|
env.close();
|
||||||
sendmax(XRP(10)));
|
|
||||||
env.close();
|
// alice successfully consumes carol's offer
|
||||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
env(pay(alice, bob, STAmount{lpIssue, 10}),
|
||||||
}
|
txflags(tfPartialPayment),
|
||||||
else
|
sendmax(XRP(10)));
|
||||||
{
|
env.close();
|
||||||
// without fixFrozenLPTokenTransfer, alice can consume carol's offer
|
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||||
// even when carol's USD is frozen
|
|
||||||
env(pay(alice, bob, STAmount{lpIssue, 10}),
|
|
||||||
txflags(tfPartialPayment),
|
|
||||||
sendmax(XRP(10)));
|
|
||||||
env.close();
|
|
||||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure carol's USD is not frozen
|
// make sure carol's USD is not frozen
|
||||||
env(trust(gw, carol["USD"](1'000'000'000), tfClearFreeze));
|
env(trust(gw, carol["USD"](1'000'000'000), tfClearFreeze));
|
||||||
@@ -205,37 +182,26 @@ class LPTokenTransfer_test : public jtx::AMMTest
|
|||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
// exercises carol's ability to create a new offer to sell lptoken with
|
// exercises carol's ability to create a new offer to sell lptoken with
|
||||||
// frozen USD, before and after fixFrozenLPTokenTransfer
|
// frozen USD
|
||||||
if (features[fixFrozenLPTokenTransfer])
|
|
||||||
{
|
|
||||||
// with fixFrozenLPTokenTransfer, carol can't create an offer to
|
|
||||||
// sell lptoken when one of the assets is frozen
|
|
||||||
|
|
||||||
// carol can't create an offer to sell lptoken
|
// carol can't create an offer to sell lptoken when one of the assets is
|
||||||
env(offer(carol, XRP(10), STAmount{lpIssue, 10}),
|
// frozen
|
||||||
txflags(tfPassive),
|
|
||||||
ter(tecUNFUNDED_OFFER));
|
|
||||||
env.close();
|
|
||||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
|
||||||
|
|
||||||
// gateway unfreezes carol's USD
|
// carol can't create an offer to sell lptoken
|
||||||
env(trust(gw, carol["USD"](1'000'000'000), tfClearFreeze));
|
env(offer(carol, XRP(10), STAmount{lpIssue, 10}),
|
||||||
env.close();
|
txflags(tfPassive),
|
||||||
|
ter(tecUNFUNDED_OFFER));
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||||
|
|
||||||
// carol can create an offer to sell lptoken after USD is unfrozen
|
// gateway unfreezes carol's USD
|
||||||
env(offer(carol, XRP(10), STAmount{lpIssue, 10}),
|
env(trust(gw, carol["USD"](1'000'000'000), tfClearFreeze));
|
||||||
txflags(tfPassive));
|
env.close();
|
||||||
env.close();
|
|
||||||
BEAST_EXPECT(expectOffers(env, carol, 1));
|
// carol can create an offer to sell lptoken after USD is unfrozen
|
||||||
}
|
env(offer(carol, XRP(10), STAmount{lpIssue, 10}), txflags(tfPassive));
|
||||||
else
|
env.close();
|
||||||
{
|
BEAST_EXPECT(expectOffers(env, carol, 1));
|
||||||
// without fixFrozenLPTokenTransfer, carol can create an offer
|
|
||||||
env(offer(carol, XRP(10), STAmount{lpIssue, 10}),
|
|
||||||
txflags(tfPassive));
|
|
||||||
env.close();
|
|
||||||
BEAST_EXPECT(expectOffers(env, carol, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// gateway freezes carol's USD
|
// gateway freezes carol's USD
|
||||||
env(trust(gw, carol["USD"](0), tfSetFreeze));
|
env(trust(gw, carol["USD"](0), tfSetFreeze));
|
||||||
@@ -276,38 +242,22 @@ class LPTokenTransfer_test : public jtx::AMMTest
|
|||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
// alice creates an offer which exhibits different behavior on offer
|
// alice creates an offer which exhibits different behavior on offer
|
||||||
// crossing depending on if fixFrozenLPTokenTransfer is enabled
|
// crossing depending on
|
||||||
env(offer(alice, STAmount{token1, 100}, STAmount{token2, 100}));
|
env(offer(alice, STAmount{token1, 100}, STAmount{token2, 100}));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
// exercises carol's offer's ability to cross with alice's offer when
|
// exercises carol's offer's ability to cross with alice's offer when
|
||||||
// carol's USD is frozen, before and after fixFrozenLPTokenTransfer
|
// carol's USD is frozen
|
||||||
if (features[fixFrozenLPTokenTransfer])
|
|
||||||
{
|
// alice's offer can no longer cross with carol's offer
|
||||||
// with fixFrozenLPTokenTransfer enabled, alice's offer can no
|
BEAST_EXPECT(
|
||||||
// longer cross with carol's offer
|
expectLine(env, alice, STAmount{token1, 10'000'000}) &&
|
||||||
BEAST_EXPECT(
|
expectLine(env, alice, STAmount{token2, 10'000'000}));
|
||||||
expectLine(env, alice, STAmount{token1, 10'000'000}) &&
|
BEAST_EXPECT(
|
||||||
expectLine(env, alice, STAmount{token2, 10'000'000}));
|
expectLine(env, carol, STAmount{token2, 10'000'000}) &&
|
||||||
BEAST_EXPECT(
|
expectLine(env, carol, STAmount{token1, 10'000'000}));
|
||||||
expectLine(env, carol, STAmount{token2, 10'000'000}) &&
|
BEAST_EXPECT(
|
||||||
expectLine(env, carol, STAmount{token1, 10'000'000}));
|
expectOffers(env, alice, 1) && expectOffers(env, carol, 0));
|
||||||
BEAST_EXPECT(
|
|
||||||
expectOffers(env, alice, 1) && expectOffers(env, carol, 0));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// alice's offer still crosses with carol's offer despite carol's
|
|
||||||
// token1 is frozen
|
|
||||||
BEAST_EXPECT(
|
|
||||||
expectLine(env, alice, STAmount{token1, 10'000'100}) &&
|
|
||||||
expectLine(env, alice, STAmount{token2, 9'999'900}));
|
|
||||||
BEAST_EXPECT(
|
|
||||||
expectLine(env, carol, STAmount{token2, 10'000'100}) &&
|
|
||||||
expectLine(env, carol, STAmount{token1, 9'999'900}));
|
|
||||||
BEAST_EXPECT(
|
|
||||||
expectOffers(env, alice, 0) && expectOffers(env, carol, 0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -340,12 +290,9 @@ class LPTokenTransfer_test : public jtx::AMMTest
|
|||||||
env(check::create(carol, bob, STAmount{lpIssue, 10}));
|
env(check::create(carol, bob, STAmount{lpIssue, 10}));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
// with fixFrozenLPTokenTransfer enabled, bob fails to cash the check
|
// bob fails to cash the check
|
||||||
if (features[fixFrozenLPTokenTransfer])
|
env(check::cash(bob, carolChkId, STAmount{lpIssue, 10}),
|
||||||
env(check::cash(bob, carolChkId, STAmount{lpIssue, 10}),
|
ter(tecPATH_PARTIAL));
|
||||||
ter(tecPATH_PARTIAL));
|
|
||||||
else
|
|
||||||
env(check::cash(bob, carolChkId, STAmount{lpIssue, 10}));
|
|
||||||
|
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
@@ -398,69 +345,43 @@ class LPTokenTransfer_test : public jtx::AMMTest
|
|||||||
|
|
||||||
// exercises one's ability to transfer NFT using lptoken when one of the
|
// exercises one's ability to transfer NFT using lptoken when one of the
|
||||||
// assets is frozen
|
// assets is frozen
|
||||||
if (features[fixFrozenLPTokenTransfer])
|
|
||||||
{
|
|
||||||
// with fixFrozenLPTokenTransfer, freezing USD will prevent buy/sell
|
|
||||||
// offers with lptokens from being created/accepted
|
|
||||||
|
|
||||||
// carol fails to accept bob's offer with lptoken because carol's
|
// Freezing USD will prevent buy/sell
|
||||||
// USD is frozen
|
// offers with lptokens from being created/accepted
|
||||||
env(token::acceptSellOffer(carol, sellOfferIndex),
|
|
||||||
ter(tecINSUFFICIENT_FUNDS));
|
|
||||||
env.close();
|
|
||||||
|
|
||||||
// gateway unfreezes carol's USD
|
// carol fails to accept bob's offer with lptoken because carol's
|
||||||
env(trust(gw, carol["USD"](1'000'000), tfClearFreeze));
|
// USD is frozen
|
||||||
env.close();
|
env(token::acceptSellOffer(carol, sellOfferIndex),
|
||||||
|
ter(tecINSUFFICIENT_FUNDS));
|
||||||
|
env.close();
|
||||||
|
|
||||||
// carol can now accept the offer and own the nft
|
// gateway unfreezes carol's USD
|
||||||
env(token::acceptSellOffer(carol, sellOfferIndex));
|
env(trust(gw, carol["USD"](1'000'000), tfClearFreeze));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
// gateway freezes bobs's USD
|
// carol can now accept the offer and own the nft
|
||||||
env(trust(gw, bob["USD"](0), tfSetFreeze));
|
env(token::acceptSellOffer(carol, sellOfferIndex));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
// bob fails to create a buy offer with lptoken for carol's nft
|
// gateway freezes bobs's USD
|
||||||
// since bob's USD is frozen
|
env(trust(gw, bob["USD"](0), tfSetFreeze));
|
||||||
env(token::createOffer(bob, nftID, STAmount{lpIssue, 10}),
|
env.close();
|
||||||
token::owner(carol),
|
|
||||||
ter(tecUNFUNDED_OFFER));
|
|
||||||
env.close();
|
|
||||||
|
|
||||||
// gateway unfreezes bob's USD
|
// bob fails to create a buy offer with lptoken for carol's nft
|
||||||
env(trust(gw, bob["USD"](1'000'000), tfClearFreeze));
|
// since bob's USD is frozen
|
||||||
env.close();
|
env(token::createOffer(bob, nftID, STAmount{lpIssue, 10}),
|
||||||
|
token::owner(carol),
|
||||||
|
ter(tecUNFUNDED_OFFER));
|
||||||
|
env.close();
|
||||||
|
|
||||||
// bob can now create a buy offer
|
// gateway unfreezes bob's USD
|
||||||
env(token::createOffer(bob, nftID, STAmount{lpIssue, 10}),
|
env(trust(gw, bob["USD"](1'000'000), tfClearFreeze));
|
||||||
token::owner(carol));
|
env.close();
|
||||||
env.close();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// without fixFrozenLPTokenTransfer, freezing USD will still allow
|
|
||||||
// buy/sell offers to be created/accepted with lptoken
|
|
||||||
|
|
||||||
// carol can still accept bob's offer despite carol's USD is frozen
|
// bob can now create a buy offer
|
||||||
env(token::acceptSellOffer(carol, sellOfferIndex));
|
env(token::createOffer(bob, nftID, STAmount{lpIssue, 10}),
|
||||||
env.close();
|
token::owner(carol));
|
||||||
|
env.close();
|
||||||
// gateway freezes bob's USD
|
|
||||||
env(trust(gw, bob["USD"](0), tfSetFreeze));
|
|
||||||
env.close();
|
|
||||||
|
|
||||||
// bob creates a buy offer with lptoken despite bob's USD is frozen
|
|
||||||
uint256 const buyOfferIndex =
|
|
||||||
keylet::nftoffer(bob, env.seq(bob)).key;
|
|
||||||
env(token::createOffer(bob, nftID, STAmount{lpIssue, 10}),
|
|
||||||
token::owner(carol));
|
|
||||||
env.close();
|
|
||||||
|
|
||||||
// carol accepts bob's offer
|
|
||||||
env(token::acceptBuyOffer(carol, buyOfferIndex));
|
|
||||||
env.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -469,15 +390,12 @@ public:
|
|||||||
{
|
{
|
||||||
FeatureBitset const all{jtx::supported_amendments()};
|
FeatureBitset const all{jtx::supported_amendments()};
|
||||||
|
|
||||||
for (auto const features : {all, all - fixFrozenLPTokenTransfer})
|
testDirectStep(all);
|
||||||
{
|
testBookStep(all);
|
||||||
testDirectStep(features);
|
testOfferCreation(all);
|
||||||
testBookStep(features);
|
testOfferCrossing(all);
|
||||||
testOfferCreation(features);
|
testCheck(all);
|
||||||
testOfferCrossing(features);
|
testNFTOffers(all);
|
||||||
testCheck(features);
|
|
||||||
testNFTOffers(features);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -336,76 +336,6 @@ changeSpotPriceQuality(
|
|||||||
Rules const& rules,
|
Rules const& rules,
|
||||||
beast::Journal j)
|
beast::Journal j)
|
||||||
{
|
{
|
||||||
if (!rules.enabled(fixAMMv1_1))
|
|
||||||
{
|
|
||||||
// Finds takerPays (i) and takerGets (o) such that given pool
|
|
||||||
// composition poolGets(I) and poolPays(O): (O - o) / (I + i) = quality.
|
|
||||||
// Where takerGets is calculated as the swapAssetIn (see below).
|
|
||||||
// The above equation produces the quadratic equation:
|
|
||||||
// i^2*(1-fee) + i*I*(2-fee) + I^2 - I*O/quality,
|
|
||||||
// which is solved for i, and o is found with swapAssetIn().
|
|
||||||
auto const f = feeMult(tfee); // 1 - fee
|
|
||||||
auto const& a = f;
|
|
||||||
auto const b = pool.in * (1 + f);
|
|
||||||
Number const c =
|
|
||||||
pool.in * pool.in - pool.in * pool.out * quality.rate();
|
|
||||||
if (auto const res = b * b - 4 * a * c; res < 0)
|
|
||||||
return std::nullopt; // LCOV_EXCL_LINE
|
|
||||||
else if (auto const nTakerPaysPropose = (-b + root2(res)) / (2 * a);
|
|
||||||
nTakerPaysPropose > 0)
|
|
||||||
{
|
|
||||||
auto const nTakerPays = [&]() {
|
|
||||||
// The fee might make the AMM offer quality less than CLOB
|
|
||||||
// quality. Therefore, AMM offer has to satisfy this constraint:
|
|
||||||
// o / i >= q. Substituting o with swapAssetIn() gives: i <= O /
|
|
||||||
// q - I / (1 - fee).
|
|
||||||
auto const nTakerPaysConstraint =
|
|
||||||
pool.out * quality.rate() - pool.in / f;
|
|
||||||
if (nTakerPaysPropose > nTakerPaysConstraint)
|
|
||||||
return nTakerPaysConstraint;
|
|
||||||
return nTakerPaysPropose;
|
|
||||||
}();
|
|
||||||
if (nTakerPays <= 0)
|
|
||||||
{
|
|
||||||
JLOG(j.trace())
|
|
||||||
<< "changeSpotPriceQuality calc failed: "
|
|
||||||
<< to_string(pool.in) << " " << to_string(pool.out) << " "
|
|
||||||
<< quality << " " << tfee;
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
auto const takerPays =
|
|
||||||
toAmount<TIn>(getIssue(pool.in), nTakerPays, Number::upward);
|
|
||||||
// should not fail
|
|
||||||
if (auto const amounts =
|
|
||||||
TAmounts<TIn, TOut>{
|
|
||||||
takerPays, swapAssetIn(pool, takerPays, tfee)};
|
|
||||||
Quality{amounts} < quality &&
|
|
||||||
!withinRelativeDistance(
|
|
||||||
Quality{amounts}, quality, Number(1, -7)))
|
|
||||||
{
|
|
||||||
JLOG(j.error())
|
|
||||||
<< "changeSpotPriceQuality failed: " << to_string(pool.in)
|
|
||||||
<< " " << to_string(pool.out) << " " << " " << quality
|
|
||||||
<< " " << tfee << " " << to_string(amounts.in) << " "
|
|
||||||
<< to_string(amounts.out);
|
|
||||||
Throw<std::runtime_error>("changeSpotPriceQuality failed");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JLOG(j.trace())
|
|
||||||
<< "changeSpotPriceQuality succeeded: "
|
|
||||||
<< to_string(pool.in) << " " << to_string(pool.out) << " "
|
|
||||||
<< " " << quality << " " << tfee << " "
|
|
||||||
<< to_string(amounts.in) << " " << to_string(amounts.out);
|
|
||||||
return amounts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JLOG(j.trace()) << "changeSpotPriceQuality calc failed: "
|
|
||||||
<< to_string(pool.in) << " " << to_string(pool.out)
|
|
||||||
<< " " << quality << " " << tfee;
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the offer starting with XRP side. Return seated offer amounts
|
// Generate the offer starting with XRP side. Return seated offer amounts
|
||||||
// if the offer can be generated, otherwise nullopt.
|
// if the offer can be generated, otherwise nullopt.
|
||||||
auto const amounts = [&]() {
|
auto const amounts = [&]() {
|
||||||
@@ -467,61 +397,49 @@ swapAssetIn(
|
|||||||
TIn const& assetIn,
|
TIn const& assetIn,
|
||||||
std::uint16_t tfee)
|
std::uint16_t tfee)
|
||||||
{
|
{
|
||||||
if (auto const& rules = getCurrentTransactionRules();
|
// set rounding to always favor the amm. Clip to zero.
|
||||||
rules && rules->enabled(fixAMMv1_1))
|
// calculate:
|
||||||
{
|
// pool.out -
|
||||||
// set rounding to always favor the amm. Clip to zero.
|
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||||
// calculate:
|
// and explicitly set the rounding modes
|
||||||
// pool.out -
|
// Favoring the amm means we should:
|
||||||
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
// minimize:
|
||||||
// and explicitly set the rounding modes
|
// pool.out -
|
||||||
// Favoring the amm means we should:
|
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||||
// minimize:
|
// maximize:
|
||||||
// pool.out -
|
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||||
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
// (pool.in * pool.out)
|
||||||
// maximize:
|
// minimize:
|
||||||
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
// (pool.in + assetIn * feeMult(tfee)),
|
||||||
// (pool.in * pool.out)
|
// minimize:
|
||||||
// minimize:
|
// assetIn * feeMult(tfee)
|
||||||
// (pool.in + assetIn * feeMult(tfee)),
|
// feeMult is: (1-fee), fee is tfee/100000
|
||||||
// minimize:
|
// minimize:
|
||||||
// assetIn * feeMult(tfee)
|
// 1-fee
|
||||||
// feeMult is: (1-fee), fee is tfee/100000
|
// maximize:
|
||||||
// minimize:
|
// fee
|
||||||
// 1-fee
|
saveNumberRoundMode _{Number::getround()};
|
||||||
// maximize:
|
|
||||||
// fee
|
|
||||||
saveNumberRoundMode _{Number::getround()};
|
|
||||||
|
|
||||||
Number::setround(Number::upward);
|
Number::setround(Number::upward);
|
||||||
auto const numerator = pool.in * pool.out;
|
auto const numerator = pool.in * pool.out;
|
||||||
auto const fee = getFee(tfee);
|
auto const fee = getFee(tfee);
|
||||||
|
|
||||||
Number::setround(Number::downward);
|
Number::setround(Number::downward);
|
||||||
auto const denom = pool.in + assetIn * (1 - fee);
|
auto const denom = pool.in + assetIn * (1 - fee);
|
||||||
|
|
||||||
if (denom.signum() <= 0)
|
if (denom.signum() <= 0)
|
||||||
return toAmount<TOut>(getIssue(pool.out), 0);
|
return toAmount<TOut>(getIssue(pool.out), 0);
|
||||||
|
|
||||||
Number::setround(Number::upward);
|
Number::setround(Number::upward);
|
||||||
auto const ratio = numerator / denom;
|
auto const ratio = numerator / denom;
|
||||||
|
|
||||||
Number::setround(Number::downward);
|
Number::setround(Number::downward);
|
||||||
auto const swapOut = pool.out - ratio;
|
auto const swapOut = pool.out - ratio;
|
||||||
|
|
||||||
if (swapOut.signum() < 0)
|
if (swapOut.signum() < 0)
|
||||||
return toAmount<TOut>(getIssue(pool.out), 0);
|
return toAmount<TOut>(getIssue(pool.out), 0);
|
||||||
|
|
||||||
return toAmount<TOut>(getIssue(pool.out), swapOut, Number::downward);
|
return toAmount<TOut>(getIssue(pool.out), swapOut, Number::downward);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return toAmount<TOut>(
|
|
||||||
getIssue(pool.out),
|
|
||||||
pool.out -
|
|
||||||
(pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
|
||||||
Number::downward);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Swap assetOut out of the pool and swap in a proportional amount
|
/** Swap assetOut out of the pool and swap in a proportional amount
|
||||||
@@ -540,61 +458,49 @@ swapAssetOut(
|
|||||||
TOut const& assetOut,
|
TOut const& assetOut,
|
||||||
std::uint16_t tfee)
|
std::uint16_t tfee)
|
||||||
{
|
{
|
||||||
if (auto const& rules = getCurrentTransactionRules();
|
// set rounding to always favor the amm. Clip to zero.
|
||||||
rules && rules->enabled(fixAMMv1_1))
|
// calculate:
|
||||||
|
// ((pool.in * pool.out) / (pool.out - assetOut) - pool.in) /
|
||||||
|
// (1-tfee/100000)
|
||||||
|
// maximize:
|
||||||
|
// ((pool.in * pool.out) / (pool.out - assetOut) - pool.in)
|
||||||
|
// maximize:
|
||||||
|
// (pool.in * pool.out) / (pool.out - assetOut)
|
||||||
|
// maximize:
|
||||||
|
// (pool.in * pool.out)
|
||||||
|
// minimize
|
||||||
|
// (pool.out - assetOut)
|
||||||
|
// minimize:
|
||||||
|
// (1-tfee/100000)
|
||||||
|
// maximize:
|
||||||
|
// tfee/100000
|
||||||
|
|
||||||
|
saveNumberRoundMode _{Number::getround()};
|
||||||
|
|
||||||
|
Number::setround(Number::upward);
|
||||||
|
auto const numerator = pool.in * pool.out;
|
||||||
|
|
||||||
|
Number::setround(Number::downward);
|
||||||
|
auto const denom = pool.out - assetOut;
|
||||||
|
if (denom.signum() <= 0)
|
||||||
{
|
{
|
||||||
// set rounding to always favor the amm. Clip to zero.
|
return toMaxAmount<TIn>(getIssue(pool.in));
|
||||||
// calculate:
|
|
||||||
// ((pool.in * pool.out) / (pool.out - assetOut) - pool.in) /
|
|
||||||
// (1-tfee/100000)
|
|
||||||
// maximize:
|
|
||||||
// ((pool.in * pool.out) / (pool.out - assetOut) - pool.in)
|
|
||||||
// maximize:
|
|
||||||
// (pool.in * pool.out) / (pool.out - assetOut)
|
|
||||||
// maximize:
|
|
||||||
// (pool.in * pool.out)
|
|
||||||
// minimize
|
|
||||||
// (pool.out - assetOut)
|
|
||||||
// minimize:
|
|
||||||
// (1-tfee/100000)
|
|
||||||
// maximize:
|
|
||||||
// tfee/100000
|
|
||||||
|
|
||||||
saveNumberRoundMode _{Number::getround()};
|
|
||||||
|
|
||||||
Number::setround(Number::upward);
|
|
||||||
auto const numerator = pool.in * pool.out;
|
|
||||||
|
|
||||||
Number::setround(Number::downward);
|
|
||||||
auto const denom = pool.out - assetOut;
|
|
||||||
if (denom.signum() <= 0)
|
|
||||||
{
|
|
||||||
return toMaxAmount<TIn>(getIssue(pool.in));
|
|
||||||
}
|
|
||||||
|
|
||||||
Number::setround(Number::upward);
|
|
||||||
auto const ratio = numerator / denom;
|
|
||||||
auto const numerator2 = ratio - pool.in;
|
|
||||||
auto const fee = getFee(tfee);
|
|
||||||
|
|
||||||
Number::setround(Number::downward);
|
|
||||||
auto const feeMult = 1 - fee;
|
|
||||||
|
|
||||||
Number::setround(Number::upward);
|
|
||||||
auto const swapIn = numerator2 / feeMult;
|
|
||||||
if (swapIn.signum() < 0)
|
|
||||||
return toAmount<TIn>(getIssue(pool.in), 0);
|
|
||||||
|
|
||||||
return toAmount<TIn>(getIssue(pool.in), swapIn, Number::upward);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return toAmount<TIn>(
|
|
||||||
getIssue(pool.in),
|
|
||||||
((pool.in * pool.out) / (pool.out - assetOut) - pool.in) /
|
|
||||||
feeMult(tfee),
|
|
||||||
Number::upward);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Number::setround(Number::upward);
|
||||||
|
auto const ratio = numerator / denom;
|
||||||
|
auto const numerator2 = ratio - pool.in;
|
||||||
|
auto const fee = getFee(tfee);
|
||||||
|
|
||||||
|
Number::setround(Number::downward);
|
||||||
|
auto const feeMult = 1 - fee;
|
||||||
|
|
||||||
|
Number::setround(Number::upward);
|
||||||
|
auto const swapIn = numerator2 / feeMult;
|
||||||
|
if (swapIn.signum() < 0)
|
||||||
|
return toAmount<TIn>(getIssue(pool.in), 0);
|
||||||
|
|
||||||
|
return toAmount<TIn>(getIssue(pool.in), swapIn, Number::upward);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return square of n.
|
/** Return square of n.
|
||||||
|
|||||||
@@ -165,13 +165,6 @@ adjustAmountsByLPTokens(
|
|||||||
|
|
||||||
if (lpTokensActual < lpTokens)
|
if (lpTokensActual < lpTokens)
|
||||||
{
|
{
|
||||||
bool const ammRoundingEnabled = [&]() {
|
|
||||||
if (auto const& rules = getCurrentTransactionRules();
|
|
||||||
rules && rules->enabled(fixAMMv1_1))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}();
|
|
||||||
|
|
||||||
// Equal trade
|
// Equal trade
|
||||||
if (amount2)
|
if (amount2)
|
||||||
{
|
{
|
||||||
@@ -179,14 +172,7 @@ adjustAmountsByLPTokens(
|
|||||||
auto const amountActual = toSTAmount(amount.issue(), fr * amount);
|
auto const amountActual = toSTAmount(amount.issue(), fr * amount);
|
||||||
auto const amount2Actual =
|
auto const amount2Actual =
|
||||||
toSTAmount(amount2->issue(), fr * *amount2);
|
toSTAmount(amount2->issue(), fr * *amount2);
|
||||||
if (!ammRoundingEnabled)
|
return std::make_tuple(amountActual, amount2Actual, lpTokensActual);
|
||||||
return std::make_tuple(
|
|
||||||
amountActual < amount ? amountActual : amount,
|
|
||||||
amount2Actual < amount2 ? amount2Actual : amount2,
|
|
||||||
lpTokensActual);
|
|
||||||
else
|
|
||||||
return std::make_tuple(
|
|
||||||
amountActual, amount2Actual, lpTokensActual);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Single trade
|
// Single trade
|
||||||
@@ -194,19 +180,11 @@ adjustAmountsByLPTokens(
|
|||||||
if (isDeposit)
|
if (isDeposit)
|
||||||
return ammAssetIn(
|
return ammAssetIn(
|
||||||
amountBalance, lptAMMBalance, lpTokensActual, tfee);
|
amountBalance, lptAMMBalance, lpTokensActual, tfee);
|
||||||
else if (!ammRoundingEnabled)
|
return withdrawByTokens(
|
||||||
return withdrawByTokens(
|
amountBalance, lptAMMBalance, lpTokensActual, tfee);
|
||||||
amountBalance, lptAMMBalance, lpTokens, tfee);
|
|
||||||
else
|
|
||||||
return withdrawByTokens(
|
|
||||||
amountBalance, lptAMMBalance, lpTokensActual, tfee);
|
|
||||||
}();
|
}();
|
||||||
if (!ammRoundingEnabled)
|
|
||||||
return amountActual < amount
|
return std::make_tuple(amountActual, std::nullopt, lpTokensActual);
|
||||||
? std::make_tuple(amountActual, std::nullopt, lpTokensActual)
|
|
||||||
: std::make_tuple(amount, std::nullopt, lpTokensActual);
|
|
||||||
else
|
|
||||||
return std::make_tuple(amountActual, std::nullopt, lpTokensActual);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
|
|||||||
@@ -118,8 +118,7 @@ ammLPHolds(
|
|||||||
{
|
{
|
||||||
// This function looks similar to `accountHolds`. However, it only checks if
|
// This function looks similar to `accountHolds`. However, it only checks if
|
||||||
// a LPToken holder has enough balance. On the other hand, `accountHolds`
|
// a LPToken holder has enough balance. On the other hand, `accountHolds`
|
||||||
// checks if the underlying assets of LPToken are frozen with the
|
// checks if the underlying assets of LPToken are frozen
|
||||||
// fixFrozenLPTokenTransfer amendment
|
|
||||||
|
|
||||||
auto const currency = ammLPTCurrency(cur1, cur2);
|
auto const currency = ammLPTCurrency(cur1, cur2);
|
||||||
STAmount amount;
|
STAmount amount;
|
||||||
|
|||||||
@@ -138,13 +138,9 @@ private:
|
|||||||
generateFibSeqOffer(TAmounts<TIn, TOut> const& balances) const;
|
generateFibSeqOffer(TAmounts<TIn, TOut> const& balances) const;
|
||||||
|
|
||||||
/** Generate max offer.
|
/** Generate max offer.
|
||||||
* If `fixAMMOverflowOffer` is active, the offer is generated as:
|
* The offer is generated as:
|
||||||
* takerGets = 99% * balances.out takerPays = swapOut(takerGets).
|
* takerGets = 99% * balances.out takerPays = swapOut(takerGets).
|
||||||
* Return nullopt if takerGets is 0 or takerGets == balances.out.
|
* Return nullopt if takerGets is 0 or takerGets == balances.out.
|
||||||
*
|
|
||||||
* If `fixAMMOverflowOffer` is not active, the offer is generated as:
|
|
||||||
* takerPays = max input amount;
|
|
||||||
* takerGets = swapIn(takerPays).
|
|
||||||
*/
|
*/
|
||||||
std::optional<AMMOffer<TIn, TOut>>
|
std::optional<AMMOffer<TIn, TOut>>
|
||||||
maxOffer(TAmounts<TIn, TOut> const& balances, Rules const& rules) const;
|
maxOffer(TAmounts<TIn, TOut> const& balances, Rules const& rules) const;
|
||||||
|
|||||||
@@ -123,26 +123,14 @@ AMMLiquidity<TIn, TOut>::maxOffer(
|
|||||||
TAmounts<TIn, TOut> const& balances,
|
TAmounts<TIn, TOut> const& balances,
|
||||||
Rules const& rules) const
|
Rules const& rules) const
|
||||||
{
|
{
|
||||||
if (!rules.enabled(fixAMMOverflowOffer))
|
auto const out = maxOut<TOut>(balances.out, issueOut());
|
||||||
{
|
if (out <= TOut{0} || out >= balances.out)
|
||||||
return AMMOffer<TIn, TOut>(
|
return std::nullopt;
|
||||||
*this,
|
return AMMOffer<TIn, TOut>(
|
||||||
{maxAmount<TIn>(),
|
*this,
|
||||||
swapAssetIn(balances, maxAmount<TIn>(), tradingFee_)},
|
{swapAssetOut(balances, out, tradingFee_), out},
|
||||||
balances,
|
balances,
|
||||||
Quality{balances});
|
Quality{balances});
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto const out = maxOut<TOut>(balances.out, issueOut());
|
|
||||||
if (out <= TOut{0} || out >= balances.out)
|
|
||||||
return std::nullopt;
|
|
||||||
return AMMOffer<TIn, TOut>(
|
|
||||||
*this,
|
|
||||||
{swapAssetOut(balances, out, tradingFee_), out},
|
|
||||||
balances,
|
|
||||||
Quality{balances});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TIn, typename TOut>
|
template <typename TIn, typename TOut>
|
||||||
@@ -213,22 +201,13 @@ AMMLiquidity<TIn, TOut>::getOffer(
|
|||||||
return AMMOffer<TIn, TOut>(
|
return AMMOffer<TIn, TOut>(
|
||||||
*this, *amounts, balances, Quality{*amounts});
|
*this, *amounts, balances, Quality{*amounts});
|
||||||
}
|
}
|
||||||
else if (view.rules().enabled(fixAMMv1_2))
|
else if (auto const maxAMMOffer = maxOffer(balances, view.rules());
|
||||||
|
maxAMMOffer &&
|
||||||
|
Quality{maxAMMOffer->amount()} > *clobQuality)
|
||||||
{
|
{
|
||||||
if (auto const maxAMMOffer = maxOffer(balances, view.rules());
|
return maxAMMOffer;
|
||||||
maxAMMOffer &&
|
|
||||||
Quality{maxAMMOffer->amount()} > *clobQuality)
|
|
||||||
return maxAMMOffer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::overflow_error const& e)
|
|
||||||
{
|
|
||||||
JLOG(j_.error()) << "AMMLiquidity::getOffer overflow " << e.what();
|
|
||||||
if (!view.rules().enabled(fixAMMOverflowOffer))
|
|
||||||
return maxOffer(balances, view.rules());
|
|
||||||
else
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
catch (std::exception const& e)
|
catch (std::exception const& e)
|
||||||
{
|
{
|
||||||
JLOG(j_.error()) << "AMMLiquidity::getOffer exception " << e.what();
|
JLOG(j_.error()) << "AMMLiquidity::getOffer exception " << e.what();
|
||||||
|
|||||||
@@ -533,10 +533,7 @@ public:
|
|||||||
// Single path AMM offer has to factor in the transfer in rate
|
// Single path AMM offer has to factor in the transfer in rate
|
||||||
// when calculating the upper bound quality and the quality function
|
// when calculating the upper bound quality and the quality function
|
||||||
// because single path AMM's offer quality is not constant.
|
// because single path AMM's offer quality is not constant.
|
||||||
if (!rules.enabled(fixAMMv1_1))
|
if (offerType == OfferType::CLOB ||
|
||||||
return ofrQ;
|
|
||||||
else if (
|
|
||||||
offerType == OfferType::CLOB ||
|
|
||||||
(this->ammLiquidity_ && this->ammLiquidity_->multiPath()))
|
(this->ammLiquidity_ && this->ammLiquidity_->multiPath()))
|
||||||
return ofrQ;
|
return ofrQ;
|
||||||
|
|
||||||
@@ -838,7 +835,7 @@ BookStep<TIn, TOut, TDerived>::forEachOffer(
|
|||||||
// If offer crossing then use either LOB quality or nullopt
|
// If offer crossing then use either LOB quality or nullopt
|
||||||
// to prevent AMM being blocked by a lower quality LOB.
|
// to prevent AMM being blocked by a lower quality LOB.
|
||||||
auto const qualityThreshold = [&]() -> std::optional<Quality> {
|
auto const qualityThreshold = [&]() -> std::optional<Quality> {
|
||||||
if (sb.rules().enabled(fixAMMv1_1) && lobQuality)
|
if (lobQuality)
|
||||||
return static_cast<TDerived const*>(this)->qualityThreshold(
|
return static_cast<TDerived const*>(this)->qualityThreshold(
|
||||||
*lobQuality);
|
*lobQuality);
|
||||||
return lobQuality;
|
return lobQuality;
|
||||||
@@ -881,11 +878,8 @@ BookStep<TIn, TOut, TDerived>::consumeOffer(
|
|||||||
{
|
{
|
||||||
// purposely written as separate if statements so we get logging even
|
// purposely written as separate if statements so we get logging even
|
||||||
// when the amendment isn't active.
|
// when the amendment isn't active.
|
||||||
if (sb.rules().enabled(fixAMMOverflowOffer))
|
Throw<FlowException>(
|
||||||
{
|
tecINVARIANT_FAILED, "AMM pool product invariant failed.");
|
||||||
Throw<FlowException>(
|
|
||||||
tecINVARIANT_FAILED, "AMM pool product invariant failed.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The offer owner gets the ofrAmt. The difference between ofrAmt and
|
// The offer owner gets the ofrAmt. The difference between ofrAmt and
|
||||||
@@ -955,7 +949,7 @@ BookStep<TIn, TOut, TDerived>::tip(ReadView const& view) const
|
|||||||
// as the result a LOB offer is partially crossed, and it might take a few
|
// as the result a LOB offer is partially crossed, and it might take a few
|
||||||
// iterations to fully cross the offer.
|
// iterations to fully cross the offer.
|
||||||
auto const qualityThreshold = [&]() -> std::optional<Quality> {
|
auto const qualityThreshold = [&]() -> std::optional<Quality> {
|
||||||
if (view.rules().enabled(fixAMMv1_1) && lobQuality)
|
if (lobQuality)
|
||||||
return static_cast<TDerived const*>(this)->qualityThreshold(
|
return static_cast<TDerived const*>(this)->qualityThreshold(
|
||||||
*lobQuality);
|
*lobQuality);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|||||||
@@ -61,23 +61,20 @@ checkFreeze(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (view.rules().enabled(fixFrozenLPTokenTransfer))
|
if (auto const sleDst = view.read(keylet::account(dst));
|
||||||
|
sleDst && sleDst->isFieldPresent(sfAMMID))
|
||||||
{
|
{
|
||||||
if (auto const sleDst = view.read(keylet::account(dst));
|
auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID]));
|
||||||
sleDst && sleDst->isFieldPresent(sfAMMID))
|
if (!sleAmm)
|
||||||
{
|
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||||
auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID]));
|
|
||||||
if (!sleAmm)
|
|
||||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
|
||||||
|
|
||||||
if (isLPTokenFrozen(
|
if (isLPTokenFrozen(
|
||||||
view,
|
view,
|
||||||
src,
|
src,
|
||||||
(*sleAmm)[sfAsset].get<Issue>(),
|
(*sleAmm)[sfAsset].get<Issue>(),
|
||||||
(*sleAmm)[sfAsset2].get<Issue>()))
|
(*sleAmm)[sfAsset2].get<Issue>()))
|
||||||
{
|
{
|
||||||
return terNO_LINE;
|
return terNO_LINE;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -311,26 +311,24 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
|||||||
|
|
||||||
// Due to rounding, the LPTokenBalance of the last LP
|
// Due to rounding, the LPTokenBalance of the last LP
|
||||||
// might not match the LP's trustline balance
|
// might not match the LP's trustline balance
|
||||||
if (sb.rules().enabled(fixAMMv1_1))
|
|
||||||
|
if (auto const res =
|
||||||
|
isOnlyLiquidityProvider(sb, lpTokens.issue(), account_);
|
||||||
|
!res)
|
||||||
|
return {res.error(), false};
|
||||||
|
else if (res.value())
|
||||||
{
|
{
|
||||||
if (auto const res =
|
if (withinRelativeDistance(
|
||||||
isOnlyLiquidityProvider(sb, lpTokens.issue(), account_);
|
lpTokens,
|
||||||
!res)
|
ammSle->getFieldAmount(sfLPTokenBalance),
|
||||||
return {res.error(), false};
|
Number{1, -3}))
|
||||||
else if (res.value())
|
|
||||||
{
|
{
|
||||||
if (withinRelativeDistance(
|
ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
|
||||||
lpTokens,
|
sb.update(ammSle);
|
||||||
ammSle->getFieldAmount(sfLPTokenBalance),
|
}
|
||||||
Number{1, -3}))
|
else
|
||||||
{
|
{
|
||||||
ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
|
return {tecAMM_INVALID_TOKENS, false};
|
||||||
sb.update(ammSle);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return {tecAMM_INVALID_TOKENS, false};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,8 +540,7 @@ AMMWithdraw::withdraw(
|
|||||||
|
|
||||||
// Should not happen since the only LP on last withdraw
|
// Should not happen since the only LP on last withdraw
|
||||||
// has the balance set to the lp token trustline balance.
|
// has the balance set to the lp token trustline balance.
|
||||||
if (view.rules().enabled(fixAMMv1_1) &&
|
if (lpTokensWithdrawActual > lpTokensAMMBalance)
|
||||||
lpTokensWithdrawActual > lpTokensAMMBalance)
|
|
||||||
{
|
{
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
JLOG(journal.debug())
|
JLOG(journal.debug())
|
||||||
@@ -597,9 +594,8 @@ AMMWithdraw::withdraw(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check the reserve in case a trustline has to be created
|
// Check the reserve in case a trustline has to be created
|
||||||
bool const enabledFixAMMv1_2 = view.rules().enabled(fixAMMv1_2);
|
|
||||||
auto sufficientReserve = [&](Issue const& issue) -> TER {
|
auto sufficientReserve = [&](Issue const& issue) -> TER {
|
||||||
if (!enabledFixAMMv1_2 || isXRP(issue))
|
if (isXRP(issue))
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
if (!view.exists(keylet::line(account, issue)))
|
if (!view.exists(keylet::line(account, issue)))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -335,29 +335,26 @@ accountHolds(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// when fixFrozenLPTokenTransfer is enabled, if currency is lptoken,
|
// If currency is lptoken, we need to check if the associated assets
|
||||||
// we need to check if the associated assets have been frozen
|
// have been frozen
|
||||||
if (view.rules().enabled(fixFrozenLPTokenTransfer))
|
auto const sleIssuer = view.read(keylet::account(issuer));
|
||||||
|
if (!sleIssuer)
|
||||||
{
|
{
|
||||||
auto const sleIssuer = view.read(keylet::account(issuer));
|
return false; // LCOV_EXCL_LINE
|
||||||
if (!sleIssuer)
|
}
|
||||||
{
|
else if (sleIssuer->isFieldPresent(sfAMMID))
|
||||||
return false; // LCOV_EXCL_LINE
|
{
|
||||||
}
|
auto const sleAmm =
|
||||||
else if (sleIssuer->isFieldPresent(sfAMMID))
|
view.read(keylet::amm((*sleIssuer)[sfAMMID]));
|
||||||
{
|
|
||||||
auto const sleAmm =
|
|
||||||
view.read(keylet::amm((*sleIssuer)[sfAMMID]));
|
|
||||||
|
|
||||||
if (!sleAmm ||
|
if (!sleAmm ||
|
||||||
isLPTokenFrozen(
|
isLPTokenFrozen(
|
||||||
view,
|
view,
|
||||||
account,
|
account,
|
||||||
(*sleAmm)[sfAsset].get<Issue>(),
|
(*sleAmm)[sfAsset].get<Issue>(),
|
||||||
(*sleAmm)[sfAsset2].get<Issue>()))
|
(*sleAmm)[sfAsset2].get<Issue>()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1443,18 +1440,9 @@ accountSendIOU(
|
|||||||
WaiveTransferFee waiveFee,
|
WaiveTransferFee waiveFee,
|
||||||
bool const senderPaysXferFees)
|
bool const senderPaysXferFees)
|
||||||
{
|
{
|
||||||
if (view.rules().enabled(fixAMMv1_1))
|
if (saAmount < beast::zero || saAmount.holds<MPTIssue>())
|
||||||
{
|
{
|
||||||
if (saAmount < beast::zero || saAmount.holds<MPTIssue>())
|
return tecINTERNAL;
|
||||||
{
|
|
||||||
return tecINTERNAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
XRPL_ASSERT(
|
|
||||||
saAmount >= beast::zero && !saAmount.holds<MPTIssue>(),
|
|
||||||
"ripple::accountSendIOU : minimum amount and not MPT");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we aren't sending anything or if the sender is the same as the
|
/* If we aren't sending anything or if the sender is the same as the
|
||||||
|
|||||||
Reference in New Issue
Block a user