mirror of
https://github.com/XRPLF/clio.git
synced 2025-12-06 17:27:58 +00:00
feat: Support Permissioned DEX (#2152)
Fix: https://github.com/XRPLF/clio/issues/2143 Will add tests
This commit is contained in:
@@ -278,7 +278,7 @@ TransactionFeed::pub(
|
||||
ripple::Book const book{
|
||||
data->getFieldAmount(ripple::sfTakerGets).issue(),
|
||||
data->getFieldAmount(ripple::sfTakerPays).issue(),
|
||||
std::nullopt
|
||||
(*data)[~ripple::sfDomainID]
|
||||
};
|
||||
if (affectedBooks.find(book) == affectedBooks.end()) {
|
||||
affectedBooks.insert(book);
|
||||
|
||||
@@ -62,6 +62,7 @@ struct BookChange {
|
||||
ripple::STAmount lowRate;
|
||||
ripple::STAmount openRate;
|
||||
ripple::STAmount closeRate;
|
||||
std::optional<ripple::uint256> domain;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -145,11 +146,15 @@ private:
|
||||
auto const deltaPays =
|
||||
finalFields.getFieldAmount(ripple::sfTakerPays) - previousFields.getFieldAmount(ripple::sfTakerPays);
|
||||
|
||||
transformAndStore(deltaGets, deltaPays);
|
||||
transformAndStore(deltaGets, deltaPays, finalFields[~ripple::sfDomainID]);
|
||||
}
|
||||
|
||||
void
|
||||
transformAndStore(ripple::STAmount const& deltaGets, ripple::STAmount const& deltaPays)
|
||||
transformAndStore(
|
||||
ripple::STAmount const& deltaGets,
|
||||
ripple::STAmount const& deltaPays,
|
||||
std::optional<ripple::uint256> const& domain
|
||||
)
|
||||
{
|
||||
auto const g = to_string(deltaGets.issue());
|
||||
auto const p = to_string(deltaPays.issue());
|
||||
@@ -189,6 +194,7 @@ private:
|
||||
entry.lowRate = rate;
|
||||
|
||||
entry.closeRate = rate;
|
||||
entry.domain = domain;
|
||||
} else {
|
||||
tally_[key] = {
|
||||
.sideAVolume = first,
|
||||
@@ -197,6 +203,7 @@ private:
|
||||
.lowRate = rate,
|
||||
.openRate = rate,
|
||||
.closeRate = rate,
|
||||
.domain = domain,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1309,7 +1309,13 @@ postProcessOrderBook(
|
||||
|
||||
// get book via currency type
|
||||
std::expected<ripple::Book, Status>
|
||||
parseBook(ripple::Currency pays, ripple::AccountID payIssuer, ripple::Currency gets, ripple::AccountID getIssuer)
|
||||
parseBook(
|
||||
ripple::Currency pays,
|
||||
ripple::AccountID payIssuer,
|
||||
ripple::Currency gets,
|
||||
ripple::AccountID getIssuer,
|
||||
std::optional<std::string> const& domain
|
||||
)
|
||||
{
|
||||
if (isXRP(pays) && !isXRP(payIssuer)) {
|
||||
return std::unexpected{Status{
|
||||
@@ -1338,7 +1344,15 @@ parseBook(ripple::Currency pays, ripple::AccountID payIssuer, ripple::Currency g
|
||||
if (pays == gets && payIssuer == getIssuer)
|
||||
return std::unexpected{Status{RippledError::rpcBAD_MARKET, "badMarket"}};
|
||||
|
||||
return ripple::Book{{pays, payIssuer}, {gets, getIssuer}, std::nullopt};
|
||||
std::optional<ripple::uint256> domainID = std::nullopt;
|
||||
if (domain.has_value()) {
|
||||
ripple::uint256 dom;
|
||||
if (!dom.parseHex(*domain))
|
||||
return std::unexpected{Status{RippledError::rpcDOMAIN_MALFORMED}};
|
||||
domainID = dom;
|
||||
}
|
||||
|
||||
return ripple::Book{{pays, payIssuer}, {gets, getIssuer}, domainID};
|
||||
}
|
||||
|
||||
std::expected<ripple::Book, Status>
|
||||
@@ -1373,6 +1387,9 @@ parseBook(boost::json::object const& request)
|
||||
}};
|
||||
}
|
||||
|
||||
if (request.contains("domain") && !request.at("domain").is_string())
|
||||
return std::unexpected{Status{RippledError::rpcDOMAIN_MALFORMED}};
|
||||
|
||||
ripple::Currency payCurrency;
|
||||
if (!ripple::to_currency(payCurrency, boost::json::value_to<std::string>(takerPays.at("currency"))))
|
||||
return std::unexpected{Status{RippledError::rpcSRC_CUR_MALFORMED}};
|
||||
@@ -1446,7 +1463,15 @@ parseBook(boost::json::object const& request)
|
||||
if (payCurrency == getCurrency && payIssuer == getIssuer)
|
||||
return std::unexpected{Status{RippledError::rpcBAD_MARKET, "badMarket"}};
|
||||
|
||||
return ripple::Book{{payCurrency, payIssuer}, {getCurrency, getIssuer}, std::nullopt};
|
||||
std::optional<ripple::uint256> domainID;
|
||||
if (request.contains("domain")) {
|
||||
ripple::uint256 dom;
|
||||
if (!dom.parseHex(boost::json::value_to<std::string>(request.at("domain"))))
|
||||
return std::unexpected{Status{RippledError::rpcDOMAIN_MALFORMED}};
|
||||
domainID = dom;
|
||||
}
|
||||
|
||||
return ripple::Book{{payCurrency, payIssuer}, {getCurrency, getIssuer}, domainID};
|
||||
}
|
||||
|
||||
std::expected<ripple::AccountID, Status>
|
||||
|
||||
@@ -625,10 +625,17 @@ postProcessOrderBook(
|
||||
* @param payIssuer The issuer of the currency to pay
|
||||
* @param gets The currency to get
|
||||
* @param getIssuer The issuer of the currency to get
|
||||
* @param domain The domain
|
||||
* @return The book or an error status
|
||||
*/
|
||||
std::expected<ripple::Book, Status>
|
||||
parseBook(ripple::Currency pays, ripple::AccountID payIssuer, ripple::Currency gets, ripple::AccountID getIssuer);
|
||||
parseBook(
|
||||
ripple::Currency pays,
|
||||
ripple::AccountID payIssuer,
|
||||
ripple::Currency gets,
|
||||
ripple::AccountID getIssuer,
|
||||
std::optional<std::string> const& domain
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Parse the book from the request
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace rpc {
|
||||
BookOffersHandler::Result
|
||||
BookOffersHandler::process(Input input, Context const& ctx) const
|
||||
{
|
||||
auto bookMaybe = parseBook(input.paysCurrency, input.paysID, input.getsCurrency, input.getsID);
|
||||
auto bookMaybe = parseBook(input.paysCurrency, input.paysID, input.getsCurrency, input.getsID, input.domain);
|
||||
if (!bookMaybe.has_value())
|
||||
return Error{bookMaybe.error()};
|
||||
|
||||
@@ -131,6 +131,9 @@ tag_invoke(boost::json::value_to_tag<BookOffersHandler::Input>, boost::json::val
|
||||
if (jsonObject.contains(JS(taker)))
|
||||
input.taker = accountFromStringStrict(boost::json::value_to<std::string>(jv.at(JS(taker))));
|
||||
|
||||
if (jsonObject.contains(JS(domain)))
|
||||
input.domain = boost::json::value_to<std::string>(jv.at(JS(domain)));
|
||||
|
||||
if (jsonObject.contains(JS(limit)))
|
||||
input.limit = jv.at(JS(limit)).as_int64();
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ public:
|
||||
// accountID will be filled by input converter, if no issuer is given, will use XRP issuer
|
||||
ripple::AccountID paysID = ripple::xrpAccount();
|
||||
ripple::AccountID getsID = ripple::xrpAccount();
|
||||
std::optional<std::string> domain;
|
||||
};
|
||||
|
||||
using Result = HandlerReturnType<Output>;
|
||||
@@ -147,6 +148,11 @@ public:
|
||||
validation::CustomValidators::accountValidator,
|
||||
Status(RippledError::rpcINVALID_PARAMS, "Invalid field 'taker'.")
|
||||
}},
|
||||
{
|
||||
JS(domain),
|
||||
validation::Type<std::string>{},
|
||||
validation::CustomValidators::uint256HexStringValidator,
|
||||
},
|
||||
{JS(limit),
|
||||
validation::Type<uint32_t>{},
|
||||
validation::Min(1u),
|
||||
|
||||
@@ -287,13 +287,13 @@ tag_invoke(boost::json::value_to_tag<SubscribeHandler::Input>, boost::json::valu
|
||||
auto internalBook = SubscribeHandler::OrderBook{};
|
||||
auto const& bookObject = book.as_object();
|
||||
|
||||
if (auto const& taker = bookObject.find(JS(taker)); taker != bookObject.end())
|
||||
if (auto const taker = bookObject.find(JS(taker)); taker != bookObject.end())
|
||||
internalBook.taker = boost::json::value_to<std::string>(taker->value());
|
||||
|
||||
if (auto const& both = bookObject.find(JS(both)); both != bookObject.end())
|
||||
if (auto const both = bookObject.find(JS(both)); both != bookObject.end())
|
||||
internalBook.both = both->value().as_bool();
|
||||
|
||||
if (auto const& snapshot = bookObject.find(JS(snapshot)); snapshot != bookObject.end())
|
||||
if (auto const snapshot = bookObject.find(JS(snapshot)); snapshot != bookObject.end())
|
||||
internalBook.snapshot = snapshot->value().as_bool();
|
||||
|
||||
auto const parsedBookMaybe = parseBook(book.as_object());
|
||||
|
||||
@@ -483,7 +483,8 @@ createOfferLedgerObject(
|
||||
std::string_view paysCurrency,
|
||||
std::string_view getsIssueId,
|
||||
std::string_view paysIssueId,
|
||||
std::string_view dirId
|
||||
std::string_view dirId,
|
||||
std::optional<std::string_view> const& domain
|
||||
)
|
||||
{
|
||||
ripple::STObject offer(ripple::sfLedgerEntry);
|
||||
@@ -501,6 +502,8 @@ createOfferLedgerObject(
|
||||
offer.setFieldH256(ripple::sfBookDirectory, ripple::uint256{dirId});
|
||||
offer.setFieldH256(ripple::sfPreviousTxnID, ripple::uint256{});
|
||||
offer.setFieldU32(ripple::sfPreviousTxnLgrSeq, 0);
|
||||
if (domain.has_value())
|
||||
offer.setFieldH256(ripple::sfDomainID, ripple::uint256{*domain});
|
||||
return offer;
|
||||
}
|
||||
|
||||
|
||||
@@ -257,7 +257,8 @@ createOfferLedgerObject(
|
||||
std::string_view paysCurrency,
|
||||
std::string_view getsIssueId,
|
||||
std::string_view paysIssueId,
|
||||
std::string_view bookDirId
|
||||
std::string_view bookDirId,
|
||||
std::optional<std::string_view> const& domain = std::nullopt
|
||||
);
|
||||
|
||||
[[nodiscard]] ripple::STObject
|
||||
|
||||
@@ -60,15 +60,14 @@ constexpr auto kLEDGER_HASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF2
|
||||
constexpr auto kINDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
|
||||
constexpr auto kINDEX2 = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321";
|
||||
|
||||
// 20 USD : 10 XRP
|
||||
constexpr auto kPAYS20_USD_GETS10_XRP_BOOK_DIR = "43B83ADC452B85FCBADA6CAEAC5181C255A213630D58FFD455071AFD498D0000";
|
||||
|
||||
// 20 XRP : 10 USD
|
||||
constexpr auto kPAYS20_XRP_GETS10_USD_BOOK_DIR = "7B1767D41DBCE79D9585CF9D0262A5FEC45E5206FF524F8B55071AFD498D0000";
|
||||
|
||||
// transfer rate x2
|
||||
constexpr auto kTRANSFER_RATE_X2 = 2000000000;
|
||||
|
||||
constexpr auto kDOMAIN = "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134E5";
|
||||
|
||||
struct ParameterTestBundle {
|
||||
std::string testName;
|
||||
std::string testJson;
|
||||
@@ -314,6 +313,57 @@ generateParameterBookOffersTestBundles()
|
||||
.expectedError = "invalidParams",
|
||||
.expectedErrorMessage = "Invalid field 'taker'."
|
||||
},
|
||||
ParameterTestBundle{
|
||||
.testName = "Domain_InvalidType",
|
||||
.testJson = R"JSON({
|
||||
"taker_pays" :
|
||||
{
|
||||
"currency" : "CNY",
|
||||
"issuer" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
|
||||
},
|
||||
"taker_gets" :
|
||||
{
|
||||
"currency" : "XRP"
|
||||
},
|
||||
"domain": 0
|
||||
})JSON",
|
||||
.expectedError = "invalidParams",
|
||||
.expectedErrorMessage = "Invalid parameters."
|
||||
},
|
||||
ParameterTestBundle{
|
||||
.testName = "Domain_InvalidInt",
|
||||
.testJson = R"JSON({
|
||||
"taker_pays" :
|
||||
{
|
||||
"currency" : "CNY",
|
||||
"issuer" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
|
||||
},
|
||||
"taker_gets" :
|
||||
{
|
||||
"currency" : "XRP"
|
||||
},
|
||||
"domain": "123"
|
||||
})JSON",
|
||||
.expectedError = "invalidParams",
|
||||
.expectedErrorMessage = "domainMalformed"
|
||||
},
|
||||
ParameterTestBundle{
|
||||
.testName = "Domain_InvalidObject",
|
||||
.testJson = R"JSON({
|
||||
"taker_pays" :
|
||||
{
|
||||
"currency" : "CNY",
|
||||
"issuer" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
|
||||
},
|
||||
"taker_gets" :
|
||||
{
|
||||
"currency" : "XRP"
|
||||
},
|
||||
"domain": {}
|
||||
})JSON",
|
||||
.expectedError = "invalidParams",
|
||||
.expectedErrorMessage = "Invalid parameters."
|
||||
},
|
||||
ParameterTestBundle{
|
||||
.testName = "LimitNotInt",
|
||||
.testJson = R"JSON({
|
||||
@@ -610,11 +660,29 @@ generateNormalPathBookOffersTestBundles()
|
||||
kPAYS20_USD_GETS10_XRP_BOOK_DIR
|
||||
);
|
||||
|
||||
auto const gets10XRPPays20USDOfferWithDomain = createOfferLedgerObject(
|
||||
kACCOUNT2,
|
||||
10,
|
||||
20,
|
||||
ripple::to_string(ripple::xrpCurrency()),
|
||||
ripple::to_string(ripple::to_currency("USD")),
|
||||
toBase58(ripple::xrpAccount()),
|
||||
kACCOUNT,
|
||||
kPAYS20_USD_GETS10_XRP_BOOK_DIR,
|
||||
kDOMAIN
|
||||
);
|
||||
|
||||
auto const getsXRPPaysUSDBook = getBookBase(
|
||||
rpc::parseBook(ripple::to_currency("USD"), account, ripple::xrpCurrency(), ripple::xrpAccount()).value()
|
||||
rpc::parseBook(ripple::to_currency("USD"), account, ripple::xrpCurrency(), ripple::xrpAccount(), std::nullopt)
|
||||
.value()
|
||||
);
|
||||
auto const getsXRPPaysUSDBookWithDomain = getBookBase(
|
||||
rpc::parseBook(ripple::to_currency("USD"), account, ripple::xrpCurrency(), ripple::xrpAccount(), kDOMAIN)
|
||||
.value()
|
||||
);
|
||||
auto const getsUSDPaysXRPBook = getBookBase(
|
||||
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), account).value()
|
||||
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), account, std::nullopt)
|
||||
.value()
|
||||
);
|
||||
|
||||
auto const getsXRPPaysUSDInputJson = fmt::format(
|
||||
@@ -632,6 +700,23 @@ generateNormalPathBookOffersTestBundles()
|
||||
kACCOUNT
|
||||
);
|
||||
|
||||
auto const getsXRPPaysUSDInputJsonWithDomain = fmt::format(
|
||||
R"JSON({{
|
||||
"taker_gets":
|
||||
{{
|
||||
"currency": "XRP"
|
||||
}},
|
||||
"taker_pays":
|
||||
{{
|
||||
"currency": "USD",
|
||||
"issuer": "{}"
|
||||
}},
|
||||
"domain": "{}"
|
||||
}})JSON",
|
||||
kACCOUNT,
|
||||
kDOMAIN
|
||||
);
|
||||
|
||||
auto const paysXRPGetsUSDInputJson = fmt::format(
|
||||
R"JSON({{
|
||||
"taker_pays":
|
||||
@@ -845,6 +930,69 @@ generateNormalPathBookOffersTestBundles()
|
||||
2
|
||||
)
|
||||
},
|
||||
BookOffersNormalTestBundle{
|
||||
.testName = "PaysUSDGetsXRPFrozenWithDomain",
|
||||
.inputJson = getsXRPPaysUSDInputJsonWithDomain,
|
||||
// prepare offer dir index
|
||||
.mockedSuccessors =
|
||||
std::map<ripple::uint256, std::optional<ripple::uint256>>{
|
||||
{getsXRPPaysUSDBookWithDomain, ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR}},
|
||||
{ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR}, std::optional<ripple::uint256>{}}
|
||||
},
|
||||
.mockedLedgerObjects =
|
||||
std::map<ripple::uint256, ripple::Blob>{
|
||||
// book dir object
|
||||
{ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR},
|
||||
createOwnerDirLedgerObject({ripple::uint256{kINDEX2}}, kINDEX1).getSerializer().peekData()},
|
||||
// pays issuer account object
|
||||
{ripple::keylet::account(account).key,
|
||||
createAccountRootObject(kACCOUNT, ripple::lsfGlobalFreeze, 2, 200, 2, kINDEX1, 2)
|
||||
.getSerializer()
|
||||
.peekData()}
|
||||
},
|
||||
.ledgerObjectCalls = 3,
|
||||
.mockedOffers = std::vector<ripple::STObject>{gets10XRPPays20USDOfferWithDomain},
|
||||
.expectedJson = fmt::format(
|
||||
R"JSON({{
|
||||
"ledger_hash":"{}",
|
||||
"ledger_index":300,
|
||||
"offers":
|
||||
[
|
||||
{{
|
||||
"Account":"{}",
|
||||
"BookDirectory":"43B83ADC452B85FCBADA6CAEAC5181C255A213630D58FFD455071AFD498D0000",
|
||||
"BookNode":"0",
|
||||
"DomainID": "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134E5",
|
||||
"Flags":0,
|
||||
"LedgerEntryType":"Offer",
|
||||
"OwnerNode":"0",
|
||||
"PreviousTxnID":"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"PreviousTxnLgrSeq":0,
|
||||
"Sequence":0,
|
||||
"TakerGets":"10",
|
||||
"TakerPays":{{
|
||||
"currency":"USD",
|
||||
"issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"value":"20"
|
||||
}},
|
||||
"index":"E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321",
|
||||
"owner_funds":"{}",
|
||||
"quality":"{}",
|
||||
"taker_gets_funded":"0",
|
||||
"taker_pays_funded":{{
|
||||
"currency":"USD",
|
||||
"issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"value":"0"
|
||||
}}
|
||||
}}
|
||||
]
|
||||
}})JSON",
|
||||
kLEDGER_HASH,
|
||||
kACCOUNT2,
|
||||
0,
|
||||
2
|
||||
)
|
||||
},
|
||||
BookOffersNormalTestBundle{
|
||||
.testName = "GetsUSDPaysXRPFrozen",
|
||||
.inputJson = paysXRPGetsUSDInputJson,
|
||||
@@ -1450,7 +1598,8 @@ TEST_F(RPCBookOffersHandlerTest, Limit)
|
||||
EXPECT_CALL(*backend_, doFetchSuccessorKey).Times(1);
|
||||
|
||||
auto const getsXRPPaysUSDBook = getBookBase(
|
||||
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value()
|
||||
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount(), std::nullopt)
|
||||
.value()
|
||||
);
|
||||
ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, seq, _))
|
||||
.WillByDefault(Return(ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR}));
|
||||
@@ -1523,7 +1672,8 @@ TEST_F(RPCBookOffersHandlerTest, LimitMoreThanMax)
|
||||
EXPECT_CALL(*backend_, doFetchSuccessorKey).Times(1);
|
||||
|
||||
auto const getsXRPPaysUSDBook = getBookBase(
|
||||
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value()
|
||||
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount(), std::nullopt)
|
||||
.value()
|
||||
);
|
||||
ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, seq, _))
|
||||
.WillByDefault(Return(ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR}));
|
||||
|
||||
@@ -822,11 +822,13 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSnapshotSet)
|
||||
auto const issuer = getAccountIdWithString(kACCOUNT);
|
||||
|
||||
auto const getsXRPPaysUSDBook = getBookBase(
|
||||
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value()
|
||||
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount(), std::nullopt)
|
||||
.value()
|
||||
);
|
||||
|
||||
auto const reversedBook = getBookBase(
|
||||
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer).value()
|
||||
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer, std::nullopt)
|
||||
.value()
|
||||
);
|
||||
|
||||
ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, kMAX_SEQ, _))
|
||||
@@ -992,11 +994,13 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothUnsetSnapshotSet)
|
||||
auto const issuer = getAccountIdWithString(kACCOUNT);
|
||||
|
||||
auto const getsXRPPaysUSDBook = getBookBase(
|
||||
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value()
|
||||
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount(), std::nullopt)
|
||||
.value()
|
||||
);
|
||||
|
||||
auto const reversedBook = getBookBase(
|
||||
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer).value()
|
||||
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer, std::nullopt)
|
||||
.value()
|
||||
);
|
||||
|
||||
ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, kMAX_SEQ, _))
|
||||
|
||||
Reference in New Issue
Block a user