Skip to content

Commit

Permalink
Merge branch 'master' into fix/withdraw_collateral
Browse files Browse the repository at this point in the history
  • Loading branch information
Jouzo authored Dec 28, 2021
2 parents 301a8ca + 3e56d57 commit 6f388d6
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 73 deletions.
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 2)
define(_CLIENT_VERSION_MINOR, 3)
define(_CLIENT_VERSION_REVISION, 1)
define(_CLIENT_VERSION_REVISION, 2)
define(_CLIENT_VERSION_BUILD, 0)
define(_CLIENT_VERSION_RC, 0)
define(_CLIENT_VERSION_IS_RELEASE, true)
Expand Down
100 changes: 60 additions & 40 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3375,7 +3375,40 @@ bool IsMempooledCustomTxCreate(const CTxMemPool & pool, const uint256 & txid)
return false;
}

std::vector<DCT_ID> CPoolSwap::CalculateSwaps(CCustomCSView& view) {
std::vector<DCT_ID> CPoolSwap::CalculateSwaps(CCustomCSView& view, bool testOnly) {

std::vector<std::vector<DCT_ID>> poolPaths = CalculatePoolPaths(view);

// Record best pair
std::pair<std::vector<DCT_ID>, CAmount> bestPair{{}, 0};

// Loop through all common pairs
for (const auto& path : poolPaths) {

// Test on copy of view
CCustomCSView dummy(view);

// Execute pool path
auto res = ExecuteSwap(dummy, path, testOnly);

// Add error for RPC user feedback
if (!res) {
const auto token = dummy.GetToken(currentID);
if (token) {
errors.emplace_back(token->symbol, res.msg);
}
}

// Record amount if more than previous or default value
if (res && result > bestPair.second) {
bestPair = {path, result};
}
}

return bestPair.first;
}

std::vector<std::vector<DCT_ID>> CPoolSwap::CalculatePoolPaths(CCustomCSView& view) {

// For tokens to be traded get all pairs and pool IDs
std::multimap<uint32_t, DCT_ID> fromPoolsID, toPoolsID;
Expand Down Expand Up @@ -3403,8 +3436,8 @@ std::vector<DCT_ID> CPoolSwap::CalculateSwaps(CCustomCSView& view) {
set_intersection(fromPoolsID.begin(), fromPoolsID.end(), toPoolsID.begin(), toPoolsID.end(),
std::inserter(commonPairs, commonPairs.begin()),
[](std::pair<uint32_t, DCT_ID> a, std::pair<uint32_t, DCT_ID> b) {
return a.first < b.first;
});
return a.first < b.first;
});

// Loop through all common pairs and record direct pool to pool swaps
std::vector<std::vector<DCT_ID>> poolPaths;
Expand Down Expand Up @@ -3435,44 +3468,22 @@ std::vector<DCT_ID> CPoolSwap::CalculateSwaps(CCustomCSView& view) {

// If a pool pairs matches from pair and to pair add it to the pool paths
if ((fromIt->first == pool.idTokenA.v && toIt->first == pool.idTokenB.v) ||
(fromIt->first == pool.idTokenB.v && toIt->first == pool.idTokenA.v)) {
(fromIt->first == pool.idTokenB.v && toIt->first == pool.idTokenA.v)) {
poolPaths.push_back({fromIt->second, id, toIt->second});
}
}
}
return true;
}, {0});

// Record best pair
std::pair<std::vector<DCT_ID>, CAmount> bestPair{{}, 0};

// Loop through all common pairs
for (const auto& path : poolPaths) {

// Test on copy of view
CCustomCSView dummy(view);

// Execute pool path
auto res = ExecuteSwap(dummy, path);

// Add error for RPC user feedback
if (!res) {
const auto token = dummy.GetToken(currentID);
if (token) {
errors.emplace_back(token->symbol, res.msg);
}
}

// Record amount if more than previous or default value
if (res && result > bestPair.second) {
bestPair = {path, result};
}
}

