1#include <xrpl/tx/transactors/dex/AMMCreate.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/beast/utility/Zero.h>
5#include <xrpl/core/ServiceRegistry.h>
6#include <xrpl/ledger/OrderBookDB.h>
7#include <xrpl/ledger/ReadView.h>
8#include <xrpl/ledger/Sandbox.h>
9#include <xrpl/ledger/View.h>
10#include <xrpl/ledger/helpers/AMMHelpers.h>
11#include <xrpl/ledger/helpers/AccountRootHelpers.h>
12#include <xrpl/ledger/helpers/MPTokenHelpers.h>
13#include <xrpl/ledger/helpers/TokenHelpers.h>
14#include <xrpl/protocol/AMMCore.h>
15#include <xrpl/protocol/AccountID.h>
16#include <xrpl/protocol/Asset.h>
17#include <xrpl/protocol/Book.h>
18#include <xrpl/protocol/Feature.h>
19#include <xrpl/protocol/Indexes.h>
20#include <xrpl/protocol/Issue.h>
21#include <xrpl/protocol/LedgerFormats.h>
22#include <xrpl/protocol/MPTIssue.h>
23#include <xrpl/protocol/SField.h>
24#include <xrpl/protocol/STAmount.h>
25#include <xrpl/protocol/STIssue.h>
26#include <xrpl/protocol/STLedgerEntry.h>
27#include <xrpl/protocol/STTx.h>
28#include <xrpl/protocol/TER.h>
29#include <xrpl/protocol/XRPAmount.h>
30#include <xrpl/tx/ApplyContext.h>
31#include <xrpl/tx/Transactor.h>
57 auto const amount = ctx.
tx[sfAmount];
58 auto const amount2 = ctx.
tx[sfAmount2];
60 if (amount.asset() == amount2.asset())
62 JLOG(ctx.
j.
debug()) <<
"AMM Instance: tokens can not have the same asset.";
68 JLOG(ctx.
j.
debug()) <<
"AMM Instance: invalid asset1 amount.";
74 JLOG(ctx.
j.
debug()) <<
"AMM Instance: invalid asset2 amount.";
80 JLOG(ctx.
j.
debug()) <<
"AMM Instance: invalid trading fee.";
97 auto const accountID = ctx.
tx[sfAccount];
98 auto const amount = ctx.
tx[sfAmount];
99 auto const amount2 = ctx.
tx[sfAmount2];
102 if (
auto const ammKeylet =
keylet::amm(amount.asset(), amount2.asset());
105 JLOG(ctx.
j.
debug()) <<
"AMM Instance: ltAMM already exists.";
111 JLOG(ctx.
j.
debug()) <<
"AMM Instance: account is not authorized, " << amount.asset();
117 JLOG(ctx.
j.
debug()) <<
"AMM Instance: account is not authorized, " << amount2.asset();
125 JLOG(ctx.
j.
debug()) <<
"AMM Instance: involves frozen or locked asset.";
130 JLOG(ctx.
j.
debug()) <<
"AMM Instance: involves frozen or locked asset.";
139 return !issuerAccount->isFlag(lsfDefaultRipple);
144 if (noDefaultRipple(ctx.
view, amount.asset()) || noDefaultRipple(ctx.
view, amount2.asset()))
146 JLOG(ctx.
j.
debug()) <<
"AMM Instance: DefaultRipple not set";
153 if (xrpBalance <= beast::kZero)
155 JLOG(ctx.
j.
debug()) <<
"AMM Instance: insufficient reserves";
159 auto insufficientBalance = [&](
STAmount const& amount) {
161 return xrpBalance < amount;
171 if (insufficientBalance(amount) || insufficientBalance(amount2))
173 JLOG(ctx.
j.
debug()) <<
"AMM Instance: insufficient funds, " << amount <<
" " << amount2;
177 auto isLPToken = [&](
STAmount const& amount) ->
bool {
179 return sle->isFieldPresent(sfAMMID);
183 if (isLPToken(amount) || isLPToken(amount2))
185 JLOG(ctx.
j.
debug()) <<
"AMM Instance: can't create with LPTokens " << amount <<
" "
192 if (
auto const accountId =
194 accountId == beast::kZero)
212 auto clawbackDisabled = [&](
Asset const& asset) ->
TER {
218 if (sle->isFlag(lsfMPTCanClawback))
228 if (sle->isFlag(lsfAllowTrustLineClawback))
234 if (
auto const ter = clawbackDisabled(amount.asset()); !
isTesSuccess(ter))
236 if (
auto const ter = clawbackDisabled(amount2.asset()); !
isTesSuccess(ter))
245 auto const amount = ctx.
tx[sfAmount];
246 auto const amount2 = ctx.
tx[sfAmount2];
248 auto const ammKeylet =
keylet::amm(amount.asset(), amount2.asset());
255 JLOG(j.
error()) <<
"AMM Instance: failed to create pseudo account.";
256 return {maybeAccount.error(),
false};
258 auto& acc = *maybeAccount;
259 auto const accountId = (*acc)[sfAccount];
262 auto const lptIss =
ammLPTIssue(amount.asset(), amount2.asset(), accountId);
265 JLOG(j.
error()) <<
"AMM Instance: LP Token already exists.";
276 auto const lpTokens =
ammLPTokens(amount, amount2, lptIss);
280 ammSle->setAccountID(sfAccount, accountId);
281 ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
282 auto const& [asset1, asset2] =
std::minmax(amount.asset(), amount2.asset());
283 ammSle->setFieldIssue(sfAsset,
STIssue{sfAsset, asset1});
284 ammSle->setFieldIssue(sfAsset2,
STIssue{sfAsset2, asset2});
289 if (
auto ter =
dirLink(sb, accountId, ammSle); ter)
291 JLOG(j.
debug()) <<
"AMM Instance: failed to insert owner dir";
300 JLOG(j.
debug()) <<
"AMM Instance: failed to send LPT " << lpTokens;
304 auto sendAndInitTrustOrMPT = [&](
STAmount const& amount) ->
TER {
306 return amount.asset().visit(
308 auto const& mptIssue = issue;
309 auto const& mptID = mptIssue.
getMptID();
340 auto const flags = sleRippleState->
getFlags();
341 sleRippleState->
setFieldU32(sfFlags, flags | lsfAMMNode);
342 sb.
update(sleRippleState);
349 res = sendAndInitTrustOrMPT(amount);
352 JLOG(j.
debug()) <<
"AMM Instance: failed to send " << amount;
357 res = sendAndInitTrustOrMPT(amount2);
360 JLOG(j.
debug()) <<
"AMM Instance: failed to send " << amount2;
364 JLOG(j.
debug()) <<
"AMM Instance: success " << accountId <<
" " << ammKeylet.key <<
" "
365 << lpTokens <<
" " << amount <<
" " << amount2;
367 Book const book{assetIn, assetOut, std::nullopt};
369 if (
auto const bookExisted =
static_cast<bool>(sb.
read(dir)); !bookExisted)
370 ctx.
registry.get().getOrderBookDB().addOrderBook(book);
372 addOrderBook(amount.asset(), amount2.asset(),
getRate(amount2, amount));
373 addOrderBook(amount2.asset(), amount.asset(),
getRate(amount, amount2));
A generic endpoint for log messages.
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
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.
constexpr MPTID const & getMptID() const
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual SLE::const_pointer 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.
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & const_ref
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)
AccountID const accountID_
void insert(SLE::ref sle) override
Insert a new state SLE.
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
void update(SLE::ref sle) override
Indicate changes to a peeked SLE.
SLE::const_pointer read(Keylet const &k) const override
Return the state item associated with a key.
Keylet quality(Keylet const &k, std::uint64_t q) noexcept
The initial directory page for a specific quality.
Keylet mptokenIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Keylet book(Book const &b)
The beginning of an order book.
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
NotTEC invalidAMMAmount(STAmount const &amount, std::optional< std::pair< Asset, Asset > > const &pair=std::nullopt, bool validZero=false)
Validate the amount.
constexpr std::uint16_t kTradingFeeThreshold
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 amendment is enabled.
bool isXRP(AccountID const &c)
STAmount ammLPTokens(STAmount const &asset1, STAmount const &asset2, Asset const &lptIssue)
Calculate LP Tokens given AMM pool reserves.
void initializeFeeAuctionVote(ApplyView &view, SLE::pointer &ammSle, AccountID const &account, Asset const &lptAsset, std::uint16_t tfee)
Initialize Auction and Voting slots and set the trading/discounted fee.
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
TER canMPTTradeAndTransfer(ReadView const &v, Asset const &asset, AccountID const &from, AccountID const &to)
Convenience to combine canTrade/Transfer.
std::expected< SLE::pointer, TER > createPseudoAccount(ApplyView &view, uint256 const &pseudoOwnerKey, SField const &ownerField)
Create pseudo-account, storing pseudoOwnerKey into ownerField.
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
static std::pair< TER, bool > applyCreate(ApplyContext &ctx, Sandbox &sb, AccountID const &account, beast::Journal j)
TERSubset< CanCvtToNotTEC > NotTEC
TER createMPToken(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, std::uint32_t const flags)
TER dirLink(ApplyView &view, AccountID const &owner, SLE::pointer &object, SF_UINT64 const &node=sfOwnerNode)
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No, AllowMPTOverflow allowOverflow=AllowMPTOverflow::No)
Calls static accountSendIOU if saAmount represents Issue.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, std::uint8_t depth=0)
Check if the account lacks required authorization for MPT.
Issue ammLPTIssue(Asset const &asset1, Asset const &asset2, AccountID const &ammAccountID)
Calculate LPT Issue from AMM asset pair.
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.