Skip to content

Commit

Permalink
Live dex statistics
Browse files Browse the repository at this point in the history
Signed-off-by: Anthony Fieroni <[email protected]>
  • Loading branch information
bvbfan committed Apr 7, 2022
1 parent 57c099c commit 4d06b66
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 21 deletions.
19 changes: 16 additions & 3 deletions src/masternodes/govvariables/attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ const std::map<uint8_t, std::map<uint8_t, std::string>>& ATTRIBUTES::displayKeys
{EconomyKeys::DFIP2203Current, "dfip2203_current"},
{EconomyKeys::DFIP2203Burned, "dfip2203_burned"},
{EconomyKeys::DFIP2203Minted, "dfip2203_minted"},
{EconomyKeys::DexTokens, "dex"},
}
},
};
Expand Down Expand Up @@ -425,7 +426,7 @@ Res ATTRIBUTES::RefundFuturesContracts(CCustomCSView &mnview, const uint32_t hei
}
}

attributes[liveKey] = balances;
SetValue(liveKey, std::move(balances));

return Res::Ok();
}
Expand Down Expand Up @@ -453,11 +454,11 @@ Res ATTRIBUTES::Import(const UniValue & val) {
} else {
newAttr.key = TokenKeys::PaybackDFIFeePCT;
}
attributes[newAttr] = value;
SetValue(newAttr, value);
return Res::Ok();
}
}
attributes[attribute] = value;
SetValue(attribute, value);
return Res::Ok();
}
);
Expand Down Expand Up @@ -506,6 +507,18 @@ UniValue ATTRIBUTES::Export() const {
result.pushKV("paybackfees", AmountsToJSON(paybacks->tokensFee.balances));
result.pushKV("paybacktokens", AmountsToJSON(paybacks->tokensPayback.balances));
ret.pushKV(key, result);
} else if (auto balances = boost::get<const CDexBalances>(&attribute.second)) {
for (const auto& pool : *balances) {
auto& dexTokenA = pool.second.totalTokenA;
auto& dexTokenB = pool.second.totalTokenB;
auto poolkey = KeyBuilder(key, pool.first.v);
ret.pushKV(KeyBuilder(poolkey, "total_commission_a"), ValueFromUint(dexTokenA.commissions));
ret.pushKV(KeyBuilder(poolkey, "total_commission_b"), ValueFromUint(dexTokenB.commissions));
ret.pushKV(KeyBuilder(poolkey, "fee_burn_a"), ValueFromUint(dexTokenA.feeburn));
ret.pushKV(KeyBuilder(poolkey, "fee_burn_b"), ValueFromUint(dexTokenB.feeburn));
ret.pushKV(KeyBuilder(poolkey, "total_swap_a"), ValueFromUint(dexTokenA.swaps));
ret.pushKV(KeyBuilder(poolkey, "total_swap_b"), ValueFromUint(dexTokenB.swaps));
}
}
} catch (const std::out_of_range&) {
// Should not get here, that's mean maps are mismatched
Expand Down
53 changes: 50 additions & 3 deletions src/masternodes/govvariables/attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum EconomyKeys : uint8_t {
DFIP2203Current = 'c',
DFIP2203Burned = 'd',
DFIP2203Minted = 'e',
DexTokens = 'f',
};

enum DFIPKeys : uint8_t {
Expand Down Expand Up @@ -113,8 +114,38 @@ struct CTokenPayback {

ResVal<CScript> GetFutureSwapContractAddress();

struct CDexTokenInfo {

struct CTokenInfo {
uint64_t swaps;
uint64_t feeburn;
uint64_t commissions;

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(swaps);
READWRITE(feeburn);
READWRITE(commissions);
}
};

CTokenInfo totalTokenA;
CTokenInfo totalTokenB;

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(totalTokenA);
READWRITE(totalTokenB);
}
};

using CDexBalances = std::map<DCT_ID, CDexTokenInfo>;
using CAttributeType = boost::variant<CDataStructureV0, CDataStructureV1>;
using CAttributeValue = boost::variant<bool, CAmount, CBalances, CTokenPayback>;
using CAttributeValue = boost::variant<bool, CAmount, CBalances, CTokenPayback, CDexBalances>;

class ATTRIBUTES : public GovVariable, public AutoRegistrator<GovVariable, ATTRIBUTES>
{
Expand Down Expand Up @@ -144,6 +175,21 @@ class ATTRIBUTES : public GovVariable, public AutoRegistrator<GovVariable, ATTRI
return std::move(value);
}

template<typename K, typename T>
void SetValue(const K& key, T&& value) {
static_assert(std::is_convertible_v<K, CAttributeType>);
static_assert(std::is_convertible_v<T, CAttributeValue>);
changed.insert(key);
attributes[key] = std::forward<T>(value);
}

template<typename K>
void EraseKey(const K& key) {
static_assert(std::is_convertible_v<K, CAttributeType>);
changed.insert(key);
attributes.erase(key);
}

template<typename K>
[[nodiscard]] bool CheckKey(const K& key) const {
static_assert(std::is_convertible_v<K, CAttributeType>);
Expand All @@ -158,10 +204,11 @@ class ATTRIBUTES : public GovVariable, public AutoRegistrator<GovVariable, ATTRI
READWRITE(attributes);
}

std::map<CAttributeType, CAttributeValue> attributes;

private:
friend class CGovView;
bool futureBlockUpdated{};
std::set<CAttributeType> changed;
std::map<CAttributeType, CAttributeValue> attributes;

// Defined allowed arguments
static const std::map<std::string, uint8_t>& allowedVersions();
Expand Down
24 changes: 23 additions & 1 deletion src/masternodes/gv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,29 @@

Res CGovView::SetVariable(GovVariable const & var)
{
return WriteBy<ByName>(var.GetName(), var) ? Res::Ok() : Res::Err("can't write to DB");
auto WriteVar = [this](GovVariable const & var) {
return WriteBy<ByName>(var.GetName(), var) ? Res::Ok() : Res::Err("can't write to DB");
};
if (var.GetName() != "ATTRIBUTES") {
return WriteVar(var);
}
auto attributes = GetAttributes();
if (!attributes) {
return WriteVar(var);
}
auto& current = dynamic_cast<const ATTRIBUTES&>(var);
if (current.changed.empty()) {
return Res::Ok();
}
for (auto& key : current.changed) {
auto it = current.attributes.find(key);
if (it == current.attributes.end()) {
attributes->attributes.erase(key);
} else {
attributes->attributes[key] = it->second;
}
}
return WriteVar(*attributes);
}

std::shared_ptr<GovVariable> CGovView::GetVariable(std::string const & name) const
Expand Down
43 changes: 37 additions & 6 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1584,11 +1584,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor
balances.Add(obj.source);
}

