-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #99 from White-Whale-Defi-Platform/feat/liquidations
Feat/liquidations
- Loading branch information
Showing
21 changed files
with
2,348 additions
and
6,855 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { toUtf8 } from "@cosmjs/encoding"; | ||
import { EncodeObject } from "@cosmjs/proto-signing"; | ||
import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx"; | ||
|
||
import { LiquidationMessage } from "../../../core/types/messages/liquidationmessages"; | ||
|
||
/** | ||
* | ||
*/ | ||
export function getliqudationMessage(sender: string, overseerAddress: string, borrowerAddress: string) { | ||
const message: LiquidationMessage = { | ||
liquidate_collateral: { | ||
borrower: borrowerAddress, | ||
}, | ||
}; | ||
const encodedMsgObject: EncodeObject = { | ||
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", | ||
value: MsgExecuteContract.fromPartial({ | ||
sender: sender, | ||
contract: overseerAddress, | ||
msg: toUtf8(JSON.stringify(message)), | ||
funds: [], | ||
}), | ||
}; | ||
return encodedMsgObject; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import { ChainOperator } from "../../../core/chainOperator/chainoperator"; | ||
import { AnchorOverseer, Loan, Loans } from "../../../core/types/base/overseer"; | ||
|
||
/** | ||
* Queries all Loans connected to one Overseeraddress. | ||
*/ | ||
export async function setLoans(overseer: AnchorOverseer, chainOperator: ChainOperator) { | ||
const allBorrowers = await getAllBorrowers(overseer, chainOperator); | ||
const allCollateral = await getAllCollaterals(overseer, chainOperator); | ||
const allLoans = await getAllLoans(overseer.marketAddress, chainOperator); | ||
|
||
const loans: Loans = {}; | ||
for (const collateral of allCollateral) { | ||
const ltv = overseer.whitelist.elems.filter((elem) => elem.collateral_token === collateral.collaterals[0][0])[0] | ||
.max_ltv; | ||
// const e = allCollaterLs[o]; | ||
if (allBorrowers.has(collateral.borrower)) { | ||
const loan: Loan = { | ||
borrowerAddress: collateral.borrower, | ||
collaterals: {}, | ||
borrowLimit: 0, | ||
riskRatio: 0, | ||
loanAmt: 0, | ||
}; | ||
collateral.collaterals.forEach((elem: [string, string]) => { | ||
loan.collaterals![elem[0]] = { amount: Number(elem[1]), ltv: +ltv }; | ||
}); | ||
|
||
loan.loanAmt = allLoans.get(collateral.borrower) ?? 0; | ||
loans[collateral.borrower] = loan; | ||
} else { | ||
console.log("Borrower not found"); | ||
} | ||
} | ||
overseer.loans = loans; | ||
} | ||
|
||
/** | ||
* | ||
*/ | ||
async function getAllLoans(marketAddress: string, chainOperator: ChainOperator): Promise<Map<string, number>> { | ||
let tmploans = await await chainOperator.queryContractSmart(marketAddress, { | ||
borrower_infos: { limit: 30 }, | ||
}); | ||
let allLoans = tmploans.borrower_infos; | ||
while (tmploans.borrower_infos.length == 30) { | ||
tmploans = await await chainOperator.queryContractSmart(marketAddress, { | ||
borrower_infos: { | ||
limit: 30, | ||
start_after: tmploans.borrower_infos[tmploans.borrower_infos.length - 1].borrower, | ||
}, | ||
}); | ||
allLoans = allLoans.concat(tmploans.borrower_infos); | ||
} | ||
|
||
const tmpMapLoans: Map<string, number> = new Map(); | ||
|
||
allLoans.forEach((elem: any) => tmpMapLoans.set(elem.borrower, Number(elem.loan_amount))); | ||
return tmpMapLoans; | ||
} | ||
|
||
/** | ||
* | ||
*/ | ||
async function getAllCollaterals( | ||
overseer: AnchorOverseer, | ||
chainOperator: ChainOperator, | ||
): Promise<CollateralResponse["all_collaterals"]> { | ||
const collQuery = { | ||
all_collaterals: { limit: 30 }, | ||
}; | ||
let collateralResponse: CollateralResponse = await chainOperator.queryContractSmart( | ||
overseer.overseerAddress, | ||
collQuery, | ||
); | ||
const allCollaterals = collateralResponse.all_collaterals; | ||
let currentCollaterals = collateralResponse.all_collaterals; | ||
while (currentCollaterals.length === 30) { | ||
const msg = { | ||
all_collaterals: { | ||
limit: 30, | ||
start_after: currentCollaterals[currentCollaterals.length - 1].borrower, | ||
}, | ||
}; | ||
collateralResponse = await chainOperator.queryContractSmart(overseer.overseerAddress, msg); | ||
currentCollaterals = collateralResponse.all_collaterals; | ||
allCollaterals.push(...currentCollaterals); | ||
} | ||
return allCollaterals; | ||
} | ||
/** | ||
* | ||
*/ | ||
async function getAllBorrowers(overseer: AnchorOverseer, chainOperator: ChainOperator): Promise<Map<string, number>> { | ||
const borrowerList: Map<string, number> = new Map(); | ||
|
||
for (const asset of overseer.whitelist.elems) { | ||
const borrowersResponse: BorrowersResponse = await chainOperator.queryContractSmart(asset.custody_contract, { | ||
borrowers: { limit: 30 }, | ||
}); | ||
const borrowersCustody = borrowersResponse.borrowers; | ||
|
||
let borrowers = borrowersResponse.borrowers; | ||
while (borrowers.length === 30) { | ||
const msg = { | ||
borrowers: { limit: 30, start_after: borrowers[borrowers.length - 1].borrower }, | ||
}; | ||
const borrowersResponseNext: BorrowersResponse = await chainOperator.queryContractSmart( | ||
asset.custody_contract, | ||
msg, | ||
); | ||
borrowersCustody.push(...borrowersResponseNext.borrowers); | ||
borrowers = borrowersResponseNext.borrowers; | ||
} | ||
processBorrowers(borrowerList, borrowersCustody); | ||
} | ||
return borrowerList; | ||
} | ||
/** | ||
* | ||
*/ | ||
function processBorrowers(borrowerList: Map<string, number>, borrowers: BorrowersResponse["borrowers"]) { | ||
for (const borrow of borrowers) { | ||
const existingBorrow = borrowerList.get(borrow.borrower); | ||
if (existingBorrow === undefined) { | ||
borrowerList.set(borrow.borrower, Number(borrow.balance) - Number(borrow.spendable)); | ||
} else { | ||
borrowerList.set(borrow.borrower, existingBorrow + Number(borrow.balance) - Number(borrow.spendable)); | ||
} | ||
} | ||
} | ||
|
||
interface BorrowersResponse { | ||
borrowers: Array<{ | ||
borrower: string; | ||
balance: string; | ||
spendable: string; | ||
}>; | ||
} | ||
interface CollateralResponse { | ||
all_collaterals: Array<{ | ||
borrower: string; | ||
collaterals: Array<[string, string]>; | ||
}>; | ||
} | ||
|
||
/** | ||
* | ||
*/ | ||
function delay(ms: number) { | ||
return new Promise((resolve) => setTimeout(resolve, ms)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import { ChainOperator } from "../../../core/chainOperator/chainoperator"; | ||
import { AnchorOverseer, AnchorWhitelist, setBorrowLimits } from "../../../core/types/base/overseer"; | ||
import { setLoans } from "./getLoans"; | ||
|
||
/** | ||
* | ||
*/ | ||
export async function initLiquidationOverseers( | ||
overseerAddresses: string | Array<string>, | ||
chainOperator: ChainOperator, | ||
) { | ||
// export async function getliqudationinfos(overseer: Array<string>, operator: ChainOperator): Promise<Liquidate> { | ||
let overseerAddresssArray: Array<string>; | ||
if (typeof overseerAddresses === "string") { | ||
overseerAddresssArray = [overseerAddresses]; | ||
} else { | ||
overseerAddresssArray = overseerAddresses; | ||
} | ||
|
||
const overseers: Array<AnchorOverseer> = []; | ||
for (const overseerAddress of overseerAddresssArray) { | ||
const overseer: AnchorOverseer | undefined = await initLiquidationOverseer(overseerAddress, chainOperator); | ||
if (!overseer) { | ||
console.log("Overseer cannot be found: ", overseerAddress); | ||
process.exit(1); | ||
} | ||
overseer.priceFeed = await initPriceFeeds(overseer, chainOperator); | ||
await setLoans(overseer, chainOperator); | ||
setBorrowLimits(overseer); | ||
overseers.push(overseer); | ||
} | ||
return overseers; | ||
} | ||
|
||
/** | ||
* | ||
*/ | ||
async function initLiquidationOverseer( | ||
overseer: string, | ||
chainOperator: ChainOperator, | ||
): Promise<AnchorOverseer | undefined> { | ||
const overseerConfig: AnchorOverseerConfig = await chainOperator.queryContractSmart(overseer, { | ||
config: { limit: 100 }, | ||
}); | ||
await delay(500); | ||
if (overseerConfig) { | ||
const whitelist: AnchorWhitelist = await chainOperator.queryContractSmart(overseer, { | ||
whitelist: { limit: 100 }, | ||
}); | ||
await delay(200); | ||
const priceFeeders: Set<string> = new Set(); | ||
|
||
for (const whitelisted of whitelist.elems) { | ||
const feeder: AnchorAssetFeeder = await chainOperator.queryContractSmart(overseerConfig.oracle_contract, { | ||
feeder: { asset: whitelisted.collateral_token }, | ||
}); | ||
await delay(200); | ||
priceFeeders.add(feeder.feeder); | ||
} | ||
const anchorOverseer: AnchorOverseer = { | ||
overseerAddress: overseer, | ||
oracleAddress: overseerConfig.oracle_contract, | ||
marketAddress: overseerConfig.market_contract, | ||
liquidatorAddress: overseerConfig.liquidation_contract, | ||
priceFeeders: Array.from(priceFeeders), | ||
priceFeed: new Map(), | ||
whitelist: whitelist, | ||
loans: {}, | ||
stableDenom: overseerConfig.stable_denom, | ||
}; | ||
return anchorOverseer; | ||
} | ||
console.log("cannot find overseer config for: ", overseer); | ||
return undefined; | ||
} | ||
/** | ||
* | ||
*/ | ||
async function initPriceFeeds(overseer: AnchorOverseer, chainOperator: ChainOperator) { | ||
const priceFeed: (typeof overseer)["priceFeed"] = new Map(); | ||
const priceFeedRes: PriceFeedResult = await chainOperator.queryContractSmart(overseer.oracleAddress, { | ||
prices: { limit: 1000 }, | ||
}); | ||
for (const price of priceFeedRes.prices) { | ||
priceFeed.set(price.asset, +price.price); | ||
} | ||
return priceFeed; | ||
} | ||
interface AnchorOverseerConfig { | ||
owner_addr: string; | ||
oracle_contract: string; | ||
market_contract: string; | ||
liquidation_contract: string; | ||
borrow_reserves_bucket_contract: string; | ||
threshold_deposit_rate: string; | ||
target_deposit_rate: string; | ||
buffer_distribution_factor: string; | ||
stable_denom: string; | ||
epoch_period: number; | ||
price_timeframe: number; | ||
dyn_rate_epoch: number; | ||
dyn_rate_maxchange: string; | ||
dyn_rate_yr_increase_expectation: string; | ||
dyn_rate_min: string; | ||
dyn_rate_max: string; | ||
} | ||
|
||
interface PriceFeedResult { | ||
prices: Array<{ | ||
asset: string; | ||
price: string; | ||
last_updated_time: 1686224223; | ||
}>; | ||
} | ||
|
||
interface AnchorAssetFeeder { | ||
asset: string; | ||
feeder: string; | ||
} | ||
/** | ||
* | ||
*/ | ||
function delay(ms: number) { | ||
return new Promise((resolve) => setTimeout(resolve, ms)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.