rippled
Loading...
Searching...
No Matches
AMMVote.cpp
1#include <xrpld/app/misc/AMMUtils.h>
2#include <xrpld/app/tx/detail/AMMVote.h>
3
4#include <xrpl/ledger/Sandbox.h>
5#include <xrpl/protocol/AMMCore.h>
6#include <xrpl/protocol/Feature.h>
7#include <xrpl/protocol/TxFlags.h>
8
9namespace xrpl {
10
11bool
16
19{
20 if (auto const res = invalidAMMAssetPair(ctx.tx[sfAsset].get<Issue>(), ctx.tx[sfAsset2].get<Issue>()))
21 {
22 JLOG(ctx.j.debug()) << "AMM Vote: invalid asset pair.";
23 return res;
24 }
25
26 if (ctx.tx[sfTradingFee] > TRADING_FEE_THRESHOLD)
27 {
28 JLOG(ctx.j.debug()) << "AMM Vote: invalid trading fee.";
29 return temBAD_FEE;
30 }
31
32 return tesSUCCESS;
33}
34
35TER
37{
38 if (auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2])); !ammSle)
39 {
40 JLOG(ctx.j.debug()) << "AMM Vote: Invalid asset pair.";
41 return terNO_AMM;
42 }
43 else if (ammSle->getFieldAmount(sfLPTokenBalance) == beast::zero)
44 return tecAMM_EMPTY;
45 else if (auto const lpTokensNew = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
46 lpTokensNew == beast::zero)
47 {
48 JLOG(ctx.j.debug()) << "AMM Vote: account is not LP.";
50 }
51
52 return tesSUCCESS;
53}
54
56applyVote(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Journal j_)
57{
58 auto const feeNew = ctx_.tx[sfTradingFee];
59 auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
60 if (!ammSle)
61 return {tecINTERNAL, false};
62 STAmount const lptAMMBalance = (*ammSle)[sfLPTokenBalance];
63 auto const lpTokensNew = ammLPHolds(sb, *ammSle, account_, ctx_.journal);
65 std::size_t minPos{0};
66 AccountID minAccount{0};
67 std::uint32_t minFee{0};
68 STArray updatedVoteSlots;
69 Number num{0};
70 Number den{0};
71 // Account already has vote entry
72 bool foundAccount = false;
73
74 // Iterate over the current vote entries and update each entry
75 // per current total tokens balance and each LP tokens balance.
76 // Find the entry with the least tokens and whether the account
77 // has the vote entry.
78 for (auto const& entry : ammSle->getFieldArray(sfVoteSlots))
79 {
80 auto const account = entry[sfAccount];
81 auto lpTokens = ammLPHolds(sb, *ammSle, account, ctx_.journal);
82 if (lpTokens == beast::zero)
83 {
84 JLOG(j_.debug()) << "AMMVote::applyVote, account " << account << " is not LP";
85 continue;
86 }
87 auto feeVal = entry[sfTradingFee];
88 STObject newEntry = STObject::makeInnerObject(sfVoteEntry);
89 // The account already has the vote entry.
90 if (account == account_)
91 {
92 lpTokens = lpTokensNew;
93 feeVal = feeNew;
94 foundAccount = true;
95 }
96 // Keep running numerator/denominator to calculate the updated fee.
97 num += feeVal * lpTokens;
98 den += lpTokens;
99 newEntry.setAccountID(sfAccount, account);
100 if (feeVal != 0)
101 newEntry.setFieldU16(sfTradingFee, feeVal);
102 newEntry.setFieldU32(
103 sfVoteWeight, static_cast<std::int64_t>(Number(lpTokens) * VOTE_WEIGHT_SCALE_FACTOR / lptAMMBalance));
104
105 // Find an entry with the least tokens/fee. Make the order deterministic
106 // if the tokens/fees are equal.
107 if (!minTokens ||
108 (lpTokens < *minTokens ||
109 (lpTokens == *minTokens && (feeVal < minFee || (feeVal == minFee && account < minAccount)))))
110 {
111 minTokens = lpTokens;
112 minPos = updatedVoteSlots.size();
113 minAccount = account;
114 minFee = feeVal;
115 }
116 updatedVoteSlots.push_back(std::move(newEntry));
117 }
118
119 // The account doesn't have the vote entry.
120 if (!foundAccount)
121 {
122 auto update = [&](std::optional<std::uint8_t> const& minPos = std::nullopt) {
123 STObject newEntry = STObject::makeInnerObject(sfVoteEntry);
124 if (feeNew != 0)
125 newEntry.setFieldU16(sfTradingFee, feeNew);
126 newEntry.setFieldU32(
127 sfVoteWeight,
128 static_cast<std::int64_t>(Number(lpTokensNew) * VOTE_WEIGHT_SCALE_FACTOR / lptAMMBalance));
129 newEntry.setAccountID(sfAccount, account_);
130 num += feeNew * lpTokensNew;
131 den += lpTokensNew;
132 if (minPos)
133 *(updatedVoteSlots.begin() + *minPos) = std::move(newEntry);
134 else
135 updatedVoteSlots.push_back(std::move(newEntry));
136 };
137 // Add new entry if the number of the vote entries
138 // is less than Max.
139 if (updatedVoteSlots.size() < VOTE_MAX_SLOTS)
140 update();
141 // Add the entry if the account has more tokens than
142 // the least token holder or same tokens and higher fee.
143 else if (lpTokensNew > *minTokens || (lpTokensNew == *minTokens && feeNew > minFee))
144 {
145 auto const entry = updatedVoteSlots.begin() + minPos;
146 // Remove the least token vote entry.
147 num -= Number((*entry)[~sfTradingFee].value_or(0)) * *minTokens;
148 den -= *minTokens;
149 update(minPos);
150 }
151 // All slots are full and the account does not hold more LPTokens.
152 // Update anyway to refresh the slots.
153 else
154 {
155 JLOG(j_.debug()) << "AMMVote::applyVote, insufficient tokens to "
156 "override other votes";
157 }
158 }
159
160 XRPL_ASSERT(
161 !ctx_.view().rules().enabled(fixInnerObjTemplate) || ammSle->isFieldPresent(sfAuctionSlot),
162 "xrpl::applyVote : has auction slot");
163
164 // Update the vote entries and the trading/discounted fee.
165 ammSle->setFieldArray(sfVoteSlots, updatedVoteSlots);
166 if (auto const fee = static_cast<std::int64_t>(num / den))
167 {
168 ammSle->setFieldU16(sfTradingFee, fee);
169 if (ammSle->isFieldPresent(sfAuctionSlot))
170 {
171 auto& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
172 if (auto const discountedFee = fee / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION)
173 auctionSlot.setFieldU16(sfDiscountedFee, discountedFee);
174 else if (auctionSlot.isFieldPresent(sfDiscountedFee))
175 auctionSlot.makeFieldAbsent(sfDiscountedFee);
176 }
177 }
178 else
179 {
180 if (ammSle->isFieldPresent(sfTradingFee))
181 ammSle->makeFieldAbsent(sfTradingFee);
182 if (ammSle->isFieldPresent(sfAuctionSlot))
183 {
184 auto& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
185 if (auctionSlot.isFieldPresent(sfDiscountedFee))
186 auctionSlot.makeFieldAbsent(sfDiscountedFee);
187 }
188 }
189 sb.update(ammSle);
190
191 return {tesSUCCESS, true};
192}
193
194TER
196{
197 // This is the ledger view that we work against. Transactions are applied
198 // as we go on processing transactions.
199 Sandbox sb(&ctx_.view());
200
201 auto const result = applyVote(ctx_, sb, account_, j_);
202 if (result.second)
203 sb.apply(ctx_.rawView());
204
205 return result.first;
206}
207
208} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:41
Stream debug() const
Definition Journal.h:301
static NotTEC preflight(PreflightContext const &ctx)
Definition AMMVote.cpp:18
static TER preclaim(PreclaimContext const &ctx)
Definition AMMVote.cpp:36
static bool checkExtraFeatures(PreflightContext const &ctx)
Definition AMMVote.cpp:12
TER doApply() override
Definition AMMVote.cpp:195
State information when applying a tx.
STTx const & tx
beast::Journal const journal
RawView & rawView()
ApplyView & view()
A currency issued by an account.
Definition Issue.h:14
Number is a floating point type that can represent a wide range of values.
Definition Number.h:208
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.
Definition Rules.cpp:118
void push_back(STObject const &object)
Definition STArray.h:188
size_type size() const
Definition STArray.h:224
iterator begin()
Definition STArray.h:200
void setFieldU32(SField const &field, std::uint32_t)
Definition STObject.cpp:718
void setFieldU16(SField const &field, std::uint16_t)
Definition STObject.cpp:712
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:72
void setAccountID(SField const &field, AccountID const &)
Definition STObject.cpp:754
Discardable, editable view to a ledger.
Definition Sandbox.h:16
void apply(RawView &to)
Definition Sandbox.h:36
AccountID const account_
Definition Transactor.h:113
beast::Journal const j_
Definition Transactor.h:111
ApplyContext & ctx_
Definition Transactor.h:109
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.
T is_same_v
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:393
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
@ terNO_AMM
Definition TER.h:208
std::uint16_t constexpr TRADING_FEE_THRESHOLD
Definition AMMCore.h:12
static std::pair< TER, bool > applyVote(ApplyContext &ctx_, Sandbox &sb, AccountID const &account_, beast::Journal j_)
Definition AMMVote.cpp:56
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
Definition AMMCore.cpp:95
std::uint32_t constexpr VOTE_WEIGHT_SCALE_FACTOR
Definition AMMCore.h:25
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt)
Definition AMMCore.cpp:55
std::uint32_t constexpr AUCTION_SLOT_DISCOUNTED_FEE_FRACTION
Definition AMMCore.h:19
TERSubset< CanCvtToTER > TER
Definition TER.h:621
std::uint16_t constexpr VOTE_MAX_SLOTS
Definition AMMCore.h:24
@ temBAD_FEE
Definition TER.h:73
@ tecAMM_EMPTY
Definition TER.h:314
@ tecAMM_INVALID_TOKENS
Definition TER.h:313
@ tecINTERNAL
Definition TER.h:292
@ tesSUCCESS
Definition TER.h:226
STAmount ammLPHolds(ReadView const &view, Currency const &cur1, Currency const &cur2, AccountID const &ammAccount, AccountID const &lpAccount, beast::Journal const j)
Get the balance of LP tokens.
Definition AMMUtils.cpp:80
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:54
ReadView const & view
Definition Transactor.h:57
beast::Journal const j
Definition Transactor.h:62
State information when preflighting a tx.
Definition Transactor.h:16
beast::Journal const j
Definition Transactor.h:23