diff --git a/src/chainparams.cpp b/src/chainparams.cpp index a8bde98a966..61b62980ee1 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -773,6 +773,9 @@ class CRegTestParams : public CChainParams { vMasternodes.push_back({"bcrt1qyrfrpadwgw7p5eh3e9h3jmu4kwlz4prx73cqny", "bcrt1qmfvw3dp3u6fdvqkdc0y3lr0e596le9cf22vtsv"}); vMasternodes.push_back({"bcrt1qyeuu9rvq8a67j86pzvh5897afdmdjpyankp4mu", "bcrt1qurwyhta75n2g75u2u5nds9p6w9v62y8wr40d2r"}); + // For testing send after Eunos: 93ViFmLeJVgKSPxWGQHmSdT5RbeGDtGW4bsiwQM2qnQyucChMqQ + consensus.burnAddress = GetScriptForDestination(DecodeDestination("mfburnZSAM7Gs1hpDeNaMotJXSGA7edosG", *this)); + genesis = CreateGenesisBlock(1579045065, 0x207fffff, 1, { CTxOut(consensus.baseBlockSubsidy, GetScriptForDestination(DecodeDestination("mud4VMfbBqXNpbt8ur33KHKx8pk3npSq8c", *this)) // 6th masternode owner. for initdist tests diff --git a/src/consensus/params.h b/src/consensus/params.h index d47d9edebf7..5817c704bbc 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -89,6 +89,8 @@ struct Params { int EunosHeight; /** Foundation share after AMK, normalized to COIN = 100% */ CAmount foundationShareDFIP1; + /** Trackable burn address */ + CScript burnAddress; /** Struct to hold percentages for coinbase distribution. * Percentages are calculated out of 10000 */ diff --git a/src/masternodes/icxorder.cpp b/src/masternodes/icxorder.cpp index b8305517fd7..562b1b41408 100644 --- a/src/masternodes/icxorder.cpp +++ b/src/masternodes/icxorder.cpp @@ -29,11 +29,14 @@ const uint8_t CICXOrder::STATUS_OPEN = 0; const uint8_t CICXOrder::STATUS_CLOSED = 1; const uint8_t CICXOrder::STATUS_FILLED = 2; const uint8_t CICXOrder::STATUS_EXPIRED = 3; +const uint8_t CICXOrder::DFI_TOKEN_ID = 0; +const std::string CICXOrder::CHAIN_BTC = "BTC"; const uint32_t CICXMakeOffer::DEFAULT_EXPIRY = 100; const uint8_t CICXMakeOffer::STATUS_OPEN = 0; const uint8_t CICXMakeOffer::STATUS_CLOSED = 1; const uint8_t CICXMakeOffer::STATUS_EXPIRED = 2; +const int64_t CICXMakeOffer::TAKER_FEE_PER_BTC = 0.1; const uint32_t CICXSubmitDFCHTLC::DEFAULT_TIMEOUT = 100; const uint8_t CICXSubmitDFCHTLC::STATUS_OPEN = 0; diff --git a/src/masternodes/icxorder.h b/src/masternodes/icxorder.h index 87f07251c11..d952e263ffb 100644 --- a/src/masternodes/icxorder.h +++ b/src/masternodes/icxorder.h @@ -21,6 +21,8 @@ class CICXOrder static const uint8_t STATUS_CLOSED; static const uint8_t STATUS_FILLED; static const uint8_t STATUS_EXPIRED; + static const uint8_t DFI_TOKEN_ID; + static const std::string CHAIN_BTC; //! basic properties uint8_t orderType; //is maker buying or selling DFC asset to know which htlc to come first @@ -108,6 +110,7 @@ class CICXMakeOffer static const uint8_t STATUS_OPEN; static const uint8_t STATUS_CLOSED; static const uint8_t STATUS_EXPIRED; + static const int64_t TAKER_FEE_PER_BTC; //! basic properties uint256 orderTx; // txid for which order is the offer @@ -115,6 +118,7 @@ class CICXMakeOffer std::vector receiveDestination; // address or pubkey of receiving asset CScript ownerAddress; // address of token asset in case of EXT/DFC order uint32_t expiry; // when the offer exipres in number of blocks + CAmount takerFee; CICXMakeOffer() : orderTx(uint256()) diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index 131d512629b..2304acc6c2a 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -455,6 +455,35 @@ class CCustomTxVisitor : public boost::static_visitor return Res::Ok(); } + Res ModifyTokenBalance(DCT_ID const & id, CAmount amount, CScript const & from, CScript const & to) const { + CTokenAmount tokenAmount({id, amount}); + + // if "from" not supplied it will only add balance on "to" address + if (!from.empty()) + { + auto res = mnview.SubBalance(from, tokenAmount); + if (!res.ok) + return Res::Err("%s: %s", __func__, res.msg); + } + + // if "to" not supplied it will only sub balance from "form" address + if (!to.empty()) + { + auto res = mnview.AddBalance(to,tokenAmount); + if (!res.ok) + return Res::Err("%s: %s", __func__, res.msg); + } + + return Res::Ok(); + } + + Res TakerFeeTransfer(CScript const & from, CScript const & to, CAmount amount) const { + DCT_ID idDFI{0}; + CTokenAmount takerFee({idDFI,amount}); + + return ModifyTokenBalance(idDFI, amount, from, to); + } + ResVal MintableToken(DCT_ID id, const CTokenImplementation& token) const { if (token.destructionTx != uint256{}) { return Res::Err("token %s already destroyed at height %i by tx %s", token.symbol, @@ -1088,9 +1117,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor Res operator()(const CICXCreateOrderMessage& obj) const { auto res = CheckICXTx(); - if (!res) { + if (!res) return res; - } CICXOrderImplemetation order; static_cast(order) = obj; @@ -1102,61 +1130,47 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor return Res::Err("%s: %s", __func__, "token assets must be specified"); if (order.chain.empty()) return Res::Err("%s: %s", __func__, "chain assets must be specified"); - - if (order.orderType != CICXOrder::TYPE_INTERNAL && order.orderType != CICXOrder::TYPE_EXTERNAL) { + if (order.orderType != CICXOrder::TYPE_INTERNAL && order.orderType != CICXOrder::TYPE_EXTERNAL) return Res::Err("invalid order type!"); - } + if (order.orderType == CICXOrder::TYPE_INTERNAL) { auto tokenFrom = mnview.GetToken(order.idToken); - if (!tokenFrom) { + if (!tokenFrom) return Res::Err("%s: %s", __func__, "tokenFrom (" + tokenFrom->CreateSymbolKey(order.idToken) + ") does not exist!"); - } if (order.ownerAddress.empty()) return Res::Err("ownerAddress must be not-null!"); // subtract the balance from tokenFrom to dedicate them for the order - CTokenAmount amount={order.idToken,order.amountFrom}; - res = mnview.SubBalance(order.ownerAddress,amount); - if (!res.ok) { - return Res::Err("%s: %s", __func__, res.msg); - } - CScript txidaddr(order.creationTx.begin(),order.creationTx.end()); - res = mnview.AddBalance(txidaddr,amount); - if (!res.ok) { + CScript txidAddr(order.creationTx.begin(), order.creationTx.end()); + res = ModifyTokenBalance(order.idToken, order.amountFrom, order.ownerAddress, txidAddr); + if (!res.ok) return Res::Err("%s: %s", __func__, res.msg); - } } else if (order.orderType == CICXOrder::TYPE_EXTERNAL) { auto tokenTo = mnview.GetToken(order.idToken); - if (!tokenTo) { - return Res::Err("%s: %s", __func__, "tokenTo id (" + tokenTo->CreateSymbolKey(order.idToken) + ") does not exist!"); - } + if (!tokenTo) + return Res::Err("%s: %s", __func__, "tokenTo id (" + tokenTo->CreateSymbolKey(order.idToken) + ") does not exist!"); } - if (order.amountFrom == 0) { + if (order.amountFrom == 0) return Res::Err("order amountFrom must be greater than 0!"); - } - if (order.amountToFill != order.amountFrom) { + if (order.amountToFill != order.amountFrom) return Res::Err("order amountToFill does not equal to amountFrom!"); - } - if (order.orderPrice == 0) { + if (order.orderPrice == 0) return Res::Err("order price must be greater than 0!"); - } - if (order.expiry == 0) { + if (order.expiry == 0) return Res::Err("order expiry must be greater than 0!"); - } return mnview.ICXCreateOrder(order); } Res operator()(const CICXMakeOfferMessage& obj) const { auto res = CheckICXTx(); - if (!res) { + if (!res) return res; - } CICXMakeOfferImplemetation makeoffer; static_cast(makeoffer) = obj; @@ -1165,17 +1179,30 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor makeoffer.creationHeight = height; auto order=mnview.GetICXOrderByCreationTx(makeoffer.orderTx); - if (!order) { + if (!order) return Res::Err("%s: %s", __func__, "order with creation tx " + makeoffer.orderTx.GetHex() + " does not exists!"); - } - if (order->amountToFill * order->orderPrice / COIN < makeoffer.amount) { + + CAmount calcedAmount(static_cast((arith_uint256(order->amountToFill) * arith_uint256(order->orderPrice) / arith_uint256(COIN)).GetLow64())); + if (calcedAmount < makeoffer.amount) return Res::Err("%s: %s", __func__, "cannot fill order with that amount, order (" + order->creationTx.GetHex() + ") has less amount to fill!"); - } + + CScript txidAddr(makeoffer.creationTx.begin(),makeoffer.creationTx.end()); + + //calculating DFI per BTC + auto DFIBTCpair = mnview.GetPoolPair({5}); + CAmount DFIperBTC = (arith_uint256(DFIBTCpair->reserveB) * arith_uint256(COIN) / DFIBTCpair->reserveA).GetLow64(); if (order->orderType == CICXOrder::TYPE_INTERNAL) { if (makeoffer.receiveDestination.empty()) return Res::Err("receiveAddress must not be null!"); + + // calculating and locking takerFee in offer txidaddr + makeoffer.takerFee = (arith_uint256(makeoffer.amount) * CICXMakeOffer::TAKER_FEE_PER_BTC * arith_uint256(DFIperBTC)).GetLow64(); + CScript receiveAddress(makeoffer.receiveDestination.begin(), makeoffer.receiveDestination.end()); + res = TakerFeeTransfer(receiveAddress, txidAddr, makeoffer.takerFee); + if (!res.ok) + return Res::Err("%s: %s", __func__, res.msg); } else if (order->orderType == CICXOrder::TYPE_EXTERNAL) { @@ -1185,16 +1212,16 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor return Res::Err("%s: %s", __func__, "Invalid receivePubKey, (" + HexStr(makeoffer.receiveDestination) + ") receivePubkey is not a valid pubkey!"); // subtract the balance from tokenTo to dedicate them for the offer - CTokenAmount amount={order->idToken,makeoffer.amount}; - res = mnview.SubBalance(makeoffer.ownerAddress,amount); - if (!res.ok) { + res = ModifyTokenBalance(order->idToken, makeoffer.amount, makeoffer.ownerAddress, txidAddr); + if (!res.ok) return Res::Err("%s: %s", __func__, res.msg); - } - CScript txidaddr(makeoffer.creationTx.begin(),makeoffer.creationTx.end()); - res = mnview.AddBalance(txidaddr,amount); - if (!res.ok) { + + // calculating and locking takerFee in offer txidaddr + CAmount BTCAmount(static_cast((arith_uint256(makeoffer.amount) * arith_uint256(COIN) / arith_uint256(order->orderPrice)).GetLow64())); + makeoffer.takerFee = (arith_uint256(BTCAmount) * CICXMakeOffer::TAKER_FEE_PER_BTC * arith_uint256(DFIperBTC)).GetLow64(); + res = TakerFeeTransfer(makeoffer.ownerAddress, txidAddr, makeoffer.takerFee); + if (!res.ok) return Res::Err("%s: %s", __func__, res.msg); - } } return mnview.ICXMakeOffer(makeoffer); @@ -1213,13 +1240,11 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor submitdfchtlc.creationHeight = height; auto offer=mnview.GetICXMakeOfferByCreationTx(submitdfchtlc.offerTx); - if (!offer) { + if (!offer) return Res::Err("offer with creation tx %s does not exists!", submitdfchtlc.offerTx.GetHex()); - } auto order=mnview.GetICXOrderByCreationTx(offer->orderTx); - if (!order) { + if (!order) return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); - } if (submitdfchtlc.hash.IsNull()) return Res::Err("Invalid hash, htlc hash is empty and it must be set!"); @@ -1228,44 +1253,54 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (submitdfchtlc.receiveAddress.empty()) return Res::Err("Invalid, receiveAddress in htlc must be destination where tokens go on claim!"); - CScript txidaddr; + CScript txidAddr; if (order->orderType == CICXOrder::TYPE_INTERNAL) { - if (submitdfchtlc.amount * order->orderPrice / COIN != offer->amount) - return Res::Err("amount in dfc htlc must match the amount necessary for offer amount - %f != %f!", ValueFromAmount(submitdfchtlc.amount * order->orderPrice / COIN).getValStr(), ValueFromAmount(offer->amount).getValStr()); + CAmount calcedAmount(static_cast((arith_uint256(submitdfchtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)).GetLow64())); + if (calcedAmount != offer->amount) + return Res::Err("amount in dfc htlc must match the amount necessary for offer amount - %f != %f!", + ValueFromAmount(calcedAmount).getValStr(), ValueFromAmount(offer->amount).getValStr()); if (!submitdfchtlc.receivePubkey.IsFullyValid()) return Res::Err("Invalid receivePubKey, (" + HexStr(submitdfchtlc.receivePubkey) + ") is not a valid pubkey!"); - txidaddr = CScript(order->creationTx.begin(),order->creationTx.end()); + txidAddr = CScript(order->creationTx.begin(),order->creationTx.end()); + + // burn DFI for takerFee==makerDeposit + CScript offerTxidAddr(offer->creationTx.begin(), offer->creationTx.end()); + + // takerFee + res = TakerFeeTransfer(offerTxidAddr, consensus.burnAddress, offer->takerFee); + if (!res.ok) + return Res::Err("%s: %s", __func__, res.msg); + + // makerFee + res = TakerFeeTransfer(order->ownerAddress, consensus.burnAddress, offer->takerFee); + if (!res.ok) + return Res::Err("%s: %s", __func__, res.msg); } else if (order->orderType == CICXOrder::TYPE_EXTERNAL) { if (submitdfchtlc.amount != offer->amount) - return Res::Err("amount in dfc htlc must match the amount in offer - %s != %s!", ValueFromAmount(submitdfchtlc.amount).getValStr(), ValueFromAmount(offer->amount).getValStr()); + return Res::Err("amount in dfc htlc must match the amount in offer - %s != %s!", + ValueFromAmount(submitdfchtlc.amount).getValStr(), ValueFromAmount(offer->amount).getValStr()); - txidaddr = CScript(offer->creationTx.begin(),offer->creationTx.end()); - } - // subtract the balance from tokenTo to dedicate them for the offer - CTokenAmount amount={order->idToken,submitdfchtlc.amount}; - res = mnview.SubBalance(txidaddr,amount); - if (!res.ok) { - return Res::Err("%s: %s", __func__, res.msg); - } - CScript htlctxidaddr(submitdfchtlc.creationTx.begin(),submitdfchtlc.creationTx.end()); - res = mnview.AddBalance(htlctxidaddr,amount); - if (!res.ok) { - return Res::Err("%s: %s", __func__, res.msg); + txidAddr = CScript(offer->creationTx.begin(), offer->creationTx.end()); } + + // subtract the balance from order/offer txidaddr and dedicate them for the dfc htlc + CScript htlcTxidAddr(submitdfchtlc.creationTx.begin(), submitdfchtlc.creationTx.end()); + res = ModifyTokenBalance(order->idToken, submitdfchtlc.amount, txidAddr, htlcTxidAddr); + if (!res.ok) + return Res::Err("%s: %s", __func__, res.msg); return mnview.ICXSubmitDFCHTLC(submitdfchtlc); } Res operator()(const CICXSubmitEXTHTLCMessage& obj) const { auto res = CheckICXTx(); - if (!res) { + if (!res) return res; - } CICXSubmitEXTHTLCImplemetation submitexthtlc; static_cast(submitexthtlc) = obj; @@ -1274,13 +1309,11 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor submitexthtlc.creationHeight = height; auto offer=mnview.GetICXMakeOfferByCreationTx(submitexthtlc.offerTx); - if (!offer) { + if (!offer) return Res::Err("order with creation tx %s does not exists!", submitexthtlc.offerTx.GetHex()); - } auto order=mnview.GetICXOrderByCreationTx(offer->orderTx); - if (!order) { + if (!order) return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); - } if (submitexthtlc.htlcscriptAddress.empty()) return Res::Err("Invalid htlcscriptAddress, htlcscriptAddress is empty and it must be set!"); @@ -1294,12 +1327,28 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (order->orderType==CICXOrder::TYPE_INTERNAL) { if (submitexthtlc.amount != offer->amount) - return Res::Err("amount in ext htlc must match the amount in the offer - %s != %s!", ValueFromAmount(submitexthtlc.amount).getValStr(), ValueFromAmount(offer->amount).getValStr()); + return Res::Err("amount in ext htlc must match the amount in the offer - %s != %s!", + ValueFromAmount(submitexthtlc.amount).getValStr(), ValueFromAmount(offer->amount).getValStr()); } else if (order->orderType==CICXOrder::TYPE_EXTERNAL) { - if (submitexthtlc.amount * order->orderPrice / COIN != offer->amount) - return Res::Err("amount in dfc htlc must match the amount necessary for offer amount - %s != %s!", ValueFromAmount(submitexthtlc.amount * order->orderPrice / COIN).getValStr(), ValueFromAmount(offer->amount).getValStr()); + CAmount calcedAmount(static_cast((arith_uint256(submitexthtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)).GetLow64())); + if (calcedAmount != offer->amount) + return Res::Err("amount in dfc htlc must match the amount necessary for offer amount - %s != %s!", + ValueFromAmount(calcedAmount).getValStr(), ValueFromAmount(offer->amount).getValStr()); + + // burn DFI for takerFee==makerDeposit + CScript offerTxidAddr(offer->creationTx.begin(),offer->creationTx.end()); + + // takerFee + res = TakerFeeTransfer(offerTxidAddr, consensus.burnAddress, offer->takerFee); + if (!res.ok) + return Res::Err("%s: %s", __func__, res.msg); + + // makerFee + res = TakerFeeTransfer(submitexthtlc.receiveAddress, consensus.burnAddress, offer->takerFee); + if (!res.ok) + return Res::Err("%s: %s", __func__, res.msg); } return mnview.ICXSubmitEXTHTLC(submitexthtlc); @@ -1307,9 +1356,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor Res operator()(const CICXClaimDFCHTLCMessage& obj) const { auto res = CheckICXTx(); - if (!res) { + if (!res) return res; - } CICXClaimDFCHTLCImplemetation claimdfchtlc; static_cast(claimdfchtlc) = obj; @@ -1318,9 +1366,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor claimdfchtlc.creationHeight = height; auto dfchtlc=mnview.GetICXSubmitDFCHTLCByCreationTx(claimdfchtlc.dfchtlcTx); - if (!dfchtlc) { + if (!dfchtlc) return Res::Err("dfc htlc with creation tx %s does not exists!", claimdfchtlc.dfchtlcTx.GetHex()); - } bool found=false; mnview.ForEachICXSubmitDFCHTLCOpen([&found,&dfchtlc](CICXOrderView::TxidPairKey const & key, uint8_t i) { @@ -1343,25 +1390,36 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor return Res::Err("hash generated from given seed is different than in dfc htlc: %s - %s!", calcHash.GetHex(), dfchtlc->hash.GetHex()); auto offer=mnview.GetICXMakeOfferByCreationTx(dfchtlc->offerTx); - if (!offer) { + if (!offer) return Res::Err("offer with creation tx %s does not exists!", dfchtlc->offerTx.GetHex()); - } auto order=mnview.GetICXOrderByCreationTx(offer->orderTx); - if (!order) { + if (!order) return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); - } - CTokenAmount amount; - CScript htlctxidaddr(dfchtlc->creationTx.begin(),dfchtlc->creationTx.end()); - amount = {order->idToken,dfchtlc->amount}; - - res = mnview.SubBalance(htlctxidaddr,amount); - if (!res.ok) { + // claim DFC HTLC to receiveAddress + CScript htlcTxidAddr(dfchtlc->creationTx.begin(),dfchtlc->creationTx.end()); + res = ModifyTokenBalance(order->idToken, dfchtlc->amount, htlcTxidAddr, dfchtlc->receiveAddress); + if (!res.ok) return Res::Err("%s: %s", __func__, res.msg); - } - res = mnview.AddBalance(dfchtlc->receiveAddress,amount); - if (!res.ok) { + + DCT_ID DFIToken({CICXOrder::DFI_TOKEN_ID}); + // refund makerDeposit + res = ModifyTokenBalance(DFIToken, offer->takerFee, CScript(), order->ownerAddress); + if (!res.ok) + return Res::Err("%s: %s", __func__, res.msg); + + // makerIncentive + res = ModifyTokenBalance(DFIToken, offer->takerFee * 25 / 100, CScript(), order->ownerAddress); + if (!res.ok) return Res::Err("%s: %s", __func__, res.msg); + + // makerBonus + DCT_ID BTC; + if (mnview.GetTokenGuessId(CICXOrder::CHAIN_BTC,BTC) && order->idToken == BTC && order->chain == CICXOrder::CHAIN_BTC) + { + res = ModifyTokenBalance(BTC, offer->takerFee * 50 / 100, CScript(), order->ownerAddress); + if (!res.ok) + return Res::Err("%s: %s", __func__, res.msg); } if (order->orderType == CICXOrder::TYPE_INTERNAL) @@ -1370,7 +1428,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor } else if (order->orderType == CICXOrder::TYPE_EXTERNAL) { - order->amountToFill -= dfchtlc->amount * COIN / order->orderPrice; + CAmount calcedAmount(static_cast((arith_uint256(dfchtlc->amount) * arith_uint256(COIN) / arith_uint256(order->orderPrice)).GetLow64())); + order->amountToFill -= calcedAmount; } if (order->amountToFill==0) @@ -1378,18 +1437,17 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor order->closeTx=claimdfchtlc.creationTx; order->closeHeight = height; res = mnview.ICXCloseOrderTx(*order, CICXOrder::STATUS_FILLED); - if (!res.ok) { + if (!res.ok) return Res::Err("%s: %s", __func__, res.msg); } - } + return mnview.ICXClaimDFCHTLC(claimdfchtlc,*order); } Res operator()(const CICXCloseOrderMessage& obj) const { auto res = CheckICXTx(); - if (!res) { + if (!res) return res; - } CICXCloseOrderImplemetation closeorder; static_cast(closeorder) = obj; @@ -1416,30 +1474,24 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (order->orderType == CICXOrder::TYPE_INTERNAL && order->amountToFill > 0) { - // subtract the balance from txidaddr and return to owner - CTokenAmount amount={order->idToken,order->amountToFill}; - CScript txidaddr(order->creationTx.begin(),order->creationTx.end()); - res = mnview.AddBalance(txidaddr,amount); - if (!res.ok) { + // subtract the balance from txidAddr and return to owner + CScript txidAddr(order->creationTx.begin(),order->creationTx.end()); + res = ModifyTokenBalance(order->idToken, order->amountToFill, txidAddr, order->ownerAddress); + if (!res.ok) return Res::Err("%s: %s", __func__, res.msg); - } - res = mnview.SubBalance(order->ownerAddress,amount); - if (!res.ok) { - return Res::Err("%s: %s", __func__, res.msg); - } } res = mnview.ICXCloseOrder(closeorder); if (!res) return res; + return mnview.ICXCloseOrderTx(*order,CICXOrder::STATUS_CLOSED); } Res operator()(const CICXCloseOfferMessage& obj) const { auto res = CheckICXTx(); - if (!res) { + if (!res) return res; - } CICXCloseOfferImplemetation closeoffer; static_cast(closeoffer) = obj; @@ -1448,39 +1500,29 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor closeoffer.creationHeight = height; std::unique_ptr offer; - if (!(offer = mnview.GetICXMakeOfferByCreationTx(closeoffer.offerTx))) { + if (!(offer = mnview.GetICXMakeOfferByCreationTx(closeoffer.offerTx))) return Res::Err("order with creation tx %s does not exists!", closeoffer.offerTx.GetHex()); - } std::unique_ptr order; - if (!(order = mnview.GetICXOrderByCreationTx(offer->orderTx))) { + if (!(order = mnview.GetICXOrderByCreationTx(offer->orderTx))) return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); - } - if (!offer->closeTx.IsNull()) { + if (!offer->closeTx.IsNull()) return Res::Err("order with creation tx %s is already closed!", closeoffer.offerTx.GetHex()); - } const Coin& auth = coins.AccessCoin(COutPoint(offer->creationTx, 1)); // always n=1 output // check auth - if (!HasAuth(auth.out.scriptPubKey)) { + if (!HasAuth(auth.out.scriptPubKey)) return Res::Err("%s: %s", __func__, "tx must have at least one input from order owner"); - } offer->closeTx = closeoffer.creationTx; offer->closeHeight = closeoffer.creationHeight; if (order->orderType == CICXOrder::TYPE_EXTERNAL) { - // subtract the balance from txidaddr and return to owner - CTokenAmount amount={order->idToken,offer->amount}; - CScript txidaddr(offer->creationTx.begin(),offer->creationTx.end()); - res = mnview.SubBalance(txidaddr,amount); - if (!res.ok) { + // subtract the balance from txidAddr and return to owner + CScript txidAddr(offer->creationTx.begin(),offer->creationTx.end()); + res = ModifyTokenBalance(order->idToken, offer->amount, txidAddr, offer->ownerAddress); + if (!res.ok) return Res::Err("%s: %s", __func__, res.msg); - } - res = mnview.AddBalance(offer->ownerAddress,amount); - if (!res.ok) { - return Res::Err("%s: %s", __func__, res.msg); - } } res = mnview.ICXCloseOffer(closeoffer); diff --git a/src/masternodes/rpc_customtx.cpp b/src/masternodes/rpc_customtx.cpp index 68c8c549d92..58c70795bdd 100644 --- a/src/masternodes/rpc_customtx.cpp +++ b/src/masternodes/rpc_customtx.cpp @@ -241,16 +241,16 @@ class CCustomTxRpcVisitor : public boost::static_visitor rpcInfo.pushKV("chainFrom", obj.chain); rpcInfo.pushKV("tokenTo", token->CreateSymbolKey(obj.idToken)); } - rpcInfo.pushKV("amountFrom", obj.amountFrom); - rpcInfo.pushKV("amountToFill", obj.amountToFill); - rpcInfo.pushKV("orderPrice", obj.orderPrice); + rpcInfo.pushKV("amountFrom", ValueFromAmount(obj.amountFrom)); + rpcInfo.pushKV("amountToFill", ValueFromAmount(obj.amountToFill)); + rpcInfo.pushKV("orderPrice", ValueFromAmount(obj.orderPrice)); rpcInfo.pushKV("expiry", static_cast(obj.expiry)); } void operator()(const CICXMakeOfferMessage& obj) const { auto order = mnview.GetICXOrderByCreationTx(obj.orderTx); rpcInfo.pushKV("orderTx", obj.orderTx.GetHex()); - rpcInfo.pushKV("amount", obj.amount); + rpcInfo.pushKV("amount", ValueFromAmount(obj.amount)); if (order->orderType == order->TYPE_INTERNAL) rpcInfo.pushKV("receiveAddress", ScriptToString(CScript(obj.receiveDestination.begin(),obj.receiveDestination.end()))); else @@ -266,7 +266,7 @@ class CCustomTxRpcVisitor : public boost::static_visitor auto offer = mnview.GetICXMakeOfferByCreationTx(obj.offerTx); auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); rpcInfo.pushKV("offerTx", obj.offerTx.GetHex()); - rpcInfo.pushKV("amount", obj.amount); + rpcInfo.pushKV("amount", ValueFromAmount(obj.amount)); rpcInfo.pushKV("receiveAddress", ScriptToString(obj.receiveAddress)); if (order->orderType == CICXOrder::TYPE_INTERNAL) rpcInfo.pushKV("receivePubkey", HexStr(obj.receivePubkey)); @@ -278,7 +278,7 @@ class CCustomTxRpcVisitor : public boost::static_visitor auto offer = mnview.GetICXMakeOfferByCreationTx(obj.offerTx); auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); rpcInfo.pushKV("offerTx", obj.offerTx.GetHex()); - rpcInfo.pushKV("amount", obj.amount); + rpcInfo.pushKV("amount", ValueFromAmount(obj.amount)); if (order->orderType == CICXOrder::TYPE_EXTERNAL) rpcInfo.pushKV("receiveAddress", ScriptToString(obj.receiveAddress)); rpcInfo.pushKV("hash", obj.hash.GetHex()); diff --git a/src/masternodes/rpc_icxorderbook.cpp b/src/masternodes/rpc_icxorderbook.cpp index 37b23cc04fe..b11d4008744 100644 --- a/src/masternodes/rpc_icxorderbook.cpp +++ b/src/masternodes/rpc_icxorderbook.cpp @@ -34,7 +34,8 @@ UniValue icxOrderToJSON(CICXOrderImplemetation const& order, uint8_t status) { orderObj.pushKV("amountFrom", ValueFromAmount(order.amountFrom)); orderObj.pushKV("amountToFill", ValueFromAmount(order.amountToFill)); orderObj.pushKV("orderPrice", ValueFromAmount(order.orderPrice)); - orderObj.pushKV("amountToFillInToAsset", ValueFromAmount(order.amountToFill * order.orderPrice / COIN)); + CAmount calcedAmount(static_cast((arith_uint256(order.amountToFill) * arith_uint256(order.orderPrice) / arith_uint256(COIN)).GetLow64())); + orderObj.pushKV("amountToFillInToAsset", ValueFromAmount(calcedAmount)); orderObj.pushKV("height", static_cast(order.creationHeight)); orderObj.pushKV("expireHeight", static_cast(order.creationHeight + order.expiry)); if (order.closeHeight > -1) @@ -380,8 +381,8 @@ UniValue icxmakeoffer(const JSONRPCRequest& request) { if (!order) throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("orderTx (%s) does not exist",makeoffer.orderTx.GetHex())); - if (order->amountToFill * order->orderPrice / COIN < makeoffer.amount) - throw JSONRPCError(RPC_INVALID_PARAMETER, "cannot make offer with that amount, order (" + order->creationTx.GetHex() + ") has less amount to fill!"); + // if (order->amountToFill * order->orderPrice / COIN < makeoffer.amount) + // throw JSONRPCError(RPC_INVALID_PARAMETER, "cannot make offer with that amount, order (" + order->creationTx.GetHex() + ") has less amount to fill!"); if (order->orderType == CICXOrder::TYPE_INTERNAL) { @@ -537,9 +538,9 @@ UniValue icxsubmitdfchtlc(const JSONRPCRequest& request) { if (order->orderType == CICXOrder::TYPE_INTERNAL) { - if (submitdfchtlc.amount * order->orderPrice / COIN != offer->amount) - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("cannot make dfc htlc with that amount, different amount necessary for offer (%s) - %s != %s!", - offer->creationTx.GetHex(), ValueFromAmount(submitdfchtlc.amount * order->orderPrice / COIN).getValStr(), ValueFromAmount(offer->amount).getValStr())); + // if (submitdfchtlc.amount * order->orderPrice / COIN != offer->amount) + // throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("cannot make dfc htlc with that amount, different amount necessary for offer (%s) - %s != %s!", + // offer->creationTx.GetHex(), ValueFromAmount(submitdfchtlc.amount * order->orderPrice / COIN).getValStr(), ValueFromAmount(offer->amount).getValStr())); if (!metaObj["receivePubkey"].isNull()) { submitdfchtlc.receivePubkey = PublickeyFromString(trim_ws(metaObj["receivePubkey"].getValStr())); @@ -773,9 +774,9 @@ UniValue icxsubmitexthtlc(const JSONRPCRequest& request) { } else if (order->orderType == CICXOrder::TYPE_EXTERNAL) { - if (submitexthtlc.amount * order->orderPrice / COIN != offer->amount ) - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("cannot make ext htlc with that amount, different amount necessary for offer (%s) - %s != %s!", - offer->creationTx.GetHex(), ValueFromAmount(submitexthtlc.amount * order->orderPrice / COIN).getValStr(), ValueFromAmount(offer->amount).getValStr())); + // if (submitexthtlc.amount * order->orderPrice / COIN != offer->amount ) + // throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("cannot make ext htlc with that amount, different amount necessary for offer (%s) - %s != %s!", + // offer->creationTx.GetHex(), ValueFromAmount(submitexthtlc.amount * order->orderPrice / COIN).getValStr(), ValueFromAmount(offer->amount).getValStr())); if (!metaObj["receiveAddress"].isNull()) submitexthtlc.receiveAddress=DecodeScript(metaObj["receiveAddress"].getValStr()); diff --git a/src/validation.cpp b/src/validation.cpp index 7d02894a8a0..2daf963710b 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2536,14 +2536,14 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl if (offer) { auto order = cache.GetICXOrderByCreationTx(offer->orderTx); + CScript txidAddr(offer->creationTx.begin(),offer->creationTx.end()); if (order && order->orderType == CICXOrder::TYPE_EXTERNAL) { CTokenAmount amount({order->idToken,offer->amount}); - CScript txidaddr = CScript(offer->creationTx.begin(),offer->creationTx.end()); - auto res = cache.SubBalance(txidaddr,amount); + auto res = cache.SubBalance(txidAddr,amount); if (!res) - LogPrintf("Can't subtract balance from offer txidaddr: %s\n", res.msg); + LogPrintf("Can't subtract balance from offer txidAddr: %s\n", res.msg); if (res.ok) { res = cache.AddBalance(offer->ownerAddress,amount); @@ -2552,6 +2552,18 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl } } + DCT_ID idDFI{0}; + CTokenAmount takerFee({idDFI,offer->takerFee}); + auto res = cache.SubBalance(txidAddr,takerFee); + if (!res) + LogPrintf("Can't subtract takerFee from offer txidAddr: %s\n", res.msg); + if (res.ok) + { + res = cache.AddBalance(offer->ownerAddress,takerFee); + if (!res) + LogPrintf("Can't refund takerFee back to owner: %s\n", res.msg); + } + if (res.ok) { res = cache.ICXCloseMakeOfferTx(*offer,status); diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py index c4701abf4d2..e6903591e79 100755 --- a/test/functional/rpc_help.py +++ b/test/functional/rpc_help.py @@ -33,7 +33,7 @@ def test_categories(self): # command titles titles = [line[3:-3] for line in node.help().splitlines() if line.startswith('==')] - components = ['Accounts', 'Blockchain', 'Control', 'Generating', 'Masternodes', 'Mining', 'Network', 'Oracles', 'Poolpair', 'Rawtransactions', 'Spv', 'Tokens', 'Util'] + components = ['Accounts', 'Blockchain', 'Control', 'Generating', "Icxorderbook", 'Masternodes', 'Mining', 'Network', 'Oracles', 'Poolpair', 'Rawtransactions', 'Spv', 'Tokens', 'Util'] if self.is_wallet_compiled(): components.append('Wallet') diff --git a/test/lint/check-rpc-mappings.py b/test/lint/check-rpc-mappings.py index 0198312a5ec..d18185bdd53 100755 --- a/test/lint/check-rpc-mappings.py +++ b/test/lint/check-rpc-mappings.py @@ -24,6 +24,7 @@ "src/masternodes/rpc_tokens.cpp", "src/masternodes/rpc_poolpair.cpp", "src/masternodes/rpc_oracles.cpp", + "src/masternodes/rpc_icxorderbook.cpp", "src/spv/spv_rpc.cpp", ] # Source file (relative to root) containing conversion mapping