Skip to content
Merged
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
240 changes: 186 additions & 54 deletions coins/src/adapters/rwa/midas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,95 +2,216 @@ import { Write } from "../utils/dbInterfaces";
import { getApi } from "../utils/sdk";
import getWrites from "../utils/getWrites";

const DATA_FEED_ABI = "function getDataInBase18() external view returns (int256 answer)";
const AGGREGATOR_ABI = "function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)";
const DATA_FEED_ABI =
"function getDataInBase18() external view returns (int256 answer)";
const AGGREGATOR_ABI =
"function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)";

type Denomination = "USD" | "BTC" | "SOL";

interface TokenConfig {
name: string;
token: string;
oracle?: string;
denomination?: Denomination;
}

// Base asset price oracles on Ethereum
const BASE_ASSET_ORACLES = {
BTC: "0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c", // BTC/USD on Ethereum
SOL: "0x4ffC43a60e009B551865A93d232E33Fce9f01507", // SOL/USD on Ethereum
} as const;

const contracts: {
[chain: string]: { name: string; token: string; oracle?: string; requiresConversion?: boolean }[];
} = {
const contracts: Record<string, TokenConfig[]> = {
ethereum: [
{ name: "mTBILL", token: "0xDD629E5241CbC5919847783e6C96B2De4754e438", oracle: "0xfCEE9754E8C375e145303b7cE7BEca3201734A2B" },
{ name: "mBASIS", token: "0x2a8c22E3b10036f3AEF5875d04f8441d4188b656", oracle: "0x1615cBC603192ae8A9FF20E98dd0e40a405d76e4" },
{ name: "mBTC", token: "0x007115416AB6c266329a03B09a8aa39aC2eF7d9d", oracle: "0x9987BE0c1dc5Cd284a4D766f4B5feB4F3cb3E28e", requiresConversion: true },
{ name: "mEDGE", token: "0xbB51E2a15A9158EBE2b0Ceb8678511e063AB7a55", oracle: "0x20cd58F72cF1727a2937eB1816593390cf8d91cB" },
{ name: "mMEV", token: "0x030b69280892c888670EDCDCD8B69Fd8026A0BF3", oracle: "0x9BF00b7CFC00D6A7a2e2C994DB8c8dCa467ee359" },
{ name: "mRe7YIELD", token: "0x87C9053C819bB28e0D73d33059E1b3DA80AFb0cf", oracle: "0x7E8C632ab231479886AF1Bc02B9D646e4634Da93" },
{ name: "mF-ONE", token: "0x238a700eD6165261Cf8b2e544ba797BC11e466Ba", oracle: "0xCF4e49f5e750Af8F2f9Aa1642B68E5839D9c1C00" },
{ name: "mHYPER", token: "0x9b5528528656DBC094765E2abB79F293c21191B9", oracle: "0x92004DCC5359eD67f287F32d12715A37916deCdE" },
{ name: "mAPOLLO", token: "0x7CF9DEC92ca9FD46f8d86e7798B72624Bc116C05", oracle: "0x9aEBf5d6F9411BAc355021ddFbe9B2c756BDD358" },
{ name: "mevBTC", token: "0xb64C014307622eB15046C66fF71D04258F5963DC", oracle: "0x56814399caaEDCEE4F58D2e55DA058A81DDE744f" },
{ name: "mFARM", token: "0xA19f6e0dF08a7917F2F8A33Db66D0AF31fF5ECA6", oracle: "0x9f49B0980B141b539e2A94Ec0864Faf699fF9524" },
{
name: "mTBILL",
token: "0xDD629E5241CbC5919847783e6C96B2De4754e438",
oracle: "0xfCEE9754E8C375e145303b7cE7BEca3201734A2B",
},
{
name: "mBASIS",
token: "0x2a8c22E3b10036f3AEF5875d04f8441d4188b656",
oracle: "0x1615cBC603192ae8A9FF20E98dd0e40a405d76e4",
},
{
name: "mBTC",
token: "0x007115416AB6c266329a03B09a8aa39aC2eF7d9d",
oracle: "0x9987BE0c1dc5Cd284a4D766f4B5feB4F3cb3E28e",
denomination: "BTC",
},
{
name: "mEDGE",
token: "0xbB51E2a15A9158EBE2b0Ceb8678511e063AB7a55",
oracle: "0x20cd58F72cF1727a2937eB1816593390cf8d91cB",
},
{
name: "mMEV",
token: "0x030b69280892c888670EDCDCD8B69Fd8026A0BF3",
oracle: "0x9BF00b7CFC00D6A7a2e2C994DB8c8dCa467ee359",
},
{
name: "mRe7YIELD",
token: "0x87C9053C819bB28e0D73d33059E1b3DA80AFb0cf",
oracle: "0x7E8C632ab231479886AF1Bc02B9D646e4634Da93",
},
{
name: "mF-ONE",
token: "0x238a700eD6165261Cf8b2e544ba797BC11e466Ba",
oracle: "0xCF4e49f5e750Af8F2f9Aa1642B68E5839D9c1C00",
},
{
name: "mHYPER",
token: "0x9b5528528656DBC094765E2abB79F293c21191B9",
oracle: "0x92004DCC5359eD67f287F32d12715A37916deCdE",
},
{
name: "mAPOLLO",
token: "0x7CF9DEC92ca9FD46f8d86e7798B72624Bc116C05",
oracle: "0x9aEBf5d6F9411BAc355021ddFbe9B2c756BDD358",
},
{
name: "mevBTC",
token: "0xb64C014307622eB15046C66fF71D04258F5963DC",
oracle: "0x56814399caaEDCEE4F58D2e55DA058A81DDE744f",
denomination: "BTC",
},
{
name: "mFARM",
token: "0xA19f6e0dF08a7917F2F8A33Db66D0AF31fF5ECA6",
oracle: "0x9f49B0980B141b539e2A94Ec0864Faf699fF9524",
},
],
base: [
{ name: "mTBILL", token: "0xDD629E5241CbC5919847783e6C96B2De4754e438", oracle: "0xcbCf1e67F1988e2572a2A620321Aef2ff73369f0" },
{ name: "mBASIS", token: "0x1C2757c1FeF1038428b5bEF062495ce94BBe92b2", oracle: "0xD48D38Ec56CDB44c4281068129038A37F5Df04e5" },
{
name: "mTBILL",
token: "0xDD629E5241CbC5919847783e6C96B2De4754e438",
oracle: "0xcbCf1e67F1988e2572a2A620321Aef2ff73369f0",
},
{
name: "mBASIS",
token: "0x1C2757c1FeF1038428b5bEF062495ce94BBe92b2",
oracle: "0xD48D38Ec56CDB44c4281068129038A37F5Df04e5",
},
],
sapphire: [
{ name: "mTBILL", token: "0xDD629E5241CbC5919847783e6C96B2De4754e438" },
],
etlk: [
{ name: "mTBILL", token: "0xDD629E5241CbC5919847783e6C96B2De4754e438" },
{ name: "mBASIS", token: "0x2247B5A46BB79421a314aB0f0b67fFd11dd37Ee4" },
{ name: "mMEV", token: "0x5542F82389b76C23f5848268893234d8A63fd5c8" },
{ name: "mRe7YIELD", token: "0x733d504435a49FC8C4e9759e756C2846c92f0160" },
],
rsk: [
{ name: "mTBILL", token: "0xDD629E5241CbC5919847783e6C96B2De4754e438" },
{ name: "mBTC", token: "0xEF85254Aa4a8490bcC9C02Ae38513Cae8303FB53" },
{
name: "mBTC",
token: "0xEF85254Aa4a8490bcC9C02Ae38513Cae8303FB53",
denomination: "BTC",
},
],
plume_mainnet: [
{ name: "mTBILL", token: "0xE85f2B707Ec5Ae8e07238F99562264f304E30109", oracle: "0x73a64469E0974371005ca0f60Dfc10405613b411" },
{ name: "mBASIS", token: "0x0c78Ca789e826fE339dE61934896F5D170b66d78", oracle: "0x7588139737f32A6da49b9BB03A0a91a45603b45F" },
{ name: "mEDGE", token: "0x69020311836D29BA7d38C1D3578736fD3dED03ED", oracle: "0xa30e78AF094EFC51434693803fEE1D77f568321E" },
{ name: "mMEV", token: "0x7d611dC23267F508DE90724731Dc88CA28Ef7473", oracle: "0x06fa9188680D8487e2b743b182CCc39654211C84" },
{
name: "mTBILL",
token: "0xE85f2B707Ec5Ae8e07238F99562264f304E30109",
oracle: "0x73a64469E0974371005ca0f60Dfc10405613b411",
},
{
name: "mBASIS",
token: "0x0c78Ca789e826fE339dE61934896F5D170b66d78",
oracle: "0x7588139737f32A6da49b9BB03A0a91a45603b45F",
},
{
name: "mEDGE",
token: "0x69020311836D29BA7d38C1D3578736fD3dED03ED",
oracle: "0xa30e78AF094EFC51434693803fEE1D77f568321E",
},
{
name: "mMEV",
token: "0x7d611dC23267F508DE90724731Dc88CA28Ef7473",
oracle: "0x06fa9188680D8487e2b743b182CCc39654211C84",
},
],
katana: [
{ name: "mRE7SOL", token: "0xC6135d59F8D10c9C035963ce9037B3635170D716", oracle: "0x001b3731c706fEd93BDA240A5BF848C28ae1cC12" },
]
{
name: "mRE7SOL",
token: "0xC6135d59F8D10c9C035963ce9037B3635170D716",
oracle: "0x001b3731c706fEd93BDA240A5BF848C28ae1cC12",
denomination: "SOL",
},
],
};