attributes->attributes[liveKey] = balances;
attributes->SetValue(liveKey, std::move(balances));

mnview.SetVariable(*attributes);

return Res::Ok();
return mnview.SetVariable(*attributes);
}

Res operator()(const CAnyAccountsToAccountsMessage& obj) const {
Expand Down Expand Up @@ -3175,7 +3173,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor

balances.Add(CTokenAmount{loanTokenId, subAmount});
balances.Add(CTokenAmount{paybackTokenId, penalty});
attributes->attributes[liveKey] = balances;
attributes->SetValue(liveKey, std::move(balances));

LogPrint(BCLog::LOAN, "CLoanPaybackLoanMessage(): Burning interest and loan in %s directly - total loan %lld (%lld %s), height - %d\n", paybackToken->symbol, subLoan + subInterest, subInToken, paybackToken->symbol, height);

Expand All @@ -3188,7 +3186,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor

balances.tokensPayback.Add(CTokenAmount{loanTokenId, subAmount});
balances.tokensFee.Add(CTokenAmount{paybackTokenId, penalty});
attributes->attributes[liveKey] = balances;
attributes->SetValue(liveKey, std::move(balances));

LogPrint(BCLog::LOAN, "CLoanPaybackLoanMessage(): Swapping %s to DFI and burning it - total loan %lld (%lld %s), height - %d\n", paybackToken->symbol, subLoan + subInterest, subInToken, paybackToken->symbol, height);

Expand Down Expand Up @@ -4064,6 +4062,14 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs, boo
mnview.Flush();
}

