xrpld
Loading...
Searching...
No Matches
AMMInfo_test.cpp
1#include <test/jtx/AMM.h>
2#include <test/jtx/AMMTest.h>
3#include <test/jtx/Account.h>
4#include <test/jtx/Env.h>
5#include <test/jtx/amount.h>
6#include <test/jtx/flags.h>
7#include <test/jtx/mpt.h>
8
9#include <xrpl/beast/unit_test/suite.h>
10#include <xrpl/protocol/AccountID.h>
11#include <xrpl/protocol/Feature.h>
12#include <xrpl/protocol/Issue.h>
13#include <xrpl/protocol/TxFlags.h>
14#include <xrpl/protocol/UintTypes.h>
15#include <xrpl/protocol/XRPAmount.h>
16#include <xrpl/protocol/jss.h>
17
18#include <cstdint>
19#include <exception>
20#include <optional>
21#include <string>
22#include <tuple>
23#include <unordered_map>
24#include <unordered_set>
25#include <vector>
26
27namespace xrpl::test {
28
30{
31public:
32 void
34 {
35 testcase("Errors");
36
37 using namespace jtx;
38
39 Account const bogie("bogie");
40 enum class TestAccount { None, Alice, Bogie };
41 auto accountId = [&](AMM const& ammAlice, TestAccount v) -> std::optional<AccountID> {
42 if (v == TestAccount::Alice)
43 {
44 return ammAlice.ammAccount();
45 }
46 if (v == TestAccount::Bogie)
47 {
48 return bogie;
49 }
50
51 return std::nullopt;
52 };
53
54 // Invalid tokens pair
55 testAMM([&](AMM& ammAlice, Env&) {
56 Account const gw("gw");
57 auto const usd = gw["USD"];
58 auto const jv = ammAlice.ammRpcInfo({}, {}, usd, usd);
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
68 // Account is not a string
69 testAMM([&](AMM& ammAlice, Env&) {
70 auto const jv =
71 ammAlice.ammRpcInfo(json::Value{42}, std::nullopt, XRP, USD, std::nullopt, true, 3);
72 BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
73 });
74
75 // AMM Account is not a string
76 testAMM([&](AMM& ammAlice, Env&) {
77 auto const jv = ammAlice.ammRpcInfo(
78 json::Value{to_string(ammAlice.ammAccount())},
79 std::nullopt,
80 XRP,
81 USD,
82 json::Value{42},
83 false,
84 3);
85 BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
86 });
87
89 invalidParams = {
90 {xrpIssue(), std::nullopt, TestAccount::None, false},
91 {std::nullopt, USD, TestAccount::None, false},
92 {xrpIssue(), std::nullopt, TestAccount::Alice, false},
93 {std::nullopt, USD, TestAccount::Alice, false},
94 {xrpIssue(), USD, TestAccount::Alice, false},
95 {std::nullopt, std::nullopt, TestAccount::None, true}};
96
97 // Invalid parameters
98 testAMM([&](AMM& ammAlice, Env&) {
99 for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParams)
100 {
101 auto const jv = ammAlice.ammRpcInfo(
102 std::nullopt,
103 std::nullopt,
104 iss1,
105 iss2,
106 accountId(ammAlice, acct),
107 ignoreParams);
108 BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
109 }
110 });
111
112 // Invalid parameters *and* invalid LP account, default API version
113 testAMM([&](AMM& ammAlice, Env&) {
114 for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParams)
115 {
116 auto const jv = ammAlice.ammRpcInfo(
117 bogie, //
118 std::nullopt,
119 iss1,
120 iss2,
121 accountId(ammAlice, acct),
122 ignoreParams);
123 BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
124 }
125 });
126
127 // Invalid parameters *and* invalid LP account, API version 3
128 testAMM([&](AMM& ammAlice, Env&) {
129 for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParams)
130 {
131 auto const jv = ammAlice.ammRpcInfo(
132 bogie, //
133 std::nullopt,
134 iss1,
135 iss2,
136 accountId(ammAlice, acct),
137 ignoreParams,
138 3);
139 BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
140 }
141 });
142
143 // Invalid AMM account id
144 testAMM([&](AMM& ammAlice, Env&) {
145 auto const jv = ammAlice.ammRpcInfo(
146 std::nullopt, std::nullopt, std::nullopt, std::nullopt, bogie.id());
147 BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
148 });
149
151 invalidParamsBadAccount = {
152 {xrpIssue(), std::nullopt, TestAccount::None, false},
153 {std::nullopt, USD, TestAccount::None, false},
154 {xrpIssue(), std::nullopt, TestAccount::Bogie, false},
155 {std::nullopt, USD, TestAccount::Bogie, false},
156 {xrpIssue(), USD, TestAccount::Bogie, false},
157 {std::nullopt, std::nullopt, TestAccount::None, true}};
158
159 // Invalid parameters *and* invalid AMM account, default API version
160 testAMM([&](AMM& ammAlice, Env&) {
161 for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParamsBadAccount)
162 {
163 auto const jv = ammAlice.ammRpcInfo(
164 std::nullopt,
165 std::nullopt,
166 iss1,
167 iss2,
168 accountId(ammAlice, acct),
169 ignoreParams);
170 BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
171 }
172 });
173
174 // Invalid parameters *and* invalid AMM account, API version 3
175 testAMM([&](AMM& ammAlice, Env&) {
176 for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParamsBadAccount)
177 {
178 auto const jv = ammAlice.ammRpcInfo(
179 std::nullopt,
180 std::nullopt,
181 iss1,
182 iss2,
183 accountId(ammAlice, acct),
184 ignoreParams,
185 3);
186 BEAST_EXPECT(
187 jv[jss::error_message] ==
188 (acct == TestAccount::Bogie ? std::string("Account malformed.")
189 : std::string("Invalid parameters.")));
190 }
191 });
192 }
193
194 void
196 {
197 testcase("RPC simple");
198
199 using namespace jtx;
200 testAMM([&](AMM& ammAlice, Env&) {
201 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
202 XRP(10000),
203 USD(10000),
204 IOUAmount{10000000, 0},
205 std::nullopt,
206 std::nullopt,
207 ammAlice.ammAccount()));
208 });
209
210 {
211 Env env{*this};
212 env.fund(XRP(1'000), gw_);
213 MPTTester mptTester(env, gw_, {.fund = false});
214 mptTester.create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
215 MPTTester mptTester1(env, gw_, {.fund = false});
216 mptTester1.create({.flags = tfMPTCanTransfer | tfMPTCanTrade});
217 auto const mpt = mptTester["MPT"];
218 auto const mpt1 = mptTester1["MPT"];
220 {XRP(100), mpt(100), IOUAmount{100'000}},
221 {USD(100), mpt(100), IOUAmount{100}},
222 {mpt(100), mpt1(100), IOUAmount{100}}};
223 for (auto& pool : pools)
224 {
225 AMM const amm(env, gw_, std::get<0>(pool), std::get<1>(pool));
226 BEAST_EXPECT(amm.expectAmmRpcInfo(
227 std::get<0>(pool),
228 std::get<1>(pool),
229 std::get<2>(pool),
230 std::nullopt,
231 std::nullopt,
232 amm.ammAccount()));
233 }
234 }
235 }
236
237 void
239 {
240 testcase("Vote and Bid");
241
242 using namespace jtx;
243 testAMM(
244 [&](AMM& ammAlice, Env& env) {
245 BEAST_EXPECT(
246 ammAlice.expectAmmRpcInfo(XRP(10000), USD(10000), IOUAmount{10000000, 0}));
248 votes.insert({alice_.human(), 0});
249 for (int i = 0; i < 7; ++i)
250 {
252 votes.insert({a.human(), 50 * (i + 1)});
253 if (!features[fixAMMv1_3])
254 {
255 fund(env, gw_, {a}, {USD(10000)}, Fund::Acct);
256 }
257 else
258 {
259 fund(env, gw_, {a}, {USD(10001)}, Fund::Acct);
260 }
261 ammAlice.deposit(a, 10000000);
262 ammAlice.vote(a, 50 * (i + 1));
263 }
264 BEAST_EXPECT(ammAlice.expectTradingFee(175));
265 Account ed("ed");
266 Account bill("bill");
267 env.fund(XRP(1000), bob_, ed, bill);
268 env(ammAlice.bid({.bidMin = 100, .authAccounts = {carol_, bob_, ed, bill}}));
269 if (!features[fixAMMv1_3])
270 {
271 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
272 XRP(80000),
273 USD(80000),
274 IOUAmount{79994400},
275 std::nullopt,
276 std::nullopt,
277 ammAlice.ammAccount()));
278 }
279 else
280 {
281 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
282 XRPAmount(80000000005),
283 STAmount{USD, UINT64_C(80'000'00000000005), -11},
284 IOUAmount{79994400},
285 std::nullopt,
286 std::nullopt,
287 ammAlice.ammAccount()));
288 }
289 for (auto i = 0; i < 2; ++i)
290 {
291 std::unordered_set<std::string> authAccounts = {
292 carol_.human(), bob_.human(), ed.human(), bill.human()};
293 auto const ammInfo = i ? ammAlice.ammRpcInfo()
294 : ammAlice.ammRpcInfo(
295 std::nullopt,
296 std::nullopt,
297 std::nullopt,
298 std::nullopt,
299 ammAlice.ammAccount());
300 auto const& amm = ammInfo[jss::amm];
301 try
302 {
303 // votes
304 auto const voteSlots = amm[jss::vote_slots];
305 auto votesCopy = votes;
306 for (std::uint8_t i = 0; i < 8; ++i)
307 {
308 if (!BEAST_EXPECT(
309 votes[voteSlots[i][jss::account].asString()] ==
310 voteSlots[i][jss::trading_fee].asUInt() &&
311 voteSlots[i][jss::vote_weight].asUInt() == 12500))
312 return;
313 votes.erase(voteSlots[i][jss::account].asString());
314 }
315 if (!BEAST_EXPECT(votes.empty()))
316 return;
317 votes = votesCopy;
318
319 // bid
320 auto const auctionSlot = amm[jss::auction_slot];
321 for (std::uint8_t i = 0; i < 4; ++i)
322 {
323 if (!BEAST_EXPECT(authAccounts.contains(
324 auctionSlot[jss::auth_accounts][i][jss::account].asString())))
325 return;
326 authAccounts.erase(
327 auctionSlot[jss::auth_accounts][i][jss::account].asString());
328 }
329 if (!BEAST_EXPECT(authAccounts.empty()))
330 return;
331 BEAST_EXPECT(
332 auctionSlot[jss::account].asString() == alice_.human() &&
333 auctionSlot[jss::discounted_fee].asUInt() == 17 &&
334 auctionSlot[jss::price][jss::value].asString() == "5600" &&
335 auctionSlot[jss::price][jss::currency].asString() ==
336 to_string(ammAlice.lptIssue().currency) &&
337 auctionSlot[jss::price][jss::issuer].asString() ==
338 to_string(ammAlice.lptIssue().account));
339 }
340 catch (std::exception const& e)
341 {
342 fail(e.what(), __FILE__, __LINE__);
343 }
344 }
345 },
346 std::nullopt,
347 0,
348 std::nullopt,
349 {features});
350 }
351
352 void
354 {
355 using namespace jtx;
356 testAMM([&](AMM& ammAlice, Env& env) {
357 env(fset(gw_, asfGlobalFreeze));
358 env.close();
359 auto test = [&](bool freeze) {
360 auto const info = ammAlice.ammRpcInfo();
361 BEAST_EXPECT(info[jss::amm][jss::asset2_frozen].asBool() == freeze);
362 };
363 test(true);
364 env(fclear(gw_, asfGlobalFreeze));
365 env.close();
366 test(false);
367 });
368 }
369
370 void
372 {
373 using namespace jtx;
374 testcase("Invalid amm field");
375
376 testAMM([&](AMM& amm, Env&) {
377 auto const resp = amm.ammRpcInfo(
378 std::nullopt, jss::validated.cStr(), std::nullopt, std::nullopt, gw_);
379 BEAST_EXPECT(resp.isMember("error") && resp["error"] == "actNotFound");
380 });
381 }
382
383 void
384 run() override
385 {
386 using namespace jtx;
387 auto const all = testableAmendments();
388 testErrors();
390 testVoteAndBid(all);
391 testVoteAndBid(all - fixAMMv1_3);
392 testFreeze();
394 }
395};
396
398
399} // namespace xrpl::test
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:522
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Represents a JSON value.
Definition json_value.h:130
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:24
Currency currency
Definition Issue.h:15
AccountID account
Definition Issue.h:16
void run() override
Runs the suite.
void testVoteAndBid(FeatureBitset features)
jtx::Account const alice_
Definition AMMTest.h:74
void testAMM(std::function< void(jtx::AMM &, jtx::Env &)> const &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={testableAmendments()})
testAMM() funds 30,000XRP and 30,000IOU for each non-XRP asset to Alice and Carol
Definition AMMTest.cpp:120
jtx::Account const gw_
Definition AMMTest.h:72
jtx::Account const carol_
Definition AMMTest.h:73
jtx::Account const bob_
Definition AMMTest.h:75
Convenience class to test AMM functionality.
bool expectTradingFee(std::uint16_t fee) const
Definition AMM.cpp:340
bool expectAmmRpcInfo(STAmount const &asset1, STAmount const &asset2, IOUAmount const &balance, std::optional< AccountID > const &account=std::nullopt, std::optional< std::string > const &ledgerIndex=std::nullopt, std::optional< AccountID > const &ammAccount=std::nullopt) const
Definition AMM.cpp:354
json::Value bid(BidArg const &arg)
Definition AMM.cpp:737
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:466
AccountID const & ammAccount() const
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< Asset, Asset > > const &assets=std::nullopt, std::optional< Ter > const &ter=std::nullopt)
Definition AMM.cpp:711
json::Value ammRpcInfo(std::optional< AccountID > const &account=std::nullopt, std::optional< std::string > const &ledgerIndex=std::nullopt, std::optional< Asset > const &asset1=std::nullopt, std::optional< Asset > const &asset2=std::nullopt, std::optional< AccountID > const &ammAccount=std::nullopt, bool ignoreParams=false, unsigned apiVersion=RPC::kApiInvalidVersion) const
Send amm_info RPC command.
Definition AMM.cpp:183
Immutable cryptographic account descriptor.
Definition jtx/Account.h:17
std::string const & human() const
Returns the human readable public key.
Definition jtx/Account.h:92
AccountID id() const
Returns the Account ID.
Definition jtx/Account.h:85
A transaction testing environment.
Definition Env.h:143
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:296
Test helper for creating, mutating, and asserting MPT and confidential MPT ledger state.
Definition mpt.h:385
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:256
T contains(T... args)
T empty(T... args)
T erase(T... args)
T insert(T... args)
std::vector< STAmount > fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
Definition AMMTest.cpp:34
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:102
FeatureBitset testableAmendments()
Definition Env.h:76
json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:15
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
constexpr XRPAmount
Convert XRP to drops (integral types).
Definition TxTest.h:48
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:97
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
T to_string(T... args)
T what(T... args)