diff --git a/package.json b/package.json index 7117f6f..0003b53 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,6 @@ "@gearbox-protocol/eslint-config": "2.0.0-next.2", "@gearbox-protocol/prettier-config": "2.0.0-next.0", "@gearbox-protocol/sdk-gov": "^1.31.2", - "@redstone-finance/protocol": "^0.3.6", - "@redstone-finance/sdk": "^0.3.6", "@semantic-release/exec": "^6.0.3", "eslint": "^8.56.0", "ethers": "^6.0.0", diff --git a/src/adapter/index.ts b/src/adapter/index.ts index adcc92b..bd6a695 100644 --- a/src/adapter/index.ts +++ b/src/adapter/index.ts @@ -14,25 +14,27 @@ async function tvl( block: number, _: unknown, { api }: ApiParameter, -) { +): Promise { // Pool TVL (Current token balances) const tokensAndOwners = await getPools(block, api); - // CreditAccounts TVL + // v1 and v2: + // return sum of balances of all credit accounts by credit manager in underlying const v1Balances = await getV1TVL(block, api); const v2Balances = await getV2TVL(block, api); + // v3 is different: + // return balances of each credit account const v3Balances = await getV3TVL(block, api); // Merge all balances for each token [...v1Balances, ...v2Balances, ...v3Balances].forEach(i => { api.add(i.token, i.bal); - tokensAndOwners.push([i.token, i.addr]); }); - return api.sumTokens({ tokensAndOwners }); + await api.sumTokens({ tokensAndOwners }); } -module.exports = { +export default { hallmarks: [[1666569600, "LM begins"]], ethereum: { tvl, diff --git a/src/adapter/pools/index.ts b/src/adapter/pools/index.ts index efdcb18..795869e 100644 --- a/src/adapter/pools/index.ts +++ b/src/adapter/pools/index.ts @@ -3,7 +3,10 @@ import type { ChainApi } from "@defillama/sdk"; import { ADDRESS_PROVIDER_V3 } from "../constants"; import { poolAbis } from "./abi"; -export async function getPools(block: number, api: ChainApi) { +export async function getPools( + block: number, + api: ChainApi, +): Promise> { const contractsRegisterAddr: string = await api.call({ block, abi: poolAbis["getAddressOrRevert"], diff --git a/src/adapter/v1/index.ts b/src/adapter/v1/index.ts index b5aa066..3fc5bec 100644 --- a/src/adapter/v1/index.ts +++ b/src/adapter/v1/index.ts @@ -5,9 +5,17 @@ import { ADDRESS_PROVIDER_V3 } from "../constants"; // @ts-ignore import { getLogs } from "../helper/cache/getLogs"; import { v1Abis } from "./abi"; -import type { CreditAccountEvent, CreditManagerData, Log } from "./types"; +import type { + CreditAccountEvent, + CreditManagerData, + Log, + TokenAndOwner, +} from "./types"; -export async function getV1TVL(block: number, api: ChainApi) { +export async function getV1TVL( + block: number, + api: ChainApi, +): Promise { const creditManagers = await getCreditManagersV1(block, api); // Silently throw if no V1 CAs available diff --git a/src/adapter/v1/types.ts b/src/adapter/v1/types.ts index f0c72cf..027aa6a 100644 --- a/src/adapter/v1/types.ts +++ b/src/adapter/v1/types.ts @@ -20,3 +20,9 @@ export interface CreditManagerData { addr: string; underlying: string; } + +export interface TokenAndOwner { + addr: string; + token: string; + bal: string; +} diff --git a/src/adapter/v2/index.ts b/src/adapter/v2/index.ts index 024e609..2985e23 100644 --- a/src/adapter/v2/index.ts +++ b/src/adapter/v2/index.ts @@ -1,5 +1,5 @@ import type { ChainApi } from "@defillama/sdk"; -import { Contract, id } from "ethers"; +import { Contract } from "ethers"; import { ADDRESS_PROVIDER_V3 } from "../constants"; // @ts-ignore @@ -10,9 +10,13 @@ import type { CreditManagerData, Log, ParsedLog, + TokenAndOwner, } from "./types"; -export async function getV2TVL(block: number, api: ChainApi) { +export async function getV2TVL( + block: number, + api: ChainApi, +): Promise { // Get Current CMs const creditManagers = await getCreditManagersV210(block, api); // Silently throw if no V2 CAs available diff --git a/src/adapter/v2/types.ts b/src/adapter/v2/types.ts index 2cffeef..6fac80f 100644 --- a/src/adapter/v2/types.ts +++ b/src/adapter/v2/types.ts @@ -81,3 +81,9 @@ export interface CreditAccountEvent { ca: string | null; cf: string; } + +export interface TokenAndOwner { + addr: string; + token: string; + bal: string; +} diff --git a/src/adapter/v3/index.ts b/src/adapter/v3/index.ts index 0cadf9e..924d053 100644 --- a/src/adapter/v3/index.ts +++ b/src/adapter/v3/index.ts @@ -2,10 +2,16 @@ import type { ChainApi } from "@defillama/sdk"; import { ADDRESS_PROVIDER_V3 } from "../constants"; import { v3Abis } from "./abi"; -import { getPriceUpdates } from "./redstone"; -import type { CreditAccountData, CreditManagerData } from "./types"; +import type { + CreditAccountData, + CreditManagerData, + TokenAndOwner, +} from "./types"; -export async function getV3TVL(block: number, api: ChainApi) { +export async function getV3TVL( + block: number, + api: ChainApi, +): Promise { const dc300 = await api.call({ abi: v3Abis["getAddressOrRevert"], target: ADDRESS_PROVIDER_V3, @@ -26,11 +32,7 @@ export async function getV3TVL(block: number, api: ChainApi) { creditManagers.map(cm => getV3CAs(dc300, cm.addr, block, api)), ); - return creditManagers.map((cm, i) => ({ - addr: cm.addr, - token: cm.underlying, - bal: caValues[i], - })); + return caValues.flat(); } export async function getCreditManagersV3( @@ -51,29 +53,26 @@ async function getV3CAs( creditManager: string, block: number, api: ChainApi, -): Promise { - let accs: CreditAccountData[] = await api.call({ +): Promise { + const accs: CreditAccountData[] = await api.call({ // IDataCompressorV3_00__factory.createInterface().getFunction("getCreditAccountsByCreditManager").format(ethers.utils.FormatTypes.full) target: dc300, abi: v3Abis["getCreditAccountsByCreditManager"], params: [creditManager, []] as any, block, }); - const priceFeedsNeeded = new Set(); - accs.forEach(acc => { - acc.priceFeedsNeeded.forEach(pf => priceFeedsNeeded.add(pf.toLowerCase())); - }); - const priceUpdates = await getPriceUpdates(priceFeedsNeeded, block, api); - accs = await api.call({ - target: dc300, - abi: v3Abis["getCreditAccountsByCreditManager"], - params: [creditManager, priceUpdates] as any, - block, - }); - let totalValue = 0n; - accs.forEach(acc => { - totalValue += BigInt(acc.totalValue); - }); - - return totalValue.toString(); + const result: TokenAndOwner[] = []; + for (const acc of accs) { + for (const { balance, token } of acc.balances) { + // reduce noize + if (balance !== "0" && balance !== "1") { + result.push({ + addr: acc.addr, + bal: balance, + token: token, + }); + } + } + } + return result; } diff --git a/src/adapter/v3/redstone/abi.ts b/src/adapter/v3/redstone/abi.ts deleted file mode 100644 index a1022a6..0000000 --- a/src/adapter/v3/redstone/abi.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const redstoneAbis = { - getAddressOrRevert: - "function getAddressOrRevert(bytes32 key, uint256 _version) view returns (address result)", - dataFeedId: "function dataFeedId() external view returns (bytes32)", - priceFeedsRaw: - "function priceFeedsRaw(address token, bool reserve) external view returns (address)", -}; diff --git a/src/adapter/v3/redstone/index.ts b/src/adapter/v3/redstone/index.ts deleted file mode 100644 index 3b27ed7..0000000 --- a/src/adapter/v3/redstone/index.ts +++ /dev/null @@ -1,246 +0,0 @@ -import type { ChainApi } from "@defillama/sdk"; -import { toUtf8String } from "ethers"; - -import { ADDRESS_PROVIDER_V3 } from "../../constants"; -// @ts-ignore -import { getLogs } from "../helper/cache/getLogs"; -import { redstoneAbis } from "./abi"; -import { getRedstonePayloadForManualUsage } from "./redstone-lib"; -import type { PriceOnDemand, RedstoneData, SetPriceFeedArgs } from "./types"; - -const REDSTONE_DICTIONARY: Record = { - STETH: "stETH", -}; - -export async function getPriceUpdates( - tokens: Set | undefined, - block: number, - api: ChainApi, -): Promise { - const redstoneFeeds = await getRedstoneFeeds(tokens, block, api); - return Promise.all( - Object.entries(redstoneFeeds).map(([t, f]) => - getRedstonePayloadForManualUsage(t, { - dataServiceId: f.dataServiceId, - dataId: f.dataId, - signersThreshold: 5, - }), - ), - ); -} - -async function getRedstoneFeeds( - tokens: Set | undefined, - block: number, - api: ChainApi, -): Promise> { - const result: Record = {}; - const priceOracleV3Addr: string = await api.call({ - abi: redstoneAbis["getAddressOrRevert"], - target: ADDRESS_PROVIDER_V3, - params: [ - // cast format-bytes32-string "PRICE_ORACLE" - "0x50524943455f4f5241434c450000000000000000000000000000000000000000", - 300, - ], - block, - }); - const feeds = await (tokens - ? getPriceFeedsForTokens(priceOracleV3Addr, Array.from(tokens), block, api) - : getAllPriceFeeds(priceOracleV3Addr, block, api)); - - const dataFeedIds = await api.multiCall({ - abi: redstoneAbis["dataFeedId"], - calls: feeds.map(([_, priceFeed]) => ({ - target: priceFeed, - })), - permitFailure: true, - block, - }); - for (let i = 0; i < feeds.length; i++) { - const [token, priceFeed] = feeds[i]; - if (dataFeedIds[i]) { - const id = toUtf8String(dataFeedIds[i]) - .trim() - .replace(/\u0000/g, ""); - result[token] = { - dataId: REDSTONE_DICTIONARY[id] ?? id, - // TODO: it's better to get it from sdk-gov, but now all of them have this value - dataServiceId: "redstone-primary-prod", - priceFeed, - }; - } - } - return result; -} - -async function getPriceFeeds( - priceOracleV3Addr: string, - eventAbi: string, - block: number, - api: ChainApi, -): Promise> { - const logs: SetPriceFeedArgs[] = await getLogs({ - api, - eventAbi, - fromBlock: 18797638, // price oracle v3 deployment block - toBlock: block, - target: priceOracleV3Addr, - onlyArgs: true, - extraKey: eventAbi.replace("event", "").trim().split("(")[0], - }); - const result: Record = {}; - for (const l of logs) { - result[l[0].toLowerCase()] = l[1].toLowerCase(); - } - return Object.entries(result); -} - -async function getAllPriceFeeds( - priceOracleV3Addr: string, - block: number, - api: ChainApi, -): Promise> { - const [mainFeeds, reserveFeeds] = await Promise.all([ - getPriceFeeds( - priceOracleV3Addr, - "event SetPriceFeed(address indexed token, address indexed priceFeed, uint32 stalenessPeriod, bool skipCheck, bool trusted)", - block, - api, - ), - getPriceFeeds( - priceOracleV3Addr, - "event SetReservePriceFeed(address indexed token, address indexed priceFeed, uint32 stalenessPeriod, bool skipCheck)", - block, - api, - ), - ]); - return [...mainFeeds, ...reserveFeeds]; -} - -async function getPriceFeedsForTokens( - priceOracleV3Addr: string, - tokens: string[], - block: number, - api: ChainApi, -): Promise> { - const feeds: string[] = await api.multiCall({ - abi: redstoneAbis["priceFeedsRaw"], - calls: [ - ...tokens.map(t => ({ - target: priceOracleV3Addr, - params: [t, false] as any, - })), - ...tokens.map(t => ({ - target: priceOracleV3Addr, - params: [t, true] as any, - })), - ], - permitFailure: true, - block, - }); - const result: Array<[token: string, priceFeed: string]> = []; - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i].toLowerCase(); - const [main, reserve] = [feeds[i], feeds[i + tokens.length]]; - if (main) { - result.push([token, main.toLowerCase()]); - } - if (reserve) { - result.push([token, reserve.toLowerCase()]); - } - } - return result; -} - -// async function updateRedstone(tokens: string[]): Promise { -// const redstoneFeeds: Array = []; - -// for (const t of tokens) { -// const token = t.toLowerCase(); -// const symbol = tokenSymbolByAddress[token]; -// if (!symbol) { -// this.log?.warn( -// `Failed price feed for token ${token} which is not found in SDK`, -// ); -// continue; -// } - -// const feed = priceFeedsByToken[symbol]; -// const entry = feed?.AllNetworks ?? feed?.Mainnet; -// if (!entry) { -// this.log?.warn( -// `Cannot find price feed for token ${symbol} (${token}) in SDK`, -// ); -// continue; -// } -// // it is technically possible to have both main and reserve price feeds to be redstone -// // but from practical standpoint this makes no sense: so use else-if, not if-if -// if (entry.Main?.type === PriceFeedType.REDSTONE_ORACLE) { -// redstoneFeeds.push({ token, ...entry.Main }); -// this.log?.debug( -// `need to update main redstone price feed ${entry.Main.dataId} in ${entry.Main.dataServiceId} for token ${symbol} (${token})`, -// ); -// } else if (entry?.Reserve?.type === PriceFeedType.REDSTONE_ORACLE) { -// redstoneFeeds.push({ token, ...entry.Reserve }); -// this.log?.debug( -// `need to update reserve redstone price feed ${entry.Reserve.dataId} in ${entry.Reserve.dataServiceId} for token ${symbol} (${token})`, -// ); -// } else { -// this.log?.warn( -// `non-restone price feed failed for token ${symbol} (${token}): ${JSON.stringify( -// entry, -// )}`, -// ); -// } -// } - -// const result = await Promise.all( -// redstoneFeeds.map(f => -// getRedstonePayloadForManualUsage( -// f.token, -// f.dataServiceId, -// f.dataId, -// f.signersThreshold, -// ), -// ), -// ); - -// return result; -// } - -// async function getRedstonePayloadForManualUsage( -// token: string, -// { dataServiceId, dataId, signersThreshold }: RedstonePriceFeed, -// ): Promise { -// const dataPayload = await new DataServiceWrapper({ -// dataServiceId, -// dataFeeds: [dataId], -// uniqueSignersCount: signersThreshold, -// }).prepareRedstonePayload(true); - -// const parser = new RedstonePayloadParser(getBytes(`0x${dataPayload}`)); -// const { signedDataPackages } = parser.parse(); - -// let dataPackageIndex = 0; -// let ts = 0; -// for (const signedDataPackage of signedDataPackages) { -// const newTimestamp = -// signedDataPackage.dataPackage.timestampMilliseconds / 1000; - -// if (dataPackageIndex === 0) { -// ts = newTimestamp; -// } else if (ts !== newTimestamp) { -// throw new Error("Timestamps are not equal"); -// } - -// ++dataPackageIndex; -// } - -// const result = ethers.AbiCoder.defaultAbiCoder().encode( -// ["uint256", "bytes"], -// [ts, getBytes(`0x${dataPayload}`)], -// ); - -// return { token, callData: result }; -// } diff --git a/src/adapter/v3/redstone/redstone-lib.ts b/src/adapter/v3/redstone/redstone-lib.ts deleted file mode 100644 index 0166205..0000000 --- a/src/adapter/v3/redstone/redstone-lib.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { PriceOnDemand, RedstonePriceFeed } from "./types"; - -export declare function getRedstonePayloadForManualUsage( - token: string, - { dataServiceId, dataId, signersThreshold }: RedstonePriceFeed, -): Promise; diff --git a/src/adapter/v3/redstone/types.ts b/src/adapter/v3/redstone/types.ts deleted file mode 100644 index 50611ee..0000000 --- a/src/adapter/v3/redstone/types.ts +++ /dev/null @@ -1,24 +0,0 @@ -export interface PriceOnDemand { - token: string; - callData: string; -} - -export interface RedstoneData { - priceFeed: string; - dataServiceId: string; - dataId: string; -} - -export interface RedstonePriceFeed { - dataServiceId: string; - dataId: string; - signersThreshold: number; - historicalTimestamp?: number; -} - -export type SetPriceFeedArgs = [ - to: string, - priceFeed: string, - stalenessPeriod: bigint, - skipCheck: boolean, -]; diff --git a/src/adapter/v3/types.ts b/src/adapter/v3/types.ts index 7ef7c7d..6588925 100644 --- a/src/adapter/v3/types.ts +++ b/src/adapter/v3/types.ts @@ -1,30 +1,29 @@ -import type { TokenBalanceStructOutput } from "../../../../deploy-v3/types/DataCompressorV3"; export interface CreditManagerData { addr: string; name: string; - cfVersion: bigint; + cfVersion: string; creditFacade: string; creditConfigurator: string; underlying: string; pool: string; - totalDebt: bigint; - totalDebtLimit: bigint; - baseBorrowRate: bigint; - minDebt: bigint; - maxDebt: bigint; - availableToBorrow: bigint; + totalDebt: string; + totalDebtLimit: string; + baseBorrowRate: string; + minDebt: string; + maxDebt: string; + availableToBorrow: string; collateralTokens: string[]; adapters: ContractAdapterData[]; - liquidationThresholds: bigint[]; + liquidationThresholds: string[]; isDegenMode: boolean; degenNFT: string; - forbiddenTokenMask: bigint; - maxEnabledTokensLength: bigint; - feeInterest: bigint; - feeLiquidation: bigint; - liquidationDiscount: bigint; - feeLiquidationExpired: bigint; - liquidationDiscountExpired: bigint; + forbiddenTokenMask: string; + maxEnabledTokensLength: string; + feeInterest: string; + feeLiquidation: string; + liquidationDiscount: string; + feeLiquidationExpired: string; + liquidationDiscountExpired: string; quotas: QuotaInfoData[]; lirm: LinearModelData; isPaused: boolean; @@ -37,22 +36,22 @@ export interface ContractAdapterData { export interface QuotaInfoData { token: string; - rate: bigint; - quotaIncreaseFee: bigint; - totalQuoted: bigint; - limit: bigint; + rate: string; + quotaIncreaseFee: string; + totalQuoted: string; + limit: string; isActive: boolean; } export interface LinearModelData { interestModel: string; - version: bigint; - U_1: bigint; - U_2: bigint; - R_base: bigint; - R_slope1: bigint; - R_slope2: bigint; - R_slope3: bigint; + version: string; + U_1: string; + U_2: string; + R_base: string; + R_slope1: string; + R_slope2: string; + R_slope3: string; isBorrowingMoreU2Forbidden: boolean; } @@ -65,33 +64,39 @@ export interface CreditAccountData { cmName: string; creditFacade: string; underlying: string; - debt: bigint; - cumulativeIndexLastUpdate: bigint; - cumulativeQuotaInterest: bigint; - accruedInterest: bigint; - accruedFees: bigint; - totalDebtUSD: bigint; - totalValue: bigint; - totalValueUSD: bigint; - twvUSD: bigint; - enabledTokensMask: bigint; - healthFactor: bigint; - baseBorrowRate: bigint; - aggregatedBorrowRate: bigint; + debt: string; + cumulativeIndexLastUpdate: string; + cumulativeQuotaInterest: string; + accruedInterest: string; + accruedFees: string; + totalDebtUSD: string; + totalValue: string; + totalValueUSD: string; + twvUSD: string; + enabledTokensMask: string; + healthFactor: string; + baseBorrowRate: string; + aggregatedBorrowRate: string; balances: TokenBalance[]; - since: bigint; - cfVersion: bigint; - expirationDate: bigint; + since: string; + cfVersion: string; + expirationDate: string; activeBots: string[]; } export interface TokenBalance { token: string; - balance: bigint; + balance: string; isForbidden: boolean; isEnabled: boolean; isQuoted: boolean; - quota: bigint; - quotaRate: bigint; - quotaCumulativeIndexLU: bigint; + quota: string; + quotaRate: string; + quotaCumulativeIndexLU: string; +} + +export interface TokenAndOwner { + addr: string; + token: string; + bal: string; } diff --git a/src/redstone-lib/ethers-v5-lib-utils-shim.ts b/src/redstone-lib/ethers-v5-lib-utils-shim.ts deleted file mode 100644 index 13e7348..0000000 --- a/src/redstone-lib/ethers-v5-lib-utils-shim.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* eslint-disable no-extend-native */ -import { - AbiCoder, - decodeBase64, - decodeBytes32String, - encodeBase64, - encodeBytes32String, - getBytes, - hexlify, - isBytesLike, - Signature, - SigningKey, - toBeHex, -} from "ethers"; - -export const arrayify = getBytes; -export const defaultAbiCoder = AbiCoder.defaultAbiCoder(); -export const base64 = { encode: encodeBase64, decode: decodeBase64 }; -export const formatBytes32String = encodeBytes32String; -export const parseBytes32String = decodeBytes32String; - -export const splitSignature = (sig: any) => { - if (isBytesLike(sig)) { - return Signature.from(hexlify(sig)); - } - return Signature.from(sig); -}; -export const joinSignature = (sig: any) => Signature.from(sig).serialized; - -function addSlice(array: Uint8Array): Uint8Array { - if (array.slice) { - return array; - } - - array.slice = function () { - const args = Array.prototype.slice.call(arguments); - return addSlice(new Uint8Array(Array.prototype.slice.apply(array, args))); - }; - - return array; -} - -export function concat(items: ReadonlyArray): Uint8Array { - const objects = items.map(item => arrayify(item)); - const length = objects.reduce((accum, item) => accum + item.length, 0); - - const result = new Uint8Array(length); - - objects.reduce((offset, object) => { - result.set(object, offset); - return offset + object.length; - }, 0); - - return addSlice(result); -} - -export function zeroPad(value: any, length: number): Uint8Array { - // eslint-disable-next-line no-param-reassign - value = arrayify(value); - - if (value.length > length) { - throw new Error("value out of range"); - } - - const result = new Uint8Array(length); - result.set(value, length - value.length); - return addSlice(result); -} - -export function recoverPublicKey(digest: any, signature: any): string { - return SigningKey.recoverPublicKey(digest, signature); -} - -export { - computeAddress, - hexlify, - isHexString, - keccak256, - parseUnits, - sha256, - SigningKey, - toUtf8Bytes, - toUtf8String, - verifyMessage, -} from "ethers"; - -// @ts-ignore -BigInt.prototype.toHexString = function () { - return toBeHex(this); -}; diff --git a/src/redstone-lib/index.ts b/src/redstone-lib/index.ts deleted file mode 100644 index f97fda7..0000000 --- a/src/redstone-lib/index.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { BigNumber } from "@ethersproject/bignumber"; -import type { SignedDataPackage } from "@redstone-finance/protocol"; -import { RedstonePayload } from "@redstone-finance/protocol"; -import { - requestDataPackages, - resolveDataServiceUrls, -} from "@redstone-finance/sdk"; -import { AbiCoder, getBytes } from "ethers"; - -import type { PriceOnDemand, RedstonePriceFeed } from "./types"; - -// This constant is here solely to make bundler bundle BigNumber -// see tsup.mjs for more info -export const _makeBundlerIncludeBigNumber = BigNumber.from(0); - -/** - * This is bits of `DataServiceWrapper({...}).prepareRedstonePayload(true) extracted into single function to minimize bundled dependencies - * https://github.com/redstone-finance/redstone-oracles-monorepo/blob/main/packages/evm-connector/src/wrappers/DataServiceWrapper.ts - * @param data - * @returns - */ -export async function getRedstonePayloadForManualUsage( - token: string, - feed: RedstonePriceFeed, -): Promise { - const signedDataPackages = await getDataPackagesForPayload(feed); - const dataPayload = await prepareRedstonePayload( - signedDataPackages, - feed.dataServiceId, - ); - - let dataPackageIndex = 0; - let ts = 0; - for (const signedDataPackage of signedDataPackages) { - const newTimestamp = - signedDataPackage.dataPackage.timestampMilliseconds / 1000; - - if (dataPackageIndex === 0) { - ts = newTimestamp; - } else if (ts !== newTimestamp) { - throw new Error("Timestamps are not equal"); - } - - ++dataPackageIndex; - } - - const result = AbiCoder.defaultAbiCoder().encode( - ["uint256", "bytes"], - [ts, getBytes(`0x${dataPayload}`)], - ); - - return { token, callData: result }; -} - -async function prepareRedstonePayload( - signedDataPackages: SignedDataPackage[], - dataServiceId: string, -): Promise { - let unsignedMetadata = getUnsignedMetadata(dataServiceId); - - const originalPayload = RedstonePayload.prepare( - signedDataPackages, - unsignedMetadata, - ); - - // Calculating the number of bytes in the hex representation of payload - // We divide by 2, beacuse 2 symbols in a hex string represent one byte - const originalPayloadLength = originalPayload.length / 2; - - // Number of bytes that we want to add to unsigned metadata so that - // payload byte size becomes a multiplicity of 32 - const bytesToAdd = 32 - (originalPayloadLength % 32); - - // Adding underscores to the end of the metadata string, each underscore - // uses one byte in UTF-8 - unsignedMetadata += "_".repeat(bytesToAdd); - - return RedstonePayload.prepare(signedDataPackages, unsignedMetadata); -} - -async function getDataPackagesForPayload({ - dataServiceId, - dataId, - signersThreshold, -}: RedstonePriceFeed): Promise { - const dpResponse = await requestDataPackages({ - dataServiceId, - dataFeeds: [dataId], - uniqueSignersCount: signersThreshold, - urls: resolveDataServiceUrls(dataServiceId), - }); - return Object.values(dpResponse).flat() as SignedDataPackage[]; -} - -function getUnsignedMetadata(dataServiceId: string): string { - return `${Date.now()}#0.3.6#${dataServiceId}`; -} diff --git a/src/redstone-lib/types.ts b/src/redstone-lib/types.ts deleted file mode 100644 index addac03..0000000 --- a/src/redstone-lib/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface RedstonePriceFeed { - dataServiceId: string; - dataId: string; - signersThreshold: number; -} - -export interface PriceOnDemand { - token: string; - callData: string; -} diff --git a/tsup.mjs b/tsup.mjs index f44aa35..5f0b1b0 100644 --- a/tsup.mjs +++ b/tsup.mjs @@ -1,5 +1,4 @@ -import { copyFile, readFile, rm, writeFile } from "node:fs/promises"; -import { dirname, resolve } from "node:path"; +import { readFile, writeFile } from "node:fs/promises"; import { build } from "tsup"; @@ -21,62 +20,9 @@ const BANNER = `/** */ async function addBanner(file) { const content = await readFile(file, "utf-8"); - await writeFile( - file, - content.replace("'use strict';", "'use strict';\n" + BANNER), - "utf-8", - ); + await writeFile(file, content.replace("'use strict';", BANNER), "utf-8"); } -const ethersLibUtilsV5Shim = { - name: "ethersLibUtilsV5Shim", - setup(build) { - build.onResolve({ filter: /^ethers/ }, async args => { - // ethers v5 is required by redstone, but is not available in defillama - // reconstruct V5 ethers/lib/utils using methods from ethers V6 - if (args.path === "ethers/lib/utils") { - const result = await build.resolve("./ethers-v5-lib-utils-shim.ts", { - kind: "import-statement", - resolveDir: resolve(process.cwd(), "src/redstone-lib"), - }); - if (result.errors.length > 0) { - return { errors: result.errors }; - } - return { path: result.path, external: false }; - } - if (args.path === "ethers") { - return { path: args.path, external: true }; - } - return { errors: "unknown path" }; - }); - }, -}; - -await build({ - entry: ["src/redstone-lib/index.ts"], - outDir: "dist/redstone-lib", - splitting: false, - sourcemap: false, - clean: true, - treeshake: true, - target: "node18", - external: ["axios", "bignumber.js", "bn.js"], - esbuildPlugins: [ethersLibUtilsV5Shim], - metafile: true, -}); - -const REDSTONE_LIB = "dist/redstone-lib/index.js"; - -// Redstone will use `import {BigNumber} from "ethers"` -// this does not work with ethers-6 in defillama -// Following replacement allows redstone to use BigNumber from `@ethersproject/bignumber`, which is available thanks to magic _makeBundlerIncludeBigNumber contant -let redstoneLibContent = await readFile(REDSTONE_LIB, "utf-8"); -redstoneLibContent = redstoneLibContent - .replaceAll("ethers.BigNumber", "BigNumber") - .replaceAll("ethers_1.BigNumber", "BigNumber"); -await writeFile(REDSTONE_LIB, redstoneLibContent, "utf-8"); -await addBanner(REDSTONE_LIB); - await build({ entry: ["src/adapter/index.ts"], outDir: "dist/adapter", @@ -85,14 +31,6 @@ await build({ clean: true, treeshake: true, target: "node18", - external: [ - "../helper/cache/getLogs", - "./redstone-lib", - "ethers", - "@defillama/sdk", - ], + external: ["../helper/cache/getLogs", "ethers", "@defillama/sdk"], }); await addBanner("dist/adapter/index.js"); - -await copyFile(REDSTONE_LIB, "dist/adapter/redstone-lib.js"); -await rm(dirname(REDSTONE_LIB), { force: true, recursive: true });