diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index 15a64b855f..eb82670707 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -527,10 +527,7 @@ class CCustomMetadataParseVisitor : public boost::static_visitor Res operator()(CLoanSetCollateralTokenMessage& obj) const { auto res = isPostFortCanningFork(); - if (!res) - return res; - res = isPostFortCanningSpiceGardenFork(); - return res ? Res::Err("called after FortCanningSpiceGarden height") : serialize(obj); + return !res ? res : serialize(obj); } Res operator()(CLoanSetLoanTokenMessage& obj) const { @@ -2307,15 +2304,41 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (!res) return res; + if (!HasFoundationAuth()) + return Res::Err("tx not from foundation member!"); + + if (height >= static_cast(consensus.FortCanningSpiceGardenHeight)) + { + const auto& tokenId = obj.idToken.v; + + auto attributes = mnview.GetAttributes(); + attributes->time = time; + + CDataStructureV0 collateralEnabled{AttributeTypes::Token, tokenId, TokenKeys::LoanCollateralEnabled}; + CDataStructureV0 collateralFactor{AttributeTypes::Token, tokenId, TokenKeys::LoanCollateralFactor}; + CDataStructureV0 pairKey{AttributeTypes::Token, tokenId, TokenKeys::FixedIntervalPriceId}; + + attributes->SetValue(collateralEnabled, true); + attributes->SetValue(collateralFactor, obj.factor); + attributes->SetValue(pairKey, obj.fixedIntervalPriceId); + + res = attributes->Validate(mnview); + if (!res) + return res; + + res = attributes->Apply(mnview, height); + if (!res) + return res; + + return mnview.SetVariable(*attributes); + } + CLoanSetCollateralTokenImplementation collToken; static_cast(collToken) = obj; collToken.creationTx = tx.GetHash(); collToken.creationHeight = height; - if (!HasFoundationAuth()) - return Res::Err("tx not from foundation member!"); - auto token = mnview.GetToken(collToken.idToken); if (!token) return Res::Err("token %s does not exist!", collToken.idToken.ToString()); diff --git a/test/functional/feature_loan_setcollateraltoken.py b/test/functional/feature_loan_setcollateraltoken.py index e24656ab03..2c4b7fffa1 100755 --- a/test/functional/feature_loan_setcollateraltoken.py +++ b/test/functional/feature_loan_setcollateraltoken.py @@ -19,7 +19,7 @@ def set_test_params(self): self.num_nodes = 1 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', '-eunosheight=50', '-fortcanningheight=50', '-fortcanninghillheight=50', '-fortcanningspicegardenheight=150', '-txindex=1']] def run_test(self): assert_equal(len(self.nodes[0].listtokens()), 1) # only one token == DFI @@ -27,20 +27,29 @@ def run_test(self): print("Generating initial chain...") self.nodes[0].generate(101) + symbolDFI = "DFI" + symbolBTC = "BTC" + symbolGOOGL = "GOOGL" + self.nodes[0].createtoken({ - "symbol": "BTC", - "name": "BTC token", + "symbol": symbolBTC, + "name": symbolBTC, "isDAT": True, "collateralAddress": self.nodes[0].get_genesis_keys().ownerAuthAddress }) - self.nodes[0].generate(1) - symbolDFI = "DFI" - symbolBTC = "BTC" + self.nodes[0].createtoken({ + "symbol": symbolGOOGL, + "name": symbolGOOGL, + "isDAT": True, + "collateralAddress": self.nodes[0].get_genesis_keys().ownerAuthAddress + }) + self.nodes[0].generate(1) idDFI = list(self.nodes[0].gettoken(symbolDFI).keys())[0] idBTC = list(self.nodes[0].gettoken(symbolBTC).keys())[0] + idGOOGL = list(self.nodes[0].gettoken(symbolGOOGL).keys())[0] try: self.nodes[0].setcollateraltoken({ @@ -62,8 +71,10 @@ def run_test(self): oracle_address1 = self.nodes[0].getnewaddress("", "legacy") price_feeds1 = [ - {"currency": "USD", "token": "DFI"}, - {"currency": "USD", "token": "BTC"}] + {"currency": "USD", "token": symbolDFI}, + {"currency": "USD", "token": symbolBTC}, + {"currency": "USD", "token": symbolGOOGL}, + ] oracle_id1 = self.nodes[0].appointoracle(oracle_address1, price_feeds1, 10) self.nodes[0].generate(1) @@ -77,8 +88,10 @@ def run_test(self): assert("no live oracles for specified request" in errorString) oracle1_prices = [ - {"currency": "USD", "tokenAmount": "1@DFI"}, - {"currency": "USD", "tokenAmount": "1@BTC"}] + {"currency": "USD", "tokenAmount": f'1@{symbolDFI}'}, + {"currency": "USD", "tokenAmount": f'1@{symbolBTC}'}, + {"currency": "USD", "tokenAmount": f'1@{symbolGOOGL}'}, + ] timestamp = calendar.timegm(time.gmtime()) self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices) self.nodes[0].generate(1) @@ -123,6 +136,8 @@ def run_test(self): self.nodes[0].generate(1) + dfi_activation_height = self.nodes[0].getblockcount() + collTokens = self.nodes[0].listcollateraltokens() assert_equal(len(collTokens), 2) @@ -138,6 +153,8 @@ def run_test(self): self.nodes[0].generate(1) + btc_activation_height = self.nodes[0].getblockcount() + collTokens = self.nodes[0].listcollateraltokens() assert_equal(len(collTokens), 3) @@ -159,13 +176,13 @@ def run_test(self): assert_equal(collTokens["token"], symbolDFI) assert_equal(collTokens["factor"], Decimal('0.5')) - assert_equal(collTokens["activateAfterBlock"], 105) + assert_equal(collTokens["activateAfterBlock"], dfi_activation_height) collTokens = self.nodes[0].getcollateraltoken(idBTC) assert_equal(collTokens["token"], symbolBTC) assert_equal(collTokens["factor"], Decimal('0.9')) - assert_equal(collTokens["activateAfterBlock"], 106) + assert_equal(collTokens["activateAfterBlock"], btc_activation_height) self.nodes[0].generate(30) @@ -179,7 +196,7 @@ def run_test(self): assert_equal(collTokens["token"], symbolBTC) assert_equal(collTokens["factor"], Decimal('0.9')) - assert_equal(collTokens["activateAfterBlock"], 106) + assert_equal(collTokens["activateAfterBlock"], btc_activation_height) self.nodes[0].setcollateraltoken({ 'token': idBTC, @@ -191,5 +208,21 @@ def run_test(self): collTokens = self.nodes[0].listcollateraltokens() assert_equal(len(collTokens), 4) + # Move to fork height + self.nodes[0].generate(150 - self.nodes[0].getblockcount()) + + # Create collateral token + self.nodes[0].setcollateraltoken({ + 'token': idGOOGL, + 'factor': 0.5, + 'fixedIntervalPriceId': "GOOGL/USD"}) + self.nodes[0].generate(1) + + # Check attributess + result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] + assert_equal(result[f'v0/token/{idGOOGL}/loan_collateral_enabled'], 'true') + assert_equal(result[f'v0/token/{idGOOGL}/loan_collateral_factor'], '0.5') + assert_equal(result[f'v0/token/{idGOOGL}/fixed_interval_price_id'], 'GOOGL/USD') + if __name__ == '__main__': LoanSetCollateralTokenTest().main() diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index 42fdbbd165..a9eb7d4f2c 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -756,10 +756,6 @@ def loan_and_collateral_token_to_govvar(self): 'fixedIntervalPriceId': "DUSD/USD", 'mintable': True, 'interest': 1}) - assert_raises_rpc_error(-32600, 'called after FortCanningSpiceGarden height', self.nodes[0].setcollateraltoken, { - 'token': self.idDFI, - 'factor': 1, - 'fixedIntervalPriceId': "DFI/USD"}) # Test setting collateral token partially self.nodes[0].setgov({"ATTRIBUTES":{f'v0/token/{self.idETH}/fixed_interval_price_id':'ETH/USD', f'v0/token/{self.idETH}/loan_collateral_enabled':'true'}})