Skip to content
Merged

axlp #10992

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
3 changes: 3 additions & 0 deletions coins/src/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as balancer from "./markets/balancer";
import * as others from "./other/index";
import * as others2 from "./other/others2";
import * as graphCoins from "./markets/graphCoins";
import * as morpho from "./moneyMarkets/morpho";

export default {
...compound.adapters,
Expand All @@ -18,6 +19,7 @@ export default {
...others.adapters,
...others2.adapters,
...graphCoins.adapters,
...morpho.adapters,
fraxtalGas: require("./other/fraxtalGas"),
reservoirprotocol: require("./rwa/reservoir-protocol"),
trize: require("./rwa/t-rize"),
Expand Down Expand Up @@ -174,4 +176,5 @@ export default {
goblin: require("./markets/goblin"),
ember: require("./yield/ember"),
suirewards: require("./markets/suirewards"),
axlp: require("./liquidStaking/axlp"),
};
66 changes: 66 additions & 0 deletions coins/src/adapters/liquidStaking/axlp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Write } from "../utils/dbInterfaces";
import { addToDBWritesList } from "../utils/database";
import { getApi } from "../utils/sdk";

const contracts: { [chain: string]: { [key: string]: { symbol: string, token: string, stakingContract: string, rate: number } } } = {
"arbitrum": {
"amlp": {
"symbol": "AMLP",
"token": "0x152f5E6142db867f905a68617dBb6408D7993a4b",
"stakingContract": "0x3a66b81be26f2d799C5A96A011e1E3FB2BA50999",
"rate": 1.0
},
"ahlp": {
"symbol": "AHLP",
"token": "0x5fd22dA8315992dbbD82d5AC1087803ff134C2c4",
"stakingContract": "0x1ba274EBbB07353657ed8C76A87ACf362E408D85",
"rate": 1.0
}
}
}

export async function axlp(timestamp: number = 0) {
const writes: Write[] = [];

await Promise.all(
Object.keys(contracts).map(async (chain) => {
const { amlp, ahlp } = contracts[chain];
const api = await getApi(chain, timestamp);
const amlpPrice = await api.call({
target: amlp.stakingContract,
abi: "uint256:price",
});

addToDBWritesList(
writes,
chain,
amlp.token,
amlpPrice / 10 ** 18,
18,
amlp.symbol,
timestamp,
"amlp",
1
);

const ahlpPrice = await api.call({
target: ahlp.stakingContract,
abi: "uint256:price",
});

addToDBWritesList(
writes,
chain,
ahlp.token,
ahlpPrice / 10 ** 18,
18,
ahlp.symbol,
timestamp,
"ahlp",
ahlp.rate
);
})
);

return writes;
}
285 changes: 285 additions & 0 deletions coins/src/adapters/moneyMarkets/morpho.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
import { runInPromisePool } from "@defillama/sdk/build/generalUtil";
import { getApi } from "../utils/sdk";
import { getCurrentUnixTimestamp } from "../../utils/date";
import { Write } from "../utils/dbInterfaces";
import { addToDBWritesList } from "../utils/database";
import { getTokenInfoMap } from "../utils/erc20";
import { request } from "@defillama/sdk/build/util/graph";
import { getConfig } from "../../utils/cache";

type VaultDatas = {
[vault: string]: {
totalAssets: number;
positions: any[];
markets: string[];
};
};

const listaConfig: { [chain: string]: { vault: string; vaultInfo: string } } = {
bsc: {
vault: "0x8F73b65B4caAf64FBA2aF91cC5D4a2A1318E5D8C",
vaultInfo:
"https://api.lista.org/api/moolah/vault/list?page=1&pageSize=1000",
},
ethereum: {
vault: "0xf820fB4680712CD7263a0D3D024D5b5aEA82Fd70",
vaultInfo:
"https://api.lista.org/api/moolah/vault/list?page=1&pageSize=1000&chain=ethereum",
},
};

async function fetchMorphoVaultAddresses(chainId: string) {
let assets: { [vault: string]: string } = {};
let skip = 0;
let length = 1000;

while (length == 1000) {
const query = `
query {
vaults (first: ${length}, skip: ${skip}, orderBy: Address, where: {
chainId_in: [${chainId}]
}) {
items {
asset {
address
}
address
}
}}`;

const res = await request("https://api.morpho.org/graphql", query, { cache: true, cacheKey: `morpho-vaults-${skip}` });
res.vaults.items.forEach((item: any) => {
assets[item.address.toLowerCase()] = item.asset.address.toLowerCase();
});
length = res.vaults.items.length;
skip += length;
}

return assets;
}

async function morpho(
timestamp: number = 0,
vaultAssets: { [vault: string]: string } = {},
api: any,
target: string
) {
const threeDaysAgo =
(timestamp == 0 ? getCurrentUnixTimestamp() : timestamp) - 3 * 24 * 60 * 60;
const threeDaysAgoApi = await getApi(api.chain, threeDaysAgo);

if (!api.chainId) throw new Error("Chain ID not found");
const allMarkets: string[] = [];
const currentVaultDatas: VaultDatas = {};
const previousVaultDatas: VaultDatas = {};

await runInPromisePool({
items: Object.keys(vaultAssets),
concurrency: 5,
processor: (vault: string) => fetchVaultPositions(vault, api, true),
});

await runInPromisePool({
items: Object.keys(vaultAssets),
concurrency: 5,
processor: (vault: string) => fetchVaultPositions(vault, threeDaysAgoApi, false),
});

async function fetchVaultPositions(vault: string, api: any, isCurrent: boolean) {
const totalAssets = await api.call({
target: vault,
abi: "uint256:totalAssets",
permitFailure: true,
});

const withdrawQueueLength = await api.call({
target: vault,
abi: "uint256:withdrawQueueLength",
permitFailure: true,
});

const markets = await api.multiCall({
target: vault,
abi: "function withdrawQueue(uint256 index) view returns (bytes32)",
calls: Array.from({ length: withdrawQueueLength }, (_, i) => ({
params: i,
})),
permitFailure: true,
});

// vaults position in market
const positions = await api.multiCall({
target,
abi: "function position(bytes32, address) view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral)",
calls: markets.map((market: string) => ({
params: [market, vault],
})),
permitFailure: true,
});

allMarkets.push(...markets);
isCurrent
? (currentVaultDatas[vault] = { totalAssets, positions, markets })
: (previousVaultDatas[vault] = { totalAssets, positions, markets });
}

const uniqueMarkets = [...new Set(allMarkets)];
const [currentMarketData, previousMarketData] = await Promise.all([
fetchMarketData(api),
fetchMarketData(threeDaysAgoApi),
]);

async function fetchMarketData(api: any) {
const marketDataArray = await api.multiCall({
target,
abi: "function market(bytes32) view returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee)",
calls: uniqueMarkets.map((market: string) => ({ params: market })),
permitFailure: true,
});
const marketData: {
[market: string]: {
totalSupplyAssets: number;
totalSupplyShares: number;
totalBorrowAssets: number;
};
} = {};
marketDataArray.forEach((m: any, i: number) => {
marketData[uniqueMarkets[i]] = m;
});

return marketData;
}

const currentTotalWithdrawables = aggregateWithdrawable(
currentVaultDatas,
currentMarketData
);
const previousTotalWithdrawables = aggregateWithdrawable(
previousVaultDatas,
previousMarketData
);

function aggregateWithdrawable(vaultDatas: VaultDatas, marketData: any) {
let totalWithdrawables: { [vault: string]: number } = {};
Object.keys(vaultDatas).map((vault: string) => {
totalWithdrawables[vault] = 0;
const { positions, markets } = vaultDatas[vault];

markets.map((market: string, i: number) => {
const { totalSupplyAssets, totalSupplyShares, totalBorrowAssets } =
marketData[market];
if (positions[i].supplyShares == 0) return;
const supplyAssets =
(positions[i].supplyShares * totalSupplyAssets) / totalSupplyShares;

const availableLiquidity = Math.max(
totalSupplyAssets - totalBorrowAssets,
0
);

const withdrawable = Math.min(supplyAssets, availableLiquidity);

totalWithdrawables[vault] += Number(withdrawable);
});
});

return totalWithdrawables;
}

const problemVaultList: string[] = [];
Object.keys(currentVaultDatas).map((vault: string) => {
const { totalAssets } = currentVaultDatas[vault];
if (totalAssets == 0) return;

const currentWithdrawable = currentTotalWithdrawables[vault];
const previousWithdrawable = previousTotalWithdrawables[vault];
if (currentWithdrawable / totalAssets > 0.01) return;

if (!previousVaultDatas[vault]) {
if (currentWithdrawable / totalAssets < 0.01)
console.log(
`Bad debt in vault ${vault} on ${api.chain}: ${((currentWithdrawable / totalAssets) * 100).toFixed(2)}% liquidity`
);

problemVaultList.push(vault);
return;
}

const { totalAssets: previousTotalAssets } = previousVaultDatas[vault];
if (
previousWithdrawable &&
previousWithdrawable / previousTotalAssets > 0.01
)
return;

problemVaultList.push(vault);
console.log(
`Bad debt in vault ${vault} on ${api.chain}: ${((currentWithdrawable / totalAssets) * 100).toFixed(2)}% liquidity`
);
});

const metadata = await getTokenInfoMap(api.chain, problemVaultList);

const writes: Write[] = [];
problemVaultList.forEach(async (vault: string) => {
const { symbol, decimals } = metadata[vault];
if (!symbol || !decimals) return;
addToDBWritesList(
writes,
api.chain,
vault,
0,
decimals,
symbol,
timestamp,
"morpho",
1.01
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is to ensure that it never gets overwritten?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah

);
});

return writes;
}

async function getListaVaults(chain: string) {
const {
data: { list: vaults },
} = await getConfig('lista-lend-vaults', listaConfig[chain].vaultInfo)
const listaVaults: { [vault: string]: string } = {};
vaults.map(
(vault: any) =>
(listaVaults[vault.address.toLowerCase()] = vault.asset.toLowerCase())
);
return listaVaults;
}

async function lista(timestamp: number = 0) {
return await Promise.all(
Object.keys(listaConfig).map(async (chain) => {
const api = await getApi(chain, timestamp);
const vaults = await getListaVaults(chain);
return await morpho(timestamp, vaults, api, listaConfig[chain].vault);
})
);
}

export async function morphoBlue(timestamp: number = 0) {
const chains = ["ethereum", "base", "hyperliquid", "katana", "arbitrum", "wc", "unichain", "polygon", "optimism", "plume_mainnet", "sei", "lisk", "etlk"];
return await Promise.all(
chains.map(async (chain) => {
const api = await getApi(chain, timestamp);
if (!api.chainId) return;
const vaults = await fetchMorphoVaultAddresses(api.chainId.toString());
return await morpho(
timestamp,
vaults,
api,
"0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
);
})
);
}

export const adapters = {
// morphoBlue,
lista,
} as any;
2 changes: 1 addition & 1 deletion coins/src/adapters/oracles/pyth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ export async function pyth(timestamp: number = now) {
})

return writes;
}
}
Loading