diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index 39c52fbbc7..94e117f601 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -3133,14 +3133,14 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor const auto totalInterest = TotalInterest(*rate, height); if (totalInterest < 0) { - loanAmountChange = currentLoanAmount > std::abs(totalInterest) ? + loanAmountChange = currentLoanAmount > std::abs(totalInterest) ? // Interest to decrease smaller than overall existing loan amount. // So reduce interest from the borrowing principal. If this is negative, - // we'll reduce from principal. - tokenAmount + totalInterest : + // we'll reduce from principal. + tokenAmount + totalInterest : // Interest to decrease is larger than old loan amount. - // We reduce from the borrowing principal. If this is negative, - // we'll reduce from principal. + // We reduce from the borrowing principal. If this is negative, + // we'll reduce from principal. tokenAmount - currentLoanAmount; resetInterestToHeight = true; } @@ -3427,7 +3427,10 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (paybackTokenId == loanTokenId) { // If interest was negative remove it from sub amount - res = mnview.SubMintedTokens(loanTokenId, subInterest > 0 ? subLoan : subLoan + subInterest); + if (height >= static_cast(consensus.FortCanningEpilogueHeight) && subInterest < 0) + subLoan += subInterest; + + res = mnview.SubMintedTokens(loanTokenId, subLoan); if (!res) return res; diff --git a/test/functional/feature_negative_interest.py b/test/functional/feature_negative_interest.py index ca5b0379d1..bbe6014649 100755 --- a/test/functional/feature_negative_interest.py +++ b/test/functional/feature_negative_interest.py @@ -7,11 +7,11 @@ 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, assert_greater_than_or_equal import calendar import time -from decimal import ROUND_DOWN, Decimal +from decimal import ROUND_DOWN, ROUND_UP, Decimal def getDecimalAmount(amount): amountTmp = amount.split('@')[0] @@ -33,7 +33,7 @@ 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', '-fortcanningheight=1', '-fortcanningmuseumheight=1', '-fortcanningspringheight=1', '-fortcanninghillheight=1', '-fortcanningcrunchheight=1', '-fortcanninggreatworldheight=1', '-jellyfish_regtest=1', '-txindex=1', '-simulatemainnet=1'] + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-fortcanningheight=1', '-fortcanningmuseumheight=1', '-fortcanningspringheight=1', '-fortcanninghillheight=1', '-fortcanningcrunchheight=1', '-fortcanninggreatworldheight=1', '-fortcanningepilogueheight=1', '-jellyfish_regtest=1', '-txindex=1', '-simulatemainnet=1'] ] def createTokens(self): @@ -200,7 +200,6 @@ def vault_interest_zero(self): # Generate some more blocks and check interest continues at 0 self.nodes[0].generate(100) - verbose = True vault = self.nodes[0].getvault(self.vaultId0, verbose) assert_equal(vault["interestAmounts"], ["0.00000000@DUSD"]) assert_equal(Decimal(vault["interestPerBlockValue"]), Decimal('0E-16')) @@ -348,7 +347,6 @@ def takeloan_after_interest_increase(self): self.nodes[0].generate(1) # Check interests = 0 - verbose = True vault = self.nodes[0].getvault(self.vaultId5, verbose) interestPerBlockAmount = Decimal(vault["interestsPerBlock"][0].split('@')[0]) assert_equal(interestPerBlockAmount, expected_IPB) @@ -392,7 +390,6 @@ def takeloan_after_interest_decrease(self): self.nodes[0].generate(1) # Check interests = 0 - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestAmounts"], ["-0.00000093@TSLA"]) assert_equal(Decimal(vault["interestPerBlockValue"]), Decimal('-0.000009322675393835616430')) @@ -401,7 +398,11 @@ def takeloan_after_interest_decrease(self): # Try payback of interest zero vault def payback_interest_zero(self): + verbose = True # Use previous vault -> vaultId6 with -49% interest (0% interest) + [balanceTSLAbefore, _] = self.nodes[0].getaccount(self.account0)[2].split('@') + vaultBefore = self.nodes[0].getvault(self.vaultId6, verbose) + self.nodes[0].paybackloan({ 'vaultId': self.vaultId6, 'from': self.account0, @@ -409,13 +410,19 @@ def payback_interest_zero(self): self.nodes[0].generate(1) # Check interests = 0 - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestAmounts"], ["-0.00000046@TSLA"]) assert_equal(Decimal(vault["interestPerBlockValue"]), Decimal('-0.000004661331637176560120')) assert_equal(Decimal(vault["interestValue"]), Decimal('-0.00000460')) assert_equal(vault["interestsPerBlock"], ["-0.000000466133163717656012@TSLA"]) assert_equal(vault["loanAmounts"], ["0.99999787@TSLA"]) + [loanAmountBefore, _] = vaultBefore["loanAmounts"][0].split('@') + [loanAmount, _] = vault["loanAmounts"][0].split('@') + [interestAmount, _] = vault["interestAmounts"][0].split('@') + assert_equal(Decimal(loanAmount), Decimal(loanAmountBefore) - Decimal('1') + Decimal(interestAmount)) + + [balanceTSLAafter, _] = self.nodes[0].getaccount(self.account0)[2].split('@') + assert_equal(Decimal(balanceTSLAafter), Decimal(balanceTSLAbefore) - Decimal('1')) # Test withdrawfromvault after setting back negative interest def withdraw_interest_zero(self): @@ -431,7 +438,6 @@ def withdraw_interest_zero(self): self.nodes[0].withdrawfromvault(self.vaultId5, self.account0, "1@DFI") self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId5, verbose) accountInfo = self.nodes[0].getaccount(self.account0) assert_equal(vault["collateralAmounts"], ['9.00000000@DFI']) @@ -470,7 +476,6 @@ def check_interest_amounts_after_interest_changes(self): # Generate some blocks to accrue interest self.nodes[0].generate(10) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000000000000000000000') assert_equal(vault["interestAmounts"], ['-0.00000137@TSLA']) @@ -480,35 +485,30 @@ def check_interest_amounts_after_interest_changes(self): self.nodes[0].generate(1) # Now total interest should be 1% - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000095129217085235920') assert_equal(vault["interestAmounts"], ['-0.00000136@TSLA']) # One block interest is added correctly self.nodes[0].generate(2) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000095129217085235920') assert_equal(vault["interestAmounts"], ['-0.00000135@TSLA']) # 10 blocks interest is added correctly self.nodes[0].generate(10) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000095129217085235920') assert_equal(vault["interestAmounts"], ['-0.00000125@TSLA']) # check rounding up to 1 sat is working self.nodes[0].generate(80) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000095129217085235920') assert_equal(vault["interestAmounts"], ['-0.00000049@TSLA']) # Check interest amounts becomming positive self.nodes[0].generate(60) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000095129217085235920') assert_equal(vault["interestAmounts"], ['0.00000008@TSLA']) @@ -528,7 +528,6 @@ def updates_of_vault_scheme(self): assert_equal(vault["interestPerBlockValue"], '0.000000095129217085235920') self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) # Same as previous IPB assert_equal(vault["interestPerBlockValue"], '0.000000095129217085235920') @@ -539,7 +538,6 @@ def updates_of_vault_scheme(self): 'amounts': "1@" + self.symbolTSLA}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000190258593036529680') @@ -547,86 +545,106 @@ def updates_of_vault_scheme(self): # Generate some blocks to check IPB behaviour self.nodes[0].generate(10) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000190258593036529680') # Update vault to LOAN1 (1%) so overall interest becomes negative -1% self.nodes[0].updatevault(self.vaultId6, {'loanSchemeId': 'LOAN1'}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '-0.000000190258593036529680') + [balanceTSLAbefore, _] = self.nodes[0].getaccount(self.account0)[2].split('@') + vaultBefore = self.nodes[0].getvault(self.vaultId6, verbose) + # After takeloan vault is updated and new interest per block are set self.nodes[0].paybackloan({ 'vaultId': self.vaultId6, 'from': self.account0, 'amounts': ["1@" + self.symbolTSLA]}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) # Updated after payback assert_equal(vault["interestPerBlockValue"], '-0.000000095129244672754940') + [loanAmountBefore, _] = vaultBefore["loanAmounts"][0].split('@') + [loanAmount, _] = vault["loanAmounts"][0].split('@') + [interestAmount, _] = vault["interestAmounts"][0].split('@') + assert_equal(Decimal(loanAmount), Decimal(loanAmountBefore) - Decimal('1') + Decimal(interestAmount)) + + [balanceTSLAafter, _] = self.nodes[0].getaccount(self.account0)[2].split('@') + assert_equal(Decimal(balanceTSLAafter), Decimal(balanceTSLAbefore) - Decimal('1')) self.nodes[0].takeloan({ 'vaultId': self.vaultId6, 'amounts': "1@" + self.symbolTSLA}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '-0.000000190258620624048700') # Update vault again and test payback self.nodes[0].updatevault(self.vaultId6, {'loanSchemeId': 'LOAN3'}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) # Updated by action updatevault assert_equal(vault["interestPerBlockValue"], '0.000000190258620624048700') + [balanceTSLAbefore, _] = self.nodes[0].getaccount(self.account0)[2].split('@') + vaultBefore = self.nodes[0].getvault(self.vaultId6, verbose) + self.nodes[0].paybackloan({ 'vaultId': self.vaultId6, 'from': self.account0, 'amounts': ["1@" + self.symbolTSLA]}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) # IPB is updated again after paybackloan assert_equal(vault["interestPerBlockValue"], '0.000000095129244672754940') + [loanAmountBefore, _] = vaultBefore["loanAmounts"][0].split('@') + [loanAmount, _] = vault["loanAmounts"][0].split('@') + [interestAmount, _] = vault["interestAmounts"][0].split('@') + assert_equal(Decimal(loanAmount), Decimal(loanAmountBefore) - Decimal('1') + Decimal(interestAmount)) + + [balanceTSLAafter, _] = self.nodes[0].getaccount(self.account0)[2].split('@') + assert_equal(Decimal(balanceTSLAafter), Decimal(balanceTSLAbefore) - Decimal('1')) # Check same with withdrawfromvault action self.nodes[0].updatevault(self.vaultId6, {'loanSchemeId': 'LOAN1'}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '-0.000000095129244672754940') # Withdraw does not update the IPB self.nodes[0].withdrawfromvault(self.vaultId6, self.account0, "1@DFI") self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '-0.000000095129244672754940') # Check same with deposittovault action self.nodes[0].deposittovault(self.vaultId6, self.account0, '1@DFI') self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) assert_equal(vault["interestPerBlockValue"], '-0.000000095129244672754940') + [balanceTSLAbefore, _] = self.nodes[0].getaccount(self.account0)[2].split('@') + vaultBefore = self.nodes[0].getvault(self.vaultId6, verbose) + # Update IPB with paybackloan self.nodes[0].paybackloan({ 'vaultId': self.vaultId6, 'from': self.account0, 'amounts': ["0.1@" + self.symbolTSLA]}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId6, verbose) # IPB is updated again after paybackloan assert_equal(vault["interestPerBlockValue"], '-0.000000085616305175038050') + [loanAmountBefore, _] = vaultBefore["loanAmounts"][0].split('@') + [loanAmount, _] = vault["loanAmounts"][0].split('@') + [interestAmount, _] = vault["interestAmounts"][0].split('@') + assert_equal(Decimal(loanAmount), Decimal(loanAmountBefore) - Decimal('0.1') + Decimal(interestAmount)) + + [balanceTSLAafter, _] = self.nodes[0].getaccount(self.account0)[2].split('@') + assert_equal(Decimal(balanceTSLAafter), Decimal(balanceTSLAbefore) - Decimal('0.1')) # Check total interest with different loans taken def total_interest_with_multiple_takeloan(self): @@ -640,13 +658,11 @@ def total_interest_with_multiple_takeloan(self): self.nodes[0].deposittovault(self.vaultId7, self.account0, "10@DFI") self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000000000000000000000') # Update with token interest self.nodes[0].setgov({"ATTRIBUTES":{f'v0/token/{self.idTSLA}/loan_minting_interest':'-1'}}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000000000000000000000') @@ -655,14 +671,12 @@ def total_interest_with_multiple_takeloan(self): 'vaultId': self.vaultId7, 'amounts': "1@" + self.symbolTSLA}) self.nodes[0].generate(10) - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000000000000000000000') self.nodes[0].updatevault(self.vaultId7, {'loanSchemeId': 'LOAN3'}) self.nodes[0].generate(1) # now total interest should be 2% - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000190258751902587510') @@ -671,7 +685,6 @@ def total_interest_with_multiple_takeloan(self): 'vaultId': self.vaultId7, 'amounts': "1@" + self.symbolTSLA}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000380517503805175030') @@ -680,7 +693,6 @@ def total_interest_with_multiple_takeloan(self): 'vaultId': self.vaultId7, 'amounts': "1@" + self.symbolGOLD}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000380517503805175030') assert_equal(vault["interestsPerBlock"], ['0.000000038051750380517503@TSLA','0.000000000000000000000000@GOLD']) @@ -689,7 +701,6 @@ def total_interest_with_multiple_takeloan(self): # GOLD loan interest -> -2% self.nodes[0].updatevault(self.vaultId7, {'loanSchemeId': 'LOAN3'}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) assert_equal(vault["interestPerBlockValue"], '0.000000380517503805175030') assert_equal(vault["interestsPerBlock"], ['0.000000038051750380517503@TSLA','0.000000000000000000000000@GOLD']) @@ -697,7 +708,6 @@ def total_interest_with_multiple_takeloan(self): # Update GOLD interest to 1 so total GOLD loan has positive interest 2% self.nodes[0].setgov({"ATTRIBUTES":{f'v0/token/{self.idGOLD}/loan_minting_interest':'-1'}}) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) # Only GOLD loan should update assert_equal(vault["interestPerBlockValue"], '0.000000570776255707762540') @@ -720,7 +730,6 @@ def vault_status_with_negative_interest(self): # Generate some blocks to put vault into liquidation self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) assert_equal(vault["state"], 'mayLiquidate') @@ -731,7 +740,6 @@ def vault_status_with_negative_interest(self): self.nodes[0].generate(1) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) assert_equal(vault["state"], 'mayLiquidate') assert_equal(vault["interestPerBlockValue"], '0.000000000000000000000000') @@ -743,31 +751,43 @@ def vault_status_with_negative_interest(self): self.nodes[0].generate(1) self.nodes[0].generate(1) - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) assert_equal(vault["state"], 'active') assert_equal(vault["interestPerBlockValue"], '-0.000002289646700913242000') def payback_interests_and_continue_with_negative_interest(self): + verbose = True + [balanceTSLAbefore, _] = self.nodes[0].getaccount(self.account0)[2].split('@') + vaultBefore = self.nodes[0].getvault(self.vaultId7, verbose) + self.nodes[0].paybackloan({ 'vaultId': self.vaultId7, 'from': self.account0, 'amounts': ["0.00001000@" + self.symbolTSLA]}) self.nodes[0].generate(1) - verbose = True + vault = self.nodes[0].getvault(self.vaultId7, verbose) + [loanAmountBefore, _] = vaultBefore["loanAmounts"][0].split('@') + [loanAmount, _] = vault["loanAmounts"][0].split('@') + [interestAmount, _] = vault["interestAmounts"][0].split('@') + assert_equal(Decimal(loanAmount), Decimal(loanAmountBefore) - Decimal('0.00001000') + Decimal(interestAmount)) + + [balanceTSLAafter, _] = self.nodes[0].getaccount(self.account0)[2].split('@') + assert_equal(Decimal(balanceTSLAafter), Decimal(balanceTSLAbefore) - Decimal('0.00001000')) + amounts0 = [] for amount in vault["loanAmounts"]: amounts0.append(getDecimalAmount(amount)) + self.nodes[0].generate(1) - verbose = True + vault = self.nodes[0].getvault(self.vaultId7, verbose) amounts1 = [] for amount in vault["loanAmounts"]: amounts1.append(getDecimalAmount(amount)) for amount in amounts0: - assert_greater_than(amount, amounts1[amounts0.index(amount)]) + assert_greater_than_or_equal(amount, amounts1[amounts0.index(amount)]) accountInfo = self.nodes[0].getaccount(self.account0) self.nodes[0].generate(100) @@ -775,16 +795,16 @@ def payback_interests_and_continue_with_negative_interest(self): # Check account balances remain the same even though loan amount is decreasing assert_equal(accountInfo, accountInfo1) - verbose = True vault = self.nodes[0].getvault(self.vaultId7, verbose) amounts2 = [] for amount in vault["loanAmounts"]: amounts2.append(getDecimalAmount(amount)) for amount in amounts1: - assert_greater_than(amount, amounts2[amounts1.index(amount)]) + assert_greater_than_or_equal(amount, amounts2[amounts1.index(amount)]) def let_loan_be_paid_by_negative_interests(self): + verbose = True # set negative high negative interest for testing self.nodes[0].setgov({"ATTRIBUTES":{f'v0/token/{self.iddUSD}/loan_minting_interest':'-1000000'}}) self.nodes[0].generate(1) @@ -808,7 +828,6 @@ def let_loan_be_paid_by_negative_interests(self): self.nodes[0].generate(104) # Just one block before loan is fully paid accountInfo = self.nodes[0].getaccount(self.account1) assert_equal(accountInfo, ['5.00000000@DUSD']) - verbose = True vault = self.nodes[0].getvault(self.vaultId8, verbose) assert_equal(vault["loanValue"], Decimal('0.00571276')) assert_equal(vault["interestPerBlockValue"], '-0.047564640410958904109589') @@ -817,7 +836,6 @@ def let_loan_be_paid_by_negative_interests(self): self.nodes[0].generate(1) accountInfo = self.nodes[0].getaccount(self.account1) assert_equal(accountInfo, ['5.00000000@DUSD']) - verbose = True vault = self.nodes[0].getvault(self.vaultId8, verbose) assert_equal(vault["loanValue"], Decimal('0E-8')) assert_equal(vault["interestPerBlockValue"], '0.000000000000000000000000') @@ -836,7 +854,6 @@ def let_loan_be_paid_by_negative_interests(self): self.rollback_to(block) accountInfo = self.nodes[0].getaccount(self.account1) assert_equal(accountInfo, ['5.00000000@DUSD']) - verbose = True vault = self.nodes[0].getvault(self.vaultId8, verbose) assert_equal(vault["loanValue"], Decimal('0E-8')) assert_equal(vault["interestPerBlockValue"], '0.000000000000000000000000') @@ -851,27 +868,142 @@ def let_loan_be_paid_by_negative_interests(self): 'amounts': "5@" + self.symboldUSD}) self.nodes[0].generate(1) + accountInfo = self.nodes[0].getaccount(self.account1) + assert_equal(accountInfo, ['10.00000000@DUSD']) storedInterest = self.nodes[0].getstoredinterest(self.vaultId8, self.symboldUSD) loanTokens = self.nodes[0].getloantokens(self.vaultId8) assert_equal(loanTokens, ['5.00000000@DUSD']) assert_equal(storedInterest["interestPerBlock"], '-0.047564640410958904109589') + assert_equal(storedInterest["interestToHeight"], '0.000000000000000000000000') - storedLoans = self.nodes[0].getloantokens(self.vaultId8) + storedLoans = loanTokens storedAmount = getDecimalAmount(storedLoans[0]) self.nodes[0].generate(10) + self.nodes[0].takeloan({ 'vaultId': self.vaultId8, 'amounts': "0.00000001@" + self.symboldUSD}) self.nodes[0].generate(1) storedLoans1 = self.nodes[0].getloantokens(self.vaultId8) storedAmount1 = getDecimalAmount(storedLoans1[0]) + storedInterest1 = self.nodes[0].getstoredinterest(self.vaultId8, self.symboldUSD) + assert(storedAmount > storedAmount1) - loanTokens = self.nodes[0].getloantokens(self.vaultId8) - assert_equal(loanTokens, ['4.47678897@DUSD']) - assert_equal(storedInterest["interestPerBlock"], '-0.047564640410958904109589') - assert_equal(storedInterest["interestToHeight"], '0.000000000000000000000000') + expectedAmount = Decimal(Decimal('5') + 11 * Decimal(storedInterest["interestPerBlock"]) + Decimal('0.00000001')).quantize(Decimal('1E-8'), ROUND_UP) + assert_equal(storedAmount1, expectedAmount) #4.47678897 + assert_equal(storedInterest1["interestPerBlock"], '-0.042587371510759417808219') + assert_equal(storedInterest1["interestToHeight"], '0.000000000000000000000000') + + def various_payback_tests(self): + verbose = True + # set negative high negative interest for testing + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/token/{self.iddUSD}/loan_minting_interest':'-1000000'}}) + self.nodes[0].generate(1) + + # Brand new vault with new account associated + self.account = self.nodes[0].getnewaddress() + toAmount = {self.account: ["10@DFI"]} + self.nodes[0].accounttoaccount(self.account0, toAmount) + self.nodes[0].generate(1) + self.vaultId9 = self.nodes[0].createvault(self.account, 'LOAN1') + self.nodes[0].generate(1) + # Deposit + self.nodes[0].deposittovault(self.vaultId9, self.account, "10@DFI") + self.nodes[0].generate(1) + # Take loan + self.nodes[0].takeloan({ + 'vaultId': self.vaultId9, + 'amounts': "10@" + self.symboldUSD}) + self.nodes[0].generate(10) + + accountInfo = self.nodes[0].getaccount(self.account) + assert_equal(accountInfo, ['10.00000000@DUSD']) + vault = self.nodes[0].getvault(self.vaultId9, verbose) + assert_equal(vault["loanValue"], Decimal('9.04870720')) + assert_equal(vault["interestPerBlockValue"], '-0.095129280821917808219178') + + storedLoans = self.nodes[0].getloantokens(self.vaultId9) + storedAmount = getDecimalAmount(storedLoans[0]) + assert_equal(storedAmount, Decimal('10.00000000')) + [balanceDUSDbefore, _] = self.nodes[0].getaccount(self.account)[0].split('@') + vaultBefore = vault + + # payback overpay + self.nodes[0].paybackloan({ + 'vaultId': self.vaultId9, + 'from': self.account, + 'amounts': ["10@" + self.symboldUSD]}) + self.nodes[0].generate(1) + + vault = self.nodes[0].getvault(self.vaultId9, verbose) + assert_equal(vault["loanAmounts"],[]) + storedLoans1 = self.nodes[0].getloantokens(self.vaultId9) + assert_equal(storedLoans1, []) + + [balanceDUSDafter, _] = self.nodes[0].getaccount(self.account)[0].split('@') + assert_equal(Decimal(balanceDUSDafter), Decimal(balanceDUSDbefore) - Decimal('9.04870720')) + + # Take loan + self.nodes[0].takeloan({ + 'vaultId': self.vaultId9, + 'amounts': "10@" + self.symboldUSD}) + self.nodes[0].generate(10) + + accountInfo = self.nodes[0].getaccount(self.account) + assert_equal(getDecimalAmount(accountInfo[0]), Decimal('10.00000000') + Decimal(balanceDUSDafter)) + vault = self.nodes[0].getvault(self.vaultId9, verbose) + assert_equal(vault["loanValue"], Decimal('9.04870720')) + assert_equal(vault["interestPerBlockValue"], '-0.095129280821917808219178') + + storedLoans = self.nodes[0].getloantokens(self.vaultId9) + storedAmount = getDecimalAmount(storedLoans[0]) + storedInterest = self.nodes[0].getstoredinterest(self.vaultId9, self.symboldUSD) + assert_equal(storedAmount, Decimal('10.00000000')) + + [balanceDUSDbefore, _] = self.nodes[0].getaccount(self.account)[0].split('@') + vaultBefore = vault + + # payback 1 satoshi + self.nodes[0].paybackloan({ + 'vaultId': self.vaultId9, + 'from': self.account, + 'amounts': ["0.00000001@" + self.symboldUSD]}) + self.nodes[0].generate(1) + + vault = self.nodes[0].getvault(self.vaultId9, verbose) + [loanAmountBefore, _] = vaultBefore["loanAmounts"][0].split('@') + [loanAmount, _] = vault["loanAmounts"][0].split('@') + [interestAmount, _] = vault["interestAmounts"][0].split('@') + assert_equal(Decimal(loanAmount), Decimal(loanAmountBefore) - Decimal('0.00000001') + Decimal(interestAmount)) + + storedLoans1 = self.nodes[0].getloantokens(self.vaultId9) + storedAmount1 = getDecimalAmount(storedLoans1[0]) + expectedAmount = Decimal(Decimal('10') + 10 * Decimal(storedInterest["interestPerBlock"]) - Decimal('0.00000001')).quantize(Decimal('1E-8'), ROUND_UP) + assert_equal(storedAmount1, expectedAmount) + + [balanceDUSDafter, _] = self.nodes[0].getaccount(self.account)[0].split('@') + assert_equal(Decimal(balanceDUSDafter), Decimal(balanceDUSDbefore) - Decimal('0.00000001')) + + balanceDUSDbefore = balanceDUSDafter + vaultBefore = vault + + # payback 1 coin + self.nodes[0].paybackloan({ + 'vaultId': self.vaultId9, + 'from': self.account, + 'amounts': ["1.00000000@" + self.symboldUSD]}) + self.nodes[0].generate(1) + + vault = self.nodes[0].getvault(self.vaultId9, verbose) + [loanAmountBefore, _] = vaultBefore["loanAmounts"][0].split('@') + [loanAmount, _] = vault["loanAmounts"][0].split('@') + [interestAmount, _] = vault["interestAmounts"][0].split('@') + assert_equal(Decimal(loanAmount), Decimal(loanAmountBefore) - Decimal('1.00000000') + Decimal(interestAmount)) + + [balanceDUSDafter, _] = self.nodes[0].getaccount(self.account)[0].split('@') + assert_equal(Decimal(balanceDUSDafter), Decimal(balanceDUSDbefore) - Decimal('1.00000000')) def run_test(self): self.setup() @@ -891,7 +1023,7 @@ def run_test(self): self.vault_status_with_negative_interest() self.payback_interests_and_continue_with_negative_interest() self.let_loan_be_paid_by_negative_interests() - + self.various_payback_tests() if __name__ == '__main__': NegativeInterestTest().main() diff --git a/test/functional/feature_negative_loan_interest.py b/test/functional/feature_negative_loan_interest.py index 89cbdeb8a1..a0e552bb31 100755 --- a/test/functional/feature_negative_loan_interest.py +++ b/test/functional/feature_negative_loan_interest.py @@ -6,27 +6,29 @@ """Test negative interest.""" from test_framework.test_framework import DefiTestFramework +from decimal import Decimal from test_framework.util import assert_equal import time +import calendar class NegativeInterestTest (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', '-fortcanningheight=1', '-fortcanninghillheight=1', '-fortcanningcrunchheight=1', '-fortcanninggreatworldheight=1', '-jellyfish_regtest=1']] + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-fortcanningheight=1', '-fortcanninghillheight=1', '-fortcanningcrunchheight=1', '-fortcanninggreatworldheight=1', '-fortcanningepilogueheight=1','-jellyfish_regtest=1', '-simulatemainnet=1']] def run_test(self): # Create tokens for tests self.setup_test_tokens() - # Setup pools - self.setup_test_pools() - # Setup Oracles self.setup_test_oracles() + # Setup pools + self.setup_test_pools() + # Test negative interest self.test_negative_interest() @@ -99,10 +101,20 @@ def setup_test_oracles(self): # Set Oracle prices oracle_prices = [ - {"currency": "USD", "tokenAmount": f"1@{self.symbolDFI}"}, + {"currency": "USD", "tokenAmount": f"10@{self.symbolDFI}"}, ] - self.nodes[0].setoracledata(oracle, int(time.time()), oracle_prices) - self.nodes[0].generate(10) + timestamp = calendar.timegm(time.gmtime()) + self.nodes[0].setoracledata(oracle, timestamp, oracle_prices) + self.nodes[0].generate(1) + + self.oracle_address2 = self.nodes[0].getnewaddress("", "legacy") + self.oracle_id2 = self.nodes[0].appointoracle(self.oracle_address2, price_feed, 10) + self.nodes[0].generate(1) + + # feed oracle + timestamp = calendar.timegm(time.gmtime()) + self.nodes[0].setoracledata(self.oracle_id2, timestamp, oracle_prices) + self.nodes[0].generate(120) # Set collateral tokens self.nodes[0].setcollateraltoken({ @@ -114,7 +126,7 @@ def setup_test_oracles(self): # Create loan scheme self.nodes[0].createloanscheme(150, 5, 'LOAN001') - self.nodes[0].generate(1) + self.nodes[0].generate(120) def test_negative_interest(self): @@ -145,19 +157,19 @@ def test_negative_interest(self): assert_equal(stored_interest['interestPerBlock'], '0.000000000000000000000000') assert_equal(stored_interest['height'], self.nodes[0].getblockcount()) - # Set overall negative interest rate to -2 + # Set overall negative interest rate to -5 self.nodes[0].setgov({"ATTRIBUTES":{f'v0/token/{self.idDUSD}/loan_minting_interest':'-10'}}) self.nodes[0].generate(1) # Check stored interest stored_interest = self.nodes[0].getstoredinterest(vault_id, self.symbolDUSD) assert_equal(stored_interest['interestToHeight'], '0.000000000000000000000000') - assert_equal(stored_interest['interestPerBlock'], '-0.000000951293759512937595') + assert_equal(stored_interest['interestPerBlock'], '-0.000000047564687975646879') assert_equal(stored_interest['height'], self.nodes[0].getblockcount()) # Check loan interest vault = self.nodes[0].getvault(vault_id) - assert_equal(vault['interestAmounts'], [f'-0.00000095@{self.symbolDUSD}']) + assert_equal(vault['interestAmounts'], [f'-0.00000004@{self.symbolDUSD}']) # Take DUSD loan self.nodes[0].takeloan({ "vaultId": vault_id, "amounts": f"1@{self.symbolDUSD}"}) @@ -166,23 +178,35 @@ def test_negative_interest(self): # Check IPB doubled and ITH wiped stored_interest = self.nodes[0].getstoredinterest(vault_id, self.symbolDUSD) assert_equal(stored_interest['interestToHeight'], '0.000000000000000000000000') - assert_equal(stored_interest['interestPerBlock'], '-0.000001902586615296803652') + assert_equal(stored_interest['interestPerBlock'], '-0.000000095129374048706240') assert_equal(stored_interest['height'], self.nodes[0].getblockcount()) # Check loan interest vault = self.nodes[0].getvault(vault_id) - assert_equal(vault['interestAmounts'], [f'-0.00000190@{self.symbolDUSD}']) + assert_equal(vault['interestAmounts'], [f'-0.00000009@{self.symbolDUSD}']) + + [balanceDUSDbefore, _] = self.nodes[0].getaccount(vault_address)[1].split('@') + vaultBefore = self.nodes[0].getvault(vault_id, True) # Payback almost all of the loan amount self.nodes[0].paybackloan({ 'vaultId': vault_id, - 'from': "*", - 'amounts': f'1.99999711@{self.symbolDUSD}' + 'from': vault_address, + 'amounts': f'1.9999@{self.symbolDUSD}' }) self.nodes[0].generate(1) + vault = self.nodes[0].getvault(vault_id, True) + [loanAmountBefore, _] = vaultBefore["loanAmounts"][0].split('@') + [loanAmount, _] = vault["loanAmounts"][0].split('@') + [interestAmount, _] = vault["interestAmounts"][0].split('@') + assert_equal(Decimal(loanAmount), Decimal(loanAmountBefore) - Decimal('1.9999') + Decimal(interestAmount)) + + [balanceDUSDafter, _] = self.nodes[0].getaccount(vault_address)[1].split('@') + assert_equal(Decimal(balanceDUSDafter), Decimal(balanceDUSDbefore) - Decimal('1.9999')) + # Set negative interest rate very high to speed up negating vault amount - self.nodes[0].setgov({"ATTRIBUTES":{f'v0/token/{self.idDUSD}/loan_minting_interest':'-300000'}}) + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/token/{self.idDUSD}/loan_minting_interest':'-8000000'}}) self.nodes[0].generate(20) # Check loan amount and interest fully negated