Skip to content

Commit

Permalink
Merge #1038: Allow any token to be traded for Bitcoin
Browse files Browse the repository at this point in the history
f7d958d Free the DEx and allow other tokens to be traded (dexX7)
1e7bd04 Ensure there can only be one active offer, when creating a new DEx offer (dexX7)
7e34e10 Handle DEx payment based on offer for sale (dexX7)
dceb321 Only check for primary tokens, when DEx is not free yet (dexX7)
eeaa883 When creating DEx orders, parse amount based on token divisibility (dexX7)
f57de0e When creating DEx offer, check there is no other (dexX7)
158250d Add feature activation for freeing the DEx (dexX7)

Pull request description:

  This pull request updates the traditional distributed exchange, where users can trade Omni and Test Omni for Bitcoin.

  When this feature is activated, any token can be traded and it's no longer limited to the two primary native tokens of the Omni Layer protocol.

Tree-SHA512: ea7836279463e9e399ee3a054ee537badc4701d85d30cbd9a0f2608c533e5cd82b07ac8d04fb2cf644cd5d4add60927b4de236e232795104b8e03581363cd50f
  • Loading branch information
dexX7 committed Oct 30, 2019
2 parents 289f032 + f7d958d commit 5f13f1c
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 17 deletions.
85 changes: 81 additions & 4 deletions src/omnicore/dex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <uint256.h>

#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

#include <openssl/sha.h>

Expand All @@ -42,6 +43,57 @@ bool DEx_offerExists(const std::string& addressSeller, uint32_t propertyId)
return !(my_offers.find(key) == my_offers.end());
}

/**
* Checks, if the seller has any open offer.
*/
bool DEx_hasOffer(const std::string& addressSeller)
{
for (auto const& offer : my_offers) {
if (offer.first.find(addressSeller) == 0) {
return true;
}
}

return false;
}

/**
* Retrieves the identifier of the token for sale.
*
* NOTE: special care, if there are multiple open offers!
* NOTE: the assumption is there can only be one active offer per seller!
*
* @param addressSeller The address of the seller with an open offer
* @param retTokenId The token identifier for sale
* @return True, if there is an open offer
*/
bool DEx_getTokenForSale(const std::string& addressSeller, uint32_t& retTokenId)
{
for (auto const& offer : my_offers) {
if (offer.first.find(addressSeller) == 0) {

// Format is: "address-tokenid"
std::vector<std::string> vstr;
boost::split(vstr, offer.first, boost::is_any_of("-"), boost::token_compress_on);

if (vstr.size() != 2) {
PrintToLog("ERROR: failed to parse token for sale: %s\n", __func__, offer.first);
return false;
}

try {
retTokenId = boost::lexical_cast<uint32_t>(vstr[1]);
return true;
}
catch (boost::bad_lexical_cast const& e) {
PrintToLog("ERROR: failed to parse token for sale: %s (%s)\n", __func__, offer.first, e.what());
}
}
}

return false;
}

