From 4629d9d3f2c4853b8dacc586420115be711f45c4 Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Tue, 9 Mar 2021 13:27:31 +0100 Subject: [PATCH] Initial development of order book (#1) --- src/Makefile.am | 7 +- src/masternodes/masternodes.h | 2 + src/masternodes/mn_checks.cpp | 145 ++++++++++ src/masternodes/mn_checks.h | 15 +- src/masternodes/mn_rpc.cpp | 73 +++++ src/masternodes/mn_rpc.h | 1 + src/masternodes/order.cpp | 104 ++++++++ src/masternodes/order.h | 222 +++++++++++++++ src/masternodes/rpc_orderbook.cpp | 430 ++++++++++++++++++++++++++++++ src/masternodes/rpc_tokens.cpp | 67 ----- src/rpc/client.cpp | 4 + src/rpc/register.h | 9 +- 12 files changed, 1003 insertions(+), 76 deletions(-) create mode 100644 src/masternodes/order.cpp create mode 100644 src/masternodes/order.h create mode 100644 src/masternodes/rpc_orderbook.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 3dedc3e176d..89d634fbd52 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -165,6 +165,7 @@ DEFI_CORE_H = \ masternodes/masternodes.h \ masternodes/mn_checks.h \ masternodes/mn_rpc.h \ + masternodes/order.h \ masternodes/res.h \ masternodes/rewardhistoryold.h \ masternodes/tokens.h \ @@ -376,10 +377,12 @@ libdefi_server_a_SOURCES = \ masternodes/mn_rpc.cpp \ masternodes/rpc_masternodes.cpp \ masternodes/rpc_accounts.cpp \ - masternodes/rpc_tokens.cpp \ + masternodes/rpc_orderbook.cpp \ masternodes/rpc_poolpair.cpp \ - masternodes/tokens.cpp \ + masternodes/rpc_tokens.cpp \ + masternodes/order.cpp \ masternodes/poolpairs.cpp \ + masternodes/tokens.cpp \ masternodes/undos.cpp \ miner.cpp \ net.cpp \ diff --git a/src/masternodes/masternodes.h b/src/masternodes/masternodes.h index 0ae3d76a3ec..8c837f32bf8 100644 --- a/src/masternodes/masternodes.h +++ b/src/masternodes/masternodes.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -217,6 +218,7 @@ class CCustomCSView , public CPoolPairView , public CGovView , public CAnchorConfirmsView + , public COrderView { public: CCustomCSView() = default; diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index 94b8f15cb74..db1643bfe03 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -189,6 +189,15 @@ Res ApplyCustomTx(CCustomCSView & base_mnview, CCoinsViewCache const & coins, CT case CustomTxType::AnyAccountsToAccounts: res = ApplyAnyAccountsToAccountsTx(mnview, coins, tx, height, metadata, consensusParams, skipAuth); break; + case CustomTxType::CreateOrder: + res = ApplyCreateOrderTx(mnview, coins, tx, height, metadata, consensusParams, skipAuth); + break; + case CustomTxType::FulfillOrder: + res = ApplyFulfillOrderTx(mnview, coins, tx, height, metadata, consensusParams, skipAuth); + break; + case CustomTxType::CloseOrder: + res = ApplyCloseOrderTx(mnview, coins, tx, height, metadata, consensusParams, skipAuth); + break; default: return Res::Ok(); // not "custom" tx } @@ -1504,3 +1513,139 @@ bool IsMempooledCustomTxCreate(const CTxMemPool & pool, const uint256 & txid) } return false; } + +Res ApplyCreateOrderTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector const & metadata, Consensus::Params const & consensusParams, bool skipAuth, UniValue *rpcInfo) +{ + // Check quick conditions first + if (tx.vout.size() !=2) { + return Res::Err("%s: %s", __func__, "malformed tx vouts (wrong number of vouts)"); + } + + COrderImplemetation order; + CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); + ss >> static_cast(order); + if (!ss.empty()) { + return Res::Err("%s: deserialization failed: excess %d bytes", __func__, ss.size()); + } + + order.creationTx=tx.GetHash(); + order.creationHeight = height; + + CTxDestination ownerDest = DecodeDestination(order.ownerAddress); + if (ownerDest.which() == 0) { + return Res::Err("%s: %s", __func__, "ownerAdress (" + order.ownerAddress + ") does not refer to any valid address"); + } + + auto tokenFrom = mnview.GetToken(order.idTokenFrom); + if (!tokenFrom) { + return Res::Err("%s: token %s does not exist!", tokenFrom->symbol); + } + auto tokenTo = mnview.GetToken(order.idTokenTo); + if (!tokenTo) { + return Res::Err("%s: token %s does not exist!", tokenTo->symbol); + } + + // Return here to avoid already exist error + if (rpcInfo) { + rpcInfo->pushKV("creationTx", order.creationTx.GetHex()); + rpcInfo->pushKV("ownerAddress", order.ownerAddress); + rpcInfo->pushKV("tokenFrom", order.tokenFrom); + rpcInfo->pushKV("tokenTo", order.tokenTo); + rpcInfo->pushKV("amountFrom", order.amountFrom); + rpcInfo->pushKV("orderPrice", order.orderPrice); + rpcInfo->pushKV("expiry", static_cast(order.expiry)); + + return Res::Ok(); + } + + auto res = mnview.CreateOrder(order); + if (!res.ok) { + return Res::Err("%s %s: %s", __func__, order.creationTx.GetHex(), res.msg); + } + + return Res::Ok(); +} + +Res ApplyFulfillOrderTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector const & metadata, Consensus::Params const & consensusParams, bool skipAuth, UniValue *rpcInfo) +{ + // Check quick conditions first + if (tx.vout.size() !=2) { + return Res::Err("%s: %s", __func__, "malformed tx vouts (wrong number of vouts)"); + } + + CFulfillOrderImplemetation fillorder; + CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); + ss >> static_cast(fillorder); + if (!ss.empty()) { + return Res::Err("%s: deserialization failed: excess %d bytes", __func__, ss.size()); + } + + fillorder.creationTx=tx.GetHash(); + fillorder.creationHeight = height; + + CTxDestination ownerDest = DecodeDestination(fillorder.ownerAddress); + if (ownerDest.which() == 0) { + return Res::Err("%s: %s", __func__, "ownerAdress (" + fillorder.ownerAddress + ") does not refer to any valid address"); + } + if (!mnview.GetOrderByCreationTx(fillorder.orderTx)) { + return Res::Err("order with creation tx %s does not exists!", fillorder.orderTx.GetHex()); + } + + // Return here to avoid already exist error + if (rpcInfo) { + rpcInfo->pushKV("ownerAddress", fillorder.ownerAddress); + rpcInfo->pushKV("orderTx", fillorder.orderTx.GetHex()); + rpcInfo->pushKV("amount", fillorder.amount); + return Res::Ok(); + } + + auto res = mnview.FulfillOrder(fillorder); + if (!res.ok) { + return Res::Err("%s %s: %s", __func__, fillorder.creationTx.GetHex(), res.msg); + } + + return Res::Ok(); +} + +Res ApplyCloseOrderTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector const & metadata, Consensus::Params const & consensusParams, bool skipAuth, UniValue *rpcInfo) +{ + // Check quick conditions first + if (tx.vout.size() !=2) { + return Res::Err("%s: %s", __func__, "malformed tx vouts (wrong number of vouts)"); + } + + CCloseOrderImplemetation closeorder; + CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); + ss >> static_cast(closeorder); + if (!ss.empty()) { + return Res::Err("%s: deserialization failed: excess %d bytes", __func__, ss.size()); + } + + closeorder.creationTx=tx.GetHash(); + closeorder.creationHeight = height; + + std::unique_ptr order; + if (!(order=mnview.GetOrderByCreationTx(closeorder.orderTx))) { + return Res::Err("order with creation tx %s does not exists!", closeorder.orderTx.GetHex()); + } + + order->closeTx=closeorder.creationTx; + order->closeHeight=closeorder.creationHeight; + + // Return here to avoid already exist error + if (rpcInfo) { + rpcInfo->pushKV("orderTx", closeorder.orderTx.GetHex()); + return Res::Ok(); + } + + // auto res = mnview.CloseOrder(closeorder); + // if (!res.ok) { + // return Res::Err("%s %s: %s", __func__, closeorder.creationTx.GetHex(), res.msg); + // } + // res = mnview.CloseOrderTx(*order); + // if (!res.ok) { + // return Res::Err("%s %s: %s", __func__, closeorder.creationTx.GetHex(), res.msg); + // } + + return Res::Ok(); +} diff --git a/src/masternodes/mn_checks.h b/src/masternodes/mn_checks.h index 06b43be01b6..1cda66598f8 100644 --- a/src/masternodes/mn_checks.h +++ b/src/masternodes/mn_checks.h @@ -38,9 +38,9 @@ enum class CustomTxType : unsigned char UpdateToken = 'N', // previous type, only DAT flag triggers UpdateTokenAny = 'n', // new type of token's update with any flags/fields possible // dex orders - just not to overlap in future -// CreateOrder = 'O', -// DestroyOrder = 'E', -// MatchOrders = 'A', + CreateOrder = 'O', + FulfillOrder = 'F', + CloseOrder = 'S', //poolpair CreatePoolPair = 'p', UpdatePoolPair = 'u', @@ -59,7 +59,7 @@ enum class CustomTxType : unsigned char }; inline CustomTxType CustomTxCodeToType(unsigned char ch) { - char const txtypes[] = "CRTMNnpuslrUbBaGA"; + char const txtypes[] = "CRTMNnOFSpuslrUbBaGA"; if (memchr(txtypes, ch, strlen(txtypes))) return static_cast(ch); else @@ -86,6 +86,9 @@ inline std::string ToString(CustomTxType type) { case CustomTxType::AnyAccountsToAccounts: return "AnyAccountsToAccounts"; case CustomTxType::SetGovVariable: return "SetGovVariable"; case CustomTxType::AutoAuthPrep: return "AutoAuth"; + case CustomTxType::CreateOrder: return "CreateOrder"; + case CustomTxType::FulfillOrder: return "FulfillOrder"; + case CustomTxType::CloseOrder: return "CloseOrder"; default: return "None"; } } @@ -131,6 +134,10 @@ Res ApplyAccountToUtxosTx(CCustomCSView & mnview, CCoinsViewCache const & coins, Res ApplyAccountToAccountTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector const & metadata, Consensus::Params const & consensusParams, bool skipAuth = false, UniValue* rpcInfo = nullptr); Res ApplyAnyAccountsToAccountsTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector const & metadata, Consensus::Params const & consensusParams, bool skipAuth = false, UniValue* rpcInfo = nullptr); +Res ApplyCreateOrderTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector const & metadata, Consensus::Params const & consensusParams, bool skipAuth = false, UniValue* rpcInfo = nullptr); +Res ApplyFulfillOrderTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector const & metadata, Consensus::Params const & consensusParams, bool skipAuth = false, UniValue* rpcInfo = nullptr); +Res ApplyCloseOrderTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector const & metadata, Consensus::Params const & consensusParams, bool skipAuth = false, UniValue *rpcInfo = nullptr); + Res ApplySetGovernanceTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector const & metadata, Consensus::Params const & consensusParams, bool skipAuth = false, UniValue* rpcInfo = nullptr); ResVal ApplyAnchorRewardTx(CCustomCSView & mnview, CTransaction const & tx, int height, uint256 const & prevStakeModifier, std::vector const & metadata, Consensus::Params const & consensusParams); diff --git a/src/masternodes/mn_rpc.cpp b/src/masternodes/mn_rpc.cpp index d928409758a..9845861ce40 100644 --- a/src/masternodes/mn_rpc.cpp +++ b/src/masternodes/mn_rpc.cpp @@ -378,6 +378,79 @@ CWallet* GetWallet(const JSONRPCRequest& request) { return pwallet; } +bool GetCustomTXInfo(const int nHeight, const CTransactionRef tx, CustomTxType& guess, Res& res, UniValue& txResults) +{ + std::vector metadata; + guess = GuessCustomTxType(*tx, metadata); + CCustomCSView mnview_dummy(*pcustomcsview); + + switch (guess) + { + case CustomTxType::CreateMasternode: + res = ApplyCreateMasternodeTx(mnview_dummy, *tx, nHeight, metadata, &txResults); + break; + case CustomTxType::ResignMasternode: + res = ApplyResignMasternodeTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, true, &txResults); + break; + case CustomTxType::CreateToken: + res = ApplyCreateTokenTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::UpdateToken: + res = ApplyUpdateTokenTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::UpdateTokenAny: + res = ApplyUpdateTokenAnyTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::MintToken: + res = ApplyMintTokenTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::CreatePoolPair: + res = ApplyCreatePoolPairTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::UpdatePoolPair: + res = ApplyUpdatePoolPairTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::PoolSwap: + res = ApplyPoolSwapTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::AddPoolLiquidity: + res = ApplyAddPoolLiquidityTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::RemovePoolLiquidity: + res = ApplyRemovePoolLiquidityTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::UtxosToAccount: + res = ApplyUtxosToAccountTx(mnview_dummy, *tx, nHeight, metadata, Params().GetConsensus(), &txResults); + break; + case CustomTxType::AccountToUtxos: + res = ApplyAccountToUtxosTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::AccountToAccount: + res = ApplyAccountToAccountTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::SetGovVariable: + res = ApplySetGovernanceTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::AnyAccountsToAccounts: + res = ApplyAnyAccountsToAccountsTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::AutoAuthPrep: + res.ok = true; + res.msg = "AutoAuth"; + break; + case CustomTxType::CreateOrder: + res = ApplyCreateOrderTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + case CustomTxType::FulfillOrder: + res = ApplyFulfillOrderTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); + break; + default: + return false; + } + + return true; +} + UniValue setgov(const JSONRPCRequest& request) { CWallet* const pwallet = GetWallet(request); diff --git a/src/masternodes/mn_rpc.h b/src/masternodes/mn_rpc.h index 4a3c1db8048..95b7f37ca50 100644 --- a/src/masternodes/mn_rpc.h +++ b/src/masternodes/mn_rpc.h @@ -69,6 +69,7 @@ CTransactionRef sign(CMutableTransaction& mtx, CWallet* const pwallet, CTransact CTransactionRef send(CTransactionRef tx, CTransactionRef optAuthTx); CTransactionRef signsend(CMutableTransaction& mtx, CWallet* const pwallet, CTransactionRef optAuthTx /* = {}*/); CWallet* GetWallet(const JSONRPCRequest& request); +bool GetCustomTXInfo(const int nHeight, const CTransactionRef tx, CustomTxType& guess, Res& res, UniValue& txResults); std::vector GetAuthInputsSmart(CWallet* const pwallet, int32_t txVersion, std::set& auths, bool needFounderAuth, CTransactionRef& optAuthTx, UniValue const& explicitInputs); std::string ScriptToString(CScript const& script); CAccounts GetAllMineAccounts(CWallet* const pwallet); diff --git a/src/masternodes/order.cpp b/src/masternodes/order.cpp new file mode 100644 index 00000000000..3cd1475b338 --- /dev/null +++ b/src/masternodes/order.cpp @@ -0,0 +1,104 @@ +#include +// #include + +/// @attention make sure that it does not overlap with other views !!! +const unsigned char COrderView::OrderCreationTx ::prefix = 'O'; +const unsigned char COrderView::OrderCreationTxId ::prefix = 'P'; +const unsigned char COrderView::FulfillCreationTx ::prefix = 'D'; +const unsigned char COrderView::FulfillOrderTxid ::prefix = 'Q'; +const unsigned char COrderView::CloseCreationTx ::prefix = 'E'; +const unsigned char COrderView::OrderCloseTx ::prefix = 'C'; + +std::unique_ptr COrderView::GetOrderByCreationTx(const uint256 & txid) const +{ + auto tokenPair=ReadBy(txid); + if (tokenPair) + { + auto orderImpl=ReadBy(std::make_pair(*tokenPair,txid)); + if (orderImpl) + return MakeUnique(*orderImpl); + } + return nullptr; +} + +ResVal COrderView::CreateOrder(const COrderView::COrderImpl& order) +{ + //this should not happen, but for sure + if (GetOrderByCreationTx(order.creationTx)) { + return Res::Err("order with creation tx %s already exists!", order.creationTx.GetHex()); + } + TokenPair pair(order.idTokenFrom,order.idTokenTo); + TokenPairKey key(pair,order.creationTx); + WriteBy(key,order); + WriteBy(order.creationTx,pair); + + return {order.creationTx, Res::Ok()}; +} + +ResVal COrderView::CloseOrderTx(const uint256& txid) +{ + auto order=GetOrderByCreationTx(txid); + if (!order) { + return Res::Err("order with creation tx %s doesn't exists!", txid.GetHex()); + } + + TokenPair pair(order->idTokenFrom,order->idTokenTo); + TokenPairKey key(pair,order->creationTx); + EraseBy(key); + WriteBy(key,*order); + WriteBy(order->closeTx, order->creationTx); + return {order->creationTx, Res::Ok()}; +} + +void COrderView::ForEachOrder(std::function)> callback, TokenPair const & pair) +{ + TokenPairKey start(pair,uint256()); + ForEachOrder([&callback] (TokenPairKey const &key, CLazySerialize orderImpl) { + return callback(key, orderImpl); + }, start); +} + +std::unique_ptr COrderView::GetFulfillOrderByCreationTx(const uint256 & txid) const +{ + auto ordertxid=ReadBy(txid); + if (!ordertxid->IsNull()) + { + auto fillorderImpl=ReadBy(std::make_pair(*ordertxid,txid)); + if (fillorderImpl) + return MakeUnique(*fillorderImpl); + return nullptr; + } +} + +ResVal COrderView::FulfillOrder(const COrderView::CFulfillOrderImpl & fillorder) +{ + //this should not happen, but for sure + if (GetFulfillOrderByCreationTx(fillorder.creationTx)) { + return Res::Err("fillorder with creation tx %s already exists!", fillorder.creationTx.GetHex()); + } + + WriteBy(fillorder.creationTx, fillorder.orderTx); + WriteBy(std::make_pair(fillorder.orderTx,fillorder.creationTx), fillorder); + return {fillorder.creationTx, Res::Ok()}; +} + +std::unique_ptr COrderView::GetCloseOrderByCreationTx(const uint256 & txid) const +{ + auto closeorderImpl=ReadBy(txid); + if (closeorderImpl) + return MakeUnique(*closeorderImpl); + return nullptr; +} + +ResVal COrderView::CloseOrder(const COrderView::CCloseOrderImpl& closeorder) +{ + if (GetCloseOrderByCreationTx(closeorder.creationTx)) { + return Res::Err("close with creation tx %s already exists!", closeorder.creationTx.GetHex()); + } + if (!CloseOrderTx(closeorder.orderTx).ok) { + return Res::Err("order with creation tx %s doesn't exists!", closeorder.orderTx.GetHex()); + } + WriteBy(closeorder.creationTx, closeorder); + + return {closeorder.creationTx, Res::Ok()}; +} diff --git a/src/masternodes/order.h b/src/masternodes/order.h new file mode 100644 index 00000000000..43598b780be --- /dev/null +++ b/src/masternodes/order.h @@ -0,0 +1,222 @@ +#ifndef DEFI_MASTERNODES_ORDER_H +#define DEFI_MASTERNODES_ORDER_H + +#include +#include + +#include +#include + +class COrder +{ +public: + static const int DEFAULT_ORDER_EXPIRY = 2880; + + //! basic properties + std::string ownerAddress; + std::string tokenFrom; + std::string tokenTo; + DCT_ID idTokenFrom; + DCT_ID idTokenTo; + CAmount amountFrom; + CAmount orderPrice; + uint32_t expiry; + + COrder() + : ownerAddress("") + , tokenFrom("") + , tokenTo("") + , idTokenFrom({0}) + , idTokenTo({0}) + , amountFrom(0) + , orderPrice(0) + , expiry(DEFAULT_ORDER_EXPIRY) + {} + virtual ~COrder() = default; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(ownerAddress); + READWRITE(tokenFrom); + READWRITE(tokenTo); + READWRITE(VARINT(idTokenFrom.v)); + READWRITE(VARINT(idTokenTo.v)); + READWRITE(amountFrom); + READWRITE(orderPrice); + READWRITE(expiry); + } +}; + +class COrderImplemetation : public COrder +{ +public: + //! tx related properties + uint256 creationTx; + uint256 closeTx; + uint32_t creationHeight; + uint32_t closeHeight; + + COrderImplemetation() + : COrder() + , creationTx() + , closeTx() + , creationHeight(-1) + , closeHeight(-1) + {} + ~COrderImplemetation() override = default; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITEAS(COrder, *this); + READWRITE(creationTx); + READWRITE(closeTx); + READWRITE(creationHeight); + READWRITE(closeHeight); + } +}; + +class CFulfillOrder +{ +public: + //! basic properties + std::string ownerAddress; + uint256 orderTx; + CAmount amount; + + CFulfillOrder() + : ownerAddress("") + , orderTx(uint256()) + , amount(0) + {} + virtual ~CFulfillOrder() = default; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(ownerAddress); + READWRITE(orderTx); + READWRITE(amount); + } +}; + +class CFulfillOrderImplemetation : public CFulfillOrder +{ +public: + //! tx related properties + uint256 creationTx; + uint256 closeTx; + uint32_t creationHeight; + uint32_t closeHeight; + + CFulfillOrderImplemetation() + : CFulfillOrder() + , creationTx() + , closeTx() + , creationHeight(-1) + , closeHeight(-1) + {} + ~CFulfillOrderImplemetation() override = default; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITEAS(CFulfillOrder, *this); + READWRITE(creationTx); + READWRITE(closeTx); + READWRITE(creationHeight); + READWRITE(closeHeight); + } +}; + +class CCloseOrder +{ +public: + //! basic properties + uint256 orderTx; + + CCloseOrder() + : orderTx(uint256()) + {} + virtual ~CCloseOrder() = default; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(orderTx); + } +}; + +class CCloseOrderImplemetation : public CCloseOrder +{ +public: + //! tx related properties + uint256 creationTx; + uint32_t creationHeight; + + CCloseOrderImplemetation() + : CCloseOrder() + , creationTx() + , creationHeight(-1) + {} + ~CCloseOrderImplemetation() override = default; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITEAS(CCloseOrder, *this); + READWRITE(creationTx); + READWRITE(creationHeight); + } +}; + +class COrderView : public virtual CStorageView { +public: + typedef std::pair TokenPair; + typedef std::pair TokenPairKey; + + using COrderImpl = COrderImplemetation; + using CFulfillOrderImpl = CFulfillOrderImplemetation; + using CCloseOrderImpl = CCloseOrderImplemetation; + + std::unique_ptr GetOrderByCreationTx(const uint256 & txid) const; + ResVal CreateOrder(const COrderImpl& order); + ResVal CloseOrderTx(const uint256& txid); + void ForEachOrder(std::function)> callback, TokenPair const & pair=TokenPair()); + + template + bool ForEachOrder(std::function)> callback, KeyType const & start = KeyType()) const { + auto& self = const_cast(*this); + auto it = self.DB().NewIterator(); + auto key = std::make_pair(By::prefix, start); + for(it->Seek(DbTypeToBytes(key)); it->Valid() && BytesToDbType(it->Key(), key) && key.first == By::prefix; it->Next()) + { + boost::this_thread::interruption_point(); + if ((start!=KeyType() && key.second.first!=start.first) || !callback(key.second, CLazySerialize(*it))) + break; + } + return true; + } + + std::unique_ptr GetFulfillOrderByCreationTx(const uint256 & txid) const; + ResVal FulfillOrder(const CFulfillOrderImpl& fillorder); + + std::unique_ptr GetCloseOrderByCreationTx(const uint256 & txid) const; + ResVal CloseOrder(const CCloseOrderImpl& closeorder); + + struct OrderCreationTx { static const unsigned char prefix; }; + struct OrderCreationTxId { static const unsigned char prefix; }; + struct FulfillCreationTx { static const unsigned char prefix; }; + struct FulfillOrderTxid { static const unsigned char prefix; }; + struct CloseCreationTx { static const unsigned char prefix; }; + struct OrderCloseTx { static const unsigned char prefix; }; +}; + +#endif // DEFI_MASTERNODES_ORDER_H diff --git a/src/masternodes/rpc_orderbook.cpp b/src/masternodes/rpc_orderbook.cpp new file mode 100644 index 00000000000..dd8ddfbdeaa --- /dev/null +++ b/src/masternodes/rpc_orderbook.cpp @@ -0,0 +1,430 @@ +#include + +UniValue orderToJSON(COrderImplemetation const& order) { + UniValue orderObj(UniValue::VOBJ); + orderObj.pushKV("ownerAddress", order.ownerAddress); + orderObj.pushKV("tokenFrom", order.tokenFrom); + orderObj.pushKV("tokenTo", order.tokenTo); + orderObj.pushKV("amountFrom", order.amountFrom); + orderObj.pushKV("orderPrice", order.orderPrice); + orderObj.pushKV("expiry", static_cast(order.expiry)); + + UniValue ret(UniValue::VOBJ); + ret.pushKV(order.creationTx.GetHex(), orderObj); + return ret; +} + +UniValue createorder(const JSONRPCRequest& request) { + CWallet* const pwallet = GetWallet(request); + + RPCHelpMan{"createorder", + "\nCreates (and submits to local node and network) a order creation transaction.\n" + + HelpRequiringPassphrase(pwallet) + "\n", + { + {"order", RPCArg::Type::OBJ, RPCArg::Optional::NO, "", + { + {"ownerAddress", RPCArg::Type::STR, RPCArg::Optional::NO, "Address of the owner of token"}, + {"tokenFrom", RPCArg::Type::STR, RPCArg::Optional::NO, "Symbol or id of selling token"}, + {"tokenTo", RPCArg::Type::STR, RPCArg::Optional::NO, "Symbol or id of buying token"}, + {"amountFrom", RPCArg::Type::NUM, RPCArg::Optional::NO, "tokenFrom coins amount"}, + {"orderPrice", RPCArg::Type::NUM, RPCArg::Optional::NO, "Price per unit"}, + {"expiry", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "Number of blocks until the order expires (Default: 2880 blocks)"} + }, + }, + }, + RPCResult{ + "\"hash\" (string) The hex-encoded hash of broadcasted transaction\n" + }, + RPCExamples{ + HelpExampleCli("createorder", "'{\"ownerAddress\":\"tokenAddress\"," + "\"tokenFrom\":\"MyToken\",\"tokenTo\":\"Token1\"," + "\"amountFrom\":\"10\",\"orderPrice\":\"0.02\"}'") + + HelpExampleCli("createorder", "'{\"ownerAddress\":\"tokenAddress\"," + "\"tokenFrom\":\"MyToken\",\"tokenTo\":\"Token2\"," + "\"amountFrom\":\"5\",\"orderPrice\":\"0.1\"," + "\"expiry\":\"120\"}'") + }, + }.Check(request); + + if (pwallet->chain().isInitialBlockDownload()) { + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot create order while still in Initial Block Download"); + } + pwallet->BlockUntilSyncedToCurrentChain(); + LockedCoinsScopedGuard lcGuard(pwallet); + + RPCTypeCheck(request.params, {UniValue::VOBJ}, false); + if (request.params[0].isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Invalid parameters, arguments 1 must be non-null and expected as object at least with " + "{\"ownerAddress\",\"tokenFrom\",\"tokenTo\",\"amountFrom\",\"orderPrice\"}"); + } + UniValue metaObj = request.params[0].get_obj(); + COrder order; + std::string tokenFromSymbol, tokenToSymbol; + + if (!metaObj["ownerAddress"].isNull()) { + order.ownerAddress = trim_ws(metaObj["ownerAddress"].getValStr()); + } + else throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"ownerAddres\" must be non-null"); + if (!metaObj["tokenFrom"].isNull()) { + tokenFromSymbol = trim_ws(metaObj["tokenFrom"].getValStr()); + } + else throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"tokenFrom\" must be non-null"); + if (!metaObj["tokenTo"].isNull()) { + tokenToSymbol = trim_ws(metaObj["tokenTo"].getValStr()); + } + else throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"tokenTo\" must be non-null"); + if (!metaObj["amountFrom"].isNull()) { + order.amountFrom = AmountFromValue(metaObj["amountFrom"]); + } + else throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"amountFrom\" must be non-null"); + if (!metaObj["orderPrice"].isNull()) { + order.orderPrice = AmountFromValue(metaObj["orderPrice"]); + } + else throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"orderPrice\" must be non-null"); + if (!metaObj["expiry"].isNull()) { + order.expiry = metaObj["expiry"].get_int(); + } + CTxDestination ownerDest = DecodeDestination(order.ownerAddress); + if (ownerDest.which() == 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "ownerAdress (" + order.ownerAddress + ") does not refer to any valid address"); + } + + int targetHeight; + { + LOCK(cs_main); + DCT_ID idTokenFrom,idTokenTo; + auto tokenFrom = pcustomcsview->GetTokenGuessId(tokenFromSymbol, idTokenFrom); + if (!tokenFrom) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Token %s does not exist!", tokenFromSymbol)); + } + auto tokenTo = pcustomcsview->GetTokenGuessId(tokenToSymbol, idTokenTo); + if (!tokenTo) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Token %s does not exist!", tokenToSymbol)); + } + + order.tokenFrom=tokenFrom->symbol; + order.tokenTo=tokenTo->symbol; + order.idTokenFrom=idTokenFrom; + order.idTokenTo=idTokenTo; + + CBalances totalBalances; + CAmount total=0; + pcustomcsview->ForEachBalance([&](CScript const & owner, CTokenAmount const & balance) { + if (IsMineCached(*pwallet, owner) == ISMINE_SPENDABLE) { + totalBalances.Add(balance); + } + return true; + }); + auto it = totalBalances.balances.begin(); + for (int i = 0; it != totalBalances.balances.end(); it++, i++) { + CTokenAmount bal = CTokenAmount{(*it).first, (*it).second}; + std::string tokenIdStr = bal.nTokenId.ToString(); + auto token = pcustomcsview->GetToken(bal.nTokenId); + if (bal.nTokenId==order.idTokenFrom) total+=bal.nValue; + } + if (total(CustomTxType::CreateOrder) + << order; + + CScript scriptMeta; + scriptMeta << OP_RETURN << ToByteVector(metadata); + + const auto txVersion = GetTransactionVersion(targetHeight); + CMutableTransaction rawTx(txVersion); + + rawTx.vout.push_back(CTxOut(0, scriptMeta)); + + fund(rawTx, pwallet, {}); + + // check execution + { + LOCK(cs_main); + CCustomCSView mnview_dummy(*pcustomcsview); // don't write into actual DB + CCoinsViewCache coinview(&::ChainstateActive().CoinsTip()); + const auto res = ApplyCreateOrderTx(mnview_dummy, coinview, CTransaction(rawTx), targetHeight, + ToByteVector(CDataStream{SER_NETWORK, PROTOCOL_VERSION, order}), Params().GetConsensus()); + if (!res.ok) { + throw JSONRPCError(RPC_INVALID_REQUEST, "Execution test failed:\n" + res.msg); + } + } + return signsend(rawTx, pwallet, {})->GetHash().GetHex(); +} + +UniValue fulfillorder(const JSONRPCRequest& request) { + CWallet* const pwallet = GetWallet(request); + + RPCHelpMan{"fulfillorder", + "\nCreates (and submits to local node and network) a fill order transaction.\n" + + HelpRequiringPassphrase(pwallet) + "\n", + { + {"order", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", + { + {"ownerAddress", RPCArg::Type::STR, RPCArg::Optional::NO, "Address of the owner of token"}, + {"orderTx", RPCArg::Type::STR, RPCArg::Optional::NO, "txid of maker order"}, + {"amount", RPCArg::Type::NUM, RPCArg::Optional::NO, "coins amount to fulfill the order"}, + }, + }, + }, + RPCResult{ + "\"hash\" (string) The hex-encoded hash of broadcasted transaction\n" + }, + RPCExamples{ + HelpExampleCli("fulfillorder", "'{\"ownerAddress\":\"tokenAddress\"," + "\"orderTx\":\"txid\",\"amount\":\"10\"}'") + }, + }.Check(request); + + if (pwallet->chain().isInitialBlockDownload()) { + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot create order while still in Initial Block Download"); + } + pwallet->BlockUntilSyncedToCurrentChain(); + LockedCoinsScopedGuard lcGuard(pwallet); + + RPCTypeCheck(request.params, {UniValue::VOBJ}, false); + if (request.params[0].isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Invalid parameters, arguments 1 must be non-null and expected as object at least with " + "{\"ownerAddress\",\"orderTx\",\"amount\"}"); + } + UniValue metaObj = request.params[0].get_obj(); + CFulfillOrder fillorder; + + if (!metaObj["ownerAddress"].isNull()) { + fillorder.ownerAddress = trim_ws(metaObj["ownerAddress"].getValStr()); + } + else throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"ownerAddres\" must be non-null"); + if (!metaObj["orderTx"].isNull()) { + fillorder.orderTx = uint256S(metaObj["orderTx"].getValStr()); + } + else throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"orderTx\" must be non-null"); + if (!metaObj["amount"].isNull()) { + fillorder.amount = AmountFromValue(metaObj["amount"]); + } + else throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, argument \"amount\" must be non-null"); + + CTxDestination ownerDest = DecodeDestination(fillorder.ownerAddress); + if (ownerDest.which() == 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "ownerAdress (" + fillorder.ownerAddress + ") does not refer to any valid address"); + } + + int targetHeight; + { + LOCK(cs_main); + auto order = pcustomcsview->GetOrderByCreationTx(fillorder.orderTx); + if (!order) + throw JSONRPCError(RPC_INVALID_PARAMETER, "orderTx (" + fillorder.orderTx.GetHex() + ") does not exist"); + + CBalances totalBalances; + pcustomcsview->ForEachBalance([&](CScript const & owner, CTokenAmount const & balance) { + if (IsMineCached(*pwallet, owner) == ISMINE_SPENDABLE) { + totalBalances.Add(balance); + } + return true; + }); + auto it = totalBalances.balances.begin(); + for (int i = 0; it != totalBalances.balances.end(); it++, i++) { + CTokenAmount bal = CTokenAmount{(*it).first, (*it).second}; + std::string tokenIdStr = bal.nTokenId.ToString(); + auto token = pcustomcsview->GetToken(bal.nTokenId); + if (token->CreateSymbolKey(bal.nTokenId)==order->tokenFrom && bal.nValueamountFrom) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Not enough balance for Token %s for order amount %s!", order->tokenFrom, order->amountFrom)); + } + + targetHeight = ::ChainActive().Height() + 1; + } + + + CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); + metadata << static_cast(CustomTxType::FulfillOrder) + << fillorder; + + CScript scriptMeta; + scriptMeta << OP_RETURN << ToByteVector(metadata); + + const auto txVersion = GetTransactionVersion(targetHeight); + CMutableTransaction rawTx(txVersion); + + rawTx.vout.push_back(CTxOut(0, scriptMeta)); + + fund(rawTx, pwallet, {}); + + // check execution + { + LOCK(cs_main); + CCustomCSView mnview_dummy(*pcustomcsview); // don't write into actual DB + CCoinsViewCache coinview(&::ChainstateActive().CoinsTip()); + const auto res = ApplyFulfillOrderTx(mnview_dummy, coinview, CTransaction(rawTx), targetHeight, + ToByteVector(CDataStream{SER_NETWORK, PROTOCOL_VERSION, fillorder}), Params().GetConsensus()); + if (!res.ok) { + throw JSONRPCError(RPC_INVALID_REQUEST, "Execution test failed:\n" + res.msg); + } + } + return signsend(rawTx, pwallet, {})->GetHash().GetHex(); +} + +UniValue closeorder(const JSONRPCRequest& request) { + CWallet* const pwallet = GetWallet(request); + + RPCHelpMan{"closeorder", + "\nCloses (and submits to local node and network) order transaction.\n" + + HelpRequiringPassphrase(pwallet) + "\n", + { + {"orderTx", RPCArg::Type::STR, RPCArg::Optional::NO, "txid of maker order"}, + }, + RPCResult{ + "\"hash\" (string) The hex-encoded hash of broadcasted transaction\n" + }, + RPCExamples{ + HelpExampleCli("closeorder", "'{\"orderTx\":\"txid\"}'") + }, + }.Check(request); + + if (pwallet->chain().isInitialBlockDownload()) { + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot close order while still in Initial Block Download"); + } + pwallet->BlockUntilSyncedToCurrentChain(); + LockedCoinsScopedGuard lcGuard(pwallet); + + RPCTypeCheck(request.params, {UniValue::VOBJ}, false); + if (request.params[0].isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Invalid parameters, arguments 1 must be non-null and expected as \"orderTx\"}"); + } + CCloseOrder closeorder; + closeorder.orderTx= uint256S(request.params[0].getValStr()); + + int targetHeight; + { + LOCK(cs_main); + auto order = pcustomcsview->GetOrderByCreationTx(closeorder.orderTx); + if (!order) + throw JSONRPCError(RPC_INVALID_PARAMETER, "orderTx (" + closeorder.orderTx.GetHex() + ") does not exist"); + + targetHeight = ::ChainActive().Height() + 1; + } + + CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); + metadata << static_cast(CustomTxType::CloseOrder) + << closeorder; + + CScript scriptMeta; + scriptMeta << OP_RETURN << ToByteVector(metadata); + + const auto txVersion = GetTransactionVersion(targetHeight); + CMutableTransaction rawTx(txVersion); + + rawTx.vout.push_back(CTxOut(0, scriptMeta)); + + fund(rawTx, pwallet, {}); + + // check execution + { + LOCK(cs_main); + CCustomCSView mnview_dummy(*pcustomcsview); // don't write into actual DB + CCoinsViewCache coinview(&::ChainstateActive().CoinsTip()); + const auto res = ApplyCloseOrderTx(mnview_dummy, coinview, CTransaction(rawTx), targetHeight, + ToByteVector(CDataStream{SER_NETWORK, PROTOCOL_VERSION, closeorder}), Params().GetConsensus()); + if (!res.ok) { + throw JSONRPCError(RPC_INVALID_REQUEST, "Execution test failed:\n" + res.msg); + } + } + return signsend(rawTx, pwallet, {})->GetHash().GetHex(); +} + +UniValue listorders(const JSONRPCRequest& request) { + CWallet* const pwallet = GetWallet(request); + + RPCHelpMan{"listorders", + "\nReturn information about orders.\n" + + HelpRequiringPassphrase(pwallet) + "\n", + { + {"by", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", + { + {"token1", RPCArg::Type::STR, RPCArg::Optional::NO, "Token symbol"}, + {"token2", RPCArg::Type::STR, RPCArg::Optional::NO, "Token symbol"}, + }, + }, + }, + RPCResult + { + "{{...},...} (array) Json object with orders information\n" + }, + RPCExamples{ + HelpExampleCli("listorders", "'{\"token\":\"MyToken\"'") + + HelpExampleCli("listorders", "'{\"token\":\"MyToken1\",\"tokenPair\":\"Mytoken2\"'") + + }, + }.Check(request); + + std::string token1Symbol,token2Symbol; + if (request.params.size() > 0) + { + UniValue byObj = request.params[0].get_obj(); + if (!byObj["token1"].isNull()) token1Symbol=trim_ws(byObj["token1"].getValStr()); + if (!byObj["token2"].isNull()) token2Symbol=trim_ws(byObj["token2"].getValStr()); + } + + DCT_ID idToken1={std::numeric_limits::max()},idToken2={std::numeric_limits::max()}; + if (!token1Symbol.empty()) + { + auto token1 = pcustomcsview->GetTokenGuessId(token1Symbol, idToken1); + if (!token1) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Token %s does not exist!", token1Symbol)); + } + } + if (!token2Symbol.empty()) + { + auto token2 = pcustomcsview->GetTokenGuessId(token2Symbol, idToken2); + if (!token2) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Token %s does not exist!", token2Symbol)); + } + } + + UniValue ret(UniValue::VOBJ); + int limit=100; + if (idToken1.v!=std::numeric_limits::max() && idToken2.v!=std::numeric_limits::max()) + { + COrderView::TokenPair prefix(idToken1,idToken2); + pcustomcsview->ForEachOrder([&](COrderView::TokenPairKey const & key, COrderImplemetation order) { + ret.pushKVs(orderToJSON(order)); + + limit--; + return limit != 0; + },prefix); + + return ret; + } + else + { + pcustomcsview->ForEachOrder([&](COrderView::TokenPairKey const & key, COrderImplemetation order) { + ret.pushKVs(orderToJSON(order)); + + limit--; + return limit != 0; + }); + return ret; + } +} + +static const CRPCCommand commands[] = +{ +// category name actor (function) params +// --------------- ---------------------- --------------------- ---------- + {"orderbook", "createorder", &createorder, {"ownerAddress", "tokenFrom", "tokenTo", "amountFrom", "orderPrice"}}, + {"orderbook", "fulfillorder", &fulfillorder, {"ownerAddress", "orderTx", "amount"}}, + {"orderbook", "closeorder", &closeorder, {"orderTx"}}, + {"orderbook", "listorders", &listorders, {"tokenFrom", "tokenTo","ownerAddress"}}, + +}; + +void RegisterOrderbookRPCCommands(CRPCTable& tableRPC) { + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); +} diff --git a/src/masternodes/rpc_tokens.cpp b/src/masternodes/rpc_tokens.cpp index 8106a5dec9e..286c6c4e6a9 100644 --- a/src/masternodes/rpc_tokens.cpp +++ b/src/masternodes/rpc_tokens.cpp @@ -1,73 +1,6 @@ #include #include -static bool GetCustomTXInfo(const int nHeight, const CTransactionRef tx, CustomTxType& guess, Res& res, UniValue& txResults) -{ - std::vector metadata; - guess = GuessCustomTxType(*tx, metadata); - CCustomCSView mnview_dummy(*pcustomcsview); - - switch (guess) - { - case CustomTxType::CreateMasternode: - res = ApplyCreateMasternodeTx(mnview_dummy, *tx, nHeight, metadata, &txResults); - break; - case CustomTxType::ResignMasternode: - res = ApplyResignMasternodeTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, true, &txResults); - break; - case CustomTxType::CreateToken: - res = ApplyCreateTokenTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::UpdateToken: - res = ApplyUpdateTokenTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::UpdateTokenAny: - res = ApplyUpdateTokenAnyTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::MintToken: - res = ApplyMintTokenTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::CreatePoolPair: - res = ApplyCreatePoolPairTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::UpdatePoolPair: - res = ApplyUpdatePoolPairTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::PoolSwap: - res = ApplyPoolSwapTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::AddPoolLiquidity: - res = ApplyAddPoolLiquidityTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::RemovePoolLiquidity: - res = ApplyRemovePoolLiquidityTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::UtxosToAccount: - res = ApplyUtxosToAccountTx(mnview_dummy, *tx, nHeight, metadata, Params().GetConsensus(), &txResults); - break; - case CustomTxType::AccountToUtxos: - res = ApplyAccountToUtxosTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::AccountToAccount: - res = ApplyAccountToAccountTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::SetGovVariable: - res = ApplySetGovernanceTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::AnyAccountsToAccounts: - res = ApplyAnyAccountsToAccountsTx(mnview_dummy, ::ChainstateActive().CoinsTip(), *tx, nHeight, metadata, Params().GetConsensus(), true, &txResults); - break; - case CustomTxType::AutoAuthPrep: - res.ok = true; - res.msg = "AutoAuth"; - break; - default: - return false; - } - - return true; -} - UniValue createtoken(const JSONRPCRequest& request) { CWallet* const pwallet = GetWallet(request); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 7ed418b66dc..f4d26e478ff 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -239,6 +239,10 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listpoolshares", 1, "verbose" }, { "listpoolshares", 2, "is_mine_only" }, + { "createorder", 0, "order" }, + { "fulfillorder", 0, "order" }, + { "listorders", 0, "order" }, + { "listaccounthistory", 1, "options" }, { "accounthistorycount", 1, "options" }, diff --git a/src/rpc/register.h b/src/rpc/register.h index 804e305fcaa..029cf233d6c 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -23,10 +23,12 @@ void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC); void RegisterMasternodesRPCCommands(CRPCTable &tableRPC); /** Register accounts RPC commands */ void RegisterAccountsRPCCommands(CRPCTable &tableRPC); -/** Register tokens RPC commands */ -void RegisterTokensRPCCommands(CRPCTable &tableRPC); +/** Register orderbook RPC commands */ +void RegisterOrderbookRPCCommands(CRPCTable &tableRPC); /** Register poolpair RPC commands */ void RegisterPoolpairRPCCommands(CRPCTable &tableRPC); +/** Register tokens RPC commands */ +void RegisterTokensRPCCommands(CRPCTable &tableRPC); /** Register blockchain masternode RPC commands */ void RegisterMNBlockchainRPCCommands(CRPCTable &tableRPC); /** Register SPV (anchoring) RPC commands */ @@ -41,8 +43,9 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &t) RegisterRawTransactionRPCCommands(t); RegisterMasternodesRPCCommands(t); RegisterAccountsRPCCommands(t); - RegisterTokensRPCCommands(t); + RegisterOrderbookRPCCommands(t); RegisterPoolpairRPCCommands(t); + RegisterTokensRPCCommands(t); RegisterMNBlockchainRPCCommands(t); RegisterSpvRPCCommands(t); }