Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds possibility to use "*" to auto-select address in placeauctionbid and paybackloan #937

Merged
merged 8 commits into from
Dec 24, 2021
37 changes: 31 additions & 6 deletions src/masternodes/rpc_loan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1124,7 +1124,7 @@ UniValue paybackloan(const JSONRPCRequest& request) {
{"metadata", RPCArg::Type::OBJ, RPCArg::Optional::NO, "",
{
{"vaultId", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Id of vault used for loan"},
{"from", RPCArg::Type::STR, RPCArg::Optional::NO, "Address containing repayment tokens"},
{"from", RPCArg::Type::STR, RPCArg::Optional::NO, "Address containing repayment tokens. If \"from\" value is: \"*\" (star), it's means auto-selection accounts from wallet."},
{"amounts", RPCArg::Type::STR, RPCArg::Optional::NO, "Amount in amount@token format."},
},
},
Expand Down Expand Up @@ -1169,16 +1169,41 @@ UniValue paybackloan(const JSONRPCRequest& request) {
else
throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"vaultId\" must be non-null");

if (!metaObj["from"].isNull())
loanPayback.from = DecodeScript(metaObj["from"].getValStr());
else
throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"from\" must not be null");

if (!metaObj["amounts"].isNull())
loanPayback.amounts = DecodeAmounts(pwallet->chain(), metaObj["amounts"], "");
else
throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"amounts\" must not be null");

if (metaObj["from"].isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"from\" must not be null");
}

auto fromStr = metaObj["from"].getValStr();
if (fromStr == "*") {
auto selectedAccounts = SelectAccountsByTargetBalances(GetAllMineAccounts(pwallet), loanPayback.amounts, SelectionPie);

for (auto& account : selectedAccounts) {
auto it = loanPayback.amounts.balances.begin();
while (it != loanPayback.amounts.balances.end()) {
if (account.second.balances[it->first] < it->second) {
break;
}
it++;
}
if (it == loanPayback.amounts.balances.end()) {
loanPayback.from = account.first;
break;
}
}

if (loanPayback.from.empty()) {
throw JSONRPCError(RPC_INVALID_REQUEST,
"Not enough tokens on account, call sendtokenstoaddress to increase it.\n");
}
} else {
loanPayback.from = DecodeScript(fromStr);
}

