rippled
Loading...
Searching...
No Matches
GatewayBalances_test.cpp
1#include <test/jtx.h>
2#include <test/jtx/WSClient.h>
3
4#include <xrpl/beast/unit_test.h>
5#include <xrpl/protocol/Feature.h>
6#include <xrpl/protocol/jss.h>
7
8namespace xrpl {
9namespace test {
10
12{
13public:
14 void
16 {
17 using namespace std::chrono_literals;
18 using namespace jtx;
19 Env env(*this, features);
20
21 {
22 // Gateway account and assets
23 Account const alice{"alice"};
24 env.fund(XRP(10000), "alice");
25 auto USD = alice["USD"];
26 auto CNY = alice["CNY"];
27 auto JPY = alice["JPY"];
28
29 // Create a hotwallet
30 Account const hw{"hw"};
31 env.fund(XRP(10000), "hw");
32 env.close();
33 env(trust(hw, USD(10000)));
34 env(trust(hw, JPY(10000)));
35 env(pay(alice, hw, USD(5000)));
36 env(pay(alice, hw, JPY(5000)));
37
38 // Create some clients
39 Account const bob{"bob"};
40 env.fund(XRP(10000), "bob");
41 env.close();
42 env(trust(bob, USD(100)));
43 env(trust(bob, CNY(100)));
44 env(pay(alice, bob, USD(50)));
45
46 Account const charley{"charley"};
47 env.fund(XRP(10000), "charley");
48 env.close();
49 env(trust(charley, CNY(500)));
50 env(trust(charley, JPY(500)));
51 env(pay(alice, charley, CNY(250)));
52 env(pay(alice, charley, JPY(250)));
53
54 Account const dave{"dave"};
55 env.fund(XRP(10000), "dave");
56 env.close();
57 env(trust(dave, CNY(100)));
58 env(pay(alice, dave, CNY(30)));
59
60 // give the gateway an asset
61 env(trust(alice, charley["USD"](50)));
62 env(pay(charley, alice, USD(10)));
63
64 // freeze dave
65 env(trust(alice, dave["CNY"](0), dave, tfSetFreeze));
66
67 env.close();
68
69 auto wsc = makeWSClient(env.app().config());
70
71 Json::Value qry;
72 qry[jss::account] = alice.human();
73 qry[jss::hotwallet] = hw.human();
74
75 auto jv = wsc->invoke("gateway_balances", qry);
76 expect(jv[jss::status] == "success");
77 if (wsc->version() == 2)
78 {
79 expect(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
80 expect(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
81 expect(jv.isMember(jss::id) && jv[jss::id] == 5);
82 }
83
84 auto const& result = jv[jss::result];
85 expect(result[jss::account] == alice.human());
86 expect(result[jss::status] == "success");
87
88 {
89 auto const& balances = result[jss::balances];
90 expect(balances.isObject(), "balances is not an object");
91 expect(balances.size() == 1, "balances size is not 1");
92
93 auto const& hwBalance = balances[hw.human()];
94 expect(hwBalance.isArray(), "hwBalance is not an array");
95 expect(hwBalance.size() == 2);
96 auto c1 = hwBalance[0u][jss::currency];
97 auto c2 = hwBalance[1u][jss::currency];
98 expect(c1 == "USD" || c2 == "USD");
99 expect(c1 == "JPY" || c2 == "JPY");
100 expect(hwBalance[0u][jss::value] == "5000" && hwBalance[1u][jss::value] == "5000");
101 }
102
103 {
104 auto const& fBalances = result[jss::frozen_balances];
105 expect(fBalances.isObject());
106 expect(fBalances.size() == 1);
107
108 auto const& fBal = fBalances[dave.human()];
109 expect(fBal.isArray());
110 expect(fBal.size() == 1);
111 expect(fBal[0u].isObject());
112 expect(fBal[0u][jss::currency] == "CNY");
113 expect(fBal[0u][jss::value] == "30");
114 }
115
116 {
117 auto const& assets = result[jss::assets];
118 expect(assets.isObject(), "assets it not an object");
119 expect(assets.size() == 1, "assets size is not 1");
120
121 auto const& cAssets = assets[charley.human()];
122 expect(cAssets.isArray());
123 expect(cAssets.size() == 1);
124 expect(cAssets[0u][jss::currency] == "USD");
125 expect(cAssets[0u][jss::value] == "10");
126 }
127
128 {
129 auto const& obligations = result[jss::obligations];
130 expect(obligations.isObject(), "obligations is not an object");
131 expect(obligations.size() == 3);
132 expect(obligations["CNY"] == "250");
133 expect(obligations["JPY"] == "250");
134 expect(obligations["USD"] == "50");
135 }
136 }
137 }
138
139 void
141 {
142 using namespace std::chrono_literals;
143 using namespace jtx;
144 Env env(*this, features);
145
146 // Gateway account and assets
147 Account const alice{"alice"};
148 env.fund(XRP(10000), alice);
149 Account const hw{"hw"};
150 env.fund(XRP(10000), hw);
151 env.close();
152
153 auto wsc = makeWSClient(env.app().config());
154
155 Json::Value qry2;
156 qry2[jss::account] = alice.human();
157 qry2[jss::hotwallet] = "asdf";
158
159 forAllApiVersions([&, this](unsigned apiVersion) {
160 qry2[jss::api_version] = apiVersion;
161 auto jv = wsc->invoke("gateway_balances", qry2);
162 expect(jv[jss::status] == "error");
163
164 auto response = jv[jss::result];
165 auto const error = apiVersion < 2u ? "invalidHotWallet" : "invalidParams";
166 BEAST_EXPECT(response[jss::error] == error);
167 });
168 }
169
170 void
172 {
173 using namespace std::chrono_literals;
174 using namespace jtx;
175 Env env(*this);
176
177 // Gateway account and assets
178 Account const alice{"alice"};
179 env.fund(XRP(10000), alice);
180 env.close();
181 auto USD = alice["USD"];
182
183 // The largest valid STAmount of USD:
184 STAmount const maxUSD(USD.issue(), STAmount::cMaxValue, STAmount::cMaxOffset);
185
186 // Create a hotwallet
187 Account const hw{"hw"};
188 env.fund(XRP(10000), hw);
189 env.close();
190 env(trust(hw, maxUSD));
191 env.close();
192 env(pay(alice, hw, maxUSD));
193
194 // Create some clients
195 Account const bob{"bob"};
196 env.fund(XRP(10000), bob);
197 env.close();
198 env(trust(bob, maxUSD));
199 env.close();
200 env(pay(alice, bob, maxUSD));
201
202 Account const charley{"charley"};
203 env.fund(XRP(10000), charley);
204 env.close();
205 env(trust(charley, maxUSD));
206 env.close();
207 env(pay(alice, charley, maxUSD));
208
209 env.close();
210
211 auto wsc = makeWSClient(env.app().config());
212
213 Json::Value query;
214 query[jss::account] = alice.human();
215 query[jss::hotwallet] = hw.human();
216
217 // Note that the sum of bob's and charley's USD balances exceeds
218 // the amount that can be represented in an STAmount. Nevertheless
219 // we get a valid "obligations" that shows the maximum valid
220 // STAmount.
221 auto jv = wsc->invoke("gateway_balances", query);
222 expect(jv[jss::status] == "success");
223 expect(jv[jss::result][jss::obligations]["USD"] == maxUSD.getText());
224 }
225
226 void
228 {
229 testcase("Gateway Balances with MPT Escrow");
230 using namespace std::chrono_literals;
231 using namespace jtx;
232
233 // Ensure MPT is enabled
234 FeatureBitset const features = testable_amendments() | featureMPTokensV1;
235 Env env(*this, features);
236
237 Account const alice{"alice"};
238 Account const bob{"bob"};
239
240 env.fund(XRP(10000), alice, bob);
241 env.close();
242
243 // Create MPT issuance (Alice) with Escrow capability
244 MPTTester mpt(env, alice, {.holders = {bob}, .fund = false});
245 mpt.create({.flags = tfMPTCanEscrow});
246
247 // Authorize Bob and fund him
248 mpt.authorize({.account = bob, .holderCount = 1});
249 mpt.pay(alice, bob, 1000);
250
251 // Bob creates an escrow of MPT to Alice.
252 auto const MPT = mpt["MPT"];
253 env(escrow::create(bob, alice, MPT(100)), escrow::finish_time(env.now() + 10s));
254 env.close();
255
256 // Query gateway_balances for Bob.
257 auto wsc = makeWSClient(env.app().config());
258 Json::Value qry;
259 qry[jss::account] = bob.human();
260
261 auto jv = wsc->invoke("gateway_balances", qry);
262 expect(jv[jss::status] == "success");
263 }
264
265 void
266 run() override
267 {
268 using namespace jtx;
269 auto const sa = testable_amendments();
270 for (auto feature : {sa - featurePermissionedDEX, sa})
271 {
272 testGWB(feature);
273 testGWBApiVersions(feature);
274 }
277 }
278};
279
280BEAST_DEFINE_TESTSUITE(GatewayBalances, rpc, xrpl);
281
282} // namespace test
283} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:150
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition suite.h:224
virtual Config & config()=0
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:51
std::string getText() const override
Definition STAmount.cpp:656
static int const cMaxOffset
Definition STAmount.h:46
void testGWB(FeatureBitset features)
void run() override
Runs the suite.
void testGWBApiVersions(FeatureBitset features)
Immutable cryptographic account descriptor.
Definition Account.h:19
A transaction testing environment.
Definition Env.h:122
Application & app()
Definition Env.h:259
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:100
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:270
NetClock::time_point now()
Returns the current network time.
Definition Env.h:282
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:138
Converts to MPT Issue or STAmount.
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
auto const finish_time
Set the "FinishAfter" time tag on a JTx.
Definition escrow.h:73
Json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount)
Definition escrow.cpp:14
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:95
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:78
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Definition WSClient.cpp:296
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void forAllApiVersions(Fn const &fn, Args &&... args)
Definition ApiVersion.h:149