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

Restart: Restore loan and collateral from auctions to vaults #3053

Merged
merged 3 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
55 changes: 47 additions & 8 deletions src/dfi/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3229,37 +3229,76 @@ static Res PaybackWithSwappedCollateral(const DCT_ID &collId,
static Res ForceCloseAllAuctions(const CBlockIndex *pindex, CCustomCSView &cache) {
std::map<uint256, uint32_t> auctionsToErase;

CAccountsHistoryWriter view(cache, pindex->nHeight, ~0u, pindex->GetBlockHash(), uint8_t(CustomTxType::AuctionBid));

auto res = Res::Ok();
cache.ForEachVaultAuction(
view.ForEachVaultAuction(
[&](const auto &vaultId, const auto &auctionData) {
auto vault = cache.GetVault(vaultId);
auto vault = view.GetVault(vaultId);
if (!vault) {
res = Res::Err("Vault not found");
return false;
}
vault->isUnderLiquidation = false;
cache.StoreVault(vaultId, *vault);
// return coll and loan from auction to vault
CBalances balances;
for (uint32_t i = 0; i < auctionData.batchCount; i++) {
if (auto bid = cache.GetAuctionBid({vaultId, i})) {
cache.CalculateOwnerRewards(bid->first, pindex->nHeight);
auto batch = view.GetAuctionBatch({vaultId, i});
assert(batch);

if (auto bid = view.GetAuctionBid({vaultId, i})) {
view.CalculateOwnerRewards(bid->first, pindex->nHeight);
// repay bid
res = cache.AddBalance(bid->first, bid->second);
res = view.AddBalance(bid->first, bid->second);
if (!res) {
return false;
}
}
// return loan and collateral either way
// we should return loan including interest
view.AddLoanToken(vaultId, batch->loanAmount);
balances.Add({batch->loanAmount.nTokenId, batch->loanInterest});

// When tracking loan amounts remove interest.
if (const auto token = view.GetToken("DUSD"); token && token->first == batch->loanAmount.nTokenId) {
TrackDUSDAdd(view, {batch->loanAmount.nTokenId, batch->loanAmount.nValue - batch->loanInterest});
}

if (auto token = view.GetLoanTokenByID(batch->loanAmount.nTokenId)) {
view.IncreaseInterest(pindex->nHeight,
vaultId,
vault->schemeId,
batch->loanAmount.nTokenId,
token->interest,
batch->loanAmount.nValue);
}
for (const auto &col : batch->collaterals.balances) {
auto tokenId = col.first;
auto tokenAmount = col.second;
view.AddVaultCollateral(vaultId, {tokenId, tokenAmount});
}
}

// Only store to attributes if there has been a rounding error.
if (!balances.balances.empty()) {
TrackLiveBalances(view, balances, EconomyKeys::ConsolidatedInterest);
}

auctionsToErase.emplace(vaultId, auctionData.liquidationHeight);

vault->isUnderLiquidation = false;
view.StoreVault(vaultId, *vault);

// Store state in vault DB
cache.GetHistoryWriters().WriteVaultState(cache, *pindex, vaultId);
cache.GetHistoryWriters().WriteVaultState(view, *pindex, vaultId);

return true;
},
pindex->nHeight);

if (!res) {
return res;
}
view.Flush();

for (const auto &[vaultId, liquidationHeight] : auctionsToErase) {
cache.EraseAuction(vaultId, liquidationHeight);
Expand Down
80 changes: 77 additions & 3 deletions test/functional/feature_restart_interest.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ def run_test(self):
# Set up
self.setup()

# Check restart on liquidated vault
self.vault_liquidation()

# Check restart skips on locked token
# self.skip_restart_on_lock()
self.skip_restart_on_lock()

# Check restart skips on pool disabled
self.skip_restart_on_pool_disabled()
Expand Down Expand Up @@ -117,14 +120,14 @@ def setup_test_oracles(self):
]

# Appoint Oracle
oracle = self.nodes[0].appointoracle(oracle_address, price_feed, 10)
self.oracle = self.nodes[0].appointoracle(oracle_address, price_feed, 10)
self.nodes[0].generate(1)

# Set Oracle prices
oracle_prices = [
{"currency": "USD", "tokenAmount": f"1@{self.symbolDFI}"},
]
self.nodes[0].setoracledata(oracle, int(time.time()), oracle_prices)
self.nodes[0].setoracledata(self.oracle, int(time.time()), oracle_prices)
self.nodes[0].generate(10)

# Create loan scheme
Expand Down Expand Up @@ -321,6 +324,77 @@ def skip_restart_on_pool_disabled(self):
attributes = self.nodes[0].getgov("ATTRIBUTES")["ATTRIBUTES"]
assert "v0/live/economy/token_lock_ratio" not in attributes

def vault_liquidation(self):

# Rollback block
self.rollback_to(self.start_block)

# Create vault
vault_address = self.nodes[0].getnewaddress("", "legacy")
vault_id = self.nodes[0].createvault(vault_address, "LOAN001")
self.nodes[0].generate(1)

# Deposit DFI to vault
self.nodes[0].deposittovault(vault_id, self.address, f"150@{self.symbolDFI}")
self.nodes[0].generate(1)

# Take DUSD loan
self.nodes[0].takeloan(
{"vaultId": vault_id, "amounts": f"100@{self.symbolDUSD}"}
)
self.nodes[0].generate(1)

# Set Oracle prices to trigger liquidation
oracle_prices = [
{"currency": "USD", "tokenAmount": f"0.9@{self.symbolDFI}"},
]
self.nodes[0].setoracledata(self.oracle, int(time.time()), oracle_prices)
self.nodes[0].generate(7)

# Check vault in liquidation
result = self.nodes[0].getvault(vault_id)
assert_equal(result["state"], "inLiquidation")

# Place bid on auction
self.nodes[0].placeauctionbid(
vault_id, 0, self.address, f"110@{self.symbolDUSD}"
)
self.nodes[0].generate(1)

# check balance before
assert_equal(
self.nodes[0].getaccount(self.address)[1],
f"89890.00000000@{self.symbolDUSD}",
)
assert_equal(
self.nodes[0].getaccount(vault_address),
[f"100.00000000@{self.symbolDUSD}"],
)

# Execute dtoken restart
self.execute_restart()

# Verify that auction bid has been refunded and DUSD locked afterwards
assert_equal(
self.nodes[0].getaccount(self.address)[1],
f"9000.00000000@{self.symbolDUSD}",
)

# DUSD is used to pay back loan
assert_equal(self.nodes[0].getaccount(vault_address), [])

# Check auctions are not cleared
assert_equal(self.nodes[0].listauctions(), [])

# Check vault
result = self.nodes[0].getvault(vault_id)
print("After vault", result)
assert_equal(result["loanAmounts"], [])
assert_equal(
result["collateralAmounts"], [f"149.99933408@{self.symbolDFI}"]
) # after paying back with account, interest remains which is paid back with collateral
assert_equal(result["interestAmounts"], [])

def interest_paid_by_balance(self):

# Rollback block
Expand Down
Loading