Skip to content

Commit

Permalink
Expand dex fee capability
Browse files Browse the repository at this point in the history
Signed-off-by: Anthony Fieroni <[email protected]>
  • Loading branch information
bvbfan committed Feb 24, 2022
1 parent 67fd693 commit 85da307
Show file tree
Hide file tree
Showing 8 changed files with 332 additions and 232 deletions.
324 changes: 219 additions & 105 deletions src/masternodes/govvariables/attributes.cpp

Large diffs are not rendered by default.

91 changes: 13 additions & 78 deletions src/masternodes/govvariables/attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ enum DFIP2201Keys : uint8_t {
enum TokenKeys : uint8_t {
PaybackDFI = 'a',
PaybackDFIFeePCT = 'b',
DexInFeePct = 'c',
DexOutFeePct = 'd',
};

enum PoolKeys : uint8_t {
Expand Down Expand Up @@ -119,88 +121,21 @@ class ATTRIBUTES : public GovVariable, public AutoRegistrator<GovVariable, ATTRI

private:
// Defined allowed arguments
inline static const std::map<std::string, uint8_t> allowedVersions{
{"v0", VersionTypes::v0},
};

inline static const std::map<std::string, uint8_t> allowedTypes{
{"params", AttributeTypes::Param},
{"poolpairs", AttributeTypes::Poolpairs},
{"token", AttributeTypes::Token},
};

inline static const std::map<std::string, uint8_t> allowedParamIDs{
{"dfip2201", ParamIDs::DFIP2201}
};

inline static const std::map<uint8_t, std::map<std::string, uint8_t>> allowedKeys{
{
AttributeTypes::Token, {
{"payback_dfi", TokenKeys::PaybackDFI},
{"payback_dfi_fee_pct", TokenKeys::PaybackDFIFeePCT},
}
},
{
AttributeTypes::Poolpairs, {
{"token_a_fee_pct", PoolKeys::TokenAFeePCT},
{"token_b_fee_pct", PoolKeys::TokenBFeePCT},
}
},
{
AttributeTypes::Param, {
{"active", DFIP2201Keys::Active},
{"minswap", DFIP2201Keys::MinSwap},
{"premium", DFIP2201Keys::Premium},
}
},
};
static const std::map<std::string, uint8_t>& allowedVersions();
static const std::map<std::string, uint8_t>& allowedTypes();
static const std::map<std::string, uint8_t>& allowedParamIDs();
static const std::map<uint8_t, std::map<std::string, uint8_t>>& allowedKeys();
static const std::map<uint8_t, std::map<uint8_t,
std::function<ResVal<CAttributeValue>(const std::string&)>>>& parseValue();

// For formatting in export
inline static const std::map<uint8_t, std::string> displayVersions{
{VersionTypes::v0, "v0"},
};

inline static const std::map<uint8_t, std::string> displayTypes{
{AttributeTypes::Live, "live"},
{AttributeTypes::Param, "params"},
{AttributeTypes::Poolpairs, "poolpairs"},
{AttributeTypes::Token, "token"},
};

inline static const std::map<uint8_t, std::string> displayParamsIDs{
{ParamIDs::DFIP2201, "dfip2201"},
{ParamIDs::Economy, "economy"},
};

inline static const std::map<uint8_t, std::map<uint8_t, std::string>> displayKeys{
{
AttributeTypes::Token, {
{TokenKeys::PaybackDFI, "payback_dfi"},
{TokenKeys::PaybackDFIFeePCT, "payback_dfi_fee_pct"},
}
},
{
AttributeTypes::Poolpairs, {
{PoolKeys::TokenAFeePCT, "token_a_fee_pct"},
{PoolKeys::TokenBFeePCT, "token_b_fee_pct"},
}
},
{
AttributeTypes::Param, {
{DFIP2201Keys::Active, "active"},
{DFIP2201Keys::Premium, "premium"},
{DFIP2201Keys::MinSwap, "minswap"},
}
},
{
AttributeTypes::Live, {
{EconomyKeys::PaybackDFITokens, "dfi_payback_tokens"},
}
},
};
static const std::map<uint8_t, std::string>& displayVersions();
static const std::map<uint8_t, std::string>& displayTypes();
static const std::map<uint8_t, std::string>& displayParamsIDs();
static const std::map<uint8_t, std::map<uint8_t, std::string>>& displayKeys();

Res ProcessVariable(const std::string& key, const std::string& value,
std::function<Res(const CAttributeType&, const CAttributeValue&)> applyVariable = {}) const;
std::function<Res(const CAttributeType&, const CAttributeValue&)> applyVariable) const;
};

