diff --git a/src/core/helpers/lp.ts b/src/core/helpers/lp.ts new file mode 100644 index 0000000..656b81a --- /dev/null +++ b/src/core/helpers/lp.ts @@ -0,0 +1,42 @@ +export function destructCoinStorePoolStr(type: string): string[] { + if (type.length === 0) return []; + return ( + type + // + .split('<')[2] + .split('>')[0] + .replaceAll(' ', '') + .split(',') + ); +} + +export function destructCoinStorePoolV1Str(type: string): string[] { + if (type.length === 0) return []; + return ( + type + // + .split('<')[1] + .split('>')[0] + .replaceAll(' ', '') + .split(',') + ); +} + +export function getTokensByLpType(type: string): {coinX: string, coinY: string} | undefined { + let result: string[] = []; + try { + const tokens = destructCoinStorePoolV1Str(type) + result = tokens + } catch { + try { + result = destructCoinStorePoolStr(type) + } catch { + result = [] + } + } + if (!result.length) return undefined; + return { + coinX: result[0], + coinY: result[1], + } +} \ No newline at end of file diff --git a/src/core/liquidswap.ts b/src/core/liquidswap.ts index 8f32c12..5caad5e 100644 --- a/src/core/liquidswap.ts +++ b/src/core/liquidswap.ts @@ -1,4 +1,6 @@ import { BE_URL } from "../constants.js"; +import { getTokensByLpType } from "./helpers/lp.js"; +import { getTokenBalancesQuery } from "./queries/token-balances.js"; export class Liquidswap { private readonly url = BE_URL @@ -27,6 +29,55 @@ export class Liquidswap { const response = await this.fetch(`/sentio/historical_tvls?start=${from}&end=${to}`) return response } + + public async getUsersBalances(address: string, offset: number = 0) { + + const data = await fetch('https://api.mainnet.aptoslabs.com/v1/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${process.env.APTOS_API_KEY}`, + }, + body: JSON.stringify({ query: getTokenBalancesQuery(address, offset) }), + }) + + const response = await data.json() + + const tokens = response.data.current_fungible_asset_balances as {asset_type: string, amount: string}[]; + + const lsTokens = await this.getTokens() + const lsTokensMap = new Map(lsTokens.map((token: any) => [token.type, token])) + + const tokensWithNames = tokens.map((token: any) => { + const lsToken = lsTokensMap.get(token.asset_type) as {name: string, symbol: string, decimals: number, logo_url: string} + + if (!lsToken) { + const tokens = getTokensByLpType(token.asset_type) + if (tokens) { + const coinX = lsTokensMap.get(tokens.coinX) as {name: string, symbol: string, decimals: number, logo_url: string} + const coinY = lsTokensMap.get(tokens.coinY) as {name: string, symbol: string, decimals: number, logo_url: string} + return { + ...token, + name: `${coinX?.name} / ${coinY?.name}`, + symbol: `${coinX?.symbol} / ${coinY?.symbol}`, + isLp: true, + coinX, + coinY + } + } + } + + return { + ...token, + name: lsToken?.name, + symbol: lsToken?.symbol, + decimals: lsToken?.decimals, + logo: lsToken?.logo_url, + } + }) + + return tokensWithNames + } } export const liquidswap = new Liquidswap() \ No newline at end of file diff --git a/src/core/queries/token-balances.ts b/src/core/queries/token-balances.ts new file mode 100644 index 0000000..0bb77dd --- /dev/null +++ b/src/core/queries/token-balances.ts @@ -0,0 +1,15 @@ +export const getTokenBalancesQuery = (address: string, offset: number = 0) => { + return ` + query GetTokenBalances { + current_fungible_asset_balances( + where: {owner_address: {_eq: "${address}"}, token_standard: {_eq: "v1"}} + offset: ${offset} + limit: 100 + order_by: {amount: desc} + ) { + asset_type + amount + } +} +` +} \ No newline at end of file diff --git a/src/tools/get-balances-by-address/get-balances-by-address.ts b/src/tools/get-balances-by-address/get-balances-by-address.ts new file mode 100644 index 0000000..2c0fcbd --- /dev/null +++ b/src/tools/get-balances-by-address/get-balances-by-address.ts @@ -0,0 +1,27 @@ +import type { Tool } from "../../types/tools.js"; +import { liquidswap } from "../../core/liquidswap.js"; +import { z } from "zod"; + +const getBalancesByAddress: Tool['execute'] = async (args) => { + const data = await liquidswap.getUsersBalances(args.address); + + return { + content: data.map((item: any) => ({ + type: "text", + text: JSON.stringify(item, null, 2) + })), + }; +}; + + +const parameters = z.object({ + address: z.string().describe("Aptos address to get the balances for"), +}); + + +export const getBalancesByAddressTool: Tool = { + name: "get_balances_by_address", + description: "Get balances for a given Aptos address", + execute: getBalancesByAddress, + parameters, +}; diff --git a/src/tools/get-balances-by-address/index.ts b/src/tools/get-balances-by-address/index.ts new file mode 100644 index 0000000..39b88a5 --- /dev/null +++ b/src/tools/get-balances-by-address/index.ts @@ -0,0 +1 @@ +export * from "./get-balances-by-address.js"; \ No newline at end of file diff --git a/src/tools/index.ts b/src/tools/index.ts index 76d6629..82c9842 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -3,12 +3,14 @@ import { getTokensTool } from "./get-tokens/get-tokens.js"; import { getPoolsTool } from "./get-pools/get-pools.js"; import { getPoolsHistoricalAprsTool } from "./get-pools-historical-aprs/index.js"; import { getPoolsHistoricalTvlsTool } from "./get-pools-historical-tvls/index.js"; +import { getBalancesByAddressTool } from "./get-balances-by-address/index.js"; const tools: Tool[] = [ getTokensTool, getPoolsTool, getPoolsHistoricalAprsTool, getPoolsHistoricalTvlsTool, + getBalancesByAddressTool, ]; export default tools; \ No newline at end of file