mirror of
https://github.com/alexgo-io/DefiLlama-Adapters.git
synced 2026-04-30 22:02:28 +08:00
149 lines
4.2 KiB
TypeScript
149 lines
4.2 KiB
TypeScript
import { gql, request } from "graphql-request";
|
|
import { Liq } from "../utils/types";
|
|
import { getPagedGql } from "../utils/gql";
|
|
|
|
const query = gql`
|
|
query users($lastId: String) {
|
|
users(first: 1000, where: { borrowedReservesCount_gt: 0, id_gt: $lastId }) {
|
|
id
|
|
reserves {
|
|
usageAsCollateralEnabledOnUser
|
|
reserve {
|
|
symbol
|
|
usageAsCollateralEnabled
|
|
underlyingAsset
|
|
price {
|
|
priceInEth
|
|
}
|
|
decimals
|
|
reserveLiquidationThreshold
|
|
}
|
|
currentATokenBalance
|
|
currentTotalDebt
|
|
}
|
|
}
|
|
_meta {
|
|
block {
|
|
number
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
const ethPriceQuery = (usdcAddress: string) => gql`
|
|
{
|
|
priceOracleAsset(id: "${usdcAddress}") {
|
|
priceInEth
|
|
}
|
|
}
|
|
`;
|
|
|
|
enum Chains {
|
|
ethereum = "ethereum",
|
|
polygon = "polygon",
|
|
}
|
|
|
|
type AaveAdapterResource = {
|
|
name: "aave";
|
|
chain: Chains;
|
|
usdcAddress: string;
|
|
subgraphUrl: string;
|
|
explorerBaseUrl: string;
|
|
};
|
|
|
|
const rc: { [chain in Chains]: AaveAdapterResource } = {
|
|
[Chains.ethereum]: {
|
|
name: "aave",
|
|
chain: Chains.ethereum,
|
|
usdcAddress: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
|
|
subgraphUrl: "https://api.thegraph.com/subgraphs/name/aave/protocol-v2",
|
|
explorerBaseUrl: "https://etherscan.io/address/",
|
|
},
|
|
[Chains.polygon]: {
|
|
name: "aave",
|
|
chain: Chains.polygon,
|
|
usdcAddress: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
|
|
subgraphUrl: "https://api.thegraph.com/subgraphs/name/aave/aave-v2-matic",
|
|
explorerBaseUrl: "https://polygonscan.com/address/",
|
|
},
|
|
};
|
|
|
|
const positions = (chain: Chains) => async () => {
|
|
const explorerBaseUrl = rc[chain].explorerBaseUrl;
|
|
const subgraphUrl = rc[chain].subgraphUrl;
|
|
const usdcAddress = rc[chain].usdcAddress;
|
|
const _ethPriceQuery = ethPriceQuery(usdcAddress);
|
|
const users = await getPagedGql(rc[chain].subgraphUrl, query, "users");
|
|
const ethPrice = 1 / ((await request(subgraphUrl, _ethPriceQuery)).priceOracleAsset.priceInEth / 1e18);
|
|
const positions = users
|
|
.map((user) => {
|
|
let totalDebt = 0,
|
|
totalCollateral = 0;
|
|
const debts = (user.reserves as any[]).map((reserve) => {
|
|
const decimals = 10 ** reserve.reserve.decimals;
|
|
const price = (Number(reserve.reserve.price.priceInEth) / 1e18) * ethPrice;
|
|
const liqThreshold = Number(reserve.reserve.reserveLiquidationThreshold) / 1e4; // belongs to [0, 1]
|
|
let debt = Number(reserve.currentTotalDebt);
|
|
if (reserve.usageAsCollateralEnabledOnUser === true) {
|
|
debt -= Number(reserve.currentATokenBalance) * liqThreshold;
|
|
}
|
|
debt *= price / decimals;
|
|
if (debt > 0) {
|
|
totalDebt += debt;
|
|
} else {
|
|
totalCollateral -= debt;
|
|
}
|
|
return {
|
|
debt,
|
|
price,
|
|
token: reserve.reserve.underlyingAsset,
|
|
totalBal: reserve.currentATokenBalance,
|
|
decimals,
|
|
};
|
|
});
|
|
|
|
const liquidablePositions: Liq[] = debts
|
|
.filter(({ debt }) => debt < 0)
|
|
.map((pos) => {
|
|
const usdPosNetCollateral = -pos.debt;
|
|
const otherCollateral = totalCollateral - usdPosNetCollateral;
|
|
const diffDebt = totalDebt - otherCollateral;
|
|
if (diffDebt > 0) {
|
|
const amountCollateral = usdPosNetCollateral / pos.price; // accounts for liqThreshold
|
|
const liqPrice = diffDebt / amountCollateral;
|
|
// if liqPrice > pos.price -> bad debt
|
|
return {
|
|
owner: user.id as string,
|
|
liqPrice,
|
|
collateral: `${chain}:` + pos.token,
|
|
collateralAmount: pos.totalBal as string,
|
|
extra: {
|
|
url: explorerBaseUrl + user.id,
|
|
},
|
|
} as Liq;
|
|
} else {
|
|
return {
|
|
owner: "",
|
|
liqPrice: 0,
|
|
collateral: "",
|
|
collateralAmount: "",
|
|
};
|
|
}
|
|
})
|
|
.filter((t) => !!t.owner);
|
|
|
|
return liquidablePositions;
|
|
})
|
|
.flat();
|
|
return positions;
|
|
};
|
|
|
|
module.exports = {
|
|
ethereum: {
|
|
liquidations: positions(Chains.ethereum),
|
|
},
|
|
polygon: {
|
|
liquidations: positions(Chains.polygon),
|
|
},
|
|
};
|