auto attributes = view.GetAttributes();
if (!attributes) {
attributes = std::make_shared<ATTRIBUTES>();
}

CDataStructureV0 dexKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DexTokens};
auto dexBalances = attributes->GetValue(dexKey, CDexBalances{});

// Set amount to be swapped in pool
CTokenAmount swapAmountResult{obj.idTokenFrom, obj.amountFrom};

Expand Down Expand Up @@ -4101,6 +4107,18 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs, boo

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

auto& balances = dexBalances[currentID];
auto forward = swapAmount.nTokenId == pool->idTokenA;

auto& totalTokenA = forward ? balances.totalTokenA : balances.totalTokenB;
auto& totalTokenB = forward ? balances.totalTokenB : balances.totalTokenA;

const auto& reserveAmount = forward ? pool->reserveA : pool->reserveB;
const auto& blockCommission = forward ? pool->blockCommissionA : pool->blockCommissionB;

const auto initReserveAmount = reserveAmount;
const auto initBlockCommission = blockCommission;

// Perform swap
poolResult = pool->Swap(swapAmount, dexfeeInPct, poolPrice, [&] (const CTokenAmount& dexfeeInAmount, const CTokenAmount& tokenAmount) {
// Save swap amount for next loop
Expand Down Expand Up @@ -4148,6 +4166,7 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs, boo
if (!res) {
return res;
}
totalTokenA.feeburn += dexfeeInAmount.nValue;
}

// burn the dex out amount
Expand All @@ -4156,6 +4175,14 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs, boo
if (!res) {
return res;
}
totalTokenB.feeburn += dexfeeOutAmount.nValue;
}

totalTokenA.swaps += (reserveAmount - initReserveAmount);
totalTokenA.commissions += (blockCommission - initBlockCommission);

if (lastSwap && obj.to == Params().GetConsensus().burnAddress) {
totalTokenB.feeburn += swapAmountResult.nValue;
}

return res;
Expand All @@ -4176,6 +4203,10 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector<DCT_ID> poolIDs, boo
}
}

if (!testOnly) {
attributes->SetValue(dexKey, std::move(dexBalances));
view.SetVariable(*attributes);
}
// Assign to result for loop testing best pool swap result
result = swapAmountResult.nValue;

Expand Down
10 changes: 5 additions & 5 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2833,8 +2833,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
CCustomCSView govCache(cache);
// Add to existing ATTRIBUTES instead of overwriting.
if (var->GetName() == "ATTRIBUTES") {
auto govVar = mnview.GetVariable(var->GetName());
if (govVar->Import(var->Export()) && govVar->Validate(govCache) && govVar->Apply(govCache, pindex->nHeight) && govCache.SetVariable(*var)) {
auto govVar = cache.GetVariable(var->GetName());
if (govVar->Import(var->Export()) && govVar->Validate(govCache) && govVar->Apply(govCache, pindex->nHeight) && govCache.SetVariable(*govVar)) {
govCache.Flush();
}
} else if (var->Validate(govCache) && var->Apply(govCache, pindex->nHeight) && govCache.SetVariable(*var)) {
Expand Down Expand Up @@ -3435,11 +3435,11 @@ void CChainState::ProcessFutures(const CBlockIndex* pindex, CCustomCSView& cache
cache.EraseFuturesUserValues(key);
}

attributes->attributes[burnKey] = burned;
attributes->attributes[mintedKey] = minted;
attributes->SetValue(burnKey, std::move(burned));
attributes->SetValue(mintedKey, std::move(minted));

if (!unpaidContracts.empty()) {
attributes->attributes[liveKey] = balances;
attributes->SetValue(liveKey, std::move(balances));
}

cache.SetVariable(*attributes);
Expand Down
43 changes: 40 additions & 3 deletions test/functional/feature_poolswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ def run_test(self):
# 7 Sync
self.sync_blocks([self.nodes[0], self.nodes[2]])

attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']
# silver is tokenB
assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idGS)], Decimal('9.0'))
assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idGS)], Decimal('1.0'))

