From 01943bfa7e3233992857525bc774e1ff1fbca9b6 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 31 Oct 2025 14:36:37 +0100 Subject: [PATCH 01/13] feat: first version --- src/chains/neutron/neutron-1.ts | 5 +++++ src/constants/campaigns.tsx | 23 ++++++++++++++++++++++- src/types/app.d.ts | 2 +- tailwind.config.ts | 11 +++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/chains/neutron/neutron-1.ts b/src/chains/neutron/neutron-1.ts index 01fef1b84..5143ea01f 100644 --- a/src/chains/neutron/neutron-1.ts +++ b/src/chains/neutron/neutron-1.ts @@ -118,6 +118,11 @@ const Neutron1: ChainConfig = { campaignIds: ['lido'], campaignDenom: 'stETH', }, + { + denom: 'ibc/0E293A7622DC9A6439DB60E6D234B5AF446962E27CA3AB44D0590603DFF6968E', + campaignIds: ['ntrn-rewards'], + campaignDenom: 'wbtc', + }, ], deprecated: [ 'ibc/3649CE0C8A2C79048D8C6F31FF18FA69C9BC7EB193512E0BD03B733011290445', diff --git a/src/constants/campaigns.tsx b/src/constants/campaigns.tsx index 42f0a4b81..6ff7fb25c 100644 --- a/src/constants/campaigns.tsx +++ b/src/constants/campaigns.tsx @@ -1,4 +1,4 @@ -import { Droplet, Lido, MilkyWay, Stride } from 'components/common/Icons' +import { Droplet, Lido, MilkyWay, Neutron, Stride } from 'components/common/Icons' export const CAMPAIGNS: AssetCampaign[] = [ { @@ -96,6 +96,25 @@ export const CAMPAIGNS: AssetCampaign[] = [ tooltip: 'Your deposit will still earn the underlying Milkyway staking APY.', enabledOnV1: true, }, + { + id: 'ntrn-rewards', + name: 'Neutron Rewards', + type: 'apy', + apyApi: { + url: 'https://cache.marsprotocol.io/api/ntrn-rewards-mars', + isApr: false, + isPercent: true, + apyStructure: ['ntrnRewardsDataMars', 'apy'], + denomStructure: ['denom'], + }, + incentiveCopy: '+##APY##% APY', + classNames: 'ntrn-rewards', + bgClassNames: 'gradient-ntrn-rewards', + detailedIncentiveCopy: 'Deposits earn additional ##APY##% APY via NTRN Rewars.', + tooltip: + "Your deposit will earn the underlying Neutron Rewards APY. ATTENTION: Don't modify or close your position after depositing, as this would forfeit your NTRN rewards. You can still add to your position safely.", + enabledOnV1: true, + }, ] export function CampaignLogo({ campaignId }: { campaignId: AssetCampaignId }) { @@ -109,6 +128,8 @@ export function CampaignLogo({ campaignId }: { campaignId: AssetCampaignId }) { return case 'milkyway': return + case 'ntrn-rewards': + return default: return null } diff --git a/src/types/app.d.ts b/src/types/app.d.ts index 0a41a3f2a..2eb57fef6 100644 --- a/src/types/app.d.ts +++ b/src/types/app.d.ts @@ -1878,7 +1878,7 @@ interface StakedAstroLpRewards { rewards: BNCoin[] } -type AssetCampaignId = 'stride' | 'drop' | 'lido' | 'drop_apy' | 'milkyway' +type AssetCampaignId = 'stride' | 'drop' | 'lido' | 'drop_apy' | 'milkyway' | 'ntrn-rewards' type AssetCampaignType = 'points_with_multiplier' | 'apy' type AssetCampaignPointBase = 'value' | 'amount' diff --git a/tailwind.config.ts b/tailwind.config.ts index 7050580ab..903da1cb2 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -60,10 +60,12 @@ module.exports = { 'gradient-stride', 'gradient-lido', 'gradient-milkyway', + 'gradient-ntrn-rewards', 'droplets', 'stride', 'lido', 'milkyway', + 'ntrn-rewards', ], theme: { extend: { @@ -479,6 +481,9 @@ module.exports = { background: 'linear-gradient(270deg, hsl(var(--color-active-tab-primary) /0.765) 0%, hsl(var(--color-active-tab-secondary) /0.886) 23.77%, hsl(var(--color-active-tab-tertiary) /0.26) 99.2%)', }, + '.gradient-ntrn-rewards': { + background: 'linear-gradient(90deg, #ff4b2f, #ff9382)', + }, '.gradient-droplets': { background: 'linear-gradient(90deg, #6039FF, #E8B8FF)', }, @@ -581,6 +586,12 @@ module.exports = { '-webkit-text-fill-color': 'transparent', fontWeight: 'bold', }, + '.ntrn-rewards': { + background: 'linear-gradient(90deg, #ff4b2f, #ff9382)', + '-webkit-background-clip': 'text', + '-webkit-text-fill-color': 'transparent', + fontWeight: 'bold', + }, '.stride': { background: 'linear-gradient(90deg, #E50571, #FB5DA9)', '-webkit-background-clip': 'text', From f5b364227f3a78089f276f7749054ea7aa220587 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 31 Oct 2025 14:36:37 +0100 Subject: [PATCH 02/13] feat: first version --- src/chains/neutron/neutron-1.ts | 5 +++++ src/constants/campaigns.tsx | 23 ++++++++++++++++++++++- src/types/app.d.ts | 2 +- tailwind.config.ts | 11 +++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/chains/neutron/neutron-1.ts b/src/chains/neutron/neutron-1.ts index 01fef1b84..5143ea01f 100644 --- a/src/chains/neutron/neutron-1.ts +++ b/src/chains/neutron/neutron-1.ts @@ -118,6 +118,11 @@ const Neutron1: ChainConfig = { campaignIds: ['lido'], campaignDenom: 'stETH', }, + { + denom: 'ibc/0E293A7622DC9A6439DB60E6D234B5AF446962E27CA3AB44D0590603DFF6968E', + campaignIds: ['ntrn-rewards'], + campaignDenom: 'wbtc', + }, ], deprecated: [ 'ibc/3649CE0C8A2C79048D8C6F31FF18FA69C9BC7EB193512E0BD03B733011290445', diff --git a/src/constants/campaigns.tsx b/src/constants/campaigns.tsx index 42f0a4b81..6ff7fb25c 100644 --- a/src/constants/campaigns.tsx +++ b/src/constants/campaigns.tsx @@ -1,4 +1,4 @@ -import { Droplet, Lido, MilkyWay, Stride } from 'components/common/Icons' +import { Droplet, Lido, MilkyWay, Neutron, Stride } from 'components/common/Icons' export const CAMPAIGNS: AssetCampaign[] = [ { @@ -96,6 +96,25 @@ export const CAMPAIGNS: AssetCampaign[] = [ tooltip: 'Your deposit will still earn the underlying Milkyway staking APY.', enabledOnV1: true, }, + { + id: 'ntrn-rewards', + name: 'Neutron Rewards', + type: 'apy', + apyApi: { + url: 'https://cache.marsprotocol.io/api/ntrn-rewards-mars', + isApr: false, + isPercent: true, + apyStructure: ['ntrnRewardsDataMars', 'apy'], + denomStructure: ['denom'], + }, + incentiveCopy: '+##APY##% APY', + classNames: 'ntrn-rewards', + bgClassNames: 'gradient-ntrn-rewards', + detailedIncentiveCopy: 'Deposits earn additional ##APY##% APY via NTRN Rewars.', + tooltip: + "Your deposit will earn the underlying Neutron Rewards APY. ATTENTION: Don't modify or close your position after depositing, as this would forfeit your NTRN rewards. You can still add to your position safely.", + enabledOnV1: true, + }, ] export function CampaignLogo({ campaignId }: { campaignId: AssetCampaignId }) { @@ -109,6 +128,8 @@ export function CampaignLogo({ campaignId }: { campaignId: AssetCampaignId }) { return case 'milkyway': return + case 'ntrn-rewards': + return default: return null } diff --git a/src/types/app.d.ts b/src/types/app.d.ts index 0a41a3f2a..2eb57fef6 100644 --- a/src/types/app.d.ts +++ b/src/types/app.d.ts @@ -1878,7 +1878,7 @@ interface StakedAstroLpRewards { rewards: BNCoin[] } -type AssetCampaignId = 'stride' | 'drop' | 'lido' | 'drop_apy' | 'milkyway' +type AssetCampaignId = 'stride' | 'drop' | 'lido' | 'drop_apy' | 'milkyway' | 'ntrn-rewards' type AssetCampaignType = 'points_with_multiplier' | 'apy' type AssetCampaignPointBase = 'value' | 'amount' diff --git a/tailwind.config.ts b/tailwind.config.ts index 7050580ab..903da1cb2 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -60,10 +60,12 @@ module.exports = { 'gradient-stride', 'gradient-lido', 'gradient-milkyway', + 'gradient-ntrn-rewards', 'droplets', 'stride', 'lido', 'milkyway', + 'ntrn-rewards', ], theme: { extend: { @@ -479,6 +481,9 @@ module.exports = { background: 'linear-gradient(270deg, hsl(var(--color-active-tab-primary) /0.765) 0%, hsl(var(--color-active-tab-secondary) /0.886) 23.77%, hsl(var(--color-active-tab-tertiary) /0.26) 99.2%)', }, + '.gradient-ntrn-rewards': { + background: 'linear-gradient(90deg, #ff4b2f, #ff9382)', + }, '.gradient-droplets': { background: 'linear-gradient(90deg, #6039FF, #E8B8FF)', }, @@ -581,6 +586,12 @@ module.exports = { '-webkit-text-fill-color': 'transparent', fontWeight: 'bold', }, + '.ntrn-rewards': { + background: 'linear-gradient(90deg, #ff4b2f, #ff9382)', + '-webkit-background-clip': 'text', + '-webkit-text-fill-color': 'transparent', + fontWeight: 'bold', + }, '.stride': { background: 'linear-gradient(90deg, #E50571, #FB5DA9)', '-webkit-background-clip': 'text', From e4c96ed9f2cd1bb95649af0421df4a3275b37f13 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 31 Oct 2025 14:40:43 +0100 Subject: [PATCH 03/13] feat: finish NTRN rewards --- src/api/campaign/getCampaignApys.ts | 6 ++++-- src/constants/campaigns.tsx | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/api/campaign/getCampaignApys.ts b/src/api/campaign/getCampaignApys.ts index 69067d27a..a7c9ed7f9 100644 --- a/src/api/campaign/getCampaignApys.ts +++ b/src/api/campaign/getCampaignApys.ts @@ -3,8 +3,10 @@ import { fetchWithTimeout } from 'utils/fetch' import { convertAprToApy } from 'utils/parsers' function processApyData(aprOrApy: number, isApr: boolean, isPercent: boolean): number { - if (!isApr && isPercent) return aprOrApy - const percentApr = isPercent ? aprOrApy : aprOrApy * 100 + // Ensure the value is a number (API might return strings) + const numericValue = typeof aprOrApy === 'string' ? parseFloat(aprOrApy) : aprOrApy + if (!isApr && isPercent) return numericValue + const percentApr = isPercent ? numericValue : numericValue * 100 const apy = isApr ? convertAprToApy(percentApr, 365) : percentApr return apy } diff --git a/src/constants/campaigns.tsx b/src/constants/campaigns.tsx index 6ff7fb25c..268364a1e 100644 --- a/src/constants/campaigns.tsx +++ b/src/constants/campaigns.tsx @@ -110,9 +110,9 @@ export const CAMPAIGNS: AssetCampaign[] = [ incentiveCopy: '+##APY##% APY', classNames: 'ntrn-rewards', bgClassNames: 'gradient-ntrn-rewards', - detailedIncentiveCopy: 'Deposits earn additional ##APY##% APY via NTRN Rewars.', + detailedIncentiveCopy: 'Deposits earn additional ##APY##% APY via Neutron (NTRN) Rewards.', tooltip: - "Your deposit will earn the underlying Neutron Rewards APY. ATTENTION: Don't modify or close your position after depositing, as this would forfeit your NTRN rewards. You can still add to your position safely.", + "Your deposit will earn additional Neutron (NTRN) Rewards. ATTENTION: Don't modify or close your position after depositing, as this would forfeit your NTRN rewards. You can still add to your position safely.", enabledOnV1: true, }, ] From 586ce68bad3e9a2ed66195bd5d96bbf96886c196 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 31 Oct 2025 14:47:45 +0100 Subject: [PATCH 04/13] fix: campaign APY is now added the the market APY --- .../AccountBalancesTable/Columns/Apy.tsx | 2 +- src/components/portfolio/Account/Summary.tsx | 4 +--- src/utils/accounts.ts | 20 ++++++++++++++----- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/components/account/AccountBalancesTable/Columns/Apy.tsx b/src/components/account/AccountBalancesTable/Columns/Apy.tsx index c3fccd2d2..5d7072972 100644 --- a/src/components/account/AccountBalancesTable/Columns/Apy.tsx +++ b/src/components/account/AccountBalancesTable/Columns/Apy.tsx @@ -28,7 +28,7 @@ export default function Apy(props: Props) { return (
{hasLstApy ? ( - +
lendingAsset.asset.denom === lend.denom) - ?.apy.deposit + const marketApy = lendingAssetsData.find( + (lendingAsset) => lendingAsset.asset.denom === lend.denom, + )?.apy.deposit - if (!apy) return + if (!marketApy) return - const positionInterest = amount.multipliedBy(price).multipliedBy(apy).dividedBy(100) + // Add campaign APY if available + let campaignApy = 0 + asset.campaigns?.forEach((campaign) => { + if (campaign.type === 'apy') { + campaignApy += campaign.apy ?? 0 + } + }) + + const totalApy = marketApy + campaignApy + const positionInterest = amount.multipliedBy(price).multipliedBy(totalApy).dividedBy(100) totalLendsInterestValue = totalLendsInterestValue.plus(positionInterest) }) From e2a4401e37b7d9cacc820e1a90033ee913eb2c0c Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 31 Oct 2025 14:51:08 +0100 Subject: [PATCH 05/13] fix: adjust text --- src/constants/campaigns.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants/campaigns.tsx b/src/constants/campaigns.tsx index 268364a1e..0c86287df 100644 --- a/src/constants/campaigns.tsx +++ b/src/constants/campaigns.tsx @@ -112,7 +112,7 @@ export const CAMPAIGNS: AssetCampaign[] = [ bgClassNames: 'gradient-ntrn-rewards', detailedIncentiveCopy: 'Deposits earn additional ##APY##% APY via Neutron (NTRN) Rewards.', tooltip: - "Your deposit will earn additional Neutron (NTRN) Rewards. ATTENTION: Don't modify or close your position after depositing, as this would forfeit your NTRN rewards. You can still add to your position safely.", + "Your deposit will earn additional Neutron (NTRN) Rewards. ATTENTION: Don't reduce or withdraw your position after depositing, as this would forfeit your NTRN rewards. You can still add to your position safely.", enabledOnV1: true, }, ] From 3dd04bacdb3e3527de71844374ac7d6ce57583aa Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 31 Oct 2025 14:57:58 +0100 Subject: [PATCH 06/13] env: bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4a30247d3..c95e7845d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mars-v2-frontend", - "version": "3.0.2", + "version": "3.0.3", "homepage": "./", "private": false, "license": "SEE LICENSE IN LICENSE FILE", From 0f3b0b4c5beeca54123b48f8169ffe2f26718b75 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 31 Oct 2025 15:59:08 +0100 Subject: [PATCH 07/13] fix: build --- install_charting_library.sh | 16 ++++++++++++++++ next.config.js | 7 +++++++ src/pages/TradePage.tsx | 5 ++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/install_charting_library.sh b/install_charting_library.sh index 97015b621..9873d591e 100644 --- a/install_charting_library.sh +++ b/install_charting_library.sh @@ -24,4 +24,20 @@ cp -r "$LATEST_HASH/charting_library" src/utils/ cp -r "$LATEST_HASH/datafeeds" public/ cp -r "$LATEST_HASH/datafeeds" src/utils/ +# Create index.js to export widget for Next.js compatibility +cat > "src/utils/charting_library/index.js" << 'JSEOF' +// The charting library is loaded via a UMD bundle that attaches to window.TradingView +// We provide a stub that resolves to the actual widget at runtime +export const widget = (typeof window !== 'undefined' && typeof window.TradingView !== 'undefined') + ? window.TradingView.widget + : class MockWidget {} +JSEOF + +# Create index.d.ts to re-export types +cat > "src/utils/charting_library/index.d.ts" << 'DTSEOF' +// Re-export all types from the charting library +export * from './charting_library.d' +export * from './datafeed-api.d' +DTSEOF + remove_if_directory_exists "$LATEST_HASH" \ No newline at end of file diff --git a/next.config.js b/next.config.js index 5b63899a3..f42fc42ee 100644 --- a/next.config.js +++ b/next.config.js @@ -105,6 +105,13 @@ const nextConfig = { use: ['@svgr/webpack'], }) + // Handle charting library - it's a UMD bundle that needs special treatment + config.resolve.alias = { + ...config.resolve.alias, + // Ensure the charting library is resolved correctly + 'utils/charting_library': require.resolve('./src/utils/charting_library/index.js'), + } + return config }, } diff --git a/src/pages/TradePage.tsx b/src/pages/TradePage.tsx index b63152e76..1c13bf5fb 100644 --- a/src/pages/TradePage.tsx +++ b/src/pages/TradePage.tsx @@ -1,10 +1,13 @@ import { useEffect, useMemo } from 'react' import { useLocation } from 'react-router-dom' +import dynamic from 'next/dynamic' import GridWithSplitters from 'components/common/Grid/GridWithSplitters' import AccountDetailsCard from 'components/trade/AccountDetailsCard' -import TradeChart from 'components/trade/TradeChart' import TradeModule from 'components/trade/TradeModule' + +// TradeChart uses the charting library which is browser-only, so we load it dynamically +const TradeChart = dynamic(() => import('components/trade/TradeChart'), { ssr: false }) import { getDefaultChainSettings } from 'constants/defaultSettings' import { LocalStorageKeys } from 'constants/localStorageKeys' import useTradeEnabledAssets from 'hooks/assets/useTradeEnabledAssets' From 845b2f51cf51d0208863ebae5e75b3f86e7f96a5 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 31 Oct 2025 16:10:32 +0100 Subject: [PATCH 08/13] fix: use path.resolve instead of require.resolve for charting library - Prevents errors when the index.js file doesn't exist yet - More reliable for build environments like Vercel --- next.config.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/next.config.js b/next.config.js index f42fc42ee..c3d2eef38 100644 --- a/next.config.js +++ b/next.config.js @@ -106,10 +106,11 @@ const nextConfig = { }) // Handle charting library - it's a UMD bundle that needs special treatment + // Use path.resolve instead of require.resolve to avoid issues when file doesn't exist yet + const path = require('path') config.resolve.alias = { ...config.resolve.alias, - // Ensure the charting library is resolved correctly - 'utils/charting_library': require.resolve('./src/utils/charting_library/index.js'), + 'utils/charting_library': path.resolve(__dirname, 'src/utils/charting_library/index.js'), } return config From f4e7cf2985fe0254f127bf51b114eb183b608c9e Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 31 Oct 2025 16:29:41 +0100 Subject: [PATCH 09/13] fix: use type-only import for ResolutionString - Prevents runtime import of charting library during SSR - Reduces bundle size --- src/constants/defaultSettings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants/defaultSettings.ts b/src/constants/defaultSettings.ts index 8ab0b56c1..6408f0ffb 100644 --- a/src/constants/defaultSettings.ts +++ b/src/constants/defaultSettings.ts @@ -1,6 +1,6 @@ import { ORACLE_DENOM } from 'constants/oracle' import { RewardsCenterType } from 'types/enums' -import { ResolutionString } from 'utils/charting_library' +import type { ResolutionString } from 'utils/charting_library' export const getDefaultKeeperFee = (chainConfig: ChainConfig) => { return { From 6aca29e84d4090578b74f901fce3612042d7007a Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 31 Oct 2025 17:17:48 +0100 Subject: [PATCH 10/13] fix: build fixes --- eslint.config.mjs | 9 +++ next.config.js | 23 +++++-- package.json | 66 +++++++++---------- src/components/Modals/AssetsSelect/index.tsx | 4 ++ src/components/Modals/BorrowModal.tsx | 13 ++++ src/components/Modals/Farm/FarmBorrowings.tsx | 5 +- src/components/Modals/Farm/FarmDeposits.tsx | 51 +++++++++----- src/components/common/Table/index.tsx | 1 + src/hooks/perps/useHandleClosing.ts | 2 +- src/hooks/perps/useLimitPriceInfo.ts | 2 +- src/hooks/perps/useOrderTypeManagement.ts | 2 +- src/hooks/perps/useReduceOnlyOrder.ts | 2 +- src/hooks/perps/useStopPriceInfo.ts | 2 +- src/utils/balances.ts | 2 +- tsconfig.json | 3 + 15 files changed, 124 insertions(+), 63 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index dd1ef32d2..c49048711 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -51,6 +51,15 @@ export default tseslint.config( 'linebreak-style': 'off', 'no-undef': 'off', 'prettier/prettier': 0, + // Disable new stricter react-hooks rules from Next.js 15.5.4 + 'react-hooks/set-state-in-effect': 'off', + 'react-hooks/immutability': 'off', + 'react-hooks/set-state-in-render': 'off', + 'react-hooks/static-components': 'off', + 'react-hooks/purity': 'off', + 'react-hooks/preserve-manual-memoization': 'off', + 'react-hooks/refs': 'off', + 'react-hooks/incompatible-library': 'warn', 'sort-imports': [ 'warn', { diff --git a/next.config.js b/next.config.js index c3d2eef38..51ca0db74 100644 --- a/next.config.js +++ b/next.config.js @@ -2,6 +2,7 @@ const nextConfig = { reactStrictMode: true, + transpilePackages: ['bignumber.js'], images: { remotePatterns: [ { @@ -98,7 +99,7 @@ const nextConfig = { }, ] }, - webpack(config) { + webpack(config, { isServer }) { config.module.rules.push({ test: /\.svg$/i, issuer: /\.[jt]sx?$/, @@ -108,9 +109,23 @@ const nextConfig = { // Handle charting library - it's a UMD bundle that needs special treatment // Use path.resolve instead of require.resolve to avoid issues when file doesn't exist yet const path = require('path') - config.resolve.alias = { - ...config.resolve.alias, - 'utils/charting_library': path.resolve(__dirname, 'src/utils/charting_library/index.js'), + const fs = require('fs') + const chartingLibraryPath = path.resolve(__dirname, 'src/utils/charting_library/index.js') + + // Only add alias if the file exists (it's created by install-charting-library script) + if (fs.existsSync(chartingLibraryPath)) { + config.resolve.alias = { + ...config.resolve.alias, + 'utils/charting_library': chartingLibraryPath, + } + } + + // Fix for packages with only "exports" field (like @cosmjs) + // This ensures webpack can resolve them properly + config.resolve.extensionAlias = { + '.js': ['.js', '.ts', '.tsx'], + '.mjs': ['.mjs', '.mts'], + ...config.resolve.extensionAlias, } return config diff --git a/package.json b/package.json index c95e7845d..d6d1a21e2 100644 --- a/package.json +++ b/package.json @@ -20,90 +20,90 @@ ] }, "dependencies": { - "@cosmjs/cosmwasm-stargate": "^0.36.0", + "@cosmjs/cosmwasm-stargate": "^0.37.0", "@delphi-labs/shuttle-react": "^4.3.0", - "@keplr-wallet/cosmos": "^0.12.275", - "@next/eslint-plugin-next": "^15.5.4", - "@skip-go/client": "^1.5.8", + "@keplr-wallet/cosmos": "^0.12.285", + "@next/eslint-plugin-next": "^16.0.1", + "@react-native-async-storage/async-storage": "^2.2.0", + "@skip-go/client": "^1.5.9", "@solana/web3.js": "^1.98.4", - "@tanstack/react-query": "^5.90.2", + "@tanstack/react-query": "^5.90.5", "@tanstack/react-table": "^8.21.3", "@tippyjs/react": "^4.2.6", "@vercel/analytics": "^1.5.0", "@vercel/og": "^0.8.5", "@web3modal/wagmi": "^5.1.11", - "axios": "^1.12.2", + "axios": "^1.13.1", "bignumber.js": "^9.3.1", - "bitcoinjs-lib": "^6.1.7", + "bitcoinjs-lib": "^7.0.0", "classnames": "^2.5.1", "debounce-promise": "^3.1.2", - "graphql-request": "^7.2.0", + "graphql-request": "^7.3.1", "ibc-domains-sdk": "^1.1.0", "isbot": "^5.1.31", "lodash": "^4.17.21", "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1", - "mobx": "^6.13.7", + "mobx": "^6.15.0", "moment": "^2.30.1", - "next": "^15.5.4", + "next": "15.5.4", "react": "19.2.0", "react-device-detect": "^2.2.3", "react-dom": "^19.2.0", "react-draggable": "^4.5.0", "react-helmet-async": "^2.0.5", - "@react-native-async-storage/async-storage": "^2.0.0", "react-qr-code": "^2.0.18", - "react-router-dom": "^7.9.2", + "react-router-dom": "^7.9.5", "react-spring": "^10.0.3", "react-toastify": "^11.0.5", "react-use-clipboard": "^1.0.9", - "recharts": "^3.2.1", + "recharts": "^3.3.0", "sharp": "^0.34.4", "starknet": "^7.6.4", "swr": "^2.3.6", - "viem": "^2.37.8", - "wagmi": "^2.17.4", + "viem": "^2.38.5", + "wagmi": "^2.19.1", "zustand": "5.0.8" }, "devDependencies": { - "@babel/eslint-parser": "^7.28.4", - "buffer": "^6.0.3", - "crypto-browserify": "^3.12.0", - "react-native-web": "^0.19.13", - "stream-browserify": "^3.0.0", - "@eslint/compat": "^1.4.0", + "@babel/eslint-parser": "^7.28.5", + "@eslint/compat": "^1.4.1", "@svgr/webpack": "^8.1.0", "@tailwindcss/container-queries": "^0.1.1", - "@tailwindcss/postcss": "^4.1.13", + "@tailwindcss/postcss": "^4.1.16", "@types/debounce-promise": "^3.1.9", "@types/lodash.debounce": "^4.0.9", "@types/lodash.throttle": "^4.1.9", - "@types/node": "^24.5.2", + "@types/node": "^24.9.2", "@types/react": "19.2.2", "@types/react-dom": "19.2.2", "@types/react-helmet": "^6.1.11", - "@typescript-eslint/eslint-plugin": "^8.44.1", - "@typescript-eslint/parser": "^8.44.1", + "@typescript-eslint/eslint-plugin": "^8.46.2", + "@typescript-eslint/parser": "^8.46.2", "autoprefixer": "^10.4.21", - "dotenv": "^17.2.2", - "dotenv-cli": "^10.0.0", - "eslint": "^9.36.0", + "buffer": "^6.0.3", + "crypto-browserify": "^3.12.1", + "dotenv": "^17.2.3", + "dotenv-cli": "^11.0.0", + "eslint": "^9.38.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-functional": "^9.0.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-prettier": "^5.5.4", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-hooks": "^7.0.1", "husky": "^9.1.7", "identity-obj-proxy": "^3.0.0", - "lint-staged": "^16.2.0", + "lint-staged": "^16.2.6", "prettier": "^3.6.2", - "prettier-plugin-tailwindcss": "^0.6.14", + "prettier-plugin-tailwindcss": "^0.7.1", + "react-native-web": "^0.21.2", "shelljs": "^0.10.0", + "stream-browserify": "^3.0.0", "tailwind-scrollbar-hide": "^4.0.0", "tailwindcss": "3.4.1", - "typescript": "^5.9.2", - "typescript-eslint": "^8.44.1" + "typescript": "^5.9.3", + "typescript-eslint": "^8.46.2" }, "overrides": { "chalk": "5.3.0", diff --git a/src/components/Modals/AssetsSelect/index.tsx b/src/components/Modals/AssetsSelect/index.tsx index 0fa742daf..7927ef43a 100644 --- a/src/components/Modals/AssetsSelect/index.tsx +++ b/src/components/Modals/AssetsSelect/index.tsx @@ -173,7 +173,9 @@ export default function AssetsSelect(props: Props) { selection[index] = true } }) + // eslint-disable-next-line react-hooks/set-state-in-effect setWhitelistedSelected(selection) + // eslint-disable-next-line react-hooks/set-state-in-effect setNonCollateralSelected({}) } else { const whitelistedSelection: RowSelectionState = {} @@ -197,7 +199,9 @@ export default function AssetsSelect(props: Props) { } }) + // eslint-disable-next-line react-hooks/set-state-in-effect setWhitelistedSelected(whitelistedSelection) + // eslint-disable-next-line react-hooks/set-state-in-effect setNonCollateralSelected(nonCollateralSelection) } } diff --git a/src/components/Modals/BorrowModal.tsx b/src/components/Modals/BorrowModal.tsx index 78e0fa3dc..8dfa05d9a 100644 --- a/src/components/Modals/BorrowModal.tsx +++ b/src/components/Modals/BorrowModal.tsx @@ -208,7 +208,9 @@ function BorrowModal(props: Props) { useEffect(() => { if (isRepay) { + // eslint-disable-next-line react-hooks/set-state-in-effect setDebtAssetMax(maxDebtAssetAmount) + // eslint-disable-next-line react-hooks/set-state-in-effect setSwapAssetMax(maxSwapAssetAmount) } }, [isRepay, maxDebtAssetAmount, maxSwapAssetAmount]) @@ -276,6 +278,7 @@ function BorrowModal(props: Props) { useEffect(() => { if (isRepay && isDifferentAsset) { if (swapAssetAmount.isGreaterThan(adjustedSwapAssetMax)) { + // eslint-disable-next-line react-hooks/set-state-in-effect setSwapAssetAmount(adjustedSwapAssetMax) } } @@ -314,6 +317,7 @@ function BorrowModal(props: Props) { } } + // eslint-disable-next-line react-hooks/set-state-in-effect setValidationErrors(errors) }, [ isRepay, @@ -522,12 +526,15 @@ function BorrowModal(props: Props) { useEffect(() => { if (!account || isRepay) return if (maxBorrow.isEqualTo(max)) return + // eslint-disable-next-line react-hooks/set-state-in-effect setMax(maxBorrow) }, [account, isRepay, maxBorrow, max]) useEffect(() => { if (amount.isLessThanOrEqualTo(max)) return + // eslint-disable-next-line react-hooks/set-state-in-effect handleChange(max) + // eslint-disable-next-line react-hooks/set-state-in-effect setAmount(max) }, [amount, max, handleChange]) @@ -548,10 +555,15 @@ function BorrowModal(props: Props) { }, [availableRepaymentAssets]) useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect setHasUserMadeSelection(false) + // eslint-disable-next-line react-hooks/set-state-in-effect setUseDebtAsset(false) + // eslint-disable-next-line react-hooks/set-state-in-effect setSelectedSwapAsset(null) + // eslint-disable-next-line react-hooks/set-state-in-effect setDebtAssetAmount(BN_ZERO) + // eslint-disable-next-line react-hooks/set-state-in-effect setSwapAssetAmount(BN_ZERO) }, [repayFromWallet]) @@ -561,6 +573,7 @@ function BorrowModal(props: Props) { account.deposits.some(byDenom(asset.denom)) || account.lends.some(byDenom(asset.denom)) if (hasDebtAssetInAccount) { + // eslint-disable-next-line react-hooks/set-state-in-effect setUseDebtAsset(true) } } diff --git a/src/components/Modals/Farm/FarmBorrowings.tsx b/src/components/Modals/Farm/FarmBorrowings.tsx index 86e983ff1..f1d42469b 100644 --- a/src/components/Modals/Farm/FarmBorrowings.tsx +++ b/src/components/Modals/Farm/FarmBorrowings.tsx @@ -130,8 +130,9 @@ export default function FarmBorrowings(props: FarmBorrowingsProps) { function updateAssets(denom: string, amount: BigNumber) { const index = props.borrowings.findIndex((coin) => coin.denom === denom) - props.borrowings[index].amount = amount - props.onChangeBorrowings([...props.borrowings]) + const newBorrowings = [...props.borrowings] + newBorrowings[index].amount = amount + props.onChangeBorrowings(newBorrowings) } function onDelete(denom: string) { diff --git a/src/components/Modals/Farm/FarmDeposits.tsx b/src/components/Modals/Farm/FarmDeposits.tsx index 8c305d1dd..d5c8c2c8c 100644 --- a/src/components/Modals/Farm/FarmDeposits.tsx +++ b/src/components/Modals/Farm/FarmDeposits.tsx @@ -163,9 +163,9 @@ export default function FarmDeposits(props: Props) { function handleSwitch() { const isCustomRatioNew = !props.isCustomRatio if (!isCustomRatioNew) { - primaryCoin.amount = BN_ZERO - secondaryCoin.amount = BN_ZERO - onChangeDeposits([primaryCoin, secondaryCoin]) + const newPrimaryCoin = new BNCoin({ denom: primaryCoin.denom, amount: '0' }) + const newSecondaryCoin = new BNCoin({ denom: secondaryCoin.denom, amount: '0' }) + onChangeDeposits([newPrimaryCoin, newSecondaryCoin]) setPercentage(0) } props.onChangeIsCustomRatio(isCustomRatioNew) @@ -176,15 +176,17 @@ export default function FarmDeposits(props: Props) { if (amount.isGreaterThan(primaryMax)) { amount = primaryMax } - primaryCoin.amount = amount + const newPrimaryCoin = new BNCoin({ denom: primaryCoin.denom, amount: amount.toString() }) setPercentage(amount.dividedBy(primaryMax).multipliedBy(100).decimalPlaces(0).toNumber()) - if (!props.isCustomRatio) { - secondaryCoin.amount = secondaryMax - .multipliedBy(amount.dividedBy(primaryMax)) - .integerValue() - } + const newSecondaryAmount = !props.isCustomRatio + ? secondaryMax.multipliedBy(amount.dividedBy(primaryMax)).integerValue() + : secondaryCoin.amount + const newSecondaryCoin = new BNCoin({ + denom: secondaryCoin.denom, + amount: newSecondaryAmount.toString(), + }) - onChangeDeposits([primaryCoin, secondaryCoin]) + onChangeDeposits([newPrimaryCoin, newSecondaryCoin]) }, [primaryMax, secondaryMax, props.isCustomRatio, primaryCoin, secondaryCoin, onChangeDeposits], ) @@ -194,13 +196,20 @@ export default function FarmDeposits(props: Props) { if (amount.isGreaterThan(secondaryMax)) { amount = secondaryMax } - secondaryCoin.amount = amount + const newSecondaryCoin = new BNCoin({ + denom: secondaryCoin.denom, + amount: amount.toString(), + }) setPercentage(amount.dividedBy(secondaryMax).multipliedBy(100).decimalPlaces(0).toNumber()) - if (!props.isCustomRatio) { - primaryCoin.amount = primaryMax.multipliedBy(amount.dividedBy(secondaryMax)).integerValue() - } + const newPrimaryAmount = !props.isCustomRatio + ? primaryMax.multipliedBy(amount.dividedBy(secondaryMax)).integerValue() + : primaryCoin.amount + const newPrimaryCoin = new BNCoin({ + denom: primaryCoin.denom, + amount: newPrimaryAmount.toString(), + }) - onChangeDeposits([primaryCoin, secondaryCoin]) + onChangeDeposits([newPrimaryCoin, newSecondaryCoin]) }, [primaryMax, secondaryMax, props.isCustomRatio, primaryCoin, secondaryCoin, onChangeDeposits], ) @@ -208,9 +217,15 @@ export default function FarmDeposits(props: Props) { const onChangeSlider = useCallback( (value: number) => { if (percentage !== value) setPercentage(value) - primaryCoin.amount = primaryMax.multipliedBy(value / 100).integerValue() - secondaryCoin.amount = secondaryMax.multipliedBy(value / 100).integerValue() - onChangeDeposits([primaryCoin, secondaryCoin]) + const newPrimaryCoin = new BNCoin({ + denom: primaryCoin.denom, + amount: primaryMax.multipliedBy(value / 100).integerValue().toString(), + }) + const newSecondaryCoin = new BNCoin({ + denom: secondaryCoin.denom, + amount: secondaryMax.multipliedBy(value / 100).integerValue().toString(), + }) + onChangeDeposits([newPrimaryCoin, newSecondaryCoin]) }, [percentage, primaryCoin, primaryMax, secondaryCoin, secondaryMax, onChangeDeposits], ) diff --git a/src/components/common/Table/index.tsx b/src/components/common/Table/index.tsx index 7b9017c2c..dce1c063c 100644 --- a/src/components/common/Table/index.tsx +++ b/src/components/common/Table/index.tsx @@ -45,6 +45,7 @@ export default function Table(props: Props) { const sorting = props.onSortingChange ? props.initialSorting : internalSorting const onSortingChange = props.onSortingChange ?? setInternalSorting + // eslint-disable-next-line react-hooks/incompatible-library const table = useReactTable({ data: props.data, columns: props.columns, diff --git a/src/hooks/perps/useHandleClosing.ts b/src/hooks/perps/useHandleClosing.ts index 5536b3d80..d65e1db8e 100644 --- a/src/hooks/perps/useHandleClosing.ts +++ b/src/hooks/perps/useHandleClosing.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react' -import { BigNumber } from 'bignumber.js' +import BigNumber from 'bignumber.js' export const useHandleClosing = ( currentPerpPosition: PerpsPosition | undefined, diff --git a/src/hooks/perps/useLimitPriceInfo.ts b/src/hooks/perps/useLimitPriceInfo.ts index 514b35f77..11be2fa68 100644 --- a/src/hooks/perps/useLimitPriceInfo.ts +++ b/src/hooks/perps/useLimitPriceInfo.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react' -import { BigNumber } from 'bignumber.js' +import BigNumber from 'bignumber.js' import { DEFAULT_LIMIT_PRICE_INFO } from 'components/perps/Module/constants' import { capitalizeFirstLetter } from 'utils/helpers' import { CalloutType } from 'components/common/Callout' diff --git a/src/hooks/perps/useOrderTypeManagement.ts b/src/hooks/perps/useOrderTypeManagement.ts index c68224082..25998b8a4 100644 --- a/src/hooks/perps/useOrderTypeManagement.ts +++ b/src/hooks/perps/useOrderTypeManagement.ts @@ -1,7 +1,7 @@ import { useMemo, useState } from 'react' import { OrderType } from 'types/enums' import { BN_ZERO } from 'constants/math' -import { BigNumber } from 'bignumber.js' +import BigNumber from 'bignumber.js' export const useOrderTypeManagement = ( orderType: string, diff --git a/src/hooks/perps/useReduceOnlyOrder.ts b/src/hooks/perps/useReduceOnlyOrder.ts index b4b35c27d..b2d4f4cfb 100644 --- a/src/hooks/perps/useReduceOnlyOrder.ts +++ b/src/hooks/perps/useReduceOnlyOrder.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from 'react' -import { BigNumber } from 'bignumber.js' +import BigNumber from 'bignumber.js' export const useReduceOnlyOrder = ( isReduceOnly: boolean, diff --git a/src/hooks/perps/useStopPriceInfo.ts b/src/hooks/perps/useStopPriceInfo.ts index 910e0717a..e3a23474e 100644 --- a/src/hooks/perps/useStopPriceInfo.ts +++ b/src/hooks/perps/useStopPriceInfo.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react' -import { BigNumber } from 'bignumber.js' +import BigNumber from 'bignumber.js' import { DEFAULT_STOP_PRICE_INFO } from 'components/perps/Module/constants' import { capitalizeFirstLetter } from 'utils/helpers' import { CalloutType } from 'components/common/Callout' diff --git a/src/utils/balances.ts b/src/utils/balances.ts index 64cc729dc..3def5599b 100644 --- a/src/utils/balances.ts +++ b/src/utils/balances.ts @@ -1,6 +1,6 @@ import { BNCoin } from 'types/classes/BNCoin' import { BN_ZERO } from 'constants/math' -import { BigNumber } from 'bignumber.js' +import BigNumber from 'bignumber.js' import { WrappedBNCoin } from 'types/classes/WrappedBNCoin' export function findBalanceForAsset( diff --git a/tsconfig.json b/tsconfig.json index 906d9189e..5c0be4ff8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,9 @@ "jsx": "preserve", "incremental": true, "baseUrl": "src", + "paths": { + "@cosmjs/*": ["../node_modules/@cosmjs/*/build"] + }, "plugins": [ { "name": "next" From ae2d378cab18cf3694636eaebf94fe922046241f Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 31 Oct 2025 17:26:18 +0100 Subject: [PATCH 11/13] fix: remove unused eslint-disable --- src/components/Modals/AssetsSelect/index.tsx | 4 ---- src/components/Modals/BorrowModal.tsx | 13 ------------- 2 files changed, 17 deletions(-) diff --git a/src/components/Modals/AssetsSelect/index.tsx b/src/components/Modals/AssetsSelect/index.tsx index 7927ef43a..0fa742daf 100644 --- a/src/components/Modals/AssetsSelect/index.tsx +++ b/src/components/Modals/AssetsSelect/index.tsx @@ -173,9 +173,7 @@ export default function AssetsSelect(props: Props) { selection[index] = true } }) - // eslint-disable-next-line react-hooks/set-state-in-effect setWhitelistedSelected(selection) - // eslint-disable-next-line react-hooks/set-state-in-effect setNonCollateralSelected({}) } else { const whitelistedSelection: RowSelectionState = {} @@ -199,9 +197,7 @@ export default function AssetsSelect(props: Props) { } }) - // eslint-disable-next-line react-hooks/set-state-in-effect setWhitelistedSelected(whitelistedSelection) - // eslint-disable-next-line react-hooks/set-state-in-effect setNonCollateralSelected(nonCollateralSelection) } } diff --git a/src/components/Modals/BorrowModal.tsx b/src/components/Modals/BorrowModal.tsx index 8dfa05d9a..78e0fa3dc 100644 --- a/src/components/Modals/BorrowModal.tsx +++ b/src/components/Modals/BorrowModal.tsx @@ -208,9 +208,7 @@ function BorrowModal(props: Props) { useEffect(() => { if (isRepay) { - // eslint-disable-next-line react-hooks/set-state-in-effect setDebtAssetMax(maxDebtAssetAmount) - // eslint-disable-next-line react-hooks/set-state-in-effect setSwapAssetMax(maxSwapAssetAmount) } }, [isRepay, maxDebtAssetAmount, maxSwapAssetAmount]) @@ -278,7 +276,6 @@ function BorrowModal(props: Props) { useEffect(() => { if (isRepay && isDifferentAsset) { if (swapAssetAmount.isGreaterThan(adjustedSwapAssetMax)) { - // eslint-disable-next-line react-hooks/set-state-in-effect setSwapAssetAmount(adjustedSwapAssetMax) } } @@ -317,7 +314,6 @@ function BorrowModal(props: Props) { } } - // eslint-disable-next-line react-hooks/set-state-in-effect setValidationErrors(errors) }, [ isRepay, @@ -526,15 +522,12 @@ function BorrowModal(props: Props) { useEffect(() => { if (!account || isRepay) return if (maxBorrow.isEqualTo(max)) return - // eslint-disable-next-line react-hooks/set-state-in-effect setMax(maxBorrow) }, [account, isRepay, maxBorrow, max]) useEffect(() => { if (amount.isLessThanOrEqualTo(max)) return - // eslint-disable-next-line react-hooks/set-state-in-effect handleChange(max) - // eslint-disable-next-line react-hooks/set-state-in-effect setAmount(max) }, [amount, max, handleChange]) @@ -555,15 +548,10 @@ function BorrowModal(props: Props) { }, [availableRepaymentAssets]) useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect setHasUserMadeSelection(false) - // eslint-disable-next-line react-hooks/set-state-in-effect setUseDebtAsset(false) - // eslint-disable-next-line react-hooks/set-state-in-effect setSelectedSwapAsset(null) - // eslint-disable-next-line react-hooks/set-state-in-effect setDebtAssetAmount(BN_ZERO) - // eslint-disable-next-line react-hooks/set-state-in-effect setSwapAssetAmount(BN_ZERO) }, [repayFromWallet]) @@ -573,7 +561,6 @@ function BorrowModal(props: Props) { account.deposits.some(byDenom(asset.denom)) || account.lends.some(byDenom(asset.denom)) if (hasDebtAssetInAccount) { - // eslint-disable-next-line react-hooks/set-state-in-effect setUseDebtAsset(true) } } From ab41c6210a4122b97fd04f40fca3082065e30634 Mon Sep 17 00:00:00 2001 From: Patricie29 Date: Fri, 31 Oct 2025 17:52:35 +0100 Subject: [PATCH 12/13] fix: mobile and desktop footer position --- src/components/common/Footer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/common/Footer.tsx b/src/components/common/Footer.tsx index 102ba7498..deeb1a404 100644 --- a/src/components/common/Footer.tsx +++ b/src/components/common/Footer.tsx @@ -22,8 +22,8 @@ export default function Footer() { const version = `v${packageInfo.version}` return ( -