@bitte-ai/agent-sdk
Version:
Agent SDK for Bitte Protocol
121 lines (120 loc) • 4.39 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.safeTxServiceUrlFor = safeTxServiceUrlFor;
exports.getSafeBalances = getSafeBalances;
exports.flatSafeBalances = flatSafeBalances;
exports.zerionToTokenBalance = zerionToTokenBalance;
exports.zerionToTokenBalances = zerionToTokenBalances;
const viem_1 = require("viem");
const zerion_sdk_1 = require("zerion-sdk");
const misc_1 = require("../misc");
const client_1 = require("./client");
const SAFE_NETWORKS = {
1: "mainnet",
10: "optimism",
56: "binance",
100: "gnosis-chain",
137: "polygon",
8453: "base",
42161: "arbitrum",
43114: "avalanche",
11155111: "sepolia",
};
function safeTxServiceUrlFor(chainId) {
const network = SAFE_NETWORKS[chainId];
if (!network) {
console.warn(`Unsupported Safe Transaction Service chainId=${chainId}`);
return undefined;
}
return `https://safe-transaction-${network}.safe.global`;
}
async function getSafeBalances(chainId, address, zerionKey) {
const client = await (0, client_1.getClientForChain)(chainId);
const codeAt = await client.getCode({ address });
if (!codeAt) {
// Not a Safe - Get balances from Zerion.
return getZerionBalances(chainId, address, zerionKey);
}
const baseUrl = safeTxServiceUrlFor(chainId);
if (!baseUrl) {
console.warn(`Chain ID ${chainId} not supported by Safe Transaction Service`);
return [];
}
const trusted = false; // Avalanche USDC not trusted: 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E
const exclude_spam = chainId === 11155111 ? false : true;
const url = `${baseUrl}/api/v1/safes/${(0, viem_1.checksumAddress)(address)}/balances/?trusted=${trusted}&exclude_spam=${exclude_spam}`;
try {
const response = await fetch(url, {
headers: {
accept: "application/json",
},
});
if (!response.ok) {
if (response.status === 404) {
console.warn(`Safe not found for ${address}`);
}
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
}
catch (error) {
console.warn("Error fetching Safe balances:", error);
return getZerionBalances(chainId, address, zerionKey);
}
}
async function getZerionBalances(chainId, address, zerionKey) {
if (!zerionKey) {
return [];
}
console.info("Zerion Key provided - using Zerion");
// Not a Safe, try Zerion
try {
const zerion = new zerion_sdk_1.ZerionAPI(zerionKey);
// TODO(bh2smith): This is not super efficient, but it works for now.
// Zerion API has its own network filter (but its not by chainID).
const balances = await zerion.ui.getUserBalances(address, {
options: { supportedChains: [chainId] },
});
return zerionToTokenBalances(balances.tokens);
}
catch (error) {
console.error("Error fetching Zerion balances:", error);
return [];
}
}
async function flatSafeBalances(chainId, address) {
const balances = await getSafeBalances(chainId, address);
// Flatten the balance Response
return {
balances: balances.map((b) => ({
token: b.tokenAddress,
balance: b.balance,
symbol: b.token?.symbol || null,
decimals: b.token?.decimals || 18,
logoUri: b.token?.logoUri || null,
})),
};
}
// TODO(bh2smith): Move this into Zerion SDK
function zerionToTokenBalance(userToken) {
const { meta, balances } = userToken;
return {
tokenAddress: meta.contractAddress || null,
token: {
name: meta.name,
symbol: meta.symbol,
decimals: meta.decimals,
logoUri: meta.tokenIcon || "",
},
balance: (0, viem_1.parseUnits)((0, misc_1.scientificToDecimal)(balances.balance), meta.decimals).toString(),
fiatBalance: balances.usdBalance.toFixed(2),
fiatConversion: (balances.price || 0).toFixed(2),
};
}
// Helper function to convert array of UserTokens to TokenBalances
function zerionToTokenBalances(userTokens) {
return userTokens
.filter((token) => !token.meta.isSpam) // Filter out spam tokens
.map(zerionToTokenBalance);
}