mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-19 01:55:48 +00:00
Add AMM functionality: - InstanceCreate - Deposit - Withdraw - Governance - Auctioning - payment engine integration To support this functionality, add: - New RPC method, `amm_info`, to fetch pool and LPT balances - AMM Root Account - trust line for each IOU AMM token - trust line to track Liquidity Provider Tokens (LPT) - `ltAMM` object The `ltAMM` object tracks: - fee votes - auction slot bids - AMM tokens pair - total outstanding tokens balance - `AMMID` to AMM `RootAccountID` mapping Add new classes to facilitate AMM integration into the payment engine. `BookStep` uses these classes to infer if AMM liquidity can be consumed. The AMM formula implementation uses the new Number class added in #4192. IOUAmount and STAmount use Number arithmetic. Add AMM unit tests for all features. AMM requires the following amendments: - featureAMM - fixUniversalNumber - featureFlowCross Notes: - Current trading fee threshold is 1% - AMM currency is generated by: 0x03 + 152 bits of sha256{cur1, cur2} - Current max AMM Offers is 30 --------- Co-authored-by: Howard Hinnant <howard.hinnant@gmail.com>
312 lines
11 KiB
C++
312 lines
11 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2023 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#ifndef RIPPLE_APP_MISC_AMMHELPERS_H_INCLUDED
|
|
#define RIPPLE_APP_MISC_AMMHELPERS_H_INCLUDED
|
|
|
|
#include <ripple/basics/IOUAmount.h>
|
|
#include <ripple/basics/Number.h>
|
|
#include <ripple/protocol/AMMCore.h>
|
|
#include <ripple/protocol/AmountConversions.h>
|
|
#include <ripple/protocol/Issue.h>
|
|
#include <ripple/protocol/Quality.h>
|
|
#include <ripple/protocol/STAccount.h>
|
|
#include <ripple/protocol/STAmount.h>
|
|
|
|
#include <type_traits>
|
|
|
|
namespace ripple {
|
|
|
|
/** Calculate LP Tokens given AMM pool reserves.
|
|
* @param asset1 AMM one side of the pool reserve
|
|
* @param asset2 AMM another side of the pool reserve
|
|
* @return LP Tokens as IOU
|
|
*/
|
|
STAmount
|
|
ammLPTokens(
|
|
STAmount const& asset1,
|
|
STAmount const& asset2,
|
|
Issue const& lptIssue);
|
|
|
|
/** Calculate LP Tokens given asset's deposit amount.
|
|
* @param asset1Balance current AMM asset1 balance
|
|
* @param asset1Deposit requested asset1 deposit amount
|
|
* @param lptAMMBalance AMM LPT balance
|
|
* @param tfee trading fee in basis points
|
|
* @return tokens
|
|
*/
|
|
STAmount
|
|
lpTokensIn(
|
|
STAmount const& asset1Balance,
|
|
STAmount const& asset1Deposit,
|
|
STAmount const& lptAMMBalance,
|
|
std::uint16_t tfee);
|
|
|
|
/** Calculate asset deposit given LP Tokens.
|
|
* @param asset1Balance current AMM asset1 balance
|
|
* @param lpTokens LP Tokens
|
|
* @param lptAMMBalance AMM LPT balance
|
|
* @param tfee trading fee in basis points
|
|
* @return
|
|
*/
|
|
STAmount
|
|
ammAssetIn(
|
|
STAmount const& asset1Balance,
|
|
STAmount const& lptAMMBalance,
|
|
STAmount const& lpTokens,
|
|
std::uint16_t tfee);
|
|
|
|
/** Calculate LP Tokens given asset's withdraw amount. Return 0
|
|
* if can't calculate.
|
|
* @param asset1Balance current AMM asset1 balance
|
|
* @param asset1Withdraw requested asset1 withdraw amount
|
|
* @param lptAMMBalance AMM LPT balance
|
|
* @param tfee trading fee in basis points
|
|
* @return tokens out amount
|
|
*/
|
|
STAmount
|
|
lpTokensOut(
|
|
STAmount const& asset1Balance,
|
|
STAmount const& asset1Withdraw,
|
|
STAmount const& lptAMMBalance,
|
|
std::uint16_t tfee);
|
|
|
|
/** Calculate asset withdrawal by tokens
|
|
* @param assetBalance balance of the asset being withdrawn
|
|
* @param lptAMMBalance total AMM Tokens balance
|
|
* @param lpTokens LP Tokens balance
|
|
* @param tfee trading fee in basis points
|
|
* @return calculated asset amount
|
|
*/
|
|
STAmount
|
|
withdrawByTokens(
|
|
STAmount const& assetBalance,
|
|
STAmount const& lptAMMBalance,
|
|
STAmount const& lpTokens,
|
|
std::uint16_t tfee);
|
|
|
|
/** Check if the relative distance between the qualities
|
|
* is within the requested distance.
|
|
* @param calcQuality calculated quality
|
|
* @param reqQuality requested quality
|
|
* @param dist requested relative distance
|
|
* @return true if within dist, false otherwise
|
|
*/
|
|
inline bool
|
|
withinRelativeDistance(
|
|
Quality const& calcQuality,
|
|
Quality const& reqQuality,
|
|
Number const& dist)
|
|
{
|
|
if (calcQuality == reqQuality)
|
|
return true;
|
|
auto const [min, max] = std::minmax(calcQuality, reqQuality);
|
|
// Relative distance is (max - min)/max. Can't use basic operations
|
|
// on Quality. Have to use Quality::rate() instead, which
|
|
// is inverse of quality: (1/max.rate - 1/min.rate)/(1/max.rate)
|
|
return ((min.rate() - max.rate()) / min.rate()) < dist;
|
|
}
|
|
|
|
/** Check if the relative distance between the amounts
|
|
* is within the requested distance.
|
|
* @param calc calculated amount
|
|
* @param req requested amount
|
|
* @param dist requested relative distance
|
|
* @return true if within dist, false otherwise
|
|
*/
|
|
// clang-format off
|
|
template <typename Amt>
|
|
requires(
|
|
std::is_same_v<Amt, STAmount> || std::is_same_v<Amt, IOUAmount> ||
|
|
std::is_same_v<Amt, XRPAmount>)
|
|
bool
|
|
withinRelativeDistance(Amt const& calc, Amt const& req, Number const& dist)
|
|
{
|
|
if (calc == req)
|
|
return true;
|
|
auto const [min, max] = std::minmax(calc, req);
|
|
return ((max - min) / max) < dist;
|
|
}
|
|
// clang-format on
|
|
|
|
/** 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().
|
|
* @param pool AMM pool balances
|
|
* @param quality requested quality
|
|
* @param tfee trading fee in basis points
|
|
* @return seated in/out amounts if the quality can be changed
|
|
*/
|
|
template <typename TIn, typename TOut>
|
|
std::optional<TAmounts<TIn, TOut>>
|
|
changeSpotPriceQuality(
|
|
TAmounts<TIn, TOut> const& pool,
|
|
Quality const& quality,
|
|
std::uint16_t tfee)
|
|
{
|
|
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;
|
|
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)
|
|
return std::nullopt;
|
|
auto const takerPays = toAmount<TIn>(
|
|
getIssue(pool.in), nTakerPays, Number::rounding_mode::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)))
|
|
Throw<std::runtime_error>("changeSpotPriceQuality failed");
|
|
else
|
|
return amounts;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
/** AMM pool invariant - the product (A * B) after swap in/out has to remain
|
|
* at least the same: (A + in) * (B - out) >= A * B
|
|
* XRP round-off may result in a smaller product after swap in/out.
|
|
* To address this:
|
|
* - if on swapIn the out is XRP then the amount is round-off
|
|
* downward, making the product slightly larger since out
|
|
* value is reduced.
|
|
* - if on swapOut the in is XRP then the amount is round-off
|
|
* upward, making the product slightly larger since in
|
|
* value is increased.
|
|
*/
|
|
|
|
/** Swap assetIn into the pool and swap out a proportional amount
|
|
* of the other asset. Implements AMM Swap in.
|
|
* @see [XLS30d:AMM
|
|
* Swap](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
|
* @param pool current AMM pool balances
|
|
* @param assetIn amount to swap in
|
|
* @param tfee trading fee in basis points
|
|
* @return
|
|
*/
|
|
template <typename TIn, typename TOut>
|
|
TOut
|
|
swapAssetIn(
|
|
TAmounts<TIn, TOut> const& pool,
|
|
TIn const& assetIn,
|
|
std::uint16_t tfee)
|
|
{
|
|
return toAmount<TOut>(
|
|
getIssue(pool.out),
|
|
pool.out - (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
|
Number::rounding_mode::downward);
|
|
}
|
|
|
|
/** Swap assetOut out of the pool and swap in a proportional amount
|
|
* of the other asset. Implements AMM Swap out.
|
|
* @see [XLS30d:AMM
|
|
* Swap](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
|
* @param pool current AMM pool balances
|
|
* @param assetOut amount to swap out
|
|
* @param tfee trading fee in basis points
|
|
* @return
|
|
*/
|
|
template <typename TIn, typename TOut>
|
|
TIn
|
|
swapAssetOut(
|
|
TAmounts<TIn, TOut> const& pool,
|
|
TOut const& assetOut,
|
|
std::uint16_t tfee)
|
|
{
|
|
return toAmount<TIn>(
|
|
getIssue(pool.in),
|
|
((pool.in * pool.out) / (pool.out - assetOut) - pool.in) /
|
|
feeMult(tfee),
|
|
Number::rounding_mode::upward);
|
|
}
|
|
|
|
/** Return square of n.
|
|
*/
|
|
Number
|
|
square(Number const& n);
|
|
|
|
/** Adjust LP tokens to deposit/withdraw.
|
|
* Amount type keeps 16 digits. Maintaining the LP balance by adding deposited
|
|
* tokens or subtracting withdrawn LP tokens from LP balance results in
|
|
* losing precision in LP balance. I.e. the resulting LP balance
|
|
* is less than the actual sum of LP tokens. To adjust for this, subtract
|
|
* old tokens balance from the new one for deposit or vice versa for withdraw
|
|
* to cancel out the precision loss.
|
|
* @param lptAMMBalance LPT AMM Balance
|
|
* @param lpTokens LP tokens to deposit or withdraw
|
|
* @param isDeposit true if deposit, false if withdraw
|
|
*/
|
|
STAmount
|
|
adjustLPTokens(
|
|
STAmount const& lptAMMBalance,
|
|
STAmount const& lpTokens,
|
|
bool isDeposit);
|
|
|
|
/** Calls adjustLPTokens() and adjusts deposit or withdraw amounts if
|
|
* the adjusted LP tokens are less than the provided LP tokens.
|
|
* @param amountBalance asset1 pool balance
|
|
* @param amount asset1 to deposit or withdraw
|
|
* @param amount2 asset2 to deposit or withdraw
|
|
* @param lptAMMBalance LPT AMM Balance
|
|
* @param lpTokens LP tokens to deposit or withdraw
|
|
* @param tfee trading fee in basis points
|
|
* @param isDeposit true if deposit, false if withdraw
|
|
* @return
|
|
*/
|
|
std::tuple<STAmount, std::optional<STAmount>, STAmount>
|
|
adjustAmountsByLPTokens(
|
|
STAmount const& amountBalance,
|
|
STAmount const& amount,
|
|
std::optional<STAmount> const& amount2,
|
|
STAmount const& lptAMMBalance,
|
|
STAmount const& lpTokens,
|
|
std::uint16_t tfee,
|
|
bool isDeposit);
|
|
|
|
/** Positive solution for quadratic equation:
|
|
* x = (-b + sqrt(b**2 + 4*a*c))/(2*a)
|
|
*/
|
|
Number
|
|
solveQuadraticEq(Number const& a, Number const& b, Number const& c);
|
|
|
|
} // namespace ripple
|
|
|
|
#endif // RIPPLE_APP_MISC_AMMHELPERS_H_INCLUDED
|