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