#endif // DEFI_MASTERNODES_GOVVARIABLES_ATTRIBUTES_H
11 changes: 5 additions & 6 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs, boo
}
}

auto dexfeeInPct = view.GetDexFeePct(currentID, swapAmount.nTokenId);
auto dexfeeInPct = view.GetDexFeeInPct(currentID, swapAmount.nTokenId);

// Perform swap
poolResult = pool->Swap(swapAmount, dexfeeInPct, poolPrice, [&] (const CTokenAmount& dexfeeInAmount, const CTokenAmount& tokenAmount) {
Expand All @@ -787,11 +787,10 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs, boo

CTokenAmount dexfeeOutAmount{tokenAmount.nTokenId, 0};

if (height >= Params().GetConsensus().FortCanningHillHeight) {
if (auto dexfeeOutPct = view.GetDexFeePct(currentID, tokenAmount.nTokenId)) {
dexfeeOutAmount.nValue = MultiplyAmounts(tokenAmount.nValue, dexfeeOutPct);
swapAmountResult.nValue -= dexfeeOutAmount.nValue;
}
auto dexfeeOutPct = view.GetDexFeeOutPct(currentID, tokenAmount.nTokenId);
if (dexfeeOutPct > 0) {
dexfeeOutAmount.nValue = MultiplyAmounts(tokenAmount.nValue, dexfeeOutPct);
swapAmountResult.nValue -= dexfeeOutAmount.nValue;
}

// If we're just testing, don't do any balance transfers.
Expand Down
27 changes: 12 additions & 15 deletions src/masternodes/poolpairs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,12 +407,6 @@ Res CPoolPair::Swap(CTokenAmount in, CAmount dexfeeInPct, PoolPrice const & maxP
if (in.nTokenId != idTokenA && in.nTokenId != idTokenB)
return Res::Err("Error, input token ID (" + in.nTokenId.ToString() + ") doesn't match pool tokens (" + idTokenA.ToString() + "," + idTokenB.ToString() + ")");

// TODO: The whole block of the fork condition can be removed safely after FCH.
if (height < Params().GetConsensus().FortCanningHillHeight) {
if (in.nValue <= 0)
return Res::Err("Input amount should be positive!");
}

if (!status)
return Res::Err("Pool trading is turned off!");

Expand Down Expand Up @@ -444,7 +438,7 @@ Res CPoolPair::Swap(CTokenAmount in, CAmount dexfeeInPct, PoolPrice const & maxP
}

CTokenAmount dexfeeInAmount{in.nTokenId, 0};
if (dexfeeInPct > 0 && height >= Params().GetConsensus().FortCanningHillHeight) {
if (dexfeeInPct > 0) {
if (dexfeeInPct > COIN) {
return Res::Err("Dex fee input percentage over 100%%");
}
Expand Down Expand Up @@ -726,20 +720,23 @@ void CPoolPairView::ForEachPoolShare(std::function<bool (DCT_ID const &, CScript
}

Res CPoolPairView::SetDexFeePct(DCT_ID poolId, DCT_ID tokenId, CAmount feePct) {
if (!HasPoolPair(poolId)) {
return Res::Err("No such pool pair");
}
if (feePct < 0 || feePct > COIN) {
return Res::Err("Token dex fee should be in percentage");
}
WriteBy<ByTokenDexFeePct>(std::make_pair(poolId, tokenId), uint32_t(feePct));
return Res::Ok();
}

CAmount CPoolPairView::GetDexFeePct(DCT_ID poolId, DCT_ID tokenId) const {
CAmount CPoolPairView::GetDexFeeInPct(DCT_ID poolId, DCT_ID tokenId) const {
uint32_t feePct;
if (ReadBy<ByTokenDexFeePct>(std::make_pair(poolId, tokenId), feePct)) {
return feePct;
}
return 0;
return ReadBy<ByTokenDexFeePct>(std::make_pair(poolId, tokenId), feePct)
|| ReadBy<ByTokenDexFeePct>(std::make_pair(tokenId, DCT_ID{~0u}), feePct)
? feePct : 0;
}

CAmount CPoolPairView::GetDexFeeOutPct(DCT_ID poolId, DCT_ID tokenId) const {
uint32_t feePct;
return ReadBy<ByTokenDexFeePct>(std::make_pair(poolId, tokenId), feePct)
|| ReadBy<ByTokenDexFeePct>(std::make_pair(DCT_ID{~0u}, tokenId), feePct)
? feePct : 0;
}
3 changes: 2 additions & 1 deletion src/masternodes/poolpairs.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ class CPoolPairView : public virtual CStorageView
bool HasPoolPair(DCT_ID const & poolId) const;

Res SetDexFeePct(DCT_ID poolId, DCT_ID tokenId, CAmount feePct);
CAmount GetDexFeePct(DCT_ID poolId, DCT_ID tokenId) const;
CAmount GetDexFeeInPct(DCT_ID poolId, DCT_ID tokenId) const;
CAmount GetDexFeeOutPct(DCT_ID poolId, DCT_ID tokenId) const;

std::pair<CAmount, CAmount> UpdatePoolRewards(std::function<CTokenAmount(CScript const &, DCT_ID)> onGetBalance, std::function<Res(CScript const &, CScript const &, CTokenAmount)> onTransfer, int nHeight = 0);

Expand Down
23 changes: 15 additions & 8 deletions src/masternodes/rpc_poolpair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,19 @@ UniValue poolToJSON(CCustomCSView& view, DCT_ID const& id, CPoolPair const& pool
poolObj.pushKV("idTokenB", pool.idTokenB.ToString());

if (verbose) {
if (const auto dexFee = view.GetDexFeePct(id, pool.idTokenA)) {
if (const auto dexFee = view.GetDexFeeInPct(id, pool.idTokenA)) {
poolObj.pushKV("dexFeePctTokenA", ValueFromAmount(dexFee));
poolObj.pushKV("dexFeeInPctTokenA", ValueFromAmount(dexFee));
}
if (const auto dexFee = view.GetDexFeePct(id, pool.idTokenB)) {
if (const auto dexFee = view.GetDexFeeOutPct(id, pool.idTokenB)) {
poolObj.pushKV("dexFeePctTokenB", ValueFromAmount(dexFee));
poolObj.pushKV("dexFeeOutPctTokenB", ValueFromAmount(dexFee));
}
if (const auto dexFee = view.GetDexFeeInPct(id, pool.idTokenB)) {
poolObj.pushKV("dexFeeInPctTokenB", ValueFromAmount(dexFee));
}
if (const auto dexFee = view.GetDexFeeOutPct(id, pool.idTokenA)) {
poolObj.pushKV("dexFeeOutPctTokenA", ValueFromAmount(dexFee));
}
poolObj.pushKV("reserveA", ValueFromAmount(pool.reserveA));
poolObj.pushKV("reserveB", ValueFromAmount(pool.reserveB));
Expand Down Expand Up @@ -1065,7 +1073,7 @@ UniValue testpoolswap(const JSONRPCRequest& request) {
throw JSONRPCError(RPC_INVALID_REQUEST, "Input amount should be positive");

CPoolPair pp = poolPair->second;
auto dexfeeInPct = mnview_dummy.GetDexFeePct(poolPair->first, poolSwapMsg.idTokenFrom);
auto dexfeeInPct = mnview_dummy.GetDexFeeInPct(poolPair->first, poolSwapMsg.idTokenFrom);

res = pp.Swap({poolSwapMsg.idTokenFrom, poolSwapMsg.amountFrom}, dexfeeInPct, poolSwapMsg.maxPrice, [&] (const CTokenAmount &, const CTokenAmount &tokenAmount) {
auto resPP = mnview_dummy.SetPoolPair(poolPair->first, targetHeight, pp);
Expand All @@ -1074,11 +1082,10 @@ UniValue testpoolswap(const JSONRPCRequest& request) {
}

auto resultAmount = tokenAmount;
if (targetHeight >= Params().GetConsensus().FortCanningHillHeight) {
if (auto dexfeeOutPct = mnview_dummy.GetDexFeePct(poolPair->first, tokenAmount.nTokenId)) {
auto dexfeeOutAmount = MultiplyAmounts(tokenAmount.nValue, dexfeeOutPct);
resultAmount.nValue -= dexfeeOutAmount;
}
auto dexfeeOutPct = mnview_dummy.GetDexFeeOutPct(poolPair->first, tokenAmount.nTokenId);
if (dexfeeOutPct > 0) {
auto dexfeeOutAmount = MultiplyAmounts(tokenAmount.nValue, dexfeeOutPct);
resultAmount.nValue -= dexfeeOutAmount;
}

return Res::Ok(resultAmount.ToString());
Expand Down
62 changes: 49 additions & 13 deletions test/functional/feature_poolswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
)

from decimal import Decimal
from math import trunc

class PoolPairTest (DefiTestFramework):
def set_test_params(self):
Expand All @@ -28,10 +29,10 @@ def set_test_params(self):
# node2: Non Foundation
self.setup_clean_chain = True
self.extra_args = [
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-acindex=1'],
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-acindex=1'],
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170'],
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170']]
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-greatworldheight=177', '-acindex=1'],
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-greatworldheight=177', '-acindex=1'],
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-greatworldheight=177'],
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-greatworldheight=177']]


def run_test(self):
Expand Down Expand Up @@ -338,10 +339,11 @@ def run_test(self):

symbolBTC = "BTC#" + self.get_id_token("BTC")
symbolLTC = "LTC#" + self.get_id_token("LTC")
idBitcoin = list(self.nodes[0].gettoken(symbolBTC).keys())[0]
idBTC = list(self.nodes[0].gettoken(symbolBTC).keys())[0]
idLTC = list(self.nodes[0].gettoken(symbolLTC).keys())[0]

self.nodes[0].minttokens("1@" + symbolBTC)
self.nodes[0].minttokens("101@" + symbolLTC)
self.nodes[0].minttokens("111@" + symbolLTC)
self.nodes[0].generate(1)

self.nodes[0].createpoolpair({
Expand Down Expand Up @@ -372,7 +374,7 @@ def run_test(self):
})
self.nodes[0].generate(1)

assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[idBitcoin], Decimal('0.00000001'))
assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[idBTC], Decimal('0.00000001'))

# Reset swap
self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount()))
Expand All @@ -388,7 +390,7 @@ def run_test(self):
})
self.nodes[0].generate(1)

assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[idBitcoin], Decimal('0.00000002'))
assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[idBTC], Decimal('0.00000002'))

# Reset swap and move to Fort Canning Park Height and try swap again
self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount()))
Expand All @@ -405,7 +407,7 @@ def run_test(self):
})
self.nodes[0].generate(1)

assert(idBitcoin not in self.nodes[0].getaccount(new_dest, {}, True))
assert(idBTC not in self.nodes[0].getaccount(new_dest, {}, True))

# Reset swap
self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount()))
Expand All @@ -421,7 +423,7 @@ def run_test(self):
})
self.nodes[0].generate(1)

assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[idBitcoin], Decimal('0.00000001'))
assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[idBTC], Decimal('0.00000001'))

self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(idGS): '0.05', 'v0/poolpairs/%s/token_b_fee_pct'%(idGS): '0.08'}})
self.nodes[0].generate(1)
Expand Down Expand Up @@ -487,11 +489,45 @@ def run_test(self):
self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(idBL): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(idBL): '0.01'}})
self.nodes[0].generate(1)

print(self.nodes[0].getblockcount())
print(self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'])

assert_equal(self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'], {'v0/poolpairs/%s/token_a_fee_pct'%(idGS): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(idGS): '0.01', 'v0/poolpairs/%s/token_a_fee_pct'%(idBL): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(idBL): '0.01'})

self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount()))
self.nodes[0].clearmempool()
self.nodes[0].generate(1)

self.nodes[0].setgov({"ATTRIBUTES":{'v0/token/%s/dex_in_fee_pct'%(idLTC): '0.02', 'v0/token/%s/dex_out_fee_pct'%(idBTC): '0.05'}})
self.nodes[0].generate(1)

result = self.nodes[0].getpoolpair(idBL)
assert_equal(result[idBL]['dexFeeInPctTokenB'], Decimal('0.02'))
assert_equal(result[idBL]['dexFeeOutPctTokenA'], Decimal('0.05'))

destBTC = self.nodes[0].getnewaddress("", "legacy")
swapltc = 10
self.nodes[0].poolswap({
"from": accountGN0,
"tokenFrom": symbolLTC,
"amountFrom": swapltc,
"to": destBTC,
"tokenTo": symbolBTC
})
commission = round((swapltc * 0.01), 8)
amountB = Decimal(swapltc - commission)
dexinfee = amountB * Decimal(0.02)
amountB = amountB - dexinfee
pool = self.nodes[0].getpoolpair("BTC-LTC")[idBL]
reserveA = pool['reserveA']
reserveB = pool['reserveB']

self.nodes[0].generate(1)

pool = self.nodes[0].getpoolpair("BTC-LTC")[idBL]
assert_equal(pool['reserveB'] - reserveB, round(amountB, 8))
swapped = self.nodes[0].getaccount(destBTC, {}, True)[idBTC]
amountA = reserveA - pool['reserveA']
dexoutfee = round(trunc(amountA * Decimal(0.05) * coin) / coin, 8)
assert_equal(round(amountA - Decimal(dexoutfee), 8), round(swapped, 8))

# REVERTING:
#========================
print ("Reverting...")
Expand Down
Loading

0 comments on commit 85da307

Please sign in to comment.