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 23, 2022
1 parent 67fd693 commit da76f89
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 136 deletions.
173 changes: 76 additions & 97 deletions src/masternodes/govvariables/attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,33 +33,40 @@ static std::vector<std::string> KeyBreaker(const std::string& str){
return strVec;
}

static ResVal<int32_t> VerifyInt32(const std::string& str) {
int32_t int32;
if (!ParseInt32(str, &int32) || int32 < 0) {
return Res::Err("Identifier must be a positive integer");
}
return {int32, Res::Ok()};
}

static ResVal<CAmount> VerifyFloat(const std::string& str) {
ResVal<CAttributeValue> ATTRIBUTES::VerifyFloat(const std::string& str) {
CAmount amount = 0;
if (!ParseFixedPoint(str, 8, &amount) || amount < 0) {
return Res::Err("Amount must be a positive value");
}
return {amount, Res::Ok()};
}

static ResVal<CAmount> VerifyPct(const std::string& str) {
ResVal<CAttributeValue> ATTRIBUTES::VerifyPct(const std::string& str) {
auto resVal = VerifyFloat(str);
if (!resVal) {
return resVal;
}
if (*resVal.val > COIN) {
if (CAttributeValue{COIN} < *resVal.val) {
return Res::Err("Percentage exceeds 100%%");
}
return resVal;
}

ResVal<CAttributeValue> ATTRIBUTES::VerifyBool(const std::string& str) {
if (str != "true" && str != "false") {
return Res::Err(R"(Boolean value must be either "true" or "false")");
}
return {str == "true", Res::Ok()};
}

static ResVal<int32_t> VerifyInt32(const std::string& str) {
int32_t int32;
if (!ParseInt32(str, &int32) || int32 < 0) {
return Res::Err("Identifier must be a positive integer");
}
return {int32, Res::Ok()};
}

static Res ShowError(const std::string& key, const std::map<std::string, uint8_t>& keys) {
std::string error{"Unrecognised " + key + " argument provided, valid " + key + "s are:"};
for (const auto& pair : keys) {
Expand Down Expand Up @@ -132,68 +139,18 @@ Res ATTRIBUTES::ProcessVariable(const std::string& key, const std::string& value
return ::ShowError("key", ikey->second);
}

UniValue univalue;
auto typeKey = itype->second;

CAttributeValue attribValue;

if (type == AttributeTypes::Token) {
if (typeKey == TokenKeys::PaybackDFI) {
if (value != "true" && value != "false") {
return Res::Err("Payback DFI value must be either \"true\" or \"false\"");
}
attribValue = value == "true";
} else if (typeKey == TokenKeys::PaybackDFIFeePCT) {
auto res = VerifyPct(value);
if (!res) {
return std::move(res);
try {
if (auto parser = parseValue.at(type).at(typeKey)) {
auto attribValue = parser(value);
if (!attribValue) {
return std::move(attribValue);
}
attribValue = *res.val;
} else {
return Res::Err("Unrecognised key");
return applyVariable(CDataStructureV0{type, typeId, typeKey}, *attribValue.val);
}
} else if (type == AttributeTypes::Poolpairs) {
if (typeKey == PoolKeys::TokenAFeePCT
|| typeKey == PoolKeys::TokenBFeePCT) {
auto res = VerifyPct(value);
if (!res) {
return std::move(res);
}
attribValue = *res.val;
} else {
return Res::Err("Unrecognised key");
}
} else if (type == AttributeTypes::Param) {
if (typeId == ParamIDs::DFIP2201) {
if (typeKey == DFIP2201Keys::Active) {
if (value != "true" && value != "false") {
return Res::Err("DFIP2201 actve value must be either \"true\" or \"false\"");
}
attribValue = value == "true";
} else if (typeKey == DFIP2201Keys::Premium) {
auto res = VerifyPct(value);
if (!res) {
return std::move(res);
}
attribValue = *res.val;
} else if (typeKey == DFIP2201Keys::MinSwap) {
auto res = VerifyFloat(value);
if (!res) {
return std::move(res);
}
attribValue = *res.val;
} else {
return Res::Err("Unrecognised key");
}
}
} else {
return Res::Err("Unrecognised type");
} catch (const std::out_of_range&) {
}

if (applyVariable) {
return applyVariable(CDataStructureV0{type, typeId, typeKey}, attribValue);
}
return Res::Ok();
return Res::Err("No parse function {%d, %d}", type, typeKey);
}

Res ATTRIBUTES::Import(const UniValue & val) {
Expand Down Expand Up @@ -267,40 +224,48 @@ Res ATTRIBUTES::Validate(const CCustomCSView & view) const
return Res::Err("Unsupported version");
}
switch (attrV0->type) {
case AttributeTypes::Token: {
if (attrV0->key == TokenKeys::PaybackDFI
|| attrV0->key == TokenKeys::PaybackDFIFeePCT) {
uint32_t tokenId = attrV0->typeId;
if (!view.GetLoanTokenByID(DCT_ID{tokenId})) {
return Res::Err("No such loan token (%d)", tokenId);
}
} else {
return Res::Err("Unsupported key");
case AttributeTypes::Token:
switch (attrV0->key) {
case TokenKeys::PaybackDFI:
case TokenKeys::PaybackDFIFeePCT:
if (!view.GetLoanTokenByID({attrV0->typeId})) {
return Res::Err("No such loan token (%d)", attrV0->typeId);
}
break;
case TokenKeys::DexInFeePct:
case TokenKeys::DexOutFeePct:
if (view.GetLastHeight() < Params().GetConsensus().GreatWorldHeight) {
return Res::Err("Cannot be set before GreatWorld");
}
if (!view.GetToken(DCT_ID{attrV0->typeId})) {
return Res::Err("No such token (%d)", attrV0->typeId);
}
break;
default:
return Res::Err("Unsupported key");
}
}
break;

case AttributeTypes::Poolpairs: {
case AttributeTypes::Poolpairs:
if (!std::get_if<CAmount>(&attribute.second)) {
return Res::Err("Unsupported value");
}
if (attrV0->key == PoolKeys::TokenAFeePCT
|| attrV0->key == PoolKeys::TokenBFeePCT) {
uint32_t poolId = attrV0->typeId;
if (!view.GetPoolPair(DCT_ID{poolId})) {
return Res::Err("No such pool (%d)", poolId);
}
} else {
return Res::Err("Unsupported key");
switch (attrV0->key) {
case PoolKeys::TokenAFeePCT:
case PoolKeys::TokenBFeePCT:
if (!view.GetPoolPair({attrV0->typeId})) {
return Res::Err("No such pool (%d)", attrV0->typeId);
}
break;
default:
return Res::Err("Unsupported key");
}
}
break;

case AttributeTypes::Param: {
case AttributeTypes::Param:
if (attrV0->typeId != ParamIDs::DFIP2201) {
return Res::Err("Unrecognised param id");
}
}
break;

// Live is set internally
Expand All @@ -319,20 +284,34 @@ Res ATTRIBUTES::Apply(CCustomCSView & mnview, const uint32_t height)
{
for (const auto& attribute : attributes) {
auto attrV0 = std::get_if<CDataStructureV0>(&attribute.first);
if (attrV0 && attrV0->type == AttributeTypes::Poolpairs) {
uint32_t poolId = attrV0->typeId;
auto pool = mnview.GetPoolPair(DCT_ID{poolId});
if (!attrV0) {
continue;
}
if (attrV0->type == AttributeTypes::Poolpairs) {
auto poolId = DCT_ID{attrV0->typeId};
auto pool = mnview.GetPoolPair(poolId);
if (!pool) {
return Res::Err("No such pool (%d)", poolId);
return Res::Err("No such pool (%d)", poolId.v);
}
auto tokenId = attrV0->key == PoolKeys::TokenAFeePCT ?
pool->idTokenA : pool->idTokenB;

auto valuePct = std::get<CAmount>(attribute.second);
auto res = mnview.SetDexFeePct(DCT_ID{poolId}, tokenId, valuePct);
if (!res) {
if (auto res = mnview.SetDexFeePct(poolId, tokenId, valuePct); !res) {
return res;
}
} else if (attrV0->type == AttributeTypes::Token) {
if (attrV0->key == TokenKeys::DexInFeePct
|| attrV0->key == TokenKeys::DexOutFeePct) {
DCT_ID tokenA{attrV0->typeId}, tokenB{~0u};
if (attrV0->key == TokenKeys::DexOutFeePct) {
std::swap(tokenA, tokenB);
}
auto valuePct = std::get<CAmount>(attribute.second);
if (auto res = mnview.SetDexFeePct(tokenA, tokenB, valuePct); !res) {
return res;
}
}
}
}
return Res::Ok();
Expand Down
35 changes: 35 additions & 0 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 @@ -118,6 +120,10 @@ class ATTRIBUTES : public GovVariable, public AutoRegistrator<GovVariable, ATTRI
std::map<CAttributeType, CAttributeValue> attributes;

private:
static ResVal<CAttributeValue> VerifyFloat(const std::string& str);
static ResVal<CAttributeValue> VerifyBool(const std::string& str);
static ResVal<CAttributeValue> VerifyPct(const std::string& str);

// Defined allowed arguments
inline static const std::map<std::string, uint8_t> allowedVersions{
{"v0", VersionTypes::v0},
Expand All @@ -138,6 +144,8 @@ class ATTRIBUTES : public GovVariable, public AutoRegistrator<GovVariable, ATTRI
AttributeTypes::Token, {
{"payback_dfi", TokenKeys::PaybackDFI},
{"payback_dfi_fee_pct", TokenKeys::PaybackDFIFeePCT},
{"dex_in_fee_pct", TokenKeys::DexInFeePct},
{"dex_out_fee_pct", TokenKeys::DexOutFeePct},
}
},
{
Expand All @@ -155,6 +163,31 @@ class ATTRIBUTES : public GovVariable, public AutoRegistrator<GovVariable, ATTRI
},
};

inline static const std::map<uint8_t, std::map<uint8_t,
std::function<ResVal<CAttributeValue>(const std::string&)>>> parseValue{
{
AttributeTypes::Token, {
{TokenKeys::PaybackDFI, VerifyBool},
{TokenKeys::PaybackDFIFeePCT, VerifyPct},
{TokenKeys::DexInFeePct, VerifyPct},
{TokenKeys::DexOutFeePct, VerifyPct},
}
},
{
AttributeTypes::Poolpairs, {
{PoolKeys::TokenAFeePCT, VerifyPct},
{PoolKeys::TokenBFeePCT, VerifyPct},
}
},
{
AttributeTypes::Param, {
{DFIP2201Keys::Active, VerifyBool},
{DFIP2201Keys::Premium, VerifyPct},
{DFIP2201Keys::MinSwap, VerifyFloat},
}
},
};

// For formatting in export
inline static const std::map<uint8_t, std::string> displayVersions{
{VersionTypes::v0, "v0"},
Expand All @@ -177,6 +210,8 @@ class ATTRIBUTES : public GovVariable, public AutoRegistrator<GovVariable, ATTRI
AttributeTypes::Token, {
{TokenKeys::PaybackDFI, "payback_dfi"},
{TokenKeys::PaybackDFIFeePCT, "payback_dfi_fee_pct"},
{TokenKeys::DexInFeePct, "dex_in_fee_pct"},
{TokenKeys::DexOutFeePct, "dex_out_fee_pct"},
}
},
{
Expand Down
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
Loading

0 comments on commit da76f89

Please sign in to comment.