From 18a53d2a1bce977393ec14409583f02d872d3494 Mon Sep 17 00:00:00 2001 From: 0age <37939117+0age@users.noreply.github.com> Date: Mon, 3 Mar 2025 08:51:26 -0500 Subject: [PATCH] add URL and fix some type issues --- README.md | 4 + src/client/App.tsx | 235 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 195 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index d179706..709e910 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ Neutrofill is an automated filler bot that processes inbound transaction request - Automated cross-chain token rebalancing via bridging using the Across Protocol - Automated single-chain token rebalancing via swaps using the Uniswap API +A hosted version can be accessed at [neutrofill.com](https://neutrofill.com) — note that this service is meant to serve as a reference for demonstration purposes and no guarantees about its uptime or reliability are provided. + +This project is mostly meant to serve as a starting point until more mature standards like the Open Intents Framework have fully integrated with The Compact. + ## Getting Started ### Prerequisites diff --git a/src/client/App.tsx b/src/client/App.tsx index dfd0cf5..9ca7fb5 100644 --- a/src/client/App.tsx +++ b/src/client/App.tsx @@ -27,6 +27,70 @@ interface EthPrices { }; } +// Define the type expected by WebSocketClient.onAggregateBalances +interface WebSocketAggregateBalances { + chainBalances: Record< + number, + { + ETH: number; + WETH: number; + USDC: number; + total: number; + percentageOfTotal: number; + } + >; + tokenBalances: { + ETH: number; + WETH: number; + USDC: number; + }; + totalBalance: number; + lastUpdated: number; + timestamp: string; +} + +// Define the structure of the data received from the WebSocket +interface WebSocketData { + type: string; + chainBalances: Record< + string, + { + tokens: { + ETH: string | number; + WETH: string | number; + USDC: string | number; + }; + usd: { + ETH: number; + WETH: number; + USDC: number; + total: number; + }; + percentageOfTotal: number; + } + >; + tokenBalances: { + tokens: { + ETH: string | number; + WETH: string | number; + USDC: string | number; + }; + usd: { + ETH: number; + WETH: number; + USDC: number; + }; + percentages: { + ETH: number; + WETH: number; + USDC: number; + }; + }; + totalBalance: number; + lastUpdated: number; + timestamp: string; +} + interface AggregateBalances { chainBalances: Record< number, @@ -119,8 +183,82 @@ export function App() { })); }; - ws.onAggregateBalances = (newAggregateBalances: AggregateBalances) => { - setAggregateBalances(newAggregateBalances); + ws.onAggregateBalances = (data: unknown) => { + try { + // Cast to WebSocketData for transformation + const rawData = data as WebSocketData; + + if (rawData && rawData.type === "aggregate_balance_update") { + // Transform the data to match our AggregateBalances interface + const transformedChainBalances: Record< + number, + { + tokens: { + ETH: string; + WETH: string; + USDC: string; + }; + usd: { + ETH: number; + WETH: number; + USDC: number; + total: number; + }; + percentageOfTotal: number; + } + > = {}; + + // Transform chain balances + for (const [chainId, values] of Object.entries( + rawData.chainBalances || {} + )) { + if (!values) continue; + + transformedChainBalances[Number(chainId)] = { + tokens: { + ETH: String(values.tokens?.ETH || "0"), + WETH: String(values.tokens?.WETH || "0"), + USDC: String(values.tokens?.USDC || "0"), + }, + usd: { + ETH: Number(values.usd?.ETH || 0), + WETH: Number(values.usd?.WETH || 0), + USDC: Number(values.usd?.USDC || 0), + total: Number(values.usd?.total || 0), + }, + percentageOfTotal: Number(values.percentageOfTotal || 0), + }; + } + + const transformedData: AggregateBalances = { + chainBalances: transformedChainBalances, + tokenBalances: { + tokens: { + ETH: String(rawData.tokenBalances?.tokens?.ETH || "0"), + WETH: String(rawData.tokenBalances?.tokens?.WETH || "0"), + USDC: String(rawData.tokenBalances?.tokens?.USDC || "0"), + }, + usd: { + ETH: Number(rawData.tokenBalances?.usd?.ETH || 0), + WETH: Number(rawData.tokenBalances?.usd?.WETH || 0), + USDC: Number(rawData.tokenBalances?.usd?.USDC || 0), + }, + percentages: { + ETH: Number(rawData.tokenBalances?.percentages?.ETH || 0), + WETH: Number(rawData.tokenBalances?.percentages?.WETH || 0), + USDC: Number(rawData.tokenBalances?.percentages?.USDC || 0), + }, + }, + totalBalance: Number(rawData.totalBalance || 0), + lastUpdated: Number(rawData.lastUpdated || Date.now()), + timestamp: String(rawData.timestamp || new Date().toISOString()), + }; + + setAggregateBalances(transformedData); + } + } catch (error) { + console.error("Error handling aggregate balances:", error); + } }; ws.onFillRequest = ( @@ -270,18 +408,21 @@ export function App() { {formatEthBalance( - aggregateBalances?.tokenBalances.tokens.ETH ?? "0" - )} - {aggregateBalances?.tokenBalances.percentages.ETH > - 0 && ( - - ( - {aggregateBalances.tokenBalances.percentages.ETH.toFixed( - 1 - )} - %) - + aggregateBalances?.tokenBalances?.tokens?.ETH ?? + "0" )} + {aggregateBalances?.tokenBalances?.percentages + ?.ETH != null && + aggregateBalances.tokenBalances.percentages.ETH > + 0 && ( + + ( + {aggregateBalances.tokenBalances.percentages.ETH.toFixed( + 1 + )} + %) + + )} @@ -305,19 +446,21 @@ export function App() { {formatEthBalance( - aggregateBalances?.tokenBalances.tokens.WETH ?? + aggregateBalances?.tokenBalances?.tokens?.WETH ?? "0" )} - {aggregateBalances?.tokenBalances.percentages.WETH > - 0 && ( - - ( - {aggregateBalances.tokenBalances.percentages.WETH.toFixed( - 1 - )} - %) - - )} + {aggregateBalances?.tokenBalances?.percentages + ?.WETH != null && + aggregateBalances.tokenBalances.percentages.WETH > + 0 && ( + + ( + {aggregateBalances.tokenBalances.percentages.WETH.toFixed( + 1 + )} + %) + + )} @@ -341,19 +484,21 @@ export function App() { {formatUsdcBalance( - aggregateBalances?.tokenBalances.tokens.USDC ?? + aggregateBalances?.tokenBalances?.tokens?.USDC ?? "0" )} - {aggregateBalances?.tokenBalances.percentages.USDC > - 0 && ( - - ( - {aggregateBalances.tokenBalances.percentages.USDC.toFixed( - 1 - )} - %) - - )} + {aggregateBalances?.tokenBalances?.percentages + ?.USDC != null && + aggregateBalances.tokenBalances.percentages.USDC > + 0 && ( + + ( + {aggregateBalances.tokenBalances.percentages.USDC.toFixed( + 1 + )} + %) + + )} @@ -370,15 +515,17 @@ export function App() { .total ?? 0 ).toFixed(2)} {aggregateBalances?.chainBalances[chainId] - ?.percentageOfTotal > 0 && ( - - ( - {aggregateBalances.chainBalances[ - chainId - ].percentageOfTotal.toFixed(1)} - %) - - )} + ?.percentageOfTotal != null && + aggregateBalances.chainBalances[chainId] + .percentageOfTotal > 0 && ( + + ( + {aggregateBalances.chainBalances[ + chainId + ].percentageOfTotal.toFixed(1)} + %) + + )} ))}