/**
* Retrieves a sell offer.
*
Expand Down Expand Up @@ -153,6 +205,13 @@ int DEx_offerCreate(const std::string& addressSeller, uint32_t propertyId, int64
return (DEX_ERROR_SELLOFFER -10); // offer already exists
}

// Ensure further there can only be one active offer
if (IsFeatureActivated(FEATURE_FREEDEX, block)) {
if (DEx_hasOffer(addressSeller)) {
return (DEX_ERROR_SELLOFFER -10); // offer already exists
}
}

const std::string key = STR_SELLOFFER_ADDR_PROP_COMBO(addressSeller, propertyId);
if (msc_debug_dex) PrintToLog("%s(%s|%s), nValue=%d)\n", __func__, addressSeller, key, amountOffered);

Expand Down Expand Up @@ -443,12 +502,30 @@ int DEx_payment(const uint256& txid, unsigned int vout, const std::string& addre

int rc = DEX_ERROR_PAYMENT;

uint32_t propertyId = OMNI_PROPERTY_MSC; // test for MSC accept first
CMPAccept* p_accept = DEx_getAccept(addressSeller, propertyId, addressBuyer);
uint32_t propertyId = OMNI_PROPERTY_MSC;
CMPAccept* p_accept = NULL;

if (!p_accept) {
propertyId = OMNI_PROPERTY_TMSC; // test for TMSC accept second
/**
* When the feature is not activated, first check, if there is an open offer
* for OMNI, and if not, check if there is an open offer for TOMNI.
*
* If the feature is activated, simply retrieve the token identifier of the
* token for sale.
*/
if (!IsFeatureActivated(FEATURE_FREEDEX, block)) {
propertyId = OMNI_PROPERTY_MSC; // test for OMNI accept first
p_accept = DEx_getAccept(addressSeller, propertyId, addressBuyer);

if (!p_accept) {
propertyId = OMNI_PROPERTY_TMSC; // test for TOMNI accept second
p_accept = DEx_getAccept(addressSeller, propertyId, addressBuyer);
}
} else {
// Retrieve and get the token for sale for that seller

if (DEx_getTokenForSale(addressSeller, propertyId)) {
p_accept = DEx_getAccept(addressSeller, propertyId, addressBuyer);
}
}

if (!p_accept) {
Expand Down
2 changes: 2 additions & 0 deletions src/omnicore/dex.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ extern AcceptMap my_accepts;
int64_t calculateDesiredBTC(const int64_t amountOffered, const int64_t amountDesired, const int64_t amountAvailable);

bool DEx_offerExists(const std::string& addressSeller, uint32_t propertyId);
bool DEx_hasOffer(const std::string& addressSeller);
bool DEx_getTokenForSale(const std::string& addressSeller, uint32_t& retTokenId);
CMPOffer* DEx_getOffer(const std::string& addressSeller, uint32_t propertyId);
bool DEx_acceptExists(const std::string& addressSeller, uint32_t propertyId, const std::string& addressBuyer);
CMPAccept* DEx_getAccept(const std::string& addressSeller, uint32_t propertyId, const std::string& addressBuyer);
Expand Down
2 changes: 1 addition & 1 deletion src/omnicore/rpcpayload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ static UniValue omni_createpayload_dexsell(const JSONRPCRequest& request)
int64_t minAcceptFee = 0; // depending on action

if (action <= CMPTransaction::UPDATE) { // actions 3 permit zero values, skip check
amountForSale = ParseAmount(request.params[1], true); // TMSC/MSC is divisible
amountForSale = ParseAmount(request.params[1], isPropertyDivisible(propertyIdForSale));
amountDesired = ParseAmount(request.params[2], true); // BTC is divisible
paymentWindow = ParseDExPaymentWindow(request.params[3]);
minAcceptFee = ParseDExFee(request.params[4]);
Expand Down
4 changes: 2 additions & 2 deletions src/omnicore/rpcrequirements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ void RequireMatchingDExOffer(const std::string& address, uint32_t propertyId)
}
}