# 8 Checking that poolswap is correct
goldCheckN0 = self.nodes[2].getaccount(accountGN0, {}, True)[idGold]
silverCheckN0 = self.nodes[2].getaccount(accountGN0, {}, True)[idSilver]
Expand Down Expand Up @@ -253,6 +258,10 @@ def run_test(self):
)
self.nodes[0].generate(1)

attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']
assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idGS)], Decimal('189.0'))
assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idGS)], Decimal('21.0'))

maxPrice = self.nodes[0].listpoolpairs()['1']['reserveB/reserveA']
# exchange tokens each other should work
self.nodes[0].poolswap({
Expand All @@ -274,6 +283,12 @@ def run_test(self):
})
self.nodes[0].generate(1)

attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']
assert_equal(attributes['v0/live/economy/dex/%s/total_swap_a'%(idGS)], Decimal('180.0'))
assert_equal(attributes['v0/live/economy/dex/%s/total_commission_a'%(idGS)], Decimal('20.0'))
assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idGS)], Decimal('369.0'))
assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idGS)], Decimal('41.0'))

# Test fort canning max price change
disconnect_nodes(self.nodes[0], 1)
disconnect_nodes(self.nodes[0], 2)
Expand Down Expand Up @@ -390,6 +405,12 @@ def run_test(self):
})
self.nodes[0].generate(1)

attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']
assert_equal(attributes['v0/live/economy/dex/%s/total_swap_a'%(idBL)], Decimal('0.0'))
assert_equal(attributes['v0/live/economy/dex/%s/total_commission_a'%(idBL)], Decimal('0.0'))
assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idBL)], Decimal('0.00000189'))
assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idBL)], Decimal('1E-8'))

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
Expand Down Expand Up @@ -428,7 +449,9 @@ def run_test(self):
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)

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

result = self.nodes[0].getpoolpair(idGS)
assert_equal(result[idGS]['dexFeePctTokenA'], Decimal('0.05'))
Expand Down Expand Up @@ -461,11 +484,17 @@ def run_test(self):

assert_equal(self.nodes[0].getburninfo()['dexfeetokens'].sort(), ['%.8f'%(dexinfee)+symbolGOLD, '%.8f'%(dexoutfee)+symbolSILVER].sort())

attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']
assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_a'%(idGS)], dexinfee)
assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_b'%(idGS)], dexoutfee)

# set 1% token dex fee and commission
self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(idGS): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(idGS): '0.01'}})
self.nodes[0].generate(1)

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'})
attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']
assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(idGS)], '0.01')
assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(idGS)], '0.01')

self.nodes[0].updatepoolpair({"pool": "GS", "commission": 0.01})
self.nodes[0].generate(1)
Expand All @@ -489,7 +518,11 @@ 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)

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'})
attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']
assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(idGS)], '0.01')
assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(idGS)], '0.01')
assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(idBL)], '0.01')
assert_equal(attributes['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()
Expand Down Expand Up @@ -528,6 +561,10 @@ def run_test(self):
dexoutfee = round(trunc(amountA * Decimal(0.05) * coin) / coin, 8)
assert_equal(round(amountA - Decimal(dexoutfee), 8), round(swapped, 8))

attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']
assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_b'%(idBL)], round(dexinfee, 8))
assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_a'%(idBL)], Decimal(str(round(dexoutfee, 8))))

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

0 comments on commit 4d06b66

Please sign in to comment.