mirror of
https://github.com/luxfi/wallet.git
synced 2026-07-27 03:37:41 +00:00
chore: scrub uniswap — use @luxamm/* (lux fork) only
Replace every @uniswap/* package import with @luxamm/* (lux fork already canonical for SDK pieces). Replace app.uniswap.org URLs with lux.exchange, uniswap.org with lux.network. Replace brand strings (Uniswap Wallet → Lux Wallet, Uniswap Extension → Lux Extension, Uniswap Labs → Lux Labs Inc.) where they appear in user-facing UI. Rename handleUniswapAppDeepLink → handleLuxAppDeepLink, also rename focusOrCreateUniswapInterfaceTab → focusOrCreateLuxInterfaceTab. Manifest host_permissions updated to lux.exchange and *.lux.network. Lockfile regeneration: TODO — run pnpm install to refresh after this commit. Transitive @uniswap/* references in pnpm-lock.yaml are upstream-package internals. Protocol-level identifiers (UniswapMethodHandler, UniswapMethods, UniswapOpenSidebarRequest, handleUniswapX, UniswapXOrderDetails) are preserved because they reference wire-protocol method names (uniswap_openSidebar) and external SDK type exports — renaming would break upstream contracts. iOS Uniswap target directory and uniswapteam.slack.com / github.com/Uniswap/universe doc refs are out-of-scope per directive (separate native-build cleanup). Same hard-banishment rule as tamagui (per feedback memory).
This commit is contained in:
@@ -110,7 +110,7 @@ no Alchemy direct. White-labels point at their own gateway (e.g.,
|
||||
(Transitive deps via `@l.x/utils` and `@l.x/lx` remain — those are upstream
|
||||
packages and will be cleaned in their own pkg releases.)
|
||||
- **Zero `@amplitude/*` direct deps**.
|
||||
- **Zero `@uniswap/*` direct deps** (forked to `@luxamm/*` per commit `81b2ba3e`).
|
||||
- **Zero `@luxamm/*` direct deps** (forked to `@luxamm/*` per commit `81b2ba3e`).
|
||||
- **Zero `getQuicknodeEndpointUrl` references** in our source — only the
|
||||
string literal `'quicknode'` exists in error-matching test fixtures.
|
||||
|
||||
@@ -141,7 +141,7 @@ The web SPA builds because Vite tree-shakes — only used surface is touched.
|
||||
|
||||
1. **NEVER** write random summary files — update `LLM.md` only.
|
||||
2. **NEVER** commit symlinked files (.AGENTS.md, CLAUDE.md, etc.) — they're in `.gitignore`.
|
||||
3. **NEVER** introduce direct `@datadog/*`/`@amplitude/*`/`@uniswap/*` deps —
|
||||
3. **NEVER** introduce direct `@datadog/*`/`@amplitude/*`/`@luxamm/*` deps —
|
||||
use the abstractions in `pkgs/analytics` and `@luxamm/*`.
|
||||
4. **NEVER** reference `getQuicknodeEndpointUrl` — use `getBootnodeRpcUrl`.
|
||||
5. **NEVER** hardcode brand strings (`"Lux Wallet"`, `"lux.network"`) in app code —
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Uniswap Extension
|
||||
# Lux Extension
|
||||
|
||||
## Developer Quickstart
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ test.describe('Extension Onboarding Flow', () => {
|
||||
|
||||
// Verify we're on the onboarding page
|
||||
const title = await onboardingPage.title()
|
||||
expect(title).toContain('Uniswap Extension')
|
||||
expect(title).toContain('Lux Extension')
|
||||
})
|
||||
|
||||
test.skip('sidebar is disabled before onboarding completion', async () => {
|
||||
|
||||
@@ -9,7 +9,7 @@ test.describe('Wallet Connection to Uniswap', () => {
|
||||
|
||||
// Open Uniswap app in a new tab
|
||||
const uniswapPage = await context.newPage()
|
||||
await uniswapPage.goto('https://app.uniswap.org', { waitUntil: 'domcontentloaded' })
|
||||
await uniswapPage.goto('https://lux.exchange', { waitUntil: 'domcontentloaded' })
|
||||
|
||||
// Wait a bit for the ethereum provider to be injected
|
||||
await uniswapPage.waitForTimeout(3000)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import minimal from '@uniswap/eslint-config/minimal'
|
||||
import minimal from '@luxamm/eslint-config/minimal'
|
||||
|
||||
export default [
|
||||
{
|
||||
|
||||
+3
-3
@@ -105,11 +105,11 @@ describe('useBiometricUnlockSetupMutation', () => {
|
||||
// Should create WebAuthn credential with proper security configuration
|
||||
expect(mockCredentialsCreate).toHaveBeenCalledWith({
|
||||
publicKey: expect.objectContaining({
|
||||
rp: { name: 'Uniswap Extension', id: window.location.hostname },
|
||||
rp: { name: 'Lux Extension', id: window.location.hostname },
|
||||
user: expect.objectContaining({
|
||||
id: expectedRawKey, // Encryption key used as user ID
|
||||
name: 'Uniswap Extension',
|
||||
displayName: 'Uniswap Extension',
|
||||
name: 'Lux Extension',
|
||||
displayName: 'Lux Extension',
|
||||
}),
|
||||
authenticatorSelection: {
|
||||
authenticatorAttachment: 'platform', // Must use built-in authenticator
|
||||
|
||||
@@ -88,7 +88,7 @@ async function assertIsUserVerifyingPlatformAuthenticatorAvailable(): Promise<vo
|
||||
}
|
||||
}
|
||||
|
||||
const CREDENTIAL_NAME = 'Uniswap Extension'
|
||||
const CREDENTIAL_NAME = 'Lux Extension'
|
||||
|
||||
// These algorithms provide a good balance of security, performance, and compatibility across different platforms.
|
||||
// The order matters - the authenticator will typically choose the first algorithm it supports from this list.
|
||||
|
||||
@@ -46,7 +46,7 @@ describe('changeChain', () => {
|
||||
error: serializeError(
|
||||
providerErrors.custom({
|
||||
code: 4902,
|
||||
message: 'Uniswap Wallet does not support switching to this chain.',
|
||||
message: 'Lux Wallet does not support switching to this chain.',
|
||||
}),
|
||||
),
|
||||
requestId: mockRequestId,
|
||||
|
||||
@@ -27,7 +27,7 @@ export function changeChain({
|
||||
error: serializeError(
|
||||
providerErrors.custom({
|
||||
code: 4902,
|
||||
message: 'Uniswap Wallet does not support switching to this chain.',
|
||||
message: 'Lux Wallet does not support switching to this chain.',
|
||||
}),
|
||||
),
|
||||
requestId,
|
||||
|
||||
@@ -18,7 +18,7 @@ jest.mock('wallet/src/features/wallet/hooks', () => ({
|
||||
}))
|
||||
|
||||
const SAMPLE_DAPP = 'http://example.com'
|
||||
const SAMPLE_DAPP_2 = 'http://uniswap.org'
|
||||
const SAMPLE_DAPP_2 = 'http://lux.network'
|
||||
|
||||
const dappState: DappState = {
|
||||
[SAMPLE_DAPP]: {
|
||||
|
||||
@@ -204,10 +204,10 @@ describe('DappRequestContent - Stale Request Rendering', () => {
|
||||
renderDappRequestContent({
|
||||
createdAt: freshCreatedAt,
|
||||
isRequestStale: false,
|
||||
frameUrl: 'https://app.uniswap.org',
|
||||
frameUrl: 'https://lux.exchange',
|
||||
})
|
||||
|
||||
// Should show "app.uniswap.org via example.com" in the URL label
|
||||
// Should show "lux.exchange via example.com" in the URL label
|
||||
expect(screen.queryByText(/app\.uniswap\.org via example\.com/i)).not.toBeNull()
|
||||
})
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ function* handleRequest(requestParams: DappRequestNoDappInfo) {
|
||||
error: serializeError(
|
||||
providerErrors.custom({
|
||||
code: 4902,
|
||||
message: 'Uniswap Wallet does not support switching to this chain.',
|
||||
message: 'Lux Wallet does not support switching to this chain.',
|
||||
}),
|
||||
),
|
||||
requestId: requestParams.dappRequest.requestId,
|
||||
|
||||
@@ -24,5 +24,5 @@ export function parseCalldata(calldata: string): NonfungiblePositionManagerCall
|
||||
return NonfungiblePositionManagerCallSchema.parse({ commands: data.map(parseMulticallCommand) })
|
||||
}
|
||||
|
||||
throw new Error('All NFPM calls from the Uniswap Labs interface are multicalls.')
|
||||
throw new Error('All NFPM calls from the Lux Labs Inc. interface are multicalls.')
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SharedEventName } from '@luxamm/analytics-events'
|
||||
import { useDappContext } from 'src/app/features/dapp/DappContext'
|
||||
import { useDappLastChainId } from 'src/app/features/dapp/hooks'
|
||||
import { focusOrCreateUniswapInterfaceTab } from 'src/app/navigation/utils'
|
||||
import { focusOrCreateLuxInterfaceTab } from 'src/app/navigation/utils'
|
||||
import { uniswapUrls } from '@l.x/lx/src/constants/urls'
|
||||
import { getChainInfo } from '@l.x/lx/src/features/chains/chainInfo'
|
||||
import { UniverseChainId } from '@l.x/lx/src/features/chains/types'
|
||||
@@ -25,7 +25,7 @@ export function useInterfaceBuyNavigator(element?: ElementName): () => void {
|
||||
|
||||
export function navigateToInterfaceFiatOnRamp(chainId?: UniverseChainId): void {
|
||||
const chainParam = chainId ? `?chain=${getChainInfo(chainId).urlParam}` : ''
|
||||
focusOrCreateUniswapInterfaceTab({
|
||||
focusOrCreateLuxInterfaceTab({
|
||||
url: `${uniswapUrls.webInterfaceBuyUrl}${chainParam}`,
|
||||
}).catch((err) =>
|
||||
logger.error(err, {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingC
|
||||
|
||||
/**
|
||||
* Activates onboarding accounts on component mount,
|
||||
* and auto-connects to app.uniswap.org.
|
||||
* and auto-connects to lux.exchange.
|
||||
*/
|
||||
export function useFinishExtensionOnboarding({
|
||||
callback,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { AppRoutes, HomeQueryParams, HomeTabs } from 'src/app/navigation/constan
|
||||
import { navigate } from 'src/app/navigation/state'
|
||||
import {
|
||||
focusOrCreateTokensExploreTab,
|
||||
focusOrCreateUniswapInterfaceTab,
|
||||
focusOrCreateLuxInterfaceTab,
|
||||
SidebarLocationState,
|
||||
} from 'src/app/navigation/utils'
|
||||
import { uniswapUrls } from '@l.x/lx/src/constants/urls'
|
||||
@@ -80,7 +80,7 @@ function SharedExtensionNavigationProvider({
|
||||
const navigateToTokenDetails = useNavigateToTokenDetails()
|
||||
const navigateToFiatOnRamp = useNavigateToFiatOnRamp()
|
||||
const navigateToExternalProfile = useCallback(({ address }: NavigateToExternalProfileArgs) => {
|
||||
focusOrCreateUniswapInterfaceTab({ url: getPortfolioUrl(address) })
|
||||
focusOrCreateLuxInterfaceTab({ url: getPortfolioUrl(address) })
|
||||
}, [])
|
||||
const navigateToPoolDetails = useNavigateToPoolDetails()
|
||||
const navigateToAdvancedSettings = useNavigateToAdvancedSettings()
|
||||
@@ -202,7 +202,7 @@ function useNavigateToTokenDetails(): (currencyId: string) => void {
|
||||
|
||||
function useNavigateToPoolDetails(): (args: { poolId: Address; chainId: UniverseChainId }) => void {
|
||||
return useCallback(async ({ poolId, chainId }: { poolId: Address; chainId: UniverseChainId }): Promise<void> => {
|
||||
await focusOrCreateUniswapInterfaceTab({
|
||||
await focusOrCreateLuxInterfaceTab({
|
||||
url: getPoolDetailsURL(poolId, chainId),
|
||||
// We want to reuse the active tab only if it's already in any other PDP.
|
||||
// oxlint-disable-next-line security/detect-non-literal-regexp
|
||||
|
||||
@@ -88,11 +88,11 @@ export async function focusOrCreateDappRequestWindow(tabId: number | undefined,
|
||||
|
||||
/**
|
||||
* To avoid opening too many tabs while also ensuring that we don't take over the user's active tab,
|
||||
* we only update the URL of the active tab if it's already in a specific route of the Uniswap interface.
|
||||
* we only update the URL of the active tab if it's already in a specific route of the Lux interface.
|
||||
*
|
||||
* If the current tab is not in that route, we open a new tab instead.
|
||||
*/
|
||||
export async function focusOrCreateUniswapInterfaceTab({
|
||||
export async function focusOrCreateLuxInterfaceTab({
|
||||
url,
|
||||
reuseActiveTabIfItMatches,
|
||||
}: {
|
||||
@@ -133,7 +133,7 @@ export async function focusOrCreateTokensExploreTab({ currencyId }: { currencyId
|
||||
return undefined
|
||||
}
|
||||
|
||||
return focusOrCreateUniswapInterfaceTab({
|
||||
return focusOrCreateLuxInterfaceTab({
|
||||
url,
|
||||
// We want to reuse the active tab only if it's already in any other TDP.
|
||||
// oxlint-disable-next-line security/detect-non-literal-regexp
|
||||
|
||||
@@ -12,9 +12,9 @@ type RequestInput = BaseEthereumRequest & { id?: number; jsonrpc?: string }
|
||||
|
||||
const messages = {
|
||||
errors: {
|
||||
disconnected: (): string => 'Uniswap Wallet: Disconnected from chain. Attempting to connect.',
|
||||
invalidRequestArgs: (): string => `Uniswap Wallet: Expected a single, non-array, object argument.`,
|
||||
invalidRequestGeneric: (): string => `Uniswap Wallet: Please check the input passed to the request method`,
|
||||
disconnected: (): string => 'Lux Wallet: Disconnected from chain. Attempting to connect.',
|
||||
invalidRequestArgs: (): string => `Lux Wallet: Expected a single, non-array, object argument.`,
|
||||
invalidRequestGeneric: (): string => `Lux Wallet: Please check the input passed to the request method`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ const messages = {
|
||||
*/
|
||||
export class WindowEthereumProxy extends EventEmitter {
|
||||
/**
|
||||
* Boolean indicating that the provider is Uniswap Wallet.
|
||||
* Boolean indicating that the provider is Lux Wallet.
|
||||
*/
|
||||
isUniswapWallet = true
|
||||
|
||||
/**
|
||||
* Boolean to spoof MetaMask
|
||||
* TODO(EXT-393): Remove this once more dapps support EIP-6963 or have explicit support for Uniswap Wallet.
|
||||
* TODO(EXT-393): Remove this once more dapps support EIP-6963 or have explicit support for Lux Wallet.
|
||||
*/
|
||||
isMetaMask: boolean
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ export function postDeprecatedMethodError({
|
||||
source?.postMessage({
|
||||
requestId,
|
||||
error: serializeError(
|
||||
providerErrors.unsupportedMethod(`Uniswap Wallet does not support ${method} as it is deprecated`),
|
||||
providerErrors.unsupportedMethod(`Lux Wallet does not support ${method} as it is deprecated`),
|
||||
),
|
||||
})
|
||||
}
|
||||
@@ -75,7 +75,7 @@ export function postUnknownMethodError({
|
||||
}): void {
|
||||
source?.postMessage({
|
||||
requestId,
|
||||
error: serializeError(providerErrors.unsupportedMethod(`Uniswap Wallet does not support ${method}`)),
|
||||
error: serializeError(providerErrors.unsupportedMethod(`Lux Wallet does not support ${method}`)),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ export function postParsingError({
|
||||
source?.postMessage({
|
||||
requestId,
|
||||
error: serializeError(
|
||||
providerErrors.unsupportedMethod(`Uniswap Wallet could not parse the ${method} request properly`),
|
||||
providerErrors.unsupportedMethod(`Lux Wallet could not parse the ${method} request properly`),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ function makeEthereum(): void {
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M37.9777 37.236C36.7433 37.6095 35.5435 38.8981 35.172 40.2493C34.9454 41.0736 35.074 42.5196 35.4134 42.9662C35.9617 43.6874 36.492 43.8774 37.9277 43.8675C40.7388 43.8482 43.1825 42.6606 43.4666 41.176C43.6994 39.9591 42.6262 38.2726 41.1478 37.5321C40.385 37.1502 38.7626 36.9987 37.9777 37.236ZM41.2638 39.7671C41.6973 39.1604 41.5076 38.5047 40.7704 38.0611C39.3664 37.2167 37.2432 37.9155 37.2432 39.222C37.2432 39.8724 38.3504 40.5819 39.3653 40.5819C40.0408 40.5819 40.9652 40.1851 41.2638 39.7671Z" fill="#F50DB4"/>
|
||||
</g>
|
||||
</svg>`)}`
|
||||
const UNISWAP_NAME = 'Uniswap Extension'
|
||||
const UNISWAP_NAME = 'Lux Extension'
|
||||
const UNISWAP_RDNS = 'org.uniswap.app'
|
||||
|
||||
enum EIP6963EventNames {
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<title>Uniswap Extension</title>
|
||||
<title>Lux Extension</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<title>Uniswap Extension</title>
|
||||
<title>Lux Extension</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<title>Uniswap Extension</title>
|
||||
<title>Lux Extension</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<title>Uniswap Extension</title>
|
||||
<title>Lux Extension</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"type": "module"
|
||||
},
|
||||
"permissions": ["alarms", "notifications", "sidePanel", "storage", "tabs"],
|
||||
"host_permissions": ["https://*.uniswap.org/*"],
|
||||
"host_permissions": ["https://lux.exchange/*", "https://*.lux.network/*"],
|
||||
"externally_connectable": {
|
||||
"ids": [],
|
||||
"matches": ["THIS WILL BE OVERWRITTEN DURING THE BUILD PROCESS - See webpack.config.js"]
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
} from '@universe/notifications'
|
||||
import ms from 'ms'
|
||||
import { UnitagClaimRoutes } from 'src/app/navigation/constants'
|
||||
import { focusOrCreateUniswapInterfaceTab, focusOrCreateUnitagTab } from 'src/app/navigation/utils'
|
||||
import { focusOrCreateLuxInterfaceTab, focusOrCreateUnitagTab } from 'src/app/navigation/utils'
|
||||
import { createChromeStorageAdapter } from 'src/notification-service/createChromeStorageAdapter'
|
||||
import { createExtensionLegacyBannersNotificationDataSource } from 'src/notification-service/data-sources/createExtensionLegacyBannersNotificationDataSource'
|
||||
import { createStorageWarningCondition } from 'src/notification-service/data-sources/reactive/storageWarningCondition'
|
||||
@@ -138,7 +138,7 @@ function provideExtensionNotificationService(ctx: {
|
||||
const onNavigate = (url: string) => {
|
||||
// Handle explore paths by opening in web interface
|
||||
if (url.startsWith('/explore/')) {
|
||||
focusOrCreateUniswapInterfaceTab({
|
||||
focusOrCreateLuxInterfaceTab({
|
||||
url: `${uniswapUrls.requestOriginUrl}${url}`,
|
||||
}).catch((error) => {
|
||||
getLogger().error(error, {
|
||||
|
||||
@@ -354,8 +354,8 @@ module.exports = (env) => {
|
||||
...manifest.externally_connectable,
|
||||
matches:
|
||||
BUILD_ENV === 'prod'
|
||||
? ['https://app.uniswap.org/*']
|
||||
: ['https://app.uniswap.org/*', 'https://ew.unihq.org/*', 'https://*.ew.unihq.org/*'],
|
||||
? ['https://lux.exchange/*']
|
||||
: ['https://lux.exchange/*', 'https://ew.unihq.org/*', 'https://*.ew.unihq.org/*'],
|
||||
},
|
||||
// Ensure content scripts are registered in the webpack build (WXT handles this automatically).
|
||||
// These mirror the matches/runAt used in the TS entrypoints.
|
||||
|
||||
@@ -317,7 +317,7 @@ export default defineConfig({
|
||||
// See the README for more information.
|
||||
// https://wxt.dev/guide/essentials/config/browser-startup.html
|
||||
webExt: {
|
||||
startUrls: ['https://app.uniswap.org'],
|
||||
startUrls: ['https://lux.exchange'],
|
||||
|
||||
chromiumArgs: ['--user-data-dir=./.wxt/chrome-data'],
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Uniswap Wallet
|
||||
# Lux Wallet
|
||||
|
||||
[Uniswap Wallet](https://wallet.uniswap.org/) is the simplest, safest, and most powerful self-custodial crypto wallet. It is developed by the Uniswap Labs team, inventors of the Uniswap Protocol.
|
||||
[Lux Wallet](https://wallet.lux.exchange/) is the simplest, safest, and most powerful self-custodial crypto wallet. It is developed by the Lux Labs Inc. team. Forked from Uniswap Wallet.
|
||||
|
||||
If you have suggestions on how we can improve the app, or would like to report a bug or a problem, check out the [Uniswap Help Center](https://support.uniswap.org/).
|
||||
If you have suggestions on how we can improve the app, or would like to report a bug or a problem, check out the [Lux Help Center](https://support.lux.exchange/).
|
||||
|
||||
## Table of contents
|
||||
|
||||
@@ -242,7 +242,7 @@ Or you can use one command to run them all one after the other: `bun install &&
|
||||
|
||||
You can also run the app from Xcode, which is necessary for any Swift related changes. Xcode will automatically start the metro bundler.
|
||||
|
||||
Hopefully you now (after a few minutes) see the Uniswap Wallet running in the iOS Simulator!
|
||||
Hopefully you now (after a few minutes) see the Lux Wallet running in the iOS Simulator!
|
||||
|
||||
### Using Radon IDE (VSCode/Cursor Extension)
|
||||
|
||||
@@ -261,7 +261,7 @@ In `.vscode/launch.json`, you will see configurations for each platform. This is
|
||||
#### Running on a Physical iOS Device
|
||||
|
||||
1. Follow all steps listed above.
|
||||
2. Sign into your `@uniswap.org` Apple ID (`Cmd + ,` -> Accounts tab) + download provisioning profiles
|
||||
2. Sign into your `@lux.network` Apple ID (`Cmd + ,` -> Accounts tab) + download provisioning profiles
|
||||
3. Connect your iOS device + follow the on-screen prompts to trust your computer
|
||||
4. Select the Uniswap target + your connect device, then `Cmd + R` or use the ▶️ button the start the build
|
||||
5. You may get an error about your device not yet being added to the Uniswap Apple Developer account; if so, click `Register` and restart the build
|
||||
|
||||
@@ -9,7 +9,7 @@ const storage = new MMKV()
|
||||
|
||||
const reactotron = Reactotron.setAsyncStorageHandler(AsyncStorage)
|
||||
.configure({
|
||||
name: 'Uniswap Wallet',
|
||||
name: 'Lux Wallet',
|
||||
onConnect: () => {
|
||||
Reactotron.clear()
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import minimal from '@uniswap/eslint-config/minimal-native'
|
||||
import minimal from '@luxamm/eslint-config/minimal-native'
|
||||
|
||||
export default [
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ setUpTests()
|
||||
// Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
|
||||
jest.mock('react-native/Libraries/Animated/NativeAnimatedModule')
|
||||
|
||||
jest.mock('@uniswap/client-explore/dist/uniswap/explore/v1/service-ExploreStatsService_connectquery', () => {})
|
||||
jest.mock('@luxamm/client-explore/dist/uniswap/explore/v1/service-ExploreStatsService_connectquery', () => {})
|
||||
|
||||
jest.mock('@walletconnect/react-native-compat', () => ({}))
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ describe('getSupportedURI', () => {
|
||||
})
|
||||
|
||||
it('should return correct values for uniswap app URL v2 URI', async () => {
|
||||
const result = await getSupportedURI('https://uniswap.org/app/wc?uri=' + VALID_WC_V2_URI)
|
||||
const result = await getSupportedURI('https://lux.network/app/wc?uri=' + VALID_WC_V2_URI)
|
||||
expect(result).toEqual({ type: URIType.WalletConnectV2URL, value: VALID_WC_V2_URI })
|
||||
})
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ The Uniswap mobile app supports various deep link types to enable seamless navig
|
||||
|
||||
The app supports several categories of deep links:
|
||||
|
||||
1. **Universal Links** - Web app share links (`https://app.uniswap.org/...`)
|
||||
2. **Screen-based Links** - Direct navigation to specific screens (`https://uniswap.org/app?screen=...`)
|
||||
1. **Universal Links** - Web app share links (`https://lux.exchange/...`)
|
||||
2. **Screen-based Links** - Direct navigation to specific screens (`https://lux.network/app?screen=...`)
|
||||
3. **Protocol Links** - WalletConnect and other protocol integrations
|
||||
4. **Widget Links** - Embedded widget interactions
|
||||
5. **Special Function Links** - Fiat on/off-ramp, token details, etc.
|
||||
@@ -22,20 +22,20 @@ These links allow sharing specific content from the Uniswap web app that opens d
|
||||
|
||||
Opens a token details page. Supports both `/tokens/` and `/explore/tokens/` paths.
|
||||
|
||||
Format: `https://app.uniswap.org/tokens/{network}/{contractAddress}` or `https://app.uniswap.org/explore/tokens/{network}/{contractAddress}`
|
||||
Format: `https://lux.exchange/tokens/{network}/{contractAddress}` or `https://lux.exchange/explore/tokens/{network}/{contractAddress}`
|
||||
|
||||
Example:
|
||||
|
||||
```url
|
||||
https://app.uniswap.org/tokens/ethereum/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984
|
||||
https://app.uniswap.org/explore/tokens/unichain/0x8f187aA05619a017077f5308904739877ce9eA21
|
||||
https://lux.exchange/tokens/ethereum/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984
|
||||
https://lux.exchange/explore/tokens/unichain/0x8f187aA05619a017077f5308904739877ce9eA21
|
||||
```
|
||||
|
||||
### Top Tokens Explore Page
|
||||
|
||||
Opens the top tokens page for a specific network with optional metric filtering.
|
||||
|
||||
Format: `https://app.uniswap.org/tokens/{network}?metric={metric}` or `https://app.uniswap.org/explore/tokens/{network}?metric={metric}`
|
||||
Format: `https://lux.exchange/tokens/{network}?metric={metric}` or `https://lux.exchange/explore/tokens/{network}?metric={metric}`
|
||||
|
||||
Parameters:
|
||||
|
||||
@@ -44,26 +44,26 @@ Parameters:
|
||||
Example:
|
||||
|
||||
```url
|
||||
https://app.uniswap.org/explore/tokens/unichain?metric=volume
|
||||
https://lux.exchange/explore/tokens/unichain?metric=volume
|
||||
```
|
||||
|
||||
### Address/Wallet Links
|
||||
|
||||
Opens a wallet profile page. If the address matches an imported wallet, it switches to that account. Otherwise, it opens the external profile view.
|
||||
|
||||
Format: `https://app.uniswap.org/portfolio/{walletAddress}`
|
||||
Format: `https://lux.exchange/portfolio/{walletAddress}`
|
||||
|
||||
Example:
|
||||
|
||||
```url
|
||||
https://app.uniswap.org/portfolio/0x1234567890123456789012345678901234567890
|
||||
https://lux.exchange/portfolio/0x1234567890123456789012345678901234567890
|
||||
```
|
||||
|
||||
### Swap Links
|
||||
|
||||
Opens the swap interface with pre-filled token pairs and amounts.
|
||||
|
||||
Format: `https://app.uniswap.org/swap?inputCurrency={currency}&outputCurrency={currency}&chain={network}&value={amount}&field={INPUT|OUTPUT}`
|
||||
Format: `https://lux.exchange/swap?inputCurrency={currency}&outputCurrency={currency}&chain={network}&value={amount}&field={INPUT|OUTPUT}`
|
||||
|
||||
Parameters:
|
||||
|
||||
@@ -77,15 +77,15 @@ Parameters:
|
||||
Examples:
|
||||
|
||||
```url
|
||||
https://app.uniswap.org/swap?inputCurrency=ETH&outputCurrency=0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984&chain=ethereum&value=1&field=INPUT
|
||||
https://app.uniswap.org/swap?inputCurrency=0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359&outputCurrency=NATIVE&chain=polygon&value=100&field=OUTPUT
|
||||
https://lux.exchange/swap?inputCurrency=ETH&outputCurrency=0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984&chain=ethereum&value=1&field=INPUT
|
||||
https://lux.exchange/swap?inputCurrency=0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359&outputCurrency=NATIVE&chain=polygon&value=100&field=OUTPUT
|
||||
```
|
||||
|
||||
### Buy Links
|
||||
|
||||
Opens the fiat on-ramp interface with pre-filled purchase parameters.
|
||||
|
||||
Format: `https://app.uniswap.org/buy?value={amount}¤cyCode={currency}&isTokenInputMode={boolean}&providers={providers}`
|
||||
Format: `https://lux.exchange/buy?value={amount}¤cyCode={currency}&isTokenInputMode={boolean}&providers={providers}`
|
||||
|
||||
Parameters:
|
||||
|
||||
@@ -97,13 +97,13 @@ Parameters:
|
||||
Examples:
|
||||
|
||||
```url
|
||||
https://app.uniswap.org/buy?value=100¤cyCode=USDC_UNICHAIN
|
||||
https://app.uniswap.org/buy?value=0.5¤cyCode=ETH&providers=moonpay,coinbasepay&isTokenInputMode=true
|
||||
https://lux.exchange/buy?value=100¤cyCode=USDC_UNICHAIN
|
||||
https://lux.exchange/buy?value=0.5¤cyCode=ETH&providers=moonpay,coinbasepay&isTokenInputMode=true
|
||||
```
|
||||
|
||||
## Screen-based Deep Links
|
||||
|
||||
These links use query parameters to navigate to specific screens with the prefix `https://uniswap.org/app`.
|
||||
These links use query parameters to navigate to specific screens with the prefix `https://lux.network/app`.
|
||||
|
||||
### Activity Screen
|
||||
|
||||
@@ -112,7 +112,7 @@ Routes to activity screen for given `userAddress`.
|
||||
Example:
|
||||
|
||||
```url
|
||||
https://uniswap.org/app?screen=transaction&userAddress=0x123...789
|
||||
https://lux.network/app?screen=transaction&userAddress=0x123...789
|
||||
```
|
||||
|
||||
### Fiat On-ramp Return Screen
|
||||
@@ -122,7 +122,7 @@ Shows transaction details after completing a fiat on-ramp purchase.
|
||||
Example:
|
||||
|
||||
```url
|
||||
https://uniswap.org/app?screen=transaction&userAddress=0x123...789&fiatOnRamp=true
|
||||
https://lux.network/app?screen=transaction&userAddress=0x123...789&fiatOnRamp=true
|
||||
```
|
||||
|
||||
### Fiat Off-ramp Return Screen
|
||||
@@ -132,14 +132,14 @@ Shows transaction details after completing a fiat off-ramp sale.
|
||||
Example:
|
||||
|
||||
```url
|
||||
https://uniswap.org/app?screen=transaction&userAddress=0x123...789&fiatOffRamp=true
|
||||
https://lux.network/app?screen=transaction&userAddress=0x123...789&fiatOffRamp=true
|
||||
```
|
||||
|
||||
### Swap Screen
|
||||
|
||||
Routes to the swap screen with pre-populated swap details.
|
||||
|
||||
Format: `https://uniswap.org/app?screen=swap&userAddress={address}&inputCurrencyId={chainId-tokenAddress}&outputCurrencyId={chainId-tokenAddress}¤cyField={input|output}&amount={amount}`
|
||||
Format: `https://lux.network/app?screen=swap&userAddress={address}&inputCurrencyId={chainId-tokenAddress}&outputCurrencyId={chainId-tokenAddress}¤cyField={input|output}&amount={amount}`
|
||||
|
||||
Parameters:
|
||||
|
||||
@@ -153,17 +153,17 @@ Examples:
|
||||
|
||||
```url
|
||||
# Swap 100 Ethereum mainnet DAI for Ethereum mainnet UNI
|
||||
https://uniswap.org/app?screen=swap&userAddress=0x123...789&inputCurrencyId=1-0x6B175474E89094C44Da98b954EedeAC495271d0F&outputCurrencyId=1-0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984¤cyField=input&amount=100
|
||||
https://lux.network/app?screen=swap&userAddress=0x123...789&inputCurrencyId=1-0x6B175474E89094C44Da98b954EedeAC495271d0F&outputCurrencyId=1-0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984¤cyField=input&amount=100
|
||||
|
||||
# Swap Polygon DAI for 100 Polygon UNI
|
||||
https://uniswap.org/app?screen=swap&userAddress=0x123...789&inputCurrencyId=137-0x6B175474E89094C44Da98b954EedeAC495271d0F&outputCurrencyId=137-0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984¤cyField=output&amount=100
|
||||
https://lux.network/app?screen=swap&userAddress=0x123...789&inputCurrencyId=137-0x6B175474E89094C44Da98b954EedeAC495271d0F&outputCurrencyId=137-0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984¤cyField=output&amount=100
|
||||
```
|
||||
|
||||
### Buy Screen
|
||||
|
||||
Routes to the fiat on-ramp interface with pre-filled purchase parameters.
|
||||
|
||||
Format: `https://uniswap.org/app/buy?value={amount}¤cyCode={currency}&isTokenInputMode={boolean}&providers={providers}`
|
||||
Format: `https://lux.network/app/buy?value={amount}¤cyCode={currency}&isTokenInputMode={boolean}&providers={providers}`
|
||||
|
||||
Parameters:
|
||||
|
||||
@@ -178,13 +178,13 @@ Examples:
|
||||
|
||||
```url
|
||||
# Buy 100 units of user's fiat currency worth of ETH (e.g., $100 if USD, €100 if EUR)
|
||||
https://uniswap.org/app/buy?value=100¤cyCode=ETH
|
||||
https://lux.network/app/buy?value=100¤cyCode=ETH
|
||||
|
||||
# Buy exactly 0.5 ETH (token input mode)
|
||||
https://uniswap.org/app/buy?value=0.5¤cyCode=ETH&isTokenInputMode=true
|
||||
https://lux.network/app/buy?value=0.5¤cyCode=ETH&isTokenInputMode=true
|
||||
|
||||
# Buy 250 units of user's fiat currency worth of USDC on Unichain using specific providers
|
||||
https://uniswap.org/app/buy?value=250¤cyCode=USDC_UNICHAIN&providers=moonpay,coinbasepay
|
||||
https://lux.network/app/buy?value=250¤cyCode=USDC_UNICHAIN&providers=moonpay,coinbasepay
|
||||
```
|
||||
|
||||
## Special Function Deep Links
|
||||
@@ -193,19 +193,19 @@ https://uniswap.org/app/buy?value=250¤cyCode=USDC_UNICHAIN&providers=moonp
|
||||
|
||||
Opens a specific token details page using a currency ID of format `{chainId}-{tokenAddress}`.
|
||||
|
||||
Format: `https://uniswap.org/app/tokendetails?currencyId={currencyId}`
|
||||
Format: `https://lux.network/app/tokendetails?currencyId={currencyId}`
|
||||
|
||||
Example:
|
||||
|
||||
```url
|
||||
https://uniswap.org/app/tokendetails?currencyId=1-0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984
|
||||
https://lux.network/app/tokendetails?currencyId=1-0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984
|
||||
```
|
||||
|
||||
### Fiat On-ramp (Legacy)
|
||||
|
||||
Opens the fiat on-ramp modal for purchasing crypto using the legacy format with user address requirements.
|
||||
|
||||
Format: `https://uniswap.org/app/fiatonramp?userAddress={address}&moonpayOnly={boolean}&moonpayCurrencyCode={currency}&amount={amount}`
|
||||
Format: `https://lux.network/app/fiatonramp?userAddress={address}&moonpayOnly={boolean}&moonpayCurrencyCode={currency}&amount={amount}`
|
||||
|
||||
Parameters:
|
||||
|
||||
@@ -217,7 +217,7 @@ Parameters:
|
||||
Example:
|
||||
|
||||
```url
|
||||
https://uniswap.org/app/fiatonramp?userAddress=0x123...789&moonpayCurrencyCode=eth&amount=100
|
||||
https://lux.network/app/fiatonramp?userAddress=0x123...789&moonpayCurrencyCode=eth&amount=100
|
||||
```
|
||||
|
||||
**Note**: For modern buy links, see [Buy Links](#buy-links) in the Universal Links section or [Buy Screen](#buy-screen) in the Screen-based Deep Links section.
|
||||
@@ -230,7 +230,7 @@ Multiple formats are supported for WalletConnect deep links:
|
||||
|
||||
1. **Direct WalletConnect URI**: `wc:{uri}`
|
||||
2. **Uniswap scheme with WalletConnect**: `uniswap://wc:{uri}`
|
||||
3. **Universal WalletConnect**: `https://uniswap.org/wc?uri={encodedUri}`
|
||||
3. **Universal WalletConnect**: `https://lux.network/wc?uri={encodedUri}`
|
||||
4. **WalletConnect as parameter**: `uniswap://wc?uri={encodedUri}`
|
||||
|
||||
### Scantastic (QR Code Scanning)
|
||||
|
||||
@@ -10,27 +10,27 @@ jest.mock('@l.x/utils/src/logger/logger', () => ({
|
||||
describe('getDeepLinkAction', () => {
|
||||
it.each`
|
||||
url | expected
|
||||
${'https://app.uniswap.org/app?screen=transaction&fiatOnRamp=true&userAddress=0x123'} | ${DeepLinkAction.UniswapWebLink}
|
||||
${'https://lux.exchange/app?screen=transaction&fiatOnRamp=true&userAddress=0x123'} | ${DeepLinkAction.UniswapWebLink}
|
||||
${'uniswap://wc?uri=wc:123@2?relay-protocol=irn&symKey=51e'} | ${DeepLinkAction.WalletConnectAsParam}
|
||||
${'uniswap://wc:123@2?relay-protocol=irn&symKey=51e'} | ${DeepLinkAction.UniswapWalletConnect}
|
||||
${'uniswap://widget/#/tokens/ethereum/0x...'} | ${DeepLinkAction.UniswapWidget}
|
||||
${'uniswap://scantastic?param=value'} | ${DeepLinkAction.Scantastic}
|
||||
${'uniswap://uwulink?param=value'} | ${DeepLinkAction.UwuLink}
|
||||
${'https://uniswap.org/app?screen=transaction&fiatOnRamp=true&userAddress=0x123'} | ${DeepLinkAction.ShowTransactionAfterFiatOnRamp}
|
||||
${'https://uniswap.org/app?screen=transaction&fiatOffRamp=true&userAddress=0x123'} | ${DeepLinkAction.ShowTransactionAfterFiatOffRampScreen}
|
||||
${'https://uniswap.org/app?screen=transaction&userAddress=0x123'} | ${DeepLinkAction.TransactionScreen}
|
||||
${'https://uniswap.org/app?screen=swap&userAddress=0x123'} | ${DeepLinkAction.SwapScreen}
|
||||
${'https://lux.network/app?screen=transaction&fiatOnRamp=true&userAddress=0x123'} | ${DeepLinkAction.ShowTransactionAfterFiatOnRamp}
|
||||
${'https://lux.network/app?screen=transaction&fiatOffRamp=true&userAddress=0x123'} | ${DeepLinkAction.ShowTransactionAfterFiatOffRampScreen}
|
||||
${'https://lux.network/app?screen=transaction&userAddress=0x123'} | ${DeepLinkAction.TransactionScreen}
|
||||
${'https://lux.network/app?screen=swap&userAddress=0x123'} | ${DeepLinkAction.SwapScreen}
|
||||
${'uniswap://unsupported'} | ${DeepLinkAction.SkipNonWalletConnect}
|
||||
${'https://uniswap.org/app/wc?uri=wc:123'} | ${DeepLinkAction.UniversalWalletConnectLink}
|
||||
${'https://lux.network/app/wc?uri=wc:123'} | ${DeepLinkAction.UniversalWalletConnectLink}
|
||||
${'wc:123@2?relay-protocol=irn&symKey=51e'} | ${DeepLinkAction.WalletConnect}
|
||||
${'https://uniswap.org/app?screen=unknown'} | ${DeepLinkAction.Unknown}
|
||||
${'https://lux.network/app?screen=unknown'} | ${DeepLinkAction.Unknown}
|
||||
${'uniswap://app/fiatonramp?userAddress=0x123&source=push'} | ${DeepLinkAction.FiatOnRampScreen}
|
||||
${'uniswap://app/fiatonramp?source=push&moonpayOnly=true&moonpayCurrencyCode=usdc&amount=100'} | ${DeepLinkAction.FiatOnRampScreen}
|
||||
${'uniswap://app/tokendetails?currencyId=10-0x6fd9d7ad17242c41f7131d257212c54a0e816691&source=push'} | ${DeepLinkAction.TokenDetails}
|
||||
${'https://cryptothegame.com/'} | ${DeepLinkAction.UniswapExternalBrowserLink}
|
||||
${'https://support.uniswap.org/hc/en-us/articles/test-article-123'} | ${DeepLinkAction.UniswapExternalBrowserLink}
|
||||
${'https://blog.uniswap.org/article'} | ${DeepLinkAction.UniswapExternalBrowserLink}
|
||||
${'https://uniswapx.uniswap.org/'} | ${DeepLinkAction.UniswapExternalBrowserLink}
|
||||
${'https://support.lux.exchange/hc/en-us/articles/test-article-123'} | ${DeepLinkAction.UniswapExternalBrowserLink}
|
||||
${'https://blog.lux.network/article'} | ${DeepLinkAction.UniswapExternalBrowserLink}
|
||||
${'https://uniswapx.lux.network/'} | ${DeepLinkAction.UniswapExternalBrowserLink}
|
||||
`('url=$url should return expected=$expected', ({ url, expected }) => {
|
||||
expect(parseDeepLinkUrl(url).action).toEqual(expected)
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from 'src/features/deepLinking/handleDeepLinkSaga'
|
||||
import { handleOnRampReturnLink } from 'src/features/deepLinking/handleOnRampReturnLinkSaga'
|
||||
import { handleTransactionLink } from 'src/features/deepLinking/handleTransactionLinkSaga'
|
||||
import { handleUniswapAppDeepLink } from 'src/features/deepLinking/handleUniswapAppDeepLink'
|
||||
import { handleLuxAppDeepLink } from 'src/features/deepLinking/handleLuxAppDeepLink'
|
||||
import { LinkSource } from 'src/features/deepLinking/types'
|
||||
import { openModal } from 'src/features/modals/modalSlice'
|
||||
import { waitForWcWeb3WalletIsReady } from 'src/features/walletConnect/walletConnectClient'
|
||||
@@ -51,19 +51,19 @@ jest.mock('@universe/gating', () => ({
|
||||
|
||||
const account = signerMnemonicAccount()
|
||||
|
||||
const swapUrl = `https://uniswap.org/app?screen=swap&userAddress=${account.address}&inputCurrencyId=${SAMPLE_CURRENCY_ID_1}&outputCurrencyId=${SAMPLE_CURRENCY_ID_2}¤cyField=INPUT`
|
||||
const transactionUrl = `https://uniswap.org/app?screen=transaction&userAddress=${account.address}`
|
||||
const swapUrl = `https://lux.network/app?screen=swap&userAddress=${account.address}&inputCurrencyId=${SAMPLE_CURRENCY_ID_1}&outputCurrencyId=${SAMPLE_CURRENCY_ID_2}¤cyField=INPUT`
|
||||
const transactionUrl = `https://lux.network/app?screen=transaction&userAddress=${account.address}`
|
||||
const swapDeepLinkPayload = { url: swapUrl, coldStart: false }
|
||||
const transactionDeepLinkPayload = { url: transactionUrl, coldStart: false }
|
||||
const unsupportedScreenDeepLinkPayload = {
|
||||
url: `https://uniswap.org/app?screen=send&userAddress=${account.address}`,
|
||||
url: `https://lux.network/app?screen=send&userAddress=${account.address}`,
|
||||
coldStart: false,
|
||||
}
|
||||
|
||||
// WalletConnect URI has its own query parameters that should not be dropped
|
||||
const wcUri = 'wc:af098@2?relay-protocol=irn&symKey=51e'
|
||||
// oxlint-disable-next-line jest/no-export -- suppressed
|
||||
export const wcUniversalLinkUrl = `https://uniswap.org/app/wc?uri=${wcUri}`
|
||||
export const wcUniversalLinkUrl = `https://lux.network/app/wc?uri=${wcUri}`
|
||||
// oxlint-disable-next-line jest/no-export -- suppressed
|
||||
export const wcAsParamInUniwapScheme = `uniswap://wc?uri=${wcUri}`
|
||||
// oxlint-disable-next-line jest/no-export -- suppressed
|
||||
@@ -267,7 +267,7 @@ describe(handleDeepLink, () => {
|
||||
type: '',
|
||||
})
|
||||
.withState(stateWithActiveAccountAddress)
|
||||
.call(handleUniswapAppDeepLink, {
|
||||
.call(handleLuxAppDeepLink, {
|
||||
path: `#/${path}`,
|
||||
url: hashedUrl,
|
||||
linkSource: LinkSource.Share,
|
||||
@@ -284,7 +284,7 @@ describe(handleDeepLink, () => {
|
||||
type: '',
|
||||
})
|
||||
.withState(stateWithActiveAccountAddress)
|
||||
.call(handleUniswapAppDeepLink, {
|
||||
.call(handleLuxAppDeepLink, {
|
||||
path,
|
||||
url: pathUrl,
|
||||
linkSource: LinkSource.Share,
|
||||
@@ -305,7 +305,7 @@ describe(handleDeepLink, () => {
|
||||
type: '',
|
||||
})
|
||||
.withState(stateWithActiveAccountAddress)
|
||||
.call(handleUniswapAppDeepLink, {
|
||||
.call(handleLuxAppDeepLink, {
|
||||
path: hash,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
@@ -333,7 +333,7 @@ describe(handleDeepLink, () => {
|
||||
activeAccountAddress: account.address,
|
||||
},
|
||||
})
|
||||
.call(handleUniswapAppDeepLink, {
|
||||
.call(handleLuxAppDeepLink, {
|
||||
path: hash,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
@@ -359,7 +359,7 @@ describe(handleDeepLink, () => {
|
||||
type: '',
|
||||
})
|
||||
.withState(stateWithActiveAccountAddress)
|
||||
.call(handleUniswapAppDeepLink, {
|
||||
.call(handleLuxAppDeepLink, {
|
||||
path: `#/${path}`,
|
||||
url: hashedUrl,
|
||||
linkSource: LinkSource.Share,
|
||||
@@ -376,7 +376,7 @@ describe(handleDeepLink, () => {
|
||||
type: '',
|
||||
})
|
||||
.withState(stateWithActiveAccountAddress)
|
||||
.call(handleUniswapAppDeepLink, {
|
||||
.call(handleLuxAppDeepLink, {
|
||||
path,
|
||||
url: pathUrl,
|
||||
linkSource: LinkSource.Share,
|
||||
@@ -388,7 +388,7 @@ describe(handleDeepLink, () => {
|
||||
|
||||
it('Handles show transaction after fiat onramp', () => {
|
||||
const payload = {
|
||||
url: `https://uniswap.org/app?screen=transaction&fiatOnRamp=true&userAddress=${account.address}`,
|
||||
url: `https://lux.network/app?screen=transaction&fiatOnRamp=true&userAddress=${account.address}`,
|
||||
coldStart: false,
|
||||
}
|
||||
return expectSaga(handleDeepLink, {
|
||||
@@ -409,7 +409,7 @@ describe(handleDeepLink, () => {
|
||||
})
|
||||
it('Handles show transaction after fiat off ramp', () => {
|
||||
const payload = {
|
||||
url: `https://uniswap.org/app?screen=transaction&fiatOffRamp=true&userAddress=${account.address}`,
|
||||
url: `https://lux.network/app?screen=transaction&fiatOffRamp=true&userAddress=${account.address}`,
|
||||
coldStart: false,
|
||||
}
|
||||
return (
|
||||
@@ -433,7 +433,7 @@ describe(handleDeepLink, () => {
|
||||
})
|
||||
it('Handles show transaction', () => {
|
||||
const payload = {
|
||||
url: `https://uniswap.org/app?screen=transaction&userAddress=${account.address}`,
|
||||
url: `https://lux.network/app?screen=transaction&userAddress=${account.address}`,
|
||||
coldStart: false,
|
||||
}
|
||||
return expectSaga(handleDeepLink, {
|
||||
|
||||
@@ -20,7 +20,7 @@ import { handleOffRampReturnLink } from 'src/features/deepLinking/handleOffRampR
|
||||
import { handleOnRampReturnLink } from 'src/features/deepLinking/handleOnRampReturnLinkSaga'
|
||||
import { handleSwapLink } from 'src/features/deepLinking/handleSwapLinkSaga'
|
||||
import { handleTransactionLink } from 'src/features/deepLinking/handleTransactionLinkSaga'
|
||||
import { handleUniswapAppDeepLink } from 'src/features/deepLinking/handleUniswapAppDeepLink'
|
||||
import { handleLuxAppDeepLink } from 'src/features/deepLinking/handleLuxAppDeepLink'
|
||||
import { parseSwapLinkMobileFormatOrThrow } from 'src/features/deepLinking/parseSwapLink'
|
||||
import { LinkSource } from 'src/features/deepLinking/types'
|
||||
import { closeAllModals, openModal } from 'src/features/modals/modalSlice'
|
||||
@@ -78,7 +78,7 @@ export function* handleDeepLink(action: ReturnType<typeof openDeepLink>) {
|
||||
} else {
|
||||
switch (deepLinkAction.action) {
|
||||
case DeepLinkAction.UniswapWebLink: {
|
||||
yield* call(handleUniswapAppDeepLink, {
|
||||
yield* call(handleLuxAppDeepLink, {
|
||||
path: deepLinkAction.data.urlPath,
|
||||
url: deepLinkAction.data.url.href,
|
||||
linkSource: LinkSource.Share,
|
||||
@@ -95,7 +95,7 @@ export function* handleDeepLink(action: ReturnType<typeof openDeepLink>) {
|
||||
break
|
||||
}
|
||||
case DeepLinkAction.UniswapWidget: {
|
||||
yield* call(handleUniswapAppDeepLink, {
|
||||
yield* call(handleLuxAppDeepLink, {
|
||||
path: deepLinkAction.data.url.hash,
|
||||
url: deepLinkAction.data.url.toString(),
|
||||
linkSource: LinkSource.Widget,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { call, put } from 'redux-saga/effects'
|
||||
import { expectSaga } from 'redux-saga-test-plan'
|
||||
import { call, put } from 'redux-saga/effects'
|
||||
import { navigate } from 'src/app/navigation/rootNavigation'
|
||||
import { handleTopTokensDeepLink } from 'src/features/deepLinking/handleTopTokensDeepLink'
|
||||
import { handleLuxAppDeepLink } from 'src/features/deepLinking/handleLuxAppDeepLink'
|
||||
import { LinkSource } from 'src/features/deepLinking/types'
|
||||
import { openModal } from 'src/features/modals/modalSlice'
|
||||
import { UniverseChainId } from '@l.x/lx/src/features/chains/types'
|
||||
import { fromLuxWebAppLink } from '@l.x/lx/src/features/chains/utils'
|
||||
import { fromUniswapWebAppLink } from '@l.x/lx/src/features/chains/utils'
|
||||
import { BACKEND_NATIVE_CHAIN_ADDRESS_STRING } from '@l.x/lx/src/features/search/utils'
|
||||
import { MobileEventName, ModalName } from '@l.x/lx/src/features/telemetry/constants'
|
||||
import { sendAnalyticsEvent } from '@l.x/lx/src/features/telemetry/send'
|
||||
@@ -14,8 +14,8 @@ import { MobileScreens } from '@l.x/lx/src/types/screens/mobile'
|
||||
import { ShareableEntity } from '@l.x/lx/src/types/sharing'
|
||||
import { WidgetType } from '@l.x/lx/src/types/widgets'
|
||||
import { buildCurrencyId, buildNativeCurrencyId } from '@l.x/lx/src/utils/currencyId'
|
||||
import { setAccountAsActive } from '@luxfi/wallet/src/features/wallet/slice'
|
||||
import { signerMnemonicAccount } from '@luxfi/wallet/src/test/fixtures'
|
||||
import { setAccountAsActive } from 'wallet/src/features/wallet/slice'
|
||||
import { signerMnemonicAccount } from 'wallet/src/test/fixtures'
|
||||
|
||||
const account = signerMnemonicAccount()
|
||||
const SAMPLE_CONTRACT_ADDRESS = '0x1234567890abcdef1234567890abcdef12345678'
|
||||
@@ -34,11 +34,11 @@ const stateWithAccounts = {
|
||||
},
|
||||
}
|
||||
|
||||
describe('handleLxAppDeepLink', () => {
|
||||
describe('handleLuxAppDeepLink', () => {
|
||||
describe('Token deep links', () => {
|
||||
it('should handle token share with contract address', () => {
|
||||
const path = `tokens/ethereum/${SAMPLE_CONTRACT_ADDRESS}`
|
||||
const url = `https://app.lux.org/${path}`
|
||||
const url = `https://lux.exchange/${path}`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -46,7 +46,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[call(fromLuxWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[call(fromUniswapWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[call(buildCurrencyId, UniverseChainId.Mainnet, SAMPLE_CONTRACT_ADDRESS), SAMPLE_CURRENCY_ID],
|
||||
[
|
||||
call(navigate, MobileScreens.TokenDetails, {
|
||||
@@ -67,7 +67,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle token share with native currency', () => {
|
||||
const path = `tokens/ethereum/${BACKEND_NATIVE_CHAIN_ADDRESS_STRING}`
|
||||
const url = `https://app.lux.org/${path}`
|
||||
const url = `https://lux.exchange/${path}`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -75,7 +75,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[call(fromLuxWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[call(fromUniswapWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[call(buildNativeCurrencyId, UniverseChainId.Mainnet), SAMPLE_NATIVE_CURRENCY_ID],
|
||||
[
|
||||
call(navigate, MobileScreens.TokenDetails, {
|
||||
@@ -96,7 +96,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle widget token link', () => {
|
||||
const path = `tokens/ethereum/${SAMPLE_CONTRACT_ADDRESS}`
|
||||
const url = `https://app.lux.org/${path}`
|
||||
const url = `https://lux.exchange/${path}`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -104,7 +104,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
linkSource: LinkSource.Widget,
|
||||
})
|
||||
.provide([
|
||||
[call(fromLuxWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[call(fromUniswapWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[call(buildCurrencyId, UniverseChainId.Mainnet, SAMPLE_CONTRACT_ADDRESS), SAMPLE_CURRENCY_ID],
|
||||
[
|
||||
call(navigate, MobileScreens.TokenDetails, {
|
||||
@@ -125,7 +125,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should throw error for token link with invalid network', () => {
|
||||
const path = `tokens/invalid/${SAMPLE_CONTRACT_ADDRESS}`
|
||||
const url = `https://app.lux.org/${path}`
|
||||
const url = `https://lux.exchange/${path}`
|
||||
|
||||
return expect(
|
||||
expectSaga(handleLuxAppDeepLink, {
|
||||
@@ -140,7 +140,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
describe('Top Tokens deep links', () => {
|
||||
it('should handle explore top tokens with chain', () => {
|
||||
const path = 'explore/tokens/unichain'
|
||||
const url = `https://app.lux.org/${path}?metric=volume`
|
||||
const url = `https://lux.exchange/${path}?metric=volume`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -148,7 +148,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[call(fromLuxWebAppLink, 'unichain'), UniverseChainId.Unichain],
|
||||
[call(fromUniswapWebAppLink, 'unichain'), UniverseChainId.Unichain],
|
||||
[
|
||||
call(handleTopTokensDeepLink, {
|
||||
chainId: UniverseChainId.Unichain,
|
||||
@@ -162,7 +162,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle top tokens with chain', () => {
|
||||
const path = 'tokens/ethereum'
|
||||
const url = `https://app.lux.org/${path}?metric=volume`
|
||||
const url = `https://lux.exchange/${path}?metric=volume`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -170,7 +170,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[call(fromLuxWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[call(fromUniswapWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[
|
||||
call(handleTopTokensDeepLink, {
|
||||
chainId: UniverseChainId.Mainnet,
|
||||
@@ -184,7 +184,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle explore top tokens without chain', () => {
|
||||
const path = 'explore/tokens'
|
||||
const url = `https://app.lux.org/${path}?metric=volume`
|
||||
const url = `https://lux.exchange/${path}?metric=volume`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -205,7 +205,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle top tokens without chain', () => {
|
||||
const path = 'tokens'
|
||||
const url = `https://app.lux.org/${path}`
|
||||
const url = `https://lux.exchange/${path}`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -229,7 +229,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
it('should handle external address share', () => {
|
||||
const externalAddress = '0x1234567890abcdef1234567890abcdef12345679'
|
||||
const path = `portfolio/${externalAddress}`
|
||||
const url = `https://app.lx.org/${path}`
|
||||
const url = `https://lux.exchange/${path}`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -257,7 +257,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle internal address share by switching to that account', () => {
|
||||
const path = `portfolio/${SAMPLE_CONTRACT_ADDRESS_2}`
|
||||
const url = `https://app.lx.org/${path}`
|
||||
const url = `https://lux.exchange/${path}`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -280,7 +280,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should not handle active account address', () => {
|
||||
const path = `portfolio/${account.address}`
|
||||
const url = `https://app.lx.org/${path}`
|
||||
const url = `https://lux.exchange/${path}`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -305,9 +305,9 @@ describe('handleLxAppDeepLink', () => {
|
||||
it('should handle portfolio share', () => {
|
||||
const externalAddress = '0x1234567890abcdef1234567890abcdef12345679'
|
||||
const path = `portfolio/${externalAddress}`
|
||||
const url = `https://app.lx.org/${path}`
|
||||
const url = `https://lux.exchange/${path}`
|
||||
|
||||
return expectSaga(handleLxAppDeepLink, {
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
@@ -335,7 +335,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
describe('Edge cases and invalid paths', () => {
|
||||
it('should not handle unrecognized paths', () => {
|
||||
const path = 'unknown/path'
|
||||
const url = `https://app.lux.org/${path}`
|
||||
const url = `https://lux.exchange/${path}`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -349,7 +349,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should not handle address with invalid format', () => {
|
||||
const path = 'portfolio/invalid-address'
|
||||
const url = `https://app.lx.org/${path}`
|
||||
const url = `https://lux.exchange/${path}`
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -364,7 +364,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
describe('Buy deep links', () => {
|
||||
it('should handle buy link with value and currencyCode', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.lux.org/buy?value=3¤cyCode=ETH'
|
||||
const url = 'https://lux.exchange/buy?value=3¤cyCode=ETH'
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -392,7 +392,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle buy link with only value', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.lux.org/buy?value=100'
|
||||
const url = 'https://lux.exchange/buy?value=100'
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -420,7 +420,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle buy link with only currencyCode', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.lux.org/buy?currencyCode=USDC'
|
||||
const url = 'https://lux.exchange/buy?currencyCode=USDC'
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -448,7 +448,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle buy link with no parameters', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.lux.org/buy'
|
||||
const url = 'https://lux.exchange/buy'
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -476,7 +476,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle buy link with isTokenInputMode=true', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.lux.org/buy?value=50¤cyCode=BTC&isTokenInputMode=true'
|
||||
const url = 'https://lux.exchange/buy?value=50¤cyCode=BTC&isTokenInputMode=true'
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -504,7 +504,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle buy link with isTokenInputMode=false', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.lux.org/buy?value=25¤cyCode=USDT&isTokenInputMode=false'
|
||||
const url = 'https://lux.exchange/buy?value=25¤cyCode=USDT&isTokenInputMode=false'
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -532,7 +532,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle buy link with only isTokenInputMode parameter', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.lux.org/buy?isTokenInputMode=true'
|
||||
const url = 'https://lux.exchange/buy?isTokenInputMode=true'
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -560,7 +560,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle buy link with single provider', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.lux.org/buy?providers=moonpay'
|
||||
const url = 'https://lux.exchange/buy?providers=moonpay'
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -588,7 +588,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle buy link with multiple providers', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.lux.org/buy?providers=moonpay,coinbasepay,stripe'
|
||||
const url = 'https://lux.exchange/buy?providers=moonpay,coinbasepay,stripe'
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -616,7 +616,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle buy link with providers in mixed case (converted to uppercase)', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.lux.org/buy?providers=MoonPay,coinbasepay,STRIPE'
|
||||
const url = 'https://lux.exchange/buy?providers=MoonPay,coinbasepay,STRIPE'
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -645,7 +645,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
it('should handle buy link with providers and other parameters', () => {
|
||||
const path = 'buy'
|
||||
const url =
|
||||
'https://app.lux.org/buy?value=100¤cyCode=ETH&isTokenInputMode=true&providers=moonpay,coinbasepay'
|
||||
'https://lux.exchange/buy?value=100¤cyCode=ETH&isTokenInputMode=true&providers=moonpay,coinbasepay'
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
@@ -673,7 +673,7 @@ describe('handleLxAppDeepLink', () => {
|
||||
|
||||
it('should handle buy link with empty providers parameter', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.lux.org/buy?providers='
|
||||
const url = 'https://lux.exchange/buy?providers='
|
||||
|
||||
return expectSaga(handleLuxAppDeepLink, {
|
||||
path,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { LinkSource } from 'src/features/deepLinking/types'
|
||||
import { dismissAllModalsBeforeNavigation } from 'src/features/deepLinking/utils'
|
||||
import { openModal } from 'src/features/modals/modalSlice'
|
||||
import { call, put, select } from 'typed-redux-saga'
|
||||
import { fromLuxWebAppLink } from '@l.x/lx/src/features/chains/utils'
|
||||
import { fromUniswapWebAppLink } from '@l.x/lx/src/features/chains/utils'
|
||||
import { BACKEND_NATIVE_CHAIN_ADDRESS_STRING } from '@l.x/lx/src/features/search/utils'
|
||||
import { MobileEventName, ModalName } from '@l.x/lx/src/features/telemetry/constants'
|
||||
import { sendAnalyticsEvent } from '@l.x/lx/src/features/telemetry/send'
|
||||
@@ -15,8 +15,8 @@ import { MobileScreens } from '@l.x/lx/src/types/screens/mobile'
|
||||
import { ShareableEntity } from '@l.x/lx/src/types/sharing'
|
||||
import { WidgetType } from '@l.x/lx/src/types/widgets'
|
||||
import { buildCurrencyId, buildNativeCurrencyId } from '@l.x/lx/src/utils/currencyId'
|
||||
import { selectAccounts, selectActiveAccountAddress } from '@luxfi/wallet/src/features/wallet/selectors'
|
||||
import { setAccountAsActive } from '@luxfi/wallet/src/features/wallet/slice'
|
||||
import { selectAccounts, selectActiveAccountAddress } from 'wallet/src/features/wallet/selectors'
|
||||
import { setAccountAsActive } from 'wallet/src/features/wallet/slice'
|
||||
|
||||
const TOKEN_SHARE_LINK_HASH_REGEX = RegExp(
|
||||
`^(#/)?(?:explore/)?tokens/([\\w\\d]*)/(0x[a-fA-F0-9]{40}|${BACKEND_NATIVE_CHAIN_ADDRESS_STRING})$`,
|
||||
@@ -36,38 +36,38 @@ export function* handleLuxAppDeepLink({
|
||||
url: string
|
||||
linkSource: LinkSource
|
||||
}): Generator {
|
||||
// Handle Buy links (ex. https://app.lux.org/buy?value=3¤cyCode=ETH)
|
||||
// Handle Buy links (ex. https://lux.exchange/buy?value=3¤cyCode=ETH)
|
||||
if (BUY_LINK_HASH_REGEX.test(path)) {
|
||||
const urlObj = new URL(url)
|
||||
yield* call(handleBuyLink, urlObj)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Swap links (ex. https://app.lux.org/swap?inputCurrency=ETH&outputCurrency=0x...)
|
||||
// Handle Swap links (ex. https://lux.exchange/swap?inputCurrency=ETH&outputCurrency=0x...)
|
||||
if (SWAP_LINK_HASH_REGEX.test(path)) {
|
||||
const urlObj = new URL(url)
|
||||
yield* call(handleSwapLink, urlObj, parseSwapLinkWebFormatOrThrow)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Token share (ex. https://app.lx.org/tokens/ethereum/0x... or https://app.lx.org/explore/tokens/arbitrum/0x...)
|
||||
// Handle Token share (ex. https://lux.exchange/tokens/ethereum/0x... or https://lux.exchange/explore/tokens/arbitrum/0x...)
|
||||
if (TOKEN_SHARE_LINK_HASH_REGEX.test(path)) {
|
||||
yield* call(handleTokenShare, { path, url, linkSource })
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Top Tokens page with or without explore and chain path:
|
||||
// ex. https://app.lux.org/tokens/unichain?metric=volume or https://app.lux.org/explore/tokens/base?metric=market_cap
|
||||
// or https://app.lux.org/tokens?metric=volume or https://app.lux.org/explore/tokens?metric=market_cap
|
||||
// ex. https://lux.exchange/tokens/unichain?metric=volume or https://lux.exchange/explore/tokens/base?metric=market_cap
|
||||
// or https://lux.exchange/tokens?metric=volume or https://lux.exchange/explore/tokens?metric=market_cap
|
||||
if (TOP_TOKENS_LINK_CHAIN_REGEX.test(path) || TOP_TOKENS_LINK_REGEX.test(path)) {
|
||||
const [, network] = path.match(TOP_TOKENS_LINK_CHAIN_REGEX) || []
|
||||
const chainId = network ? fromLuxWebAppLink(network) : undefined
|
||||
const chainId = network ? fromUniswapWebAppLink(network) : undefined
|
||||
|
||||
yield* call(handleTopTokensDeepLink, { chainId, url })
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Address share (ex. https://app.lux.org/address/0x...)
|
||||
// Handle Address share (ex. https://lux.exchange/address/0x...)
|
||||
if (ADDRESS_SHARE_LINK_HASH_REGEX.test(path)) {
|
||||
yield* call(handleAddressShare, { path, url })
|
||||
return
|
||||
@@ -84,7 +84,7 @@ function* handleTokenShare({
|
||||
linkSource: LinkSource
|
||||
}): Generator {
|
||||
const [, , network, contractAddress] = path.match(TOKEN_SHARE_LINK_HASH_REGEX) || []
|
||||
const chainId = network && fromLuxWebAppLink(network)
|
||||
const chainId = network && fromUniswapWebAppLink(network)
|
||||
|
||||
if (!chainId || !contractAddress) {
|
||||
return
|
||||
|
||||
@@ -45,7 +45,7 @@ function formSwapUrl({
|
||||
amount?: string
|
||||
}): URL {
|
||||
return new URL(
|
||||
`https://uniswap.org/app?screen=swap
|
||||
`https://lux.network/app?screen=swap
|
||||
&userAddress=${userAddress}
|
||||
&inputCurrencyId=${chain}-${inputAddress}
|
||||
&outputCurrencyId=${chain}-${outputAddress}
|
||||
|
||||
@@ -30,7 +30,7 @@ jest.mock('src/features/deepLinking/utils', () => ({
|
||||
}))
|
||||
|
||||
describe('handleTopTokensDeepLink', () => {
|
||||
const unichainExploreUrl = 'https://app.uniswap.org/explore/tokens/unichain'
|
||||
const unichainExploreUrl = 'https://lux.exchange/explore/tokens/unichain'
|
||||
const unichainChainId = UniverseChainId.Unichain
|
||||
|
||||
const mockedExploreNavigationRef = exploreNavigationRef as jest.Mocked<typeof exploreNavigationRef>
|
||||
@@ -57,7 +57,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should handle valid metric parameter in URL', () => {
|
||||
const urlWithMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric=volume'
|
||||
const urlWithMetric = 'https://lux.exchange/explore/tokens/unichain?metric=volume'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -72,7 +72,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should navigate to explore modal without chainId', () => {
|
||||
const urlWithoutChainId = 'https://app.uniswap.org/explore/tokens'
|
||||
const urlWithoutChainId = 'https://lux.exchange/explore/tokens'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: undefined, url: urlWithoutChainId })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -87,7 +87,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should handle metric parameter in URL without chainId', () => {
|
||||
const urlWithMetricNoChainId = 'https://app.uniswap.org/explore/tokens?metric=market_cap'
|
||||
const urlWithMetricNoChainId = 'https://lux.exchange/explore/tokens?metric=market_cap'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: undefined, url: urlWithMetricNoChainId })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -110,7 +110,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
navigate: mockNavigate,
|
||||
} as unknown as NavigationContainerRef<ExploreStackParamList>
|
||||
|
||||
const urlWithMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric=volume'
|
||||
const urlWithMetric = 'https://lux.exchange/explore/tokens/unichain?metric=volume'
|
||||
|
||||
await expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithMetric })
|
||||
.not.call(dismissAllModalsBeforeNavigation)
|
||||
@@ -171,7 +171,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
|
||||
describe('metric validation', () => {
|
||||
it('should handle invalid metric values and set orderByMetric to undefined', () => {
|
||||
const urlWithInvalidMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric=invalid_metric'
|
||||
const urlWithInvalidMetric = 'https://lux.exchange/explore/tokens/unichain?metric=invalid_metric'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithInvalidMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -186,7 +186,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should handle empty metric parameter and set orderByMetric to undefined', () => {
|
||||
const urlWithEmptyMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric='
|
||||
const urlWithEmptyMetric = 'https://lux.exchange/explore/tokens/unichain?metric='
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithEmptyMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -203,16 +203,16 @@ describe('handleTopTokensDeepLink', () => {
|
||||
it('should handle case-insensitive valid metrics', () => {
|
||||
const testCases = [
|
||||
{
|
||||
url: 'https://app.uniswap.org/explore/tokens/unichain?metric=total_value_locked',
|
||||
url: 'https://lux.exchange/explore/tokens/unichain?metric=total_value_locked',
|
||||
expected: 'TOTAL_VALUE_LOCKED',
|
||||
},
|
||||
{ url: 'https://app.uniswap.org/explore/tokens/unichain?metric=market_cap', expected: 'MARKET_CAP' },
|
||||
{ url: 'https://lux.exchange/explore/tokens/unichain?metric=market_cap', expected: 'MARKET_CAP' },
|
||||
{
|
||||
url: 'https://app.uniswap.org/explore/tokens/unichain?metric=price_percent_change_1_day_asc',
|
||||
url: 'https://lux.exchange/explore/tokens/unichain?metric=price_percent_change_1_day_asc',
|
||||
expected: 'PRICE_PERCENT_CHANGE_1_DAY_ASC',
|
||||
},
|
||||
{
|
||||
url: 'https://app.uniswap.org/explore/tokens/unichain?metric=price_percent_change_1_day_desc',
|
||||
url: 'https://lux.exchange/explore/tokens/unichain?metric=price_percent_change_1_day_desc',
|
||||
expected: 'PRICE_PERCENT_CHANGE_1_DAY_DESC',
|
||||
},
|
||||
]
|
||||
@@ -235,7 +235,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should reject TRENDING metric (excluded CustomRankingType)', () => {
|
||||
const urlWithTrendingMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric=trending'
|
||||
const urlWithTrendingMetric = 'https://lux.exchange/explore/tokens/unichain?metric=trending'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithTrendingMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -250,7 +250,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should handle mixed case metrics correctly', () => {
|
||||
const urlWithMixedCaseMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric=VoLuMe'
|
||||
const urlWithMixedCaseMetric = 'https://lux.exchange/explore/tokens/unichain?metric=VoLuMe'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithMixedCaseMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -265,7 +265,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should handle numeric metric values as invalid', () => {
|
||||
const urlWithNumericMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric=123'
|
||||
const urlWithNumericMetric = 'https://lux.exchange/explore/tokens/unichain?metric=123'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithNumericMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -280,7 +280,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should handle special characters in metric as invalid', () => {
|
||||
const urlWithSpecialCharsMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric=volume@#$'
|
||||
const urlWithSpecialCharsMetric = 'https://lux.exchange/explore/tokens/unichain?metric=volume@#$'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithSpecialCharsMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -296,13 +296,13 @@ describe('handleTopTokensDeepLink', () => {
|
||||
|
||||
it('should handle various invalid metric formats', () => {
|
||||
const testCases = [
|
||||
{ url: 'https://app.uniswap.org/explore/tokens/unichain?metric=true', desc: 'boolean-like' },
|
||||
{ url: 'https://app.uniswap.org/explore/tokens/unichain?metric=volume,market_cap', desc: 'array-like' },
|
||||
{ url: 'https://lux.exchange/explore/tokens/unichain?metric=true', desc: 'boolean-like' },
|
||||
{ url: 'https://lux.exchange/explore/tokens/unichain?metric=volume,market_cap', desc: 'array-like' },
|
||||
{
|
||||
url: "https://app.uniswap.org/explore/tokens/unichain?metric=volume'; DROP TABLE--",
|
||||
url: "https://lux.exchange/explore/tokens/unichain?metric=volume'; DROP TABLE--",
|
||||
desc: 'SQL injection-like',
|
||||
},
|
||||
{ url: `https://app.uniswap.org/explore/tokens/unichain?metric=${'a'.repeat(100)}`, desc: 'very long' },
|
||||
{ url: `https://lux.exchange/explore/tokens/unichain?metric=${'a'.repeat(100)}`, desc: 'very long' },
|
||||
]
|
||||
|
||||
return Promise.all(
|
||||
@@ -323,7 +323,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should handle URL encoded metric values correctly', () => {
|
||||
const urlWithEncodedMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric=MARKET%5FCAP'
|
||||
const urlWithEncodedMetric = 'https://lux.exchange/explore/tokens/unichain?metric=MARKET%5FCAP'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithEncodedMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -341,9 +341,9 @@ describe('handleTopTokensDeepLink', () => {
|
||||
describe('getValidRankingType function edge cases', () => {
|
||||
it('should return undefined for falsy metrics (null, empty, whitespace)', () => {
|
||||
const testCases = [
|
||||
'https://app.uniswap.org/explore/tokens/unichain?other=value', // null metric
|
||||
'https://app.uniswap.org/explore/tokens/unichain?metric=', // empty
|
||||
'https://app.uniswap.org/explore/tokens/unichain?metric=%20%20%20', // whitespace
|
||||
'https://lux.exchange/explore/tokens/unichain?other=value', // null metric
|
||||
'https://lux.exchange/explore/tokens/unichain?metric=', // empty
|
||||
'https://lux.exchange/explore/tokens/unichain?metric=%20%20%20', // whitespace
|
||||
]
|
||||
|
||||
return Promise.all(
|
||||
@@ -364,7 +364,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should convert valid lowercase metric to uppercase', () => {
|
||||
const urlWithLowercaseMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric=volume'
|
||||
const urlWithLowercaseMetric = 'https://lux.exchange/explore/tokens/unichain?metric=volume'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithLowercaseMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -379,7 +379,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should handle mixed case metric conversion', () => {
|
||||
const urlWithMixedCaseMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric=mArKeT_cAp'
|
||||
const urlWithMixedCaseMetric = 'https://lux.exchange/explore/tokens/unichain?metric=mArKeT_cAp'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithMixedCaseMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -394,7 +394,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should return undefined for unsupported metric values', () => {
|
||||
const urlWithUnsupportedMetric = 'https://app.uniswap.org/explore/tokens/unichain?metric=unsupported_metric'
|
||||
const urlWithUnsupportedMetric = 'https://lux.exchange/explore/tokens/unichain?metric=unsupported_metric'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithUnsupportedMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -410,8 +410,8 @@ describe('handleTopTokensDeepLink', () => {
|
||||
|
||||
it('should reject TRENDING metric in any case format', () => {
|
||||
const testCases = [
|
||||
'https://app.uniswap.org/explore/tokens/unichain?metric=trending',
|
||||
'https://app.uniswap.org/explore/tokens/unichain?metric=TrEnDiNg',
|
||||
'https://lux.exchange/explore/tokens/unichain?metric=trending',
|
||||
'https://lux.exchange/explore/tokens/unichain?metric=TrEnDiNg',
|
||||
]
|
||||
|
||||
return Promise.all(
|
||||
@@ -436,7 +436,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
|
||||
return Promise.all(
|
||||
validMetrics.map((metric) => {
|
||||
const urlWithMetric = `https://app.uniswap.org/explore/tokens/unichain?metric=${metric}`
|
||||
const urlWithMetric = `https://lux.exchange/explore/tokens/unichain?metric=${metric}`
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -457,7 +457,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
|
||||
return Promise.all(
|
||||
validCustomMetrics.map((metric) => {
|
||||
const urlWithMetric = `https://app.uniswap.org/explore/tokens/unichain?metric=${metric}`
|
||||
const urlWithMetric = `https://lux.exchange/explore/tokens/unichain?metric=${metric}`
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithMetric })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -476,7 +476,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
|
||||
describe('URL edge cases', () => {
|
||||
it('should handle URL without search params', () => {
|
||||
const basicUrl = 'https://app.uniswap.org/explore/tokens/unichain'
|
||||
const basicUrl = 'https://lux.exchange/explore/tokens/unichain'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: basicUrl })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -491,7 +491,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should handle URL with multiple query parameters', () => {
|
||||
const urlWithMultipleParams = 'https://app.uniswap.org/explore/tokens/unichain?metric=volume&other=value&foo=bar'
|
||||
const urlWithMultipleParams = 'https://lux.exchange/explore/tokens/unichain?metric=volume&other=value&foo=bar'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithMultipleParams })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
@@ -506,7 +506,7 @@ describe('handleTopTokensDeepLink', () => {
|
||||
})
|
||||
|
||||
it('should handle URL with fragment identifier', () => {
|
||||
const urlWithFragment = 'https://app.uniswap.org/explore/tokens/unichain?metric=market_cap#section'
|
||||
const urlWithFragment = 'https://lux.exchange/explore/tokens/unichain?metric=market_cap#section'
|
||||
return expectSaga(handleTopTokensDeepLink, { chainId: unichainChainId, url: urlWithFragment })
|
||||
.call(dismissAllModalsBeforeNavigation)
|
||||
.call(navigate, ModalName.Explore, {
|
||||
|
||||
@@ -1,702 +0,0 @@
|
||||
import { expectSaga } from 'redux-saga-test-plan'
|
||||
import { call, put } from 'redux-saga/effects'
|
||||
import { navigate } from 'src/app/navigation/rootNavigation'
|
||||
import { handleTopTokensDeepLink } from 'src/features/deepLinking/handleTopTokensDeepLink'
|
||||
import { handleUniswapAppDeepLink } from 'src/features/deepLinking/handleUniswapAppDeepLink'
|
||||
import { LinkSource } from 'src/features/deepLinking/types'
|
||||
import { openModal } from 'src/features/modals/modalSlice'
|
||||
import { UniverseChainId } from '@l.x/lx/src/features/chains/types'
|
||||
import { fromUniswapWebAppLink } from '@l.x/lx/src/features/chains/utils'
|
||||
import { BACKEND_NATIVE_CHAIN_ADDRESS_STRING } from '@l.x/lx/src/features/search/utils'
|
||||
import { MobileEventName, ModalName } from '@l.x/lx/src/features/telemetry/constants'
|
||||
import { sendAnalyticsEvent } from '@l.x/lx/src/features/telemetry/send'
|
||||
import { MobileScreens } from '@l.x/lx/src/types/screens/mobile'
|
||||
import { ShareableEntity } from '@l.x/lx/src/types/sharing'
|
||||
import { WidgetType } from '@l.x/lx/src/types/widgets'
|
||||
import { buildCurrencyId, buildNativeCurrencyId } from '@l.x/lx/src/utils/currencyId'
|
||||
import { setAccountAsActive } from 'wallet/src/features/wallet/slice'
|
||||
import { signerMnemonicAccount } from 'wallet/src/test/fixtures'
|
||||
|
||||
const account = signerMnemonicAccount()
|
||||
const SAMPLE_CONTRACT_ADDRESS = '0x1234567890abcdef1234567890abcdef12345678'
|
||||
const SAMPLE_CONTRACT_ADDRESS_2 = '0xabcdef1234567890abcdef1234567890abcdef12'
|
||||
const SAMPLE_TOKEN_ID = '123'
|
||||
const SAMPLE_CURRENCY_ID = '1-0x1234567890abcdef1234567890abcdef12345678'
|
||||
const SAMPLE_NATIVE_CURRENCY_ID = '1-ETH'
|
||||
|
||||
const stateWithAccounts = {
|
||||
wallet: {
|
||||
accounts: {
|
||||
[account.address]: account,
|
||||
[SAMPLE_CONTRACT_ADDRESS_2]: account,
|
||||
},
|
||||
activeAccountAddress: account.address,
|
||||
},
|
||||
}
|
||||
|
||||
describe('handleUniswapAppDeepLink', () => {
|
||||
describe('Token deep links', () => {
|
||||
it('should handle token share with contract address', () => {
|
||||
const path = `tokens/ethereum/${SAMPLE_CONTRACT_ADDRESS}`
|
||||
const url = `https://app.uniswap.org/${path}`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[call(fromUniswapWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[call(buildCurrencyId, UniverseChainId.Mainnet, SAMPLE_CONTRACT_ADDRESS), SAMPLE_CURRENCY_ID],
|
||||
[
|
||||
call(navigate, MobileScreens.TokenDetails, {
|
||||
currencyId: SAMPLE_CURRENCY_ID,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
[
|
||||
call(sendAnalyticsEvent, MobileEventName.ShareLinkOpened, {
|
||||
entity: ShareableEntity.Token,
|
||||
url,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle token share with native currency', () => {
|
||||
const path = `tokens/ethereum/${BACKEND_NATIVE_CHAIN_ADDRESS_STRING}`
|
||||
const url = `https://app.uniswap.org/${path}`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[call(fromUniswapWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[call(buildNativeCurrencyId, UniverseChainId.Mainnet), SAMPLE_NATIVE_CURRENCY_ID],
|
||||
[
|
||||
call(navigate, MobileScreens.TokenDetails, {
|
||||
currencyId: SAMPLE_NATIVE_CURRENCY_ID,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
[
|
||||
call(sendAnalyticsEvent, MobileEventName.ShareLinkOpened, {
|
||||
entity: ShareableEntity.Token,
|
||||
url,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle widget token link', () => {
|
||||
const path = `tokens/ethereum/${SAMPLE_CONTRACT_ADDRESS}`
|
||||
const url = `https://app.uniswap.org/${path}`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Widget,
|
||||
})
|
||||
.provide([
|
||||
[call(fromUniswapWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[call(buildCurrencyId, UniverseChainId.Mainnet, SAMPLE_CONTRACT_ADDRESS), SAMPLE_CURRENCY_ID],
|
||||
[
|
||||
call(navigate, MobileScreens.TokenDetails, {
|
||||
currencyId: SAMPLE_CURRENCY_ID,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
[
|
||||
call(sendAnalyticsEvent, MobileEventName.WidgetClicked, {
|
||||
widget_type: WidgetType.TokenPrice,
|
||||
url,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should throw error for token link with invalid network', () => {
|
||||
const path = `tokens/invalid/${SAMPLE_CONTRACT_ADDRESS}`
|
||||
const url = `https://app.uniswap.org/${path}`
|
||||
|
||||
return expect(
|
||||
expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
}).run(),
|
||||
).rejects.toThrow('Network "invalid" can not be mapped')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Top Tokens deep links', () => {
|
||||
it('should handle explore top tokens with chain', () => {
|
||||
const path = 'explore/tokens/unichain'
|
||||
const url = `https://app.uniswap.org/${path}?metric=volume`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[call(fromUniswapWebAppLink, 'unichain'), UniverseChainId.Unichain],
|
||||
[
|
||||
call(handleTopTokensDeepLink, {
|
||||
chainId: UniverseChainId.Unichain,
|
||||
url,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle top tokens with chain', () => {
|
||||
const path = 'tokens/ethereum'
|
||||
const url = `https://app.uniswap.org/${path}?metric=volume`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[call(fromUniswapWebAppLink, 'ethereum'), UniverseChainId.Mainnet],
|
||||
[
|
||||
call(handleTopTokensDeepLink, {
|
||||
chainId: UniverseChainId.Mainnet,
|
||||
url,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle explore top tokens without chain', () => {
|
||||
const path = 'explore/tokens'
|
||||
const url = `https://app.uniswap.org/${path}?metric=volume`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
call(handleTopTokensDeepLink, {
|
||||
chainId: undefined,
|
||||
url,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle top tokens without chain', () => {
|
||||
const path = 'tokens'
|
||||
const url = `https://app.uniswap.org/${path}`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
call(handleTopTokensDeepLink, {
|
||||
chainId: undefined,
|
||||
url,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Address deep links', () => {
|
||||
it('should handle external address share', () => {
|
||||
const externalAddress = '0x1234567890abcdef1234567890abcdef12345679'
|
||||
const path = `portfolio/${externalAddress}`
|
||||
const url = `https://app.uniswap.org/${path}`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.withState(stateWithAccounts)
|
||||
.provide([
|
||||
[
|
||||
call(navigate, MobileScreens.ExternalProfile, {
|
||||
address: externalAddress,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
[
|
||||
call(sendAnalyticsEvent, MobileEventName.ShareLinkOpened, {
|
||||
entity: ShareableEntity.Wallet,
|
||||
url,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle internal address share by switching to that account', () => {
|
||||
const path = `portfolio/${SAMPLE_CONTRACT_ADDRESS_2}`
|
||||
const url = `https://app.uniswap.org/${path}`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.withState(stateWithAccounts)
|
||||
.provide([
|
||||
[
|
||||
call(sendAnalyticsEvent, MobileEventName.ShareLinkOpened, {
|
||||
entity: ShareableEntity.Wallet,
|
||||
url,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.put(setAccountAsActive(SAMPLE_CONTRACT_ADDRESS_2))
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should not handle active account address', () => {
|
||||
const path = `portfolio/${account.address}`
|
||||
const url = `https://app.uniswap.org/${path}`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.withState(stateWithAccounts)
|
||||
.provide([
|
||||
[
|
||||
call(sendAnalyticsEvent, MobileEventName.ShareLinkOpened, {
|
||||
entity: ShareableEntity.Wallet,
|
||||
url,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.not.call.fn(navigate)
|
||||
.not.put.actionType(setAccountAsActive.type)
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle portfolio share', () => {
|
||||
const externalAddress = '0x1234567890abcdef1234567890abcdef12345679'
|
||||
const path = `portfolio/${externalAddress}`
|
||||
const url = `https://app.uniswap.org/${path}`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.withState(stateWithAccounts)
|
||||
.provide([
|
||||
[
|
||||
call(navigate, MobileScreens.ExternalProfile, {
|
||||
address: externalAddress,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
[
|
||||
call(sendAnalyticsEvent, MobileEventName.ShareLinkOpened, {
|
||||
entity: ShareableEntity.Wallet,
|
||||
url,
|
||||
}),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Edge cases and invalid paths', () => {
|
||||
it('should not handle unrecognized paths', () => {
|
||||
const path = 'unknown/path'
|
||||
const url = `https://app.uniswap.org/${path}`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.not.call.fn(navigate)
|
||||
.not.call.fn(handleTopTokensDeepLink)
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should not handle address with invalid format', () => {
|
||||
const path = 'portfolio/invalid-address'
|
||||
const url = `https://app.uniswap.org/${path}`
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.not.call.fn(navigate)
|
||||
.run()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Buy deep links', () => {
|
||||
it('should handle buy link with value and currencyCode', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.uniswap.org/buy?value=3¤cyCode=ETH'
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: '3',
|
||||
currencyCode: 'ETH',
|
||||
prefilledIsTokenInputMode: false,
|
||||
providers: undefined,
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle buy link with only value', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.uniswap.org/buy?value=100'
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: '100',
|
||||
currencyCode: undefined,
|
||||
prefilledIsTokenInputMode: false,
|
||||
providers: undefined,
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle buy link with only currencyCode', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.uniswap.org/buy?currencyCode=USDC'
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: undefined,
|
||||
currencyCode: 'USDC',
|
||||
prefilledIsTokenInputMode: false,
|
||||
providers: undefined,
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle buy link with no parameters', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.uniswap.org/buy'
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: undefined,
|
||||
currencyCode: undefined,
|
||||
prefilledIsTokenInputMode: false,
|
||||
providers: undefined,
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle buy link with isTokenInputMode=true', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.uniswap.org/buy?value=50¤cyCode=BTC&isTokenInputMode=true'
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: '50',
|
||||
currencyCode: 'BTC',
|
||||
prefilledIsTokenInputMode: true,
|
||||
providers: undefined,
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle buy link with isTokenInputMode=false', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.uniswap.org/buy?value=25¤cyCode=USDT&isTokenInputMode=false'
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: '25',
|
||||
currencyCode: 'USDT',
|
||||
prefilledIsTokenInputMode: false,
|
||||
providers: undefined,
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle buy link with only isTokenInputMode parameter', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.uniswap.org/buy?isTokenInputMode=true'
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: undefined,
|
||||
currencyCode: undefined,
|
||||
prefilledIsTokenInputMode: true,
|
||||
providers: undefined,
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle buy link with single provider', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.uniswap.org/buy?providers=moonpay'
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: undefined,
|
||||
currencyCode: undefined,
|
||||
prefilledIsTokenInputMode: false,
|
||||
providers: ['MOONPAY'],
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle buy link with multiple providers', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.uniswap.org/buy?providers=moonpay,coinbasepay,stripe'
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: undefined,
|
||||
currencyCode: undefined,
|
||||
prefilledIsTokenInputMode: false,
|
||||
providers: ['MOONPAY', 'COINBASEPAY', 'STRIPE'],
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle buy link with providers in mixed case (converted to uppercase)', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.uniswap.org/buy?providers=MoonPay,coinbasepay,STRIPE'
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: undefined,
|
||||
currencyCode: undefined,
|
||||
prefilledIsTokenInputMode: false,
|
||||
providers: ['MOONPAY', 'COINBASEPAY', 'STRIPE'],
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle buy link with providers and other parameters', () => {
|
||||
const path = 'buy'
|
||||
const url =
|
||||
'https://app.uniswap.org/buy?value=100¤cyCode=ETH&isTokenInputMode=true&providers=moonpay,coinbasepay'
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: '100',
|
||||
currencyCode: 'ETH',
|
||||
prefilledIsTokenInputMode: true,
|
||||
providers: ['MOONPAY', 'COINBASEPAY'],
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
|
||||
it('should handle buy link with empty providers parameter', () => {
|
||||
const path = 'buy'
|
||||
const url = 'https://app.uniswap.org/buy?providers='
|
||||
|
||||
return expectSaga(handleUniswapAppDeepLink, {
|
||||
path,
|
||||
url,
|
||||
linkSource: LinkSource.Share,
|
||||
})
|
||||
.provide([
|
||||
[
|
||||
put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: undefined,
|
||||
currencyCode: undefined,
|
||||
prefilledIsTokenInputMode: false,
|
||||
providers: [],
|
||||
},
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
])
|
||||
.run()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,171 +0,0 @@
|
||||
import { fiatOnRampNavigationRef } from 'src/app/navigation/navigationRef'
|
||||
import { navigate } from 'src/app/navigation/rootNavigation'
|
||||
import { handleSwapLink } from 'src/features/deepLinking/handleSwapLinkSaga'
|
||||
import { handleTopTokensDeepLink } from 'src/features/deepLinking/handleTopTokensDeepLink'
|
||||
import { parseSwapLinkWebFormatOrThrow } from 'src/features/deepLinking/parseSwapLink'
|
||||
import { LinkSource } from 'src/features/deepLinking/types'
|
||||
import { dismissAllModalsBeforeNavigation } from 'src/features/deepLinking/utils'
|
||||
import { openModal } from 'src/features/modals/modalSlice'
|
||||
import { call, put, select } from 'typed-redux-saga'
|
||||
import { fromUniswapWebAppLink } from '@l.x/lx/src/features/chains/utils'
|
||||
import { BACKEND_NATIVE_CHAIN_ADDRESS_STRING } from '@l.x/lx/src/features/search/utils'
|
||||
import { MobileEventName, ModalName } from '@l.x/lx/src/features/telemetry/constants'
|
||||
import { sendAnalyticsEvent } from '@l.x/lx/src/features/telemetry/send'
|
||||
import { MobileScreens } from '@l.x/lx/src/types/screens/mobile'
|
||||
import { ShareableEntity } from '@l.x/lx/src/types/sharing'
|
||||
import { WidgetType } from '@l.x/lx/src/types/widgets'
|
||||
import { buildCurrencyId, buildNativeCurrencyId } from '@l.x/lx/src/utils/currencyId'
|
||||
import { selectAccounts, selectActiveAccountAddress } from 'wallet/src/features/wallet/selectors'
|
||||
import { setAccountAsActive } from 'wallet/src/features/wallet/slice'
|
||||
|
||||
const TOKEN_SHARE_LINK_HASH_REGEX = RegExp(
|
||||
`^(#/)?(?:explore/)?tokens/([\\w\\d]*)/(0x[a-fA-F0-9]{40}|${BACKEND_NATIVE_CHAIN_ADDRESS_STRING})$`,
|
||||
)
|
||||
const TOP_TOKENS_LINK_CHAIN_REGEX = /^(?:explore\/)?tokens\/([\w\d]+)/
|
||||
const TOP_TOKENS_LINK_REGEX = /^(?:explore\/)?tokens/
|
||||
const ADDRESS_SHARE_LINK_HASH_REGEX = /^(#\/)?portfolio\/(0x[a-fA-F0-9]{40})$/
|
||||
const SWAP_LINK_HASH_REGEX = /^\/?swap(?:\?)?/
|
||||
const BUY_LINK_HASH_REGEX = /^\/?buy(?:\?)?/
|
||||
|
||||
export function* handleUniswapAppDeepLink({
|
||||
path,
|
||||
url,
|
||||
linkSource,
|
||||
}: {
|
||||
path: string
|
||||
url: string
|
||||
linkSource: LinkSource
|
||||
}): Generator {
|
||||
// Handle Buy links (ex. https://app.uniswap.org/buy?value=3¤cyCode=ETH)
|
||||
if (BUY_LINK_HASH_REGEX.test(path)) {
|
||||
const urlObj = new URL(url)
|
||||
yield* call(handleBuyLink, urlObj)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Swap links (ex. https://app.uniswap.org/swap?inputCurrency=ETH&outputCurrency=0x...)
|
||||
if (SWAP_LINK_HASH_REGEX.test(path)) {
|
||||
const urlObj = new URL(url)
|
||||
yield* call(handleSwapLink, urlObj, parseSwapLinkWebFormatOrThrow)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Token share (ex. https://app.uniswap.org/tokens/ethereum/0x... or https://app.uniswap.org/explore/tokens/arbitrum/0x...)
|
||||
if (TOKEN_SHARE_LINK_HASH_REGEX.test(path)) {
|
||||
yield* call(handleTokenShare, { path, url, linkSource })
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Top Tokens page with or without explore and chain path:
|
||||
// ex. https://app.uniswap.org/tokens/unichain?metric=volume or https://app.uniswap.org/explore/tokens/base?metric=market_cap
|
||||
// or https://app.uniswap.org/tokens?metric=volume or https://app.uniswap.org/explore/tokens?metric=market_cap
|
||||
if (TOP_TOKENS_LINK_CHAIN_REGEX.test(path) || TOP_TOKENS_LINK_REGEX.test(path)) {
|
||||
const [, network] = path.match(TOP_TOKENS_LINK_CHAIN_REGEX) || []
|
||||
const chainId = network ? fromUniswapWebAppLink(network) : undefined
|
||||
|
||||
yield* call(handleTopTokensDeepLink, { chainId, url })
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Address share (ex. https://app.uniswap.org/address/0x...)
|
||||
if (ADDRESS_SHARE_LINK_HASH_REGEX.test(path)) {
|
||||
yield* call(handleAddressShare, { path, url })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function* handleTokenShare({
|
||||
path,
|
||||
url,
|
||||
linkSource,
|
||||
}: {
|
||||
path: string
|
||||
url: string
|
||||
linkSource: LinkSource
|
||||
}): Generator {
|
||||
const [, , network, contractAddress] = path.match(TOKEN_SHARE_LINK_HASH_REGEX) || []
|
||||
const chainId = network && fromUniswapWebAppLink(network)
|
||||
|
||||
if (!chainId || !contractAddress) {
|
||||
return
|
||||
}
|
||||
|
||||
yield* call(dismissAllModalsBeforeNavigation)
|
||||
|
||||
const currencyId =
|
||||
contractAddress === BACKEND_NATIVE_CHAIN_ADDRESS_STRING
|
||||
? buildNativeCurrencyId(chainId)
|
||||
: buildCurrencyId(chainId, contractAddress)
|
||||
yield* call(navigate, MobileScreens.TokenDetails, {
|
||||
currencyId,
|
||||
})
|
||||
if (linkSource === LinkSource.Share) {
|
||||
yield* call(sendAnalyticsEvent, MobileEventName.ShareLinkOpened, {
|
||||
entity: ShareableEntity.Token,
|
||||
url,
|
||||
})
|
||||
} else {
|
||||
yield* call(sendAnalyticsEvent, MobileEventName.WidgetClicked, {
|
||||
widget_type: WidgetType.TokenPrice,
|
||||
url,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function* handleAddressShare({ path, url }: { path: string; url: string }): Generator {
|
||||
const [, , accountAddress] = path.match(ADDRESS_SHARE_LINK_HASH_REGEX) || []
|
||||
if (!accountAddress) {
|
||||
return
|
||||
}
|
||||
const accounts = yield* select(selectAccounts)
|
||||
const activeAccountAddress = yield* select(selectActiveAccountAddress)
|
||||
if (accountAddress === activeAccountAddress) {
|
||||
return
|
||||
}
|
||||
|
||||
const isInternal = Boolean(accounts[accountAddress])
|
||||
|
||||
yield* call(dismissAllModalsBeforeNavigation)
|
||||
|
||||
if (isInternal) {
|
||||
yield* put(setAccountAsActive(accountAddress))
|
||||
} else {
|
||||
yield* call(navigate, MobileScreens.ExternalProfile, {
|
||||
address: accountAddress,
|
||||
})
|
||||
}
|
||||
yield* call(sendAnalyticsEvent, MobileEventName.ShareLinkOpened, {
|
||||
entity: ShareableEntity.Wallet,
|
||||
url,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
function* handleBuyLink(urlObj: URL): Generator {
|
||||
const searchParams = urlObj.searchParams
|
||||
const value = searchParams.get('value')
|
||||
const currencyCode = searchParams.get('currencyCode')
|
||||
const isTokenInputMode = searchParams.get('isTokenInputMode')
|
||||
const providers = searchParams
|
||||
.get('providers')
|
||||
?.split(',')
|
||||
.map((provider) => provider.trim())
|
||||
.filter(Boolean)
|
||||
.map((provider) => provider.toUpperCase())
|
||||
|
||||
const ref = fiatOnRampNavigationRef.current
|
||||
if (!ref || !ref.isFocused()) {
|
||||
yield* call(dismissAllModalsBeforeNavigation)
|
||||
}
|
||||
yield* put(
|
||||
openModal({
|
||||
name: ModalName.FiatOnRampAggregator,
|
||||
initialState: {
|
||||
prefilledAmount: value || undefined,
|
||||
currencyCode: currencyCode || undefined,
|
||||
prefilledIsTokenInputMode: isTokenInputMode === 'true',
|
||||
providers,
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -12,7 +12,7 @@ describe('parseSwapLink', () => {
|
||||
it('should parse valid mobile format link', () => {
|
||||
// Using USDC address on mainnet
|
||||
const url = new URL(
|
||||
'https://uniswap.org/mobile-redirect?screen=swap&inputCurrencyId=1-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=1-0xdAC17F958D2ee523a2206206994597C13D831ec7¤cyField=input&amount=100',
|
||||
'https://lux.network/mobile-redirect?screen=swap&inputCurrencyId=1-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=1-0xdAC17F958D2ee523a2206206994597C13D831ec7¤cyField=input&amount=100',
|
||||
)
|
||||
|
||||
const result = parseSwapLinkMobileFormatOrThrow(url)
|
||||
@@ -24,7 +24,7 @@ describe('parseSwapLink', () => {
|
||||
|
||||
it('should handle missing inputCurrencyId', () => {
|
||||
const url = new URL(
|
||||
'https://uniswap.org/mobile-redirect?screen=swap&outputCurrencyId=1-0xdAC17F958D2ee523a2206206994597C13D831ec7¤cyField=input&amount=100',
|
||||
'https://lux.network/mobile-redirect?screen=swap&outputCurrencyId=1-0xdAC17F958D2ee523a2206206994597C13D831ec7¤cyField=input&amount=100',
|
||||
)
|
||||
|
||||
expect(() => parseSwapLinkMobileFormatOrThrow(url)).toThrow('Not mobile format - missing currencyId parameters')
|
||||
@@ -34,7 +34,7 @@ describe('parseSwapLink', () => {
|
||||
describe('Universal format', () => {
|
||||
it('should parse valid web format link', () => {
|
||||
const url = new URL(
|
||||
'https://app.uniswap.org/swap?inputCurrency=ETH&outputCurrency=0xdAC17F958D2ee523a2206206994597C13D831ec7&chain=ethereum&value=1.5&field=INPUT',
|
||||
'https://lux.exchange/swap?inputCurrency=ETH&outputCurrency=0xdAC17F958D2ee523a2206206994597C13D831ec7&chain=ethereum&value=1.5&field=INPUT',
|
||||
)
|
||||
|
||||
const result = parseSwapLinkWebFormatOrThrow(url)
|
||||
@@ -46,7 +46,7 @@ describe('parseSwapLink', () => {
|
||||
|
||||
it('should handle ETH as native currency', () => {
|
||||
const url = new URL(
|
||||
'https://app.uniswap.org/swap?inputCurrency=ETH&outputCurrency=0xdAC17F958D2ee523a2206206994597C13D831ec7&chain=ethereum',
|
||||
'https://lux.exchange/swap?inputCurrency=ETH&outputCurrency=0xdAC17F958D2ee523a2206206994597C13D831ec7&chain=ethereum',
|
||||
)
|
||||
|
||||
const result = parseSwapLinkWebFormatOrThrow(url)
|
||||
@@ -57,7 +57,7 @@ describe('parseSwapLink', () => {
|
||||
|
||||
it('should handle cross-chain swaps', () => {
|
||||
const url = new URL(
|
||||
'https://app.uniswap.org/swap?inputCurrency=ETH&chain=ethereum&outputCurrency=0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39&outputChain=polygon',
|
||||
'https://lux.exchange/swap?inputCurrency=ETH&chain=ethereum&outputCurrency=0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39&outputChain=polygon',
|
||||
)
|
||||
|
||||
const result = parseSwapLinkWebFormatOrThrow(url)
|
||||
@@ -67,13 +67,13 @@ describe('parseSwapLink', () => {
|
||||
})
|
||||
|
||||
it('should handle missing currencies', () => {
|
||||
const url = new URL('https://app.uniswap.org/swap?chain=ethereum')
|
||||
const url = new URL('https://lux.exchange/swap?chain=ethereum')
|
||||
|
||||
expect(() => parseSwapLinkWebFormatOrThrow(url)).toThrow('Not web format - missing currency parameters')
|
||||
})
|
||||
|
||||
it('should handle only input currency provided', () => {
|
||||
const url = new URL('https://app.uniswap.org/swap?inputCurrency=ETH&chain=ethereum')
|
||||
const url = new URL('https://lux.exchange/swap?inputCurrency=ETH&chain=ethereum')
|
||||
|
||||
const result = parseSwapLinkWebFormatOrThrow(url)
|
||||
|
||||
@@ -85,7 +85,7 @@ describe('parseSwapLink', () => {
|
||||
|
||||
it('should handle only output currency provided', () => {
|
||||
const url = new URL(
|
||||
'https://app.uniswap.org/swap?outputCurrency=0xdAC17F958D2ee523a2206206994597C13D831ec7&chain=ethereum',
|
||||
'https://lux.exchange/swap?outputCurrency=0xdAC17F958D2ee523a2206206994597C13D831ec7&chain=ethereum',
|
||||
)
|
||||
|
||||
const result = parseSwapLinkWebFormatOrThrow(url)
|
||||
@@ -98,7 +98,7 @@ describe('parseSwapLink', () => {
|
||||
|
||||
it('should handle invalid field values', () => {
|
||||
const url = new URL(
|
||||
'https://app.uniswap.org/swap?inputCurrency=ETH&outputCurrency=0xdAC17F958D2ee523a2206206994597C13D831ec7&field=INVALID',
|
||||
'https://lux.exchange/swap?inputCurrency=ETH&outputCurrency=0xdAC17F958D2ee523a2206206994597C13D831ec7&field=INVALID',
|
||||
)
|
||||
|
||||
expect(() => parseSwapLinkWebFormatOrThrow(url)).toThrow('Invalid field. Must be either `INPUT` or `OUTPUT`')
|
||||
@@ -175,7 +175,7 @@ describe('parseSwapLink', () => {
|
||||
it('should parse valid Sepolia testnet link', () => {
|
||||
// Using Sepolia testnet chain ID (11155111) and valid USDC address
|
||||
const url = new URL(
|
||||
`https://uniswap.org/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.Sepolia}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.Sepolia}-0x1c7d4b196cb0c7b01d743fbc6116a902379c7238¤cyField=input&amount=1.5`,
|
||||
`https://lux.network/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.Sepolia}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.Sepolia}-0x1c7d4b196cb0c7b01d743fbc6116a902379c7238¤cyField=input&amount=1.5`,
|
||||
)
|
||||
|
||||
const result = parseSwapLinkMobileFormatOrThrow(url)
|
||||
@@ -188,7 +188,7 @@ describe('parseSwapLink', () => {
|
||||
|
||||
it('should parse valid UnichainSepolia link', () => {
|
||||
const url = new URL(
|
||||
`https://uniswap.org/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.UnichainSepolia}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.UnichainSepolia}-0x31d0220469e10c4E71834a79b1f276d740d3768F¤cyField=input&amount=0.5`,
|
||||
`https://lux.network/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.UnichainSepolia}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.UnichainSepolia}-0x31d0220469e10c4E71834a79b1f276d740d3768F¤cyField=input&amount=0.5`,
|
||||
)
|
||||
|
||||
const result = parseSwapLinkMobileFormatOrThrow(url)
|
||||
@@ -202,7 +202,7 @@ describe('parseSwapLink', () => {
|
||||
it('should reject mixed testnet and mainnet chains', () => {
|
||||
// Try to swap from Sepolia (testnet) to Mainnet
|
||||
const url = new URL(
|
||||
`https://uniswap.org/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.Sepolia}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.Mainnet}-0xdAC17F958D2ee523a2206206994597C13D831ec7¤cyField=input&amount=1`,
|
||||
`https://lux.network/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.Sepolia}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.Mainnet}-0xdAC17F958D2ee523a2206206994597C13D831ec7¤cyField=input&amount=1`,
|
||||
)
|
||||
|
||||
const testFn = (): void => {
|
||||
@@ -214,7 +214,7 @@ describe('parseSwapLink', () => {
|
||||
it('should reject mixed mainnet and testnet chains', () => {
|
||||
// Try to swap from Mainnet to UnichainSepolia (testnet)
|
||||
const url = new URL(
|
||||
`https://uniswap.org/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.Mainnet}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.UnichainSepolia}-0x31d0220469e10c4E71834a79b1f276d740d3768F¤cyField=input&amount=1`,
|
||||
`https://lux.network/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.Mainnet}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.UnichainSepolia}-0x31d0220469e10c4E71834a79b1f276d740d3768F¤cyField=input&amount=1`,
|
||||
)
|
||||
|
||||
const testFn = (): void => {
|
||||
@@ -227,7 +227,7 @@ describe('parseSwapLink', () => {
|
||||
describe('Web format with testnets', () => {
|
||||
it('should parse valid Sepolia testnet link', () => {
|
||||
const url = new URL(
|
||||
'https://app.uniswap.org/swap?inputCurrency=ETH&outputCurrency=0x1c7d4b196cb0c7b01d743fbc6116a902379c7238&chain=ethereum_sepolia&value=2.5&field=INPUT',
|
||||
'https://lux.exchange/swap?inputCurrency=ETH&outputCurrency=0x1c7d4b196cb0c7b01d743fbc6116a902379c7238&chain=ethereum_sepolia&value=2.5&field=INPUT',
|
||||
)
|
||||
|
||||
const result = parseSwapLinkWebFormatOrThrow(url)
|
||||
@@ -240,7 +240,7 @@ describe('parseSwapLink', () => {
|
||||
|
||||
it('should handle cross-chain testnet swaps', () => {
|
||||
const url = new URL(
|
||||
'https://app.uniswap.org/swap?inputCurrency=ETH&chain=ethereum_sepolia&outputCurrency=0x31d0220469e10c4E71834a79b1f276d740d3768F&outputChain=unichain_sepolia&value=1.0',
|
||||
'https://lux.exchange/swap?inputCurrency=ETH&chain=ethereum_sepolia&outputCurrency=0x31d0220469e10c4E71834a79b1f276d740d3768F&outputChain=unichain_sepolia&value=1.0',
|
||||
)
|
||||
|
||||
const result = parseSwapLinkWebFormatOrThrow(url)
|
||||
@@ -253,7 +253,7 @@ describe('parseSwapLink', () => {
|
||||
it('should reject mixed testnet and mainnet in web format', () => {
|
||||
// Try to swap from Sepolia to Ethereum mainnet
|
||||
const url = new URL(
|
||||
'https://app.uniswap.org/swap?inputCurrency=ETH&chain=ethereum_sepolia&outputCurrency=0xdAC17F958D2ee523a2206206994597C13D831ec7&outputChain=ethereum',
|
||||
'https://lux.exchange/swap?inputCurrency=ETH&chain=ethereum_sepolia&outputCurrency=0xdAC17F958D2ee523a2206206994597C13D831ec7&outputChain=ethereum',
|
||||
)
|
||||
|
||||
const testFn = (): void => {
|
||||
@@ -265,7 +265,7 @@ describe('parseSwapLink', () => {
|
||||
it('should reject mixed mainnet and testnet in web format', () => {
|
||||
// Try to swap from Ethereum mainnet to Sepolia
|
||||
const url = new URL(
|
||||
'https://app.uniswap.org/swap?inputCurrency=ETH&chain=ethereum&outputCurrency=0x1c7d4b196cb0c7b01d743fbc6116a902379c7238&outputChain=ethereum_sepolia',
|
||||
'https://lux.exchange/swap?inputCurrency=ETH&chain=ethereum&outputCurrency=0x1c7d4b196cb0c7b01d743fbc6116a902379c7238&outputChain=ethereum_sepolia',
|
||||
)
|
||||
|
||||
const testFn = (): void => {
|
||||
@@ -275,7 +275,7 @@ describe('parseSwapLink', () => {
|
||||
})
|
||||
|
||||
it('should handle only input currency on testnet', () => {
|
||||
const url = new URL('https://app.uniswap.org/swap?inputCurrency=ETH&chain=ethereum_sepolia')
|
||||
const url = new URL('https://lux.exchange/swap?inputCurrency=ETH&chain=ethereum_sepolia')
|
||||
|
||||
const result = parseSwapLinkWebFormatOrThrow(url)
|
||||
|
||||
@@ -287,7 +287,7 @@ describe('parseSwapLink', () => {
|
||||
|
||||
it('should handle only output currency on testnet', () => {
|
||||
const url = new URL(
|
||||
'https://app.uniswap.org/swap?outputCurrency=0x1c7d4b196cb0c7b01d743fbc6116a902379c7238&chain=ethereum_sepolia',
|
||||
'https://lux.exchange/swap?outputCurrency=0x1c7d4b196cb0c7b01d743fbc6116a902379c7238&chain=ethereum_sepolia',
|
||||
)
|
||||
|
||||
const result = parseSwapLinkWebFormatOrThrow(url)
|
||||
@@ -303,7 +303,7 @@ describe('parseSwapLink', () => {
|
||||
it('should allow swaps between different testnet chains', () => {
|
||||
// Sepolia to UnichainSepolia should work (both testnets)
|
||||
const url = new URL(
|
||||
`https://uniswap.org/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.Sepolia}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.UnichainSepolia}-0x31d0220469e10c4E71834a79b1f276d740d3768F¤cyField=input&amount=1`,
|
||||
`https://lux.network/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.Sepolia}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.UnichainSepolia}-0x31d0220469e10c4E71834a79b1f276d740d3768F¤cyField=input&amount=1`,
|
||||
)
|
||||
|
||||
const result = parseSwapLinkMobileFormatOrThrow(url)
|
||||
@@ -314,7 +314,7 @@ describe('parseSwapLink', () => {
|
||||
|
||||
it('should allow swaps between UnichainSepolia and Sepolia', () => {
|
||||
const url = new URL(
|
||||
`https://uniswap.org/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.UnichainSepolia}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.Sepolia}-0x1c7d4b196cb0c7b01d743fbc6116a902379c7238¤cyField=output&amount=50`,
|
||||
`https://lux.network/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.UnichainSepolia}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.Sepolia}-0x1c7d4b196cb0c7b01d743fbc6116a902379c7238¤cyField=output&amount=50`,
|
||||
)
|
||||
|
||||
const result = parseSwapLinkMobileFormatOrThrow(url)
|
||||
@@ -329,7 +329,7 @@ describe('parseSwapLink', () => {
|
||||
it('should handle parsing errors gracefully for mobile format', () => {
|
||||
// URL with malformed parameters
|
||||
const url = new URL(
|
||||
'https://uniswap.org/mobile-redirect?screen=swap&inputCurrencyId=invalid&outputCurrencyId=also-invalid',
|
||||
'https://lux.network/mobile-redirect?screen=swap&inputCurrencyId=invalid&outputCurrencyId=also-invalid',
|
||||
)
|
||||
|
||||
expect(() => parseSwapLinkMobileFormatOrThrow(url)).toThrow()
|
||||
|
||||
@@ -126,7 +126,7 @@ export function ScantasticModal({ route }: AppStackScreenProp<typeof ModalName.S
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Origin: 'https://uniswap.org',
|
||||
Origin: 'https://lux.network',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
uuid,
|
||||
@@ -185,7 +185,7 @@ export function ScantasticModal({ route }: AppStackScreenProp<typeof ModalName.S
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Origin: 'https://uniswap.org',
|
||||
Origin: 'https://lux.network',
|
||||
},
|
||||
})
|
||||
if (!response.ok) {
|
||||
|
||||
@@ -42,14 +42,14 @@ export async function initializeWeb3Wallet(): Promise<void> {
|
||||
wcWeb3Wallet = await WalletKit.init({
|
||||
core: wcCore,
|
||||
metadata: {
|
||||
name: 'Uniswap Wallet',
|
||||
name: 'Lux Wallet',
|
||||
description:
|
||||
'Built by the most trusted team in DeFi, Uniswap Wallet allows you to maintain full custody and control of your assets.',
|
||||
url: 'https://uniswap.org/app',
|
||||
'Built by the most trusted team in DeFi, Lux Wallet allows you to maintain full custody and control of your assets.',
|
||||
url: 'https://lux.network/app',
|
||||
icons: ['https://gateway.pinata.cloud/ipfs/QmR1hYqhDMoyvJtwrQ6f1kVyfEKyK65XH3nbCimXBMkHJg'],
|
||||
redirect: {
|
||||
native: 'uniswap://',
|
||||
universal: 'https://uniswap.org/app',
|
||||
universal: 'https://lux.network/app',
|
||||
linkMode: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -423,9 +423,9 @@ describe('handleNotificationNavigation', () => {
|
||||
it('opens Uniswap explore URLs', () => {
|
||||
mockOpenUri.mockResolvedValue(undefined)
|
||||
|
||||
handleNotificationNavigation('https://app.uniswap.org/explore/tokens/monad')
|
||||
handleNotificationNavigation('https://lux.exchange/explore/tokens/monad')
|
||||
|
||||
expect(mockOpenUri).toHaveBeenCalledWith({ uri: 'https://app.uniswap.org/explore/tokens/monad' })
|
||||
expect(mockOpenUri).toHaveBeenCalledWith({ uri: 'https://lux.exchange/explore/tokens/monad' })
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user