diff --git a/src/components/Portfolio.tsx b/src/components/Portfolio.tsx index 579ea7a..e10ecfb 100644 --- a/src/components/Portfolio.tsx +++ b/src/components/Portfolio.tsx @@ -832,6 +832,15 @@ const Portfolio = () => { for (const token of tokens) { if (token.underlyingContractId && token.poolId) { + if ( + isMarketsTableExcludedMarket( + networkId as NetworkId, + token.poolId, + token.configKey + ) + ) { + continue; + } const rowTokenConfigRaw = getTokenConfig( networkId as NetworkId, token.configKey ?? token.originalSymbol ?? token.symbol @@ -2983,10 +2992,9 @@ const Portfolio = () => { try { // fetch market from node api for accurate position info // Fetch fresh market data and global data first - const markets = filterPortfolioVisibleMarketRows( - currentNetwork, - await fetchAllMarkets(currentNetwork) - ); + const markets = await fetchAllMarkets(currentNetwork, { + excludeMarketsTableHidden: true, + }); // const marketDataResponse = // await dorkfiAPIService.getAllMarketDataByNetwork(currentNetwork); // const freshMarketData = marketDataResponse.success diff --git a/src/components/SupplyBorrowCongrats.tsx b/src/components/SupplyBorrowCongrats.tsx index a97f2e4..a0201d0 100644 --- a/src/components/SupplyBorrowCongrats.tsx +++ b/src/components/SupplyBorrowCongrats.tsx @@ -1,12 +1,13 @@ import React from "react"; import { CheckCircle2, Sparkles } from "lucide-react"; import DorkFiButton from "@/components/ui/DorkFiButton"; +import LpPairIconStack from "@/components/pools/LpPairIconStack"; interface SupplyBorrowCongratsProps { transactionType: "deposit" | "borrow" | "withdraw" | "repay"; asset: string; assetIcon: string; - /** Stacked underlying icons for Tinyman LP pair markets. */ + /** Underlying pair icons for LP markets (preferred over `assetIcon` when set). */ assetPairIcons?: { asset1Icon: string; asset2Icon: string }; amount: string; onViewTransaction: () => void; @@ -54,29 +55,24 @@ const SupplyBorrowCongrats: React.FC = ({ - {assetPairIcons ? ( -
- + {assetPairIcons ? ( + + ) : ( -
- ) : ( - {`${asset} - )} + )} +

diff --git a/src/components/SupplyBorrowModal.tsx b/src/components/SupplyBorrowModal.tsx index d0690ad..72a97ef 100644 --- a/src/components/SupplyBorrowModal.tsx +++ b/src/components/SupplyBorrowModal.tsx @@ -3201,9 +3201,7 @@ const SupplyBorrowModal = ({ }; const handleViewTransaction = () => { - if (!transactionId) { - throw new Error("Transaction ID not found"); - } + if (!transactionId) return; const net = (transactionNetworkId || network || currentNetwork) as NetworkId; window.open(getExplorerTransactionUrl(net, transactionId), "_blank"); }; @@ -3290,6 +3288,7 @@ const SupplyBorrowModal = ({ onGoToPortfolio={handleGoToPortfolio} onMakeAnother={handleMakeAnother} onClose={onClose} + viewTransactionDisabled={!transactionId} /> ) : ( diff --git a/src/components/WithdrawModal.tsx b/src/components/WithdrawModal.tsx index f0c6e64..daadea5 100644 --- a/src/components/WithdrawModal.tsx +++ b/src/components/WithdrawModal.tsx @@ -1378,10 +1378,7 @@ const WithdrawModal = ({ } assetIcon={tokenIcon} assetPairIcons={tokenPairIcons} - amount={ - successSnapshot?.amount ?? - (amount !== "" ? amount.toString() : "") - } + amount={amount !== "" ? amount.toString() : ""} onViewTransaction={handleViewTransaction} onGoToPortfolio={handleGoToPortfolio} onMakeAnother={handleMakeAnother} diff --git a/src/config/__tests__/lendingPoolByMarketContract.test.ts b/src/config/__tests__/lendingPoolByMarketContract.test.ts index fbf4303..db7426e 100644 --- a/src/config/__tests__/lendingPoolByMarketContract.test.ts +++ b/src/config/__tests__/lendingPoolByMarketContract.test.ts @@ -4,31 +4,39 @@ import { getPoolCMarketContractIds, getPoolCLendingPoolId, getPoolELendingPoolId, + getPoolFLendingPoolId, getUnitLendingCollateralContractIds, getUnitLendingWadBorrowMarketConfig, getUnitLendingWadBorrowMarketRef, + getUsdcLpLendingCollateralContractIds, + getUsdcLpLendingWadBorrowMarketConfig, + getUsdcLpLendingWadBorrowMarketRef, getWadLpLendingCollateralContractIds, getWadLpLendingWadBorrowMarketConfig, getWadLpLendingWadBorrowMarketRef, getWadSupplyMarketConfigsExcludingPoolCBorrow, isPoolCMarketContract, isUnitLpCollateralMarketContract, + isUsdcLpCollateralMarketContract, isWadLpCollateralMarketContract, } from "@/config"; const POOL_C = "3578814346"; const POOL_E = "3585829377"; +const POOL_F = "3589083110"; const WAD_STOKEN = "3333688448"; const WAD_NTOKEN_POOL_C = "3583297246"; const WAD_NTOKEN_POOL_E = "3585972631"; +const WAD_NTOKEN_POOL_F = "3589241382"; const LP_UNIT_ALGO = "3577729953"; const LP_UNIT_GOBTC = "3577777819"; const LP_WAD_UNIT = "3577783311"; describe("LENDING_POOL_BY_MARKET_CONTRACT", () => { - it("returns Pool C and Pool E ids for algorand-mainnet", () => { + it("returns Pool C, Pool E, and Pool F ids for algorand-mainnet", () => { expect(getPoolCLendingPoolId("algorand-mainnet")).toBe(POOL_C); expect(getPoolELendingPoolId("algorand-mainnet")).toBe(POOL_E); + expect(getPoolFLendingPoolId("algorand-mainnet")).toBe(POOL_F); }); it("maps WAD SToken and TMPOOL2 nt200 contracts to Pool C", () => { @@ -88,6 +96,36 @@ describe("LENDING_POOL_BY_MARKET_CONTRACT", () => { ).toBe(POOL_E); } }); + + it("maps Pool F USDC TMPOOL2 nt200 contracts to Pool F", () => { + for (const contractId of getUsdcLpLendingCollateralContractIds( + "algorand-mainnet" + )) { + expect( + getLendingPoolIdForMarketContract("algorand-mainnet", contractId) + ).toBe(POOL_F); + expect( + isUsdcLpCollateralMarketContract("algorand-mainnet", contractId) + ).toBe(true); + } + }); + + it("points USDC LP collateral at WAD @ Pool F (tokens.WAD row)", () => { + expect(getUsdcLpLendingWadBorrowMarketRef("algorand-mainnet")).toEqual({ + poolId: POOL_F, + contractId: WAD_STOKEN, + nTokenId: WAD_NTOKEN_POOL_F, + configKey: "WAD", + }); + + const wadMarket = getUsdcLpLendingWadBorrowMarketConfig("algorand-mainnet"); + expect(wadMarket).toMatchObject({ + poolId: POOL_F, + contractId: WAD_STOKEN, + nTokenId: WAD_NTOKEN_POOL_F, + symbol: "WAD", + }); + }); }); describe("UNIT lending → Pool C WAD borrow market", () => { diff --git a/src/config/__tests__/marketsTableExclusion.test.ts b/src/config/__tests__/marketsTableExclusion.test.ts index 0c39684..52ef4c4 100644 --- a/src/config/__tests__/marketsTableExclusion.test.ts +++ b/src/config/__tests__/marketsTableExclusion.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { + getMarketsTableVisibleTokensWithDisplayInfo, getPortfolioVisibleTokens, isMarketsTableExcludedMarket, isMarketsTableExcludedPool, @@ -8,14 +9,16 @@ import { const POOL_C = "3578814346"; const POOL_E = "3585829377"; +const POOL_F = "3589083110"; describe("markets table Pool C exclusion", () => { - it("excludes Pool C and Pool E at pool level", () => { + it("excludes Pool C, Pool E, and Pool F at pool level", () => { expect(isMarketsTableExcludedPool("algorand-mainnet", POOL_C)).toBe(true); expect(isMarketsTableExcludedPool("algorand-mainnet", POOL_E)).toBe(true); + expect(isMarketsTableExcludedPool("algorand-mainnet", POOL_F)).toBe(true); }); - it("hides Pool C and Pool E LP markets from the table", () => { + it("hides Pool C, Pool E, and Pool F LP markets from the table", () => { expect( isMarketsTableExcludedMarket( "algorand-mainnet", @@ -30,12 +33,25 @@ describe("markets table Pool C exclusion", () => { "LP_TMPOOL2_WAD_ALGO" ) ).toBe(true); + expect( + isMarketsTableExcludedMarket( + "algorand-mainnet", + POOL_F, + "LP_TMPOOL2_USDC_ALGO" + ) + ).toBe(true); }); - it("keeps WAD on Pool C visible (exception)", () => { + it("keeps WAD on Pool C, Pool E, and Pool F visible (exception)", () => { expect( isMarketsTableExcludedMarket("algorand-mainnet", POOL_C, "WAD") ).toBe(false); + expect( + isMarketsTableExcludedMarket("algorand-mainnet", POOL_E, "WAD") + ).toBe(false); + expect( + isMarketsTableExcludedMarket("algorand-mainnet", POOL_F, "WAD") + ).toBe(false); }); it("does not exclude WAD on Pool A", () => { @@ -44,36 +60,22 @@ describe("markets table Pool C exclusion", () => { ).toBe(false); }); - it("excludes Pool C LP from portfolio visible tokens", () => { - const visible = getPortfolioVisibleTokens("algorand-mainnet"); + it("omits Pool F TMPOOL2 rows from visible token list", () => { + const visible = getMarketsTableVisibleTokensWithDisplayInfo( + "algorand-mainnet" + ); expect( visible.some( - (t) => - String(t.poolId) === POOL_C && - t.configKey === "LP_TMPOOL2_UNIT_ALGO" + (token) => + token.configKey === "LP_TMPOOL2_USDC_ALGO" && + String(token.poolId) === POOL_F ) ).toBe(false); expect( visible.some( - (t) => String(t.poolId) === POOL_C && t.configKey === "WAD" + (token) => + token.configKey === "WAD" && String(token.poolId) === POOL_F ) ).toBe(true); }); - - it("resolves portfolio exclusion by market contract id", () => { - expect( - isPortfolioExcludedMarketContract( - "algorand-mainnet", - POOL_C, - "3577729953" - ) - ).toBe(true); - expect( - isPortfolioExcludedMarketContract( - "algorand-mainnet", - POOL_C, - "3333688448" - ) - ).toBe(false); - }); }); diff --git a/src/config/index.ts b/src/config/index.ts index d5188d9..f29e5a2 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -2109,6 +2109,7 @@ const algorandProdBMarket = "3345940978"; // B Sub-prime const algorandProdCMarket = "3578814346"; // C UNIT Pair LPs const algorandProdDMarket = "3526240577"; // D Folks Markets const algorandProdEMarket = "3585829377"; // C WAD Pair LPs +const algorandProdFMarket = "3589083110"; // C USDC Pair LPs const algorandProdPriceOracle = "3333688500"; const algorandProdLiquidationEngine = undefined; const algorandProdGovernance = { @@ -2131,10 +2132,12 @@ const algorandProdLendingPools = [ algorandProdCMarket, algorandProdDMarket, algorandProdEMarket, + algorandProdFMarket, ]; const algorandProdCLendingPools = [ algorandProdCMarket, algorandProdEMarket, + algorandProdFMarket, ]; const algorandProdContracts: ContractConfig = { lendingPools: algorandProdLendingPools, @@ -3048,7 +3051,20 @@ const algorandProdTokens: { [symbol: string]: TokenConfig | TokenConfig[] } = { logoPath: "/lovable-uploads/WAD_fixed.png", tokenStandard: "arc200-exchange", dataAddedAt: "2026-06-03T00:00:00.000Z", - } + }, + // Pool F USDC-pair LP collateral → WAD borrow + { + assetId: "3334160924", + contractId: "3333688448", + poolId: "3589083110", + nTokenId: "3589241382", + decimals: 6, + name: "WAD", + symbol: "WAD", + logoPath: "/lovable-uploads/WAD_fixed.png", + tokenStandard: "arc200-exchange", + dataAddedAt: "2026-06-05T00:00:00.000Z", + }, ], // TMPOOL2 3157974960 6 3577729953 // name: "TinymanPool2.0 UNIT-ALGO", @@ -3169,6 +3185,78 @@ const algorandProdTokens: { [symbol: string]: TokenConfig | TokenConfig[] } = { tokenStandard: "asa", dataAddedAt: "2026-06-03T00:00:00.000Z", }, + // ----------------- + // TMPOOL2 1002590888 6 3589026317 + // name: "TinymanPool2.0 USDC-ALGO", + // symbol: "TMPOOL2", + // decimals: 6, + // contractId: 3589026317 + LP_TMPOOL2_USDC_ALGO: { + assetId: "1002590888", + contractId: "3589026317", + poolId: "3589083110", + nTokenId: "3589091424", + decimals: 6, + name: "TinymanPool2.0 USDC-ALGO", + symbol: "TMPOOL2", + logoPath: "/lovable-uploads/LP_TMPOOL2_USDC_ALGO.png", + tokenStandard: "asa", + dataAddedAt: "2026-06-05T00:00:00.000Z", + }, + // ----------------- + // TMPOOL2 2537254960 6 3589029580 + // name: "TinymanPool2.0 TALGO-USDC", + // symbol: "TMPOOL2", + // decimals: 6, + // contractId: 3589029580 + LP_TMPOOL2_TALGO_USDC: { + assetId: "2537254960", + contractId: "3589029580", + poolId: "3589083110", + nTokenId: "3589093059", + decimals: 6, + name: "TinymanPool2.0 TALGO-USDC", + symbol: "TMPOOL2", + logoPath: "/lovable-uploads/LP_TMPOOL2_TALGO_USDC.png", + tokenStandard: "asa", + dataAddedAt: "2026-06-05T00:00:00.000Z", + }, + // ----------------- + // TMPOOL2 3196310546 6 3589032117 + // name: "TinymanPool2.0 HAY-USDC", + // symbol: "TMPOOL2", + // decimals: 6, + // contractId: 3589032117 + LP_TMPOOL2_HAY_USDC: { + assetId: "3196310546", + contractId: "3589032117", + poolId: "3589083110", + nTokenId: "3589094325", + decimals: 6, + name: "TinymanPool2.0 HAY-USDC", + symbol: "TMPOOL2", + logoPath: "/lovable-uploads/LP_TMPOOL2_HAY_USDC.png", + tokenStandard: "asa", + dataAddedAt: "2026-06-05T00:00:00.000Z", + }, + // ----------------- + // TMPOOL2 2741116468 6 3589036846 + // name: "TinymanPool2.0 ALPHA-USDC", + // symbol: "TMPOOL2", + // decimals: 6, + // contractId: 3589036846 + LP_TMPOOL2_ALPHA_USDC: { + assetId: "2741116468", + contractId: "3589036846", + poolId: "3589083110", + nTokenId: "3589095663", + decimals: 6, + name: "TinymanPool2.0 ALPHA-USDC", + symbol: "TMPOOL2", + logoPath: "/lovable-uploads/LP_TMPOOL2_ALPHA_USDC.png", + tokenStandard: "asa", + dataAddedAt: "2026-06-05T00:00:00.000Z", + }, }; const algorandMainnetProdConfig: NetworkConfig = { networkId: "algorand-mainnet", @@ -3469,6 +3557,7 @@ export const marketLabelMap: Record = { /** Third prod lending pool (array order is A, B, C, D). */ "algorand-mainnet-3526240577": "D", "algorand-mainnet-3585829377": "E", + "algorand-mainnet-3589083110": "F", }; /** @@ -3640,12 +3729,16 @@ export const getLendingPoolLabel = ( /** * Lending pools whose LP nt200 markets are omitted from the Markets table * (still in config for Pools page, Admin, etc.). - * Algorand prod C/E pools hold Tinyman LP markets — surfaced on the Pools page instead. + * Algorand prod C/E/F pools hold Tinyman LP markets — surfaced on the Pools page instead. */ const MARKETS_TABLE_EXCLUDED_POOL_IDS: Partial< Record > = { - "algorand-mainnet": [algorandProdCMarket, algorandProdEMarket], + "algorand-mainnet": [ + algorandProdCMarket, + algorandProdEMarket, + algorandProdFMarket, + ], }; /** @@ -3665,6 +3758,10 @@ const LENDING_POOL_BY_MARKET_CONTRACT: Partial< "3577799583": algorandProdEMarket, "3578394082": algorandProdEMarket, "3578387558": algorandProdEMarket, + "3589026317": algorandProdFMarket, + "3589029580": algorandProdFMarket, + "3589032117": algorandProdFMarket, + "3589036846": algorandProdFMarket, }, }; @@ -3728,6 +3825,33 @@ const WAD_LP_LENDING_WAD_BORROW_MARKET: Partial< }, }; +/** + * USDC-base TMPOOL2 nt200 collateral markets on Pool F that borrow against + * {@link USDC_LP_LENDING_WAD_BORROW_MARKET} when configured. + */ +const USDC_LP_LENDING_COLLATERAL_CONTRACT_IDS: Partial< + Record +> = { + "algorand-mainnet": [ + "3589026317", + "3589029580", + "3589032117", + "3589036846", + ], +}; + +/** WAD borrow market on Pool F paired with USDC-base TMPOOL2 collateral. */ +const USDC_LP_LENDING_WAD_BORROW_MARKET: Partial< + Record +> = { + "algorand-mainnet": { + poolId: algorandProdFMarket, + contractId: algorandProdSToken, + nTokenId: "3589241382", + configKey: "WAD", + }, +}; + /** nt200 contract ids for UNIT TMPOOL2 collateral markets on Pool C. */ export function getUnitLendingCollateralContractIds( networkId: NetworkId | string | null | undefined @@ -3829,6 +3953,45 @@ export function getWadLpLendingWadBorrowMarketConfig( ); } +/** nt200 contract ids for USDC-base TMPOOL2 collateral markets on Pool F. */ +export function getUsdcLpLendingCollateralContractIds( + networkId: NetworkId | string | null | undefined +): readonly string[] { + if (!networkId) return []; + return USDC_LP_LENDING_COLLATERAL_CONTRACT_IDS[networkId as NetworkId] ?? []; +} + +/** True when `marketContractId` is a USDC-base LP collateral market on Pool F. */ +export function isUsdcLpCollateralMarketContract( + networkId: NetworkId | string | null | undefined, + marketContractId: string | number | null | undefined +): boolean { + if (marketContractId == null || String(marketContractId) === "") return false; + const id = String(marketContractId); + return getUsdcLpLendingCollateralContractIds(networkId).some( + (contractId) => contractId === id + ); +} + +/** Pool F WAD borrow market ref paired with USDC-base TMPOOL2 collateral. */ +export function getUsdcLpLendingWadBorrowMarketRef( + networkId: NetworkId | string | null | undefined +): WadLpLendingWadBorrowMarketRef | null { + if (!networkId) return null; + return USDC_LP_LENDING_WAD_BORROW_MARKET[networkId as NetworkId] ?? null; +} + +/** Resolve WAD borrow {@link TokenConfig} for Pool F USDC LP lending. */ +export function getUsdcLpLendingWadBorrowMarketConfig( + networkId: NetworkId | string | null | undefined +): TokenConfig | null { + if (!networkId) return null; + return resolveWadBorrowMarketConfigFromRef( + networkId as NetworkId, + getUsdcLpLendingWadBorrowMarketRef(networkId) + ); +} + /** WAD deposit markets on other pools (excludes TMPOOL collateral borrow rows and sToken mint). */ export function getWadSupplyMarketConfigsExcludingPoolCBorrow( networkId: NetworkId | string | null | undefined @@ -3837,6 +4000,7 @@ export function getWadSupplyMarketConfigsExcludingPoolCBorrow( const borrowRefs = [ getUnitLendingWadBorrowMarketRef(networkId), getWadLpLendingWadBorrowMarketRef(networkId), + getUsdcLpLendingWadBorrowMarketRef(networkId), ].filter((ref): ref is UnitLendingWadBorrowMarketRef => ref != null); const wadToken = getNetworkConfig(networkId as NetworkId).tokens?.WAD; const configs: TokenConfig[] = Array.isArray(wadToken) @@ -3876,6 +4040,16 @@ export function getPoolELendingPoolId( return String(excluded[1]); } +/** Lending pool app id for Pool F (USDC-base TMPOOL2 markets) on this network. */ +export function getPoolFLendingPoolId( + networkId: NetworkId | string | null | undefined +): string | null { + if (!networkId) return null; + const excluded = MARKETS_TABLE_EXCLUDED_POOL_IDS[networkId as NetworkId]; + if (!excluded || excluded.length < 3) return null; + return String(excluded[2]); +} + /** Resolve lending pool app id from nt200 / underlying market `contractId`. */ export function getLendingPoolIdForMarketContract( networkId: NetworkId | string | null | undefined, @@ -3930,7 +4104,7 @@ export function isMarketsTableExcludedPool( /** * True when a configured market row should not appear on the Markets table (or matching portfolio market lists). - * Pool C/E LP (`LP_TMPOOL2_*`) stays hidden; WAD borrow on those pools remains visible. + * Pool C/E/F LP (`LP_TMPOOL2_*`) stays hidden; WAD borrow on those pools remains visible. */ export function isMarketsTableExcludedMarket( networkId: NetworkId | string | null | undefined, @@ -4749,6 +4923,20 @@ export const getAllTokensWithDisplayInfo = (networkId: NetworkId) => { export type DisplayTokenInfo = ReturnType[number]; +/** Tokens shown on Markets / Portfolio (excludes Pool C/E/F TMPOOL2 LP rows; WAD borrow stays). */ +export function getMarketsTableVisibleTokensWithDisplayInfo( + networkId: NetworkId +): DisplayTokenInfo[] { + return getAllTokensWithDisplayInfo(networkId).filter( + (token) => + !isMarketsTableExcludedMarket( + networkId, + token.poolId, + token.configKey + ) + ); +} + function tokenConfigRowMatchesPool(tc: TokenConfig, poolId: string): boolean { if (String(tc.poolId ?? "") === poolId) return true; if (tc.migration && String(tc.migration.poolId) === poolId) return true; diff --git a/src/constants/__tests__/liquidityPools.test.ts b/src/constants/__tests__/liquidityPools.test.ts index dec228d..41c1e76 100644 --- a/src/constants/__tests__/liquidityPools.test.ts +++ b/src/constants/__tests__/liquidityPools.test.ts @@ -2,15 +2,19 @@ import { describe, expect, it } from "vitest"; import { CURATED_LIQUIDITY_POOLS, pairHasPoolsPageLendingPosition, + pairHasUsdcLpCollateralLendingMarket, + pairHasUsdcLpLendingMarket, pairHasWadLpCollateralLendingMarket, pairHasWadLpLendingMarket, resolvePoolsPageLendingMarket, + resolveUsdcLendingPoolIdsForFilter, resolveWadLendingPoolIdsForFilter, } from "@/constants/liquidityPools"; const NETWORK = "algorand-mainnet" as const; const POOL_C = "3578814346"; const POOL_E = "3585829377"; +const POOL_F = "3589083110"; const LP_WAD_UNIT = "3577783311"; function pairById(id: string) { @@ -85,6 +89,44 @@ describe("resolvePoolsPageLendingMarket", () => { }); } }); + + it("resolves Pool F USDC LP pairs (ALGO, tALGO, HAY, ALPHA)", () => { + const expectations: Record< + string, + { configSymbol: string; marketId: string; assetId: string } + > = { + "usdc-algo": { + configSymbol: "LP_TMPOOL2_USDC_ALGO", + marketId: "3589026317", + assetId: "1002590888", + }, + "talgo-usdc": { + configSymbol: "LP_TMPOOL2_TALGO_USDC", + marketId: "3589029580", + assetId: "2537254960", + }, + "hay-usdc": { + configSymbol: "LP_TMPOOL2_HAY_USDC", + marketId: "3589032117", + assetId: "3196310546", + }, + "alpha-usdc": { + configSymbol: "LP_TMPOOL2_ALPHA_USDC", + marketId: "3589036846", + assetId: "2741116468", + }, + }; + + for (const [id, expected] of Object.entries(expectations)) { + const pair = pairById(id); + expect(pairHasUsdcLpLendingMarket(NETWORK, pair)).toBe(true); + expect(pairHasUsdcLpCollateralLendingMarket(NETWORK, pair)).toBe(true); + expect(resolvePoolsPageLendingMarket(NETWORK, pair)).toMatchObject({ + poolId: POOL_F, + ...expected, + }); + } + }); }); describe("resolveWadLendingPoolIdsForFilter", () => { @@ -99,3 +141,14 @@ describe("resolveWadLendingPoolIdsForFilter", () => { ]); }); }); + +describe("resolveUsdcLendingPoolIdsForFilter", () => { + it("returns Pool F when USDC LP collateral pairs are in the filter", () => { + const usdcPairs = CURATED_LIQUIDITY_POOLS.filter((p) => + ["usdc-algo", "talgo-usdc", "hay-usdc", "alpha-usdc"].includes(p.id) + ); + expect(resolveUsdcLendingPoolIdsForFilter(NETWORK, usdcPairs)).toEqual([ + POOL_F, + ]); + }); +}); diff --git a/src/constants/liquidityPools.ts b/src/constants/liquidityPools.ts index 4c9dbba..0729356 100644 --- a/src/constants/liquidityPools.ts +++ b/src/constants/liquidityPools.ts @@ -1,12 +1,15 @@ import { getPoolCLendingPoolId, getPoolELendingPoolId, + getPoolFLendingPoolId, getUnitLendingWadBorrowMarketConfig, + getUsdcLpLendingWadBorrowMarketConfig, getWadLpLendingWadBorrowMarketConfig, getNetworkConfig, getLendingPoolIdForMarketContract, getTokenDisplayInfo, isUnitLpCollateralMarketContract, + isUsdcLpCollateralMarketContract, isWadLpCollateralMarketContract, type NetworkId, type TokenConfig, @@ -53,10 +56,11 @@ export function poolHasTinymanFarm( export const POOL_FARM_SUPPLY_NOTICE = "If pool has an active Tinyman farm. Supplying LP to the platform may disqualify your LP from farm rewards."; -/** Base-token filters for the Pools page (UNIT / WAD curated pairs). */ +/** Base-token filters for the Pools page (UNIT / WAD / USDC curated pairs). */ export const POOL_BASE_TOKEN_FILTERS = [ { id: "unit", symbol: "UNIT", assetId: 3121954282 }, { id: "wad", symbol: "WAD", assetId: 3334160924 }, + { id: "usdc", symbol: "USDC", assetId: 31566704 }, ] as const; export type PoolBaseTokenFilterId = @@ -100,6 +104,7 @@ export function countPoolsByBaseTokenFilter( all: pairs.length, unit: 0, wad: 0, + usdc: 0, }; for (const filter of POOL_BASE_TOKEN_FILTERS) { @@ -257,6 +262,62 @@ export const CURATED_LIQUIDITY_POOLS: LiquidityPoolPairConfig[] = [ "T4VXZUUONE2DS7G5QXGVBDR27G32MCM25KEQLBITC4ENOK6N7CMM5VLRSM", farms: [], }, + // TMPOOL2 1002590888 6 3589026317 + { + id: "usdc-algo", + platform: "tinyman", + networkId: "algorand-mainnet", + lpTokenId: 1002590888, + lpContractId: 3589026317, + asset1Id: 31566704, + asset2Id: 0, + label: "USDC / ALGO", + poolAddr: + "2PIFZW53RHCSFSYMCFUBW4XOCXOMB7XOYQSQ6KGT3KVGJTL4HM6COZRNMM", + farms: [], + }, + // TMPOOL2 2537254960 6 3589029580 + { + id: "talgo-usdc", + platform: "tinyman", + networkId: "algorand-mainnet", + lpTokenId: 2537254960, + lpContractId: 3589029580, + asset1Id: 2537013734, + asset2Id: 31566704, + label: "tALGO / USDC", + poolAddr: + "VVBWRAJ3YSZGXBGJ2K654J3WA2VZOJW5U4SJCZWLM634H572WTGWTT53EE", + farms: [], + }, + // TMPOOL2 3196310546 6 3589032117 + { + id: "hay-usdc", + platform: "tinyman", + networkId: "algorand-mainnet", + lpTokenId: 3196310546, + lpContractId: 3589032117, + asset1Id: 3160000000, + asset2Id: 31566704, + label: "HAY / USDC", + poolAddr: + "HHDWSEYBFMKLEUC6ZNHUK4AAKXJMSVK4NQFKO7JFB4OEHRAS4SJ32LOTVA", + farms: [], + }, + // TMPOOL2 2741116468 6 3589036846 + { + id: "alpha-usdc", + platform: "tinyman", + networkId: "algorand-mainnet", + lpTokenId: 2741116468, + lpContractId: 3589036846, + asset1Id: 2726252423, + asset2Id: 31566704, + label: "ALPHA / USDC", + poolAddr: + "J7CN6WTMUWXY2KODKAMM5BLZ42FEIQLM4D32FRQ23VOOJC2SEUHKE565T4", + farms: [], + }, ]; export function getCuratedLiquidityPoolsForNetwork( @@ -379,14 +440,15 @@ export function pairHasPoolsPageLendingPosition( return pairHasUnitLpLendingMarket(networkId, pair); } -/** Lending market row for Pools page supply/withdraw (UNIT LP collateral or WAD LP markets). */ +/** Lending market row for Pools page supply/withdraw (UNIT LP, WAD LP, or USDC LP markets). */ export function resolvePoolsPageLendingMarket( networkId: NetworkId, pair: LiquidityPoolPairConfig ): LiquidityPoolLendingMarket | null { const hasLending = pairHasPoolsPageLendingPosition(networkId, pair) || - pairHasWadLpLendingMarket(networkId, pair); + pairHasWadLpLendingMarket(networkId, pair) || + pairHasUsdcLpLendingMarket(networkId, pair); if (!hasLending) return null; return resolveLiquidityPoolLendingMarket(networkId, pair); } @@ -425,6 +487,23 @@ export function resolveWadLendingPoolIdsForFilter( return resolveWadLendingPoolIdsForPairs(networkId, wadPairs); } +/** Pool ids for USDC LP global user reads on the Pools page (Pool F). */ +export function resolveUsdcLendingPoolIdsForFilter( + networkId: NetworkId, + filteredPairs: LiquidityPoolPairConfig[] +): string[] { + const usdcPairs = filteredPairs.filter( + (pair) => + pairHasUsdcLpLendingMarket(networkId, pair) && + pairHasUsdcLpCollateralLendingMarket(networkId, pair) + ); + if (usdcPairs.length === 0) return []; + + const poolF = getPoolFLendingPoolId(networkId); + if (poolF) return [poolF]; + return resolveUsdcLendingPoolIdsForPairs(networkId, usdcPairs); +} + /** LP lending is enabled for WAD-base Tinyman pairs with configured `LP_TMPOOL2_WAD_*` markets. */ export function pairHasWadLpLendingMarket( networkId: NetworkId, @@ -465,3 +544,45 @@ export function resolvePoolEWadMarket( ): TokenConfig | null { return getWadLpLendingWadBorrowMarketConfig(networkId); } + +/** WAD borrow market paired with Pool F USDC LP collateral. */ +export function resolvePoolFWadMarket( + networkId: NetworkId +): TokenConfig | null { + return getUsdcLpLendingWadBorrowMarketConfig(networkId); +} + +/** LP lending is enabled for USDC-base Tinyman pairs on Pool F. */ +export function pairHasUsdcLpLendingMarket( + networkId: NetworkId, + pair: LiquidityPoolPairConfig +): boolean { + if (!isUsdcLpCollateralMarketContract(networkId, pair.lpContractId)) { + return false; + } + return resolveLiquidityPoolLendingMarket(networkId, pair) != null; +} + +/** True when the curated pair supplies USDC-base LP collateral on Pool F. */ +export function pairHasUsdcLpCollateralLendingMarket( + networkId: NetworkId, + pair: LiquidityPoolPairConfig +): boolean { + return isUsdcLpCollateralMarketContract(networkId, pair.lpContractId); +} + +/** Lending pool app ids for USDC LP markets among the given curated pairs. */ +export function resolveUsdcLendingPoolIdsForPairs( + networkId: NetworkId, + pairs: LiquidityPoolPairConfig[] +): string[] { + const poolIds = new Set(); + for (const pair of pairs) { + if (!pairHasUsdcLpLendingMarket(networkId, pair)) continue; + const market = resolveLiquidityPoolLendingMarket(networkId, pair); + if (market?.poolId) { + poolIds.add(market.poolId); + } + } + return [...poolIds]; +} diff --git a/src/constants/marketUi.ts b/src/constants/marketUi.ts index d95d31f..db1cbd3 100644 --- a/src/constants/marketUi.ts +++ b/src/constants/marketUi.ts @@ -66,7 +66,7 @@ export function borrowApyBadgeClassName( return fallbackClassName; } -/** Background classes for A/B/C/D pool letter badges (markets table, portfolio, lists). */ +/** Background classes for A–F pool letter badges (markets table, portfolio, lists). */ export function marketPoolBadgeBgClassName( label: string | null | undefined ): string { @@ -74,5 +74,7 @@ export function marketPoolBadgeBgClassName( if (label === "B") return "bg-purple-500 dark:bg-purple-600"; if (label === "C") return "bg-teal-500 dark:bg-teal-600"; if (label === "D") return "bg-amber-500 dark:bg-amber-600"; + if (label === "E") return "bg-indigo-500 dark:bg-indigo-600"; + if (label === "F") return "bg-rose-500 dark:bg-rose-600"; return "bg-slate-500 dark:bg-slate-600"; } diff --git a/src/hooks/useOnDemandMarketData.ts b/src/hooks/useOnDemandMarketData.ts index 3b02ea9..ab79e80 100644 --- a/src/hooks/useOnDemandMarketData.ts +++ b/src/hooks/useOnDemandMarketData.ts @@ -2,7 +2,7 @@ import { useState, useMemo, useCallback, useEffect } from "react"; import { useNetwork } from "@/contexts/NetworkContext"; import { getAllTokensWithDisplayInfo, - isMarketsTableExcludedMarket, + getMarketsTableVisibleTokensWithDisplayInfo, NetworkId, getNetworkConfig, getLendingPools, @@ -358,18 +358,12 @@ export const useOnDemandMarketData = ({ ] ); - // Omit Pool C LP markets from the table; WAD @ Pool C remains visible. + // Omit Pool C/E/F LP markets from the table; WAD @ those pools remains visible. const tokens = useMemo( () => - getAllTokensWithDisplayInfo(currentNetwork).filter( - (token) => - includeExcludedPools || - !isMarketsTableExcludedMarket( - currentNetwork, - token.poolId, - token.configKey - ) - ), + includeExcludedPools + ? getAllTokensWithDisplayInfo(currentNetwork) + : getMarketsTableVisibleTokensWithDisplayInfo(currentNetwork), [currentNetwork, includeExcludedPools] ); diff --git a/src/pages/Pools.tsx b/src/pages/Pools.tsx index 7c08022..167645c 100644 --- a/src/pages/Pools.tsx +++ b/src/pages/Pools.tsx @@ -18,7 +18,9 @@ import { POOL_BASE_TOKEN_FILTERS, resolvePoolCWadMarket, resolvePoolEWadMarket, + resolvePoolFWadMarket, resolveUnitLendingPoolIdsForFilter, + resolveUsdcLendingPoolIdsForFilter, resolveWadLendingPoolIdsForFilter, type PoolBaseTokenFilterId, } from "@/constants/liquidityPools"; @@ -64,6 +66,9 @@ const PoolsPage = ({ activeTab, onTabChange }: PoolsPageProps) => { if (tokenFilter === "wad") { return resolveWadLendingPoolIdsForFilter(networkId, filteredPairs); } + if (tokenFilter === "usdc") { + return resolveUsdcLendingPoolIdsForFilter(networkId, filteredPairs); + } return []; }, [currentNetwork, filteredPairs, tokenFilter]); const showLendingGlobalSummary = lendingPoolIds.length > 0; @@ -71,10 +76,15 @@ const PoolsPage = ({ activeTab, onTabChange }: PoolsPageProps) => { const networkId = currentNetwork as NetworkId; if (tokenFilter === "unit") return resolvePoolCWadMarket(networkId); if (tokenFilter === "wad") return resolvePoolEWadMarket(networkId); + if (tokenFilter === "usdc") return resolvePoolFWadMarket(networkId); return null; }, [currentNetwork, tokenFilter]); const wadBorrowCollateralLabel = - tokenFilter === "wad" ? "WAD LP" : "UNIT LP"; + tokenFilter === "wad" + ? "WAD LP" + : tokenFilter === "usdc" + ? "USDC LP" + : "UNIT LP"; const { summary, poolIds, isLoading: lendingGlobalLoading } = usePoolsLendingGlobalSummary( currentNetwork as NetworkId, @@ -84,7 +94,9 @@ const PoolsPage = ({ activeTab, onTabChange }: PoolsPageProps) => { ); const canBorrowWad = (summary?.totalCollateralValue ?? 0) > 0; const showWadBorrowSection = - (tokenFilter === "unit" || tokenFilter === "wad") && + (tokenFilter === "unit" || + tokenFilter === "wad" || + tokenFilter === "usdc") && poolsWadBorrowMarket != null && Boolean(activeAccount?.address); diff --git a/src/services/lendingService.ts b/src/services/lendingService.ts index df3ce6b..8d67400 100644 --- a/src/services/lendingService.ts +++ b/src/services/lendingService.ts @@ -15,6 +15,7 @@ import { getAlgorandNetworkFromNetworkId, NetworkId, getAllTokensWithDisplayInfo, + getMarketsTableVisibleTokensWithDisplayInfo, getTokenConfig, resolveTokenConfigFromDisplayToken, tokenConfigLookupKeyFromDisplayToken, @@ -1016,14 +1017,17 @@ export async function fetchFreshMarketInfo( * Fetch all markets information */ export const fetchAllMarkets = async ( - networkId: NetworkId + networkId: NetworkId, + options?: { excludeMarketsTableHidden?: boolean } ): Promise => { try { const networkConfig = getNetworkConfig(networkId); if (isAlgorandCompatibleNetwork(networkId)) { // Get markets from config - const tokens = getAllTokensWithDisplayInfo(networkId); + const tokens = options?.excludeMarketsTableHidden + ? getMarketsTableVisibleTokensWithDisplayInfo(networkId) + : getAllTokensWithDisplayInfo(networkId); console.log("Fetching real market data for", tokens.length, "tokens");