mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-20 10:35:50 +00:00
test: Unit test for AMM offer overflow (#4986)
This commit is contained in:
@@ -4889,6 +4889,280 @@ private:
|
|||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testFixOverflowOffer()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
FeatureBitset const all{supported_amendments()};
|
||||||
|
|
||||||
|
Account const gatehub{"gatehub"};
|
||||||
|
Account const bitstamp{"bitstamp"};
|
||||||
|
Account const trader{"trader"};
|
||||||
|
auto const usdGH = gatehub["USD"];
|
||||||
|
auto const btcGH = gatehub["BTC"];
|
||||||
|
auto const usdBIT = bitstamp["USD"];
|
||||||
|
|
||||||
|
struct InputSet
|
||||||
|
{
|
||||||
|
char const* testCase;
|
||||||
|
double const poolUsdBIT;
|
||||||
|
double const poolUsdGH;
|
||||||
|
sendmax const sendMaxUsdBIT;
|
||||||
|
STAmount const sendUsdGH;
|
||||||
|
STAmount const failUsdGH;
|
||||||
|
STAmount const failUsdBIT;
|
||||||
|
STAmount const goodUsdGH;
|
||||||
|
STAmount const goodUsdBIT;
|
||||||
|
IOUAmount const lpTokenBalance;
|
||||||
|
double const offer1BtcGH = 0.1;
|
||||||
|
double const offer2BtcGH = 0.1;
|
||||||
|
double const offer2UsdGH = 1;
|
||||||
|
double const rateBIT = 0.0;
|
||||||
|
double const rateGH = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using uint64_t = std::uint64_t;
|
||||||
|
|
||||||
|
for (auto const& input : {
|
||||||
|
InputSet{
|
||||||
|
.testCase = "Test Fix Overflow Offer", //
|
||||||
|
.poolUsdBIT = 3, //
|
||||||
|
.poolUsdGH = 273, //
|
||||||
|
.sendMaxUsdBIT{usdBIT(50)}, //
|
||||||
|
.sendUsdGH{usdGH, uint64_t(272'455089820359), -12}, //
|
||||||
|
.failUsdGH = STAmount{0}, //
|
||||||
|
.failUsdBIT{usdBIT, uint64_t(46'47826086956522), -14}, //
|
||||||
|
.goodUsdGH{usdGH, uint64_t(96'7543114220382), -13}, //
|
||||||
|
.goodUsdBIT{usdBIT, uint64_t(8'464739069120721), -15}, //
|
||||||
|
.lpTokenBalance = {28'61817604250837, -14}, //
|
||||||
|
.offer1BtcGH = 0.1, //
|
||||||
|
.offer2BtcGH = 0.1, //
|
||||||
|
.offer2UsdGH = 1, //
|
||||||
|
.rateBIT = 1.15, //
|
||||||
|
.rateGH = 1.2, //
|
||||||
|
},
|
||||||
|
InputSet{
|
||||||
|
.testCase = "Overflow test {1, 100, 0.111}", //
|
||||||
|
.poolUsdBIT = 1, //
|
||||||
|
.poolUsdGH = 100, //
|
||||||
|
.sendMaxUsdBIT{usdBIT(0.111)}, //
|
||||||
|
.sendUsdGH{usdGH, 100}, //
|
||||||
|
.failUsdGH = STAmount{0}, //
|
||||||
|
.failUsdBIT{usdBIT, uint64_t(1'111), -3}, //
|
||||||
|
.goodUsdGH{usdGH, uint64_t(90'04347888284115), -14}, //
|
||||||
|
.goodUsdBIT{usdBIT, uint64_t(1'111), -3}, //
|
||||||
|
.lpTokenBalance{10, 0}, //
|
||||||
|
.offer1BtcGH = 1e-5, //
|
||||||
|
.offer2BtcGH = 1, //
|
||||||
|
.offer2UsdGH = 1e-5, //
|
||||||
|
.rateBIT = 0, //
|
||||||
|
.rateGH = 0, //
|
||||||
|
},
|
||||||
|
InputSet{
|
||||||
|
.testCase = "Overflow test {1, 100, 1.00}", //
|
||||||
|
.poolUsdBIT = 1, //
|
||||||
|
.poolUsdGH = 100, //
|
||||||
|
.sendMaxUsdBIT{usdBIT(1.00)}, //
|
||||||
|
.sendUsdGH{usdGH, 100}, //
|
||||||
|
.failUsdGH = STAmount{0}, //
|
||||||
|
.failUsdBIT{usdBIT, uint64_t(2), 0}, //
|
||||||
|
.goodUsdGH{usdGH, uint64_t(52'94379354424079), -14}, //
|
||||||
|
.goodUsdBIT{usdBIT, uint64_t(2), 0}, //
|
||||||
|
.lpTokenBalance{10, 0}, //
|
||||||
|
.offer1BtcGH = 1e-5, //
|
||||||
|
.offer2BtcGH = 1, //
|
||||||
|
.offer2UsdGH = 1e-5, //
|
||||||
|
.rateBIT = 0, //
|
||||||
|
.rateGH = 0, //
|
||||||
|
},
|
||||||
|
InputSet{
|
||||||
|
.testCase = "Overflow test {1, 100, 4.6432}", //
|
||||||
|
.poolUsdBIT = 1, //
|
||||||
|
.poolUsdGH = 100, //
|
||||||
|
.sendMaxUsdBIT{usdBIT(4.6432)}, //
|
||||||
|
.sendUsdGH{usdGH, 100}, //
|
||||||
|
.failUsdGH = STAmount{0}, //
|
||||||
|
.failUsdBIT{usdBIT, uint64_t(5'6432), -4}, //
|
||||||
|
.goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
|
||||||
|
.goodUsdBIT{usdBIT, uint64_t(2'821579689703915), -15}, //
|
||||||
|
.lpTokenBalance{10, 0}, //
|
||||||
|
.offer1BtcGH = 1e-5, //
|
||||||
|
.offer2BtcGH = 1, //
|
||||||
|
.offer2UsdGH = 1e-5, //
|
||||||
|
.rateBIT = 0, //
|
||||||
|
.rateGH = 0, //
|
||||||
|
},
|
||||||
|
InputSet{
|
||||||
|
.testCase = "Overflow test {1, 100, 10}", //
|
||||||
|
.poolUsdBIT = 1, //
|
||||||
|
.poolUsdGH = 100, //
|
||||||
|
.sendMaxUsdBIT{usdBIT(10)}, //
|
||||||
|
.sendUsdGH{usdGH, 100}, //
|
||||||
|
.failUsdGH = STAmount{0}, //
|
||||||
|
.failUsdBIT{usdBIT, uint64_t(11), 0}, //
|
||||||
|
.goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
|
||||||
|
.goodUsdBIT{usdBIT, uint64_t(2'821579689703915), -15}, //
|
||||||
|
.lpTokenBalance{10, 0}, //
|
||||||
|
.offer1BtcGH = 1e-5, //
|
||||||
|
.offer2BtcGH = 1, //
|
||||||
|
.offer2UsdGH = 1e-5, //
|
||||||
|
.rateBIT = 0, //
|
||||||
|
.rateGH = 0, //
|
||||||
|
},
|
||||||
|
InputSet{
|
||||||
|
.testCase = "Overflow test {50, 100, 5.55}", //
|
||||||
|
.poolUsdBIT = 50, //
|
||||||
|
.poolUsdGH = 100, //
|
||||||
|
.sendMaxUsdBIT{usdBIT(5.55)}, //
|
||||||
|
.sendUsdGH{usdGH, 100}, //
|
||||||
|
.failUsdGH = STAmount{0}, //
|
||||||
|
.failUsdBIT{usdBIT, uint64_t(55'55), -2}, //
|
||||||
|
.goodUsdGH{usdGH, uint64_t(90'04347888284113), -14}, //
|
||||||
|
.goodUsdBIT{usdBIT, uint64_t(55'55), -2}, //
|
||||||
|
.lpTokenBalance{uint64_t(70'71067811865475), -14}, //
|
||||||
|
.offer1BtcGH = 1e-5, //
|
||||||
|
.offer2BtcGH = 1, //
|
||||||
|
.offer2UsdGH = 1e-5, //
|
||||||
|
.rateBIT = 0, //
|
||||||
|
.rateGH = 0, //
|
||||||
|
},
|
||||||
|
InputSet{
|
||||||
|
.testCase = "Overflow test {50, 100, 50.00}", //
|
||||||
|
.poolUsdBIT = 50, //
|
||||||
|
.poolUsdGH = 100, //
|
||||||
|
.sendMaxUsdBIT{usdBIT(50.00)}, //
|
||||||
|
.sendUsdGH{usdGH, 100}, //
|
||||||
|
.failUsdGH{usdGH, uint64_t(52'94379354424081), -14}, //
|
||||||
|
.failUsdBIT{usdBIT, uint64_t(100), 0}, //
|
||||||
|
.goodUsdGH{usdGH, uint64_t(52'94379354424081), -14}, //
|
||||||
|
.goodUsdBIT{usdBIT, uint64_t(100), 0}, //
|
||||||
|
.lpTokenBalance{uint64_t(70'71067811865475), -14}, //
|
||||||
|
.offer1BtcGH = 1e-5, //
|
||||||
|
.offer2BtcGH = 1, //
|
||||||
|
.offer2UsdGH = 1e-5, //
|
||||||
|
.rateBIT = 0, //
|
||||||
|
.rateGH = 0, //
|
||||||
|
},
|
||||||
|
InputSet{
|
||||||
|
.testCase = "Overflow test {50, 100, 232.16}", //
|
||||||
|
.poolUsdBIT = 50, //
|
||||||
|
.poolUsdGH = 100, //
|
||||||
|
.sendMaxUsdBIT{usdBIT(232.16)}, //
|
||||||
|
.sendUsdGH{usdGH, 100}, //
|
||||||
|
.failUsdGH = STAmount{0}, //
|
||||||
|
.failUsdBIT{usdBIT, uint64_t(282'16), -2}, //
|
||||||
|
.goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
|
||||||
|
.goodUsdBIT{usdBIT, uint64_t(141'0789844851958), -13}, //
|
||||||
|
.lpTokenBalance{70'71067811865475, -14}, //
|
||||||
|
.offer1BtcGH = 1e-5, //
|
||||||
|
.offer2BtcGH = 1, //
|
||||||
|
.offer2UsdGH = 1e-5, //
|
||||||
|
.rateBIT = 0, //
|
||||||
|
.rateGH = 0, //
|
||||||
|
},
|
||||||
|
InputSet{
|
||||||
|
.testCase = "Overflow test {50, 100, 500}", //
|
||||||
|
.poolUsdBIT = 50, //
|
||||||
|
.poolUsdGH = 100, //
|
||||||
|
.sendMaxUsdBIT{usdBIT(500)}, //
|
||||||
|
.sendUsdGH{usdGH, 100}, //
|
||||||
|
.failUsdGH = STAmount{0}, //
|
||||||
|
.failUsdBIT{usdBIT, uint64_t(550), 0}, //
|
||||||
|
.goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
|
||||||
|
.goodUsdBIT{usdBIT, uint64_t(141'0789844851958), -13}, //
|
||||||
|
.lpTokenBalance{70'71067811865475, -14}, //
|
||||||
|
.offer1BtcGH = 1e-5, //
|
||||||
|
.offer2BtcGH = 1, //
|
||||||
|
.offer2UsdGH = 1e-5, //
|
||||||
|
.rateBIT = 0, //
|
||||||
|
.rateGH = 0, //
|
||||||
|
},
|
||||||
|
})
|
||||||
|
{
|
||||||
|
testcase(input.testCase);
|
||||||
|
for (auto const& features :
|
||||||
|
{all - fixAMMOverflowOffer, all | fixAMMOverflowOffer})
|
||||||
|
{
|
||||||
|
Env env(*this, features);
|
||||||
|
|
||||||
|
env.fund(XRP(5'000), gatehub, bitstamp, trader);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
if (input.rateGH != 0.0)
|
||||||
|
env(rate(gatehub, input.rateGH));
|
||||||
|
if (input.rateBIT != 0.0)
|
||||||
|
env(rate(bitstamp, input.rateBIT));
|
||||||
|
|
||||||
|
env(trust(trader, usdGH(10'000'000)));
|
||||||
|
env(trust(trader, usdBIT(10'000'000)));
|
||||||
|
env(trust(trader, btcGH(10'000'000)));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(pay(gatehub, trader, usdGH(100'000)));
|
||||||
|
env(pay(gatehub, trader, btcGH(100'000)));
|
||||||
|
env(pay(bitstamp, trader, usdBIT(100'000)));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
AMM amm{
|
||||||
|
env,
|
||||||
|
trader,
|
||||||
|
usdGH(input.poolUsdGH),
|
||||||
|
usdBIT(input.poolUsdBIT)};
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
IOUAmount const preSwapLPTokenBalance =
|
||||||
|
amm.getLPTokensBalance();
|
||||||
|
|
||||||
|
env(offer(trader, usdBIT(1), btcGH(input.offer1BtcGH)));
|
||||||
|
env(offer(
|
||||||
|
trader,
|
||||||
|
btcGH(input.offer2BtcGH),
|
||||||
|
usdGH(input.offer2UsdGH)));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(pay(trader, trader, input.sendUsdGH),
|
||||||
|
path(~usdGH),
|
||||||
|
path(~btcGH, ~usdGH),
|
||||||
|
sendmax(input.sendMaxUsdBIT),
|
||||||
|
txflags(tfPartialPayment));
|
||||||
|
env.close();
|
||||||
|
if (!features[fixAMMOverflowOffer])
|
||||||
|
BEAST_EXPECT(amm.expectBalances(
|
||||||
|
input.failUsdGH,
|
||||||
|
input.failUsdBIT,
|
||||||
|
input.lpTokenBalance));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(amm.expectBalances(
|
||||||
|
input.goodUsdGH,
|
||||||
|
input.goodUsdBIT,
|
||||||
|
input.lpTokenBalance));
|
||||||
|
|
||||||
|
// Invariant: LPToken balance must not change in a payment
|
||||||
|
// or a swap transaction
|
||||||
|
BEAST_EXPECT(
|
||||||
|
amm.getLPTokensBalance() == preSwapLPTokenBalance);
|
||||||
|
|
||||||
|
// Invariant: The square root of (product of the pool
|
||||||
|
// balances) must be at least the LPTokenBalance
|
||||||
|
Number const sqrtPoolProduct =
|
||||||
|
root2(input.goodUsdGH * input.goodUsdBIT);
|
||||||
|
|
||||||
|
// Include a tiny tolerance for the test cases using
|
||||||
|
// .goodUsdGH{usdGH, uint64_t(35'44113971506987), -14},
|
||||||
|
// .goodUsdBIT{usdBIT, uint64_t(2'821579689703915), -15},
|
||||||
|
// These two values multiply
|
||||||
|
// to 99.99999999999994227040383754105 which gets internally
|
||||||
|
// rounded to 100, due to representation error.
|
||||||
|
BEAST_EXPECT(
|
||||||
|
(sqrtPoolProduct + Number{1, -14} >=
|
||||||
|
input.lpTokenBalance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testCore()
|
testCore()
|
||||||
{
|
{
|
||||||
@@ -4916,6 +5190,7 @@ private:
|
|||||||
testAMMID();
|
testAMMID();
|
||||||
testSelection();
|
testSelection();
|
||||||
testFixDefaultInnerObj();
|
testFixDefaultInnerObj();
|
||||||
|
testFixOverflowOffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
Reference in New Issue
Block a user