rippled
Loading...
Searching...
No Matches
AMMVote.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2023 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/misc/AMMUtils.h>
21#include <xrpld/app/tx/detail/AMMVote.h>
22
23#include <xrpl/ledger/Sandbox.h>
24#include <xrpl/protocol/AMMCore.h>
25#include <xrpl/protocol/Feature.h>
26#include <xrpl/protocol/TxFlags.h>
27
28namespace ripple {
29
30bool
35
38{
39 if (auto const res = invalidAMMAssetPair(
40 ctx.tx[sfAsset].get<Issue>(), ctx.tx[sfAsset2].get<Issue>()))
41 {
42 JLOG(ctx.j.debug()) << "AMM Vote: invalid asset pair.";
43 return res;
44 }
45
46 if (ctx.tx[sfTradingFee] > TRADING_FEE_THRESHOLD)
47 {
48 JLOG(ctx.j.debug()) << "AMM Vote: invalid trading fee.";
49 return temBAD_FEE;
50 }
51
52 return tesSUCCESS;
53}
54
55TER
57{
58 if (auto const ammSle =
59 ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
60 !ammSle)
61 {
62 JLOG(ctx.j.debug()) << "AMM Vote: Invalid asset pair.";
63 return terNO_AMM;
64 }
65 else if (ammSle->getFieldAmount(sfLPTokenBalance) == beast::zero)
66 return tecAMM_EMPTY;
67 else if (auto const lpTokensNew =
68 ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
69 lpTokensNew == beast::zero)
70 {
71 JLOG(ctx.j.debug()) << "AMM Vote: account is not LP.";
73 }
74
75 return tesSUCCESS;
76}
77
80 ApplyContext& ctx_,
81 Sandbox& sb,
82 AccountID const& account_,
84{
85 auto const feeNew = ctx_.tx[sfTradingFee];
86 auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
87 if (!ammSle)
88 return {tecINTERNAL, false};
89 STAmount const lptAMMBalance = (*ammSle)[sfLPTokenBalance];
90 auto const lpTokensNew = ammLPHolds(sb, *ammSle, account_, ctx_.journal);
92 std::size_t minPos{0};
93 AccountID minAccount{0};
94 std::uint32_t minFee{0};
95 STArray updatedVoteSlots;
96 Number num{0};
97 Number den{0};
98 // Account already has vote entry
99 bool foundAccount = false;
100
101 // Iterate over the current vote entries and update each entry
102 // per current total tokens balance and each LP tokens balance.
103 // Find the entry with the least tokens and whether the account
104 // has the vote entry.
105 for (auto const& entry : ammSle->getFieldArray(sfVoteSlots))
106 {
107 auto const account = entry[sfAccount];
108 auto lpTokens = ammLPHolds(sb, *ammSle, account, ctx_.journal);
109 if (lpTokens == beast::zero)
110 {
111 JLOG(j_.debug())
112 << "AMMVote::applyVote, account " << account << " is not LP";
113 continue;
114 }
115 auto feeVal = entry[sfTradingFee];
116 STObject newEntry = STObject::makeInnerObject(sfVoteEntry);
117 // The account already has the vote entry.
118 if (account == account_)
119 {
120 lpTokens = lpTokensNew;
121 feeVal = feeNew;
122 foundAccount = true;
123 }
124 // Keep running numerator/denominator to calculate the updated fee.
125 num += feeVal * lpTokens;
126 den += lpTokens;
127 newEntry.setAccountID(sfAccount, account);
128 if (feeVal != 0)
129 newEntry.setFieldU16(sfTradingFee, feeVal);
130 newEntry.setFieldU32(
131 sfVoteWeight,
132 static_cast<std::int64_t>(
133 Number(lpTokens) * VOTE_WEIGHT_SCALE_FACTOR / lptAMMBalance));
134
135 // Find an entry with the least tokens/fee. Make the order deterministic
136 // if the tokens/fees are equal.
137 if (!minTokens ||
138 (lpTokens < *minTokens ||
139 (lpTokens == *minTokens &&
140 (feeVal < minFee || (feeVal == minFee && account < minAccount)))))
141 {
142 minTokens = lpTokens;
143 minPos = updatedVoteSlots.size();
144 minAccount = account;
145 minFee = feeVal;
146 }
147 updatedVoteSlots.push_back(std::move(newEntry));
148 }
149
150 // The account doesn't have the vote entry.
151 if (!foundAccount)
152 {
153 auto update = [&](std::optional<std::uint8_t> const& minPos =
154 std::nullopt) {
155 STObject newEntry = STObject::makeInnerObject(sfVoteEntry);
156 if (feeNew != 0)
157 newEntry.setFieldU16(sfTradingFee, feeNew);
158 newEntry.setFieldU32(
159 sfVoteWeight,
160 static_cast<std::int64_t>(
161 Number(lpTokensNew) * VOTE_WEIGHT_SCALE_FACTOR /
162 lptAMMBalance));
163 newEntry.setAccountID(sfAccount, account_);
164 num += feeNew * lpTokensNew;
165 den += lpTokensNew;
166 if (minPos)
167 *(updatedVoteSlots.begin() + *minPos) = std::move(newEntry);
168 else
169 updatedVoteSlots.push_back(std::move(newEntry));
170 };
171 // Add new entry if the number of the vote entries
172 // is less than Max.
173 if (updatedVoteSlots.size() < VOTE_MAX_SLOTS)
174 update();
175 // Add the entry if the account has more tokens than
176 // the least token holder or same tokens and higher fee.
177 else if (
178 lpTokensNew > *minTokens ||
179 (lpTokensNew == *minTokens && feeNew > minFee))
180 {
181 auto const entry = updatedVoteSlots.begin() + minPos;
182 // Remove the least token vote entry.
183 num -= Number((*entry)[~sfTradingFee].value_or(0)) * *minTokens;
184 den -= *minTokens;
185 update(minPos);
186 }
187 // All slots are full and the account does not hold more LPTokens.
188 // Update anyway to refresh the slots.
189 else
190 {
191 JLOG(j_.debug()) << "AMMVote::applyVote, insufficient tokens to "
192 "override other votes";
193 }
194 }
195
196 XRPL_ASSERT(
197 !ctx_.view().rules().enabled(fixInnerObjTemplate) ||
198 ammSle->isFieldPresent(sfAuctionSlot),
199 "ripple::applyVote : has auction slot");
200
201 // Update the vote entries and the trading/discounted fee.
202 ammSle->setFieldArray(sfVoteSlots, updatedVoteSlots);
203 if (auto const fee = static_cast<std::int64_t>(num / den))
204 {
205 ammSle->setFieldU16(sfTradingFee, fee);
206 if (ammSle->isFieldPresent(sfAuctionSlot))
207 {
208 auto& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
209 if (auto const discountedFee =
211 auctionSlot.setFieldU16(sfDiscountedFee, discountedFee);
212 else if (auctionSlot.isFieldPresent(sfDiscountedFee))
213 auctionSlot.makeFieldAbsent(sfDiscountedFee);
214 }
215 }
216 else
217 {
218 if (ammSle->isFieldPresent(sfTradingFee))
219 ammSle->makeFieldAbsent(sfTradingFee);
220 if (ammSle->isFieldPresent(sfAuctionSlot))
221 {
222 auto& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
223 if (auctionSlot.isFieldPresent(sfDiscountedFee))
224 auctionSlot.makeFieldAbsent(sfDiscountedFee);
225 }
226 }
227 sb.update(ammSle);
228
229 return {tesSUCCESS, true};
230}
231
232TER
234{
235 // This is the ledger view that we work against. Transactions are applied
236 // as we go on processing transactions.
237 Sandbox sb(&ctx_.view());
238
239 auto const result = applyVote(ctx_, sb, account_, j_);
240 if (result.second)
241 sb.apply(ctx_.rawView());
242
243 return result.first;
244}
245
246} // namespace ripple
A generic endpoint for log messages.
Definition Journal.h:60
Stream debug() const
Definition Journal.h:328
static NotTEC preflight(PreflightContext const &ctx)
Definition AMMVote.cpp:37
static TER preclaim(PreclaimContext const &ctx)
Definition AMMVote.cpp:56
TER doApply() override
Definition AMMVote.cpp:233
static bool checkExtraFeatures(PreflightContext const &ctx)
Definition AMMVote.cpp:31
State information when applying a tx.
ApplyView & view()
beast::Journal const journal
A currency issued by an account.
Definition Issue.h:33
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:130
void push_back(STObject const &object)
Definition STArray.h:212
iterator begin()
Definition STArray.h:224
size_type size() const
Definition STArray.h:248
void setFieldU16(SField const &field, std::uint16_t)
Definition STObject.cpp:751
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:95
void setAccountID(SField const &field, AccountID const &)
Definition STObject.cpp:793
void setFieldU32(SField const &field, std::uint32_t)
Definition STObject.cpp:757
Discardable, editable view to a ledger.
Definition Sandbox.h:35
void apply(RawView &to)
Definition Sandbox.h:55
AccountID const account_
Definition Transactor.h:147
beast::Journal const j_
Definition Transactor.h:145
ApplyContext & ctx_
Definition Transactor.h:143
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:446
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
static std::pair< TER, bool > applyVote(ApplyContext &ctx_, Sandbox &sb, AccountID const &account_, beast::Journal j_)
Definition AMMVote.cpp:79
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
Definition AMMCore.cpp:129
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:113
@ tecAMM_EMPTY
Definition TER.h:333
@ tecINTERNAL
Definition TER.h:311
@ tecAMM_INVALID_TOKENS
Definition TER.h:332
std::uint32_t constexpr VOTE_WEIGHT_SCALE_FACTOR
Definition AMMCore.h:45
@ tesSUCCESS
Definition TER.h:245
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt)
Definition AMMCore.cpp:80
std::uint16_t constexpr VOTE_MAX_SLOTS
Definition AMMCore.h:44
@ terNO_AMM
Definition TER.h:227
TERSubset< CanCvtToTER > TER
Definition TER.h:649
std::uint16_t constexpr TRADING_FEE_THRESHOLD
Definition AMMCore.h:31
std::uint32_t constexpr AUCTION_SLOT_DISCOUNTED_FEE_FRACTION
Definition AMMCore.h:38
@ temBAD_FEE
Definition TER.h:92
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:80
ReadView const & view
Definition Transactor.h:83
beast::Journal const j
Definition Transactor.h:88
State information when preflighting a tx.
Definition Transactor.h:35
beast::Journal const j
Definition Transactor.h:42