Skip to content
Merged

v3.0.3 #1685

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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',
{
Expand Down
16 changes: 16 additions & 0 deletions install_charting_library.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
25 changes: 24 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const nextConfig = {
reactStrictMode: true,
transpilePackages: ['bignumber.js'],
images: {
remotePatterns: [
{
Expand Down Expand Up @@ -98,13 +99,35 @@ const nextConfig = {
},
]
},
webpack(config) {
webpack(config, { isServer }) {
config.module.rules.push({
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack'],
})

// 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')
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
},
}
Expand Down
68 changes: 34 additions & 34 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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",
Expand Down
6 changes: 4 additions & 2 deletions src/api/campaign/getCampaignApys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
5 changes: 5 additions & 0 deletions src/chains/neutron/neutron-1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ const Neutron1: ChainConfig = {
campaignIds: ['lido'],
campaignDenom: 'stETH',
},
{
denom: 'ibc/0E293A7622DC9A6439DB60E6D234B5AF446962E27CA3AB44D0590603DFF6968E',
campaignIds: ['ntrn-rewards'],
campaignDenom: 'wbtc',
},
],
deprecated: [
'ibc/3649CE0C8A2C79048D8C6F31FF18FA69C9BC7EB193512E0BD03B733011290445',
Expand Down
5 changes: 3 additions & 2 deletions src/components/Modals/Farm/FarmBorrowings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
51 changes: 33 additions & 18 deletions src/components/Modals/Farm/FarmDeposits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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],
)
Expand All @@ -194,23 +196,36 @@ 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],
)

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],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function Apy(props: Props) {
return (
<div className='flex justify-end my-auto text-xs'>
{hasLstApy ? (
<Tooltip content='Includes underlying staking APY from Liquid Staking Token' type='info'>
<Tooltip content='Includes underlying staking or rewards APY' type='info'>
<div className='border-b border-dashed hover:cursor-help border-white/40 hover:border-transparent'>
<FormattedNumber
amount={totalApy}
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export default function Footer() {

const version = `v${packageInfo.version}`
return (
<footer className='flex items-center justify-center w-full h-6 -mt-6'>
<div className='w-full p-2 pt-0 text-right md:p-0 md:px-4'>
<footer className='flex items-center justify-center w-full h-6 -mt-6 md:fixed md:bottom-0 md:right-0 md:justify-end md:w-auto'>
<div className='w-full p-2 pt-0 text-right md:p-0 md:px-4 md:w-auto'>
<TextLink
className='text-xs text-white opacity-50 hover:text-white hover:opacity-80'
href={`${DocURL.FEATURE_URL}${version}`}
Expand Down
1 change: 1 addition & 0 deletions src/components/common/Table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default function Table<T>(props: Props<T>) {
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,
Expand Down
4 changes: 1 addition & 3 deletions src/components/portfolio/Account/Summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ function Content(props: Props) {
title: (
<Tooltip
type='info'
content={
hasLstApy ? 'Includes underlying staking APY from Liquid Staking Tokens' : undefined
}
content={hasLstApy ? 'Includes underlying staking or rewards APY' : undefined}
>
<div className='flex w-full justify-center'>
<div
Expand Down
Loading