Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix removal of last liquidity on empty used pool #1287

Merged
merged 4 commits into from
May 25, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 112 additions & 87 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2203,6 +2203,69 @@ Res AddNonTxToBurnIndex(const CScript& from, const CBalances& amounts)
return mapBurnAmounts[from].AddBalances(amounts.balances);
}

void CChainState::ProcessEunosEvents(const CBlockIndex* pindex, CCustomCSView& cache, const CChainParams& chainparams) {
if (pindex->nHeight != chainparams.GetConsensus().EunosHeight) {
return;
}

// Move funds from old burn address to new one
CBalances burnAmounts;
cache.ForEachBalance([&burnAmounts](CScript const & owner, CTokenAmount balance) {
if (owner != Params().GetConsensus().retiredBurnAddress) {
return false;
}

burnAmounts.Add({balance.nTokenId, balance.nValue});

return true;
}, BalanceKey{chainparams.GetConsensus().retiredBurnAddress, DCT_ID{}});

AddNonTxToBurnIndex(chainparams.GetConsensus().retiredBurnAddress, burnAmounts);

// Zero foundation balances
for (const auto& script : chainparams.GetConsensus().accountDestruction)
{
CBalances zeroAmounts;
cache.ForEachBalance([&zeroAmounts, script](CScript const & owner, CTokenAmount balance) {
if (owner != script) {
return false;
}

zeroAmounts.Add({balance.nTokenId, balance.nValue});

return true;
}, BalanceKey{script, DCT_ID{}});

cache.SubBalances(script, zeroAmounts);
}

// Add any non-Tx burns to index as phantom Txs
for (const auto& item : mapBurnAmounts)
{
for (const auto& subItem : item.second.balances)
{
// If amount cannot be deducted then burn skipped.
auto result = cache.SubBalance(item.first, {subItem.first, subItem.second});
if (result.ok)
{
cache.AddBalance(chainparams.GetConsensus().burnAddress, {subItem.first, subItem.second});

// Add transfer as additional TX in block
pburnHistoryDB->WriteAccountHistory({Params().GetConsensus().burnAddress, static_cast<uint32_t>(pindex->nHeight), GetNextBurnPosition()},
{uint256{}, static_cast<uint8_t>(CustomTxType::AccountToAccount), {{subItem.first, subItem.second}}});
}
else // Log burn failure
{
CTxDestination dest;
ExtractDestination(item.first, dest);
LogPrintf("Burn failed: %s Address: %s Token: %d Amount: %d\n", result.msg, EncodeDestination(dest), subItem.first.v, subItem.second);
}
}
}

mapBurnAmounts.clear();
}

template<typename GovVar>
static void UpdateDailyGovVariables(const std::map<CommunityAccountType, uint32_t>::const_iterator& incentivePair, CCustomCSView& cache, int nHeight) {
if (incentivePair != Params().GetConsensus().newNonUTXOSubsidies.end())
Expand Down Expand Up @@ -2911,92 +2974,20 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
cache.BayfrontFlagsCleanup();
}

if (pindex->nHeight == chainparams.GetConsensus().EunosHeight)
{
// Move funds from old burn address to new one
CBalances burnAmounts;
cache.ForEachBalance([&burnAmounts](CScript const & owner, CTokenAmount balance) {
if (owner != Params().GetConsensus().retiredBurnAddress) {
return false;
}

burnAmounts.Add({balance.nTokenId, balance.nValue});

return true;
}, BalanceKey{chainparams.GetConsensus().retiredBurnAddress, DCT_ID{}});

AddNonTxToBurnIndex(chainparams.GetConsensus().retiredBurnAddress, burnAmounts);

// Zero foundation balances
for (const auto& script : chainparams.GetConsensus().accountDestruction)
{
CBalances zeroAmounts;
cache.ForEachBalance([&zeroAmounts, script](CScript const & owner, CTokenAmount balance) {
if (owner != script) {
return false;
}

zeroAmounts.Add({balance.nTokenId, balance.nValue});

return true;
}, BalanceKey{script, DCT_ID{}});

cache.SubBalances(script, zeroAmounts);
}
}

// Add any non-Tx burns to index as phantom Txs
for (const auto& item : mapBurnAmounts)
{
for (const auto& subItem : item.second.balances)
{
// If amount cannot be deducted then burn skipped.
auto result = cache.SubBalance(item.first, {subItem.first, subItem.second});
if (result.ok)
{
cache.AddBalance(chainparams.GetConsensus().burnAddress, {subItem.first, subItem.second});

// Add transfer as additional TX in block
pburnHistoryDB->WriteAccountHistory({Params().GetConsensus().burnAddress, static_cast<uint32_t>(pindex->nHeight), GetNextBurnPosition()},
{uint256{}, static_cast<uint8_t>(CustomTxType::AccountToAccount), {{subItem.first, subItem.second}}});
}
else // Log burn failure
{
CTxDestination dest;
ExtractDestination(item.first, dest);
LogPrintf("Burn failed: %s Address: %s Token: %d Amount: %d\n", result.msg, EncodeDestination(dest), subItem.first.v, subItem.second);
}
}
}

mapBurnAmounts.clear();
// burn DFI on Eunos height
ProcessEunosEvents(pindex, cache, chainparams);

// set oracle prices
ProcessOracleEvents(pindex, cache, chainparams);