const btcToUsdOracleEth = "0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c";

async function getBtcToUsdPrice(timestamp: number): Promise<number> {
async function getBaseAssetPrices(
timestamp: number
): Promise<Record<string, number>> {
const api = await getApi("ethereum", timestamp);
const response = await api.call({ abi: AGGREGATOR_ABI, target: btcToUsdOracleEth });
return response.answer / 1e8;
const prices: Record<string, number> = {};

const calls = await api.multiCall({
abi: AGGREGATOR_ABI,
calls: Object.values(BASE_ASSET_ORACLES),
});

Object.keys(BASE_ASSET_ORACLES).forEach((asset, i) => {
if (calls[i]?.answer) {
prices[asset] = Number(calls[i].answer) / 1e8;
}
});

return prices;
}

async function getTokenPrices(chain: string, timestamp: number, ethereumPrices: Record<string, number>): Promise<Write[]> {
async function getTokenPrices(
chain: string,
timestamp: number,
baseAssetPrices: Record<string, number>,
ethereumPrices: Record<string, number>
): Promise<Write[]> {
const api = await getApi(chain, timestamp);
const tokens = contracts[chain] || [];
const btcToUsdPrice = await getBtcToUsdPrice(timestamp);

const tokensWithOracles = tokens.filter(t => t.oracle);
const tokensWithoutOracles = tokens.filter(t => !t.oracle);
const tokensWithOracles = tokens.filter((t) => t.oracle);
const tokensWithoutOracles = tokens.filter((t) => !t.oracle);

const prices: Record<string, { price: number }> = {};

let prices: Record<string, { price: number }> = {};
if (tokensWithOracles.length > 0) {
const dataFeedResponses = await api.multiCall({
const oracleResponses = await api.multiCall({
abi: DATA_FEED_ABI,
calls: tokensWithOracles.map(({ oracle }) => oracle as string),
calls: tokensWithOracles.map((t) => t.oracle as string),
});

prices = tokensWithOracles.reduce((acc, { token, requiresConversion, name }, i) => {
const rawVal = dataFeedResponses[i];
if (rawVal !== null && rawVal !== undefined) {
const price = (rawVal / 1e18) * (requiresConversion ? btcToUsdPrice : 1);
acc[token] = { price };
tokensWithOracles.forEach((token, i) => {
const rawPrice = oracleResponses[i];
if (rawPrice !== null && rawPrice !== undefined) {
let usdPrice = Number(rawPrice) / 1e18;

// Convert to USD if denominated in another asset
if (token.denomination && token.denomination !== "USD") {
const basePrice = baseAssetPrices[token.denomination];
if (basePrice) {
usdPrice = usdPrice * basePrice;
}
}

prices[token.token] = { price: usdPrice };

if (chain === 'ethereum') {
ethereumPrices[name] = price;
// Store Ethereum prices for other chains to use as fallback
if (chain === "ethereum") {
ethereumPrices[token.name] = usdPrice;
}
}
return acc;
}, {} as Record<string, { price: number }>);
});
}

for (const t of tokensWithoutOracles) {
const { name, token } = t;
const fallbackPrice = ethereumPrices[name];
tokensWithoutOracles.forEach((token) => {
const fallbackPrice = ethereumPrices[token.name];
if (fallbackPrice !== undefined) {
prices[token] = { price: fallbackPrice };
prices[token.token] = { price: fallbackPrice };
}
}
});

return getWrites({
chain,
Expand All @@ -101,21 +222,32 @@ async function getTokenPrices(chain: string, timestamp: number, ethereumPrices:
}

export async function midas(timestamp: number = 0): Promise<Write[]> {
const baseAssetPrices = await getBaseAssetPrices(timestamp);

const ethereumPrices: Record<string, number> = {};

const chains = Object.keys(contracts);
const ethereumIndex = chains.indexOf('ethereum');
const ethereumIndex = chains.indexOf("ethereum");

let ethereumWrites: Write[] = [];
let otherChainWrites: Write[] = [];

if (ethereumIndex !== -1) {
ethereumWrites = await getTokenPrices('ethereum', timestamp, ethereumPrices);
ethereumWrites = await getTokenPrices(
"ethereum",
timestamp,
baseAssetPrices,
ethereumPrices
);
chains.splice(ethereumIndex, 1);
}

if (chains.length > 0) {
const results = await Promise.all(chains.map(c => getTokenPrices(c, timestamp, ethereumPrices)));
const results = await Promise.all(
chains.map((chain) =>
getTokenPrices(chain, timestamp, baseAssetPrices, ethereumPrices)
)
);
otherChainWrites = results.flat();
}

Expand Down