return bestPair.first;
// return pool paths
return poolPaths;
}

Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs) {
// Note: `testOnly` doesn't update views, and as such can result in a previous price calculations
// for a pool, if used multiple times (or duplicated pool IDs) with the same view.
// testOnly is only meant for one-off tests per well defined view.
Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs, bool testOnly) {

CTokenAmount swapAmountResult{{},0};
Res poolResult = Res::Ok();
Expand All @@ -3498,10 +3509,12 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs) {
poolPrice = obj.maxPrice;
}

CCustomCSView mnview(view);
mnview.CalculateOwnerRewards(obj.from, height);
mnview.CalculateOwnerRewards(obj.to, height);
mnview.Flush();
if (!testOnly) {
CCustomCSView mnview(view);
mnview.CalculateOwnerRewards(obj.from, height);
mnview.CalculateOwnerRewards(obj.to, height);
mnview.Flush();
}

for (size_t i{0}; i < poolIDs.size(); ++i) {

Expand Down Expand Up @@ -3534,14 +3547,21 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs) {

// Perform swap
poolResult = pool->Swap(swapAmount, poolPrice, [&] (const CTokenAmount &tokenAmount) {
// Save swap amount for next loop
swapAmountResult = tokenAmount;

// If we're just testing, don't do any balance transfers.
// Just go over pools and return result. The only way this can
// cause inaccurate result is if we go over the same path twice,
// which shouldn't happen in the first place.
if (testOnly)
return Res::Ok();

auto res = view.SetPoolPair(currentID, height, *pool);
if (!res) {
return res;
}

// Save swap amount for next loop
swapAmountResult = tokenAmount;

CCustomCSView intermediateView(view);
// hide interemidiate swaps
auto& subView = i == 0 ? view : intermediateView;
Expand All @@ -3558,7 +3578,7 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs) {
}
intermediateView.Flush();

return res;
return res;
}, static_cast<int>(height));

if (!poolResult) {
Expand Down
6 changes: 4 additions & 2 deletions src/masternodes/mn_checks.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,10 @@ class CPoolSwap {
CPoolSwap(const CPoolSwapMessage& obj, uint32_t height)
: obj(obj), height(height) {}

std::vector<DCT_ID> CalculateSwaps(CCustomCSView& view);
Res ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs);
std::vector<DCT_ID> CalculateSwaps(CCustomCSView& view, bool testOnly = false);
Res ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs, bool testOnly = false);
std::vector<std::vector<DCT_ID>> CalculatePoolPaths(CCustomCSView& view);
CTokenAmount GetResult() { return CTokenAmount{obj.idTokenTo, result}; };
};

#endif // DEFI_MASTERNODES_MN_CHECKS_H
124 changes: 98 additions & 26 deletions src/masternodes/rpc_poolpair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,18 @@ UniValue poolShareToJSON(DCT_ID const & poolId, CScript const & provider, CAmoun
return ret;
}


UniValue poolPathsToJSON(std::vector<std::vector<DCT_ID>> & poolPaths) {
UniValue paths(UniValue::VARR);
for (auto poolIds : poolPaths) {
UniValue pathObj(UniValue::VARR);
for (auto poolId : poolIds){
pathObj.push_back(poolId.ToString());
}
paths.push_back(pathObj);
}
return paths;
}
void CheckAndFillPoolSwapMessage(const JSONRPCRequest& request, CPoolSwapMessage &poolSwapMsg) {
std::string tokenFrom, tokenTo;
UniValue metadataObj = request.params[0].get_obj();
Expand Down Expand Up @@ -974,6 +986,17 @@ UniValue testpoolswap(const JSONRPCRequest& request) {
"Maximum acceptable price"},
},
},
{
"path", RPCArg::Type::STR, RPCArg::Optional::OMITTED,
"One of auto/direct (default = direct)\n"
"auto - automatically use composite swap or direct swap as needed.\n"
"direct - uses direct path only or fails.\n"
"Note: The default will be switched to auto in the upcoming versions."
},
{
"verbose", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED,
"Returns estimated composite path when true (default = false)"
},
},
RPCResult{
"\"amount@tokenId\" (string) The string with amount result of poolswap in format AMOUNT@TOKENID.\n"
Expand All @@ -996,7 +1019,21 @@ UniValue testpoolswap(const JSONRPCRequest& request) {
},
}.Check(request);