// loan scheme, collateral ratio, liquidations
ProcessLoanEvents(pindex, cache, chainparams);

// Must be before set gov by height to clear futures in case there's a disabling of loan token in v3+
ProcessFutures(pindex, cache, chainparams);

if (pindex->nHeight >= chainparams.GetConsensus().FortCanningHeight) {
// Apply any pending GovVariable changes. Will come into effect on the next block.
auto storedGovVars = cache.GetStoredVariables(static_cast<uint32_t>(pindex->nHeight));
for (const auto& var : storedGovVars) {
if (var) {
CCustomCSView govCache(cache);
// Add to existing ATTRIBUTES instead of overwriting.
if (var->GetName() == "ATTRIBUTES") {
auto govVar = mnview.GetAttributes();
govVar->time = pindex->GetBlockTime();
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)) {
govCache.Flush();
}
}
}
cache.EraseStoredVariables(static_cast<uint32_t>(pindex->nHeight));
}
// update governance variables
ProcessGovEvents(pindex, cache, chainparams);

// Migrate loan and collateral tokens to Gov vars.
ProcessTokenToGovVar(pindex, cache, chainparams);
Expand Down Expand Up @@ -3681,6 +3672,31 @@ void CChainState::ProcessOracleEvents(const CBlockIndex* pindex, CCustomCSView&
});
}

void CChainState::ProcessGovEvents(const CBlockIndex* pindex, CCustomCSView& cache, const CChainParams& chainparams) {
if (pindex->nHeight < chainparams.GetConsensus().FortCanningHeight) {
return;
}

// Apply any pending GovVariable changes. Will come into effect on the next block.
auto storedGovVars = cache.GetStoredVariables(pindex->nHeight);
for (const auto& var : storedGovVars) {
if (var) {
CCustomCSView govCache(cache);
// Add to existing ATTRIBUTES instead of overwriting.
if (var->GetName() == "ATTRIBUTES") {
auto govVar = cache.GetAttributes();
govVar->time = pindex->GetBlockTime();
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)) {
govCache.Flush();
}
}
}
cache.EraseStoredVariables(static_cast<uint32_t>(pindex->nHeight));
}

void CChainState::ProcessTokenToGovVar(const CBlockIndex* pindex, CCustomCSView& cache, const CChainParams& chainparams) {

// Migrate at +1 height so that GetLastHeight() in Gov var
Expand Down Expand Up @@ -3891,19 +3907,28 @@ static Res PoolSplits(CCustomCSView& view, CAmount& totalBalance, ATTRIBUTES& at
return a.second > b.second;
});

// Special case. No liquidity providers in a previously used pool.
if (balancesToMigrate.empty() && oldPoolPair->totalLiquidity == CPoolPair::MINIMUM_LIQUIDITY) {
balancesToMigrate.emplace_back(Params().GetConsensus().burnAddress, CAmount{CPoolPair::MINIMUM_LIQUIDITY});
}

for (auto& [owner, amount] : balancesToMigrate) {

if (oldPoolPair->totalLiquidity < CPoolPair::MINIMUM_LIQUIDITY) {
throw std::runtime_error("totalLiquidity less than minimum.");
}
if (owner != Params().GetConsensus().burnAddress) {
view.CalculateOwnerRewards(owner, pindex->nHeight);

view.CalculateOwnerRewards(owner, pindex->nHeight);
res = view.SubBalance(owner, CTokenAmount{oldPoolId, amount});
if (!res.ok) {
throw std::runtime_error(strprintf("SubBalance failed: %s", res.msg));
}
}

res = view.SubBalance(owner, CTokenAmount{oldPoolId, amount});
if (!res.ok) {
throw std::runtime_error(strprintf("SubBalance failed: %s", res.msg));
if (oldPoolPair->totalLiquidity < CPoolPair::MINIMUM_LIQUIDITY) {
throw std::runtime_error("totalLiquidity less than minimum.");
}

// First deposit to the pool has MINIMUM_LIQUIDITY removed and does not
// belong to anyone. Give this to the last person leaving the pool.
if (oldPoolPair->totalLiquidity - amount == CPoolPair::MINIMUM_LIQUIDITY) {
amount += CPoolPair::MINIMUM_LIQUIDITY;
}
Expand All @@ -3930,7 +3955,7 @@ static Res PoolSplits(CCustomCSView& view, CAmount& totalBalance, ATTRIBUTES& at
view.AddBalance(owner, {newPoolPair.idTokenB, amountB});
};

if (amountA <= 0 || amountB <= 0) {
if (amountA <= 0 || amountB <= 0 || owner == Params().GetConsensus().burnAddress) {
refundBalances();
continue;
}
Expand Down
4 changes: 4 additions & 0 deletions src/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,10 @@ class CChainState {

static void ProcessLoanEvents(const CBlockIndex* pindex, CCustomCSView& cache, const CChainParams& chainparams);

static void ProcessEunosEvents(const CBlockIndex* pindex, CCustomCSView& cache, const CChainParams& chainparams);

static void ProcessGovEvents(const CBlockIndex* pindex, CCustomCSView& cache, const CChainParams& chainparams);

static void ProcessOracleEvents(const CBlockIndex* pindex, CCustomCSView& cache, const CChainParams& chainparams);

static void ProcessFutures(const CBlockIndex* pindex, CCustomCSView& cache, const CChainParams& chainparams);
Expand Down