Skip to content

Commit 01598d8

Browse files
committed
feat: Support Lending Protocol
1 parent 9d3dbce commit 01598d8

File tree

10 files changed

+466
-18
lines changed

10 files changed

+466
-18
lines changed

conan.lock

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@
33
"requires": [
44
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1765850150.075",
55
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
6-
"xrpl/3.0.0#534d3f65a336109eee929b88962bae4e%1765375071.547",
6+
"xrpl/3.1.0#3d408ab8c8020014fa7dd52bc7cc7ea8%1769706825.165",
77
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1765850149.926",
8-
"spdlog/1.17.0#bcbaaf7147bda6ad24ffbd1ac3d7142c%1767636069.964",
8+
"spdlog/1.17.0#bcbaaf7147bda6ad24ffbd1ac3d7142c%1768312128.781",
99
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1765850149.46",
1010
"re2/20230301#ca3b241baec15bd31ea9187150e0b333%1765850148.103",
1111
"rapidjson/cci.20220822#1b9d8c2256876a154172dc5cfbe447c6%1754325007.656",
1212
"protobuf/3.21.12#44ee56c0a6eea0c19aeeaca680370b88%1764175361.456",
1313
"openssl/1.1.1w#a8f0792d7c5121b954578a7149d23e03%1756223730.729",
14-
"nudb/2.0.9#fb8dfd1a5557f5e0528114c2da17721e%1765850143.957",
14+
"nudb/2.0.9#0432758a24204da08fee953ec9ea03cb%1769436073.32",
1515
"minizip/1.2.13#9e87d57804bd372d6d1e32b1871517a3%1754325004.374",
1616
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
1717
"libuv/1.46.0#dc28c1f653fa197f00db5b577a6f6011%1754325003.592",
1818
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
1919
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
2020
"libarchive/3.8.1#ffee18995c706e02bf96e7a2f7042e0d%1765850144.736",
2121
"http_parser/2.9.4#98d91690d6fd021e9e624218a85d9d97%1754325001.385",
22-
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1755784855.585",
22+
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
2323
"grpc/1.50.1#02291451d1e17200293a409410d1c4e1%1756234248.958",
2424
"fmt/12.1.0#50abab23274d56bb8f42c94b3b9a40c7%1763984116.926",
2525
"doctest/2.4.11#a4211dfc329a16ba9f280f9574025659%1756234220.819",
@@ -40,17 +40,20 @@
4040
],
4141
"python_requires": [],
4242
"overrides": {
43-
"boost/1.83.0": [
43+
"protobuf/3.21.12#44ee56c0a6eea0c19aeeaca680370b88": [
4444
null,
45-
"boost/1.83.0#91d8b1572534d2c334d6790e3c34d0c1"
45+
"protobuf/3.21.12"
4646
],
47-
"protobuf/3.21.12": [
47+
"boost/1.83.0#91d8b1572534d2c334d6790e3c34d0c1": [
4848
null,
49-
"protobuf/3.21.12#44ee56c0a6eea0c19aeeaca680370b88"
49+
"boost/1.83.0#91d8b1572534d2c334d6790e3c34d0c1"
5050
],
5151
"lz4/1.9.4": [
5252
"lz4/1.10.0"
5353
],
54+
"boost/1.90.0": [
55+
"boost/1.83.0"
56+
],
5457
"sqlite3/3.44.2": [
5558
"sqlite3/3.49.1"
5659
]

conanfile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,14 @@ class ClioConan(ConanFile):
1212
options = {}
1313

1414
requires = [
15-
"boost/1.83.0",
1615
"cassandra-cpp-driver/2.17.0",
1716
"fmt/12.1.0",
1817
"grpc/1.50.1",
1918
"libbacktrace/cci.20210118",
2019
"openssl/1.1.1w",
2120
"protobuf/3.21.12",
2221
"spdlog/1.17.0",
23-
"xrpl/3.0.0",
22+
"xrpl/3.1.0",
2423
"zlib/1.3.1",
2524
]
2625

@@ -43,6 +42,7 @@ class ClioConan(ConanFile):
4342
exports_sources = ("CMakeLists.txt", "cmake/*", "src/*")
4443

4544
def requirements(self):
45+
self.requires("boost/1.83.0", force=True)
4646
self.requires("gtest/1.17.0")
4747
self.requires("benchmark/1.9.4")
4848

src/data/AmendmentCenter.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ struct Amendments {
177177
REGISTER(fix1512);
178178
REGISTER(fix1523);
179179
REGISTER(fix1528);
180+
REGISTER(fixBatchInnerSigs);
180181
// NOLINTEND(readability-identifier-naming)
181182
/** @endcond */
182183
};

src/rpc/handlers/AccountInfo.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <boost/json/value.hpp>
3535
#include <boost/json/value_to.hpp>
3636
#include <xrpl/basics/strHex.h>
37+
#include <xrpl/ledger/View.h>
3738
#include <xrpl/protocol/ErrorCodes.h>
3839
#include <xrpl/protocol/Indexes.h>
3940
#include <xrpl/protocol/LedgerFormats.h>
@@ -173,6 +174,22 @@ tag_invoke(boost::json::value_from_tag, boost::json::value& jv, AccountInfoHandl
173174

174175
jv.as_object()[JS(account_flags)] = std::move(acctFlags);
175176

177+
auto const pseudoFields = ripple::getPseudoAccountFields();
178+
for (auto const& pseudoField : pseudoFields) {
179+
if (output.accountData.isFieldPresent(*pseudoField)) {
180+
std::string name = pseudoField->fieldName;
181+
if (name.ends_with("ID")) {
182+
// Remove the ID suffix from the field name.
183+
name = name.substr(0, name.size() - 2);
184+
ASSERT(!name.empty(), "name is not empty");
185+
}
186+
// ValidPseudoAccounts invariant guarantees that only one field
187+
// can be set
188+
jv.as_object()[JS(pseudo_account)].as_object()[JS(type)] = name;
189+
break;
190+
}
191+
}
192+
176193
if (output.signerLists) {
177194
auto signers = boost::json::array();
178195
std::transform(

src/rpc/handlers/LedgerEntry.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,15 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input const& input, Context cons
192192
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(input.vault->at(JS(owner))));
193193
auto const seq = util::integralValueAs<uint32_t>(input.vault->at(JS(seq)));
194194
key = ripple::keylet::vault(*account, seq).key;
195+
} else if (input.loanBroker) {
196+
auto const account =
197+
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(input.loanBroker->at(JS(owner))));
198+
auto const seq = util::integralValueAs<uint32_t>(input.loanBroker->at(JS(seq)));
199+
key = ripple::keylet::loanbroker(*account, seq).key;
200+
} else if (input.loan) {
201+
auto const id = ripple::uint256{boost::json::value_to<std::string>(input.loan->at(JS(loan_broker_id)))};
202+
auto const seq = util::integralValueAs<uint32_t>(input.loan->at(JS(loan_seq)));
203+
key = ripple::keylet::loan(id, seq).key;
195204
} else if (input.delegate) {
196205
auto const account =
197206
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(input.delegate->at(JS(account))));
@@ -333,13 +342,15 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
333342
{JS(mptoken), ripple::ltMPTOKEN},
334343
{JS(permissioned_domain), ripple::ltPERMISSIONED_DOMAIN},
335344
{JS(vault), ripple::ltVAULT},
345+
{JS(loan_broker), ripple::ltLOAN_BROKER},
346+
{JS(loan), ripple::ltLOAN},
336347
{JS(delegate), ripple::ltDELEGATE},
337348
{JS(amendments), ripple::ltAMENDMENTS},
338349
{JS(fee), ripple::ltFEE_SETTINGS},
339350
{JS(hashes), ripple::ltLEDGER_HASHES},
340351
{JS(nft_offer), ripple::ltNFTOKEN_OFFER},
341352
{JS(nunl), ripple::ltNEGATIVE_UNL},
342-
{JS(signer_list), ripple::ltSIGNER_LIST}
353+
{JS(signer_list), ripple::ltSIGNER_LIST},
343354
};
344355

345356
auto const parseBridgeFromJson = [](boost::json::value const& bridgeJson) {
@@ -430,6 +441,10 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
430441
input.permissionedDomain = jv.at(JS(permissioned_domain)).as_object();
431442
} else if (jsonObject.contains(JS(vault))) {
432443
input.vault = jv.at(JS(vault)).as_object();
444+
} else if (jsonObject.contains(JS(loan_broker))) {
445+
input.loanBroker = jv.at(JS(loan_broker)).as_object();
446+
} else if (jsonObject.contains(JS(loan))) {
447+
input.loan = jv.at(JS(loan)).as_object();
433448
} else if (jsonObject.contains(JS(delegate))) {
434449
input.delegate = jv.at(JS(delegate)).as_object();
435450
}

src/rpc/handlers/LedgerEntry.hpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ class LedgerEntryHandler {
105105
std::optional<boost::json::object> mptoken;
106106
std::optional<boost::json::object> permissionedDomain;
107107
std::optional<boost::json::object> vault;
108+
std::optional<boost::json::object> loanBroker;
109+
std::optional<boost::json::object> loan;
108110
std::optional<ripple::STXChainBridge> bridge;
109111
std::optional<std::string> bridgeAccount;
110112
std::optional<uint32_t> chainClaimId;
@@ -411,6 +413,40 @@ class LedgerEntryHandler {
411413
},
412414
},
413415
}}},
416+
{JS(loan_broker),
417+
meta::WithCustomError{
418+
validation::Type<std::string, boost::json::object>{}, Status(ClioError::RpcMalformedRequest)
419+
},
420+
meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
421+
meta::IfType<boost::json::object>{meta::Section{
422+
{JS(seq),
423+
meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
424+
meta::WithCustomError{validation::Type<uint32_t>{}, Status(ClioError::RpcMalformedRequest)}},
425+
{
426+
JS(owner),
427+
meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
428+
meta::WithCustomError{
429+
validation::CustomValidators::accountBase58Validator, Status(ClioError::RpcMalformedOwner)
430+
},
431+
},
432+
}}},
433+
{JS(loan),
434+
meta::WithCustomError{
435+
validation::Type<std::string, boost::json::object>{}, Status(ClioError::RpcMalformedRequest)
436+
},
437+
meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
438+
meta::IfType<boost::json::object>{meta::Section{
439+
{JS(loan_seq),
440+
meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
441+
meta::WithCustomError{validation::Type<uint32_t>{}, Status(ClioError::RpcMalformedRequest)}},
442+
{
443+
JS(loan_broker_id),
444+
meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
445+
meta::WithCustomError{
446+
validation::CustomValidators::uint256HexStringValidator, Status(ClioError::RpcMalformedRequest)
447+
},
448+
},
449+
}}},
414450
{JS(delegate),
415451
meta::WithCustomError{
416452
validation::Type<std::string, boost::json::object>{}, Status(ClioError::RpcMalformedRequest)

tests/common/util/TestObject.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,3 +1800,67 @@ createVault(
18001800

18011801
return vault;
18021802
}
1803+
1804+
ripple::STObject
1805+
createLoanBroker(
1806+
std::string_view owner,
1807+
std::string_view account,
1808+
ripple::LedgerIndex seq,
1809+
ripple::uint256 vaultID,
1810+
uint32_t loanSequence,
1811+
ripple::uint256 previousTxId,
1812+
uint32_t previousTxSeq
1813+
)
1814+
{
1815+
auto loanBroker = ripple::STObject(ripple::sfLedgerEntry);
1816+
loanBroker.setAccountID(ripple::sfOwner, getAccountIdWithString(owner));
1817+
loanBroker.setAccountID(ripple::sfAccount, getAccountIdWithString(account));
1818+
loanBroker.setFieldU32(ripple::sfSequence, seq);
1819+
loanBroker.setFieldU64(ripple::sfOwnerNode, 0);
1820+
loanBroker.setFieldU64(ripple::sfVaultNode, 0);
1821+
loanBroker.setFieldH256(ripple::sfVaultID, vaultID);
1822+
loanBroker.setFieldH256(ripple::sfPreviousTxnID, previousTxId);
1823+
loanBroker.setFieldU32(ripple::sfPreviousTxnLgrSeq, previousTxSeq);
1824+
loanBroker.setFieldU32(ripple::sfLoanSequence, loanSequence);
1825+
1826+
// Optional/default fields - not setting them as they will use default values
1827+
1828+
loanBroker.setFieldU32(ripple::sfFlags, 0);
1829+
loanBroker.setFieldU16(ripple::sfLedgerEntryType, ripple::ltLOAN_BROKER);
1830+
1831+
return loanBroker;
1832+
}
1833+
1834+
ripple::STObject
1835+
createLoan(
1836+
std::string_view borrower,
1837+
ripple::uint256 loanBrokerID,
1838+
uint32_t loanSequence,
1839+
uint32_t startDate,
1840+
uint32_t paymentInterval,
1841+
int64_t periodicPaymentValue,
1842+
ripple::uint256 previousTxId,
1843+
uint32_t previousTxSeq
1844+
)
1845+
{
1846+
auto loan = ripple::STObject(ripple::sfLedgerEntry);
1847+
loan.setAccountID(ripple::sfBorrower, getAccountIdWithString(borrower));
1848+
loan.setFieldH256(ripple::sfLoanBrokerID, loanBrokerID);
1849+
loan.setFieldU32(ripple::sfLoanSequence, loanSequence);
1850+
loan.setFieldU64(ripple::sfOwnerNode, 0);
1851+
loan.setFieldU64(ripple::sfLoanBrokerNode, 0);
1852+
loan.setFieldH256(ripple::sfPreviousTxnID, previousTxId);
1853+
loan.setFieldU32(ripple::sfPreviousTxnLgrSeq, previousTxSeq);
1854+
1855+
loan.setFieldU32(ripple::sfStartDate, startDate);
1856+
loan.setFieldU32(ripple::sfPaymentInterval, paymentInterval);
1857+
1858+
loan.setFieldNumber(ripple::sfPeriodicPayment, ripple::STNumber{ripple::sfPeriodicPayment, periodicPaymentValue});
1859+
1860+
// Optional/default fields - not setting them as they will use default values
1861+
1862+
loan.setFieldU32(ripple::sfFlags, 0);
1863+
loan.setFieldU16(ripple::sfLedgerEntryType, ripple::ltLOAN);
1864+
1865+
return loan;
1866+
}

tests/common/util/TestObject.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,3 +581,26 @@ createVault(
581581
ripple::uint256 previousTxId,
582582
uint32_t previousTxSeq
583583
);
584+
585+
[[nodiscard]] ripple::STObject
586+
createLoanBroker(
587+
std::string_view owner,
588+
std::string_view account,
589+
ripple::LedgerIndex seq,
590+
ripple::uint256 vaultID,
591+
uint32_t loanSequence,
592+
ripple::uint256 previousTxId,
593+
uint32_t previousTxSeq
594+
);
595+
596+
[[nodiscard]] ripple::STObject
597+
createLoan(
598+
std::string_view borrower,
599+
ripple::uint256 loanBrokerID,
600+
uint32_t loanSequence,
601+
uint32_t startDate,
602+
uint32_t paymentInterval,
603+
int64_t periodicPaymentValue,
604+
ripple::uint256 previousTxId,
605+
uint32_t previousTxSeq
606+
);

tests/unit/rpc/handlers/GetAggregatePriceTests.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryMultipleOraclesOdd)
741741
"entire_set": {{
742742
"mean": "110",
743743
"size": 3,
744-
"standard_deviation": "164.6207763315433"
744+
"standard_deviation": "164.6207763315432795"
745745
}},
746746
"median": "20",
747747
"time": 4321,
@@ -817,7 +817,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryMultipleOraclesEven)
817817
"entire_set": {{
818818
"mean": "92.5",
819819
"size": 4,
820-
"standard_deviation": "138.8944443333378"
820+
"standard_deviation": "138.8944443333377776"
821821
}},
822822
"median": "30",
823823
"time": 4321,
@@ -895,12 +895,12 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryTrim)
895895
"entire_set": {{
896896
"mean": "92.5",
897897
"size": 4,
898-
"standard_deviation": "138.8944443333378"
898+
"standard_deviation": "138.8944443333377776"
899899
}},
900900
"trimmed_set": {{
901901
"mean": "30",
902902
"size": 2,
903-
"standard_deviation": "14.14213562373095"
903+
"standard_deviation": "14.14213562373095049"
904904
}},
905905
"median": "30",
906906
"time": 4321,
@@ -1134,7 +1134,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, ValidTimeThreshold)
11341134
"entire_set": {{
11351135
"mean": "15",
11361136
"size": 2,
1137-
"standard_deviation": "7.071067811865475"
1137+
"standard_deviation": "7.071067811865475245"
11381138
}},
11391139
"median": "15",
11401140
"time": {},
@@ -1216,7 +1216,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdTooLong)
12161216
"entire_set": {{
12171217
"mean": "92.5",
12181218
"size": 4,
1219-
"standard_deviation": "138.8944443333378"
1219+
"standard_deviation": "138.8944443333377776"
12201220
}},
12211221
"median": "30",
12221222
"time": 1711461384,
@@ -1297,7 +1297,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdIncludeOldest)
12971297
"entire_set": {{
12981298
"mean": "92.5",
12991299
"size": 4,
1300-
"standard_deviation": "138.8944443333378"
1300+
"standard_deviation": "138.8944443333377776"
13011301
}},
13021302
"median": "30",
13031303
"time": 1711461384,

0 commit comments

Comments
 (0)