Skip to content

Commit

Permalink
Do not redistribute rewards per block
Browse files Browse the repository at this point in the history
Signed-off-by: Anthony Fieroni <[email protected]>
  • Loading branch information
bvbfan committed Mar 11, 2021
1 parent 7f6cc51 commit 836c4d0
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 136 deletions.
102 changes: 20 additions & 82 deletions src/masternodes/poolpairs.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,112 +302,50 @@ class CPoolPairView : public virtual CStorageView
return Res::Ok();
}

CAmount DistributeRewards(CAmount yieldFarming, std::function<CTokenAmount(CScript const & owner, DCT_ID tokenID)> onGetBalance, std::function<Res(CScript const & to, CScript const & from, DCT_ID poolID, uint8_t type, CTokenAmount amount)> onTransfer, int nHeight = 0) {
void UpdatePoolCommissions(std::function<CTokenAmount(CScript const & owner, DCT_ID tokenID)> onGetBalance, int nHeight = 0) {

bool newRewardCalc = nHeight >= Params().GetConsensus().BayfrontGardensHeight;

uint32_t const PRECISION = 10000; // (== 100%) just searching the way to avoid arith256 inflating
CAmount totalDistributed = 0;
constexpr uint32_t const PRECISION = 10000; // (== 100%) just searching the way to avoid arith256 inflating

ForEachPoolPair([&] (DCT_ID const & poolId, CPoolPair pool) {

// yield farming counters
CAmount const poolReward = yieldFarming * pool.rewardPct / COIN; // 'rewardPct' should be defined by 'setgov "LP_SPLITS"', also, it is assumed that it was totally validated and normalized to 100%
CAmount distributedFeeA = 0;
CAmount distributedFeeB = 0;

auto rewards = GetPoolCustomReward(poolId);
bool payCustomRewards{false};

if (nHeight >= Params().GetConsensus().ClarkeQuayHeight && rewards && !rewards->balances.empty()) {
for (auto it = rewards->balances.cbegin(), next_it = it; it != rewards->balances.cend(); it = next_it) {
++next_it;

// Get token balance
const auto balance = onGetBalance(pool.ownerAddress, it->first).nValue;

// Make there's enough to pay reward otherwise remove it
if (balance < it->second) {
rewards->balances.erase(it);
}
}

if (!rewards->balances.empty()) {
payCustomRewards = true;
}
}

if (pool.totalLiquidity == 0 || (!pool.swapEvent && poolReward == 0 && !payCustomRewards)) {
if (!pool.swapEvent) {
return true; // no events, skip to the next pool
}

CAmount distributedFeeA = 0;
CAmount distributedFeeB = 0;

ForEachPoolShare([&] (DCT_ID const & currentId, CScript const & provider, uint32_t height) {
if (currentId != poolId) {
return false; // stop
}
CAmount const liquidity = onGetBalance(provider, poolId).nValue;

uint32_t const liqWeight = liquidity * PRECISION / pool.totalLiquidity;
assert (liqWeight < PRECISION);

// distribute trading fees
if (pool.swapEvent) {
CAmount feeA, feeB;
if (newRewardCalc) {
feeA = static_cast<CAmount>((arith_uint256(pool.blockCommissionA) * arith_uint256(liquidity) / arith_uint256(pool.totalLiquidity)).GetLow64());
feeB = static_cast<CAmount>((arith_uint256(pool.blockCommissionB) * arith_uint256(liquidity) / arith_uint256(pool.totalLiquidity)).GetLow64());
} else {
feeA = pool.blockCommissionA * liqWeight / PRECISION;
feeB = pool.blockCommissionB * liqWeight / PRECISION;
}
if (onTransfer(provider, CScript(), poolId, uint8_t(RewardType::Commission), {pool.idTokenA, feeA})) {
distributedFeeA += feeA;
}
if (onTransfer(provider, CScript(), poolId, uint8_t(RewardType::Commission), {pool.idTokenB, feeB})) {
distributedFeeB += feeB;
}
}

// distribute yield farming
if (poolReward) {
CAmount providerReward = static_cast<CAmount>((arith_uint256(poolReward) * arith_uint256(liquidity) / arith_uint256(pool.totalLiquidity)).GetLow64());
if (!newRewardCalc) {
providerReward = poolReward * liqWeight / PRECISION;
}
if (providerReward) {
if (onTransfer(provider, CScript(), poolId, uint8_t(RewardType::Rewards), {DCT_ID{0}, providerReward})) {
totalDistributed += providerReward;
}
}
if (newRewardCalc) {
distributedFeeA += static_cast<CAmount>((arith_uint256(pool.blockCommissionA) * arith_uint256(liquidity) / arith_uint256(pool.totalLiquidity)).GetLow64());
distributedFeeB += static_cast<CAmount>((arith_uint256(pool.blockCommissionB) * arith_uint256(liquidity) / arith_uint256(pool.totalLiquidity)).GetLow64());
} else {
uint32_t const liqWeight = liquidity * PRECISION / pool.totalLiquidity;
assert (liqWeight < PRECISION);
distributedFeeA += pool.blockCommissionA * liqWeight / PRECISION;
distributedFeeB += pool.blockCommissionB * liqWeight / PRECISION;
}

// Distribute custom rewards
if (payCustomRewards) {
for (const auto& reward : rewards->balances) {
CAmount providerReward = static_cast<CAmount>((arith_uint256(reward.second) * arith_uint256(liquidity) / arith_uint256(pool.totalLiquidity)).GetLow64());

if (providerReward) {
onTransfer(provider, pool.ownerAddress, poolId, uint8_t(RewardType::Rewards), {reward.first, providerReward});
}
}
}

return true;
}, PoolShareKey{poolId, CScript{}});

if (pool.swapEvent) {
pool.blockCommissionA -= distributedFeeA;
pool.blockCommissionB -= distributedFeeB;
pool.swapEvent = false;
pool.blockCommissionA -= distributedFeeA;
pool.blockCommissionB -= distributedFeeB;
pool.swapEvent = false;

auto res = SetPoolPair(poolId, UINT_MAX, pool);
if (!res.ok) {
LogPrintf("Pool rewards: can't update pool (id=%s) state: %s\n", poolId.ToString(), res.msg);
}
auto res = SetPoolPair(poolId, UINT_MAX, pool);
if (!res.ok) {
LogPrintf("Pool rewards: can't update pool (id=%s) state: %s\n", poolId.ToString(), res.msg);
}
return true;
});
return totalDistributed;
}

// tags
Expand Down
23 changes: 5 additions & 18 deletions src/test/liquidity_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,30 +309,17 @@ BOOST_AUTO_TEST_CASE(math_rewards)
});

// distribute 100 coins
CAmount totalRwd = 100*COIN;

int64_t nTimeBegin = GetTimeMicros();
CAmount distributed = cache.DistributeRewards(totalRwd,
[&cache] (CScript const & owner, DCT_ID tokenID) {
return cache.GetBalance(owner, tokenID);
},
[&cache] (CScript const & to, CScript const & from, DCT_ID poolID, uint8_t type, CTokenAmount amount) {
return cache.AddBalance(to, amount);
}
);
int64_t nTimeEnd = GetTimeMicros(); auto nTimeRwd = nTimeEnd - nTimeBegin;
// printf("Rewarded %d pools with %d shares each: %.2fms \n", PoolCount, ProvidersCount, 0.001 * (nTimeRwd));

// printf("Distributed: = %ld\n", distributed);
BOOST_CHECK(distributed == 9999000000); // always slightly less due to MINIMUM_LIQUIDITY & rounding
CAmount totalRwd = 100*COIN*2880;
cache.SetDailyReward(1, totalRwd);
// fund community
cache.AddCommunityBalance(CommunityAccountType::IncentiveFunding, totalRwd);

// check it
auto rwd25 = 25 * COIN / ProvidersCount;
auto rwd50 = 50 * COIN / ProvidersCount;
cache.ForEachPoolShare([&] (DCT_ID const & id, CScript const & owner, uint32_t) {
// if (id == RWD25/* || id == RWD50*/)
// printf("owner = %s: %s,\n", owner.GetHex().c_str(), cache.GetBalance(owner, DCT_ID{0}).ToString().c_str());

cache.CalculateOwnerRewards(owner, 2); // one block
// check only first couple of pools and the last (zero)
if (id == RWD25 && owner != CScript(id.v * ProvidersCount)) { // first got slightly less due to MINIMUM_LIQUIDITY
CAmount rwd = cache.GetBalance(owner, DCT_ID{0}).nValue;
Expand Down
40 changes: 4 additions & 36 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2344,42 +2344,10 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
// make all changes to the new cache/snapshot to make it possible to take a diff later:
CCustomCSView cache(mnview);

// cache.CallYourInterblockProcessingsHere();

// distribute pool incentive rewards and trading fees:
{
std::shared_ptr<LP_DAILY_DFI_REWARD> var = std::dynamic_pointer_cast<LP_DAILY_DFI_REWARD>(cache.GetVariable(LP_DAILY_DFI_REWARD::TypeName()));
CAmount poolsBlockReward = std::min(
cache.GetCommunityBalance(CommunityAccountType::IncentiveFunding),
var->dailyReward / (60*60*24/chainparams.GetConsensus().pos.nTargetSpacing) // 2880
);

CAmount distributed = cache.DistributeRewards(poolsBlockReward,
[&cache] (CScript const & owner, DCT_ID tokenID) {
return cache.GetBalance(owner, tokenID);
},
[&cache, &block] (CScript const & to, CScript const & from, DCT_ID poolID, uint8_t type, CTokenAmount amount) {
if (from != CScript()) {
auto res = cache.SubBalance(from, amount);
if (!res.ok) {
LogPrintf("Custom pool rewards: can't update balance of %s: %s, Block %ld (%s)\n", to.GetHex(), res.msg, block.height, block.GetHash().ToString());
return res; // no funds, no rewards
}
}
auto res = cache.AddBalance(to, amount);
if (!res.ok) {
LogPrintf("Pool rewards: can't update balance of %s: %s, Block %ld (%s)\n", to.GetHex(), res.msg, block.height, block.GetHash().ToString());
}
return res;
},
pindex->nHeight
);

auto res = cache.SubCommunityBalance(CommunityAccountType::IncentiveFunding, distributed);
if (!res.ok) {
LogPrintf("Pool rewards: can't update community balance: %s. Block %ld (%s)\n", res.msg, block.height, block.GetHash().ToString());
}
}
// hardfork commissions update
cache.UpdatePoolCommissions([&cache](CScript const & owner, DCT_ID tokenID) {
return cache.GetBalance(owner, tokenID);
}, pindex->nHeight);
// Remove `Finalized` and/or `LPS` flags _possibly_set_ by bytecoded (cheated) txs before bayfront fork
if (pindex->nHeight == chainparams.GetConsensus().BayfrontHeight - 1) { // call at block _before_ fork
cache.BayfrontFlagsCleanup();
Expand Down

0 comments on commit 836c4d0

Please sign in to comment.