1#include <xrpl/ledger/OrderBookDB.h>
2#include <xrpl/ledger/Sandbox.h>
3#include <xrpl/ledger/View.h>
4#include <xrpl/ledger/helpers/AccountRootHelpers.h>
5#include <xrpl/protocol/AMMCore.h>
6#include <xrpl/protocol/Feature.h>
7#include <xrpl/protocol/STIssue.h>
8#include <xrpl/protocol/TxFlags.h>
9#include <xrpl/tx/transactors/dex/AMMCreate.h>
10#include <xrpl/tx/transactors/dex/AMMHelpers.h>
11#include <xrpl/tx/transactors/dex/AMMUtils.h>
24 auto const amount = ctx.
tx[sfAmount];
25 auto const amount2 = ctx.
tx[sfAmount2];
27 if (amount.issue() == amount2.issue())
29 JLOG(ctx.
j.
debug()) <<
"AMM Instance: tokens can not have the same currency/issuer.";
35 JLOG(ctx.
j.
debug()) <<
"AMM Instance: invalid asset1 amount.";
41 JLOG(ctx.
j.
debug()) <<
"AMM Instance: invalid asset2 amount.";
47 JLOG(ctx.
j.
debug()) <<
"AMM Instance: invalid trading fee.";
64 auto const accountID = ctx.
tx[sfAccount];
65 auto const amount = ctx.
tx[sfAmount];
66 auto const amount2 = ctx.
tx[sfAmount2];
69 if (
auto const ammKeylet =
keylet::amm(amount.issue(), amount2.issue());
72 JLOG(ctx.
j.
debug()) <<
"AMM Instance: ltAMM already exists.";
78 JLOG(ctx.
j.
debug()) <<
"AMM Instance: account is not authorized, " << amount.issue();
84 JLOG(ctx.
j.
debug()) <<
"AMM Instance: account is not authorized, " << amount2.issue();
92 JLOG(ctx.
j.
debug()) <<
"AMM Instance: involves frozen asset.";
101 return (issuerAccount->getFlags() & lsfDefaultRipple) == 0;
106 if (noDefaultRipple(ctx.
view, amount.issue()) || noDefaultRipple(ctx.
view, amount2.issue()))
108 JLOG(ctx.
j.
debug()) <<
"AMM Instance: DefaultRipple not set";
115 if (xrpBalance <= beast::zero)
117 JLOG(ctx.
j.
debug()) <<
"AMM Instance: insufficient reserves";
121 auto insufficientBalance = [&](
STAmount const& asset) {
123 return xrpBalance < asset;
130 if (insufficientBalance(amount) || insufficientBalance(amount2))
132 JLOG(ctx.
j.
debug()) <<
"AMM Instance: insufficient funds, " << amount <<
" " << amount2;
136 auto isLPToken = [&](
STAmount const& amount) ->
bool {
138 return sle->isFieldPresent(sfAMMID);
142 if (isLPToken(amount) || isLPToken(amount2))
144 JLOG(ctx.
j.
debug()) <<
"AMM Instance: can't create with LPTokens " << amount <<
" "
151 if (
auto const accountId =
153 accountId == beast::zero)
164 auto clawbackDisabled = [&](
Issue const& issue) ->
TER {
170 if (sle->getFlags() & lsfAllowTrustLineClawback)
175 if (
auto const ter = clawbackDisabled(amount.issue()); !
isTesSuccess(ter))
177 return clawbackDisabled(amount2.issue());
183 auto const amount = ctx_.
tx[sfAmount];
184 auto const amount2 = ctx_.
tx[sfAmount2];
186 auto const ammKeylet =
keylet::amm(amount.issue(), amount2.issue());
193 JLOG(j_.
error()) <<
"AMM Instance: failed to create pseudo account.";
194 return {maybeAccount.error(),
false};
196 auto& account = *maybeAccount;
197 auto const accountId = (*account)[sfAccount];
200 auto const lptIss =
ammLPTIssue(amount.issue().currency, amount2.issue().currency, accountId);
203 JLOG(j_.
error()) <<
"AMM Instance: LP Token already exists.";
214 auto const lpTokens =
ammLPTokens(amount, amount2, lptIss);
218 ammSle->setAccountID(sfAccount, accountId);
219 ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
220 auto const& [issue1, issue2] =
std::minmax(amount.issue(), amount2.issue());
221 ammSle->setFieldIssue(sfAsset,
STIssue{sfAsset, issue1});
222 ammSle->setFieldIssue(sfAsset2,
STIssue{sfAsset2, issue2});
227 if (
auto ter =
dirLink(sb, accountId, ammSle); ter)
229 JLOG(j_.
debug()) <<
"AMM Instance: failed to insert owner dir";
238 JLOG(j_.
debug()) <<
"AMM Instance: failed to send LPT " << lpTokens;
242 auto sendAndTrustSet = [&](
STAmount const& amount) ->
TER {
255 auto const flags = sleRippleState->
getFlags();
256 sleRippleState->
setFieldU32(sfFlags, flags | lsfAMMNode);
257 sb.
update(sleRippleState);
263 res = sendAndTrustSet(amount);
266 JLOG(j_.
debug()) <<
"AMM Instance: failed to send " << amount;
271 res = sendAndTrustSet(amount2);
274 JLOG(j_.
debug()) <<
"AMM Instance: failed to send " << amount2;
278 JLOG(j_.
debug()) <<
"AMM Instance: success " << accountId <<
" " << ammKeylet.key <<
" "
279 << lpTokens <<
" " << amount <<
" " << amount2;
283 if (
auto const bookExisted =
static_cast<bool>(sb.
read(dir)); !bookExisted)
284 ctx_.
registry.get().getOrderBookDB().addOrderBook(book);
286 addOrderBook(amount.issue(), amount2.issue(),
getRate(amount2, amount));
287 addOrderBook(amount2.issue(), amount.issue(),
getRate(amount, amount2));
A generic endpoint for log messages.
TER doApply() override
Attempt to create the AMM instance.
static bool checkExtraFeatures(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
State information when applying a tx.
std::reference_wrapper< ServiceRegistry > registry
beast::Journal const journal
A currency issued by an account.
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Issue const & issue() const
void setFieldU32(SField const &field, std::uint32_t)
std::uint32_t getFlags() const
Discardable, editable view to a ledger.
static XRPAmount calculateOwnerReserveFee(ReadView const &view, STTx const &tx)
void update(std::shared_ptr< SLE > const &sle) override
Indicate changes to a peeked SLE.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
void insert(std::shared_ptr< SLE > const &sle) override
Insert a new state SLE.
Keylet quality(Keylet const &k, std::uint64_t q) noexcept
The initial directory page for a specific quality.
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Keylet line(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Keylet account(AccountID const &id) noexcept
AccountID root.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::uint16_t constexpr TRADING_FEE_THRESHOLD
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Generate a pseudo-account address from a pseudo owner key.
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
bool isXRP(AccountID const &c)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
STAmount ammLPTokens(STAmount const &asset1, STAmount const &asset2, Issue const &lptIssue)
Calculate LP Tokens given AMM pool reserves.
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, int depth=0)
TERSubset< CanCvtToTER > TER
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
void initializeFeeAuctionVote(ApplyView &view, std::shared_ptr< SLE > &ammSle, AccountID const &account, Issue const &lptIssue, std::uint16_t tfee)
Initialize Auction and Voting slots and set the trading/discounted fee.
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object, SF_UINT64 const &node=sfOwnerNode)
NotTEC invalidAMMAmount(STAmount const &amount, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt, bool validZero=false)
Validate the amount.
bool isTesSuccess(TER x) noexcept
Expected< std::shared_ptr< SLE >, TER > createPseudoAccount(ApplyView &view, uint256 const &pseudoOwnerKey, SField const &ownerField)
Create pseudo-account, storing pseudoOwnerKey into ownerField.
Issue ammLPTIssue(Currency const &cur1, Currency const &cur2, AccountID const &ammAccountID)
Calculate LPT Issue from AMM asset pair.
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, int depth=0)
Check if the account lacks required authorization for MPT.
static std::pair< TER, bool > applyCreate(ApplyContext &ctx_, Sandbox &sb, AccountID const &account_, beast::Journal j_)
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.