diff --git a/src/masternodes/accounts.cpp b/src/masternodes/accounts.cpp index 2b79fb5368..bef8fe94d8 100644 --- a/src/masternodes/accounts.cpp +++ b/src/masternodes/accounts.cpp @@ -101,7 +101,7 @@ uint32_t CAccountsView::GetBalancesHeight(CScript const & owner) Res CAccountsView::StoreFuturesUserValues(const CFuturesUserKey& key, const CFuturesUserValue& futures) { - if (!WriteBy(key, futures)) { + if (!WriteBy(key, futures)) { return Res::Err("Failed to store futures"); } @@ -110,12 +110,12 @@ Res CAccountsView::StoreFuturesUserValues(const CFuturesUserKey& key, const CFut void CAccountsView::ForEachFuturesUserValues(std::function callback, const CFuturesUserKey& start) { - ForEach(callback, start); + ForEach(callback, start); } Res CAccountsView::EraseFuturesUserValues(const CFuturesUserKey& key) { - if (!EraseBy(key)) { + if (!EraseBy(key)) { return Res::Err("Failed to erase futures"); } @@ -125,7 +125,7 @@ Res CAccountsView::EraseFuturesUserValues(const CFuturesUserKey& key) boost::optional CAccountsView::GetMostRecentFuturesHeight() { const CFuturesUserKey key{std::numeric_limits::max(), {}, std::numeric_limits::max()}; - auto it = LowerBound(key); + auto it = LowerBound(key); if (it.Valid()) { return it.Key().height; } @@ -133,25 +133,11 @@ boost::optional CAccountsView::GetMostRecentFuturesHeight() return {}; } -Res CAccountsView::StoreFuturesDestValues(const CFuturesUserKey& key, const CFuturesUserValue& destination) -{ - if (!WriteBy(key, destination)) { - return Res::Err("Failed to store futures destination"); - } - - return Res::Ok(); -} - ResVal CAccountsView::GetFuturesUserValues(const CFuturesUserKey& key) { CFuturesUserValue source; - if (!ReadBy(key, source)) { + if (!ReadBy(key, source)) { return Res::Err("Failed to read futures source"); } return {source, Res::Ok()}; } - -void CAccountsView::ForEachFuturesDestValues(std::function callback, const CFuturesUserKey& start) -{ - ForEach(callback, start); -} diff --git a/src/masternodes/accounts.h b/src/masternodes/accounts.h index fe259f17c5..a720df62cb 100644 --- a/src/masternodes/accounts.h +++ b/src/masternodes/accounts.h @@ -70,18 +70,15 @@ class CAccountsView : public virtual CStorageView Res UpdateBalancesHeight(CScript const & owner, uint32_t height); Res StoreFuturesUserValues(const CFuturesUserKey& key, const CFuturesUserValue& futures); - Res StoreFuturesDestValues(const CFuturesUserKey& key, const CFuturesUserValue& destination); ResVal GetFuturesUserValues(const CFuturesUserKey& key); Res EraseFuturesUserValues(const CFuturesUserKey& key); boost::optional GetMostRecentFuturesHeight(); void ForEachFuturesUserValues(std::function callback, const CFuturesUserKey& start = {}); - void ForEachFuturesDestValues(std::function callback, const CFuturesUserKey& start = {}); // tags struct ByBalanceKey { static constexpr uint8_t prefix() { return 'a'; } }; struct ByHeightKey { static constexpr uint8_t prefix() { return 'b'; } }; - struct ByFuturesSourceKey { static constexpr uint8_t prefix() { return 'J'; } }; - struct ByFuturesDestKey { static constexpr uint8_t prefix() { return 's'; } }; + struct ByFuturesSwapKey { static constexpr uint8_t prefix() { return 'J'; } }; private: Res SetBalance(CScript const & owner, CTokenAmount amount); diff --git a/src/masternodes/govvariables/attributes.cpp b/src/masternodes/govvariables/attributes.cpp index 383908f825..f8af1de9da 100644 --- a/src/masternodes/govvariables/attributes.cpp +++ b/src/masternodes/govvariables/attributes.cpp @@ -4,8 +4,11 @@ #include -#include /// ValueFromAmount +#include /// CAccountsHistoryWriter #include /// CCustomCSView +#include /// CustomTxType + +#include /// ValueFromAmount #include extern UniValue AmountsToJSON(TAmounts const & diffs); @@ -146,7 +149,8 @@ const std::map>& ATTRIBUTES::displayKeys { AttributeTypes::Live, { {EconomyKeys::PaybackDFITokens, "dfi_payback_tokens"}, - {EconomyKeys::DFIP2203Tokens, "dfip_tokens"}, + {EconomyKeys::DFIP2203Current, "dfip2203_current"}, + {EconomyKeys::DFIP2203Burned, "dfip2203_burned"}, } }, }; @@ -370,14 +374,9 @@ Res ATTRIBUTES::RefundFuturesContracts(CCustomCSView &mnview, const uint32_t hei return Res::Ok(); } - const uint32_t startHeight = height - (height % blockPeriod); std::map userFuturesValues; mnview.ForEachFuturesUserValues([&](const CFuturesUserKey& key, const CFuturesUserValue& futuresValues) { - if (key.height <= startHeight) { - return false; - } - if (tokenID != std::numeric_limits::max()) { if (futuresValues.source.nTokenId.v == tokenID || futuresValues.destination == tokenID) { userFuturesValues[key] = futuresValues; @@ -394,18 +393,22 @@ Res ATTRIBUTES::RefundFuturesContracts(CCustomCSView &mnview, const uint32_t hei return contractAddressValue; } - CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Tokens}; + CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Current}; auto balances = GetValue(liveKey, CBalances{}); + + CHistoryWriters writers{paccountHistoryDB.get(), nullptr, nullptr}; + CAccountsHistoryWriter view(mnview, height, ~0u, {}, uint8_t(CustomTxType::FutureSwapRefund), &writers); + for (const auto& [key, value] : userFuturesValues) { - mnview.EraseFuturesUserValues(key); + view.EraseFuturesUserValues(key); - auto res = mnview.SubBalance(*contractAddressValue, value.source); + auto res = view.SubBalance(*contractAddressValue, value.source); if (!res) { return res; } - res = mnview.AddBalance(key.owner, value.source); + res = view.AddBalance(key.owner, value.source); if (!res) { return res; } @@ -416,6 +419,8 @@ Res ATTRIBUTES::RefundFuturesContracts(CCustomCSView &mnview, const uint32_t hei } } + view.Flush(); + attributes[liveKey] = balances; return Res::Ok(); @@ -685,13 +690,6 @@ Res ATTRIBUTES::Apply(CCustomCSView & mnview, const uint32_t height) if (GetValue(activeKey, false)) { return Res::Err("Cannot set block period while DFIP2203 is active"); } - - auto blockPeriod = boost::get(attribute.second); - const auto recentFuturesHeight = mnview.GetMostRecentFuturesHeight(); - - if (recentFuturesHeight && *recentFuturesHeight > height - (height % blockPeriod)) { - return Res::Err("Historical Futures contracts in this period"); - } } } } diff --git a/src/masternodes/govvariables/attributes.h b/src/masternodes/govvariables/attributes.h index f7f32ec4db..75cad78450 100644 --- a/src/masternodes/govvariables/attributes.h +++ b/src/masternodes/govvariables/attributes.h @@ -29,7 +29,8 @@ enum ParamIDs : uint8_t { enum EconomyKeys : uint8_t { PaybackDFITokens = 'a', PaybackTokens = 'b', - DFIP2203Tokens = 'c', + DFIP2203Current = 'c', + DFIP2203Burned = 'd', }; enum DFIPKeys : uint8_t { diff --git a/src/masternodes/masternodes.h b/src/masternodes/masternodes.h index b352d52913..3ab67523e3 100644 --- a/src/masternodes/masternodes.h +++ b/src/masternodes/masternodes.h @@ -363,7 +363,7 @@ class CCustomCSView CFoundationsDebtView :: Debt, CAnchorRewardsView :: BtcTx, CTokensView :: ID, Symbol, CreationTx, LastDctId, - CAccountsView :: ByBalanceKey, ByHeightKey, ByFuturesSourceKey, ByFuturesDestKey, + CAccountsView :: ByBalanceKey, ByHeightKey, ByFuturesSwapKey, CCommunityBalancesView :: ById, CUndosView :: ByUndoKey, CPoolPairView :: ByID, ByPair, ByShare, ByIDPair, ByPoolSwap, ByReserves, ByRewardPct, ByRewardLoanPct, diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index 24c5acdb77..15b41802f7 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -79,6 +79,8 @@ std::string ToString(CustomTxType type) { case CustomTxType::PaybackLoan: return "PaybackLoan"; case CustomTxType::PaybackLoanV2: return "PaybackLoan"; case CustomTxType::AuctionBid: return "AuctionBid"; + case CustomTxType::FutureSwapExecution: return "FutureSwapExecution"; + case CustomTxType::FutureSwapRefund: return "FutureSwapRefund"; case CustomTxType::Reject: return "Reject"; case CustomTxType::None: return "None"; } @@ -162,6 +164,8 @@ CCustomTxMessage customTypeToMessage(CustomTxType txType) { case CustomTxType::PaybackLoan: return CLoanPaybackLoanMessage{}; case CustomTxType::PaybackLoanV2: return CLoanPaybackLoanV2Message{}; case CustomTxType::AuctionBid: return CAuctionBidMessage{}; + case CustomTxType::FutureSwapExecution: return CCustomTxMessageNone{}; + case CustomTxType::FutureSwapRefund: return CCustomTxMessageNone{}; case CustomTxType::Reject: return CCustomTxMessageNone{}; case CustomTxType::None: return CCustomTxMessageNone{}; } @@ -1520,19 +1524,13 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor return contractAddressValue; } - CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Tokens}; + CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Current}; auto balances = attributes->GetValue(liveKey, CBalances{}); if (obj.withdraw) { - const auto blockPeriod = attributes->GetValue(blockKey, CAmount{}); - const uint32_t startHeight = height - (height % blockPeriod); std::map userFuturesValues; mnview.ForEachFuturesUserValues([&](const CFuturesUserKey& key, const CFuturesUserValue& futuresValues) { - if (key.height <= startHeight) { - return false; - } - if (key.owner == obj.owner && futuresValues.source.nTokenId == obj.source.nTokenId && futuresValues.destination == obj.destination) { diff --git a/src/masternodes/mn_checks.h b/src/masternodes/mn_checks.h index 298c3197c4..07a14eb129 100644 --- a/src/masternodes/mn_checks.h +++ b/src/masternodes/mn_checks.h @@ -101,7 +101,9 @@ enum class CustomTxType : uint8_t TakeLoan = 'X', PaybackLoan = 'H', PaybackLoanV2 = 'k', - AuctionBid = 'I' + AuctionBid = 'I', + FutureSwapExecution = 'q', + FutureSwapRefund = 'w', }; inline CustomTxType CustomTxCodeToType(uint8_t ch) { @@ -157,6 +159,8 @@ inline CustomTxType CustomTxCodeToType(uint8_t ch) { case CustomTxType::PaybackLoan: case CustomTxType::PaybackLoanV2: case CustomTxType::AuctionBid: + case CustomTxType::FutureSwapExecution: + case CustomTxType::FutureSwapRefund: case CustomTxType::Reject: case CustomTxType::None: return type; diff --git a/src/masternodes/mn_rpc.cpp b/src/masternodes/mn_rpc.cpp index d8de7e82d6..7515fef733 100644 --- a/src/masternodes/mn_rpc.cpp +++ b/src/masternodes/mn_rpc.cpp @@ -453,7 +453,7 @@ CWalletCoinsUnlocker GetWallet(const JSONRPCRequest& request) { return CWalletCoinsUnlocker{std::move(wallet)}; } -std::optional> GetFuturesBlockAndReward() +std::optional GetFuturesBlock() { LOCK(cs_main); @@ -474,7 +474,7 @@ std::optional> GetFuturesBlockAndReward() return {}; } - return std::pair{attributes->GetValue(blockKey, CAmount{}), attributes->GetValue(rewardKey, CAmount{})}; + return attributes->GetValue(blockKey, CAmount{}); } UniValue setgov(const JSONRPCRequest& request) { diff --git a/src/masternodes/mn_rpc.h b/src/masternodes/mn_rpc.h index 67cc490017..2621e61880 100644 --- a/src/masternodes/mn_rpc.h +++ b/src/masternodes/mn_rpc.h @@ -61,6 +61,6 @@ CAccounts SelectAccountsByTargetBalances(const CAccounts& accounts, const CBalan void execTestTx(const CTransaction& tx, uint32_t height, CTransactionRef optAuthTx = {}); CScript CreateScriptForHTLC(const JSONRPCRequest& request, uint32_t &blocks, std::vector& image); CPubKey PublickeyFromString(const std::string &pubkey); -std::optional> GetFuturesBlockAndReward(); +std::optional GetFuturesBlock(); #endif // DEFI_MASTERNODES_MN_RPC_H diff --git a/src/masternodes/rpc_accounts.cpp b/src/masternodes/rpc_accounts.cpp index 755f3944e9..598d73e1c3 100644 --- a/src/masternodes/rpc_accounts.cpp +++ b/src/masternodes/rpc_accounts.cpp @@ -1869,7 +1869,7 @@ UniValue getburninfo(const JSONRPCRequest& request) { paybackfees = std::move(paybacks.tokensFee); paybacktokens = std::move(paybacks.tokensPayback); - liveKey = {AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Tokens}; + liveKey = {AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Burned}; dfi2203Tokens = attributes->GetValue(liveKey, CBalances{}); } @@ -2041,7 +2041,7 @@ UniValue futureswap(const JSONRPCRequest& request) { { {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "Address to fund contract and receive resulting token"}, {"amount", RPCArg::Type::STR, RPCArg::Optional::NO, "Amount to send in amount@token format"}, - {"destination", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Expected dToken if DUSD supplied"}, + {"destination", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Expected dToken if DUSD supplied"}, {"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of json objects", { @@ -2080,7 +2080,14 @@ UniValue futureswap(const JSONRPCRequest& request) { msg.source = DecodeAmount(pwallet->chain(), request.params[1], ""); if (!request.params[2].isNull()) { - msg.destination = request.params[2].get_int(); + DCT_ID destTokenID{}; + + const auto destToken = pcustomcsview->GetTokenGuessId(request.params[2].getValStr(), destTokenID); + if (!destToken) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Destination token not found"); + } + + msg.destination = destTokenID.v; } // Encode @@ -2126,7 +2133,7 @@ UniValue withdrawfutureswap(const JSONRPCRequest& request) { { {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "Address used to fund contract with"}, {"amount", RPCArg::Type::STR, RPCArg::Optional::NO, "Amount to withdraw in amount@token format"}, - {"destination", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "The dToken if DUSD supplied"}, + {"destination", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "The dToken if DUSD supplied"}, {"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of json objects", { @@ -2164,7 +2171,14 @@ UniValue withdrawfutureswap(const JSONRPCRequest& request) { msg.withdraw = true; if (!request.params[2].isNull()) { - msg.destination = request.params[2].get_int(); + DCT_ID destTokenID{}; + + const auto destToken = pcustomcsview->GetTokenGuessId(request.params[2].getValStr(), destTokenID); + if (!destToken) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Destination token not found"); + } + + msg.destination = destTokenID.v; } // Encode @@ -2218,21 +2232,10 @@ UniValue listpendingfutureswaps(const JSONRPCRequest& request) { }.Check(request); UniValue listFutures{UniValue::VARR}; - const auto blockAndReward = GetFuturesBlockAndReward(); - if (!blockAndReward) { - return listFutures; - } LOCK(cs_main); - const auto currentHeight = ::ChainActive().Height(); - const auto startPeriod = currentHeight - (currentHeight % blockAndReward->first); - pcustomcsview->ForEachFuturesUserValues([&](const CFuturesUserKey& key, const CFuturesUserValue& futuresValues){ - if (key.height <= startPeriod) { - return false; - } - CTxDestination dest; ExtractDestination(key.owner, dest); if (!IsValidDestination(dest)) { @@ -2288,30 +2291,20 @@ UniValue getpendingfutureswaps(const JSONRPCRequest& request) { }.Check(request); UniValue listValues{UniValue::VARR}; - const auto blockAndReward = GetFuturesBlockAndReward(); - if (!blockAndReward) { - return listValues; - } const auto owner = DecodeScript(request.params[0].get_str()); LOCK(cs_main); - const auto currentHeight = ::ChainActive().Height(); - const auto startPeriod = currentHeight - (currentHeight % blockAndReward->first); - std::vector storedFutures; pcustomcsview->ForEachFuturesUserValues([&](const CFuturesUserKey& key, const CFuturesUserValue& futuresValues) { - if (key.height <= startPeriod) { - return false; - } if (key.owner == owner) { storedFutures.push_back(futuresValues); } return true; - }, {static_cast(currentHeight), owner, std::numeric_limits::max()}); + }, {static_cast(::ChainActive().Height()), owner, std::numeric_limits::max()}); for (const auto& item : storedFutures) { UniValue value{UniValue::VOBJ}; @@ -2343,173 +2336,6 @@ UniValue getpendingfutureswaps(const JSONRPCRequest& request) { } -UniValue listfutureswaphistory(const JSONRPCRequest& request) { - auto pwallet = GetWallet(request); - - RPCHelpMan{"listfutureswaphistory", - "\nReturns information about future swap history.\n", - { - {"owner", RPCArg::Type::STR, RPCArg::Optional::OMITTED, - "Single account ID (CScript or address) or reserved words: \"mine\" - to list history for all owned accounts or \"all\" to list whole DB (default = \"mine\")."}, - {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", - { - {"maxBlockHeight", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, - "Optional height to iterate from (downto genesis block), (default = chaintip)."}, - {"depth", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, - "Maximum depth, from the genesis block is the default"}, - {"token", RPCArg::Type::STR, RPCArg::Optional::OMITTED, - "Filter by token"}, - {"limit", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, - "Maximum number of records to return, 100 by default"}, - }, - }, - }, - RPCResult{ - "[{},{}...] (array) Objects with future swap history information\n" - }, - RPCExamples{ - HelpExampleCli("listfutureswaphistory", "all '{\"maxBlockHeight\":160,\"depth\":10}'") - + HelpExampleRpc("listfutureswaphistory", "all, '{\"maxBlockHeight\":160,\"depth\":10}'") - }, - }.Check(request); - - - std::string accounts = "mine"; - if (request.params.size() > 0) { - accounts = request.params[0].getValStr(); - } - - if (!paccountHistoryDB) { - throw JSONRPCError(RPC_INVALID_REQUEST, "-acindex is needed for account history"); - } - - auto maxBlockHeight = std::numeric_limits::max(); - auto depth{maxBlockHeight}; - std::string tokenFilter; - uint32_t limit = 100; - - if (request.params.size() > 1) { - UniValue optionsObj = request.params[1].get_obj(); - RPCTypeCheckObj(optionsObj, - { - {"maxBlockHeight", UniValueType(UniValue::VNUM)}, - {"depth", UniValueType(UniValue::VNUM)}, - {"token", UniValueType(UniValue::VSTR)}, - {"limit", UniValueType(UniValue::VNUM)}, - }, true, true); - - if (!optionsObj["maxBlockHeight"].isNull()) { - maxBlockHeight = (uint32_t) optionsObj["maxBlockHeight"].get_int64(); - } - - if (!optionsObj["depth"].isNull()) { - depth = (uint32_t) optionsObj["depth"].get_int64(); - } - - if (!optionsObj["token"].isNull()) { - tokenFilter = optionsObj["token"].get_str(); - } - - if (!optionsObj["limit"].isNull()) { - limit = (uint32_t) optionsObj["limit"].get_int64(); - } - - if (limit == 0) { - limit = std::numeric_limits::max(); - } - } - - pwallet->BlockUntilSyncedToCurrentChain(); - - std::function isMatchOwner = [](const CScript &) { - return true; - }; - - DCT_ID tokenID{}; - - if (!tokenFilter.empty()) { - const auto token = pcustomcsview->GetTokenGuessId(tokenFilter, tokenID); - - if (!token) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Token %s does not exist.", tokenFilter)); - } - } - - CScript account{}; - auto isMine{false}; - auto filter{ISMINE_ALL}; - - if (accounts == "mine") { - isMine = true; - filter = ISMINE_SPENDABLE; - } else if (accounts != "all") { - account = DecodeScript(accounts); - isMatchOwner = [&account](CScript const & owner) { - return owner == account; - }; - } - - maxBlockHeight = std::min(maxBlockHeight, uint32_t(::ChainActive().Height())); - if (depth > maxBlockHeight) { - depth = maxBlockHeight; - } - - UniValue result(UniValue::VARR); - pcustomcsview->ForEachFuturesDestValues([&](const CFuturesUserKey& key, const CFuturesUserValue& destination) { - if (key.height < maxBlockHeight - depth) { - return false; - } - - if (!isMatchOwner(key.owner)) { - return true; - } - - if (isMine && !(IsMineCached(*pwallet, key.owner) & filter)) { - return true; - } - - const auto userValue = pcustomcsview->GetFuturesUserValues(key); - if (!userValue) { - return true; - } - - const auto& source = userValue.val->source; - - if (!tokenFilter.empty()) { - if (tokenID != source.nTokenId && tokenID != destination.source.nTokenId) { - return true; - } - } - - const auto sourceToken = pcustomcsview->GetToken(source.nTokenId); - if (!sourceToken) { - return true; - } - - const auto destToken = pcustomcsview->GetToken(destination.source.nTokenId); - if (!destToken) { - return true; - } - - CTxDestination dest; - if (!ExtractDestination(key.owner, dest)) { - return true; - } - - UniValue item(UniValue::VOBJ); - item.pushKV("height", static_cast(destination.destination)); - item.pushKV("address", EncodeDestination(dest)); - item.pushKV("source", GetDecimaleString(source.nValue) + '@' + sourceToken->symbol); - item.pushKV("destination", GetDecimaleString(destination.source.nValue) + '@' + destToken->symbol); - result.push_back(item); - - return --limit > 0; - }, {maxBlockHeight, account, std::numeric_limits::max()}); - - return result; -} - - static const CRPCCommand commands[] = { // category name actor (function) params @@ -2533,7 +2359,6 @@ static const CRPCCommand commands[] = {"accounts", "withdrawfutureswap", &withdrawfutureswap, {"address", "amount", "destination", "inputs"}}, {"accounts", "listpendingfutureswaps", &listpendingfutureswaps, {}}, {"accounts", "getpendingfutureswaps", &getpendingfutureswaps, {"address"}}, - {"accounts", "listfutureswaphistory", &listfutureswaphistory, {"owner", "options"}}, }; void RegisterAccountsRPCCommands(CRPCTable& tableRPC) { diff --git a/src/masternodes/rpc_oracles.cpp b/src/masternodes/rpc_oracles.cpp index 42ef3b8102..93b3c8c311 100644 --- a/src/masternodes/rpc_oracles.cpp +++ b/src/masternodes/rpc_oracles.cpp @@ -1140,12 +1140,12 @@ UniValue getfutureswapblock(const JSONRPCRequest& request) { const auto currentHeight = ::ChainActive().Height(); - const auto blockAndReward = GetFuturesBlockAndReward(); - if (!blockAndReward) { + const auto block = GetFuturesBlock(); + if (!block) { return 0; } - return currentHeight + (blockAndReward->first - (currentHeight % blockAndReward->first)); + return currentHeight + (*block - (currentHeight % *block)); } diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 9136385b65..48b8ab26b0 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -215,9 +215,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "accounttoaccount", 2, "inputs" }, { "accounttoutxos", 1, "to" }, { "accounttoutxos", 2, "inputs" }, - { "futureswap", 2, "destination"}, { "futureswap", 3, "inputs"}, - { "withdrawfutureswap", 2, "destination"}, { "withdrawfutureswap", 3, "inputs"}, { "icx_createorder", 0, "order" }, @@ -309,7 +307,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getaccounthistory", 2, "txn" }, { "listburnhistory", 0, "options" }, { "accounthistorycount", 1, "options" }, - { "listfutureswaphistory", 1, "options" }, { "setgov", 0, "variables" }, { "setgov", 1, "inputs" }, diff --git a/src/validation.cpp b/src/validation.cpp index 0b9da9c623..1b1e272a30 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3342,19 +3342,25 @@ void CChainState::ProcessFutures(const CBlockIndex* pindex, CCustomCSView& cache return true; }); - const uint32_t startHeight = pindex->nHeight - blockPeriod; + CDataStructureV0 burnKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Burned}; + + auto burned = attributes->GetValue(burnKey, CBalances{}); + std::map unpaidContracts; - cache.ForEachFuturesUserValues([&](const CFuturesUserKey& key, const CFuturesUserValue& futuresValues){ - if (key.height <= startHeight) { - return false; - } + std::set deletionPending; + + CHistoryWriters writers{paccountHistoryDB.get(), nullptr, nullptr}; + CAccountsHistoryWriter view(cache, pindex->nHeight, ~0u, {}, uint8_t(CustomTxType::FutureSwapExecution), &writers); - const auto source = cache.GetLoanTokenByID(futuresValues.source.nTokenId); + view.ForEachFuturesUserValues([&](const CFuturesUserKey& key, const CFuturesUserValue& futuresValues){ + deletionPending.insert(key); + + const auto source = view.GetLoanTokenByID(futuresValues.source.nTokenId); assert(source); if (source->symbol == "DUSD") { const DCT_ID destId{futuresValues.destination}; - const auto destToken = cache.GetLoanTokenByID(destId); + const auto destToken = view.GetLoanTokenByID(destId); assert(destToken); try { @@ -3362,8 +3368,8 @@ void CChainState::ProcessFutures(const CBlockIndex* pindex, CCustomCSView& cache if (premiumPrice > 0) { const auto total = DivideAmounts(futuresValues.source.nValue, premiumPrice); CTokenAmount destination{destId, total}; - cache.AddBalance(key.owner, destination); - cache.StoreFuturesDestValues(key, {destination, static_cast(pindex->nHeight)}); + view.AddBalance(key.owner, destination); + burned.Add(futuresValues.source); LogPrint(BCLog::FUTURESWAP, "ProcessFutures(): Owner %s source %s destination %s\n", key.owner.GetHex(), futuresValues.source.ToString(), destination.ToString()); } @@ -3372,16 +3378,16 @@ void CChainState::ProcessFutures(const CBlockIndex* pindex, CCustomCSView& cache } } else { - const auto tokenDUSD = cache.GetToken("DUSD"); + const auto tokenDUSD = view.GetToken("DUSD"); assert(tokenDUSD); try { const auto& discountPrice = futuresPrices.at(futuresValues.source.nTokenId).discount; const auto total = MultiplyAmounts(futuresValues.source.nValue, discountPrice); CTokenAmount destination{tokenDUSD->first, total}; - cache.AddBalance(key.owner, destination); - cache.StoreFuturesDestValues(key, {destination, static_cast(pindex->nHeight)}); - LogPrint(BCLog::FUTURESWAP, "ProcessFutures(): Owner %s source %s destination %s\n", + view.AddBalance(key.owner, destination); + burned.Add(futuresValues.source); + LogPrint(BCLog::FUTURESWAP, "ProcessFutures(): Payment Owner %s source %s destination %s\n", key.owner.GetHex(), futuresValues.source.ToString(), destination.ToString()); } catch (const std::out_of_range&) { unpaidContracts.emplace(key, futuresValues); @@ -3391,24 +3397,40 @@ void CChainState::ProcessFutures(const CBlockIndex* pindex, CCustomCSView& cache return true; }, {static_cast(pindex->nHeight), {}, std::numeric_limits::max()}); + view.Flush(); + const auto contractAddressValue = GetFutureSwapContractAddress(); assert(contractAddressValue); - CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Tokens}; + CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Current}; + auto balances = attributes->GetValue(liveKey, CBalances{}); + CHistoryWriters refundWriters{paccountHistoryDB.get(), nullptr, nullptr}; + CAccountsHistoryWriter refundView(cache, pindex->nHeight, ~0u - 1, {}, uint8_t(CustomTxType::FutureSwapRefund), &refundWriters); + // Refund unpaid contracts for (const auto& [key, value] : unpaidContracts) { - cache.EraseFuturesUserValues(key); - cache.SubBalance(*contractAddressValue, value.source); - cache.AddBalance(key.owner, value.source); + refundView.SubBalance(*contractAddressValue, value.source); + refundView.AddBalance(key.owner, value.source); + LogPrint(BCLog::FUTURESWAP, "ProcessFutures(): Refund Owner %s source %s destination %s\n", + key.owner.GetHex(), value.source.ToString(), value.source.ToString()); balances.Sub(value.source); } + refundView.Flush(); + + for (const auto& key : deletionPending) { + cache.EraseFuturesUserValues(key); + } + + attributes->attributes[burnKey] = burned; + if (!unpaidContracts.empty()) { attributes->attributes[liveKey] = balances; - cache.SetVariable(*attributes); } + + cache.SetVariable(*attributes); } void CChainState::ProcessOracleEvents(const CBlockIndex* pindex, CCustomCSView& cache, const CChainParams& chainparams){ diff --git a/test/functional/feature_futures.py b/test/functional/feature_futures.py index 7a0dfeb72c..e5dc4fabc4 100755 --- a/test/functional/feature_futures.py +++ b/test/functional/feature_futures.py @@ -55,8 +55,9 @@ def run_test(self): def setup_test(self): - # Store address + # Store addresses self.address = self.nodes[0].get_genesis_keys().ownerAuthAddress + self.contract_address = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc' # Store interval self.futures_interval = 25 @@ -300,6 +301,15 @@ def test_dtoken_to_dusd(self): assert_equal(result['values'][0]['source'], f'{Decimal("1.00000000")}@{self.symbolTWTR}') assert_equal(result['values'][0]['destination'], self.symbolDUSD) + # Check DFI2203 amounts do not show up as burns yet + result = self.nodes[0].getburninfo() + assert_equal(result['dfip2203'], []) + + # Check DFI2203 address on listgovs, current shows pending, burn should be empty. + result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] + assert_equal(result['v0/live/economy/dfip2203_current'], [f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) + assert('v0/live/economy/dfip2203_burned' not in result) + # Move to next futures block next_futures_block = self.nodes[0].getblockcount() + (self.futures_interval - (self.nodes[0].getblockcount() % self.futures_interval)) self.nodes[0].generate(next_futures_block - self.nodes[0].getblockcount()) @@ -317,12 +327,12 @@ def test_dtoken_to_dusd(self): assert_equal(len(result['values']), 0) # Check contract address - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Check DFI2203 address on listgovs result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] - assert_equal(result['v0/live/economy/dfip_tokens'], [f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) + assert_equal(result['v0/live/economy/dfip2203_current'], [f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Check DFI2203 address on getburninfo result = self.nodes[0].getburninfo() @@ -340,10 +350,10 @@ def test_dtoken_to_dusd(self): # Populate RPC check self.list_history.append({'height': self.nodes[0].getblockcount(), 'swaps': [ - {'address': address_tsla, 'source': f'{Decimal("1.00000000")}@{self.symbolTSLA}', 'destination': f'{self.prices[0]["discountPrice"]}@{self.symbolDUSD}'}, - {'address': address_googl, 'source': f'{Decimal("1.00000000")}@{self.symbolGOOGL}', 'destination': f'{self.prices[1]["discountPrice"]}@{self.symbolDUSD}'}, - {'address': address_twtr, 'source': f'{Decimal("1.00000000")}@{self.symbolTWTR}', 'destination': f'{self.prices[1]["discountPrice"]}@{self.symbolDUSD}'}, - {'address': address_msft, 'source': f'{Decimal("1.00000000")}@{self.symbolMSFT}', 'destination': f'{self.prices[3]["discountPrice"]}@{self.symbolDUSD}'}, + {'address': address_tsla, 'destination': f'{self.prices[0]["discountPrice"]}@{self.symbolDUSD}'}, + {'address': address_googl, 'destination': f'{self.prices[1]["discountPrice"]}@{self.symbolDUSD}'}, + {'address': address_twtr, 'destination': f'{self.prices[2]["discountPrice"]}@{self.symbolDUSD}'}, + {'address': address_msft, 'destination': f'{self.prices[3]["discountPrice"]}@{self.symbolDUSD}'}, ]}) def test_dusd_to_dtoken(self): @@ -362,13 +372,13 @@ def test_dusd_to_dtoken(self): self.nodes[0].generate(1) # Create user futures contracts - self.nodes[0].futureswap(address_msft, f'{self.prices[3]["premiumPrice"]}@{self.symbolDUSD}', int(self.idMSFT)) + self.nodes[0].futureswap(address_msft, f'{self.prices[3]["premiumPrice"]}@{self.symbolDUSD}', self.idMSFT) self.nodes[0].generate(1) - self.nodes[0].futureswap(address_twtr, f'{self.prices[2]["premiumPrice"]}@{self.symbolDUSD}', int(self.idTWTR)) + self.nodes[0].futureswap(address_twtr, f'{self.prices[2]["premiumPrice"]}@{self.symbolDUSD}', self.idTWTR) self.nodes[0].generate(1) - self.nodes[0].futureswap(address_googl, f'{self.prices[1]["premiumPrice"]}@{self.symbolDUSD}', int(self.idGOOGL)) + self.nodes[0].futureswap(address_googl, f'{self.prices[1]["premiumPrice"]}@{self.symbolDUSD}', self.symbolGOOGL) self.nodes[0].generate(1) - self.nodes[0].futureswap(address_tsla, f'{self.prices[0]["premiumPrice"]}@{self.symbolDUSD}', int(self.idTSLA)) + self.nodes[0].futureswap(address_tsla, f'{self.prices[0]["premiumPrice"]}@{self.symbolDUSD}', self.symbolTSLA) self.nodes[0].generate(1) # List user futures contracts @@ -406,6 +416,15 @@ def test_dusd_to_dtoken(self): assert_equal(result['values'][0]['source'], f'{self.prices[3]["premiumPrice"]}@{self.symbolDUSD}') assert_equal(result['values'][0]['destination'], self.symbolMSFT) + # Check new DFI2203 amounts do not show up as burns yet + result = self.nodes[0].getburninfo() + assert_equal(result['dfip2203'], [f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) + + # Check DFI2203 address on listgovs, current shows pending, new swaps should not show up on burn. + result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] + assert_equal(result['v0/live/economy/dfip2203_current'], [f'3992.10000000@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) + assert_equal(result['v0/live/economy/dfip2203_burned'], [f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) + # Move to next futures block next_futures_block = self.nodes[0].getblockcount() + (self.futures_interval - (self.nodes[0].getblockcount() % self.futures_interval)) self.nodes[0].generate(next_futures_block - self.nodes[0].getblockcount()) @@ -423,12 +442,12 @@ def test_dusd_to_dtoken(self): assert_equal(len(result['values']), 0) # Check contract address - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'3992.10000000@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Check DFI2203 address on listgovs result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] - assert_equal(result['v0/live/economy/dfip_tokens'], [f'3992.10000000@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) + assert_equal(result['v0/live/economy/dfip2203_current'], [f'3992.10000000@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Check DFI2203 address on getburninfo result = self.nodes[0].getburninfo() @@ -446,10 +465,10 @@ def test_dusd_to_dtoken(self): # Populate RPC check self.list_history.append({'height': self.nodes[0].getblockcount(), 'swaps': [ - {'address': address_tsla, 'source': f'{self.prices[0]["premiumPrice"]}@{self.symbolDUSD}', 'destination': f'1.00000000@{self.symbolTSLA}'}, - {'address': address_googl, 'source': f'{self.prices[1]["premiumPrice"]}@{self.symbolDUSD}', 'destination': f'1.00000000@{self.symbolGOOGL}'}, - {'address': address_twtr, 'source': f'{self.prices[2]["premiumPrice"]}@{self.symbolDUSD}', 'destination': f'1.00000000@{self.symbolTWTR}'}, - {'address': address_msft, 'source': f'{self.prices[3]["premiumPrice"]}@{self.symbolDUSD}', 'destination': f'1.00000000@{self.symbolMSFT}'}, + {'address': address_tsla, 'destination': f'1.00000000@{self.symbolTSLA}'}, + {'address': address_googl, 'destination': f'1.00000000@{self.symbolGOOGL}'}, + {'address': address_twtr, 'destination': f'1.00000000@{self.symbolTWTR}'}, + {'address': address_msft, 'destination': f'1.00000000@{self.symbolMSFT}'}, ]}) def check_swap_block_range(self): @@ -486,7 +505,7 @@ def check_swap_block_range(self): # Populate RPC check self.list_history.append({'height': self.nodes[0].getblockcount(), 'swaps': [ - {'address': address, 'source': f'{self.prices[0]["premiumPrice"]}@{self.symbolDUSD}', 'destination': f'1.00000000@{self.symbolTSLA}'}, + {'address': address, 'destination': f'1.00000000@{self.symbolTSLA}'}, ]}) # Move to next futures block @@ -498,7 +517,7 @@ def check_swap_block_range(self): assert_equal(result, [f'913.50000000@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}']) # Check contract address - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'4905.60000000@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) def check_multiple_swaps(self): @@ -544,7 +563,7 @@ def check_multiple_swaps(self): assert_equal(result, [f'2.00000000@{self.symbolTWTR}']) # Check contract address - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'6810.30000000@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) def check_withdrawals(self): @@ -658,12 +677,12 @@ def check_withdrawals(self): assert_equal(result, [f'0.00000001@{self.symbolDUSD}', f'1.99999999@{self.symbolMSFT}']) # Check contract address - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'7468.64999999@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Check DFI2203 address on listgovs result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] - assert_equal(result['v0/live/economy/dfip_tokens'], [f'7468.64999999@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) + assert_equal(result['v0/live/economy/dfip2203_current'], [f'7468.64999999@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Check DFI2203 address on getburninfo result = self.nodes[0].getburninfo() @@ -691,7 +710,7 @@ def check_minimum_swaps(self): assert_equal(result, [f'{self.prices[0]["premiumPrice"] - Decimal("0.00000001")}@{self.symbolDUSD}']) # Check contract address - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'7468.65000000@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Create user futures contract to purchase one Satoshi of TSLA @@ -708,7 +727,7 @@ def check_minimum_swaps(self): assert_equal(result, [f'{self.prices[0]["premiumPrice"] - Decimal("0.00000001") - Decimal(min_purchase)}@{self.symbolDUSD}', f'0.00000001@{self.symbolTSLA}']) # Check contract address - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'7468.65000914@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) def check_gov_var_change(self): @@ -729,7 +748,7 @@ def check_gov_var_change(self): self.nodes[0].generate(1) # Check contract address has updated - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'7468.65000915@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Test changing block period while DFIP2203 still active @@ -740,13 +759,9 @@ def check_gov_var_change(self): self.nodes[0].generate(1) # Check contract address has not changed, no refund on disabling DFIP2203. - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'7468.65000915@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) - # Test changing block period to include historical future contracts - self.futures_interval = self.futures_interval * 2 - assert_raises_rpc_error(-32600, 'Historical Futures contracts in this period', self.nodes[0].setgov, {"ATTRIBUTES":{'v0/params/dfip2203/block_period':f'{self.futures_interval}'}}) - # Move to next futures block next_futures_block = self.nodes[0].getblockcount() + (self.futures_interval - (self.nodes[0].getblockcount() % self.futures_interval)) self.nodes[0].generate(next_futures_block - self.nodes[0].getblockcount()) @@ -777,6 +792,26 @@ def check_gov_var_change(self): self.nodes[0].setgov({"ATTRIBUTES":{'v0/params/dfip2203/active':'false'}}) self.nodes[0].generate(1) + # Check refunds show up in history + result = self.nodes[0].listaccounthistory('all', {"maxBlockHeight":self.nodes[0].getblockcount(), 'depth':0, 'txtype':'w'}) + assert_equal(result[0]['owner'], self.contract_address) + assert_equal(result[0]['blockHeight'], self.nodes[0].getblockcount()) + assert_equal(result[0]['type'], 'FutureSwapRefund') + assert_equal(result[0]['amounts'], [f'{-self.prices[0]["premiumPrice"] - self.prices[1]["premiumPrice"]}@{self.symbolDUSD}']) + assert_equal(result[1]['type'], 'FutureSwapRefund') + assert_equal(result[2]['type'], 'FutureSwapRefund') + assert_equal(result[1]['blockHeight'], self.nodes[0].getblockcount()) + assert_equal(result[2]['blockHeight'], self.nodes[0].getblockcount()) + if result[1]['owner'] == address_googl: + assert_equal(result[1]['amounts'], [f'{self.prices[1]["premiumPrice"]}@{self.symbolDUSD}']) + assert_equal(result[2]['owner'], address_tsla) + assert_equal(result[2]['amounts'], [f'{self.prices[0]["premiumPrice"]}@{self.symbolDUSD}']) + else: + assert_equal(result[1]['owner'], address_tsla) + assert_equal(result[1]['amounts'], [f'{self.prices[0]["premiumPrice"]}@{self.symbolDUSD}']) + assert_equal(result[2]['owner'], address_googl) + assert_equal(result[2]['amounts'], [f'{self.prices[1]["premiumPrice"]}@{self.symbolDUSD}']) + # Balances should be restored result = self.nodes[0].getaccount(address_tsla) assert_equal(result, [f'{self.prices[0]["premiumPrice"]}@{self.symbolDUSD}']) @@ -784,7 +819,7 @@ def check_gov_var_change(self): assert_equal(result, [f'{self.prices[1]["premiumPrice"]}@{self.symbolDUSD}']) # Check contract address remains the same - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'7468.65000915@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Enable DFIP2203 @@ -826,12 +861,12 @@ def check_gov_var_change(self): assert_equal(result, [f'1.00000000@{self.symbolTSLA}']) # Check contract address - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'8382.15000915@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Check DFI2203 address on listgovs result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] - assert_equal(result['v0/live/economy/dfip_tokens'], [f'8382.15000915@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) + assert_equal(result['v0/live/economy/dfip2203_current'], [f'8382.15000915@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Check DFI2203 address on getburninfo result = self.nodes[0].getburninfo() @@ -858,17 +893,26 @@ def unpaid_contract(self): next_futures_block = self.nodes[0].getblockcount() + (self.futures_interval - (self.nodes[0].getblockcount() % self.futures_interval)) self.nodes[0].generate(next_futures_block - self.nodes[0].getblockcount()) + # Check refund in history + result = self.nodes[0].listaccounthistory('all', {"maxBlockHeight":self.nodes[0].getblockcount(), 'depth':0}) + assert_equal(result[0]['owner'], self.contract_address) + assert_equal(result[0]['type'], 'FutureSwapRefund') + assert_equal(result[0]['amounts'], [f'{-self.prices[0]["premiumPrice"]}@{self.symbolDUSD}']) + assert_equal(result[1]['owner'], address) + assert_equal(result[1]['type'], 'FutureSwapRefund') + assert_equal(result[0]['amounts'], [f'{-self.prices[0]["premiumPrice"]}@{self.symbolDUSD}']) + # Check user has been refunded result = self.nodes[0].getaccount(address) assert_equal(result, [f'{self.prices[0]["premiumPrice"]}@{self.symbolDUSD}']) # Check contract address - result = self.nodes[0].getaccount('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpsqgljc') + result = self.nodes[0].getaccount(self.contract_address) assert_equal(result, [f'8382.15000915@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Check DFI2203 address on listgovs result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] - assert_equal(result['v0/live/economy/dfip_tokens'], [f'8382.15000915@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) + assert_equal(result['v0/live/economy/dfip2203_current'], [f'8382.15000915@{self.symbolDUSD}', f'1.00000000@{self.symbolTSLA}', f'1.00000000@{self.symbolGOOGL}', f'1.00000000@{self.symbolTWTR}', f'1.00000000@{self.symbolMSFT}']) # Check DFI2203 address on getburninfo result = self.nodes[0].getburninfo() @@ -878,41 +922,33 @@ def rpc_history(self): # Check some historical swaps for history in self.list_history: - result = self.nodes[0].listfutureswaphistory('all', {"maxBlockHeight":history['height'], "depth":1}) - swap_count = len(result) - assert_equal(len(result), swap_count) - for index in range(swap_count): - assert_equal(result[index]['height'], history['height']) - assert_equal(result[index]['address'], history['swaps'][index]['address']) - assert_equal(result[index]['source'], history['swaps'][index]['source']) - assert_equal(result[index]['destination'], history['swaps'][index]['destination']) + result = self.nodes[0].listaccounthistory('all', {"maxBlockHeight":history['height'], 'depth':0, 'txtype':'q'}) + for history_entry in history['swaps']: + found = False + for result_entry in result: + assert_equal(history['height'], result_entry['blockHeight']) + if result_entry['owner'] == history_entry['address']: + assert_equal(result_entry['owner'], history_entry['address']) + assert_equal(result_entry['type'], 'FutureSwapExecution') + assert_equal(result_entry['amounts'], [history_entry['destination']]) + found = True + assert(found) # Check all swaps present - result = self.nodes[0].listfutureswaphistory('all') - assert_equal(len(result), 20) + result = self.nodes[0].listaccounthistory('all', {'txtype':'q'}) + assert_equal(len(result), 15) - # Check swap by specific address - result = self.nodes[0].listfutureswaphistory(self.list_history[0]['swaps'][0]['address']) - assert_equal(len(result), 1) - assert_equal(result[0]['height'], self.list_history[0]['height']) - assert_equal(result[0]['address'], self.list_history[0]['swaps'][0]['address']) - assert_equal(result[0]['source'], self.list_history[0]['swaps'][0]['source']) - assert_equal(result[0]['destination'], self.list_history[0]['swaps'][0]['destination']) + # Check all swap refunds present + result = self.nodes[0].listaccounthistory('all', {'txtype':'w'}) + assert_equal(len(result), 7) - # Check all wallet swaps present, still all of them! - result = self.nodes[0].listfutureswaphistory('mine') - assert_equal(len(result), 20) - - # Check limit working - result = self.nodes[0].listfutureswaphistory('all', {'limit': 1}) + # Check swap by specific address + result = self.nodes[0].listaccounthistory(self.list_history[0]['swaps'][0]['address'], {'txtype':'q'}) assert_equal(len(result), 1) + assert_equal(result[0]['blockHeight'], self.list_history[0]['height']) + assert_equal(result[0]['owner'], self.list_history[0]['swaps'][0]['address']) + assert_equal(result[0]['amounts'], [self.list_history[0]['swaps'][0]['destination']]) - # Filter on token - result = self.nodes[0].listfutureswaphistory('all', {'token': 'MSFT'}) - assert_equal(len(result), 3) - for history in result: - if history['source'].find('MSFT') == -1: - assert(history['destination'].find('MSFT') != -1) if __name__ == '__main__': FuturesTest().main()