rippled
Loading...
Searching...
No Matches
AMMInfo_test.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 <test/jtx.h>
21#include <test/jtx/AMM.h>
22#include <test/jtx/AMMTest.h>
23
24#include <xrpl/protocol/jss.h>
25
26#include <unordered_map>
27
28namespace ripple {
29namespace test {
30
32{
33public:
34 void
36 {
37 testcase("Errors");
38
39 using namespace jtx;
40
41 Account const bogie("bogie");
42 enum TestAccount { None, Alice, Bogie };
43 auto accountId = [&](AMM const& ammAlice,
44 TestAccount v) -> std::optional<AccountID> {
45 if (v == Alice)
46 return ammAlice.ammAccount();
47 else if (v == Bogie)
48 return bogie;
49 else
50 return std::nullopt;
51 };
52
53 // Invalid tokens pair
54 testAMM([&](AMM& ammAlice, Env&) {
55 Account const gw("gw");
56 auto const USD = gw["USD"];
57 auto const jv =
58 ammAlice.ammRpcInfo({}, {}, USD.issue(), USD.issue());
59 BEAST_EXPECT(jv[jss::error_message] == "Account not found.");
60 });
61
62 // Invalid LP account id
63 testAMM([&](AMM& ammAlice, Env&) {
64 auto const jv = ammAlice.ammRpcInfo(bogie.id());
65 BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
66 });
67
71 TestAccount,
72 bool>> const invalidParams = {
73 {xrpIssue(), std::nullopt, None, false},
74 {std::nullopt, USD.issue(), None, false},
75 {xrpIssue(), std::nullopt, Alice, false},
76 {std::nullopt, USD.issue(), Alice, false},
77 {xrpIssue(), USD.issue(), Alice, false},
79
80 // Invalid parameters
81 testAMM([&](AMM& ammAlice, Env&) {
82 for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParams)
83 {
84 auto const jv = ammAlice.ammRpcInfo(
87 iss1,
88 iss2,
89 accountId(ammAlice, acct),
90 ignoreParams);
91 BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
92 }
93 });
94
95 // Invalid parameters *and* invalid LP account, default API version
96 testAMM([&](AMM& ammAlice, Env&) {
97 for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParams)
98 {
99 auto const jv = ammAlice.ammRpcInfo(
100 bogie, //
102 iss1,
103 iss2,
104 accountId(ammAlice, acct),
105 ignoreParams);
106 BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
107 }
108 });
109
110 // Invalid parameters *and* invalid LP account, API version 3
111 testAMM([&](AMM& ammAlice, Env&) {
112 for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParams)
113 {
114 auto const jv = ammAlice.ammRpcInfo(
115 bogie, //
117 iss1,
118 iss2,
119 accountId(ammAlice, acct),
120 ignoreParams,
121 3);
122 BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
123 }
124 });
125
126 // Invalid AMM account id
127 testAMM([&](AMM& ammAlice, Env&) {
128 auto const jv = ammAlice.ammRpcInfo(
133 bogie.id());
134 BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
135 });
136
140 TestAccount,
141 bool>> const invalidParamsBadAccount = {
142 {xrpIssue(), std::nullopt, None, false},
143 {std::nullopt, USD.issue(), None, false},
144 {xrpIssue(), std::nullopt, Bogie, false},
145 {std::nullopt, USD.issue(), Bogie, false},
146 {xrpIssue(), USD.issue(), Bogie, false},
147 {std::nullopt, std::nullopt, None, true}};
148
149 // Invalid parameters *and* invalid AMM account, default API version
150 testAMM([&](AMM& ammAlice, Env&) {
151 for (auto const& [iss1, iss2, acct, ignoreParams] :
152 invalidParamsBadAccount)
153 {
154 auto const jv = ammAlice.ammRpcInfo(
157 iss1,
158 iss2,
159 accountId(ammAlice, acct),
160 ignoreParams);
161 BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
162 }
163 });
164
165 // Invalid parameters *and* invalid AMM account, API version 3
166 testAMM([&](AMM& ammAlice, Env&) {
167 for (auto const& [iss1, iss2, acct, ignoreParams] :
168 invalidParamsBadAccount)
169 {
170 auto const jv = ammAlice.ammRpcInfo(
173 iss1,
174 iss2,
175 accountId(ammAlice, acct),
176 ignoreParams,
177 3);
178 BEAST_EXPECT(
179 jv[jss::error_message] ==
180 (acct == Bogie ? std::string("Account malformed.")
181 : std::string("Invalid parameters.")));
182 }
183 });
184 }
185
186 void
188 {
189 testcase("RPC simple");
190
191 using namespace jtx;
192 testAMM([&](AMM& ammAlice, Env&) {
193 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
194 XRP(10000), USD(10000), IOUAmount{10000000, 0}));
195 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
196 XRP(10000),
197 USD(10000),
198 IOUAmount{10000000, 0},
201 ammAlice.ammAccount()));
202 });
203 }
204
205 void
207 {
208 testcase("Vote and Bid");
209
210 using namespace jtx;
211 testAMM(
212 [&](AMM& ammAlice, Env& env) {
213 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
214 XRP(10000), USD(10000), IOUAmount{10000000, 0}));
216 votes.insert({alice.human(), 0});
217 for (int i = 0; i < 7; ++i)
218 {
220 votes.insert({a.human(), 50 * (i + 1)});
221 if (!features[fixAMMv1_3])
222 fund(env, gw, {a}, {USD(10000)}, Fund::Acct);
223 else
224 fund(env, gw, {a}, {USD(10001)}, Fund::Acct);
225 ammAlice.deposit(a, 10000000);
226 ammAlice.vote(a, 50 * (i + 1));
227 }
228 BEAST_EXPECT(ammAlice.expectTradingFee(175));
229 Account ed("ed");
230 Account bill("bill");
231 env.fund(XRP(1000), bob, ed, bill);
232 env(ammAlice.bid(
233 {.bidMin = 100, .authAccounts = {carol, bob, ed, bill}}));
234 if (!features[fixAMMv1_3])
235 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
236 XRP(80000),
237 USD(80000),
238 IOUAmount{79994400},
241 ammAlice.ammAccount()));
242 else
243 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
244 XRPAmount(80000000005),
245 STAmount{USD, UINT64_C(80'000'00000000005), -11},
246 IOUAmount{79994400},
249 ammAlice.ammAccount()));
250 for (auto i = 0; i < 2; ++i)
251 {
252 std::unordered_set<std::string> authAccounts = {
253 carol.human(), bob.human(), ed.human(), bill.human()};
254 auto const ammInfo = i ? ammAlice.ammRpcInfo()
255 : ammAlice.ammRpcInfo(
260 ammAlice.ammAccount());
261 auto const& amm = ammInfo[jss::amm];
262 try
263 {
264 // votes
265 auto const voteSlots = amm[jss::vote_slots];
266 auto votesCopy = votes;
267 for (std::uint8_t i = 0; i < 8; ++i)
268 {
269 if (!BEAST_EXPECT(
270 votes[voteSlots[i][jss::account]
271 .asString()] ==
272 voteSlots[i][jss::trading_fee]
273 .asUInt() &&
274 voteSlots[i][jss::vote_weight].asUInt() ==
275 12500))
276 return;
277 votes.erase(voteSlots[i][jss::account].asString());
278 }
279 if (!BEAST_EXPECT(votes.empty()))
280 return;
281 votes = votesCopy;
282
283 // bid
284 auto const auctionSlot = amm[jss::auction_slot];
285 for (std::uint8_t i = 0; i < 4; ++i)
286 {
287 if (!BEAST_EXPECT(authAccounts.contains(
288 auctionSlot[jss::auth_accounts][i]
289 [jss::account]
290 .asString())))
291 return;
292 authAccounts.erase(
293 auctionSlot[jss::auth_accounts][i][jss::account]
294 .asString());
295 }
296 if (!BEAST_EXPECT(authAccounts.empty()))
297 return;
298 BEAST_EXPECT(
299 auctionSlot[jss::account].asString() ==
300 alice.human() &&
301 auctionSlot[jss::discounted_fee].asUInt() == 17 &&
302 auctionSlot[jss::price][jss::value].asString() ==
303 "5600" &&
304 auctionSlot[jss::price][jss::currency].asString() ==
305 to_string(ammAlice.lptIssue().currency) &&
306 auctionSlot[jss::price][jss::issuer].asString() ==
307 to_string(ammAlice.lptIssue().account));
308 }
309 catch (std::exception const& e)
310 {
311 fail(e.what(), __FILE__, __LINE__);
312 }
313 }
314 },
316 0,
318 {features});
319 }
320
321 void
323 {
324 using namespace jtx;
325 testAMM([&](AMM& ammAlice, Env& env) {
326 env(fset(gw, asfGlobalFreeze));
327 env.close();
328 auto test = [&](bool freeze) {
329 auto const info = ammAlice.ammRpcInfo();
330 BEAST_EXPECT(
331 info[jss::amm][jss::asset2_frozen].asBool() == freeze);
332 };
333 test(true);
334 env(fclear(gw, asfGlobalFreeze));
335 env.close();
336 test(false);
337 });
338 }
339
340 void
342 {
343 using namespace jtx;
344 testcase("Invalid amm field");
345
346 testAMM([&](AMM& amm, Env&) {
347 auto const resp = amm.ammRpcInfo(
349 jss::validated.c_str(),
352 gw);
353 BEAST_EXPECT(
354 resp.isMember("error") && resp["error"] == "actNotFound");
355 });
356 }
357
358 void
359 run() override
360 {
361 using namespace jtx;
362 auto const all = testable_amendments();
363 testErrors();
364 testSimpleRpc();
365 testVoteAndBid(all);
366 testVoteAndBid(all - fixAMMv1_3);
367 testFreeze();
368 testInvalidAmmField();
369 }
370};
371
372BEAST_DEFINE_TESTSUITE(AMMInfo, rpc, ripple);
373
374} // namespace test
375} // namespace ripple
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:155
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:533
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:46
AccountID account
Definition Issue.h:36
Currency currency
Definition Issue.h:35
void testVoteAndBid(FeatureBitset features)
void run() override
Runs the suite.
jtx::Account const alice
Definition AMMTest.h:77
jtx::Account const gw
Definition AMMTest.h:75
jtx::Account const bob
Definition AMMTest.h:78
void testAMM(std::function< void(jtx::AMM &, jtx::Env &)> &&cb, std::optional< std::pair< STAmount, STAmount > > const &pool=std::nullopt, std::uint16_t tfee=0, std::optional< jtx::ter > const &ter=std::nullopt, std::vector< FeatureBitset > const &features={testable_amendments()})
testAMM() funds 30,000XRP and 30,000IOU for each non-XRP asset to Alice and Carol
Definition AMMTest.cpp:103
jtx::Account const carol
Definition AMMTest.h:76
Convenience class to test AMM functionality.
Definition AMM.h:124
Json::Value ammRpcInfo(std::optional< AccountID > const &account=std::nullopt, std::optional< std::string > const &ledgerIndex=std::nullopt, std::optional< Issue > issue1=std::nullopt, std::optional< Issue > issue2=std::nullopt, std::optional< AccountID > const &ammAccount=std::nullopt, bool ignoreParams=false, unsigned apiVersion=RPC::apiInvalidVersion) const
Send amm_info RPC command.
Definition AMM.cpp:166
void vote(std::optional< Account > const &account, std::uint32_t feeVal, std::optional< std::uint32_t > const &flags=std::nullopt, std::optional< jtx::seq > const &seq=std::nullopt, std::optional< std::pair< Issue, Issue > > const &assets=std::nullopt, std::optional< ter > const &ter=std::nullopt)
Definition AMM.cpp:642
AccountID const & ammAccount() const
Definition AMM.h:331
bool expectTradingFee(std::uint16_t fee) const
Definition AMM.cpp:317
Issue lptIssue() const
Definition AMM.h:337
IOUAmount deposit(std::optional< Account > const &account, LPToken tokens, std::optional< STAmount > const &asset1InDetails=std::nullopt, std::optional< std::uint32_t > const &flags=std::nullopt, std::optional< ter > const &ter=std::nullopt)
Definition AMM.cpp:416
Json::Value bid(BidArg const &arg)
Definition AMM.cpp:669
bool expectAmmRpcInfo(STAmount const &asset1, STAmount const &asset2, IOUAmount const &balance, std::optional< AccountID > const &account=std::nullopt, std::optional< std::string > const &ledger_index=std::nullopt, std::optional< AccountID > const &ammAccount=std::nullopt) const
Definition AMM.cpp:333
Immutable cryptographic account descriptor.
Definition Account.h:39
AccountID id() const
Returns the Account ID.
Definition Account.h:111
std::string const & human() const
Returns the human readable public key.
Definition Account.h:118
A transaction testing environment.
Definition Env.h:121
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:121
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:289
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:35
T contains(T... args)
T empty(T... args)
T erase(T... args)
T insert(T... args)
T is_same_v
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:121
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:29
void fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
Definition AMMTest.cpp:37
FeatureBitset testable_amendments()
Definition Env.h:74
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:111
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:115
constexpr std::uint32_t asfGlobalFreeze
Definition TxFlags.h:83
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
T to_string(T... args)
T what(T... args)