From 8e57391abb96f16a1b52d2e2d3ed25f0d2005d87 Mon Sep 17 00:00:00 2001 From: jouzo Date: Mon, 22 Nov 2021 22:04:05 +0100 Subject: [PATCH 1/7] Adds estimateloan RPC --- src/masternodes/rpc_vault.cpp | 99 ++++++++ src/rpc/client.cpp | 2 + test/functional/feature_loan_estimateloan.py | 227 +++++++++++++++++++ test/functional/test_runner.py | 1 + 4 files changed, 329 insertions(+) create mode 100644 test/functional/feature_loan_estimateloan.py diff --git a/src/masternodes/rpc_vault.cpp b/src/masternodes/rpc_vault.cpp index 529459e6d42..6af2e90aa73 100644 --- a/src/masternodes/rpc_vault.cpp +++ b/src/masternodes/rpc_vault.cpp @@ -1146,6 +1146,104 @@ UniValue listauctionhistory(const JSONRPCRequest& request) { return ret; } +UniValue estimateloan(const JSONRPCRequest& request) { + + RPCHelpMan{"estimateloan", + "Returns amount of loan tokens a vault can take depending on a target collateral ratio.\n", + { + {"vaultId", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "vault hex id",}, + {"tokens", RPCArg::Type::OBJ, RPCArg::Optional::NO, "Object with loans token as key and their percent split as value", + { + + {"split", RPCArg::Type::NUM, RPCArg::Optional::NO, "The percent split"}, + }, + }, + {"targetRatio", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "Target collateral ratio. (defaults to vault's loan scheme ratio)"} + }, + RPCResult{ + "\"json\" (Array) Array of strings\n" + }, + RPCExamples{ + HelpExampleCli("estimateloan", R"(5474b2e9bfa96446e5ef3c9594634e1aa22d3a0722cb79084d61253acbdf87bf '{"TSLA":0.5, "FB": 0.4, "GOOGL":0.1}' 150)") + + HelpExampleRpc("estimateloan", R"("5474b2e9bfa96446e5ef3c9594634e1aa22d3a0722cb79084d61253acbdf87bf", {"TSLA":0.5, "FB": 0.4, "GOOGL":0.1}, 150)") + }, + }.Check(request); + + RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ, UniValue::VNUM}, false); + + CVaultId vaultId = ParseHashV(request.params[0], "vaultId"); + + LOCK(cs_main); + auto height = ::ChainActive().Height(); + + auto vault = pcustomcsview->GetVault(vaultId); + if (!vault) { + throw JSONRPCError(RPC_DATABASE_ERROR, strprintf("Vault <%s> not found.", vaultId.GetHex())); + } + + auto vaultState = GetVaultState(vaultId, *vault); + if (vaultState == VaultState::InLiquidation) { + throw JSONRPCError(RPC_MISC_ERROR, strprintf("Vault <%s> is in liquidation.", vaultId.GetHex())); + } + + auto scheme = pcustomcsview->GetLoanScheme(vault->schemeId); + uint32_t ratio = scheme->ratio; + if (request.params.size() > 2) { + ratio = (size_t) request.params[2].get_int64(); + } + + auto collaterals = pcustomcsview->GetVaultCollaterals(vaultId); + if (!collaterals) { + throw JSONRPCError(RPC_MISC_ERROR, "Cannot estimate loan without collaterals."); + } + + auto blockTime = ::ChainActive().Tip()->GetBlockTime(); + auto rate = pcustomcsview->GetLoanCollaterals(vaultId, *collaterals, height + 1, blockTime, false, true); + if (!rate.ok) { + throw JSONRPCError(RPC_MISC_ERROR, rate.msg); + } + + auto collValue = ValueFromUint(rate.val->totalCollaterals); + UniValue ret(UniValue::VARR); + CBalances loanBalances; + CAmount totalSplit{0}; + if (request.params.size() > 1 && request.params[1].isObject()) { + for (const auto& tokenId : request.params[1].getKeys()) { + CAmount split = AmountFromValue(request.params[1][tokenId]); + + auto token = pcustomcsview->GetToken(tokenId); + if (!token) { + throw JSONRPCError(RPC_DATABASE_ERROR, strprintf("Token %d does not exist!", tokenId)); + } + + auto loanToken = pcustomcsview->GetLoanTokenByID(token->first); + if (!loanToken) { + throw JSONRPCError(RPC_DATABASE_ERROR, strprintf("(%s) is not a loan token!", tokenId)); + } + + auto priceFeed = pcustomcsview->GetFixedIntervalPrice(loanToken->fixedIntervalPriceId); + if (!priceFeed.ok) { + throw JSONRPCError(RPC_DATABASE_ERROR, priceFeed.msg); + } + + auto price = priceFeed.val->priceRecord[0]; + if (!priceFeed.val->isLive(pcustomcsview->GetPriceDeviation())) { + throw JSONRPCError(RPC_MISC_ERROR, strprintf("No live fixed price for %s", tokenId)); + } + + auto availableValue = MultiplyAmounts(rate.val->totalCollaterals, split); + auto loanAmount = DivideAmounts(availableValue, price); + auto amountRatio = MultiplyAmounts(DivideAmounts(loanAmount, ratio), 100); + + loanBalances.Add({token->first, amountRatio}); + totalSplit += split; + } + if (totalSplit != COIN) + throw JSONRPCError(RPC_MISC_ERROR, strprintf("total split between loan tokens = %d vs expected %d", totalSplit, COIN)); + } + return AmountsToJSON(loanBalances.balances); +} + static const CRPCCommand commands[] = { // category name actor (function) params @@ -1160,6 +1258,7 @@ static const CRPCCommand commands[] = {"vault", "placeauctionbid", &placeauctionbid, {"id", "index", "from", "amount", "inputs"}}, {"vault", "listauctions", &listauctions, {"pagination"}}, {"vault", "listauctionhistory", &listauctionhistory, {"owner", "pagination"}}, + {"vault", "estimateloan", &estimateloan, {"vaultId", "tokens"}}, }; void RegisterVaultRPCCommands(CRPCTable& tableRPC) { diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 951c81fc8e5..8a47f650d9d 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -260,6 +260,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listvaults", 1, "pagination" }, { "listauctions", 0, "pagination" }, { "listauctionhistory", 1, "pagination" }, + { "estimateloan", 1, "tokens" }, + { "estimateloan", 2, "targetRatio" }, { "spv_sendrawtx", 0, "rawtx" }, { "spv_createanchor", 0, "inputs" }, diff --git a/test/functional/feature_loan_estimateloan.py b/test/functional/feature_loan_estimateloan.py new file mode 100644 index 00000000000..a158927d90a --- /dev/null +++ b/test/functional/feature_loan_estimateloan.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) DeFi Blockchain Developers +# Distributed under the MIT software license, see the accompanying +# file LICENSE or http://www.opensource.org/licenses/mit-license.php. +"""Test Loan Scheme.""" + +from decimal import Decimal +from test_framework.test_framework import DefiTestFramework + +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_greater_than +import time + +class DepositToVaultTest (DefiTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + self.extra_args = [ + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1'], + ] + + def run_test(self): + self.nodes[0].generate(125) + + self.nodes[0].createtoken({ + "symbol": "BTC", + "name": "BTC token", + "isDAT": True, + "collateralAddress": self.nodes[0].get_genesis_keys().ownerAuthAddress + }) + self.nodes[0].generate(1) + + + symbolDFI = "DFI" + symbolBTC = "BTC" + idDFI = list(self.nodes[0].gettoken(symbolDFI).keys())[0] + idBTC = list(self.nodes[0].gettoken(symbolBTC).keys())[0] + + self.nodes[0].minttokens("10@" + symbolBTC) + self.nodes[0].generate(1) + + account = self.nodes[0].get_genesis_keys().ownerAuthAddress + + self.nodes[0].utxostoaccount({account: "100@" + symbolDFI}) + self.nodes[0].generate(1) + + oracle_address1 = self.nodes[0].getnewaddress("", "legacy") + price_feeds1 = [ + {"currency": "USD", "token": "DFI"}, + {"currency": "USD", "token": "BTC"}, + {"currency": "USD", "token": "TSLA"}, + {"currency": "USD", "token": "TWTR"}, + ] + oracle_id1 = self.nodes[0].appointoracle(oracle_address1, price_feeds1, 10) + self.nodes[0].generate(1) + + oracle1_prices = [ + {"currency": "USD", "tokenAmount": "1@DFI"}, + {"currency": "USD", "tokenAmount": "100@BTC"}, + {"currency": "USD", "tokenAmount": "5@TSLA"}, + {"currency": "USD", "tokenAmount": "10@TWTR"}, + ] + mock_time = int(time.time()) + self.nodes[0].setmocktime(mock_time) + self.nodes[0].setoracledata(oracle_id1, mock_time, oracle1_prices) + + self.nodes[0].generate(1) + + self.nodes[0].setcollateraltoken({ + 'token': idDFI, + 'factor': 1, + 'fixedIntervalPriceId': "DFI/USD"}) + + self.nodes[0].setcollateraltoken({ + 'token': idBTC, + 'factor': 1, + 'fixedIntervalPriceId': "BTC/USD"}) + + self.nodes[0].generate(7) + + loanSchemeRatio = 200 + self.nodes[0].createloanscheme(loanSchemeRatio, 1, 'LOAN0001') + self.nodes[0].generate(1) + + ownerAddress1 = self.nodes[0].getnewaddress('', 'legacy') + vaultId1 = self.nodes[0].createvault(ownerAddress1) # default loan scheme + self.nodes[0].generate(1) + + # Vault not found + try: + self.nodes[0].estimateloan("af03dbd05492caf362d0daf623be182469bcbae7095d3bab682e40ea3d7c2cbb", {"TSLA": 1}) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Vault not found." in errorString) + # Without collaterals + try: + self.nodes[0].estimateloan(vaultId1, {"TSLA": 1}) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Cannot estimate loan without collaterals." in errorString) + + self.nodes[0].setloantoken({ + 'symbol': "TSLA", + 'name': "Tesla Token", + 'fixedIntervalPriceId': "TSLA/USD", + 'mintable': True, + 'interest': 0.01}) + self.nodes[0].setloantoken({ + 'symbol': "TWTR", + 'name': "Twitter Token", + 'fixedIntervalPriceId': "TWTR/USD", + 'mintable': True, + 'interest': 0.01}) + self.nodes[0].generate(1) + + self.nodes[0].deposittovault(vaultId1, account, '1@DFI') + self.nodes[0].generate(1) + self.nodes[0].deposittovault(vaultId1, account, '0.01@BTC') + self.nodes[0].generate(1) + + # Negative split value + try: + self.nodes[0].estimateloan(vaultId1, {"TSLA": -1}) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Amount out of range" in errorString) + # Token that does not exists + try: + self.nodes[0].estimateloan(vaultId1, {"TSLAAA": 1}) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Token TSLAAA does not exist!" in errorString) + # Token not set as loan token + try: + self.nodes[0].estimateloan(vaultId1, {"DFI": 1}) + except JSONRPCException as e: + errorString = e.error['message'] + assert("(DFI) is not a loan token!" in errorString) + # Token without live price + try: + self.nodes[0].estimateloan(vaultId1, {"TSLA": 1}) + except JSONRPCException as e: + errorString = e.error['message'] + assert("No live fixed price for TSLA" in errorString) + + oracle1_prices = [ + {"currency": "USD", "tokenAmount": "1@DFI"}, + {"currency": "USD", "tokenAmount": "100@BTC"}, + {"currency": "USD", "tokenAmount": "5@TSLA"}, + {"currency": "USD", "tokenAmount": "10@TWTR"}, + ] + mock_time = int(time.time()) + self.nodes[0].setmocktime(mock_time) + self.nodes[0].setoracledata(oracle_id1, mock_time, oracle1_prices) + + self.nodes[0].generate(8) # activate prices + + # Total split should be equal to 1 + try: + self.nodes[0].estimateloan(vaultId1, {"TSLA": 0.8}) + except JSONRPCException as e: + errorString = e.error['message'] + assert("total split between loan tokens = 80000000 vs expected 100000000" in errorString) + + + estimateloan = self.nodes[0].estimateloan(vaultId1, {"TSLA":1}) + # Cannot take more loan than estimated + try: + [amount, token] = estimateloan[0].split("@") + newAmount = "@".join([str(float(amount) * 1.01), token]) + self.nodes[0].takeloan({ "vaultId": vaultId1, "amounts": newAmount }) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Vault does not have enough collateralization ratio" in errorString) + + self.nodes[0].takeloan({ "vaultId": vaultId1, "amounts": estimateloan }) # should be able to take loan amount from estimateloan + self.nodes[0].generate(1) + + vault1 = self.nodes[0].getvault(vaultId1) + assert_equal(vault1["collateralRatio"], loanSchemeRatio) # vault collateral ratio should be equal to its loan scheme ratio. + + vaultId2 = self.nodes[0].createvault(ownerAddress1) + self.nodes[0].generate(1) + self.nodes[0].deposittovault(vaultId2, account, '1@DFI') + self.nodes[0].generate(1) + self.nodes[0].deposittovault(vaultId2, account, '0.01@BTC') + self.nodes[0].generate(1) + + estimateloan = self.nodes[0].estimateloan(vaultId2, {"TSLA":0.8, "TWTR": 0.2}) + self.nodes[0].takeloan({ "vaultId": vaultId2, "amounts": estimateloan }) # Take multiple loan amount from estimateloan + self.nodes[0].generate(1) + + vault2 = self.nodes[0].getvault(vaultId2) + assert_equal(vault2["collateralRatio"], loanSchemeRatio) + + vaultId3 = self.nodes[0].createvault(ownerAddress1) + self.nodes[0].generate(1) + self.nodes[0].deposittovault(vaultId3, account, '1@DFI') + self.nodes[0].generate(1) + self.nodes[0].deposittovault(vaultId3, account, '0.01@BTC') + self.nodes[0].generate(1) + + targetRatio = 400 + estimateloan = self.nodes[0].estimateloan(vaultId3, {"TSLA":0.8, "TWTR": 0.2}, targetRatio) + self.nodes[0].takeloan({ "vaultId": vaultId3, "amounts": estimateloan }) + self.nodes[0].generate(1) + + vault3 = self.nodes[0].getvault(vaultId3) + assert_equal(vault3["collateralRatio"], targetRatio) + + # make vault enter under liquidation state + oracle1_prices = [{"currency": "USD", "tokenAmount": "20@TSLA"}] + mock_time = int(time.time()) + self.nodes[0].setmocktime(mock_time) + self.nodes[0].setoracledata(oracle_id1, mock_time, oracle1_prices) + self.nodes[0].generate(12) # let fixed price update + + try: + self.nodes[0].estimateloan(vaultId1, {"TSLA":1}) + except JSONRPCException as e: + errorString = e.error['message'] + print("errorString", errorString) + assert("Vault <" + vaultId1 + "> is in liquidation" in errorString) + +if __name__ == '__main__': + DepositToVaultTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 98c60dd544f..0c93c9bdd50 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -253,6 +253,7 @@ #'feature_forced_reward_address.py', 'feature_loan_vault.py', 'feature_loan_deposittovault.py', + 'feature_loan_estimateloan.py', 'feature_loan_priceupdate.py', 'feature_loan_vaultstate.py', 'feature_loan.py', From a8ab61f39b367eeea84579fd50e143822d3093fe Mon Sep 17 00:00:00 2001 From: jouzo Date: Mon, 22 Nov 2021 22:09:00 +0100 Subject: [PATCH 2/7] Adds missing targetRatio argument to dispatch table --- src/masternodes/rpc_vault.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/masternodes/rpc_vault.cpp b/src/masternodes/rpc_vault.cpp index 6af2e90aa73..1e5f1381a5b 100644 --- a/src/masternodes/rpc_vault.cpp +++ b/src/masternodes/rpc_vault.cpp @@ -1258,7 +1258,7 @@ static const CRPCCommand commands[] = {"vault", "placeauctionbid", &placeauctionbid, {"id", "index", "from", "amount", "inputs"}}, {"vault", "listauctions", &listauctions, {"pagination"}}, {"vault", "listauctionhistory", &listauctionhistory, {"owner", "pagination"}}, - {"vault", "estimateloan", &estimateloan, {"vaultId", "tokens"}}, + {"vault", "estimateloan", &estimateloan, {"vaultId", "tokens", "targetRatio"}}, }; void RegisterVaultRPCCommands(CRPCTable& tableRPC) { From 6a8413907afd904eba1dbe7cabe49c73fa5cdd76 Mon Sep 17 00:00:00 2001 From: jouzo Date: Mon, 22 Nov 2021 22:13:37 +0100 Subject: [PATCH 3/7] Remove unused imports --- test/functional/feature_loan_estimateloan.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/functional/feature_loan_estimateloan.py b/test/functional/feature_loan_estimateloan.py index a158927d90a..96dfc25be15 100644 --- a/test/functional/feature_loan_estimateloan.py +++ b/test/functional/feature_loan_estimateloan.py @@ -5,11 +5,10 @@ # file LICENSE or http://www.opensource.org/licenses/mit-license.php. """Test Loan Scheme.""" -from decimal import Decimal from test_framework.test_framework import DefiTestFramework from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, assert_greater_than +from test_framework.util import assert_equal import time class DepositToVaultTest (DefiTestFramework): From 9ee426460ee3300f2c25d890685908f490c2d0b3 Mon Sep 17 00:00:00 2001 From: jouzo Date: Tue, 23 Nov 2021 13:24:45 +0100 Subject: [PATCH 4/7] Rename class name and update header comment to correctly reflect test scope --- test/functional/feature_loan_deposittovault.py | 2 +- test/functional/feature_loan_estimateloan.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/functional/feature_loan_deposittovault.py b/test/functional/feature_loan_deposittovault.py index 9d1701f67f7..c2ba7f67a3b 100755 --- a/test/functional/feature_loan_deposittovault.py +++ b/test/functional/feature_loan_deposittovault.py @@ -3,7 +3,7 @@ # Copyright (c) DeFi Blockchain Developers # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. -"""Test Loan Scheme.""" +"""Test Loan - deposittovault.""" from decimal import Decimal from test_framework.test_framework import DefiTestFramework diff --git a/test/functional/feature_loan_estimateloan.py b/test/functional/feature_loan_estimateloan.py index 96dfc25be15..d74e974c72b 100644 --- a/test/functional/feature_loan_estimateloan.py +++ b/test/functional/feature_loan_estimateloan.py @@ -3,7 +3,7 @@ # Copyright (c) DeFi Blockchain Developers # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. -"""Test Loan Scheme.""" +"""Test loan - estimateloan.""" from test_framework.test_framework import DefiTestFramework @@ -11,7 +11,7 @@ from test_framework.util import assert_equal import time -class DepositToVaultTest (DefiTestFramework): +class EstimateLoanTest (DefiTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True @@ -223,4 +223,4 @@ def run_test(self): assert("Vault <" + vaultId1 + "> is in liquidation" in errorString) if __name__ == '__main__': - DepositToVaultTest().main() + EstimateLoanTest().main() From bc7cd2119510204390dae6188166529f5b3c8f61 Mon Sep 17 00:00:00 2001 From: jouzo Date: Tue, 23 Nov 2021 15:56:51 +0100 Subject: [PATCH 5/7] Use GetDecimalString to format error --- src/masternodes/rpc_vault.cpp | 2 +- test/functional/feature_loan_estimateloan.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/masternodes/rpc_vault.cpp b/src/masternodes/rpc_vault.cpp index 1e5f1381a5b..236867dea2a 100644 --- a/src/masternodes/rpc_vault.cpp +++ b/src/masternodes/rpc_vault.cpp @@ -1239,7 +1239,7 @@ UniValue estimateloan(const JSONRPCRequest& request) { totalSplit += split; } if (totalSplit != COIN) - throw JSONRPCError(RPC_MISC_ERROR, strprintf("total split between loan tokens = %d vs expected %d", totalSplit, COIN)); + throw JSONRPCError(RPC_MISC_ERROR, strprintf("total split between loan tokens = %s vs expected %s", GetDecimaleString(totalSplit), GetDecimaleString(COIN))); } return AmountsToJSON(loanBalances.balances); } diff --git a/test/functional/feature_loan_estimateloan.py b/test/functional/feature_loan_estimateloan.py index d74e974c72b..fc3d0920117 100644 --- a/test/functional/feature_loan_estimateloan.py +++ b/test/functional/feature_loan_estimateloan.py @@ -160,8 +160,7 @@ def run_test(self): self.nodes[0].estimateloan(vaultId1, {"TSLA": 0.8}) except JSONRPCException as e: errorString = e.error['message'] - assert("total split between loan tokens = 80000000 vs expected 100000000" in errorString) - + assert("total split between loan tokens = 0.80000000 vs expected 1.00000000" in errorString) estimateloan = self.nodes[0].estimateloan(vaultId1, {"TSLA":1}) # Cannot take more loan than estimated @@ -219,7 +218,6 @@ def run_test(self): self.nodes[0].estimateloan(vaultId1, {"TSLA":1}) except JSONRPCException as e: errorString = e.error['message'] - print("errorString", errorString) assert("Vault <" + vaultId1 + "> is in liquidation" in errorString) if __name__ == '__main__': From 0c7ed98915aa255c68adba20851121e8a02fde24 Mon Sep 17 00:00:00 2001 From: jouzo Date: Wed, 24 Nov 2021 15:14:02 +0100 Subject: [PATCH 6/7] Remove unused vars --- src/masternodes/rpc_vault.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/masternodes/rpc_vault.cpp b/src/masternodes/rpc_vault.cpp index 954acf6a7bf..6e91d30c1eb 100644 --- a/src/masternodes/rpc_vault.cpp +++ b/src/masternodes/rpc_vault.cpp @@ -1175,7 +1175,6 @@ UniValue estimateloan(const JSONRPCRequest& request) { CVaultId vaultId = ParseHashV(request.params[0], "vaultId"); LOCK(cs_main); - auto height = ::ChainActive().Height(); auto vault = pcustomcsview->GetVault(vaultId); if (!vault) { @@ -1198,14 +1197,13 @@ UniValue estimateloan(const JSONRPCRequest& request) { throw JSONRPCError(RPC_MISC_ERROR, "Cannot estimate loan without collaterals."); } + auto height = ::ChainActive().Height(); auto blockTime = ::ChainActive().Tip()->GetBlockTime(); auto rate = pcustomcsview->GetLoanCollaterals(vaultId, *collaterals, height + 1, blockTime, false, true); if (!rate.ok) { throw JSONRPCError(RPC_MISC_ERROR, rate.msg); } - auto collValue = ValueFromUint(rate.val->totalCollaterals); - UniValue ret(UniValue::VARR); CBalances loanBalances; CAmount totalSplit{0}; if (request.params.size() > 1 && request.params[1].isObject()) { From 8fc11737773c20341baeb04c1e9c23e75401e453 Mon Sep 17 00:00:00 2001 From: jouzo Date: Thu, 2 Dec 2021 09:57:14 +0100 Subject: [PATCH 7/7] Use GetAmountInCurrency utility function in estimatecollateral --- src/masternodes/rpc_vault.cpp | 23 +++++++------------ .../feature_loan_estimatecollateral.py | 2 +- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/masternodes/rpc_vault.cpp b/src/masternodes/rpc_vault.cpp index 0688e2b4283..02b0bbd1ec3 100644 --- a/src/masternodes/rpc_vault.cpp +++ b/src/masternodes/rpc_vault.cpp @@ -1620,7 +1620,7 @@ UniValue estimatecollateral(const JSONRPCRequest& request) { RPCTypeCheck(request.params, {UniValueType(), UniValue::VNUM, UniValue::VOBJ}, false); - const CBalances loanAmounts = DecodeAmounts(pwallet->chain(), request.params[0], ""); + const CBalances loanBalances = DecodeAmounts(pwallet->chain(), request.params[0], ""); auto ratio = request.params[1].get_int(); std::map collateralSplits; @@ -1633,22 +1633,15 @@ UniValue estimatecollateral(const JSONRPCRequest& request) { LOCK(cs_main); CAmount totalLoanValue{0}; - for (const auto& balance : loanAmounts.balances) { - auto loanToken = pcustomcsview->GetLoanTokenByID(balance.first); - if (!loanToken) { - throw JSONRPCError(RPC_DATABASE_ERROR, strprintf("(%d) is not a loan token!", balance.first.v)); - } - - auto priceFeed = pcustomcsview->GetFixedIntervalPrice(loanToken->fixedIntervalPriceId); - if (!priceFeed.ok) { - throw JSONRPCError(RPC_DATABASE_ERROR, priceFeed.msg); - } + for (const auto& loan : loanBalances.balances) { + auto loanToken = pcustomcsview->GetLoanTokenByID(loan.first); + if (!loanToken) throw JSONRPCError(RPC_INVALID_PARAMETER, "Token with id (" + loan.first.ToString() + ") is not a loan token!"); - auto price = priceFeed.val->priceRecord[0]; - if (!priceFeed.val->isLive(pcustomcsview->GetPriceDeviation())) { - throw JSONRPCError(RPC_MISC_ERROR, strprintf("No live fixed price for %s", loanToken->symbol)); + auto amountInCurrency = pcustomcsview->GetAmountInCurrency(loan.second, loanToken->fixedIntervalPriceId); + if (!amountInCurrency) { + throw JSONRPCError(RPC_DATABASE_ERROR, amountInCurrency.msg); } - totalLoanValue += MultiplyAmounts(balance.second, price); + totalLoanValue += *amountInCurrency.val; } uint32_t height = ::ChainActive().Height(); diff --git a/test/functional/feature_loan_estimatecollateral.py b/test/functional/feature_loan_estimatecollateral.py index 78c2e33583f..26bfb28c0af 100644 --- a/test/functional/feature_loan_estimatecollateral.py +++ b/test/functional/feature_loan_estimatecollateral.py @@ -116,7 +116,7 @@ def run_test(self): self.nodes[0].estimatecollateral("10@TSLA", 200) except JSONRPCException as e: errorString = e.error['message'] - assert("No live fixed price for TSLA" in errorString) + assert("No live fixed prices for TSLA/USD" in errorString) oracle1_prices = [ {"currency": "USD", "tokenAmount": "1@DFI"},