diff --git a/packages/common/src/api/tan-query/coins/useArtistCoinByTicker.ts b/packages/common/src/api/tan-query/coins/useArtistCoinByTicker.ts
index 472b53e8c97..2b84906916e 100644
--- a/packages/common/src/api/tan-query/coins/useArtistCoinByTicker.ts
+++ b/packages/common/src/api/tan-query/coins/useArtistCoinByTicker.ts
@@ -10,6 +10,7 @@ import {
useQueryContext,
type QueryContextType
} from '~/api/tan-query/utils/QueryContext'
+import { formatTicker } from '~/utils'
import { QUERY_KEYS } from '../queryKeys'
import { combineQueryStatuses } from '../utils'
@@ -32,7 +33,7 @@ export const fetchCoinTickerAvailability = async (
const sdk = await audiusSdk()
try {
// Use getCoinByTicker - if it returns a coin, the ticker is taken
- await sdk.coins.getCoinByTicker({ ticker })
+ await sdk.coins.getCoinByTicker({ ticker: formatTicker(ticker) })
// If we get a coin back, the ticker is not available
return { available: false }
} catch (error: any) {
@@ -66,8 +67,9 @@ const getArtistCoinByTickerQueryFn =
const [_ignored, ticker] = queryKey
const { audiusSdk } = context
const sdk = await audiusSdk()
+ // NOTE: Might not need to format the ticker here, but being safe
const response = await sdk.coins.getCoinByTicker({
- ticker
+ ticker: formatTicker(ticker)
})
const coin = coinFromSdk(response.data)
@@ -88,7 +90,7 @@ export const getArtistCoinByTickerOptions = (
{ ticker }: UseArtistCoinByTickerParams
) => {
return queryOptions({
- queryKey: getArtistCoinByTickerQueryKey(ticker),
+ queryKey: getArtistCoinByTickerQueryKey(formatTicker(ticker)),
queryFn: getArtistCoinByTickerQueryFn(context),
enabled: !!ticker
})
@@ -103,7 +105,10 @@ export const useArtistCoinByTicker = (
const mintQuery = useQuery({
...options,
- ...getArtistCoinByTickerOptions({ ...context, queryClient }, params)
+ ...getArtistCoinByTickerOptions(
+ { ...context, queryClient },
+ { ...params, ticker: formatTicker(params.ticker) }
+ )
})
const coinQuery = useArtistCoin(mintQuery.data!)
diff --git a/packages/common/src/utils/formatUtil.ts b/packages/common/src/utils/formatUtil.ts
index 308c8ce2a64..da01390e575 100644
--- a/packages/common/src/utils/formatUtil.ts
+++ b/packages/common/src/utils/formatUtil.ts
@@ -190,8 +190,18 @@ export const getHash = (str: string) =>
export const formatDoubleDigit = (value: number) =>
value.toString().padStart(2, '0')
+/**
+ * Formats a ticker to be uppercase and trimmed
+ */
+export const formatTicker = (ticker?: string) =>
+ ticker?.trim().toUpperCase() ?? ''
+
/**
* Formats a ticker to be url friendly
*/
-export const formatTickerForUrl = (ticker: string) =>
- ticker.startsWith('$') ? ticker.slice(1) : ticker
+export const formatTickerForUrl = (ticker: string) => {
+ const formattedTicker = formatTicker(ticker)
+ return formattedTicker.startsWith('$')
+ ? formattedTicker.slice(1)
+ : formattedTicker
+}
diff --git a/packages/common/src/utils/route.ts b/packages/common/src/utils/route.ts
index 1525fb7ae75..2f6dc2ac11a 100644
--- a/packages/common/src/utils/route.ts
+++ b/packages/common/src/utils/route.ts
@@ -3,7 +3,7 @@ import { matchPath, generatePath } from 'react-router'
import { ID, SearchCategory, SearchFilters } from '~/models'
-import { encodeUrlName } from './formatUtil'
+import { encodeUrlName, formatTickerForUrl } from './formatUtil'
import { convertGenreLabelToValue, Genre } from './genres'
// External Routes
@@ -473,4 +473,7 @@ export const searchPage = (searchOptions: SearchOptions) => {
}
export const coinPage = (ticker: string) =>
- `/coins/${ticker.startsWith('$') ? ticker.slice(1) : ticker}`
+ `/coins/${formatTickerForUrl(ticker)}`
+
+export const coinRedeemPage = (ticker: string, code?: string) =>
+ `/coins/${formatTickerForUrl(ticker)}/redeem${code ? `/${code}` : ''}`
diff --git a/packages/web/src/pages/coin-detail-page/CoinDetailPage.tsx b/packages/web/src/pages/coin-detail-page/CoinDetailPage.tsx
index 07b39483422..de60bdfbfd4 100644
--- a/packages/web/src/pages/coin-detail-page/CoinDetailPage.tsx
+++ b/packages/web/src/pages/coin-detail-page/CoinDetailPage.tsx
@@ -4,7 +4,8 @@ import {
useUser
} from '@audius/common/api'
import { coinDetailsMessages } from '@audius/common/messages'
-import { route } from '@audius/common/utils'
+import { coinPage } from '@audius/common/src/utils/route'
+import { formatTickerForUrl, route } from '@audius/common/utils'
import { Flex, LoadingSpinner } from '@audius/harmony'
import { Redirect, useParams } from 'react-router-dom'
@@ -91,13 +92,14 @@ export const CoinDetailPage = () => {
const { ticker } = useParams<{ ticker: string }>()
const isMobile = useIsMobile()
const { data: currentUserId } = useCurrentUserId()
+ const formattedTicker = formatTickerForUrl(ticker)
const {
data: coin,
isPending: coinPending,
isError,
isSuccess
- } = useArtistCoinByTicker({ ticker })
+ } = useArtistCoinByTicker({ ticker: formattedTicker })
const { data: owner } = useUser(coin?.ownerId, {
enabled: !!coin?.ownerId
@@ -107,6 +109,10 @@ export const CoinDetailPage = () => {
return
}
+ if (ticker !== formattedTicker) {
+ return
+ }
+
if (isError || (isSuccess && !coin)) {
return
}
diff --git a/packages/web/src/pages/coin-redeem-page/CoinRedeemPage.tsx b/packages/web/src/pages/coin-redeem-page/CoinRedeemPage.tsx
index 037c3f76d17..3599a129af8 100644
--- a/packages/web/src/pages/coin-redeem-page/CoinRedeemPage.tsx
+++ b/packages/web/src/pages/coin-redeem-page/CoinRedeemPage.tsx
@@ -12,9 +12,11 @@ import {
import { toast } from '@audius/common/src/store/ui/toast/slice'
import {
coinPage,
+ coinRedeemPage,
COINS_EXPLORE_PAGE,
NOT_FOUND_PAGE
} from '@audius/common/src/utils/route'
+import { formatTickerForUrl } from '@audius/common/utils'
import {
Button,
Flex,
@@ -173,6 +175,7 @@ const PageContent = ({
export const CoinRedeemPage = () => {
const { ticker, code } = useParams<{ ticker: string; code?: string }>()
const isMobile = useIsMobile()
+ const formattedTicker = formatTickerForUrl(ticker)
const {
data: coin,
@@ -203,6 +206,10 @@ export const CoinRedeemPage = () => {
return
}
+ if (ticker !== formattedTicker) {
+ return
+ }
+
if (isError || (isSuccess && !coin)) {
return
}