xrpld
Loading...
Searching...
No Matches
LoanMisc_test.cpp
1#include <test/app/lending/LoanTestBase.h>
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/amount.h>
6#include <test/jtx/fee.h>
7#include <test/jtx/mpt.h>
8#include <test/jtx/noop.h>
9#include <test/jtx/offer.h>
10#include <test/jtx/pay.h>
11#include <test/jtx/seq.h>
12#include <test/jtx/sig.h>
13#include <test/jtx/tags.h>
14#include <test/jtx/ter.h>
15
16#include <xrpl/basics/Number.h>
17#include <xrpl/beast/unit_test/suite.h>
18#include <xrpl/beast/xor_shift_engine.h>
19#include <xrpl/json/json_value.h>
20#include <xrpl/json/to_string.h>
21#include <xrpl/protocol/Feature.h>
22#include <xrpl/protocol/Indexes.h>
23#include <xrpl/protocol/KeyType.h>
24#include <xrpl/protocol/SField.h>
25#include <xrpl/protocol/SeqProxy.h>
26#include <xrpl/protocol/TER.h>
27#include <xrpl/protocol/TxFlags.h>
28#include <xrpl/protocol/Units.h>
29#include <xrpl/protocol/jss.h>
30
31#include <algorithm>
32#include <cstddef>
33#include <cstdint>
34#include <random>
35#include <string>
36
37namespace xrpl::test {
38
40{
41private:
42 void
44 {
45 // This will expand as more test cases are added. Some functionality
46 // is tested in other test functions.
47 testcase("RPC");
48
49 using namespace jtx;
50
51 Env env(*this, features);
52
53 auto lowerFee = [&]() {
54 // Run the local fee back down.
55 while (env.app().getFeeTrack().lowerLocalFee())
56 ;
57 };
58
59 auto const baseFee = env.current()->fees().base;
60
61 Account const alice{"alice"};
62 std::string const borrowerPass = "borrower";
63 Account const borrower{borrowerPass, KeyType::Ed25519};
64 auto const lenderPass = "lender";
65 Account const lender{lenderPass, KeyType::Ed25519};
66
67 env.fund(XRP(1'000'000), alice, lender, borrower);
68 env.close();
69 env(noop(lender));
70 env(noop(lender));
71 env(noop(lender));
72 env(noop(lender));
73 env(noop(lender));
74 env.close();
75
76 {
77 testcase("RPC AccountSet");
79 txJson[sfTransactionType] = "AccountSet";
80 txJson[sfAccount] = borrower.human();
81
82 auto const signParams = [&]() {
84 signParams[jss::passphrase] = borrowerPass;
85 signParams[jss::key_type] = "ed25519";
86 signParams[jss::tx_json] = txJson;
87 return signParams;
88 }();
89 auto const jSign = env.rpc("json", "sign", to_string(signParams));
90 BEAST_EXPECT(jSign.isMember(jss::result) && jSign[jss::result].isMember(jss::tx_json));
91 auto txSignResult = jSign[jss::result][jss::tx_json];
92 auto txSignBlob = jSign[jss::result][jss::tx_blob].asString();
93 txSignResult.removeMember(jss::hash);
94
95 auto const jtx = env.jt(txJson, Sig(borrower));
96 BEAST_EXPECT(txSignResult == jtx.jv);
97
98 lowerFee();
99 auto const jSubmit = env.rpc("submit", txSignBlob);
100 BEAST_EXPECT(
101 jSubmit.isMember(jss::result) &&
102 jSubmit[jss::result].isMember(jss::engine_result) &&
103 jSubmit[jss::result][jss::engine_result].asString() == "tesSUCCESS");
104
105 lowerFee();
106 env(jtx.jv, Sig(kNone), Seq(kNone), Fee(kNone), Ter(tefPAST_SEQ));
107 }
108
109 {
110 testcase("RPC LoanSet - illegal signature_target");
111
113 txJson[sfTransactionType] = "AccountSet";
114 txJson[sfAccount] = borrower.human();
115
116 auto const borrowerSignParams = [&]() {
118 params[jss::passphrase] = borrowerPass;
119 params[jss::key_type] = "ed25519";
120 params[jss::signature_target] = "Destination";
121 params[jss::tx_json] = txJson;
122 return params;
123 }();
124 auto const jSignBorrower = env.rpc("json", "sign", to_string(borrowerSignParams));
125 BEAST_EXPECT(
126 jSignBorrower.isMember(jss::result) &&
127 jSignBorrower[jss::result].isMember(jss::error) &&
128 jSignBorrower[jss::result][jss::error] == "invalidParams" &&
129 jSignBorrower[jss::result].isMember(jss::error_message) &&
130 jSignBorrower[jss::result][jss::error_message] == "Destination");
131 }
132 {
133 testcase("RPC LoanSet - sign and submit borrower initiated");
134 // 1. Borrower creates the transaction
136 txJson[sfTransactionType] = "LoanSet";
137 txJson[sfAccount] = borrower.human();
138 txJson[sfCounterparty] = lender.human();
139 txJson[sfLoanBrokerID] =
140 "FF924CD18A236C2B49CF8E80A351CEAC6A10171DC9F110025646894FEC"
141 "F83F"
142 "5C";
143 txJson[sfPrincipalRequested] = "100000000";
144 txJson[sfPaymentTotal] = 10000;
145 txJson[sfPaymentInterval] = 3600;
146 txJson[sfGracePeriod] = 300;
147 txJson[sfFlags] = 65536; // tfLoanOverpayment
148 txJson[sfFee] = to_string(24 * baseFee / 10);
149
150 // 2. Borrower signs the transaction
151 auto const borrowerSignParams = [&]() {
153 params[jss::passphrase] = borrowerPass;
154 params[jss::key_type] = "ed25519";
155 params[jss::tx_json] = txJson;
156 return params;
157 }();
158 auto const jSignBorrower = env.rpc("json", "sign", to_string(borrowerSignParams));
159 BEAST_EXPECTS(
160 jSignBorrower.isMember(jss::result) &&
161 jSignBorrower[jss::result].isMember(jss::tx_json),
162 to_string(jSignBorrower));
163 auto const txBorrowerSignResult = jSignBorrower[jss::result][jss::tx_json];
164 auto const txBorrowerSignBlob = jSignBorrower[jss::result][jss::tx_blob].asString();
165
166 // 2a. Borrower attempts to submit the transaction. It doesn't
167 // work
168 {
169 lowerFee();
170 auto const jSubmitBlob = env.rpc("submit", txBorrowerSignBlob);
171 BEAST_EXPECT(jSubmitBlob.isMember(jss::result));
172 auto const jSubmitBlobResult = jSubmitBlob[jss::result];
173 BEAST_EXPECT(jSubmitBlobResult.isMember(jss::tx_json));
174 // Transaction fails because the CounterpartySignature is
175 // missing
176 BEAST_EXPECT(
177 jSubmitBlobResult.isMember(jss::engine_result) &&
178 jSubmitBlobResult[jss::engine_result].asString() == "temBAD_SIGNER");
179 }
180
181 // 3. Borrower sends the signed transaction to the lender
182 // 4. Lender signs the transaction
183 auto const lenderSignParams = [&]() {
185 params[jss::passphrase] = lenderPass;
186 params[jss::key_type] = "ed25519";
187 params[jss::signature_target] = "CounterpartySignature";
188 params[jss::tx_json] = txBorrowerSignResult;
189 return params;
190 }();
191 auto const jSignLender = env.rpc("json", "sign", to_string(lenderSignParams));
192 BEAST_EXPECT(
193 jSignLender.isMember(jss::result) &&
194 jSignLender[jss::result].isMember(jss::tx_json));
195 auto const txLenderSignResult = jSignLender[jss::result][jss::tx_json];
196 auto const txLenderSignBlob = jSignLender[jss::result][jss::tx_blob].asString();
197
198 // 5. Lender submits the signed transaction blob
199 lowerFee();
200 auto const jSubmitBlob = env.rpc("submit", txLenderSignBlob);
201 BEAST_EXPECT(jSubmitBlob.isMember(jss::result));
202 auto const jSubmitBlobResult = jSubmitBlob[jss::result];
203 BEAST_EXPECT(jSubmitBlobResult.isMember(jss::tx_json));
204 auto const jSubmitBlobTx = jSubmitBlobResult[jss::tx_json];
205 // To get far enough to return tecNO_ENTRY means that the
206 // signatures all validated. Of course the transaction won't
207 // succeed because no Vault or Broker were created.
208 BEAST_EXPECTS(
209 jSubmitBlobResult.isMember(jss::engine_result) &&
210 jSubmitBlobResult[jss::engine_result].asString() == "tecNO_ENTRY",
211 to_string(jSubmitBlobResult));
212
213 BEAST_EXPECT(
214 !jSubmitBlob.isMember(jss::error) && !jSubmitBlobResult.isMember(jss::error));
215
216 // 4-alt. Lender submits the transaction json originally
217 // received from the Borrower. It gets signed, but is now a
218 // duplicate, so fails. Borrower could done this instead of
219 // steps 4 and 5.
220 lowerFee();
221 auto const jSubmitJson = env.rpc("json", "submit", to_string(lenderSignParams));
222 BEAST_EXPECT(jSubmitJson.isMember(jss::result));
223 auto const jSubmitJsonResult = jSubmitJson[jss::result];
224 BEAST_EXPECT(jSubmitJsonResult.isMember(jss::tx_json));
225 auto const jSubmitJsonTx = jSubmitJsonResult[jss::tx_json];
226 // Since the previous tx claimed a fee, this duplicate is not
227 // going anywhere
228 BEAST_EXPECTS(
229 jSubmitJsonResult.isMember(jss::engine_result) &&
230 jSubmitJsonResult[jss::engine_result].asString() == "tefPAST_SEQ",
231 to_string(jSubmitJsonResult));
232
233 BEAST_EXPECT(
234 !jSubmitJson.isMember(jss::error) && !jSubmitJsonResult.isMember(jss::error));
235
236 BEAST_EXPECT(jSubmitBlobTx == jSubmitJsonTx);
237 }
238
239 {
240 testcase("RPC LoanSet - sign and submit lender initiated");
241 // 1. Lender creates the transaction
243 txJson[sfTransactionType] = "LoanSet";
244 txJson[sfAccount] = lender.human();
245 txJson[sfCounterparty] = borrower.human();
246 txJson[sfLoanBrokerID] =
247 "FF924CD18A236C2B49CF8E80A351CEAC6A10171DC9F110025646894FEC"
248 "F83F"
249 "5C";
250 txJson[sfPrincipalRequested] = "100000000";
251 txJson[sfPaymentTotal] = 10000;
252 txJson[sfPaymentInterval] = 3600;
253 txJson[sfGracePeriod] = 300;
254 txJson[sfFlags] = 65536; // tfLoanOverpayment
255 txJson[sfFee] = to_string(24 * baseFee / 10);
256
257 // 2. Lender signs the transaction
258 auto const lenderSignParams = [&]() {
260 params[jss::passphrase] = lenderPass;
261 params[jss::key_type] = "ed25519";
262 params[jss::tx_json] = txJson;
263 return params;
264 }();
265 auto const jSignLender = env.rpc("json", "sign", to_string(lenderSignParams));
266 BEAST_EXPECT(
267 jSignLender.isMember(jss::result) &&
268 jSignLender[jss::result].isMember(jss::tx_json));
269 auto const txLenderSignResult = jSignLender[jss::result][jss::tx_json];
270 auto const txLenderSignBlob = jSignLender[jss::result][jss::tx_blob].asString();
271
272 // 2a. Lender attempts to submit the transaction. It doesn't
273 // work
274 {
275 lowerFee();
276 auto const jSubmitBlob = env.rpc("submit", txLenderSignBlob);
277 BEAST_EXPECT(jSubmitBlob.isMember(jss::result));
278 auto const jSubmitBlobResult = jSubmitBlob[jss::result];
279 BEAST_EXPECT(jSubmitBlobResult.isMember(jss::tx_json));
280 // Transaction fails because the CounterpartySignature is
281 // missing
282 BEAST_EXPECT(
283 jSubmitBlobResult.isMember(jss::engine_result) &&
284 jSubmitBlobResult[jss::engine_result].asString() == "temBAD_SIGNER");
285 }
286
287 // 3. Lender sends the signed transaction to the Borrower
288 // 4. Borrower signs the transaction
289 auto const borrowerSignParams = [&]() {
291 params[jss::passphrase] = borrowerPass;
292 params[jss::key_type] = "ed25519";
293 params[jss::signature_target] = "CounterpartySignature";
294 params[jss::tx_json] = txLenderSignResult;
295 return params;
296 }();
297 auto const jSignBorrower = env.rpc("json", "sign", to_string(borrowerSignParams));
298 BEAST_EXPECT(
299 jSignBorrower.isMember(jss::result) &&
300 jSignBorrower[jss::result].isMember(jss::tx_json));
301 auto const txBorrowerSignResult = jSignBorrower[jss::result][jss::tx_json];
302 auto const txBorrowerSignBlob = jSignBorrower[jss::result][jss::tx_blob].asString();
303
304 // 5. Borrower submits the signed transaction blob
305 lowerFee();
306 auto const jSubmitBlob = env.rpc("submit", txBorrowerSignBlob);
307 BEAST_EXPECT(jSubmitBlob.isMember(jss::result));
308 auto const jSubmitBlobResult = jSubmitBlob[jss::result];
309 BEAST_EXPECT(jSubmitBlobResult.isMember(jss::tx_json));
310 auto const jSubmitBlobTx = jSubmitBlobResult[jss::tx_json];
311 // To get far enough to return tecNO_ENTRY means that the
312 // signatures all validated. Of course the transaction won't
313 // succeed because no Vault or Broker were created.
314 BEAST_EXPECTS(
315 jSubmitBlobResult.isMember(jss::engine_result) &&
316 jSubmitBlobResult[jss::engine_result].asString() == "tecNO_ENTRY",
317 to_string(jSubmitBlobResult));
318
319 BEAST_EXPECT(
320 !jSubmitBlob.isMember(jss::error) && !jSubmitBlobResult.isMember(jss::error));
321
322 // 4-alt. Borrower submits the transaction json originally
323 // received from the Lender. It gets signed, but is now a
324 // duplicate, so fails. Lender could done this instead of steps
325 // 4 and 5.
326 lowerFee();
327 auto const jSubmitJson = env.rpc("json", "submit", to_string(borrowerSignParams));
328 BEAST_EXPECT(jSubmitJson.isMember(jss::result));
329 auto const jSubmitJsonResult = jSubmitJson[jss::result];
330 BEAST_EXPECT(jSubmitJsonResult.isMember(jss::tx_json));
331 auto const jSubmitJsonTx = jSubmitJsonResult[jss::tx_json];
332 // Since the previous tx claimed a fee, this duplicate is not
333 // going anywhere
334 BEAST_EXPECTS(
335 jSubmitJsonResult.isMember(jss::engine_result) &&
336 jSubmitJsonResult[jss::engine_result].asString() == "tefPAST_SEQ",
337 to_string(jSubmitJsonResult));
338
339 BEAST_EXPECT(
340 !jSubmitJson.isMember(jss::error) && !jSubmitJsonResult.isMember(jss::error));
341
342 BEAST_EXPECT(jSubmitBlobTx == jSubmitJsonTx);
343 }
344 }
345
346 void
348 {
349 testcase("Lending: CanTrade disabled has no impact");
350 using namespace jtx;
351 using namespace loan;
352 using namespace loan_broker;
353
354 Env env(*this, all_);
355
356 Account const issuer{"issuer"};
357 Account const lender{"lender"};
358 Account const borrower{"borrower"};
359
360 env.fund(XRP(1'000'000), issuer, lender, borrower);
361 env.close();
362
363 MPTTester mpt(
364 {.env = env,
365 .issuer = issuer,
366 .holders = {lender, borrower},
367 .flags = tfMPTCanTransfer | tfMPTCanLock});
368 PrettyAsset const asset = mpt.issuanceID();
369 env(pay(issuer, lender, asset(10'000'000)));
370 env(pay(issuer, borrower, asset(100'000)));
371 env.close();
372
373 auto const broker = createVaultAndBroker(env, asset, lender);
374
375 // CanTrade is not set
376 env(offer(lender, XRP(1), asset(10)), Ter{tecNO_PERMISSION});
377 env.close();
378
379 auto const loanSetFee = Fee(env.current()->fees().base * 2);
380
381 // New cover deposits still work.
382 env(coverDeposit(lender, broker.brokerID, asset(100)));
383 env.close();
384
385 // New loan issuance still works.
386 env(loan::set(borrower, broker.brokerID, 1'000),
387 Sig(sfCounterpartySignature, lender),
388 loanSetFee);
389 env.close();
390 auto const loanKeylet = keylet::loan(broker.brokerID, SeqProxy::rawSequence(1));
391 BEAST_EXPECT(env.le(loanKeylet));
392
393 // Repayment still works.
394 env(pay(borrower, loanKeylet.key, asset(1'000)));
395 env.close();
396
397 // Cover withdrawal still works.
398 env(coverWithdraw(lender, broker.brokerID, asset(100)));
399 env.close();
400
401 // Enable CanTrade and verify the DEX path is restored.
402 mpt.set({.flags = tfMPTSetCanTrade});
403 env.close();
404
405 env(offer(lender, XRP(1), asset(10)));
406 env.close();
407 }
408
409 void
414
415 // Tests run under each entry in amendmentCombinations().
416 void
418 {
419 testRPC(features);
420 }
421
422public:
423 void
424 run() override
425 {
427 for (auto const& features : jtx::amendmentCombinations(
428 {fixCleanup3_1_3, fixCleanup3_2_0, featureMPTokensV2}, all_))
429 runAmendmentSensitive(features);
430 }
431};
432
434{
435protected:
437
445 /*
446 # Generate parameters that are more likely to be valid
447 principal = Decimal(str(rand.randint(100000,
448 100'000'000))).quantize(ROUND_TARGET)
449
450 interest_rate = Decimal(rand.randint(1, 10000)) /
451 Decimal(100000)
452
453 payment_total = rand.randint(12, 10000)
454
455 payment_interval = Decimal(str(rand.randint(60, 2629746)))
456
457 interest_fee = Decimal(rand.randint(0, 100000)) /
458 Decimal(100000)
459*/
460
461 void
463 {
464 using namespace jtx;
465
466 Account const issuer("issuer");
467 Account const lender("lender");
468 Account const borrower("borrower");
469
470 // Determine all the random parameters at once
471 auto const assetType = static_cast<AssetType>(assetDist_(engine_));
472 auto const principalRequest = principalDist_(engine_);
473 TenthBips16 const managementFeeRate{managementFeeRateDist_(engine_)};
474 auto const serviceFee = serviceFeeDist_(engine_);
476 auto payTotal = paymentTotalDist_(engine_);
477 auto const payInterval = paymentIntervalDist_(engine_);
478 // The end of the last payment's grace period must fit in a 32-bit
479 // ripple-epoch timestamp, or LoanSet fails with tecKILLED. Cap the
480 // schedule well below that horizon (2e9 seconds is roughly 63 years,
481 // leaving ample headroom over the ledger start date).
482 constexpr std::uint32_t kMaxScheduleSeconds = 2'000'000'000;
483 payTotal = std::min(payTotal, static_cast<int>(kMaxScheduleSeconds / payInterval));
484
485 BrokerParameters const brokerParams{
486 .vaultDeposit = principalRequest * 10,
487 .debtMax = 0,
488 .coverRateMin = TenthBips32{0},
489 .managementFeeRate = managementFeeRate,
490 .coverRateLiquidation = TenthBips32{0}};
491 LoanParameters const loanParams{
492 .account = lender,
493 .counter = borrower,
494 .principalRequest = principalRequest,
495 .serviceFee = serviceFee,
496 .interest = interest,
497 .payTotal = payTotal,
498 .payInterval = payInterval,
499 };
500
501 runLoan(assetType, brokerParams, loanParams, all_);
502 }
503
504public:
505 void
506 run() override
507 {
508 auto const numIterations = [s = arg()]() -> int {
509 int const defaultNum = 5;
510 if (s.empty())
511 return defaultNum;
512 try
513 {
514 std::size_t pos = 0;
515 auto const r = stoi(s, &pos);
516 if (pos != s.size())
517 return defaultNum;
518 return r;
519 }
520 catch (...)
521 {
522 return defaultNum;
523 }
524 }();
525
526 using namespace jtx;
527
528 auto const updateInterval = std::max(std::min(numIterations / 5, 100), 1);
529
530 for (int i = 0; i < numIterations; ++i)
531 {
532 if (i % updateInterval == 0)
533 testcase << "Random Loan Test iteration " << (i + 1) << "/" << numIterations;
535 }
536 }
537};
540{
541 void
542 run() override
543 {
544 using namespace jtx;
545
546 BrokerParameters const brokerParams{
547 .vaultDeposit = 10000,
548 .debtMax = 0,
549 .coverRateMin = TenthBips32{0},
550 .managementFeeRate = TenthBips16{0},
551 .coverRateLiquidation = TenthBips32{0}};
552 LoanParameters const loanParams{
553 .account = Account("lender"),
554 .counter = Account("borrower"),
555 .principalRequest = Number{200000, -6},
556 .interest = TenthBips32{50000},
557 .payTotal = 2,
558 .payInterval = 200};
559
560 runLoan(AssetType::XRP, brokerParams, loanParams, all_);
561 }
562};
566BEAST_DEFINE_TESTSUITE_MANUAL(LoanArbitrary, tx, xrpl);
567
568} // namespace xrpl::test
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
std::string const & arg() const
Return the argument associated with the runner.
Definition suite.h:292
Represents a JSON value.
Definition json_value.h:117
Number is a floating point type that can represent a wide range of values.
Definition Number.h:351
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition SeqProxy.h:62
virtual LoadFeeTrack & getFeeTrack()=0
void run() override
Runs the suite.
beast::xor_shift_engine engine_
std::uniform_int_distribution paymentIntervalDist_
std::uniform_int_distribution paymentTotalDist_
std::uniform_int_distribution< std::int64_t > principalDist_
void run() override
Runs the suite.
std::uniform_int_distribution< std::uint16_t > managementFeeRateDist_
std::uniform_int_distribution serviceFeeDist_
std::uniform_int_distribution< std::uint32_t > interestRateDist_
std::uniform_int_distribution assetDist_
void runAmendmentSensitive(FeatureBitset features)
void run() override
Runs the suite.
void testRPC(FeatureBitset features)
FeatureBitset const all_
void runLoan(AssetType assetType, BrokerParameters const &brokerParams, LoanParameters const &loanParams, FeatureBitset features)
BrokerInfo createVaultAndBroker(jtx::Env &env, jtx::PrettyAsset const &asset, jtx::Account const &lender, BrokerParameters const &params=BrokerParameters::defaults())
Immutable cryptographic account descriptor.
Definition jtx/Account.h:21
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
Definition Env.h:161
Application & app()
Definition Env.h:300
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
SLE::const_pointer le(Account const &account) const
Return an account root.
Definition Env.cpp:311
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:323
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:1056
JTx jt(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
Definition Env.h:721
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:377
Set the fee on a JTx.
Definition fee.h:20
Test helper for creating, mutating, and asserting MPT and confidential MPT ledger state.
Definition mpt.h:447
Set the regular signature on a JTx.
Definition sig.h:19
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:18
T max(T... args)
T min(T... args)
detail::XorShiftEngine<> xor_shift_engine
XOR-shift Generator.
@ Object
object value (collection of name/value pairs).
Definition json_value.h:29
Keylet loan(uint256 const &loanBrokerID, SeqProxy const &loanSeq) noexcept
Definition Indexes.cpp:573
json::Value set(AccountID const &account, uint256 const &loanBrokerID, Number principalRequested, std::uint32_t flags)
static NoneT const kNone
Definition tags.h:9
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
std::vector< FeatureBitset > amendmentCombinations(std::initializer_list< uint256 > features, FeatureBitset seed)
Returns all 2^N permutations of a seed FeatureBitset with each subset of the given features excluded.
Definition Env.h:123
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
json::Value noop(Account const &account)
The null transaction.
Definition noop.h:14
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:14
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
BEAST_DEFINE_TESTSUITE_MANUAL(AMMCalc, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ tefPAST_SEQ
Definition TER.h:167
TenthBips< std::uint32_t > TenthBips32
Definition Units.h:454
TenthBips< std::uint16_t > TenthBips16
Definition Units.h:453
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
@ tecNO_PERMISSION
Definition TER.h:308
Set the sequence number on a JTx.
Definition seq.h:16