if (!::IsMine(*pwallet, loanPayback.from))
throw JSONRPCError(RPC_INVALID_PARAMETER,
strprintf("Address (%s) is not owned by the wallet", metaObj["from"].getValStr()));
Expand Down
23 changes: 21 additions & 2 deletions src/masternodes/rpc_vault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ UniValue placeauctionbid(const JSONRPCRequest& request) {
{
{"vaultId", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Vault id"},
{"index", RPCArg::Type::NUM, RPCArg::Optional::NO, "Auction index"},
{"from", RPCArg::Type::STR, RPCArg::Optional::NO, "Address to get tokens"},
{"from", RPCArg::Type::STR, RPCArg::Optional::NO, "Address to get tokens. If \"from\" value is: \"*\" (star), it's means auto-selection accounts from wallet."},
{"amount", RPCArg::Type::STR, RPCArg::Optional::NO, "Amount of amount@symbol format"},
{"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of json objects",
{
Expand Down Expand Up @@ -902,9 +902,28 @@ UniValue placeauctionbid(const JSONRPCRequest& request) {
// decode vaultId
CVaultId vaultId = ParseHashV(request.params[0], "vaultId");
uint32_t index = request.params[1].get_int();
auto from = DecodeScript(request.params[2].get_str());
CTokenAmount amount = DecodeAmount(pwallet->chain(), request.params[3].get_str(), "amount");

CScript from = {};
auto fromStr = request.params[2].get_str();
if (fromStr == "*") {
auto selectedAccounts = SelectAccountsByTargetBalances(GetAllMineAccounts(pwallet), CBalances{TAmounts{{amount.nTokenId, amount.nValue}}}, SelectionPie);

for (auto& account : selectedAccounts) {
if (account.second.balances[amount.nTokenId] >= amount.nValue) {
from = account.first;
break;
}
}

if (from.empty()) {
throw JSONRPCError(RPC_INVALID_REQUEST,
"Not enough tokens on account, call sendtokenstoaddress to increase it.\n");
}
} else {
from = DecodeScript(fromStr);
}

CAuctionBidMessage msg{vaultId, index, from, amount};
CDataStream markedMetadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION);
markedMetadata << static_cast<unsigned char>(CustomTxType::AuctionBid)
Expand Down
49 changes: 47 additions & 2 deletions test/functional/feature_loan.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
self.extra_args = [
['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1'],
['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1']
['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1'],
['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1']
]

def run_test(self):
Expand Down Expand Up @@ -233,11 +233,56 @@ def run_test(self):
assert_equal(vault1['state'], "active")
assert_equal(accountBal, ['1600.00000000@DFI', '1600.00000000@BTC', '226.00000000@TSLA'])


self.nodes[0].deposittovault(vaultId1, account, '600@DFI')
self.nodes[0].generate(1)
vault1 = self.nodes[0].getvault(vaultId1)
assert_equal(vault1['collateralAmounts'], ['600.00000000@DFI'])

# Trigger liquidation updating price in oracle
oracle1_prices = [{"currency": "USD", "tokenAmount": "2@TSLA"}]
oracle2_prices = [{"currency": "USD", "tokenAmount": "3@TSLA"}]
timestamp = calendar.timegm(time.gmtime())
self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices)
self.nodes[0].setoracledata(oracle_id2, timestamp, oracle2_prices)
self.nodes[0].generate(36)

# take loan
self.nodes[0].takeloan({
'vaultId': vaultId1,
'amounts': "1000@TSLA"
})
self.nodes[0].generate(1)

# Trigger liquidation updating price in oracle
oracle1_prices = [{"currency": "USD", "tokenAmount": "200@TSLA"}]
oracle2_prices = [{"currency": "USD", "tokenAmount": "300@TSLA"}]
timestamp = calendar.timegm(time.gmtime())
self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices)
self.nodes[0].setoracledata(oracle_id2, timestamp, oracle2_prices)
self.nodes[0].generate(36)

# Not enought tokens on account
try:
self.nodes[0].placeauctionbid(vaultId1, 0, "*", "1600@TSLA")
except JSONRPCException as e:
errorString = e.error['message']
assert("Not enough tokens on account, call sendtokenstoaddress to increase it." in errorString)

newAddress = self.nodes[0].getnewaddress("")
self.nodes[0].sendtokenstoaddress({}, {newAddress: "1600@TSLA"}) # newAddress has now the highest amount of TSLA token
self.nodes[0].generate(1)

self.nodes[0].placeauctionbid(vaultId1, 0, "*", "1600@TSLA") # Autoselect address with highest amount of TSLA token
self.nodes[0].generate(40) # let auction end

auction = self.nodes[0].listauctionhistory(newAddress)[0]
assert_equal(auction['winner'], newAddress)
assert_equal(auction['blockHeight'], 600)
assert_equal(auction['vaultId'], vaultId1)
assert_equal(auction['batchIndex'], 0)
assert_equal(auction['auctionBid'], "1600.00000000@TSLA")

if __name__ == '__main__':
LoanTest().main()

66 changes: 62 additions & 4 deletions test/functional/feature_loan_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
self.extra_args = [
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-fortcanningheight=50', '-eunosheight=50', '-txindex=1'],
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-fortcanningheight=50', '-eunosheight=50', '-txindex=1']]
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=1', '-fortcanningheight=50', '-eunosheight=50', '-txindex=1'],
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=1', '-fortcanningheight=50', '-eunosheight=50', '-txindex=1']]

def run_test(self):
assert_equal(len(self.nodes[0].listtokens()), 1) # only one token == DFI
Expand Down Expand Up @@ -370,7 +370,7 @@ def run_test(self):

# loan payback burn
vaultInfo = self.nodes[0].getvault(vaultId)
assert_equal(self.nodes[0].getburninfo()['paybackburn'], Decimal('0.00186824'))
assert_equal(self.nodes[0].getburninfo()['paybackburn'], Decimal('0.00186822'))
assert_equal(sorted(vaultInfo['loanAmounts']), sorted(['0.50000057@' + symbolTSLA, '1.00000133@' + symbolGOOGL]))

try:
Expand Down Expand Up @@ -409,7 +409,7 @@ def run_test(self):
vaultInfo = self.nodes[0].getvault(vaultId)
assert_equal(vaultInfo['loanAmounts'], [])
assert_equal(sorted(self.nodes[0].listaccounthistory(account0)[0]['amounts']), sorted(['-1.00001463@GOOGL', '-0.50000627@TSLA']))
assert_greater_than_or_equal(self.nodes[0].getburninfo()['paybackburn'], Decimal('0.00443691'))
assert_greater_than_or_equal(self.nodes[0].getburninfo()['paybackburn'], Decimal('0.00443685'))

for interest in self.nodes[0].getinterest('LOAN150'):
if interest['token'] == symbolTSLA:
Expand Down Expand Up @@ -458,5 +458,63 @@ def run_test(self):
assert_equal(len(vaultInfo['batches']), 1)
assert_equal(vaultInfo['batches'][0]['collaterals'], ['400.00000000@DFI'])

address = self.nodes[0].getnewaddress()
self.nodes[0].utxostoaccount({address: "100@" + symbolDFI})
self.nodes[0].generate(1)

vaultId3 = self.nodes[0].createvault(address)
self.nodes[0].generate(1)

self.nodes[0].deposittovault(vaultId3, address, "100@DFI")
self.nodes[0].generate(1)
vault = self.nodes[0].getvault(vaultId3)
print("vault", vault)
# take loan
self.nodes[0].takeloan({
'vaultId': vaultId3,
'amounts': ["10@TSLA", "10@GOOGL"]
})
self.nodes[0].generate(1)

address2 = self.nodes[0].getnewaddress()
self.nodes[0].sendtokenstoaddress({}, {address2:["5@TSLA", "5@GOOGL"]}) # split into two address
self.nodes[0].generate(1)

listaccounts = self.nodes[0].listaccounts({}, False, False, True)
print("listaccounts", listaccounts)

try:
self.nodes[0].paybackloan({
'vaultId': vaultId3,
'from': "*",
'amounts': ["10@" + symbolTSLA, "10@" + symbolGOOGL]
})
except JSONRPCException as e:
errorString = e.error['message']
assert("Not enough tokens on account, call sendtokenstoaddress to increase it." in errorString)

self.nodes[0].paybackloan({
'vaultId': vaultId3,
'from': "*",
'amounts': "5@" + symbolTSLA
})
self.nodes[0].generate(1)

vault = self.nodes[0].getvault(vaultId3)
assert_equal(sorted(vault['loanAmounts']), sorted(['5.00002853@' + symbolTSLA, '10.00003993@' + symbolGOOGL]))

self.nodes[0].sendtokenstoaddress({}, {address2:["5@" + symbolTSLA, "10@" + symbolGOOGL]})
self.nodes[0].generate(1)

self.nodes[0].paybackloan({
'vaultId': vaultId3,
'from': "*",
'amounts': ["5@" + symbolTSLA, "10@" + symbolGOOGL]
})
self.nodes[0].generate(1)

vault = self.nodes[0].getvault(vaultId3)
assert_equal(sorted(vault['loanAmounts']), sorted(['0.00003425@' + symbolTSLA, '0.00005324@' + symbolGOOGL]))

if __name__ == '__main__':
LoanTakeLoanTest().main()