RPCTypeCheck(request.params, {UniValue::VOBJ}, true);
RPCTypeCheck(request.params, {UniValue::VOBJ, UniValue::VSTR, UniValue::VBOOL}, true);

std::string path = "direct";
if (request.params.size() > 1) {
path = request.params[1].get_str();
if (!(path == "direct" || path == "auto"))
throw JSONRPCError(RPC_INVALID_REQUEST, std::string{"Invalid path"});
}

bool verbose = false;
if (request.params.size() > 2) {
verbose = request.params[2].get_bool();
}

UniValue pools{UniValue::VARR};

CPoolSwapMessage poolSwapMsg{};
CheckAndFillPoolSwapMessage(request, poolSwapMsg);
Expand All @@ -1008,22 +1045,57 @@ UniValue testpoolswap(const JSONRPCRequest& request) {
CCustomCSView mnview_dummy(*pcustomcsview); // create dummy cache for test state writing

int targetHeight = ::ChainActive().Height() + 1;
auto poolPair = mnview_dummy.GetPoolPair(poolSwapMsg.idTokenFrom, poolSwapMsg.idTokenTo);

CPoolPair pp = poolPair->second;
res = pp.Swap({poolSwapMsg.idTokenFrom, poolSwapMsg.amountFrom}, poolSwapMsg.maxPrice, [&] (const CTokenAmount &tokenAmount) {
auto resPP = mnview_dummy.SetPoolPair(poolPair->first, targetHeight, pp);
if (!resPP) {
return resPP;
}
// If no direct swap found search for composite swap
if (path == "direct") {
auto poolPair = mnview_dummy.GetPoolPair(poolSwapMsg.idTokenFrom, poolSwapMsg.idTokenTo);
if (!poolPair)
throw JSONRPCError(RPC_INVALID_REQUEST, std::string{"Direct pool pair not found. Use 'auto' mode to use composite swap."});

CPoolPair pp = poolPair->second;
res = pp.Swap({poolSwapMsg.idTokenFrom, poolSwapMsg.amountFrom}, poolSwapMsg.maxPrice, [&] (const CTokenAmount &tokenAmount) {
auto resPP = mnview_dummy.SetPoolPair(poolPair->first, targetHeight, pp);
if (!resPP) {
return resPP;
}

return Res::Ok(tokenAmount.ToString());
}, targetHeight >= Params().GetConsensus().BayfrontGardensHeight);

return Res::Ok(tokenAmount.ToString());
}, targetHeight >= Params().GetConsensus().BayfrontGardensHeight);
if (!res)
throw JSONRPCError(RPC_VERIFY_ERROR, res.msg);

if (!res)
throw JSONRPCError(RPC_VERIFY_ERROR, res.msg);
pools.push_back(poolPair->first.ToString());
} else {
auto compositeSwap = CPoolSwap(poolSwapMsg, targetHeight);
std::vector<DCT_ID> poolIds = compositeSwap.CalculateSwaps(mnview_dummy, true);
res = compositeSwap.ExecuteSwap(mnview_dummy, poolIds, true);
if (!res) {
std::string errorMsg{"Cannot find usable pool pair."};
if (!compositeSwap.errors.empty()) {
errorMsg += " Details: (";
for (size_t i{0}; i < compositeSwap.errors.size(); ++i) {
errorMsg += '"' + compositeSwap.errors[i].first + "\":\"" + compositeSwap.errors[i].second + '"' + (i + 1 < compositeSwap.errors.size() ? "," : "");
}
errorMsg += ')';
}
throw JSONRPCError(RPC_INVALID_REQUEST, errorMsg);
}
for (const auto& id : poolIds) {
pools.push_back(id.ToString());
}
res.msg = compositeSwap.GetResult().ToString();
}
}
if (verbose) {
UniValue swapObj{UniValue::VOBJ};
swapObj.pushKV("path", path);
swapObj.pushKV("pools", pools);
swapObj.pushKV("amount", res.msg);
return swapObj;
}
return UniValue(res.msg);

return res.msg;
}