void RequireNoOtherDExOffer(const std::string& address, uint32_t propertyId)
void RequireNoOtherDExOffer(const std::string& address)
{
LOCK(cs_tally);
if (mastercore::DEx_offerExists(address, propertyId)) {
if (mastercore::DEx_hasOffer(address)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Another active sell offer from the given address already exists on the distributed exchange");
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/omnicore/rpcrequirements.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ void RequireActiveCrowdsale(uint32_t propertyId);
void RequireManagedProperty(uint32_t propertyId);
void RequireTokenIssuer(const std::string& address, uint32_t propertyId);
void RequireMatchingDExOffer(const std::string& address, uint32_t propertyId);
void RequireNoOtherDExOffer(const std::string& address, uint32_t propertyId);
void RequireNoOtherDExOffer(const std::string& address);
void RequireSaneReferenceAmount(int64_t amount);
void RequireSaneDExPaymentWindow(const std::string& address, uint32_t propertyId);
void RequireSaneDExFee(const std::string& address, uint32_t propertyId);
Expand Down
15 changes: 9 additions & 6 deletions src/omnicore/rpctx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
#include <omnicore/pending.h>
#include <omnicore/rpcrequirements.h>
#include <omnicore/rpcvalues.h>
#include <omnicore/rules.h>
#include <omnicore/sp.h>
#include <omnicore/tx.h>
#include <omnicore/utilsbitcoin.h>
#include <omnicore/wallettxbuilder.h>

#include <interfaces/wallet.h>
Expand Down Expand Up @@ -356,31 +358,32 @@ static UniValue omni_senddexsell(const JSONRPCRequest& request)

// perform conversions
if (action <= CMPTransaction::UPDATE) { // actions 3 permit zero values, skip check
amountForSale = ParseAmount(request.params[2], true); // TMSC/MSC is divisible
amountForSale = ParseAmount(request.params[2], isPropertyDivisible(propertyIdForSale));
amountDesired = ParseAmount(request.params[3], true); // BTC is divisible
paymentWindow = ParseDExPaymentWindow(request.params[4]);
minAcceptFee = ParseDExFee(request.params[5]);
}

// perform checks
if (!IsFeatureActivated(FEATURE_FREEDEX, GetHeight())) {
RequirePrimaryToken(propertyIdForSale);
}

switch (action) {
case CMPTransaction::NEW:
{
RequirePrimaryToken(propertyIdForSale);
RequireBalance(fromAddress, propertyIdForSale, amountForSale);
RequireNoOtherDExOffer(fromAddress, propertyIdForSale);
RequireNoOtherDExOffer(fromAddress);
break;
}
case CMPTransaction::UPDATE:
{
RequirePrimaryToken(propertyIdForSale);
RequireBalance(fromAddress, propertyIdForSale, amountForSale);
RequireMatchingDExOffer(fromAddress, propertyIdForSale);
break;
}
case CMPTransaction::CANCEL:
{
RequirePrimaryToken(propertyIdForSale);
RequireMatchingDExOffer(fromAddress, propertyIdForSale);
break;
}
Expand Down Expand Up @@ -441,7 +444,7 @@ static UniValue omni_senddexaccept(const JSONRPCRequest& request)
std::string fromAddress = ParseAddress(request.params[0]);
std::string toAddress = ParseAddress(request.params[1]);
uint32_t propertyId = ParsePropertyId(request.params[2]);
int64_t amount = ParseAmount(request.params[3], true); // MSC/TMSC is divisible
int64_t amount = ParseAmount(request.params[3], isPropertyDivisible(propertyId));
bool override = (request.params.size() > 4) ? request.params[4].get_bool(): false;

// perform checks
Expand Down
13 changes: 13 additions & 0 deletions src/omnicore/rules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ CMainConsensusParams::CMainConsensusParams()
TRADEALLPAIRS_FEATURE_BLOCK = 438500;
FEES_FEATURE_BLOCK = 999999;
FREEZENOTICE_FEATURE_BLOCK = 999999;
FREEDEX_FEATURE_BLOCK = 999999;
}

/**
Expand Down Expand Up @@ -294,6 +295,7 @@ CTestNetConsensusParams::CTestNetConsensusParams()
TRADEALLPAIRS_FEATURE_BLOCK = 0;
FEES_FEATURE_BLOCK = 0;
FREEZENOTICE_FEATURE_BLOCK = 0;
FREEDEX_FEATURE_BLOCK = 0;
}

/**
Expand Down Expand Up @@ -335,6 +337,7 @@ CRegTestConsensusParams::CRegTestConsensusParams()
TRADEALLPAIRS_FEATURE_BLOCK = 999999;
FEES_FEATURE_BLOCK = 999999;
FREEZENOTICE_FEATURE_BLOCK = 999999;
FREEDEX_FEATURE_BLOCK = 999999;
}

//! Consensus parameters for mainnet
Expand Down Expand Up @@ -500,6 +503,9 @@ bool ActivateFeature(uint16_t featureId, int activationBlock, uint32_t minClient
case FEATURE_FREEZENOTICE:
MutableConsensusParams().FREEZENOTICE_FEATURE_BLOCK = activationBlock;
break;
case FEATURE_FREEDEX:
MutableConsensusParams().FREEDEX_FEATURE_BLOCK = activationBlock;
break;
default:
supported = false;
break;
Expand Down Expand Up @@ -571,6 +577,9 @@ bool DeactivateFeature(uint16_t featureId, int transactionBlock)
case FEATURE_FREEZENOTICE:
MutableConsensusParams().FREEZENOTICE_FEATURE_BLOCK = 999999;
break;
case FEATURE_FREEDEX:
MutableConsensusParams().FREEDEX_FEATURE_BLOCK = 999999;
break;
default:
return false;
break;
Expand Down Expand Up @@ -602,6 +611,7 @@ std::string GetFeatureName(uint16_t featureId)
case FEATURE_FEES: return "Fee system (inc 0.05% fee from trades of non-Omni pairs)";
case FEATURE_STOV1: return "Cross-property Send To Owners";
case FEATURE_FREEZENOTICE: return "Activate the waiting period for enabling freezing";
case FEATURE_FREEDEX: return "Activate trading of any token on the distributed exchange";

default: return "Unknown feature";
}
Expand Down Expand Up @@ -649,6 +659,9 @@ bool IsFeatureActivated(uint16_t featureId, int transactionBlock)
case FEATURE_FREEZENOTICE:
activationBlock = params.FREEZENOTICE_FEATURE_BLOCK;
break;
case FEATURE_FREEDEX:
activationBlock = params.FREEDEX_FEATURE_BLOCK;
break;
default:
return false;
}
Expand Down
4 changes: 4 additions & 0 deletions src/omnicore/rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const uint16_t FEATURE_FEES = 9;
const uint16_t FEATURE_STOV1 = 10;
//! Feature identifier to activate the waiting period for enabling managed property address freezing
const uint16_t FEATURE_FREEZENOTICE = 14;
//! Feature identifier to activate trading of any token on the distributed exchange
const uint16_t FEATURE_FREEDEX = 15;

//! When (propertyTotalTokens / OMNI_FEE_THRESHOLD) is reached fee distribution will occur
const int64_t OMNI_FEE_THRESHOLD = 100000; // 0.001%
Expand Down Expand Up @@ -141,6 +143,8 @@ class CConsensusParams
int FEES_FEATURE_BLOCK;
//! Block to activate the waiting period for enabling managed property address freezing
int FREEZENOTICE_FEATURE_BLOCK;
//! Block to activate the waiting period to activate trading of any token on the distributed exchange
int FREEDEX_FEATURE_BLOCK;

/** Returns a mapping of transaction types, and the blocks at which they are enabled. */
virtual std::vector<TransactionRestriction> GetRestrictions() const;
Expand Down
9 changes: 6 additions & 3 deletions src/omnicore/tx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1275,9 +1275,12 @@ int CMPTransaction::logicMath_TradeOffer()
return (PKT_ERROR_TRADEOFFER -23);
}

if (OMNI_PROPERTY_TMSC != property && OMNI_PROPERTY_MSC != property) {
PrintToLog("%s(): rejected: property for sale %d must be OMN or TOMN\n", __func__, property);
return (PKT_ERROR_TRADEOFFER -47);
// Ensure only OMNI and TOMNI are allowed, when the DEx is not yet free
if (!IsFeatureActivated(FEATURE_FREEDEX, block)) {
if (OMNI_PROPERTY_TMSC != property && OMNI_PROPERTY_MSC != property) {
PrintToLog("%s(): rejected: property for sale %d must be OMN or TOMN\n", __func__, property);
return (PKT_ERROR_TRADEOFFER -47);
}
}

// ------------------------------------------
Expand Down

0 comments on commit 5f13f1c

Please sign in to comment.