From 7579cebe78a8d6f076b704783859e9e18e647498 Mon Sep 17 00:00:00 2001 From: daiwanwei Date: Wed, 9 Aug 2023 16:25:45 +0800 Subject: [PATCH 1/8] feat: add teahouse yield adapter --- src/adaptors/teahouse-v3/config.ts | 9 ++ src/adaptors/teahouse-v3/index.js | 12 ++ src/adaptors/teahouse-v3/utils.ts | 184 +++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 src/adaptors/teahouse-v3/config.ts create mode 100644 src/adaptors/teahouse-v3/index.js create mode 100644 src/adaptors/teahouse-v3/utils.ts diff --git a/src/adaptors/teahouse-v3/config.ts b/src/adaptors/teahouse-v3/config.ts new file mode 100644 index 0000000000..39364ee92a --- /dev/null +++ b/src/adaptors/teahouse-v3/config.ts @@ -0,0 +1,9 @@ +const TEAHOUSE_VAULT_STAT_API_URL = 'https://vault-api.teahouse.finance'; +const TEAHOUSE_VAULT_CONTENT_API_URL = 'https://vault-content-api.teahouse.finance'; +const TEAHOUSE_WEBSITE_URL = 'https://vault.teahouse.finance'; + +module.exports = { + TEAHOUSE_VAULT_STAT_API_URL, + TEAHOUSE_VAULT_CONTENT_API_URL, + TEAHOUSE_WEBSITE_URL +} diff --git a/src/adaptors/teahouse-v3/index.js b/src/adaptors/teahouse-v3/index.js new file mode 100644 index 0000000000..f5feb790af --- /dev/null +++ b/src/adaptors/teahouse-v3/index.js @@ -0,0 +1,12 @@ +const {topLvl} = require("./utils"); + + +const main = async (timestamp = null) => { + const data = await topLvl(timestamp) + return data; +}; + +module.exports = { + timetravel: false, + apy: main, +}; diff --git a/src/adaptors/teahouse-v3/utils.ts b/src/adaptors/teahouse-v3/utils.ts new file mode 100644 index 0000000000..4147bc11c0 --- /dev/null +++ b/src/adaptors/teahouse-v3/utils.ts @@ -0,0 +1,184 @@ +const utils = require('../utils'); +const {TEAHOUSE_VAULT_STAT_API_URL, TEAHOUSE_VAULT_CONTENT_API_URL, TEAHOUSE_WEBSITE_URL} = require('./config'); +const bn= require('bignumber.js'); + +function generateVaultURL(chainName: string, vaultAddress: string): string { + return `${TEAHOUSE_WEBSITE_URL}/${chainName}/${vaultAddress}`; +} + +async function getVaultData(type:string): Promise { + const statUrl = `${TEAHOUSE_VAULT_STAT_API_URL}/vaults/type/${type}` + let {vaults: stats} = await utils.getData(statUrl) + const vaults = await addVaultInfo(stats) + return vaults +} + +async function addVaultInfo(stats: any[]): Promise { + const vaultMap = new Map(); + stats.forEach((el) => { + const chain = el.network || "" + //key format: chain:address + const key=`${chain}:${el.address}` + vaultMap.set(key, el); + }) + const contentUrl = `${TEAHOUSE_VAULT_CONTENT_API_URL}/vaults` + let infoList = []; + let {vaults} = await utils.getData(contentUrl) + vaults.forEach((el) => { + //key format: chain:address + const address = el.share?.address || "" + const chain = el.chain || "" + const key=`${chain}:${address}` + if (vaultMap.has(key)) { + infoList.push(mergeToVault(el, vaultMap.get(key))); + } + }); + return infoList +} + +function mergeToVault(info: any, stat: any): Vault { + const chain = info.chain || "" + const contract = info.share + const address = contract?.address || "" + const name = info.name || "" + const vaultName=info.feeTier? `${name} (${info.feeTier})`:name; + const vaultMeta = info.feeTier || "" + const url = generateVaultURL(chain, address) + const tokens = [] + if (stat.latestInfo?.token0) + tokens.push(getTokenData("0", info, stat)) + if (stat.latestInfo?.token1) + tokens.push(getTokenData("1", info, stat)) + const data = { + address, chain, name:vaultName,vaultMeta, underlyingTokens: tokens, url, + isAsset0Main: info.isAsset0Main, + } + return data +} + +function getTokenData(num: string, info: any, stat: any): UnderlyingToken { + const asset = `asset${num}` + const tokenInfo = info[asset] || {} + const statInfo = stat.latestInfo || {} + const token = `token${num}` + const tokenStat = statInfo[token] || {} + const data = { + name: token, + address: tokenInfo.address || "", + symbol: tokenInfo.symbol || "", + decimals: tokenInfo.decimals || 0, + tvl: tokenStat.tvl || "0", + shareTokenApr: tokenStat.shareTokenApr || 0, + } + return data +} + +function getUnderlyingToken(name:string,vault: Vault): UnderlyingToken { + const token = vault.underlyingTokens.find((el) => el.name === name) + return token +} + + +async function calculatePerformance(vault: Vault, _: number): Promise { + const tokenName=vault.isAsset0Main?"token0":"token1" + const token = getUnderlyingToken(tokenName, vault) + const prices = await utils.getPrices([token.address], vault.chain) + //how to get native eth price? what is the address of native eth? + const tokenPrice = prices.pricesByAddress[token.address.toLowerCase()] || 0 + const tokenTvl = new bn(token.tvl) + const tokenDecimals = new bn(10).pow(token.decimals) + const performance = { + tvlUsd: tokenTvl.multipliedBy(tokenPrice).div(tokenDecimals).toNumber(), + // shareTokenApr is 1e-6, so we need to divide by 10000 + apy: token.shareTokenApr/10000, + } + return performance; +} + +function mergeToPoolData(vault: Vault, performance: Performance): Promise { + const tokens = vault.underlyingTokens.map((el) => el.address) + const pool = { + pool: vault.address, + chain: vault.chain, + symbol: vault.name, + url: vault.url, + project: 'teahouse-v3', + tvlUsd: performance.tvlUsd, + apyBase: performance.apy, + apyReward: 0, + underlyingTokens: tokens, + rewardTokens: [], + poolMeta: vault.vaultMeta, + apyBaseBorrow: 0, + apyRewardBorrow: 0, + totalSupplyUsd: 0, + totalBorrowUsd: 0, + ltv: 0, + } + return pool; +} + +async function topLvl(timestamp: number): Promise { + // step 1: get vault data + const vaultType = 'permissionless' + const vaults = await getVaultData(vaultType) + const pools = await Promise.all( + vaults.map(async (el) => + // step 3: merge to pool data + mergeToPoolData(el, + // step 2: calculate performance + await calculatePerformance(el, timestamp)) + ) + ) + return pools +} + +interface Vault { + address: string; + name: string; + chain: string; + underlyingTokens: Array; + isAsset0Main: boolean; + url: string; + vaultMeta: string; //other info +} + +interface UnderlyingToken { + name: string; + address: string; + symbol: string; + decimals: number; + tvl: string; + shareTokenApr: number; +} + +interface Performance { + tvlUsd: number; + apy: number; +} + +interface Pool { + pool: string; + chain: string; + project: string; + symbol: string; + tvlUsd: number; // for lending protocols: tvlUsd = totalSupplyUsd - totalBorrowUsd + apyBase?: number; + apyReward?: number; + rewardTokens?: Array; + underlyingTokens?: Array; + poolMeta?: string; + url?: string; + // optional lending protocol specific fields: + apyBaseBorrow?: number; + apyRewardBorrow?: number; + totalSupplyUsd?: number; + totalBorrowUsd?: number; + ltv?: number; // btw [0, 1] +} + +module.exports = { + getVaultData, + calculatePerformance, + topLvl, +} From cd5783e3cf5b13e93f6faaa9be0e9c768bcedd45 Mon Sep 17 00:00:00 2001 From: daiwanwei Date: Thu, 10 Aug 2023 16:34:47 +0800 Subject: [PATCH 2/8] remove borrow fields --- src/adaptors/teahouse-v3/utils.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/adaptors/teahouse-v3/utils.ts b/src/adaptors/teahouse-v3/utils.ts index 4147bc11c0..8865ca2e63 100644 --- a/src/adaptors/teahouse-v3/utils.ts +++ b/src/adaptors/teahouse-v3/utils.ts @@ -109,10 +109,7 @@ function mergeToPoolData(vault: Vault, performance: Performance): Promise underlyingTokens: tokens, rewardTokens: [], poolMeta: vault.vaultMeta, - apyBaseBorrow: 0, - apyRewardBorrow: 0, totalSupplyUsd: 0, - totalBorrowUsd: 0, ltv: 0, } return pool; From 191882b61c87eaf46bd9dd0127d80383e49307bf Mon Sep 17 00:00:00 2001 From: daiwanwei Date: Tue, 15 Aug 2023 12:10:03 +0800 Subject: [PATCH 3/8] feat: calculate on-chain APY --- .../config.ts | 0 .../index.js | 0 src/adaptors/teahouse-permissionless/utils.ts | 326 ++++++++++++++++++ src/adaptors/teahouse-v3/utils.ts | 181 ---------- 4 files changed, 326 insertions(+), 181 deletions(-) rename src/adaptors/{teahouse-v3 => teahouse-permissionless}/config.ts (100%) rename src/adaptors/{teahouse-v3 => teahouse-permissionless}/index.js (100%) create mode 100644 src/adaptors/teahouse-permissionless/utils.ts delete mode 100644 src/adaptors/teahouse-v3/utils.ts diff --git a/src/adaptors/teahouse-v3/config.ts b/src/adaptors/teahouse-permissionless/config.ts similarity index 100% rename from src/adaptors/teahouse-v3/config.ts rename to src/adaptors/teahouse-permissionless/config.ts diff --git a/src/adaptors/teahouse-v3/index.js b/src/adaptors/teahouse-permissionless/index.js similarity index 100% rename from src/adaptors/teahouse-v3/index.js rename to src/adaptors/teahouse-permissionless/index.js diff --git a/src/adaptors/teahouse-permissionless/utils.ts b/src/adaptors/teahouse-permissionless/utils.ts new file mode 100644 index 0000000000..c7076f4c8a --- /dev/null +++ b/src/adaptors/teahouse-permissionless/utils.ts @@ -0,0 +1,326 @@ +const {ethers} = require("ethers"); +const sdk = require('@defillama/sdk'); +const utils = require('../utils'); +const {TEAHOUSE_VAULT_STAT_API_URL, TEAHOUSE_VAULT_CONTENT_API_URL, TEAHOUSE_WEBSITE_URL} = require('./config'); +const bn = require('bignumber.js'); +const {TEAHOUSE_VAULT_V3_ABI} = require('./abi'); + + +const interval = 24 * 60 * 60 * 7 + +function generateVaultURL(chainName: string, vaultAddress: string): string { + return `${TEAHOUSE_WEBSITE_URL}/${chainName}/${vaultAddress}`; +} + +async function getVaultData(type: string): Promise { + const statUrl = `${TEAHOUSE_VAULT_STAT_API_URL}/vaults/type/${type}` + let {vaults: stats} = await utils.getData(statUrl) + const vaults = await addVaultInfo(stats) + return vaults +} + +async function addVaultInfo(stats: any[]): Promise { + const vaultMap = new Map(); + stats.forEach((el) => { + const chain = el.network || "" + //key format: chain:address + const key = `${chain}:${el.address}` + vaultMap.set(key, el); + }) + const contentUrl = `${TEAHOUSE_VAULT_CONTENT_API_URL}/vaults` + let infoList = []; + let {vaults} = await utils.getData(contentUrl) + vaults.forEach((el) => { + //key format: chain:address + const address = el.share?.address || "" + const chain = el.chain || "" + const key = `${chain}:${address}` + if (vaultMap.has(key)) { + infoList.push(mergeToVault(el, vaultMap.get(key))); + } + }); + return infoList +} + +function mergeToVault(info: any, stat: any): Vault { + const chain = info.chain || "" + const contract = info.share + const address = contract?.address || "" + const name = info.name || "" + const vaultMeta = info.feeTier || "" + const url = generateVaultURL(chain, address) + const shareDecimals = new bn(contract?.decimals || 0) + const tokens = [] + if (stat.latestInfo?.token0) + tokens.push(getTokenData("0", info, stat)) + if (stat.latestInfo?.token1) + tokens.push(getTokenData("1", info, stat)) + const data = { + address, chain, name, vaultMeta, underlyingTokens: tokens, url, + isAsset0Main: info.isAsset0Main, shareDecimals + } + return data +} + +function getTokenData(num: string, info: any, stat: any): UnderlyingToken { + const asset = `asset${num}` + const tokenInfo = info[asset] || {} + const statInfo = stat.latestInfo || {} + const token = `token${num}` + const tokenStat = statInfo[token] || {} + const data = { + name: token, + address: tokenInfo.address || "", + symbol: tokenInfo.symbol || "", + decimals: tokenInfo.decimals || 0, + tvl: tokenStat.tvl || "0", + shareTokenApr: tokenStat.shareTokenApr || 0, + } + return data +} + +function getUnderlyingToken(name: string, vault: Vault): UnderlyingToken { + const token = vault.underlyingTokens.find((el) => el.name === name) + return token +} + +function updateRpcUrl(sdk: any, chain: string, chainId: number, rpcUrl: string) { + const provider = new ethers.providers.StaticJsonRpcProvider( + rpcUrl, {name: chain, chainId: chainId}) + sdk.api.config.setProvider(chain, provider); +} + +async function makeMulticall(abi: any, addresses: string[], chain: string, params = null, options) { + const block = options.block || `latest`; + const data = await sdk.api.abi.multiCall({ + abi, + calls: addresses.map((address) => ({ + target: address, + params, + })), chain, block + }); + let outputByArray = [] + let outputByAddress = {} + for (let i = 0; i < data.output.length; i++) { + const key = addresses[i].toLowerCase(); + outputByArray.push(data.output[i].output); + outputByAddress[key] = data.output[i].output; + } + return { + outputByArray: outputByArray, + outputByAddress: outputByAddress + }; +}; + + +async function getLiquidityData(vault: Vault, block?: number): Promise<{ + tvl: bn, shareSupply: bn +}> { + const chain = vault.chain + const estimatedFn = `estimatedValueInToken${vault.isAsset0Main ? "0" : "1"}` + const estimatedFnABI = TEAHOUSE_VAULT_V3_ABI.find( + (el) => el.name === estimatedFn + ) + const [tvl] = (await makeMulticall( + estimatedFnABI, [vault.address], chain, null, {block: block})) + .outputByArray + const supplyFnABI = TEAHOUSE_VAULT_V3_ABI.find( + (el) => el.name === `totalSupply` + ) + const [shareSupply] = (await makeMulticall( + supplyFnABI, [vault.address], chain, null, {block: block})) + .outputByArray + return { + tvl: new bn(tvl || 0), + shareSupply: new bn(shareSupply || 0) + } +} + +async function searchInterval(start: number, end: number, minInterval: number, check: (x: number) => Promise): Promise { + if (end <= start + minInterval) return -1 + const mid = Math.floor((start + end) / 2) + if (await check(mid)) { + return mid + } else { + return searchInterval(mid, end, minInterval, check) + } + +} + +async function checkVaultSupply(vault: Vault, time: number): Promise { + const [block] = await utils.getBlocksByTime([time], vault.chain) + const supplyFnABI = TEAHOUSE_VAULT_V3_ABI.find( + (el) => el.name === `totalSupply` + ) + const [shareSupply] = (await makeMulticall( + supplyFnABI, [vault.address], vault.chain, null, {block: block})) + .outputByArray + const supply = new bn(shareSupply || 0) + if (supply?.gt(0)) return true + return false +} + +async function addLiquidityData(vault: Vault, interval: number): Promise { + const chain = vault.chain + const tokenName = vault.isAsset0Main ? "token0" : "token1" + const token = getUnderlyingToken(tokenName, vault) + const { + tvl, shareSupply + } = await getLiquidityData(vault) + const before = (Math.floor(Date.now() / 1000)) - interval + const [blockBefore] = await utils.getBlocksByTime([before], chain) + const { + tvl: tvlBefore, shareSupply: shareSupplyBefore + } = await getLiquidityData(vault, blockBefore) + const prices = (await utils.getPrices([token.address], chain)).pricesByAddress + const tokenKey = token.address.toLowerCase() + const tokenDecimals = new bn(10).pow(token.decimals) + const tvlUsd = new bn(tvl || 0).multipliedBy(prices[tokenKey]).div(tokenDecimals) + vault.tvlUsd = tvlUsd + vault.tvl = new bn(tvl || 0) + vault.tvlBefore = new bn(tvlBefore || 0) + vault.shareSupply = new bn(shareSupply || 0) + vault.shareSupplyBefore = new bn(shareSupplyBefore || 0) + return vault +} + +async function updateBeforeLiquidityData(vault: Vault, time: number): Promise { + const chain = vault.chain + const [blockBefore] = await utils.getBlocksByTime([time], chain) + const { + tvl: tvlBefore, shareSupply: shareSupplyBefore + } = await getLiquidityData(vault, blockBefore) + vault.tvlBefore = new bn(tvlBefore || 0) + vault.shareSupplyBefore = new bn(shareSupplyBefore || 0) + return vault +} + + +function calculateSharePrice(tvl: bn, supply: bn, decimals: bn): bn { + if (supply.isZero()) { + return new bn(0) + } + const price = tvl.multipliedBy(decimals).dividedBy(supply) + return price +} + +function calculateAPY(price: bn, priceBefore: bn, interval: number): number { + if (priceBefore.isZero()) { + return 0 + } + const diff = price.minus(priceBefore) + const diffPercent = diff.dividedBy(priceBefore) + const diffPercentYear = diffPercent.dividedBy(interval).multipliedBy(365 * 24 * 60 * 60) + return diffPercentYear +} + +function convertToPool(vault: Vault): Promise { + const tokens = vault.underlyingTokens.map((el) => el.address) + const pool = { + pool: vault.address, + chain: vault.chain, + symbol: vault.name, + url: vault.url, + project: 'teahouse-permissionless', + tvlUsd: vault.tvlUsd.toNumber(), + apyBase: vault.apy, + apyReward: 0, + underlyingTokens: tokens, + rewardTokens: [], + poolMeta: vault.vaultMeta, + totalSupplyUsd: 0, + ltv: 0, + } + return pool; +} + +async function topLvl(_: number): Promise { + // step 1: get vault data + const vaultType = 'permissionless' + const vaults = await getVaultData(vaultType) + const interval = 24 * 60 * 60 * 14 + updateRpcUrl(sdk, 'arbitrum', 42161, "https://rpc.ankr.com/arbitrum") + const pools = [] + + for (let vault of vaults) { + //step 2: get TVL and Share Supply + vault = await addLiquidityData(vault, interval) + const decimals = new bn(10).pow(vault.shareDecimals) + //step 3: if TVL is 0, update the start time + if (!(vault.shareSupplyBefore?.gt(0))) { + const end = Math.floor(Date.now() / 1000) + const start = end - interval + const minInterval = 60 * 60 * 7 + const check = async (x: number) => { + return await checkVaultSupply(vault, x) + } + const newStart = await searchInterval(start, end, minInterval, check) + if (newStart === -1) continue + vault = await updateBeforeLiquidityData(vault, newStart) + } + //step 4: calculate share token price + const sharePrice = calculateSharePrice(vault.tvl, vault.shareSupply, decimals) + const sharePriceBefore = calculateSharePrice(vault.tvlBefore, vault.shareSupplyBefore, decimals) + //step 5: calculate apy + const apy = calculateAPY(sharePrice, sharePriceBefore, interval) + vault.apy = apy * 100 + const pool = await convertToPool(vault) + pools.push(pool) + } + return pools +} + +interface Vault { + address: string; + name: string; + chain: string; + underlyingTokens: Array; + isAsset0Main: boolean; + url: string; + vaultMeta: string; //other info + shareDecimals: number; + tvlUsd?: bn; + tvl?: bn; + tvlBefore?: bn; + shareSupply?: bn; + shareSupplyBefore?: bn; + apy?: number; +} + +interface UnderlyingToken { + name: string; + address: string; + symbol: string; + decimals: number; + tvl: string; + shareTokenApr: number; +} + +interface Performance { + tvlUsd: number; + apy: number; +} + +interface Pool { + pool: string; + chain: string; + project: string; + symbol: string; + tvlUsd: number; // for lending protocols: tvlUsd = totalSupplyUsd - totalBorrowUsd + apyBase?: number; + apyReward?: number; + rewardTokens?: Array; + underlyingTokens?: Array; + poolMeta?: string; + url?: string; + // optional lending protocol specific fields: + apyBaseBorrow?: number; + apyRewardBorrow?: number; + totalSupplyUsd?: number; + totalBorrowUsd?: number; + ltv?: number; // btw [0, 1] +} + +module.exports = { + topLvl, +} diff --git a/src/adaptors/teahouse-v3/utils.ts b/src/adaptors/teahouse-v3/utils.ts deleted file mode 100644 index 8865ca2e63..0000000000 --- a/src/adaptors/teahouse-v3/utils.ts +++ /dev/null @@ -1,181 +0,0 @@ -const utils = require('../utils'); -const {TEAHOUSE_VAULT_STAT_API_URL, TEAHOUSE_VAULT_CONTENT_API_URL, TEAHOUSE_WEBSITE_URL} = require('./config'); -const bn= require('bignumber.js'); - -function generateVaultURL(chainName: string, vaultAddress: string): string { - return `${TEAHOUSE_WEBSITE_URL}/${chainName}/${vaultAddress}`; -} - -async function getVaultData(type:string): Promise { - const statUrl = `${TEAHOUSE_VAULT_STAT_API_URL}/vaults/type/${type}` - let {vaults: stats} = await utils.getData(statUrl) - const vaults = await addVaultInfo(stats) - return vaults -} - -async function addVaultInfo(stats: any[]): Promise { - const vaultMap = new Map(); - stats.forEach((el) => { - const chain = el.network || "" - //key format: chain:address - const key=`${chain}:${el.address}` - vaultMap.set(key, el); - }) - const contentUrl = `${TEAHOUSE_VAULT_CONTENT_API_URL}/vaults` - let infoList = []; - let {vaults} = await utils.getData(contentUrl) - vaults.forEach((el) => { - //key format: chain:address - const address = el.share?.address || "" - const chain = el.chain || "" - const key=`${chain}:${address}` - if (vaultMap.has(key)) { - infoList.push(mergeToVault(el, vaultMap.get(key))); - } - }); - return infoList -} - -function mergeToVault(info: any, stat: any): Vault { - const chain = info.chain || "" - const contract = info.share - const address = contract?.address || "" - const name = info.name || "" - const vaultName=info.feeTier? `${name} (${info.feeTier})`:name; - const vaultMeta = info.feeTier || "" - const url = generateVaultURL(chain, address) - const tokens = [] - if (stat.latestInfo?.token0) - tokens.push(getTokenData("0", info, stat)) - if (stat.latestInfo?.token1) - tokens.push(getTokenData("1", info, stat)) - const data = { - address, chain, name:vaultName,vaultMeta, underlyingTokens: tokens, url, - isAsset0Main: info.isAsset0Main, - } - return data -} - -function getTokenData(num: string, info: any, stat: any): UnderlyingToken { - const asset = `asset${num}` - const tokenInfo = info[asset] || {} - const statInfo = stat.latestInfo || {} - const token = `token${num}` - const tokenStat = statInfo[token] || {} - const data = { - name: token, - address: tokenInfo.address || "", - symbol: tokenInfo.symbol || "", - decimals: tokenInfo.decimals || 0, - tvl: tokenStat.tvl || "0", - shareTokenApr: tokenStat.shareTokenApr || 0, - } - return data -} - -function getUnderlyingToken(name:string,vault: Vault): UnderlyingToken { - const token = vault.underlyingTokens.find((el) => el.name === name) - return token -} - - -async function calculatePerformance(vault: Vault, _: number): Promise { - const tokenName=vault.isAsset0Main?"token0":"token1" - const token = getUnderlyingToken(tokenName, vault) - const prices = await utils.getPrices([token.address], vault.chain) - //how to get native eth price? what is the address of native eth? - const tokenPrice = prices.pricesByAddress[token.address.toLowerCase()] || 0 - const tokenTvl = new bn(token.tvl) - const tokenDecimals = new bn(10).pow(token.decimals) - const performance = { - tvlUsd: tokenTvl.multipliedBy(tokenPrice).div(tokenDecimals).toNumber(), - // shareTokenApr is 1e-6, so we need to divide by 10000 - apy: token.shareTokenApr/10000, - } - return performance; -} - -function mergeToPoolData(vault: Vault, performance: Performance): Promise { - const tokens = vault.underlyingTokens.map((el) => el.address) - const pool = { - pool: vault.address, - chain: vault.chain, - symbol: vault.name, - url: vault.url, - project: 'teahouse-v3', - tvlUsd: performance.tvlUsd, - apyBase: performance.apy, - apyReward: 0, - underlyingTokens: tokens, - rewardTokens: [], - poolMeta: vault.vaultMeta, - totalSupplyUsd: 0, - ltv: 0, - } - return pool; -} - -async function topLvl(timestamp: number): Promise { - // step 1: get vault data - const vaultType = 'permissionless' - const vaults = await getVaultData(vaultType) - const pools = await Promise.all( - vaults.map(async (el) => - // step 3: merge to pool data - mergeToPoolData(el, - // step 2: calculate performance - await calculatePerformance(el, timestamp)) - ) - ) - return pools -} - -interface Vault { - address: string; - name: string; - chain: string; - underlyingTokens: Array; - isAsset0Main: boolean; - url: string; - vaultMeta: string; //other info -} - -interface UnderlyingToken { - name: string; - address: string; - symbol: string; - decimals: number; - tvl: string; - shareTokenApr: number; -} - -interface Performance { - tvlUsd: number; - apy: number; -} - -interface Pool { - pool: string; - chain: string; - project: string; - symbol: string; - tvlUsd: number; // for lending protocols: tvlUsd = totalSupplyUsd - totalBorrowUsd - apyBase?: number; - apyReward?: number; - rewardTokens?: Array; - underlyingTokens?: Array; - poolMeta?: string; - url?: string; - // optional lending protocol specific fields: - apyBaseBorrow?: number; - apyRewardBorrow?: number; - totalSupplyUsd?: number; - totalBorrowUsd?: number; - ltv?: number; // btw [0, 1] -} - -module.exports = { - getVaultData, - calculatePerformance, - topLvl, -} From 9c3f00997bafa8a2b887857920280bacaa4bf87b Mon Sep 17 00:00:00 2001 From: daiwanwei Date: Tue, 15 Aug 2023 13:51:18 +0800 Subject: [PATCH 4/8] add abi --- src/adaptors/teahouse-permissionless/abi.ts | 927 ++++++++++++++++++++ 1 file changed, 927 insertions(+) create mode 100644 src/adaptors/teahouse-permissionless/abi.ts diff --git a/src/adaptors/teahouse-permissionless/abi.ts b/src/adaptors/teahouse-permissionless/abi.ts new file mode 100644 index 0000000000..acc35168a8 --- /dev/null +++ b/src/adaptors/teahouse-permissionless/abi.ts @@ -0,0 +1,927 @@ +const TEAHOUSE_VAULT_V3_ABI = [{"inputs": [], "stateMutability": "nonpayable", "type": "constructor"}, { + "inputs": [], + "name": "CallerIsNotManager", + "type": "error" +}, { + "inputs": [{"internalType": "uint256", "name": "minAmount", "type": "uint256"}, { + "internalType": "uint256", + "name": "convertedAmount", + "type": "uint256" + }], "name": "InsufficientSwapResult", "type": "error" +}, {"inputs": [], "name": "InvalidCallbackCaller", "type": "error"}, { + "inputs": [], + "name": "InvalidCallbackStatus", + "type": "error" +}, {"inputs": [], "name": "InvalidFeeCap", "type": "error"}, { + "inputs": [], + "name": "InvalidFeePercentage", + "type": "error" +}, { + "inputs": [{"internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }], "name": "InvalidPriceSlippage", "type": "error" +}, {"inputs": [], "name": "InvalidShareAmount", "type": "error"}, { + "inputs": [], + "name": "InvalidSwapReceiver", + "type": "error" +}, {"inputs": [], "name": "InvalidSwapToken", "type": "error"}, { + "inputs": [], + "name": "InvalidTokenOrder", + "type": "error" +}, {"inputs": [], "name": "PoolNotInitialized", "type": "error"}, { + "inputs": [], + "name": "PositionDoesNotExist", + "type": "error" +}, {"inputs": [], "name": "PositionLengthExceedsLimit", "type": "error"}, { + "inputs": [], + "name": "SwapInZeroLiquidityRegion", + "type": "error" +}, {"inputs": [], "name": "TransactionExpired", "type": "error"}, { + "inputs": [], + "name": "ZeroLiquidity", + "type": "error" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "pool", "type": "address"}, { + "indexed": false, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, {"indexed": false, "internalType": "int24", "name": "tickUpper", "type": "int24"}, { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, {"indexed": false, "internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }], + "name": "AddLiquidity", + "type": "event" +}, { + "anonymous": false, + "inputs": [{ + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, {"indexed": false, "internalType": "address", "name": "newAdmin", "type": "address"}], + "name": "AdminChanged", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "owner", "type": "address"}, { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, {"indexed": false, "internalType": "uint256", "name": "value", "type": "uint256"}], + "name": "Approval", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "beacon", "type": "address"}], + "name": "BeaconUpgraded", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "pool", "type": "address"}, { + "indexed": false, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, {"indexed": false, "internalType": "int24", "name": "tickUpper", "type": "int24"}, { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, {"indexed": false, "internalType": "uint256", "name": "amount1", "type": "uint256"}], + "name": "Collect", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "pool", "type": "address"}, { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, {"indexed": false, "internalType": "uint256", "name": "amount1", "type": "uint256"}, { + "indexed": false, + "internalType": "uint256", + "name": "feeAmount0", + "type": "uint256" + }, {"indexed": false, "internalType": "uint256", "name": "feeAmount1", "type": "uint256"}], + "name": "CollectSwapFees", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "shareOwner", "type": "address"}, { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, {"indexed": false, "internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, {"indexed": false, "internalType": "uint256", "name": "feeAmount0", "type": "uint256"}, { + "indexed": false, + "internalType": "uint256", + "name": "feeAmount1", + "type": "uint256" + }], + "name": "DepositShares", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "sender", "type": "address"}, { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, { + "components": [{"internalType": "address", "name": "vault", "type": "address"}, { + "internalType": "uint24", + "name": "entryFee", + "type": "uint24" + }, {"internalType": "uint24", "name": "exitFee", "type": "uint24"}, { + "internalType": "uint24", + "name": "performanceFee", + "type": "uint24" + }, {"internalType": "uint24", "name": "managementFee", "type": "uint24"}], + "indexed": false, + "internalType": "struct ITeaVaultV3Pair.FeeConfig", + "name": "feeConfig", + "type": "tuple" + }], + "name": "FeeConfigChanged", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": false, "internalType": "uint8", "name": "version", "type": "uint8"}], + "name": "Initialized", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": false, "internalType": "uint256", "name": "shares", "type": "uint256"}], + "name": "ManagementFeeCollected", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "sender", "type": "address"}, { + "indexed": true, + "internalType": "address", + "name": "newManager", + "type": "address" + }], + "name": "ManagerChanged", + "type": "event" +}, { + "anonymous": false, + "inputs": [{ + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, {"indexed": true, "internalType": "address", "name": "newOwner", "type": "address"}], + "name": "OwnershipTransferred", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "pool", "type": "address"}, { + "indexed": false, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, {"indexed": false, "internalType": "int24", "name": "tickUpper", "type": "int24"}, { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, {"indexed": false, "internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }], + "name": "RemoveLiquidity", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "bool", "name": "zeroForOne", "type": "bool"}, { + "indexed": true, + "internalType": "bool", + "name": "exactInput", + "type": "bool" + }, {"indexed": false, "internalType": "uint256", "name": "amountIn", "type": "uint256"}, { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }], + "name": "Swap", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "teaVaultAddress", "type": "address"}], + "name": "TeaVaultV3PairCreated", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "from", "type": "address"}, { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, {"indexed": false, "internalType": "uint256", "name": "value", "type": "uint256"}], + "name": "Transfer", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "implementation", "type": "address"}], + "name": "Upgraded", + "type": "event" +}, { + "anonymous": false, + "inputs": [{"indexed": true, "internalType": "address", "name": "shareOwner", "type": "address"}, { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, {"indexed": false, "internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, {"indexed": false, "internalType": "uint256", "name": "feeShares", "type": "uint256"}], + "name": "WithdrawShares", + "type": "event" +}, { + "inputs": [], + "name": "DECIMALS_MULTIPLIER", + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "FEE_CAP", + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "FEE_MULTIPLIER", + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "SECONDS_IN_A_YEAR", + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "int24", "name": "_tickLower", "type": "int24"}, { + "internalType": "int24", + "name": "_tickUpper", + "type": "int24" + }, {"internalType": "uint128", "name": "_liquidity", "type": "uint128"}, { + "internalType": "uint256", + "name": "_amount0Min", + "type": "uint256" + }, {"internalType": "uint256", "name": "_amount1Min", "type": "uint256"}, { + "internalType": "uint64", + "name": "_deadline", + "type": "uint64" + }], + "name": "addLiquidity", + "outputs": [{"internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [], + "name": "allPositionInfo", + "outputs": [{"internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, {"internalType": "uint256", "name": "fee0", "type": "uint256"}, { + "internalType": "uint256", + "name": "fee1", + "type": "uint256" + }], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "owner", "type": "address"}, { + "internalType": "address", + "name": "spender", + "type": "address" + }], + "name": "allowance", + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "spender", "type": "address"}, { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }], + "name": "approve", + "outputs": [{"internalType": "bool", "name": "", "type": "bool"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [], + "name": "assetToken0", + "outputs": [{"internalType": "address", "name": "", "type": "address"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "assetToken1", + "outputs": [{"internalType": "address", "name": "", "type": "address"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "_manager", "type": "address"}], + "name": "assignManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "_router1Inch", "type": "address"}], + "name": "assignRouter1Inch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "account", "type": "address"}], + "name": "balanceOf", + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "clipperExchange", "type": "address"}, { + "internalType": "address", + "name": "srcToken", + "type": "address" + }, {"internalType": "address", "name": "dstToken", "type": "address"}, { + "internalType": "uint256", + "name": "inputAmount", + "type": "uint256" + }, {"internalType": "uint256", "name": "outputAmount", "type": "uint256"}, { + "internalType": "uint256", + "name": "goodUntil", + "type": "uint256" + }, {"internalType": "bytes32", "name": "r", "type": "bytes32"}, { + "internalType": "bytes32", + "name": "vs", + "type": "bytes32" + }], + "name": "clipperSwap", + "outputs": [{"internalType": "uint256", "name": "returnAmount", "type": "uint256"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [], + "name": "collectAllSwapFee", + "outputs": [{"internalType": "uint128", "name": "amount0", "type": "uint128"}, { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + }], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [], + "name": "collectManagementFee", + "outputs": [{"internalType": "uint256", "name": "collectedShares", "type": "uint256"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "int24", "name": "_tickLower", "type": "int24"}, { + "internalType": "int24", + "name": "_tickUpper", + "type": "int24" + }], + "name": "collectPositionSwapFee", + "outputs": [{"internalType": "uint128", "name": "amount0", "type": "uint128"}, { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + }], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [], + "name": "decimals", + "outputs": [{"internalType": "uint8", "name": "", "type": "uint8"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "spender", "type": "address"}, { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + }], + "name": "decreaseAllowance", + "outputs": [{"internalType": "bool", "name": "", "type": "bool"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "uint256", "name": "_shares", "type": "uint256"}, { + "internalType": "uint256", + "name": "_amount0Max", + "type": "uint256" + }, {"internalType": "uint256", "name": "_amount1Max", "type": "uint256"}], + "name": "deposit", + "outputs": [{"internalType": "uint256", "name": "depositedAmount0", "type": "uint256"}, { + "internalType": "uint256", + "name": "depositedAmount1", + "type": "uint256" + }], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [], + "name": "estimatedValueInToken0", + "outputs": [{"internalType": "uint256", "name": "value0", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "estimatedValueInToken1", + "outputs": [{"internalType": "uint256", "name": "value1", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "feeConfig", + "outputs": [{"internalType": "address", "name": "vault", "type": "address"}, { + "internalType": "uint24", + "name": "entryFee", + "type": "uint24" + }, {"internalType": "uint24", "name": "exitFee", "type": "uint24"}, { + "internalType": "uint24", + "name": "performanceFee", + "type": "uint24" + }, {"internalType": "uint24", "name": "managementFee", "type": "uint24"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "getAllPositions", + "outputs": [{ + "components": [{ + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, {"internalType": "int24", "name": "tickUpper", "type": "int24"}, { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }], "internalType": "struct ITeaVaultV3Pair.Position[]", "name": "results", "type": "tuple[]" + }], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "int24", "name": "tickLower", "type": "int24"}, { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, {"internalType": "uint128", "name": "liquidity", "type": "uint128"}], + "name": "getAmountsForLiquidity", + "outputs": [{"internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "int24", "name": "tickLower", "type": "int24"}, { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, {"internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }], + "name": "getLiquidityForAmounts", + "outputs": [{"internalType": "uint128", "name": "liquidity", "type": "uint128"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "getPoolInfo", + "outputs": [{"internalType": "address", "name": "", "type": "address"}, { + "internalType": "address", + "name": "", + "type": "address" + }, {"internalType": "uint8", "name": "", "type": "uint8"}, { + "internalType": "uint8", + "name": "", + "type": "uint8" + }, {"internalType": "uint24", "name": "", "type": "uint24"}, { + "internalType": "uint160", + "name": "", + "type": "uint160" + }, {"internalType": "int24", "name": "", "type": "int24"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "getToken0Balance", + "outputs": [{"internalType": "uint256", "name": "amount", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "getToken1Balance", + "outputs": [{"internalType": "uint256", "name": "amount", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "spender", "type": "address"}, { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + }], + "name": "increaseAllowance", + "outputs": [{"internalType": "bool", "name": "", "type": "bool"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "string", "name": "_name", "type": "string"}, { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, {"internalType": "address", "name": "_factory", "type": "address"}, { + "internalType": "address", + "name": "_token0", + "type": "address" + }, {"internalType": "address", "name": "_token1", "type": "address"}, { + "internalType": "uint24", + "name": "_feeTier", + "type": "uint24" + }, {"internalType": "uint8", "name": "_decimalOffset", "type": "uint8"}, { + "internalType": "uint24", + "name": "_feeCap", + "type": "uint24" + }, { + "components": [{"internalType": "address", "name": "vault", "type": "address"}, { + "internalType": "uint24", + "name": "entryFee", + "type": "uint24" + }, {"internalType": "uint24", "name": "exitFee", "type": "uint24"}, { + "internalType": "uint24", + "name": "performanceFee", + "type": "uint24" + }, {"internalType": "uint24", "name": "managementFee", "type": "uint24"}], + "internalType": "struct ITeaVaultV3Pair.FeeConfig", + "name": "_feeConfig", + "type": "tuple" + }, {"internalType": "address", "name": "_owner", "type": "address"}], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [], + "name": "lastCollectManagementFee", + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "manager", + "outputs": [{"internalType": "address", "name": "", "type": "address"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "bytes[]", "name": "data", "type": "bytes[]"}], + "name": "multicall", + "outputs": [{"internalType": "bytes[]", "name": "results", "type": "bytes[]"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [], + "name": "name", + "outputs": [{"internalType": "string", "name": "", "type": "string"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "owner", + "outputs": [{"internalType": "address", "name": "", "type": "address"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "pool", + "outputs": [{"internalType": "contract IUniswapV3Pool", "name": "", "type": "address"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "uint256", "name": "_index", "type": "uint256"}], + "name": "positionInfo", + "outputs": [{"internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, {"internalType": "uint256", "name": "fee0", "type": "uint256"}, { + "internalType": "uint256", + "name": "fee1", + "type": "uint256" + }], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "int24", "name": "_tickLower", "type": "int24"}, { + "internalType": "int24", + "name": "_tickUpper", + "type": "int24" + }], + "name": "positionInfo", + "outputs": [{"internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, {"internalType": "uint256", "name": "fee0", "type": "uint256"}, { + "internalType": "uint256", + "name": "fee1", + "type": "uint256" + }], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "name": "positions", + "outputs": [{"internalType": "int24", "name": "tickLower", "type": "int24"}, { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, {"internalType": "uint128", "name": "liquidity", "type": "uint128"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "proxiableUUID", + "outputs": [{"internalType": "bytes32", "name": "", "type": "bytes32"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "int24", "name": "_tickLower", "type": "int24"}, { + "internalType": "int24", + "name": "_tickUpper", + "type": "int24" + }, {"internalType": "uint128", "name": "_liquidity", "type": "uint128"}, { + "internalType": "uint256", + "name": "_amount0Min", + "type": "uint256" + }, {"internalType": "uint256", "name": "_amount1Min", "type": "uint256"}, { + "internalType": "uint64", + "name": "_deadline", + "type": "uint64" + }], + "name": "removeLiquidity", + "outputs": [{"internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [], + "name": "router1Inch", + "outputs": [{"internalType": "contract IGenericRouter1Inch", "name": "", "type": "address"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{ + "components": [{ + "internalType": "address", + "name": "vault", + "type": "address" + }, {"internalType": "uint24", "name": "entryFee", "type": "uint24"}, { + "internalType": "uint24", + "name": "exitFee", + "type": "uint24" + }, {"internalType": "uint24", "name": "performanceFee", "type": "uint24"}, { + "internalType": "uint24", + "name": "managementFee", + "type": "uint24" + }], "internalType": "struct ITeaVaultV3Pair.FeeConfig", "name": "_feeConfig", "type": "tuple" + }], "name": "setFeeConfig", "outputs": [], "stateMutability": "nonpayable", "type": "function" +}, { + "inputs": [{"internalType": "bool", "name": "_zeroForOne", "type": "bool"}, { + "internalType": "uint256", + "name": "_amountIn", + "type": "uint256" + }], "name": "simulateSwapInputSingleInternal", "outputs": [], "stateMutability": "nonpayable", "type": "function" +}, { + "inputs": [{ + "internalType": "address", + "name": "executor", + "type": "address" + }, { + "components": [{"internalType": "address", "name": "srcToken", "type": "address"}, { + "internalType": "address", + "name": "dstToken", + "type": "address" + }, { + "internalType": "address payable", + "name": "srcReceiver", + "type": "address" + }, {"internalType": "address payable", "name": "dstReceiver", "type": "address"}, { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, {"internalType": "uint256", "name": "minReturnAmount", "type": "uint256"}, { + "internalType": "uint256", + "name": "flags", + "type": "uint256" + }], "internalType": "struct IGenericRouter1Inch.SwapDescription", "name": "desc", "type": "tuple" + }, {"internalType": "bytes", "name": "permit", "type": "bytes"}, { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }], + "name": "swap", + "outputs": [{"internalType": "uint256", "name": "returnAmount", "type": "uint256"}, { + "internalType": "uint256", + "name": "spentAmount", + "type": "uint256" + }], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "bool", "name": "_zeroForOne", "type": "bool"}, { + "internalType": "uint256", + "name": "_amountIn", + "type": "uint256" + }, {"internalType": "uint256", "name": "_amountOutMin", "type": "uint256"}, { + "internalType": "uint160", + "name": "_minPriceInSqrtPriceX96", + "type": "uint160" + }, {"internalType": "uint64", "name": "_deadline", "type": "uint64"}], + "name": "swapInputSingle", + "outputs": [{"internalType": "uint256", "name": "amountOut", "type": "uint256"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "bool", "name": "_zeroForOne", "type": "bool"}, { + "internalType": "uint256", + "name": "_amountOut", + "type": "uint256" + }, {"internalType": "uint256", "name": "_amountInMax", "type": "uint256"}, { + "internalType": "uint160", + "name": "_maxPriceInSqrtPriceX96", + "type": "uint160" + }, {"internalType": "uint64", "name": "_deadline", "type": "uint64"}], + "name": "swapOutputSingle", + "outputs": [{"internalType": "uint256", "name": "amountIn", "type": "uint256"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [], + "name": "symbol", + "outputs": [{"internalType": "string", "name": "", "type": "string"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [], + "name": "totalSupply", + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "to", "type": "address"}, { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }], + "name": "transfer", + "outputs": [{"internalType": "bool", "name": "", "type": "bool"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "from", "type": "address"}, { + "internalType": "address", + "name": "to", + "type": "address" + }, {"internalType": "uint256", "name": "amount", "type": "uint256"}], + "name": "transferFrom", + "outputs": [{"internalType": "bool", "name": "", "type": "bool"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "newOwner", "type": "address"}], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "uint256", "name": "_amount0Owed", "type": "uint256"}, { + "internalType": "uint256", + "name": "_amount1Owed", + "type": "uint256" + }, {"internalType": "bytes", "name": "_data", "type": "bytes"}], + "name": "uniswapV3MintCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "uint256", "name": "amount", "type": "uint256"}, { + "internalType": "uint256", + "name": "minReturn", + "type": "uint256" + }, {"internalType": "uint256[]", "name": "pools", "type": "uint256[]"}], + "name": "uniswapV3Swap", + "outputs": [{"internalType": "uint256", "name": "returnAmount", "type": "uint256"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "int256", "name": "_amount0Delta", "type": "int256"}, { + "internalType": "int256", + "name": "_amount1Delta", + "type": "int256" + }, {"internalType": "bytes", "name": "_data", "type": "bytes"}], + "name": "uniswapV3SwapCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "srcToken", "type": "address"}, { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, {"internalType": "uint256", "name": "minReturn", "type": "uint256"}, { + "internalType": "uint256[]", + "name": "pools", + "type": "uint256[]" + }], + "name": "unoswap", + "outputs": [{"internalType": "uint256", "name": "returnAmount", "type": "uint256"}], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "newImplementation", "type": "address"}], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [{"internalType": "address", "name": "newImplementation", "type": "address"}, { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }], "name": "upgradeToAndCall", "outputs": [], "stateMutability": "payable", "type": "function" +}, { + "inputs": [], + "name": "vaultAllUnderlyingAssets", + "outputs": [{"internalType": "uint256", "name": "amount0", "type": "uint256"}, { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }], + "stateMutability": "view", + "type": "function" +}, { + "inputs": [{"internalType": "uint256", "name": "_shares", "type": "uint256"}, { + "internalType": "uint256", + "name": "_amount0Min", + "type": "uint256" + }, {"internalType": "uint256", "name": "_amount1Min", "type": "uint256"}], + "name": "withdraw", + "outputs": [{"internalType": "uint256", "name": "withdrawnAmount0", "type": "uint256"}, { + "internalType": "uint256", + "name": "withdrawnAmount1", + "type": "uint256" + }], + "stateMutability": "nonpayable", + "type": "function" +}] + +module.exports = { + TEAHOUSE_VAULT_V3_ABI +} From cf1a0a832a750a14e2c00a1127fa9b829ddff70d Mon Sep 17 00:00:00 2001 From: daiwanwei Date: Tue, 15 Aug 2023 13:54:17 +0800 Subject: [PATCH 5/8] rename pool name --- src/adaptors/teahouse-permissionless/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adaptors/teahouse-permissionless/utils.ts b/src/adaptors/teahouse-permissionless/utils.ts index c7076f4c8a..8d94cac38a 100644 --- a/src/adaptors/teahouse-permissionless/utils.ts +++ b/src/adaptors/teahouse-permissionless/utils.ts @@ -217,7 +217,7 @@ function calculateAPY(price: bn, priceBefore: bn, interval: number): number { function convertToPool(vault: Vault): Promise { const tokens = vault.underlyingTokens.map((el) => el.address) const pool = { - pool: vault.address, + pool: `${vault.address}-${vault.chain}`, chain: vault.chain, symbol: vault.name, url: vault.url, From 66bbbbeebfe92c9aa622218006009a3457686c70 Mon Sep 17 00:00:00 2001 From: Wade Date: Tue, 15 Aug 2023 22:35:42 +0800 Subject: [PATCH 6/8] remove the borrowing fields --- src/adaptors/teahouse-permissionless/utils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/adaptors/teahouse-permissionless/utils.ts b/src/adaptors/teahouse-permissionless/utils.ts index 8d94cac38a..56d028ef05 100644 --- a/src/adaptors/teahouse-permissionless/utils.ts +++ b/src/adaptors/teahouse-permissionless/utils.ts @@ -228,8 +228,6 @@ function convertToPool(vault: Vault): Promise { underlyingTokens: tokens, rewardTokens: [], poolMeta: vault.vaultMeta, - totalSupplyUsd: 0, - ltv: 0, } return pool; } From 9bd03a870dc559e11788af1dce55df1050f9272b Mon Sep 17 00:00:00 2001 From: Wade Date: Tue, 15 Aug 2023 22:41:10 +0800 Subject: [PATCH 7/8] feat: update the apy base --- src/adaptors/teahouse-permissionless/utils.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/adaptors/teahouse-permissionless/utils.ts b/src/adaptors/teahouse-permissionless/utils.ts index 56d028ef05..69ab276895 100644 --- a/src/adaptors/teahouse-permissionless/utils.ts +++ b/src/adaptors/teahouse-permissionless/utils.ts @@ -236,7 +236,7 @@ async function topLvl(_: number): Promise { // step 1: get vault data const vaultType = 'permissionless' const vaults = await getVaultData(vaultType) - const interval = 24 * 60 * 60 * 14 + const interval = 24 * 60 * 60 updateRpcUrl(sdk, 'arbitrum', 42161, "https://rpc.ankr.com/arbitrum") const pools = [] @@ -245,17 +245,17 @@ async function topLvl(_: number): Promise { vault = await addLiquidityData(vault, interval) const decimals = new bn(10).pow(vault.shareDecimals) //step 3: if TVL is 0, update the start time - if (!(vault.shareSupplyBefore?.gt(0))) { - const end = Math.floor(Date.now() / 1000) - const start = end - interval - const minInterval = 60 * 60 * 7 - const check = async (x: number) => { - return await checkVaultSupply(vault, x) - } - const newStart = await searchInterval(start, end, minInterval, check) - if (newStart === -1) continue - vault = await updateBeforeLiquidityData(vault, newStart) - } + // if (!(vault.shareSupplyBefore?.gt(0))) { + // const end = Math.floor(Date.now() / 1000) + // const start = end - interval + // const minInterval = 60 * 60 * 24 + // const check = async (x: number) => { + // return await checkVaultSupply(vault, x) + // } + // const newStart = await searchInterval(start, end, minInterval, check) + // if (newStart === -1) continue + // vault = await updateBeforeLiquidityData(vault, newStart) + // } //step 4: calculate share token price const sharePrice = calculateSharePrice(vault.tvl, vault.shareSupply, decimals) const sharePriceBefore = calculateSharePrice(vault.tvlBefore, vault.shareSupplyBefore, decimals) From ff16801a685154534a300e0a9a2d0bac2e14bdb1 Mon Sep 17 00:00:00 2001 From: Wade Date: Mon, 13 Nov 2023 14:53:32 +0800 Subject: [PATCH 8/8] feat: Add the calculation for the reward token. --- src/adaptors/teahouse-permissionless/utils.ts | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/src/adaptors/teahouse-permissionless/utils.ts b/src/adaptors/teahouse-permissionless/utils.ts index 69ab276895..9313b1331a 100644 --- a/src/adaptors/teahouse-permissionless/utils.ts +++ b/src/adaptors/teahouse-permissionless/utils.ts @@ -51,13 +51,26 @@ function mergeToVault(info: any, stat: any): Vault { const url = generateVaultURL(chain, address) const shareDecimals = new bn(contract?.decimals || 0) const tokens = [] + let rewardTokens = [] if (stat.latestInfo?.token0) tokens.push(getTokenData("0", info, stat)) if (stat.latestInfo?.token1) tokens.push(getTokenData("1", info, stat)) + if (info.rewardTokens){ + rewardTokens=info.rewardTokens.map((el)=>({ + address:el.address, + chain:chain, + decimals:el.decimals, + rewardBook:el.rewardBook, + symbol:el.symbol, + type:el.type + })) + }else { + rewardTokens=[] + } const data = { address, chain, name, vaultMeta, underlyingTokens: tokens, url, - isAsset0Main: info.isAsset0Main, shareDecimals + isAsset0Main: info.isAsset0Main, shareDecimals,rewardTokens:rewardTokens } return data } @@ -164,6 +177,7 @@ async function addLiquidityData(vault: Vault, interval: number): Promise const chain = vault.chain const tokenName = vault.isAsset0Main ? "token0" : "token1" const token = getUnderlyingToken(tokenName, vault) + const rewardTokens = vault.rewardTokens const { tvl, shareSupply } = await getLiquidityData(vault) @@ -176,14 +190,33 @@ async function addLiquidityData(vault: Vault, interval: number): Promise const tokenKey = token.address.toLowerCase() const tokenDecimals = new bn(10).pow(token.decimals) const tvlUsd = new bn(tvl || 0).multipliedBy(prices[tokenKey]).div(tokenDecimals) + const rewardApy = await getRewardTokenApy(chain,rewardTokens,tvlUsd) vault.tvlUsd = tvlUsd vault.tvl = new bn(tvl || 0) vault.tvlBefore = new bn(tvlBefore || 0) vault.shareSupply = new bn(shareSupply || 0) vault.shareSupplyBefore = new bn(shareSupplyBefore || 0) + vault.rewardApy = rewardApy return vault } +async function getRewardTokenApy(chain:string,rewardTokens:RewardToken[],tvlUsd:bn):Promise{ + if (rewardTokens.length===0) return 0 + const addresses= rewardTokens.map((el)=>el.address.toLowerCase()) + const prices = (await utils.getPrices(addresses, chain)).pricesByAddress + const totalApr = rewardTokens.reduce((acc,el)=>{ + const rewardBook = el.rewardBook + const reward = new bn(rewardBook.totalReward) + const totalRewardUsd = reward.multipliedBy(prices[el.address.toLowerCase()]).div(new bn(10).pow(el.decimals)) + const apy = totalRewardUsd + .div(Number(rewardBook.endTime)-Number(rewardBook.startTime)) + .multipliedBy(365*24*60*60) + .div(tvlUsd).multipliedBy(100) + return acc.plus(apy) + },new bn(0)) + return totalApr.toNumber() +} + async function updateBeforeLiquidityData(vault: Vault, time: number): Promise { const chain = vault.chain const [blockBefore] = await utils.getBlocksByTime([time], chain) @@ -216,6 +249,7 @@ function calculateAPY(price: bn, priceBefore: bn, interval: number): number { function convertToPool(vault: Vault): Promise { const tokens = vault.underlyingTokens.map((el) => el.address) + const rewardTokens = vault.rewardTokens.map((el) => el.address) const pool = { pool: `${vault.address}-${vault.chain}`, chain: vault.chain, @@ -224,9 +258,9 @@ function convertToPool(vault: Vault): Promise { project: 'teahouse-permissionless', tvlUsd: vault.tvlUsd.toNumber(), apyBase: vault.apy, - apyReward: 0, + apyReward: vault.rewardApy, underlyingTokens: tokens, - rewardTokens: [], + rewardTokens: rewardTokens, poolMeta: vault.vaultMeta, } return pool; @@ -273,6 +307,7 @@ interface Vault { name: string; chain: string; underlyingTokens: Array; + rewardTokens: Array; isAsset0Main: boolean; url: string; vaultMeta: string; //other info @@ -283,6 +318,7 @@ interface Vault { shareSupply?: bn; shareSupplyBefore?: bn; apy?: number; + rewardApy?: number; } interface UnderlyingToken { @@ -294,6 +330,22 @@ interface UnderlyingToken { shareTokenApr: number; } +interface RewardBook { + address: string; + endTime: string; + startTime: string; + totalReward: string; +} + +interface RewardToken { + address: string; + chain: string; + decimals: number; + rewardBook: RewardBook; + symbol: string; + type: string; +} + interface Performance { tvlUsd: number; apy: number;