UniValue listpoolshares(const JSONRPCRequest& request) {
Expand Down Expand Up @@ -1123,19 +1195,19 @@ UniValue listpoolshares(const JSONRPCRequest& request) {
}

static const CRPCCommand commands[] =
{
// category name actor (function) params
// ------------- ----------------------- --------------------- ----------
{"poolpair", "listpoolpairs", &listpoolpairs, {"pagination", "verbose"}},
{"poolpair", "getpoolpair", &getpoolpair, {"key", "verbose" }},
{"poolpair", "addpoolliquidity", &addpoolliquidity, {"from", "shareAddress", "inputs"}},
{"poolpair", "removepoolliquidity", &removepoolliquidity, {"from", "amount", "inputs"}},
{"poolpair", "createpoolpair", &createpoolpair, {"metadata", "inputs"}},
{"poolpair", "updatepoolpair", &updatepoolpair, {"metadata", "inputs"}},
{"poolpair", "poolswap", &poolswap, {"metadata", "inputs"}},
{"poolpair", "compositeswap", &compositeswap, {"metadata", "inputs"}},
{"poolpair", "listpoolshares", &listpoolshares, {"pagination", "verbose", "is_mine_only"}},
{"poolpair", "testpoolswap", &testpoolswap, {"metadata"}},
{
// category name actor (function) params
// ------------- ----------------------- --------------------- ----------
{"poolpair", "listpoolpairs", &listpoolpairs, {"pagination", "verbose"}},
{"poolpair", "getpoolpair", &getpoolpair, {"key", "verbose" }},
{"poolpair", "addpoolliquidity", &addpoolliquidity, {"from", "shareAddress", "inputs"}},
{"poolpair", "removepoolliquidity", &removepoolliquidity, {"from", "amount", "inputs"}},
{"poolpair", "createpoolpair", &createpoolpair, {"metadata", "inputs"}},
{"poolpair", "updatepoolpair", &updatepoolpair, {"metadata", "inputs"}},
{"poolpair", "poolswap", &poolswap, {"metadata", "inputs"}},
{"poolpair", "compositeswap", &compositeswap, {"metadata", "inputs"}},
{"poolpair", "listpoolshares", &listpoolshares, {"pagination", "verbose", "is_mine_only"}},
{"poolpair", "testpoolswap", &testpoolswap, {"metadata", "path", "verbose"}},
};

void RegisterPoolpairRPCCommands(CRPCTable& tableRPC) {
Expand Down
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "compositeswap", 0, "metadata" },
{ "compositeswap", 1, "inputs" },
{ "testpoolswap", 0, "metadata"},
{ "testpoolswap", 2, "verbose"},
{ "listpoolshares", 0, "pagination" },
{ "listpoolshares", 1, "verbose" },
{ "listpoolshares", 2, "is_mine_only" },
Expand Down
18 changes: 15 additions & 3 deletions test/functional/feature_poolswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,24 @@ def run_test(self):
print("goldCheckPS:", goldCheckPS)
print("testPoolSwapRes:", testPoolSwapRes)

testPoolSwapRes = str(testPoolSwapRes).split("@", 2)
testPoolSwapSplit = str(testPoolSwapRes).split("@", 2)

psTestAmount = testPoolSwapRes[0]
psTestTokenId = testPoolSwapRes[1]
psTestAmount = testPoolSwapSplit[0]
psTestTokenId = testPoolSwapSplit[1]
assert_equal(psTestTokenId, idGold)

testPoolSwapVerbose = self.nodes[0].testpoolswap({
"from": accountGN0,
"tokenFrom": symbolSILVER,
"amountFrom": 10,
"to": accountSN1,
"tokenTo": symbolGOLD,
}, "direct", True)

assert_equal(testPoolSwapVerbose["path"], "direct")
assert_equal(testPoolSwapVerbose["pools"][0], idGS)
assert_equal(testPoolSwapVerbose["amount"], testPoolSwapRes)

self.nodes[0].poolswap({
"from": accountGN0,
"tokenFrom": symbolSILVER,
Expand Down
Loading

0 comments on commit 6f388d6

Please sign in to comment.